buddy-agent-harness 1.0.1 → 1.1.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/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/.cursor-plugin/plugin.json +1 -1
- package/CHANGELOG.md +25 -0
- package/dist/cli.mjs +2227 -840
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +521 -439
- package/dist/index.mjs.map +1 -1
- package/dist/skill-scripts/doctor.mjs +74 -67
- package/dist/skill-scripts/init.mjs +74 -67
- package/dist/skill-scripts/mcp.mjs +74 -67
- package/llms.txt +2 -1
- package/package.json +4 -4
- package/plugin.json +1 -1
- package/skills/doctor-buddy-agent-harness/README.md +1 -1
- package/skills/doctor-buddy-agent-harness/SKILL.md +1 -1
- package/skills/doctor-buddy-agent-harness/scripts/doctor.mjs +74 -67
- package/skills/enhance/SKILL.md +1 -1
- package/skills/enhance/references/list-identifiers.history.md +9 -1
- package/skills/enhance/references/list-identifiers.md +6 -9
- package/skills/enhance/scripts/doctor.mjs +74 -67
- package/skills/init-buddy-agent-harness/SKILL.md +1 -1
- package/skills/init-buddy-agent-harness/scripts/doctor.mjs +74 -67
- package/skills/init-buddy-agent-harness/scripts/init.mjs +74 -67
- package/skills/repair/SKILL.md +1 -1
- package/skills/repair/scripts/doctor.mjs +74 -67
- package/skills/repair/scripts/mcp.mjs +74 -67
- package/src/cli.ts +13 -3
- package/src/command-output/command-output.ts +28 -5
- package/src/dep-plugins/dep-plugins.command.ts +4 -0
- package/src/diagnose-bridges/doctor.command.ts +67 -49
- package/src/governance-overrides/governance.command.ts +13 -8
- package/src/initialize-harnesses/init.command.ts +9 -3
- package/src/project-mcp/mcp.command.ts +15 -9
- package/src/reconcile-mcp/reconcile.command.ts +17 -11
- package/src/references/references.ts +1 -0
package/dist/cli.mjs
CHANGED
|
@@ -1,18 +1,46 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
|
-
import { cpSync, existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, readlinkSync, realpathSync, rmSync, rmdirSync, statSync, symlinkSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { cpSync, existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, readlinkSync, realpathSync, renameSync, rmSync, rmdirSync, statSync, symlinkSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { inspect, styleText } from "node:util";
|
|
4
4
|
import { homedir, platform } from "node:os";
|
|
5
5
|
import { basename, dirname, extname, isAbsolute, join, posix, relative, resolve, sep, win32 } from "node:path";
|
|
6
6
|
import { readFile, readdir } from "node:fs/promises";
|
|
7
7
|
import { execFileSync } from "node:child_process";
|
|
8
8
|
import { fileURLToPath } from "node:url";
|
|
9
|
-
//#region ../../node_modules/.pnpm/clibuilder@12.
|
|
9
|
+
//#region ../../node_modules/.pnpm/clibuilder@12.3.0_js-yaml@4.3.2/node_modules/clibuilder/esm/render/ref.js
|
|
10
10
|
/**
|
|
11
11
|
* Next-step references: `ref()` names another command, and the runner renders it as a command line.
|
|
12
12
|
* Imports nothing from the framework (axi-native RFC section 4.1; next-step-refs RFC).
|
|
13
13
|
*/
|
|
14
14
|
const refKey = Symbol.for("clibuilder.ref");
|
|
15
15
|
const placeholderKey = Symbol.for("clibuilder.placeholder");
|
|
16
|
+
/**
|
|
17
|
+
* A reference to another command, for a `help` list.
|
|
18
|
+
*
|
|
19
|
+
* The runner renders it as a complete command line for the channel it runs in:
|
|
20
|
+
* `<cli name> task get --gid 123` under `cli()`, or the sibling script, `pnpm exec` or pinned `npx` line under `runScript()`.
|
|
21
|
+
*
|
|
22
|
+
* Options render as `--name value` in the order given. `true` renders as `--name`, `false` and `undefined` are left out,
|
|
23
|
+
* an array repeats the flag, and a value is single-quoted when the shell would read it otherwise.
|
|
24
|
+
*
|
|
25
|
+
* @param path the command path as a user types it, without the cli name.
|
|
26
|
+
* @param options the options to pass, keyed by option name. Use {@link placeholder} for a value not known yet.
|
|
27
|
+
* @param why what running it does, as a verb phrase: `Run \`…\` to <why>`.
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```ts
|
|
31
|
+
* return result(task, { help: ref('task get', { gid: task.parent.gid }, 'show the parent task') })
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
function ref(path, options = {}, why) {
|
|
35
|
+
const words = path.trim().split(/\s+/).filter(Boolean);
|
|
36
|
+
if (words.length === 0) throw new TypeError("ref() needs a command path, such as \"task get\"");
|
|
37
|
+
return {
|
|
38
|
+
[refKey]: true,
|
|
39
|
+
path: words.join(" "),
|
|
40
|
+
options: { ...options },
|
|
41
|
+
why
|
|
42
|
+
};
|
|
43
|
+
}
|
|
16
44
|
function isRef(value) {
|
|
17
45
|
return typeof value === "object" && value !== null && value[refKey] === true;
|
|
18
46
|
}
|
|
@@ -66,7 +94,7 @@ function shellQuote(value) {
|
|
|
66
94
|
return /^[\w./:@%+=,-]+$/.test(s) ? s : `'${s.replace(/'/g, `'\\''`)}'`;
|
|
67
95
|
}
|
|
68
96
|
//#endregion
|
|
69
|
-
//#region ../../node_modules/.pnpm/clibuilder@12.
|
|
97
|
+
//#region ../../node_modules/.pnpm/clibuilder@12.3.0_js-yaml@4.3.2/node_modules/clibuilder/esm/app/errors.js
|
|
70
98
|
var _a;
|
|
71
99
|
/**
|
|
72
100
|
* The exit codes a clibuilder cli uses, following the AXI convention so an agent
|
|
@@ -142,7 +170,7 @@ var CliError = class CliError extends Error {
|
|
|
142
170
|
super(report.message);
|
|
143
171
|
this.name = "CliError";
|
|
144
172
|
this[_a] = true;
|
|
145
|
-
this.exitCode = validateExitCode(report.exitCode ?? exitCodes.error);
|
|
173
|
+
this.exitCode = validateExitCode$1(report.exitCode ?? exitCodes.error);
|
|
146
174
|
this.code = validateCode(report.code ?? "error");
|
|
147
175
|
this.details = validateDetails(report.details ?? {});
|
|
148
176
|
const lines = report.help === void 0 ? [] : Array.isArray(report.help) ? [...report.help] : [report.help];
|
|
@@ -163,7 +191,7 @@ function helpLinesOf(err) {
|
|
|
163
191
|
function isReportedCliError(err) {
|
|
164
192
|
return err[reportedBrand] === true;
|
|
165
193
|
}
|
|
166
|
-
function validateExitCode(exitCode) {
|
|
194
|
+
function validateExitCode$1(exitCode) {
|
|
167
195
|
if (Number.isInteger(exitCode) && exitCode >= 1 && exitCode <= 255) return exitCode;
|
|
168
196
|
throw new RangeError(`CliError exitCode must be an integer from 1 to 255, received ${exitCode}`);
|
|
169
197
|
}
|
|
@@ -176,7 +204,7 @@ function validateDetails(details) {
|
|
|
176
204
|
return { ...details };
|
|
177
205
|
}
|
|
178
206
|
//#endregion
|
|
179
|
-
//#region ../../node_modules/.pnpm/clibuilder@12.
|
|
207
|
+
//#region ../../node_modules/.pnpm/clibuilder@12.3.0_js-yaml@4.3.2/node_modules/clibuilder/esm/command/define.js
|
|
180
208
|
/**
|
|
181
209
|
* Defines a command with its argument and option types inferred.
|
|
182
210
|
*
|
|
@@ -198,7 +226,7 @@ function command(cmd) {
|
|
|
198
226
|
return cmd;
|
|
199
227
|
}
|
|
200
228
|
//#endregion
|
|
201
|
-
//#region ../../node_modules/.pnpm/clibuilder@12.
|
|
229
|
+
//#region ../../node_modules/.pnpm/clibuilder@12.3.0_js-yaml@4.3.2/node_modules/clibuilder/esm/descriptor.js
|
|
202
230
|
/** Whether a descriptor option takes a value, as `args-minus` decides: a boolean or boolean literal is a flag. */
|
|
203
231
|
function takesValue$1(data) {
|
|
204
232
|
const d = data.kind === "array" ? data.element : data;
|
|
@@ -228,7 +256,22 @@ function describeRange(min, max) {
|
|
|
228
256
|
return "";
|
|
229
257
|
}
|
|
230
258
|
//#endregion
|
|
231
|
-
//#region ../../node_modules/.pnpm/
|
|
259
|
+
//#region ../../node_modules/.pnpm/clibuilder@12.3.0_js-yaml@4.3.2/node_modules/clibuilder/esm/props.js
|
|
260
|
+
/** The property `key` of `object` itself, or `undefined` when only its prototype has one. */
|
|
261
|
+
function getProp$1(object, key) {
|
|
262
|
+
return Object.prototype.hasOwnProperty.call(object, key) ? object[key] : void 0;
|
|
263
|
+
}
|
|
264
|
+
/** Sets `key` on `object` itself, even when the key is `__proto__`. */
|
|
265
|
+
function setProp(object, key, value) {
|
|
266
|
+
Object.defineProperty(object, key, {
|
|
267
|
+
value,
|
|
268
|
+
enumerable: true,
|
|
269
|
+
writable: true,
|
|
270
|
+
configurable: true
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
//#endregion
|
|
274
|
+
//#region ../../node_modules/.pnpm/args-minus@0.2.3/node_modules/args-minus/esm/t.js
|
|
232
275
|
const descriptorBrand = Symbol.for("args-minus.descriptor");
|
|
233
276
|
const descriptorVersion = 1;
|
|
234
277
|
const methods = {
|
|
@@ -400,7 +443,7 @@ Array.prototype.reduce;
|
|
|
400
443
|
const ArraySlice = Array.prototype.slice;
|
|
401
444
|
const ArrayPush = Array.prototype.push;
|
|
402
445
|
//#endregion
|
|
403
|
-
//#region ../../node_modules/.pnpm/args-minus@0.2.
|
|
446
|
+
//#region ../../node_modules/.pnpm/args-minus@0.2.3/node_modules/args-minus/esm/tokenize_args.js
|
|
404
447
|
const DEFAULT_GRAMMAR = {
|
|
405
448
|
modifiers: "-",
|
|
406
449
|
terminator: "--"
|
|
@@ -472,7 +515,7 @@ function tokenizeArgs(args, grammar) {
|
|
|
472
515
|
return tokens;
|
|
473
516
|
}
|
|
474
517
|
//#endregion
|
|
475
|
-
//#region ../../node_modules/.pnpm/args-minus@0.2.
|
|
518
|
+
//#region ../../node_modules/.pnpm/args-minus@0.2.3/node_modules/args-minus/esm/parse_args.js
|
|
476
519
|
/**
|
|
477
520
|
* Thrown when the input does not fit the declarations: a usage error the caller can fix.
|
|
478
521
|
*/
|
|
@@ -602,7 +645,7 @@ function* parse$1(config) {
|
|
|
602
645
|
const raw = names[i];
|
|
603
646
|
const isLast = i === names.length - 1;
|
|
604
647
|
const name = canonical.get(raw) ?? raw;
|
|
605
|
-
const declaration = declarations
|
|
648
|
+
const declaration = declarations && getProp(declarations, name);
|
|
606
649
|
if (config.strict && !declaration) throw new ParseArgsError(`Unknown option '${raw}' (from '${token.raw}')`, raw, token.raw, { kind: "unknown-option" });
|
|
607
650
|
if (!isLast && declaration && takesValue(declaration)) throw new ParseArgsError(`Invalid option group '${token.raw}': '${raw}' takes a value, so it can only be the last option in a group`, name, token.raw, {
|
|
608
651
|
kind: "invalid-group",
|
|
@@ -666,6 +709,10 @@ function* parse$1(config) {
|
|
|
666
709
|
if (config.tokens) result.tokens = tokens;
|
|
667
710
|
return result;
|
|
668
711
|
}
|
|
712
|
+
/** The property `key` of `object` itself, or `undefined` when only its prototype has one. */
|
|
713
|
+
function getProp(object, key) {
|
|
714
|
+
return Object.prototype.hasOwnProperty.call(object, key) ? object[key] : void 0;
|
|
715
|
+
}
|
|
669
716
|
function checkDeclarations(declarations) {
|
|
670
717
|
for (const [name, declaration] of Object.entries(declarations ?? {})) {
|
|
671
718
|
const type = declaration.type;
|
|
@@ -901,10 +948,10 @@ function* checkDefault(declaration, name) {
|
|
|
901
948
|
checkedDefaults.set(declaration, true);
|
|
902
949
|
}
|
|
903
950
|
//#endregion
|
|
904
|
-
//#region ../../node_modules/.pnpm/clibuilder@12.
|
|
951
|
+
//#region ../../node_modules/.pnpm/clibuilder@12.3.0_js-yaml@4.3.2/node_modules/clibuilder/esm/invocation/argv_occurrences.js
|
|
905
952
|
const optionOccurrences = /* @__PURE__ */ new WeakMap();
|
|
906
953
|
//#endregion
|
|
907
|
-
//#region ../../node_modules/.pnpm/clibuilder@12.
|
|
954
|
+
//#region ../../node_modules/.pnpm/clibuilder@12.3.0_js-yaml@4.3.2/node_modules/clibuilder/esm/invocation/lookup.js
|
|
908
955
|
/**
|
|
909
956
|
* @param fallback a command whose options apply to every command (the global options).
|
|
910
957
|
* It decides how many following tokens such an option takes,
|
|
@@ -986,13 +1033,13 @@ function assignOptionValues(rawArgs, command, fallback) {
|
|
|
986
1033
|
});
|
|
987
1034
|
positionals.push(...o.following.slice(count));
|
|
988
1035
|
const own = o.inline.length + taken.length > 0 ? [...o.inline, ...taken] : ["true"];
|
|
989
|
-
values
|
|
1036
|
+
setProp(values, o.key, [...getProp$1(values, o.key) ?? [], ...own]);
|
|
990
1037
|
}
|
|
991
1038
|
const result = {
|
|
992
1039
|
...rawArgs,
|
|
993
1040
|
_: [...rawArgs._, ...positionals]
|
|
994
1041
|
};
|
|
995
|
-
for (const key of Object.keys(values)) if (rawArgs
|
|
1042
|
+
for (const key of Object.keys(values)) if (getProp$1(rawArgs, key)) setProp(result, key, values[key]);
|
|
996
1043
|
return [result, descriptorOccurrences];
|
|
997
1044
|
}
|
|
998
1045
|
function countFollowing(entry, o) {
|
|
@@ -1116,7 +1163,7 @@ function collectDescriptorOptions(state, occurrences) {
|
|
|
1116
1163
|
function lookupOptions(command, key) {
|
|
1117
1164
|
const opts = command.options;
|
|
1118
1165
|
if (!opts) return [];
|
|
1119
|
-
const options = opts
|
|
1166
|
+
const options = getProp$1(opts, key);
|
|
1120
1167
|
if (options) return [key, options];
|
|
1121
1168
|
const optKey = Object.keys(opts).find((k) => {
|
|
1122
1169
|
const opt = opts[k];
|
|
@@ -1180,6 +1227,25 @@ var RawString = class {
|
|
|
1180
1227
|
this.value = value;
|
|
1181
1228
|
}
|
|
1182
1229
|
};
|
|
1230
|
+
/**
|
|
1231
|
+
* Wraps a pre-formatted string for verbatim emission, typically returned from
|
|
1232
|
+
* an encode `replacer`. Compose with `escapeString` to control quoting yourself.
|
|
1233
|
+
*
|
|
1234
|
+
* @param value The exact text to emit at the value position
|
|
1235
|
+
* @returns A `RawString` marker honored at primitive value positions
|
|
1236
|
+
*
|
|
1237
|
+
* @example
|
|
1238
|
+
* ```ts
|
|
1239
|
+
* encode({ name: 'Ada', age: 30 }, {
|
|
1240
|
+
* replacer: (key, value) => rawString(`"${escapeString(String(value))}"`)
|
|
1241
|
+
* })
|
|
1242
|
+
* // name: "Ada"
|
|
1243
|
+
* // age: "30"
|
|
1244
|
+
* ```
|
|
1245
|
+
*/
|
|
1246
|
+
function rawString(value) {
|
|
1247
|
+
return new RawString(value);
|
|
1248
|
+
}
|
|
1183
1249
|
function isRawString(value) {
|
|
1184
1250
|
return value instanceof RawString;
|
|
1185
1251
|
}
|
|
@@ -1674,7 +1740,7 @@ function resolveOptions(options) {
|
|
|
1674
1740
|
};
|
|
1675
1741
|
}
|
|
1676
1742
|
//#endregion
|
|
1677
|
-
//#region ../../node_modules/.pnpm/clibuilder@12.
|
|
1743
|
+
//#region ../../node_modules/.pnpm/clibuilder@12.3.0_js-yaml@4.3.2/node_modules/clibuilder/esm/render/text.js
|
|
1678
1744
|
/**
|
|
1679
1745
|
* The `text` encoder, the same rendering as `@clibuilder/axi`'s `renderText`.
|
|
1680
1746
|
* Imports nothing from the framework (axi-native RFC section 4.1).
|
|
@@ -1682,14 +1748,14 @@ function resolveOptions(options) {
|
|
|
1682
1748
|
function isRecord$3(value) {
|
|
1683
1749
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1684
1750
|
}
|
|
1685
|
-
function cell(value) {
|
|
1751
|
+
function cell$1(value) {
|
|
1686
1752
|
return value === void 0 ? "" : String(value);
|
|
1687
1753
|
}
|
|
1688
1754
|
function table(rows) {
|
|
1689
1755
|
const columns = [...new Set(rows.flatMap((row) => Object.keys(row)))];
|
|
1690
|
-
const widths = columns.map((column) => Math.max(column.length, ...rows.map((row) => cell(row[column]).length)));
|
|
1756
|
+
const widths = columns.map((column) => Math.max(column.length, ...rows.map((row) => cell$1(row[column]).length)));
|
|
1691
1757
|
const line = (values) => ` ${values.map((value, index) => value.padEnd(widths[index])).join(" ")}`.trimEnd();
|
|
1692
|
-
return [line(columns), ...rows.map((row) => line(columns.map((column) => cell(row[column]))))];
|
|
1758
|
+
return [line(columns), ...rows.map((row) => line(columns.map((column) => cell$1(row[column]))))];
|
|
1693
1759
|
}
|
|
1694
1760
|
/**
|
|
1695
1761
|
* Renders a value for a person: a key per line, a list of records as an aligned table, a list of values as bullets,
|
|
@@ -1708,7 +1774,7 @@ function renderText(value) {
|
|
|
1708
1774
|
}).join("\n");
|
|
1709
1775
|
}
|
|
1710
1776
|
//#endregion
|
|
1711
|
-
//#region ../../node_modules/.pnpm/clibuilder@12.
|
|
1777
|
+
//#region ../../node_modules/.pnpm/clibuilder@12.3.0_js-yaml@4.3.2/node_modules/clibuilder/esm/render/encode.js
|
|
1712
1778
|
/**
|
|
1713
1779
|
* The single place an answer is turned into text.
|
|
1714
1780
|
* Imports nothing from the framework (axi-native RFC section 4.1).
|
|
@@ -1718,11 +1784,40 @@ const formats = [
|
|
|
1718
1784
|
"json",
|
|
1719
1785
|
"text"
|
|
1720
1786
|
];
|
|
1721
|
-
/**
|
|
1787
|
+
/**
|
|
1788
|
+
* Encodes `value` in `format`, without a trailing newline.
|
|
1789
|
+
*
|
|
1790
|
+
* `toon` uses TOON only for collections (change 1 of the AXI conventions): an array, at any depth, is encoded
|
|
1791
|
+
* by the official `@toon-format/toon` encoder, and a string field outside any array is a plain `key: value` line.
|
|
1792
|
+
* The output is still TOON: a TOON decoder reads it back to the same value.
|
|
1793
|
+
*/
|
|
1722
1794
|
function encode(value, format) {
|
|
1723
1795
|
if (format === "json") return JSON.stringify(value);
|
|
1724
1796
|
if (format === "text") return renderText(value);
|
|
1725
|
-
return encode$1(value);
|
|
1797
|
+
return encode$1(value, { replacer: plainLine });
|
|
1798
|
+
}
|
|
1799
|
+
function plainLine(_key, value, path) {
|
|
1800
|
+
if (typeof value !== "string" || path.length === 0 || path.some((p) => typeof p === "number")) return value;
|
|
1801
|
+
return readsBackAsIs(value) ? rawString(value) : value;
|
|
1802
|
+
}
|
|
1803
|
+
/**
|
|
1804
|
+
* Whether a TOON decoder reads `value`, written unquoted after `key: `, back as the same string.
|
|
1805
|
+
* Quoted are: the empty string, edge whitespace, control characters such as a newline, a leading `"`,
|
|
1806
|
+
* a leading `#` (the encoder keeps it for comments), and what reads as another type:
|
|
1807
|
+
* `true`, `false`, `null`, a number and `[]`.
|
|
1808
|
+
*/
|
|
1809
|
+
function readsBackAsIs(value) {
|
|
1810
|
+
if (value === "" || value === "[]" || value.startsWith("\"") || value.startsWith("#")) return false;
|
|
1811
|
+
if (/^\s|\s$/.test(value) || hasControlCharacter(value)) return false;
|
|
1812
|
+
if (value === "true" || value === "false" || value === "null") return false;
|
|
1813
|
+
return !/^[+-]?\d+(?:\.\d+)?(?:e[+-]?\d+)?$/i.test(value);
|
|
1814
|
+
}
|
|
1815
|
+
function hasControlCharacter(value) {
|
|
1816
|
+
for (let i = 0; i < value.length; i++) {
|
|
1817
|
+
const code = value.charCodeAt(i);
|
|
1818
|
+
if (code < 32 || code === 127) return true;
|
|
1819
|
+
}
|
|
1820
|
+
return false;
|
|
1726
1821
|
}
|
|
1727
1822
|
/** The error as a flat value: `error`, `code`, the `details` fields, then `help` (left out when empty). Keys match `@clibuilder/axi`'s `errorOutput` and axi-sdk-js. */
|
|
1728
1823
|
function errorOutput({ message, code, details, help }) {
|
|
@@ -1735,7 +1830,7 @@ function errorOutput({ message, code, details, help }) {
|
|
|
1735
1830
|
return output;
|
|
1736
1831
|
}
|
|
1737
1832
|
//#endregion
|
|
1738
|
-
//#region ../../node_modules/.pnpm/clibuilder@12.
|
|
1833
|
+
//#region ../../node_modules/.pnpm/clibuilder@12.3.0_js-yaml@4.3.2/node_modules/clibuilder/esm/builtin/base_command.js
|
|
1739
1834
|
const globalFormat = Symbol.for("clibuilder.global-format");
|
|
1740
1835
|
/**
|
|
1741
1836
|
* Marks a command's own `format` option that gives way to the global `--format`:
|
|
@@ -1807,7 +1902,7 @@ function yieldToGlobalFormat(command) {
|
|
|
1807
1902
|
};
|
|
1808
1903
|
}
|
|
1809
1904
|
//#endregion
|
|
1810
|
-
//#region ../../node_modules/.pnpm/clibuilder@12.
|
|
1905
|
+
//#region ../../node_modules/.pnpm/clibuilder@12.3.0_js-yaml@4.3.2/node_modules/clibuilder/esm/plugins/registry.js
|
|
1811
1906
|
/** Creates a key for a single value. `id` must be unique across extensions. */
|
|
1812
1907
|
function defineKey(id) {
|
|
1813
1908
|
return {
|
|
@@ -1857,7 +1952,7 @@ function createRegistry() {
|
|
|
1857
1952
|
};
|
|
1858
1953
|
}
|
|
1859
1954
|
//#endregion
|
|
1860
|
-
//#region ../../node_modules/.pnpm/clibuilder@12.
|
|
1955
|
+
//#region ../../node_modules/.pnpm/clibuilder@12.3.0_js-yaml@4.3.2/node_modules/clibuilder/esm/config/key.js
|
|
1861
1956
|
/**
|
|
1862
1957
|
* The shared-state key the `clibuilder/config` extension stores the resolved config under.
|
|
1863
1958
|
*
|
|
@@ -1866,7 +1961,7 @@ function createRegistry() {
|
|
|
1866
1961
|
*/
|
|
1867
1962
|
const configKey = defineKey("clibuilder.config");
|
|
1868
1963
|
//#endregion
|
|
1869
|
-
//#region ../../node_modules/.pnpm/clibuilder@12.
|
|
1964
|
+
//#region ../../node_modules/.pnpm/clibuilder@12.3.0_js-yaml@4.3.2/node_modules/clibuilder/esm/invocation/argv.js
|
|
1870
1965
|
function parseArgv(argv) {
|
|
1871
1966
|
const occurrences = [];
|
|
1872
1967
|
const result = argv.slice(2).reduce((result, v) => {
|
|
@@ -1941,9 +2036,10 @@ function startMultiCharacterOptions(result, value) {
|
|
|
1941
2036
|
function endOption(state) {
|
|
1942
2037
|
const { key, values } = state;
|
|
1943
2038
|
const args = { ...state.args };
|
|
1944
|
-
|
|
1945
|
-
if (values.length === 0)
|
|
1946
|
-
else
|
|
2039
|
+
const list = getProp$1(args, key) ?? [];
|
|
2040
|
+
if (values.length === 0) list.push("true");
|
|
2041
|
+
else list.push(...values);
|
|
2042
|
+
setProp(args, key, list);
|
|
1947
2043
|
return {
|
|
1948
2044
|
args,
|
|
1949
2045
|
key: "",
|
|
@@ -1951,7 +2047,7 @@ function endOption(state) {
|
|
|
1951
2047
|
};
|
|
1952
2048
|
}
|
|
1953
2049
|
//#endregion
|
|
1954
|
-
//#region ../../node_modules/.pnpm/clibuilder@12.
|
|
2050
|
+
//#region ../../node_modules/.pnpm/clibuilder@12.3.0_js-yaml@4.3.2/node_modules/clibuilder/esm/invocation/descriptors.js
|
|
1955
2051
|
const declarations = /* @__PURE__ */ new WeakMap();
|
|
1956
2052
|
/**
|
|
1957
2053
|
* Converts and checks every declaration of the matched command through `args-minus`,
|
|
@@ -2003,7 +2099,7 @@ function configOf(input) {
|
|
|
2003
2099
|
};
|
|
2004
2100
|
}
|
|
2005
2101
|
function assign(result, input, { options }) {
|
|
2006
|
-
if (options[input.name] !== void 0) result.args
|
|
2102
|
+
if (options[input.name] !== void 0) setProp(result.args, input.name, options[input.name]);
|
|
2007
2103
|
}
|
|
2008
2104
|
function report$1(result, input, e) {
|
|
2009
2105
|
if (!(e instanceof ParseArgsError)) throw e;
|
|
@@ -2050,7 +2146,7 @@ function rawValues(input) {
|
|
|
2050
2146
|
return input.occurrences.map((o) => o.tokens.join(" "));
|
|
2051
2147
|
}
|
|
2052
2148
|
//#endregion
|
|
2053
|
-
//#region ../../node_modules/.pnpm/clibuilder@12.
|
|
2149
|
+
//#region ../../node_modules/.pnpm/clibuilder@12.3.0_js-yaml@4.3.2/node_modules/clibuilder/esm/render/answer.js
|
|
2054
2150
|
/**
|
|
2055
2151
|
* The result channel: what `run()` returns, and how it is printed under `output: 'axi'`.
|
|
2056
2152
|
* Imports nothing from the framework (axi-native RFC section 4.1).
|
|
@@ -2063,19 +2159,31 @@ const answerKey = Symbol.for("clibuilder.answer");
|
|
|
2063
2159
|
* inside it. A renderer for the format replaces the encoder: its string is written verbatim, and the
|
|
2064
2160
|
* `help` follows it, encoded. `parse()` resolves to `value`.
|
|
2065
2161
|
*
|
|
2162
|
+
* The cli exits with `exitCode`, 0 by default, in either output mode.
|
|
2163
|
+
* A non-zero one returns an answer that is also a failure, printed as a result rather than as an error.
|
|
2164
|
+
*
|
|
2066
2165
|
* @example
|
|
2067
2166
|
* ```ts
|
|
2068
2167
|
* run() {
|
|
2069
2168
|
* return result({ items }, { help: 'Run `my-cli show <id>` for one', render: { text: (v) => table(v.items) } })
|
|
2070
2169
|
* }
|
|
2071
2170
|
* ```
|
|
2171
|
+
*
|
|
2172
|
+
* @example
|
|
2173
|
+
* ```ts
|
|
2174
|
+
* run({ name }) {
|
|
2175
|
+
* const exists = panes.has(name)
|
|
2176
|
+
* return result({ exists }, { exitCode: exists ? exitCodes.success : exitCodes.error })
|
|
2177
|
+
* }
|
|
2178
|
+
* ```
|
|
2072
2179
|
*/
|
|
2073
2180
|
function result(value, options = {}) {
|
|
2074
2181
|
return {
|
|
2075
2182
|
[answerKey]: "result",
|
|
2076
2183
|
value,
|
|
2077
2184
|
help: helpLines(options.help),
|
|
2078
|
-
render: options.render
|
|
2185
|
+
render: options.render,
|
|
2186
|
+
exitCode: validateExitCode(options.exitCode)
|
|
2079
2187
|
};
|
|
2080
2188
|
}
|
|
2081
2189
|
/**
|
|
@@ -2083,11 +2191,13 @@ function result(value, options = {}) {
|
|
|
2083
2191
|
* whatever the format, ending on a newline. `parse()` resolves to `content`.
|
|
2084
2192
|
*
|
|
2085
2193
|
* For output that is already the answer, such as Markdown, a diff or a file.
|
|
2194
|
+
* The cli exits with `exitCode`, 0 by default, in either output mode.
|
|
2086
2195
|
*/
|
|
2087
|
-
function document(content) {
|
|
2196
|
+
function document(content, options = {}) {
|
|
2088
2197
|
return {
|
|
2089
2198
|
[answerKey]: "document",
|
|
2090
|
-
value: String(content)
|
|
2199
|
+
value: String(content),
|
|
2200
|
+
exitCode: validateExitCode(options.exitCode)
|
|
2091
2201
|
};
|
|
2092
2202
|
}
|
|
2093
2203
|
/**
|
|
@@ -2115,6 +2225,15 @@ function answerValue(returned) {
|
|
|
2115
2225
|
return isAnswer(returned) ? returned.value : returned;
|
|
2116
2226
|
}
|
|
2117
2227
|
/**
|
|
2228
|
+
* The code the cli exits with for what `run()` returned: the `exitCode` of a `result()` or `document()`, otherwise 0.
|
|
2229
|
+
* Read field by field: an answer from an older copy of clibuilder has no `exitCode`.
|
|
2230
|
+
*/
|
|
2231
|
+
function answerExitCode(returned) {
|
|
2232
|
+
if (!isAnswer(returned)) return 0;
|
|
2233
|
+
const code = returned.exitCode;
|
|
2234
|
+
return typeof code === "number" ? code : 0;
|
|
2235
|
+
}
|
|
2236
|
+
/**
|
|
2118
2237
|
* The text written on stdout for what `run()` returned, ending on one newline, or `undefined` when nothing is printed.
|
|
2119
2238
|
* A string is written verbatim, an object is encoded, and an array or scalar is encoded under the `result` key
|
|
2120
2239
|
* so `help` has a place beside it.
|
|
@@ -2122,18 +2241,22 @@ function answerValue(returned) {
|
|
|
2122
2241
|
function printAnswer(returned, format, options = {}) {
|
|
2123
2242
|
if (returned === void 0) return void 0;
|
|
2124
2243
|
const tail = (lines) => renderHelp(lines, options.resolve, options.refs);
|
|
2244
|
+
const withVerbs = (value, lines) => options.verbs && !hasOwnCommands(value) ? [...lines, options.verbs] : lines;
|
|
2125
2245
|
if (!isAnswer(returned)) return print(returned, tail([]), format);
|
|
2126
2246
|
switch (returned[answerKey]) {
|
|
2127
2247
|
case "written": return;
|
|
2128
2248
|
case "document": return line(returned.value);
|
|
2129
|
-
case "empty": return line(encode(withHelp({ empty: returned.value }, tail(returned.help)), format));
|
|
2249
|
+
case "empty": return line(encode(withHelp({ empty: returned.value }, tail(withVerbs(returned.value, returned.help))), format));
|
|
2130
2250
|
case "result": {
|
|
2131
2251
|
const render = returned.render?.[format];
|
|
2132
|
-
if (render) return withRenderedHelp(render(returned.value), tail(returned.help), format);
|
|
2133
|
-
return print(returned.value, tail(returned.help), format);
|
|
2252
|
+
if (render) return withRenderedHelp(render(returned.value), tail(withVerbs(returned.value, returned.help)), format);
|
|
2253
|
+
return print(returned.value, tail(withVerbs(returned.value, returned.help)), format);
|
|
2134
2254
|
}
|
|
2135
2255
|
}
|
|
2136
2256
|
}
|
|
2257
|
+
function hasOwnCommands(value) {
|
|
2258
|
+
return typeof value === "object" && value !== null && "commands" in value;
|
|
2259
|
+
}
|
|
2137
2260
|
function print(value, tail, format) {
|
|
2138
2261
|
if (typeof value === "string") return withRenderedHelp(value, tail, format);
|
|
2139
2262
|
return line(encode(withHelp(toRecord(value), tail), format));
|
|
@@ -2164,11 +2287,16 @@ function helpLines(help) {
|
|
|
2164
2287
|
if (help === void 0) return [];
|
|
2165
2288
|
return Array.isArray(help) ? [...help] : [help];
|
|
2166
2289
|
}
|
|
2290
|
+
function validateExitCode(exitCode) {
|
|
2291
|
+
if (exitCode === void 0) return 0;
|
|
2292
|
+
if (Number.isInteger(exitCode) && exitCode >= 0 && exitCode <= 255) return exitCode;
|
|
2293
|
+
throw new RangeError(`an answer's exitCode must be an integer from 0 to 255, received ${exitCode}`);
|
|
2294
|
+
}
|
|
2167
2295
|
function line(text) {
|
|
2168
2296
|
return text.endsWith("\n") ? text : `${text}\n`;
|
|
2169
2297
|
}
|
|
2170
2298
|
//#endregion
|
|
2171
|
-
//#region ../../node_modules/.pnpm/clibuilder@12.
|
|
2299
|
+
//#region ../../node_modules/.pnpm/clibuilder@12.3.0_js-yaml@4.3.2/node_modules/clibuilder/esm/render/error.js
|
|
2172
2300
|
/** Describes one `lookupCommand` error in the terms the caller used. `command` tells an argument name from an option name. */
|
|
2173
2301
|
function formatLookupError(error, command) {
|
|
2174
2302
|
switch (error.type) {
|
|
@@ -2196,7 +2324,7 @@ function toArray$1(value) {
|
|
|
2196
2324
|
return Array.isArray(value) ? value : [value];
|
|
2197
2325
|
}
|
|
2198
2326
|
//#endregion
|
|
2199
|
-
//#region ../../node_modules/.pnpm/clibuilder@12.
|
|
2327
|
+
//#region ../../node_modules/.pnpm/clibuilder@12.3.0_js-yaml@4.3.2/node_modules/clibuilder/esm/render/wrap.js
|
|
2200
2328
|
/**
|
|
2201
2329
|
* Wraps `text` at `width` columns, breaking only between words; a longer word stays whole.
|
|
2202
2330
|
* A tab counts as four spaces.
|
|
@@ -2219,15 +2347,19 @@ function wrap(text, width) {
|
|
|
2219
2347
|
return lines.join("\n");
|
|
2220
2348
|
}
|
|
2221
2349
|
//#endregion
|
|
2222
|
-
//#region ../../node_modules/.pnpm/clibuilder@12.
|
|
2350
|
+
//#region ../../node_modules/.pnpm/clibuilder@12.3.0_js-yaml@4.3.2/node_modules/clibuilder/esm/render/help.js
|
|
2223
2351
|
const RIGHT_PADDING = 2;
|
|
2224
2352
|
const MIN_LHS_WIDTH = 25;
|
|
2225
|
-
|
|
2353
|
+
/**
|
|
2354
|
+
* `signatures` lists a group's sub-commands with their full signatures (AXI change 2).
|
|
2355
|
+
* Left off, the list is names only, as `output: 'console'` has always printed it.
|
|
2356
|
+
*/
|
|
2357
|
+
function generateHelpMessage(cliName, command, options) {
|
|
2226
2358
|
return `
|
|
2227
2359
|
${[
|
|
2228
2360
|
generateUsageSection(cliName, command),
|
|
2229
2361
|
generateDescriptionSection(command),
|
|
2230
|
-
generateCommandsSection(command),
|
|
2362
|
+
generateCommandsSection(command, options?.signatures ?? false),
|
|
2231
2363
|
generateArgumentsSection(command),
|
|
2232
2364
|
generateOptionsSection(command),
|
|
2233
2365
|
generateAliasSection(command),
|
|
@@ -2241,12 +2373,23 @@ function generateUsageSection(cliName, command) {
|
|
|
2241
2373
|
const hasCommand = command.commands && command.commands.length > 0;
|
|
2242
2374
|
let message = `Usage: ${nameChain.join(" ")}${hasCommand ? " <command>" : ""}`;
|
|
2243
2375
|
if (command.arguments) message += command.arguments.some((a) => isRequired(a, true)) ? " <arguments>" : " [arguments]";
|
|
2244
|
-
if (command
|
|
2376
|
+
if (hasOptions(command)) {
|
|
2245
2377
|
const options = command.options;
|
|
2246
2378
|
message += Object.keys(options).some((k) => isRequired(options[k], false)) ? " <options>" : " [options]";
|
|
2247
2379
|
}
|
|
2248
2380
|
return message;
|
|
2249
2381
|
}
|
|
2382
|
+
function hasOptions(command) {
|
|
2383
|
+
return !!command.options && Object.keys(command.options).length > 0;
|
|
2384
|
+
}
|
|
2385
|
+
/**
|
|
2386
|
+
* A sub-command as a bare group lists it: its name and its required arguments, such as `get <id>`
|
|
2387
|
+
* (AXI change 2: names and required arguments only).
|
|
2388
|
+
*/
|
|
2389
|
+
function commandSignature(command) {
|
|
2390
|
+
const required = (command.arguments ?? []).filter((a) => isRequired(a, true)).map((a) => `<${a.name}>`);
|
|
2391
|
+
return [command.name, ...required].join(" ");
|
|
2392
|
+
}
|
|
2250
2393
|
function isRequired({ type, default: d }, defaultValue) {
|
|
2251
2394
|
if (!type) return defaultValue;
|
|
2252
2395
|
return !type.isOptional && d === void 0;
|
|
@@ -2262,22 +2405,37 @@ function getCommandNameChain(cliName, command) {
|
|
|
2262
2405
|
}
|
|
2263
2406
|
return [cliName, ...commands.map((c) => c.name).filter((x) => x)];
|
|
2264
2407
|
}
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2408
|
+
/**
|
|
2409
|
+
* A sub-command as its group's `--help` lists it: name, alias, and the full signature of its
|
|
2410
|
+
* arguments and options, such as `add <name: string> [--force]` (AXI change 2: a node's --help is its schema).
|
|
2411
|
+
*/
|
|
2412
|
+
function commandFullSignature(command) {
|
|
2413
|
+
const parts = [command.alias?.length ? `${command.name} (${command.alias.join("|")})` : command.name];
|
|
2414
|
+
if (command.commands && command.commands.length > 0) parts.push("<command>");
|
|
2415
|
+
for (const a of command.arguments ?? []) parts.push(a.type ? formatArgumentDescriptor(a.name, a.type) : `<${a.name}>`);
|
|
2416
|
+
if (hasOptions(command)) {
|
|
2417
|
+
const options = command.options;
|
|
2418
|
+
for (const key in options) parts.push(formatKeyValue(key, options[key]));
|
|
2419
|
+
}
|
|
2420
|
+
return parts.join(" ");
|
|
2421
|
+
}
|
|
2422
|
+
function generateCommandsSection(command, signatures) {
|
|
2423
|
+
const commands = command.commands ?? [];
|
|
2424
|
+
if (commands.length === 0) return "";
|
|
2425
|
+
if (!signatures) return `Commands:
|
|
2426
|
+
${wrap(commands.map((c) => c.alias ? `${c.name} (${c.alias.join("|")})` : c.name).join(", "), 80)}
|
|
2427
|
+
|
|
2428
|
+
${(command.name ? `${command.name} <command> -h` : " <command> -h").padEnd(MIN_LHS_WIDTH)}Get help for <command>`;
|
|
2429
|
+
const sigs = commands.map(commandFullSignature);
|
|
2430
|
+
const width = Math.max(23, ...sigs.map((x) => x.length + RIGHT_PADDING));
|
|
2268
2431
|
return `Commands:
|
|
2269
|
-
|
|
2432
|
+
${commands.map((c, i) => {
|
|
2433
|
+
const line = c.description?.split("\n")[0] ?? "";
|
|
2434
|
+
return ` ${sigs[i].padEnd(width)}${line}`.trimEnd();
|
|
2435
|
+
}).join("\n")}
|
|
2270
2436
|
|
|
2271
2437
|
${(command.name ? `${command.name} <command> -h` : " <command> -h").padEnd(MIN_LHS_WIDTH)}Get help for <command>`;
|
|
2272
2438
|
}
|
|
2273
|
-
function getCommandsNamesAndAlias(commands) {
|
|
2274
|
-
const result = [];
|
|
2275
|
-
if (commands) commands.forEach((c) => {
|
|
2276
|
-
if (c.alias) result.push(`${c.name} (${c.alias.join("|")})`);
|
|
2277
|
-
else result.push(c.name);
|
|
2278
|
-
});
|
|
2279
|
-
return result;
|
|
2280
|
-
}
|
|
2281
2439
|
function generateArgumentsSection(command) {
|
|
2282
2440
|
if (!command.arguments) return "";
|
|
2283
2441
|
let message = "Arguments:\n";
|
|
@@ -2293,14 +2451,15 @@ function generateArgumentsSection(command) {
|
|
|
2293
2451
|
return message;
|
|
2294
2452
|
}
|
|
2295
2453
|
function generateOptionsSection(command) {
|
|
2296
|
-
if (!command
|
|
2454
|
+
if (!hasOptions(command)) return "";
|
|
2297
2455
|
let message = "Options:\n";
|
|
2298
2456
|
const entries = [];
|
|
2299
2457
|
let maxOptionStrWidth = 0;
|
|
2300
|
-
|
|
2301
|
-
|
|
2458
|
+
const options = command.options;
|
|
2459
|
+
for (const key in options) {
|
|
2460
|
+
const value = options[key];
|
|
2302
2461
|
const optionStr = formatKeyValue(key, value);
|
|
2303
|
-
const description = formatDescription(value, conflictsOf(key,
|
|
2462
|
+
const description = formatDescription(value, conflictsOf(key, options));
|
|
2304
2463
|
entries.push([optionStr, description]);
|
|
2305
2464
|
maxOptionStrWidth = Math.max(maxOptionStrWidth, optionStr.length);
|
|
2306
2465
|
}
|
|
@@ -2396,7 +2555,13 @@ function generateHelpModel(cliName, command, globalOptions) {
|
|
|
2396
2555
|
if (command.description) model.description = command.description;
|
|
2397
2556
|
if (command.alias && command.alias.length > 0) model.aliases = [...command.alias];
|
|
2398
2557
|
if (command.commands && command.commands.length > 0) model.commands = command.commands.map((c) => {
|
|
2399
|
-
const summary = {
|
|
2558
|
+
const summary = {
|
|
2559
|
+
name: c.name,
|
|
2560
|
+
usage: commandFullSignature({
|
|
2561
|
+
...c,
|
|
2562
|
+
alias: void 0
|
|
2563
|
+
})
|
|
2564
|
+
};
|
|
2400
2565
|
if (c.alias && c.alias.length > 0) summary.aliases = [...c.alias];
|
|
2401
2566
|
const line = c.description?.split("\n")[0];
|
|
2402
2567
|
if (line) summary.description = line;
|
|
@@ -2420,7 +2585,12 @@ function generateHelpModel(cliName, command, globalOptions) {
|
|
|
2420
2585
|
command,
|
|
2421
2586
|
description
|
|
2422
2587
|
}));
|
|
2423
|
-
model.globalOptions = optionModels(globalOptions, []);
|
|
2588
|
+
if (isRoot) model.globalOptions = optionModels(globalOptions, []);
|
|
2589
|
+
else {
|
|
2590
|
+
const others = Object.keys(globalOptions).filter((k) => k !== "help" && k !== "format");
|
|
2591
|
+
model.globalOptions = optionModels(globalOptions, others);
|
|
2592
|
+
if (others.length > 0) model.otherGlobalOptions = others;
|
|
2593
|
+
}
|
|
2424
2594
|
return model;
|
|
2425
2595
|
}
|
|
2426
2596
|
function optionModels(options, exclude) {
|
|
@@ -2470,8 +2640,42 @@ function describeData(data) {
|
|
|
2470
2640
|
default: return { type: data.kind };
|
|
2471
2641
|
}
|
|
2472
2642
|
}
|
|
2643
|
+
/**
|
|
2644
|
+
* The structured help shaped for `toon`: each collection of rows becomes a uniform table.
|
|
2645
|
+
*
|
|
2646
|
+
* TOON writes an array as a table only when every row has the same keys and every cell is a scalar.
|
|
2647
|
+
* A row of the help model leaves out a key with nothing to say, and some keys hold an array,
|
|
2648
|
+
* so the collections fell back to list form. Here every row of a collection carries the union of its keys,
|
|
2649
|
+
* a missing one as `null`, and an array of scalars as one comma-joined string.
|
|
2650
|
+
* The `json` shape is unchanged: this is applied for `toon` only.
|
|
2651
|
+
*/
|
|
2652
|
+
function tabularHelpModel(model) {
|
|
2653
|
+
const shaped = { ...model };
|
|
2654
|
+
for (const key of [
|
|
2655
|
+
"commands",
|
|
2656
|
+
"arguments",
|
|
2657
|
+
"options",
|
|
2658
|
+
"examples",
|
|
2659
|
+
"globalOptions"
|
|
2660
|
+
]) {
|
|
2661
|
+
const rows = model[key];
|
|
2662
|
+
if (rows) shaped[key] = uniformRows(rows);
|
|
2663
|
+
}
|
|
2664
|
+
return shaped;
|
|
2665
|
+
}
|
|
2666
|
+
function uniformRows(rows) {
|
|
2667
|
+
const keys = [];
|
|
2668
|
+
for (const row of rows) for (const key of Object.keys(row)) if (!keys.includes(key)) keys.push(key);
|
|
2669
|
+
return rows.map((row) => Object.fromEntries(keys.map((key) => [key, cell(row[key])])));
|
|
2670
|
+
}
|
|
2671
|
+
function cell(value) {
|
|
2672
|
+
if (value === void 0) return null;
|
|
2673
|
+
if (Array.isArray(value)) return value.map(String).join(",");
|
|
2674
|
+
if (typeof value === "object" && value !== null) return JSON.stringify(value);
|
|
2675
|
+
return value;
|
|
2676
|
+
}
|
|
2473
2677
|
//#endregion
|
|
2474
|
-
//#region ../../node_modules/.pnpm/clibuilder@12.
|
|
2678
|
+
//#region ../../node_modules/.pnpm/clibuilder@12.3.0_js-yaml@4.3.2/node_modules/clibuilder/esm/render/usage_error.js
|
|
2475
2679
|
const rank = {
|
|
2476
2680
|
"invalid-key": 0,
|
|
2477
2681
|
"conflicting-options": 1,
|
|
@@ -2564,7 +2768,7 @@ function describeUsageError(error, command, cliName, hasHelp = true) {
|
|
|
2564
2768
|
/** A command group invoked without its sub-command (#609): `missing-command`, with the valid sub-commands in `help`. */
|
|
2565
2769
|
function describeMissingCommand(command, cliName) {
|
|
2566
2770
|
const name = commandName(command, cliName);
|
|
2567
|
-
const commands = (command.commands ?? []).map(
|
|
2771
|
+
const commands = (command.commands ?? []).map(commandSignature);
|
|
2568
2772
|
return {
|
|
2569
2773
|
message: `missing command for \`${name}\``,
|
|
2570
2774
|
code: "missing-command",
|
|
@@ -2594,7 +2798,7 @@ function isOptional(type) {
|
|
|
2594
2798
|
}
|
|
2595
2799
|
function argumentsHelp(command, cliName) {
|
|
2596
2800
|
const name = command.name || cliName;
|
|
2597
|
-
const commands = (command.commands ?? []).map(
|
|
2801
|
+
const commands = (command.commands ?? []).map(commandSignature);
|
|
2598
2802
|
if (!command.arguments?.length && commands.length > 0) return `valid commands for \`${name}\`: ${commands.join(", ")}`;
|
|
2599
2803
|
const args = (command.arguments ?? []).map((a) => isOptional(a.type) ? `[${a.name}]` : `<${a.name}>`);
|
|
2600
2804
|
return args.length > 0 ? `\`${name}\` takes: ${args.join(" ")}` : `\`${name}\` takes no arguments`;
|
|
@@ -2622,7 +2826,7 @@ function toArray(value) {
|
|
|
2622
2826
|
return Array.isArray(value) ? value : [value];
|
|
2623
2827
|
}
|
|
2624
2828
|
//#endregion
|
|
2625
|
-
//#region ../../node_modules/.pnpm/clibuilder@12.
|
|
2829
|
+
//#region ../../node_modules/.pnpm/clibuilder@12.3.0_js-yaml@4.3.2/node_modules/clibuilder/esm/app/state.js
|
|
2626
2830
|
function state(options) {
|
|
2627
2831
|
const { name, description = "", version } = options;
|
|
2628
2832
|
return {
|
|
@@ -2634,7 +2838,7 @@ function state(options) {
|
|
|
2634
2838
|
};
|
|
2635
2839
|
}
|
|
2636
2840
|
//#endregion
|
|
2637
|
-
//#region ../../node_modules/.pnpm/clibuilder@12.
|
|
2841
|
+
//#region ../../node_modules/.pnpm/clibuilder@12.3.0_js-yaml@4.3.2/node_modules/clibuilder/esm/app/usage_errors.js
|
|
2638
2842
|
/**
|
|
2639
2843
|
* Looks the invocation up on the base command, the single source of truth for the global options.
|
|
2640
2844
|
* A bad `--format` value falls back to `format`, so its usage error is reported in a valid format and cannot loop.
|
|
@@ -2693,7 +2897,7 @@ function withAllowedFormats(baseCommand) {
|
|
|
2693
2897
|
} : e;
|
|
2694
2898
|
}
|
|
2695
2899
|
//#endregion
|
|
2696
|
-
//#region ../../node_modules/.pnpm/clibuilder@12.
|
|
2900
|
+
//#region ../../node_modules/.pnpm/clibuilder@12.3.0_js-yaml@4.3.2/node_modules/clibuilder/esm/app/builder.js
|
|
2697
2901
|
function builder(context, options) {
|
|
2698
2902
|
const s = state(options);
|
|
2699
2903
|
const format = options.format ?? "toon";
|
|
@@ -2881,7 +3085,7 @@ function builder(context, options) {
|
|
|
2881
3085
|
for (const hook of beforeRunHooks) await hook(run);
|
|
2882
3086
|
} catch (e) {
|
|
2883
3087
|
if (!isCliError(e)) throw e;
|
|
2884
|
-
reportCliError(e, globals.format, ui);
|
|
3088
|
+
reportCliError(e, globals.format, ui, command);
|
|
2885
3089
|
return exit(e.exitCode);
|
|
2886
3090
|
}
|
|
2887
3091
|
s.config = run.config;
|
|
@@ -2897,16 +3101,16 @@ function builder(context, options) {
|
|
|
2897
3101
|
commandInstance.ui.showVersion = () => writeVersion();
|
|
2898
3102
|
}
|
|
2899
3103
|
try {
|
|
2900
|
-
return answer(await commandInstance.run(args), globals.format);
|
|
3104
|
+
return answer(await commandInstance.run(args), globals.format, command);
|
|
2901
3105
|
} catch (e) {
|
|
2902
3106
|
if (!isCliError(e)) throw e;
|
|
2903
|
-
reportCliError(e, globals.format, commandInstance.ui);
|
|
3107
|
+
reportCliError(e, globals.format, commandInstance.ui, command);
|
|
2904
3108
|
return exit(e.exitCode);
|
|
2905
3109
|
}
|
|
2906
3110
|
}
|
|
2907
|
-
function reportCliError(e, format, ui) {
|
|
3111
|
+
function reportCliError(e, format, ui, command) {
|
|
2908
3112
|
if (isReportedCliError(e)) return;
|
|
2909
|
-
const { help } = renderHelp(helpLinesOf(e), resolveRef);
|
|
3113
|
+
const { help } = renderHelp(helpLinesOf(e), (r) => resolveRef(r, command));
|
|
2910
3114
|
if (output === "axi") {
|
|
2911
3115
|
const report = errorOutput({
|
|
2912
3116
|
message: e.message,
|
|
@@ -2924,36 +3128,72 @@ function builder(context, options) {
|
|
|
2924
3128
|
function writeReport(report, format) {
|
|
2925
3129
|
context.writeStdout(`${encode(errorOutput(report), format)}\n`);
|
|
2926
3130
|
}
|
|
2927
|
-
function answer(returned, format) {
|
|
3131
|
+
function answer(returned, format, command) {
|
|
2928
3132
|
if (output === "axi") {
|
|
3133
|
+
const verbs = command?.commands?.length ? `valid commands for \`${commandName(command, s.name)}\`: ${command.commands.map(commandSignature).join(", ")}` : void 0;
|
|
2929
3134
|
const text = printAnswer(returned, format, {
|
|
2930
|
-
resolve: resolveRef,
|
|
2931
|
-
refs
|
|
3135
|
+
resolve: (r) => resolveRef(r, command),
|
|
3136
|
+
refs,
|
|
3137
|
+
verbs
|
|
2932
3138
|
});
|
|
2933
3139
|
if (text !== void 0) context.writeStdout(text);
|
|
2934
3140
|
}
|
|
3141
|
+
const code = answerExitCode(returned);
|
|
3142
|
+
if (code !== exitCodes.success) context.exit(code);
|
|
2935
3143
|
return answerValue(returned);
|
|
2936
3144
|
}
|
|
2937
|
-
function
|
|
2938
|
-
|
|
3145
|
+
function helpModel(command, baseCommand, format) {
|
|
3146
|
+
const model = generateHelpModel(s.name, command, baseCommand.options);
|
|
3147
|
+
return format === "toon" ? tabularHelpModel(model) : model;
|
|
2939
3148
|
}
|
|
2940
|
-
function
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
3149
|
+
function writeHelp(command, baseCommand, format) {
|
|
3150
|
+
context.writeStdout(format === "text" ? `${generateHelpMessage(s.name, command, { signatures: true })}\n` : `${encode(helpModel(command, baseCommand, format), format)}\n`);
|
|
3151
|
+
}
|
|
3152
|
+
function resolveRef(ref, from) {
|
|
3153
|
+
const words = ref.path.split(" ");
|
|
3154
|
+
let found;
|
|
3155
|
+
for (const scope of scopesOf(from)) {
|
|
3156
|
+
const command = walk(scope, words);
|
|
3157
|
+
if (command) {
|
|
3158
|
+
found = {
|
|
3159
|
+
command,
|
|
3160
|
+
scope
|
|
3161
|
+
};
|
|
3162
|
+
break;
|
|
3163
|
+
}
|
|
2946
3164
|
}
|
|
3165
|
+
if (!found) throw new TypeError(`ref('${ref.path}'): ${s.name} has no command "${ref.path}"`);
|
|
3166
|
+
const { command, scope } = found;
|
|
2947
3167
|
for (const name of Object.keys(ref.options)) {
|
|
2948
3168
|
if (command.options?.[name] || s.command.options?.[name]) continue;
|
|
2949
3169
|
throw new TypeError(`ref('${ref.path}'): the command has no option "${name}"`);
|
|
2950
3170
|
}
|
|
2951
3171
|
return [
|
|
2952
3172
|
s.name,
|
|
3173
|
+
...pathOf(scope),
|
|
2953
3174
|
ref.path,
|
|
2954
3175
|
...refArgs(ref)
|
|
2955
3176
|
].join(" ");
|
|
2956
3177
|
}
|
|
3178
|
+
function scopesOf(from) {
|
|
3179
|
+
const scopes = [];
|
|
3180
|
+
for (let c = from?.parent; c?.parent; c = c.parent) scopes.push(c);
|
|
3181
|
+
scopes.push(s.command);
|
|
3182
|
+
return scopes;
|
|
3183
|
+
}
|
|
3184
|
+
function walk(scope, words) {
|
|
3185
|
+
let command = scope;
|
|
3186
|
+
for (const word of words) {
|
|
3187
|
+
command = command.commands?.find((c) => c.name === word || c.alias?.includes(word));
|
|
3188
|
+
if (!command) return void 0;
|
|
3189
|
+
}
|
|
3190
|
+
return command;
|
|
3191
|
+
}
|
|
3192
|
+
function pathOf(command) {
|
|
3193
|
+
const words = [];
|
|
3194
|
+
for (let c = command; c?.parent; c = c.parent) words.unshift(c.name);
|
|
3195
|
+
return words;
|
|
3196
|
+
}
|
|
2957
3197
|
function writeVersion() {
|
|
2958
3198
|
context.writeStdout(`${s.version || "not versioned"}\n`);
|
|
2959
3199
|
}
|
|
@@ -3018,7 +3258,7 @@ function adjustCommand(base, command) {
|
|
|
3018
3258
|
return command;
|
|
3019
3259
|
}
|
|
3020
3260
|
//#endregion
|
|
3021
|
-
//#region ../../node_modules/.pnpm/clibuilder@12.
|
|
3261
|
+
//#region ../../node_modules/.pnpm/clibuilder@12.3.0_js-yaml@4.3.2/node_modules/clibuilder/esm/drivers/logger.js
|
|
3022
3262
|
function createBuilderUI(ui) {
|
|
3023
3263
|
let pending = true;
|
|
3024
3264
|
const entries = [];
|
|
@@ -3042,7 +3282,7 @@ function createBuilderUI(ui) {
|
|
|
3042
3282
|
};
|
|
3043
3283
|
}
|
|
3044
3284
|
//#endregion
|
|
3045
|
-
//#region ../../node_modules/.pnpm/clibuilder@12.
|
|
3285
|
+
//#region ../../node_modules/.pnpm/clibuilder@12.3.0_js-yaml@4.3.2/node_modules/clibuilder/esm/drivers/stream_ui.js
|
|
3046
3286
|
/**
|
|
3047
3287
|
* Which stream each `ui` method writes to, per output mode. Internal: a cli picks a preset
|
|
3048
3288
|
* with `cli({ output })`, never a table (axi-native RFC section 3.10).
|
|
@@ -3119,7 +3359,7 @@ function formatLine(args, colors) {
|
|
|
3119
3359
|
return args.map((v) => typeof v === "object" && v !== null ? inspect(v, { colors }) : v).join(" ");
|
|
3120
3360
|
}
|
|
3121
3361
|
//#endregion
|
|
3122
|
-
//#region ../../node_modules/.pnpm/clibuilder@12.
|
|
3362
|
+
//#region ../../node_modules/.pnpm/clibuilder@12.3.0_js-yaml@4.3.2/node_modules/clibuilder/esm/drivers/streams.js
|
|
3123
3363
|
/**
|
|
3124
3364
|
* The stdout and stderr driver: where every byte the cli writes goes through.
|
|
3125
3365
|
*
|
|
@@ -3173,7 +3413,7 @@ function handleEpipe(stream) {
|
|
|
3173
3413
|
});
|
|
3174
3414
|
}
|
|
3175
3415
|
//#endregion
|
|
3176
|
-
//#region ../../node_modules/.pnpm/clibuilder@12.
|
|
3416
|
+
//#region ../../node_modules/.pnpm/clibuilder@12.3.0_js-yaml@4.3.2/node_modules/clibuilder/esm/drivers/context.js
|
|
3177
3417
|
/** Creates the app context: the built-in ui writing to `streams` as `output` routes it. */
|
|
3178
3418
|
function context(options = {}) {
|
|
3179
3419
|
const cwd = process.cwd();
|
|
@@ -3197,7 +3437,7 @@ function context(options = {}) {
|
|
|
3197
3437
|
};
|
|
3198
3438
|
}
|
|
3199
3439
|
//#endregion
|
|
3200
|
-
//#region ../../node_modules/.pnpm/clibuilder@12.
|
|
3440
|
+
//#region ../../node_modules/.pnpm/clibuilder@12.3.0_js-yaml@4.3.2/node_modules/clibuilder/esm/cli.js
|
|
3201
3441
|
function cli(options) {
|
|
3202
3442
|
return builder(context({
|
|
3203
3443
|
streams: options.streams,
|
|
@@ -3205,37 +3445,76 @@ function cli(options) {
|
|
|
3205
3445
|
}), options);
|
|
3206
3446
|
}
|
|
3207
3447
|
//#endregion
|
|
3208
|
-
//#region
|
|
3448
|
+
//#region ../../node_modules/.pnpm/clibuilder@12.3.0_js-yaml@4.3.2/node_modules/clibuilder/esm/axi/home.js
|
|
3449
|
+
/** Shortens a path under the home directory to `~/...`: fewer tokens, and no user name in the output. */
|
|
3450
|
+
function collapseHome$2(path, home = homedir()) {
|
|
3451
|
+
const rest = path.slice(home.length);
|
|
3452
|
+
if (!home || !path.startsWith(home) || rest !== "" && rest[0] !== "/" && rest[0] !== "\\") return path;
|
|
3453
|
+
return `~${rest}`;
|
|
3454
|
+
}
|
|
3209
3455
|
/**
|
|
3210
|
-
*
|
|
3211
|
-
*
|
|
3456
|
+
* The first lines of a CLI's home view — what it prints when run with no arguments — so an agent
|
|
3457
|
+
* knows which tool answered: `bin: ~/.local/bin/my-cli` and `description: ...`.
|
|
3458
|
+
*
|
|
3459
|
+
* Spread it in front of the live content the home view shows:
|
|
3460
|
+
*
|
|
3461
|
+
* @example
|
|
3462
|
+
* ```ts
|
|
3463
|
+
* return result({ ...homeHeader({ description: 'Manage plugins' }), plugins })
|
|
3464
|
+
* ```
|
|
3212
3465
|
*/
|
|
3213
|
-
function
|
|
3214
|
-
|
|
3215
|
-
|
|
3216
|
-
|
|
3466
|
+
function homeHeader(options) {
|
|
3467
|
+
return {
|
|
3468
|
+
bin: collapseHome$2(options.bin ?? process.argv[1] ?? "", options.home ?? homedir()),
|
|
3469
|
+
description: options.description
|
|
3470
|
+
};
|
|
3217
3471
|
}
|
|
3472
|
+
//#endregion
|
|
3473
|
+
//#region ../../node_modules/.pnpm/clibuilder@12.3.0_js-yaml@4.3.2/node_modules/clibuilder/esm/axi/truncate.js
|
|
3218
3474
|
/**
|
|
3219
|
-
*
|
|
3220
|
-
*
|
|
3475
|
+
* Keeps the first `limit` items and counts the rest, so the reader knows whether it saw everything
|
|
3476
|
+
* without another call.
|
|
3477
|
+
*
|
|
3478
|
+
* `count` reads `30 of 847 total`. The kept list's own length (`items[30]` in TOON) stays the
|
|
3479
|
+
* number of rows shown, so a header never claims rows that are not there.
|
|
3221
3480
|
*/
|
|
3222
|
-
function
|
|
3223
|
-
const
|
|
3224
|
-
|
|
3225
|
-
|
|
3481
|
+
function truncateList(items, options) {
|
|
3482
|
+
const total = items.length;
|
|
3483
|
+
const kept = options.full ? [...items] : items.slice(0, options.limit);
|
|
3484
|
+
return {
|
|
3485
|
+
items: kept,
|
|
3486
|
+
count: `${kept.length} of ${total} total`,
|
|
3487
|
+
total,
|
|
3488
|
+
truncated: kept.length < total
|
|
3489
|
+
};
|
|
3226
3490
|
}
|
|
3491
|
+
//#endregion
|
|
3492
|
+
//#region ../../node_modules/.pnpm/clibuilder@12.3.0_js-yaml@4.3.2/node_modules/clibuilder/esm/axi.js
|
|
3227
3493
|
/**
|
|
3228
|
-
*
|
|
3229
|
-
*
|
|
3494
|
+
* `clibuilder/axi`: helpers that shape the value a command returns for an agent.
|
|
3495
|
+
*
|
|
3496
|
+
* Core already renders errors, usage errors and results the AXI way.
|
|
3497
|
+
* This subpath adds truncation, counts, the empty state and the home header,
|
|
3498
|
+
* plus the result channel and exit codes so one import covers a command.
|
|
3499
|
+
*
|
|
3500
|
+
* It also works without `cli()`: `encode`, `errorOutput` and `printAnswer` are the output contract,
|
|
3501
|
+
* and `runScript` runs a command headless, for a script that is not a cli.
|
|
3230
3502
|
*/
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
}
|
|
3234
|
-
|
|
3235
|
-
|
|
3236
|
-
|
|
3503
|
+
/**
|
|
3504
|
+
* The `--full` option: the escape hatch from truncation. Add it to a command's options, and pass
|
|
3505
|
+
* `args.full` to {@link truncateText} and {@link truncateList}.
|
|
3506
|
+
*
|
|
3507
|
+
* @example
|
|
3508
|
+
* ```ts
|
|
3509
|
+
* options: { full: fullOption }
|
|
3510
|
+
* ```
|
|
3511
|
+
*/
|
|
3512
|
+
const fullOption = {
|
|
3513
|
+
type: t.boolean().optional(),
|
|
3514
|
+
description: "Show the complete output instead of truncating it"
|
|
3515
|
+
};
|
|
3237
3516
|
//#endregion
|
|
3238
|
-
//#region ../../node_modules/.pnpm/cyber-agentkit@2.
|
|
3517
|
+
//#region ../../node_modules/.pnpm/cyber-agentkit@2.2.0_clibuilder@12.3.0_js-yaml@4.3.2_/node_modules/cyber-agentkit/dist/reference-where-C93SAYP9.js
|
|
3239
3518
|
var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
|
|
3240
3519
|
var __require = /* #__PURE__ */ (() => createRequire(import.meta.url))();
|
|
3241
3520
|
/** Read a file, or `undefined` when it does not exist. Other read errors are thrown. */
|
|
@@ -11611,7 +11890,15 @@ const referenceCodes = {
|
|
|
11611
11890
|
},
|
|
11612
11891
|
"reference-renamed": {
|
|
11613
11892
|
kind: "warning",
|
|
11614
|
-
when: "a name or file under an older name,
|
|
11893
|
+
when: "a name or file under an older name, an interface's older id, or a name a plugin's rename table renames (R96, R97, R120)"
|
|
11894
|
+
},
|
|
11895
|
+
"rename-invalid": {
|
|
11896
|
+
kind: "warning",
|
|
11897
|
+
when: "a plugin's rename table, or an entry of it, that is not a pair of bare reference names (R120)"
|
|
11898
|
+
},
|
|
11899
|
+
"reference-legacy-format": {
|
|
11900
|
+
kind: "warning",
|
|
11901
|
+
when: "a contribution in a form version 1 wrote and 2.0 retired, which `reference migrate` rewrites (R119)"
|
|
11615
11902
|
},
|
|
11616
11903
|
"alias-not-used": {
|
|
11617
11904
|
kind: "warning",
|
|
@@ -11659,7 +11946,7 @@ const referenceCodes = {
|
|
|
11659
11946
|
},
|
|
11660
11947
|
"interface-unknown": {
|
|
11661
11948
|
kind: "refused",
|
|
11662
|
-
when: "`implementations` of an unknown interface (R35)"
|
|
11949
|
+
when: "`implementations` of an unknown interface (R35), or of a family matching none; `customize` of a family matching none (R135)"
|
|
11663
11950
|
},
|
|
11664
11951
|
"template-title": {
|
|
11665
11952
|
kind: "refused",
|
|
@@ -11719,7 +12006,7 @@ const referenceCodes = {
|
|
|
11719
12006
|
},
|
|
11720
12007
|
"name-invalid": {
|
|
11721
12008
|
kind: "usage",
|
|
11722
|
-
when: "a write given a string that is not a name, such as `../x` (J1, R1)"
|
|
12009
|
+
when: "a write given a string that is not a name, such as `../x` (J1, R1); `*` anywhere but the last segment of a family (R136)"
|
|
11723
12010
|
},
|
|
11724
12011
|
"reference-qualified": {
|
|
11725
12012
|
kind: "usage",
|
|
@@ -12042,6 +12329,19 @@ const namePattern$1 = /^[a-z0-9]+(?:[-.][a-z0-9]+)*$/i;
|
|
|
12042
12329
|
const defaultSuffix = ".default";
|
|
12043
12330
|
/** The suffix a metadata file's name carries before `.md` (K31); a name ending in it is reserved for that file. */
|
|
12044
12331
|
const metadataSuffix = ".meta";
|
|
12332
|
+
/** Whether a value is written as a family: it ends in `.*`. Its prefix may still be invalid (R136). */
|
|
12333
|
+
function isFamilyName(value) {
|
|
12334
|
+
return value.endsWith(".*");
|
|
12335
|
+
}
|
|
12336
|
+
/**
|
|
12337
|
+
* The prefix of a family (K39), or why the value is not one. `*` anywhere but the last segment, a
|
|
12338
|
+
* qualified or file-like prefix, or an empty prefix makes the name invalid (R136).
|
|
12339
|
+
*/
|
|
12340
|
+
function parseFamily(value) {
|
|
12341
|
+
const prefix = value.slice(0, -2);
|
|
12342
|
+
if (!isFamilyName(value) || !prefix || prefix.includes("*") || !namePattern$1.test(prefix)) return { invalid: `"${value}" is not a family. A family is \`<prefix>.*\`, with \`*\` only as the last segment and a prefix of letters, digits, hyphens, and dots.` };
|
|
12343
|
+
return { prefix };
|
|
12344
|
+
}
|
|
12045
12345
|
/** No name at all is a usage error (`name-missing`), unlike a string that is not a name, which is `invalid`. */
|
|
12046
12346
|
function requireName(value) {
|
|
12047
12347
|
if (!value.trim()) throw new ReferenceFailure("name-missing", "name a reference.", { help: "Run `reference list` for every name." });
|
|
@@ -12060,6 +12360,7 @@ function parseReferenceName(value) {
|
|
|
12060
12360
|
if (value.endsWith(".md")) return invalid(`"${value}" names a file. Ask for the reference by name, without the .md extension.`);
|
|
12061
12361
|
const extension = dataExtensions.find((item) => value.endsWith(item.extension));
|
|
12062
12362
|
if (extension) return invalid(`"${value}" names a file. Ask for the reference by name, without the ${extension.extension} extension.`);
|
|
12363
|
+
if (value.includes("*")) return invalid(`"${value}" is not a reference name: \`*\` makes a family, \`<prefix>.*\`, which only customize and implementations take.`);
|
|
12063
12364
|
const parts = value.split("/");
|
|
12064
12365
|
if (parts.length > 2 || value.includes("\\")) return invalid(`"${value}" is a path. A name is letters, digits, hyphens, and dots, optionally after "<plugin>/".`);
|
|
12065
12366
|
const [first, second] = parts;
|
|
@@ -12112,11 +12413,12 @@ function parseDocument(raw, warn) {
|
|
|
12112
12413
|
body
|
|
12113
12414
|
};
|
|
12114
12415
|
}
|
|
12115
|
-
/** The contribution's own `merge`; `undefined` when it writes none. An unknown value reads as `patch`. */
|
|
12416
|
+
/** The contribution's own `merge`; `undefined` when it writes none. An unknown or version 1 value reads as `patch`. */
|
|
12116
12417
|
function readMergeOperation(metadata, warn) {
|
|
12117
12418
|
const value = metadata["merge"];
|
|
12118
12419
|
if (value === void 0) return void 0;
|
|
12119
12420
|
if (isMergeOperation(value)) return value;
|
|
12421
|
+
if (value === "merge-sections" || value === "first-wins") return "patch";
|
|
12120
12422
|
warn("merge-unknown", `unknown merge "${String(value)}"; read as patch`);
|
|
12121
12423
|
return "patch";
|
|
12122
12424
|
}
|
|
@@ -12473,6 +12775,184 @@ function pickSections(body, picks) {
|
|
|
12473
12775
|
missing
|
|
12474
12776
|
};
|
|
12475
12777
|
}
|
|
12778
|
+
const legacyMerges = {
|
|
12779
|
+
"merge-sections": void 0,
|
|
12780
|
+
"first-wins": "replace"
|
|
12781
|
+
};
|
|
12782
|
+
function isLegacyMerge(value) {
|
|
12783
|
+
return value === "merge-sections" || value === "first-wins";
|
|
12784
|
+
}
|
|
12785
|
+
/** The plugins `overrides` names, each qualified entry reduced to its plugin; `undefined` when it is not a list of them. */
|
|
12786
|
+
function overriddenPlugins(value) {
|
|
12787
|
+
const list = typeof value === "string" ? [value] : Array.isArray(value) ? value : void 0;
|
|
12788
|
+
if (!list?.length || !list.every((item) => typeof item === "string" && item.trim())) return void 0;
|
|
12789
|
+
return [...new Set(list.map((item) => item.split("/")[0].trim()))];
|
|
12790
|
+
}
|
|
12791
|
+
/** The plugin or `default` each `extends` step names, as written; `undefined` when it is not a list of steps. */
|
|
12792
|
+
function stepTargets(value) {
|
|
12793
|
+
const list = typeof value === "string" ? [value] : Array.isArray(value) ? value : void 0;
|
|
12794
|
+
if (!list) return void 0;
|
|
12795
|
+
const targets = [];
|
|
12796
|
+
for (const item of list) if (typeof item === "string") targets.push(item);
|
|
12797
|
+
else if (isRecord$1(item) && Object.keys(item).length === 1) targets.push(Object.keys(item)[0]);
|
|
12798
|
+
else return void 0;
|
|
12799
|
+
return targets;
|
|
12800
|
+
}
|
|
12801
|
+
const flow = (items) => `[${items.join(", ")}]`;
|
|
12802
|
+
/**
|
|
12803
|
+
* The legacy forms a Markdown contribution's frontmatter holds (K37), and the frontmatter that says
|
|
12804
|
+
* the same in 2.0 (R122). The body and every other key are left as written.
|
|
12805
|
+
*/
|
|
12806
|
+
function legacyRewrite(metadata, body, place, name) {
|
|
12807
|
+
const findings = [];
|
|
12808
|
+
const keys = {};
|
|
12809
|
+
const merge = metadata["merge"];
|
|
12810
|
+
if (isLegacyMerge(merge)) {
|
|
12811
|
+
const into = legacyMerges[merge];
|
|
12812
|
+
keys["merge"] = place === "project" ? void 0 : into;
|
|
12813
|
+
findings.push({
|
|
12814
|
+
form: merge,
|
|
12815
|
+
from: `merge: ${merge}`,
|
|
12816
|
+
to: place === "project" ? "no merge: a project section carries none" : into === void 0 ? "no merge: patch is the default" : `merge: ${into}`,
|
|
12817
|
+
reads: "it reads as patch"
|
|
12818
|
+
});
|
|
12819
|
+
}
|
|
12820
|
+
if ("overrides" in metadata) findings.push(overridesFinding(metadata, place, keys));
|
|
12821
|
+
if (place === "project" && metadata["extends"] !== void 0 && !hasBody(body)) {
|
|
12822
|
+
const steps = stepTargets(metadata["extends"]);
|
|
12823
|
+
findings.push({
|
|
12824
|
+
form: "project-selection",
|
|
12825
|
+
from: `extends: ${steps ? flow(steps) : JSON.stringify(metadata["extends"])}`,
|
|
12826
|
+
manual: `a pick now applies to the whole repository: run \`reference customize ${name} --extends ${steps?.join(" ") ?? "<step>..."}\` at the root, then delete this file`,
|
|
12827
|
+
reads: "its extends is ignored"
|
|
12828
|
+
});
|
|
12829
|
+
}
|
|
12830
|
+
return {
|
|
12831
|
+
findings,
|
|
12832
|
+
keys
|
|
12833
|
+
};
|
|
12834
|
+
}
|
|
12835
|
+
function overridesFinding(metadata, place, keys) {
|
|
12836
|
+
const value = metadata["overrides"];
|
|
12837
|
+
const plugins = overriddenPlugins(value);
|
|
12838
|
+
const from = `overrides: ${plugins ? flow(Array.isArray(value) ? value : [String(value)]) : JSON.stringify(value)}`;
|
|
12839
|
+
const reads = "it is ignored";
|
|
12840
|
+
if (place === "project") {
|
|
12841
|
+
keys["overrides"] = void 0;
|
|
12842
|
+
return {
|
|
12843
|
+
form: "overrides",
|
|
12844
|
+
from,
|
|
12845
|
+
to: "removed: a project section carries no extends",
|
|
12846
|
+
reads
|
|
12847
|
+
};
|
|
12848
|
+
}
|
|
12849
|
+
if (!plugins) return {
|
|
12850
|
+
form: "overrides",
|
|
12851
|
+
from,
|
|
12852
|
+
manual: "`overrides` is not a list of plugin names",
|
|
12853
|
+
reads
|
|
12854
|
+
};
|
|
12855
|
+
const written = metadata["extends"];
|
|
12856
|
+
if (written === void 0) {
|
|
12857
|
+
keys["overrides"] = void 0;
|
|
12858
|
+
keys["extends"] = plugins;
|
|
12859
|
+
return {
|
|
12860
|
+
form: "overrides",
|
|
12861
|
+
from,
|
|
12862
|
+
to: `extends: ${flow(plugins)}`,
|
|
12863
|
+
reads
|
|
12864
|
+
};
|
|
12865
|
+
}
|
|
12866
|
+
const targets = stepTargets(written);
|
|
12867
|
+
const missing = plugins.filter((plugin) => !targets?.includes(plugin));
|
|
12868
|
+
if (targets && !missing.length) {
|
|
12869
|
+
keys["overrides"] = void 0;
|
|
12870
|
+
return {
|
|
12871
|
+
form: "overrides",
|
|
12872
|
+
from,
|
|
12873
|
+
to: "removed: extends already names each plugin",
|
|
12874
|
+
reads
|
|
12875
|
+
};
|
|
12876
|
+
}
|
|
12877
|
+
return {
|
|
12878
|
+
form: "overrides",
|
|
12879
|
+
from,
|
|
12880
|
+
manual: `\`extends\` is also written and does not name ${missing.join(", ")}; add ${missing.length > 1 ? "them" : "it"} to \`extends\` where it belongs, then remove \`overrides\``,
|
|
12881
|
+
reads
|
|
12882
|
+
};
|
|
12883
|
+
}
|
|
12884
|
+
/** The 2.0 form of a skill's default in the old layout (K37). */
|
|
12885
|
+
function defaultLayoutFinding(name) {
|
|
12886
|
+
return {
|
|
12887
|
+
form: "default-layout",
|
|
12888
|
+
from: `references/${name}.md`,
|
|
12889
|
+
to: `references/${name}.default.md`,
|
|
12890
|
+
reads: "it is not read as the default"
|
|
12891
|
+
};
|
|
12892
|
+
}
|
|
12893
|
+
/** The warning a legacy form raises at load (R119): the form, how it reads, its 2.0 form, and the fix. */
|
|
12894
|
+
function warnLegacy(finding, fix, warn) {
|
|
12895
|
+
const note = finding.note === void 0 ? "" : `, and ${finding.note}`;
|
|
12896
|
+
const into = finding.to === void 0 ? "" : `; its 2.0 form is ${finding.to}${note}`;
|
|
12897
|
+
const how = finding.manual === void 0 ? fix : `To fix it, ${finding.manual}${finding.note === void 0 ? "" : `; ${finding.note}`}.`;
|
|
12898
|
+
warn("reference-legacy-format", `\`${finding.from}\` is a version 1 form, and ${finding.reads}${into}. ${how}`);
|
|
12899
|
+
}
|
|
12900
|
+
function directoryEntries(dir) {
|
|
12901
|
+
try {
|
|
12902
|
+
return readdirSync(dir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
|
|
12903
|
+
} catch {
|
|
12904
|
+
return [];
|
|
12905
|
+
}
|
|
12906
|
+
}
|
|
12907
|
+
/**
|
|
12908
|
+
* The qualified-name overrides a tier or project folder holds, of `name` when given. A subfolder
|
|
12909
|
+
* holding `README.md` is a reference in folder form, not a plugin's, so its files are left alone.
|
|
12910
|
+
*/
|
|
12911
|
+
function qualifiedOverrides(dir, name) {
|
|
12912
|
+
const found = [];
|
|
12913
|
+
for (const folder of directoryEntries(dir)) {
|
|
12914
|
+
const plugin = folder.name;
|
|
12915
|
+
if (!folder.isDirectory() || !namePattern$1.test(plugin)) continue;
|
|
12916
|
+
const files = directoryEntries(join(dir, plugin));
|
|
12917
|
+
if (files.some((file) => file.name === "README.md")) continue;
|
|
12918
|
+
for (const file of files) {
|
|
12919
|
+
if (!file.isFile() || !file.name.endsWith(".md")) continue;
|
|
12920
|
+
const held = file.name.slice(0, -3);
|
|
12921
|
+
if (!namePattern$1.test(held) || held.endsWith(".default") || held.endsWith(".meta")) continue;
|
|
12922
|
+
if (name !== void 0 && held !== name) continue;
|
|
12923
|
+
found.push({
|
|
12924
|
+
plugin,
|
|
12925
|
+
name: held,
|
|
12926
|
+
path: join(dir, plugin, file.name),
|
|
12927
|
+
file: `${plugin}/${file.name}`
|
|
12928
|
+
});
|
|
12929
|
+
}
|
|
12930
|
+
}
|
|
12931
|
+
return found;
|
|
12932
|
+
}
|
|
12933
|
+
const neverApplied = "no released version read it, so the override never applied";
|
|
12934
|
+
const turnsOn = "applying the plan turns it on";
|
|
12935
|
+
/**
|
|
12936
|
+
* The 2.0 form of a qualified-name override (K37): `<name>.md` beside the subfolder, with
|
|
12937
|
+
* `extends: [<plugin>]`. A project section carries no `extends` (K24), so there it is manual.
|
|
12938
|
+
*/
|
|
12939
|
+
function qualifiedOverrideFinding(found, place) {
|
|
12940
|
+
const { plugin, name, file } = found;
|
|
12941
|
+
const said = {
|
|
12942
|
+
form: "qualified-override",
|
|
12943
|
+
from: file,
|
|
12944
|
+
reads: neverApplied,
|
|
12945
|
+
note: turnsOn
|
|
12946
|
+
};
|
|
12947
|
+
if (place === "project") return {
|
|
12948
|
+
...said,
|
|
12949
|
+
manual: `pick plugin ${plugin} for the whole repository with \`reference customize ${name} --extends ${plugin}\` at the root, since a project section carries no extends; then move this file's body into ${name}.md in this folder and delete this file`
|
|
12950
|
+
};
|
|
12951
|
+
return {
|
|
12952
|
+
...said,
|
|
12953
|
+
to: `${name}.md with extends: [${plugin}]`
|
|
12954
|
+
};
|
|
12955
|
+
}
|
|
12476
12956
|
/** Keys a metadata file cannot carry: a non-Markdown contribution always replaces below (R68), and an interface's sections are Markdown. */
|
|
12477
12957
|
const ignoredKeys$1 = ["merge", "implements"];
|
|
12478
12958
|
/** The metadata file beside a contribution of `name` in a layer: `<name>.meta.md`, or a default's `<name>.default.meta.md`. */
|
|
@@ -12822,6 +13302,11 @@ function readContribution(layer, name, entry, { display, without, warnAt, older
|
|
|
12822
13302
|
const stray = join(layer.dir, `${name}${defaultSuffix}.md`);
|
|
12823
13303
|
if (stray !== without && tryRead$2(stray) !== void 0) warnAt(display(stray))("default-in-tier", "it is ignored: a default goes in a skill's references/ folder");
|
|
12824
13304
|
}
|
|
13305
|
+
if (layer.tier === "repo" || layer.tier === "user") warnQualifiedOverrides$1(layer, name, {
|
|
13306
|
+
display,
|
|
13307
|
+
without,
|
|
13308
|
+
warnAt
|
|
13309
|
+
});
|
|
12825
13310
|
const owner = layer.tier === "plugin" || layer.tier === "default";
|
|
12826
13311
|
const [hit, ...ignored] = [name, ...owner ? [] : older].flatMap((as) => candidateFiles(layer, as).flatMap((file) => {
|
|
12827
13312
|
const path = join(layer.dir, file);
|
|
@@ -12849,6 +13334,11 @@ function readContribution(layer, name, entry, { display, without, warnAt, older
|
|
|
12849
13334
|
break;
|
|
12850
13335
|
}
|
|
12851
13336
|
if (hit && hit.as !== name) renamed(entry, hit.as, name, hit.path, display, warnAt);
|
|
13337
|
+
if (!hit && !sidecar && layer.tier === "default") warnDefaultLayout(layer, name, {
|
|
13338
|
+
display,
|
|
13339
|
+
without,
|
|
13340
|
+
warnAt
|
|
13341
|
+
});
|
|
12852
13342
|
if (!hit) return sidecar ? sidecarSelection(layer, entry, sidecar, display, warnAt) : void 0;
|
|
12853
13343
|
const label = contributionLabel(layer, hit.path, display);
|
|
12854
13344
|
const warn = warnAt(label);
|
|
@@ -12878,6 +13368,8 @@ function readContribution(layer, name, entry, { display, without, warnAt, older
|
|
|
12878
13368
|
};
|
|
12879
13369
|
}
|
|
12880
13370
|
const { metadata, body } = parseDocument(hit.raw, warn);
|
|
13371
|
+
const fix = legacyFix(layer, name, display);
|
|
13372
|
+
for (const finding of legacyRewrite(metadata, body, "contribution", name).findings) warnLegacy(finding, fix, warn);
|
|
12881
13373
|
const merge = readMergeOperation(metadata, warn);
|
|
12882
13374
|
const steps = readExtends(metadata["extends"], warn);
|
|
12883
13375
|
Object.assign(entry, {
|
|
@@ -12904,6 +13396,26 @@ function readContribution(layer, name, entry, { display, without, warnAt, older
|
|
|
12904
13396
|
steps
|
|
12905
13397
|
};
|
|
12906
13398
|
}
|
|
13399
|
+
/** Who rewrites a legacy form, and how (R119): only a file `migrate` may change gets the command itself. */
|
|
13400
|
+
function legacyFix(layer, name, display) {
|
|
13401
|
+
if (layer.tier === "plugin") return `Plugin ${layer.plugin}'s author rewrites it with \`reference migrate --plugin <its source folder>\`; until then, customize the name.`;
|
|
13402
|
+
if (layer.tier === "default") return `Its skill's author rewrites it with \`reference migrate ${name} --default ${display(dirname(layer.dir))}\`.`;
|
|
13403
|
+
if (layer.tier === "managed") return "An admin rewrites it; `reference migrate` never changes a managed file.";
|
|
13404
|
+
return "Run `reference migrate` to rewrite it.";
|
|
13405
|
+
}
|
|
13406
|
+
/** A qualified-name override of the name in a tier folder is not read; each warns (R119). */
|
|
13407
|
+
function warnQualifiedOverrides$1(layer, name, { display, without, warnAt }) {
|
|
13408
|
+
for (const found of qualifiedOverrides(layer.dir, name)) {
|
|
13409
|
+
if (found.path === without) continue;
|
|
13410
|
+
warnLegacy(qualifiedOverrideFinding(found, "contribution"), legacyFix(layer, name, display), warnAt(contributionLabel(layer, found.path, display)));
|
|
13411
|
+
}
|
|
13412
|
+
}
|
|
13413
|
+
/** A skill's default in the old layout, `references/<name>.md`, is not read; it warns when the skill's folder has no default of the name (R119). */
|
|
13414
|
+
function warnDefaultLayout(layer, name, { display, without, warnAt }) {
|
|
13415
|
+
const path = join(layer.dir, `${name}.md`);
|
|
13416
|
+
if (path === without || tryRead$2(path) === void 0) return;
|
|
13417
|
+
warnLegacy(defaultLayoutFinding(name), legacyFix(layer, name, display), warnAt(display(path)));
|
|
13418
|
+
}
|
|
12907
13419
|
/** A file filed under an older name applies as the new one, with a warning naming the file to rename (R97). */
|
|
12908
13420
|
function renamed(entry, as, name, path, display, warnAt) {
|
|
12909
13421
|
entry.alias = as;
|
|
@@ -13147,7 +13659,7 @@ function checkImplementation(iface, { name, version, content, accept = iface.ver
|
|
|
13147
13659
|
unsupported
|
|
13148
13660
|
};
|
|
13149
13661
|
}
|
|
13150
|
-
const cache = /* @__PURE__ */ new WeakMap();
|
|
13662
|
+
const cache$1 = /* @__PURE__ */ new WeakMap();
|
|
13151
13663
|
const quiet = () => () => {};
|
|
13152
13664
|
function ownerLabel(owner) {
|
|
13153
13665
|
return owner.tier === "plugin" ? `plugin ${owner.plugin}` : `default of ${owner.skill}`;
|
|
@@ -13169,7 +13681,7 @@ function listed(value) {
|
|
|
13169
13681
|
*/
|
|
13170
13682
|
function aliasIndex(source) {
|
|
13171
13683
|
const keyed = Array.isArray(source) ? void 0 : source;
|
|
13172
|
-
const hit = keyed && cache.get(keyed);
|
|
13684
|
+
const hit = keyed && cache$1.get(keyed);
|
|
13173
13685
|
if (hit) return hit;
|
|
13174
13686
|
const layers = Array.isArray(source) ? source : source.layers;
|
|
13175
13687
|
const byAlias = /* @__PURE__ */ new Map();
|
|
@@ -13209,16 +13721,15 @@ function aliasIndex(source) {
|
|
|
13209
13721
|
});
|
|
13210
13722
|
continue;
|
|
13211
13723
|
}
|
|
13212
|
-
if (id !== void 0) {
|
|
13213
|
-
|
|
13214
|
-
|
|
13215
|
-
|
|
13216
|
-
|
|
13217
|
-
|
|
13724
|
+
if (id !== void 0) push(interfaces, id, {
|
|
13725
|
+
alias,
|
|
13726
|
+
owner
|
|
13727
|
+
});
|
|
13728
|
+
for (const older of id === void 0 ? [alias] : documentNames$1(alias, read.metadata)) {
|
|
13729
|
+
push(byAlias, older, owner);
|
|
13730
|
+
const known = byName.get(name) ?? [];
|
|
13731
|
+
if (!known.includes(older)) byName.set(name, [...known, older]);
|
|
13218
13732
|
}
|
|
13219
|
-
push(byAlias, alias, owner);
|
|
13220
|
-
const older = byName.get(name) ?? [];
|
|
13221
|
-
if (!older.includes(alias)) byName.set(name, [...older, alias]);
|
|
13222
13733
|
}
|
|
13223
13734
|
}
|
|
13224
13735
|
}
|
|
@@ -13229,9 +13740,18 @@ function aliasIndex(source) {
|
|
|
13229
13740
|
warnings,
|
|
13230
13741
|
interfaces
|
|
13231
13742
|
};
|
|
13232
|
-
if (keyed) cache.set(keyed, index);
|
|
13743
|
+
if (keyed) cache$1.set(keyed, index);
|
|
13233
13744
|
return index;
|
|
13234
13745
|
}
|
|
13746
|
+
/**
|
|
13747
|
+
* The older names of an interface document that lists `alias` (R128): `<old id>.interface` always,
|
|
13748
|
+
* and `<old id>` itself when the interface is `many`, as no implementation of one is named `<old id>`.
|
|
13749
|
+
* For a `one` interface, `<old id>` is the implementation's older name (R96).
|
|
13750
|
+
*/
|
|
13751
|
+
function documentNames$1(alias, metadata) {
|
|
13752
|
+
const block = metadata["interface"];
|
|
13753
|
+
return isRecord$1(block) && block["cardinality"] === "many" ? [alias, `${alias}${interfaceSuffix}`] : [`${alias}${interfaceSuffix}`];
|
|
13754
|
+
}
|
|
13235
13755
|
function aliasFile(read) {
|
|
13236
13756
|
return read.entry.frontmatter && read.whole ? read.path.replace(/\.[^./]+$/, ".meta.md") : read.path;
|
|
13237
13757
|
}
|
|
@@ -13476,7 +13996,7 @@ function ignoredFolderOf(root, entry) {
|
|
|
13476
13996
|
const folder = segments.join("/");
|
|
13477
13997
|
return folder && isDirectory$3(join(root, ...segments, ...referencesPath)) ? folder : void 0;
|
|
13478
13998
|
}
|
|
13479
|
-
function entries(dir) {
|
|
13999
|
+
function entries$1(dir) {
|
|
13480
14000
|
try {
|
|
13481
14001
|
return readdirSync(dir, { withFileTypes: true });
|
|
13482
14002
|
} catch {
|
|
@@ -13493,7 +14013,7 @@ function walk(root) {
|
|
|
13493
14013
|
const found = [];
|
|
13494
14014
|
const packages = [];
|
|
13495
14015
|
const visit = (relative) => {
|
|
13496
|
-
const listing = entries(join(root, relative));
|
|
14016
|
+
const listing = entries$1(join(root, relative));
|
|
13497
14017
|
if (relative && !relative.split("/").includes("node_modules")) {
|
|
13498
14018
|
if (listing.some((entry) => entry.isFile() && entry.name === "package.json")) packages.push(relative);
|
|
13499
14019
|
}
|
|
@@ -13504,8 +14024,8 @@ function walk(root) {
|
|
|
13504
14024
|
visit(child);
|
|
13505
14025
|
continue;
|
|
13506
14026
|
}
|
|
13507
|
-
if (relative && entries(join(root, child, referencesPath[1])).length) found.push(relative);
|
|
13508
|
-
for (const inner of entries(join(root, child))) if (inner.isDirectory() && inner.name !== referencesPath[1]) visit(`${child}/${inner.name}`);
|
|
14027
|
+
if (relative && entries$1(join(root, child, referencesPath[1])).length) found.push(relative);
|
|
14028
|
+
for (const inner of entries$1(join(root, child))) if (inner.isDirectory() && inner.name !== referencesPath[1]) visit(`${child}/${inner.name}`);
|
|
13509
14029
|
}
|
|
13510
14030
|
};
|
|
13511
14031
|
visit("");
|
|
@@ -13978,10 +14498,26 @@ function readPluginSection(name, found, read, result) {
|
|
|
13978
14498
|
plugin: plugin.plugin
|
|
13979
14499
|
});
|
|
13980
14500
|
}
|
|
14501
|
+
/** A qualified-name override of the name in a project folder is not read; each warns (R119). */
|
|
14502
|
+
function warnQualifiedOverrides(name, folder, read) {
|
|
14503
|
+
const { display, without, warnAt, listed } = read;
|
|
14504
|
+
for (const found of qualifiedOverrides(folder.dir, name)) {
|
|
14505
|
+
const hit = {
|
|
14506
|
+
folder,
|
|
14507
|
+
file: found.file,
|
|
14508
|
+
as: name,
|
|
14509
|
+
path: found.path,
|
|
14510
|
+
relative: `${folder.folder}/.agents/references/${found.file}`
|
|
14511
|
+
};
|
|
14512
|
+
if (found.path === without || !listed(hit)) continue;
|
|
14513
|
+
warnLegacy(qualifiedOverrideFinding(found, "project"), "", warnAt(`project ${display(found.path)}`));
|
|
14514
|
+
}
|
|
14515
|
+
}
|
|
13981
14516
|
/** The folder's own `.agents/references/<name>.md` or `<name>/README.md`, when git does not ignore it. */
|
|
13982
14517
|
function readOwnSection(name, folder, ignored, read, result) {
|
|
13983
14518
|
const { trace, sections } = result;
|
|
13984
14519
|
const { format, display, without, warnAt, catalog, older, listed, dropping, managed } = read;
|
|
14520
|
+
if (!ignored) warnQualifiedOverrides(name, folder, read);
|
|
13985
14521
|
const filed = [name, ...older].map((as) => filesFor(folder, as, without));
|
|
13986
14522
|
const markdown = filed.flatMap((files) => files.markdown);
|
|
13987
14523
|
const data = filed.flatMap((files) => files.data);
|
|
@@ -14017,9 +14553,14 @@ function readOwnSection(name, folder, ignored, read, result) {
|
|
|
14017
14553
|
const { metadata, body } = parseDocument(tryRead$2(hit.path) ?? "", warnAt(label));
|
|
14018
14554
|
row.frontmatter = metadata;
|
|
14019
14555
|
const kept = { ...metadata };
|
|
14556
|
+
const legacy = legacyRewrite(metadata, body, "project", name).findings;
|
|
14557
|
+
for (const finding of legacy) warnLegacy(finding, "Run `reference migrate` to rewrite it.", warnAt(label));
|
|
14558
|
+
const warnedAsLegacy = new Set(legacy.flatMap(({ form }) => form === "project-selection" ? ["extends"] : form === "overrides" ? ["overrides"] : ["merge"]));
|
|
14559
|
+
delete kept["overrides"];
|
|
14020
14560
|
for (const key of ignoredKeys) {
|
|
14021
14561
|
if (!(key in kept)) continue;
|
|
14022
14562
|
delete kept[key];
|
|
14563
|
+
if (warnedAsLegacy.has(key)) continue;
|
|
14023
14564
|
warnAt(label)("project-section-key-ignored", `\`${key}\` is ignored in the project section for ${folder.folder}: a project section states only what differs in its folder`);
|
|
14024
14565
|
}
|
|
14025
14566
|
if (dropping) {
|
|
@@ -14102,6 +14643,88 @@ function projectSectionNames(root, cache, plugins = /* @__PURE__ */ new Set()) {
|
|
|
14102
14643
|
}
|
|
14103
14644
|
return [...held];
|
|
14104
14645
|
}
|
|
14646
|
+
/** Every name a qualified-name override (K37) in a project folder below the root names, in a file git does not ignore. */
|
|
14647
|
+
function projectQualifiedNames(root, cache) {
|
|
14648
|
+
const found = projectFolders(root, cache);
|
|
14649
|
+
return found.folders.flatMap((folder) => qualifiedOverrides(folder.dir).filter(({ file }) => !found.listed || found.listed.has(`${folder.folder}/.agents/references/${file}`)).map(({ name }) => name));
|
|
14650
|
+
}
|
|
14651
|
+
/**
|
|
14652
|
+
* A plugin's rename table, in its `references/` folder (K38). A leading `_` is not a reference name,
|
|
14653
|
+
* so the file never answers one.
|
|
14654
|
+
*/
|
|
14655
|
+
const renameTableFile = "_renames.yaml";
|
|
14656
|
+
const cache = /* @__PURE__ */ new WeakMap();
|
|
14657
|
+
function bare(value) {
|
|
14658
|
+
if (typeof value !== "string") return void 0;
|
|
14659
|
+
const parsed = parseReferenceName(value.trim());
|
|
14660
|
+
return parsed.invalid || parsed.plugin !== void 0 ? void 0 : parsed.name;
|
|
14661
|
+
}
|
|
14662
|
+
/**
|
|
14663
|
+
* Every rename table the plugin tier's plugins ship (R120, R124), read once per set of layers. Only
|
|
14664
|
+
* a plugin renames its names, so no other tier is read.
|
|
14665
|
+
*/
|
|
14666
|
+
function renameTable(source) {
|
|
14667
|
+
const keyed = Array.isArray(source) ? void 0 : source;
|
|
14668
|
+
const hit = keyed && cache.get(keyed);
|
|
14669
|
+
if (hit) return hit;
|
|
14670
|
+
const layers = Array.isArray(source) ? source : source.layers;
|
|
14671
|
+
const byOld = /* @__PURE__ */ new Map();
|
|
14672
|
+
const warnings = [];
|
|
14673
|
+
const seen = /* @__PURE__ */ new Set();
|
|
14674
|
+
for (const layer of layers) {
|
|
14675
|
+
if (layer.tier !== "plugin" || layer.skipped || layer.plugin === void 0) continue;
|
|
14676
|
+
const path = join(layer.dir, renameTableFile);
|
|
14677
|
+
if (seen.has(path)) continue;
|
|
14678
|
+
seen.add(path);
|
|
14679
|
+
const raw = tryRead$2(path);
|
|
14680
|
+
if (raw === void 0) continue;
|
|
14681
|
+
const plugin = layer.plugin;
|
|
14682
|
+
let value;
|
|
14683
|
+
try {
|
|
14684
|
+
value = (0, import_dist.parse)(raw);
|
|
14685
|
+
} catch {
|
|
14686
|
+
value = void 0;
|
|
14687
|
+
}
|
|
14688
|
+
if (value === null || value === void 0) {
|
|
14689
|
+
if (raw.trim()) warnings.push({
|
|
14690
|
+
code: "rename-invalid",
|
|
14691
|
+
message: `plugin ${plugin}: ${path} is not YAML; it is ignored`
|
|
14692
|
+
});
|
|
14693
|
+
continue;
|
|
14694
|
+
}
|
|
14695
|
+
if (!isRecord$1(value)) {
|
|
14696
|
+
warnings.push({
|
|
14697
|
+
code: "rename-invalid",
|
|
14698
|
+
message: `plugin ${plugin}: ${path} is not a mapping of older names to new names; it is ignored`
|
|
14699
|
+
});
|
|
14700
|
+
continue;
|
|
14701
|
+
}
|
|
14702
|
+
for (const [key, target] of Object.entries(value)) {
|
|
14703
|
+
const from = bare(key);
|
|
14704
|
+
const to = bare(target);
|
|
14705
|
+
if (from === void 0 || to === void 0 || from === to) {
|
|
14706
|
+
warnings.push({
|
|
14707
|
+
code: "rename-invalid",
|
|
14708
|
+
message: `plugin ${plugin}: the rename ${JSON.stringify(key)}: ${JSON.stringify(target)} in ${renameTableFile} is not a pair of bare reference names; it is ignored`
|
|
14709
|
+
});
|
|
14710
|
+
continue;
|
|
14711
|
+
}
|
|
14712
|
+
const list = byOld.get(from) ?? [];
|
|
14713
|
+
if (!list.some((entry) => entry.to === to && entry.plugin === plugin)) list.push({
|
|
14714
|
+
from,
|
|
14715
|
+
to,
|
|
14716
|
+
plugin
|
|
14717
|
+
});
|
|
14718
|
+
byOld.set(from, list);
|
|
14719
|
+
}
|
|
14720
|
+
}
|
|
14721
|
+
const table = {
|
|
14722
|
+
byOld,
|
|
14723
|
+
warnings
|
|
14724
|
+
};
|
|
14725
|
+
if (keyed) cache.set(keyed, table);
|
|
14726
|
+
return table;
|
|
14727
|
+
}
|
|
14105
14728
|
function layersOf(source) {
|
|
14106
14729
|
return Array.isArray(source) ? {
|
|
14107
14730
|
layers: [...source],
|
|
@@ -14188,6 +14811,7 @@ function resolveAdjusted(input, source, { display = (path) => path, without, bel
|
|
|
14188
14811
|
resolvedAs: ref.name
|
|
14189
14812
|
};
|
|
14190
14813
|
const older = olderNames(ref.name, aliases, warn);
|
|
14814
|
+
if (followed.kind === "own") warnTableRename(ref, source, aliases, warn);
|
|
14191
14815
|
const cut = below === void 0 ? -1 : all.findIndex((layer) => layer.dir === below);
|
|
14192
14816
|
const layers = cut === -1 ? all : all.slice(cut + 1);
|
|
14193
14817
|
const contributions = [];
|
|
@@ -14475,8 +15099,20 @@ function resolveInterface(ref, contributions, holders, defaultContribution, resu
|
|
|
14475
15099
|
function referenceNames(source) {
|
|
14476
15100
|
return [...new Set(layersOf(source).layers.flatMap(layerNames))].sort((a, b) => a.localeCompare(b));
|
|
14477
15101
|
}
|
|
14478
|
-
/**
|
|
14479
|
-
|
|
15102
|
+
/**
|
|
15103
|
+
* A load of a name a plugin's rename table lists as older (R120) answers as itself, with a warning
|
|
15104
|
+
* naming the new name. A name a plugin or a default still holds as its own is not renamed.
|
|
15105
|
+
*/
|
|
15106
|
+
function warnTableRename(ref, source, aliases, warn) {
|
|
15107
|
+
if (aliases.owned.has(ref.name)) return;
|
|
15108
|
+
const entries = (renameTable(source).byOld.get(ref.name) ?? []).filter((entry) => ref.plugin === void 0 || entry.plugin === ref.plugin);
|
|
15109
|
+
if (!entries.length) return;
|
|
15110
|
+
const by = [...new Set(entries.map(({ plugin }) => `plugin ${plugin}`))].join(" and ");
|
|
15111
|
+
const targets = [...new Set(entries.map(({ to }) => `"${to}"`))].join(" or ");
|
|
15112
|
+
warn("reference-renamed", `${by} renamed "${ref.name}" to ${targets}: load ${targets} instead, and run \`reference migrate\` to rename the files filed under "${ref.name}"`);
|
|
15113
|
+
}
|
|
15114
|
+
/** Every name each installed skill holds a default of, by name then skill. */
|
|
15115
|
+
function skillHoldings(source) {
|
|
14480
15116
|
return (Array.isArray(source) ? [] : source.skills?.skills ?? []).flatMap((skill) => layerNames(defaultLayer(skill.folder)).map((name) => ({
|
|
14481
15117
|
name,
|
|
14482
15118
|
skill
|
|
@@ -14790,6 +15426,8 @@ const checkWarningClass = {
|
|
|
14790
15426
|
"reference-stale": "environment",
|
|
14791
15427
|
"reference-renamed": "environment",
|
|
14792
15428
|
"alias-not-used": "environment",
|
|
15429
|
+
"rename-invalid": "environment",
|
|
15430
|
+
"reference-legacy-format": "defect",
|
|
14793
15431
|
"alias-invalid": "defect",
|
|
14794
15432
|
"project-section-not-markdown": "project-section",
|
|
14795
15433
|
"reference-hidden": "environment",
|
|
@@ -14874,6 +15512,14 @@ function interfaceLookup(source, options = {}) {
|
|
|
14874
15512
|
}
|
|
14875
15513
|
function lookUp(id, source, options) {
|
|
14876
15514
|
const resolved = resolveReference(`${id}${interfaceSuffix}`, source, options);
|
|
15515
|
+
const owner = interfaceIdOf(resolved.trace.alias?.resolvedAs ?? "");
|
|
15516
|
+
if (owner !== void 0) {
|
|
15517
|
+
const found = readFound(owner, resolved);
|
|
15518
|
+
return found.value ? {
|
|
15519
|
+
...found,
|
|
15520
|
+
renamed: owner
|
|
15521
|
+
} : found;
|
|
15522
|
+
}
|
|
14877
15523
|
if (resolved.status === "missing") {
|
|
14878
15524
|
const owners = [...aliasIndex(source).interfaces].filter(([, aliases]) => aliases.some(({ alias }) => alias === id)).map(([owner]) => owner);
|
|
14879
15525
|
if (owners.length > 1) return lookupFailure(id, `${id} is an older id of ${owners.join(" and ")}; implement one of them by its own id`);
|
|
@@ -14903,13 +15549,15 @@ function readFound(id, resolved) {
|
|
|
14903
15549
|
const { value, problems } = readInterface(id, resolved.metadata ?? {});
|
|
14904
15550
|
return value ? { value } : lookupFailure(id, `${name} is malformed: ${problems.join("; ")}`);
|
|
14905
15551
|
}
|
|
14906
|
-
|
|
15552
|
+
/** The bare name a document answers as: the name an older one asked for resolves as (R96, R128), or the name itself. */
|
|
15553
|
+
function bareName$1(resolved) {
|
|
15554
|
+
const name = resolved.trace.alias?.resolvedAs ?? resolved.name;
|
|
14907
15555
|
return name.slice(name.lastIndexOf("/") + 1);
|
|
14908
15556
|
}
|
|
14909
15557
|
/** What a composed document declares: an interface, an implementation, or neither. */
|
|
14910
15558
|
function kindOf(resolved) {
|
|
14911
15559
|
const metadata = resolved.metadata ?? {};
|
|
14912
|
-
if (interfaceIdOf(bareName$1(resolved
|
|
15560
|
+
if (interfaceIdOf(bareName$1(resolved)) !== void 0 || metadata["interface"] !== void 0) return "interface";
|
|
14913
15561
|
return metadata["implements"] === void 0 ? "none" : "implementation";
|
|
14914
15562
|
}
|
|
14915
15563
|
/** The `compose` failure for the defects composing a document raised (UC6.8), if any. */
|
|
@@ -14935,7 +15583,7 @@ function checkResolved(resolved, lookup, accept) {
|
|
|
14935
15583
|
};
|
|
14936
15584
|
if (resolved.status !== "found") return result;
|
|
14937
15585
|
const { failures, warnings } = result;
|
|
14938
|
-
const name = bareName$1(resolved
|
|
15586
|
+
const name = bareName$1(resolved);
|
|
14939
15587
|
const metadata = resolved.metadata ?? {};
|
|
14940
15588
|
if (kindOf(resolved) === "interface") {
|
|
14941
15589
|
const id = interfaceIdOf(name);
|
|
@@ -15052,7 +15700,7 @@ function checkReferences(source, options = {}) {
|
|
|
15052
15700
|
};
|
|
15053
15701
|
const lookup = interfaceLookup(source, rest);
|
|
15054
15702
|
const layers = Array.isArray(source) ? void 0 : source;
|
|
15055
|
-
const sectionNames = new Set(layers?.root === void 0 ? [] : projectSectionNames(layers.root, layers.projects, heldPlugins(layers)));
|
|
15703
|
+
const sectionNames = new Set(layers?.root === void 0 ? [] : [...projectSectionNames(layers.root, layers.projects, heldPlugins(layers)), ...projectQualifiedNames(layers.root, layers.projects)]);
|
|
15056
15704
|
const resolve = (name) => {
|
|
15057
15705
|
const ref = parseReferenceName(name);
|
|
15058
15706
|
const main = resolveReference(ref, source, resolveOptions);
|
|
@@ -15260,6 +15908,198 @@ function generatorBase(resolved, iface) {
|
|
|
15260
15908
|
if (stub !== void 0) base.stub = `${stub}\n`;
|
|
15261
15909
|
return base;
|
|
15262
15910
|
}
|
|
15911
|
+
function firstLine(value) {
|
|
15912
|
+
return typeof value === "string" ? value.split("\n")[0]?.trim() || "" : "";
|
|
15913
|
+
}
|
|
15914
|
+
/** Whether a name is in the interface's namespace but declares another interface that its name fits. */
|
|
15915
|
+
function belongsElsewhere(name, declared, id) {
|
|
15916
|
+
return declared !== void 0 && declared !== id && (name === declared || name.startsWith(`${declared}.`));
|
|
15917
|
+
}
|
|
15918
|
+
/** The highest `repo` or `user` file with a body the document holds: what makes the bare name its own. */
|
|
15919
|
+
function ownTier(resolved) {
|
|
15920
|
+
return resolved.trace.layers.find((layer) => (layer.tier === "repo" || layer.tier === "user") && layer.file && (layer.outcome === "used" || layer.outcome === "stale") && !layer.selection && !layer.empty)?.tier;
|
|
15921
|
+
}
|
|
15922
|
+
/** The plugins holding a file for the name, as read. */
|
|
15923
|
+
function holders(resolved) {
|
|
15924
|
+
return [...new Set(resolved.trace.layers.flatMap(({ tier, plugin, file }) => tier === "plugin" && plugin !== void 0 && file ? [plugin] : []))];
|
|
15925
|
+
}
|
|
15926
|
+
function referenceImplementations(id, source, options = {}) {
|
|
15927
|
+
return isFamilyName(id) || id.includes("*") ? familyImplementations(id, source, options) : interfaceImplementations(id, source, options);
|
|
15928
|
+
}
|
|
15929
|
+
/**
|
|
15930
|
+
* The interfaces a family names (K39): each interface document whose id starts with the prefix and a
|
|
15931
|
+
* dot, at any depth, never a plain document or an implementation (R130). Sorted by id. Throws
|
|
15932
|
+
* `name-invalid` for a malformed family (R136), and `interface-unknown` when none match (R135).
|
|
15933
|
+
*/
|
|
15934
|
+
function familyInterfaces(family, source, options = {}) {
|
|
15935
|
+
const parsed = parseFamily(family);
|
|
15936
|
+
if (parsed.invalid !== void 0) throw new ReferenceFailure("name-invalid", parsed.invalid);
|
|
15937
|
+
const lookup = interfaceLookup(source, options);
|
|
15938
|
+
const aliases = aliasIndex(source);
|
|
15939
|
+
const ids = /* @__PURE__ */ new Set();
|
|
15940
|
+
for (const name of referenceNames(source)) {
|
|
15941
|
+
const id = interfaceIdOf(shownName(name, aliases));
|
|
15942
|
+
if (id === void 0 || !id.startsWith(`${parsed.prefix}.`)) continue;
|
|
15943
|
+
const value = lookup(id).value;
|
|
15944
|
+
if (value) ids.add(value.id);
|
|
15945
|
+
}
|
|
15946
|
+
if (!ids.size) throw new ReferenceFailure("interface-unknown", `no interface document matches ${family}.`, { help: "Run `reference list` for the names found." });
|
|
15947
|
+
return [...ids].sort((a, b) => a.localeCompare(b));
|
|
15948
|
+
}
|
|
15949
|
+
/** `implementations <prefix>.*`: each matched interface, in name order, as `implementations <id>` lists it (R134). */
|
|
15950
|
+
function familyImplementations(family, source, options = {}) {
|
|
15951
|
+
return {
|
|
15952
|
+
family,
|
|
15953
|
+
interfaces: familyInterfaces(family, source, options).map((id) => interfaceImplementations(id, source, options))
|
|
15954
|
+
};
|
|
15955
|
+
}
|
|
15956
|
+
function interfaceImplementations(id, source, options) {
|
|
15957
|
+
const { accept: acceptOption, ...rest } = options;
|
|
15958
|
+
const resolveOptions = {
|
|
15959
|
+
...rest,
|
|
15960
|
+
projectSections: false
|
|
15961
|
+
};
|
|
15962
|
+
const ifaceName = parseReferenceName(`${id}${interfaceSuffix}`);
|
|
15963
|
+
if (ifaceName.invalid || ifaceName.plugin !== void 0) throw new ReferenceFailure("interface-unknown", `"${id}" is not an interface id.`);
|
|
15964
|
+
const lookup = interfaceLookup(source, rest);
|
|
15965
|
+
const iface = lookup(id);
|
|
15966
|
+
const value = iface.value;
|
|
15967
|
+
if (!value) throw new ReferenceFailure("interface-unknown", iface.failure?.message ?? `no interface ${id}`);
|
|
15968
|
+
const aliases = aliasIndex(source);
|
|
15969
|
+
const ids = [value.id, ...(aliases.interfaces.get(value.id) ?? []).map(({ alias }) => alias)];
|
|
15970
|
+
const accept = [...acceptOption?.length ? acceptOption : value.versions];
|
|
15971
|
+
const report = {
|
|
15972
|
+
interface: value.id,
|
|
15973
|
+
cardinality: value.cardinality,
|
|
15974
|
+
accept,
|
|
15975
|
+
implementations: [],
|
|
15976
|
+
excluded: [],
|
|
15977
|
+
warnings: []
|
|
15978
|
+
};
|
|
15979
|
+
const seen = /* @__PURE__ */ new Set();
|
|
15980
|
+
const warn = (warnings) => {
|
|
15981
|
+
for (const warning of warnings) {
|
|
15982
|
+
const key = `${warning.code} ${warning.message}`;
|
|
15983
|
+
if (!seen.has(key)) report.warnings.push(warning);
|
|
15984
|
+
seen.add(key);
|
|
15985
|
+
}
|
|
15986
|
+
};
|
|
15987
|
+
if (iface.renamed !== void 0) warn([{
|
|
15988
|
+
code: "reference-renamed",
|
|
15989
|
+
message: `${id} is an older id of ${value.id}; ask for ${value.id} instead`
|
|
15990
|
+
}]);
|
|
15991
|
+
const inNamespace = (name) => ids.some((each) => name === each || name.startsWith(`${each}.`));
|
|
15992
|
+
/** Whether a resolved document is a candidate at all: it declares the interface, or its name is in the namespace. */
|
|
15993
|
+
const relevant = (name, resolved) => {
|
|
15994
|
+
const declared = readImplements(resolved.metadata ?? {}).entry?.id;
|
|
15995
|
+
if (declared !== void 0 && ids.includes(declared)) return true;
|
|
15996
|
+
return inNamespace(name) && !ids.some((each) => belongsElsewhere(name, declared, each));
|
|
15997
|
+
};
|
|
15998
|
+
const consider = ({ pin, name, resolved, tier }) => {
|
|
15999
|
+
warn(resolved.warnings);
|
|
16000
|
+
const declared = readImplements(resolved.metadata ?? {}).entry;
|
|
16001
|
+
if (!declared || !ids.includes(declared.id)) {
|
|
16002
|
+
const shape = fitsNamespace(value.cardinality, ids, name) ? "" : ` and is not named ${value.cardinality === "one" ? value.id : `${value.id}.<key>`}`;
|
|
16003
|
+
report.excluded.push({
|
|
16004
|
+
name: pin,
|
|
16005
|
+
reasons: [`declares no implements ${value.id}@<version>${shape}`]
|
|
16006
|
+
});
|
|
16007
|
+
return;
|
|
16008
|
+
}
|
|
16009
|
+
const checked = checkResolved(resolved, lookup, {
|
|
16010
|
+
id: value.id,
|
|
16011
|
+
versions: accept
|
|
16012
|
+
});
|
|
16013
|
+
warn(checked.warnings);
|
|
16014
|
+
if (checked.failures.length) {
|
|
16015
|
+
report.excluded.push({
|
|
16016
|
+
name: pin,
|
|
16017
|
+
reasons: checked.failures.map(({ check, message }) => `${check}: ${message}`)
|
|
16018
|
+
});
|
|
16019
|
+
return;
|
|
16020
|
+
}
|
|
16021
|
+
report.implementations.push({
|
|
16022
|
+
pin,
|
|
16023
|
+
description: firstLine(resolved.metadata?.["description"]),
|
|
16024
|
+
unsupported: checked.unsupported,
|
|
16025
|
+
name,
|
|
16026
|
+
tier,
|
|
16027
|
+
version: declared.version
|
|
16028
|
+
});
|
|
16029
|
+
};
|
|
16030
|
+
const qualified = (plugin, name) => resolveReference(parseReferenceName(`${plugin}/${name}`), source, resolveOptions);
|
|
16031
|
+
const names = [...new Set(referenceNames(source).map((name) => shownName(name, aliases)))];
|
|
16032
|
+
for (const name of names) {
|
|
16033
|
+
if (interfaceIdOf(name) !== void 0) continue;
|
|
16034
|
+
const resolved = resolveReference(name, source, resolveOptions);
|
|
16035
|
+
if (resolved.status === "ambiguous") {
|
|
16036
|
+
for (const option of resolved.options ?? []) {
|
|
16037
|
+
if (option === "default") continue;
|
|
16038
|
+
const copy = resolveReference(parseReferenceName(option), source, resolveOptions);
|
|
16039
|
+
if (copy.status !== "found" || !relevant(name, copy)) continue;
|
|
16040
|
+
warn(copy.warnings);
|
|
16041
|
+
report.excluded.push({
|
|
16042
|
+
name: option,
|
|
16043
|
+
reasons: [`ambiguous: ${resolved.message}; each is left out until a customizer picks one`]
|
|
16044
|
+
});
|
|
16045
|
+
}
|
|
16046
|
+
continue;
|
|
16047
|
+
}
|
|
16048
|
+
if (resolved.status !== "found" || !relevant(name, resolved)) continue;
|
|
16049
|
+
const plugins = holders(resolved);
|
|
16050
|
+
const tier = value.cardinality === "many" ? ownTier(resolved) : void 0;
|
|
16051
|
+
if (tier !== void 0) {
|
|
16052
|
+
consider({
|
|
16053
|
+
pin: name,
|
|
16054
|
+
name,
|
|
16055
|
+
resolved,
|
|
16056
|
+
tier
|
|
16057
|
+
});
|
|
16058
|
+
for (const plugin of plugins) report.excluded.push({
|
|
16059
|
+
name: `${plugin}/${name}`,
|
|
16060
|
+
reasons: [`the ${tier} implementation answers ${name}; a consumer loads it by that name`]
|
|
16061
|
+
});
|
|
16062
|
+
continue;
|
|
16063
|
+
}
|
|
16064
|
+
const pick = resolved.trace.stack.filter((target) => target !== "default").at(-1);
|
|
16065
|
+
if (pick === void 0) continue;
|
|
16066
|
+
consider({
|
|
16067
|
+
pin: `${pick}/${name}`,
|
|
16068
|
+
name,
|
|
16069
|
+
resolved: qualified(pick, name),
|
|
16070
|
+
tier: "plugin"
|
|
16071
|
+
});
|
|
16072
|
+
for (const plugin of plugins) {
|
|
16073
|
+
if (plugin === pick) continue;
|
|
16074
|
+
report.excluded.push({
|
|
16075
|
+
name: `${plugin}/${name}`,
|
|
16076
|
+
reasons: [`${name} loads the stack of ${pick}, the pick; load ${pick}/${name}`]
|
|
16077
|
+
});
|
|
16078
|
+
}
|
|
16079
|
+
}
|
|
16080
|
+
if (value.cardinality === "one") {
|
|
16081
|
+
const inUse = implementationInUse(value.id, source, resolveOptions, report.implementations);
|
|
16082
|
+
if (inUse !== void 0) report.inUse = inUse;
|
|
16083
|
+
}
|
|
16084
|
+
return report;
|
|
16085
|
+
}
|
|
16086
|
+
/**
|
|
16087
|
+
* The implementation a load of a `one` interface's name builds on (R129), read from the stack the
|
|
16088
|
+
* load composes: the pin of the listed candidate for the plugin it builds on, or `default` when only
|
|
16089
|
+
* the skill's default is in the stack. None when the name is not `found`, or the plugin it builds on
|
|
16090
|
+
* is not listed.
|
|
16091
|
+
*/
|
|
16092
|
+
function implementationInUse(id, source, options, listed) {
|
|
16093
|
+
const resolved = resolveReference(parseReferenceName(id), source, options);
|
|
16094
|
+
if (resolved.status !== "found") return void 0;
|
|
16095
|
+
const pick = resolved.trace.stack.filter((target) => target !== "default").at(-1);
|
|
16096
|
+
if (pick === void 0) return resolved.trace.stack.includes("default") ? "default" : void 0;
|
|
16097
|
+
return listed.find(({ pin }) => pin === `${pick}/${id}`)?.pin;
|
|
16098
|
+
}
|
|
16099
|
+
/** Whether a name fits the interface's cardinality under its id or one of its older ids. */
|
|
16100
|
+
function fitsNamespace(cardinality, ids, name) {
|
|
16101
|
+
return ids.some((id) => cardinality === "one" ? name === id : name.startsWith(`${id}.`) && name.length > id.length + 1);
|
|
16102
|
+
}
|
|
15263
16103
|
/** A bare, valid name, or a refusal naming the bare name to use. */
|
|
15264
16104
|
function bareName(value) {
|
|
15265
16105
|
requireName(value);
|
|
@@ -16071,13 +16911,91 @@ function after(result, plan, layers, options) {
|
|
|
16071
16911
|
}, options)
|
|
16072
16912
|
};
|
|
16073
16913
|
}
|
|
16914
|
+
/** Whether a step, at any depth, gives `sections`: they differ per interface, so a family takes none (UC24.4). */
|
|
16915
|
+
function givesSections(value) {
|
|
16916
|
+
if (Array.isArray(value)) return value.some(givesSections);
|
|
16917
|
+
if (typeof value !== "object" || value === null) return false;
|
|
16918
|
+
return Object.values(value).some((keys) => {
|
|
16919
|
+
if (typeof keys !== "object" || keys === null || Array.isArray(keys)) return false;
|
|
16920
|
+
const record = keys;
|
|
16921
|
+
return record["sections"] !== void 0 || givesSections(record["extends"]);
|
|
16922
|
+
});
|
|
16923
|
+
}
|
|
16074
16924
|
/**
|
|
16075
|
-
* Customizes
|
|
16076
|
-
*
|
|
16077
|
-
*
|
|
16078
|
-
*
|
|
16925
|
+
* Customizes every `one` interface of a family (K39) with the same steps (R131): for each, the file
|
|
16926
|
+
* `customize <id>` with those steps writes, in the tier asked. An interface no step can answer keeps
|
|
16927
|
+
* what it had (R132); a `many` interface is skipped (R133). Every interface is planned first, so a
|
|
16928
|
+
* refusal for any of them, a family matching none (R135), or a step giving `sections` leaves every
|
|
16929
|
+
* file as it was.
|
|
16079
16930
|
*/
|
|
16931
|
+
function customizeFamily(layers, args, options = {}) {
|
|
16932
|
+
const parsed = parseFamily(args.name);
|
|
16933
|
+
if (parsed.invalid !== void 0) throw new ReferenceFailure("name-invalid", parsed.invalid);
|
|
16934
|
+
const usage = validateWrite({
|
|
16935
|
+
command: "customize",
|
|
16936
|
+
...args,
|
|
16937
|
+
name: parsed.prefix
|
|
16938
|
+
});
|
|
16939
|
+
if ([
|
|
16940
|
+
args.base,
|
|
16941
|
+
args.request,
|
|
16942
|
+
args.body,
|
|
16943
|
+
args.implements,
|
|
16944
|
+
args.merge
|
|
16945
|
+
].some((value) => value !== void 0)) throw new ReferenceFailure("options-conflict", "a family takes only picks: --extends or --extends-file, with --scope and --dry-run; a body, a merge, or a base is per name.");
|
|
16946
|
+
const picks = args.extendsFile === void 0 ? usage.picks : readExtendsFile(args.extendsFile);
|
|
16947
|
+
if (picks !== void 0 && picks !== "none" && givesSections(picks.values)) throw new ReferenceFailure("step-invalid", `a step of ${args.name} gives sections, which differ per interface; pick for one interface at a time to change sections.`);
|
|
16948
|
+
const lookup = interfaceLookup(layers, options);
|
|
16949
|
+
const interfaces = familyInterfaces(args.name, layers, options).map((id) => {
|
|
16950
|
+
if (lookup(id).value?.cardinality === "many") return {
|
|
16951
|
+
interface: id,
|
|
16952
|
+
outcome: "skipped",
|
|
16953
|
+
reason: `${id} is a many interface, which has no single pick.`
|
|
16954
|
+
};
|
|
16955
|
+
const each = {
|
|
16956
|
+
...args,
|
|
16957
|
+
name: id
|
|
16958
|
+
};
|
|
16959
|
+
try {
|
|
16960
|
+
const plan = validateWrite({
|
|
16961
|
+
command: "customize",
|
|
16962
|
+
...each
|
|
16963
|
+
}, layers, options);
|
|
16964
|
+
if (picks !== "none" && plan.base.status !== "found") return {
|
|
16965
|
+
interface: id,
|
|
16966
|
+
outcome: "kept",
|
|
16967
|
+
reason: `no step holds ${id}; it keeps what it had.`
|
|
16968
|
+
};
|
|
16969
|
+
} catch (error) {
|
|
16970
|
+
if (error instanceof ReferenceFailure && error.code === "step-invalid") return {
|
|
16971
|
+
interface: id,
|
|
16972
|
+
outcome: "kept",
|
|
16973
|
+
reason: `${error.message} It keeps what it had.`
|
|
16974
|
+
};
|
|
16975
|
+
throw error;
|
|
16976
|
+
}
|
|
16977
|
+
return {
|
|
16978
|
+
interface: id,
|
|
16979
|
+
outcome: "written",
|
|
16980
|
+
answer: each
|
|
16981
|
+
};
|
|
16982
|
+
}).map(({ answer, ...entry }) => answer === void 0 ? entry : {
|
|
16983
|
+
...entry,
|
|
16984
|
+
result: customizeSingle(layers, answer, options)
|
|
16985
|
+
});
|
|
16986
|
+
return {
|
|
16987
|
+
family: args.name,
|
|
16988
|
+
scope: args.scope ?? "repo",
|
|
16989
|
+
dryRun: Boolean(args.dryRun),
|
|
16990
|
+
interfaces
|
|
16991
|
+
};
|
|
16992
|
+
}
|
|
16080
16993
|
function customizeContribution(layers, args, options = {}) {
|
|
16994
|
+
if (isFamilyName(args.name) || args.name.includes("*")) return customizeFamily(layers, args, options);
|
|
16995
|
+
return customizeSingle(layers, args, options);
|
|
16996
|
+
}
|
|
16997
|
+
/** Customizes one name that is not a family. */
|
|
16998
|
+
function customizeSingle(layers, args, options) {
|
|
16081
16999
|
const plan = validateWrite({
|
|
16082
17000
|
command: "customize",
|
|
16083
17001
|
...args
|
|
@@ -16099,160 +17017,324 @@ function customizeContribution(layers, args, options = {}) {
|
|
|
16099
17017
|
if (plan.request !== void 0) return writeGenerated(plan, layers, result, options);
|
|
16100
17018
|
return writePicks(plan, layers, result, options);
|
|
16101
17019
|
}
|
|
16102
|
-
|
|
16103
|
-
|
|
16104
|
-
|
|
16105
|
-
|
|
16106
|
-
|
|
16107
|
-
|
|
17020
|
+
const dataSuffixes = dataExtensions.map(({ extension }) => extension);
|
|
17021
|
+
function entries(dir) {
|
|
17022
|
+
try {
|
|
17023
|
+
return readdirSync(dir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
|
|
17024
|
+
} catch {
|
|
17025
|
+
return [];
|
|
17026
|
+
}
|
|
16108
17027
|
}
|
|
16109
|
-
|
|
16110
|
-
|
|
16111
|
-
|
|
17028
|
+
function valid(name) {
|
|
17029
|
+
return !parseReferenceName(name).invalid;
|
|
17030
|
+
}
|
|
17031
|
+
/** The files a place holds for each name, in the forms its tier answers. */
|
|
17032
|
+
function heldFiles(place) {
|
|
17033
|
+
const held = [];
|
|
17034
|
+
const isDefault = place.tier === "default";
|
|
17035
|
+
const own = isDefault ? defaultSuffix : "";
|
|
17036
|
+
for (const entry of entries(place.dir)) {
|
|
17037
|
+
const file = entry.name;
|
|
17038
|
+
const path = join(place.dir, file);
|
|
17039
|
+
if (entry.isDirectory()) {
|
|
17040
|
+
if (!isDefault && valid(file) && tryRead$2(join(path, "README.md")) !== void 0) held.push({
|
|
17041
|
+
name: file,
|
|
17042
|
+
path: join(path, "README.md"),
|
|
17043
|
+
suffix: "/README.md",
|
|
17044
|
+
kind: "markdown",
|
|
17045
|
+
folderForm: true
|
|
17046
|
+
});
|
|
17047
|
+
continue;
|
|
17048
|
+
}
|
|
17049
|
+
if (file === "_renames.yaml") continue;
|
|
17050
|
+
const metadata = `${own}${metadataSuffix}.md`;
|
|
17051
|
+
if (file.endsWith(metadata)) {
|
|
17052
|
+
const name = file.slice(0, -metadata.length);
|
|
17053
|
+
if (valid(name)) held.push({
|
|
17054
|
+
name,
|
|
17055
|
+
path,
|
|
17056
|
+
suffix: metadata,
|
|
17057
|
+
kind: "metadata",
|
|
17058
|
+
folderForm: false
|
|
17059
|
+
});
|
|
17060
|
+
continue;
|
|
17061
|
+
}
|
|
17062
|
+
const markdown = `${own}.md`;
|
|
17063
|
+
if (file.endsWith(markdown)) {
|
|
17064
|
+
const name = file.slice(0, -markdown.length);
|
|
17065
|
+
if (valid(name)) held.push({
|
|
17066
|
+
name,
|
|
17067
|
+
path,
|
|
17068
|
+
suffix: markdown,
|
|
17069
|
+
kind: "markdown",
|
|
17070
|
+
folderForm: false
|
|
17071
|
+
});
|
|
17072
|
+
continue;
|
|
17073
|
+
}
|
|
17074
|
+
const data = dataSuffixes.find((extension) => file.endsWith(`${own}${extension}`));
|
|
17075
|
+
if (data) {
|
|
17076
|
+
const name = file.slice(0, -`${own}${data}`.length);
|
|
17077
|
+
if (valid(name)) held.push({
|
|
17078
|
+
name,
|
|
17079
|
+
path,
|
|
17080
|
+
suffix: `${own}${data}`,
|
|
17081
|
+
kind: "data",
|
|
17082
|
+
folderForm: false
|
|
17083
|
+
});
|
|
17084
|
+
}
|
|
17085
|
+
}
|
|
17086
|
+
return held;
|
|
16112
17087
|
}
|
|
16113
|
-
/** The
|
|
16114
|
-
function
|
|
16115
|
-
|
|
17088
|
+
/** The places a run reads (R123): a source folder alone, or the `repo` and `user` tiers and every project section. */
|
|
17089
|
+
function placesOf(layers, root, options) {
|
|
17090
|
+
if (options.plugin !== void 0 && options.default !== void 0) throw new ReferenceFailure("options-conflict", "pass one of --plugin and --default.");
|
|
17091
|
+
if (options.plugin !== void 0) return [{
|
|
17092
|
+
tier: "plugin",
|
|
17093
|
+
dir: join(sourceFolder(options.plugin, root, "plugin"), "references")
|
|
17094
|
+
}];
|
|
17095
|
+
if (options.default !== void 0) return [{
|
|
17096
|
+
tier: "default",
|
|
17097
|
+
dir: join(sourceFolder(options.default, root, "skill"), "references")
|
|
17098
|
+
}];
|
|
17099
|
+
const places = [
|
|
17100
|
+
{
|
|
17101
|
+
tier: "repo",
|
|
17102
|
+
dir: repoReferencesDir(root)
|
|
17103
|
+
},
|
|
17104
|
+
{
|
|
17105
|
+
tier: "user",
|
|
17106
|
+
dir: userReferencesDir(options.home)
|
|
17107
|
+
},
|
|
17108
|
+
...projectFolders(root, layers.projects).folders.map(({ folder, dir }) => ({
|
|
17109
|
+
tier: "project",
|
|
17110
|
+
dir,
|
|
17111
|
+
folder
|
|
17112
|
+
}))
|
|
17113
|
+
];
|
|
17114
|
+
const seen = /* @__PURE__ */ new Set();
|
|
17115
|
+
return places.filter(({ dir }) => {
|
|
17116
|
+
const key = resolve(dir);
|
|
17117
|
+
if (seen.has(key)) return false;
|
|
17118
|
+
seen.add(key);
|
|
17119
|
+
return true;
|
|
17120
|
+
});
|
|
16116
17121
|
}
|
|
16117
|
-
/**
|
|
16118
|
-
|
|
16119
|
-
|
|
16120
|
-
|
|
16121
|
-
|
|
16122
|
-
|
|
16123
|
-
|
|
16124
|
-
|
|
16125
|
-
|
|
16126
|
-
|
|
16127
|
-
|
|
16128
|
-
|
|
16129
|
-
|
|
16130
|
-
|
|
16131
|
-
projectSections: false
|
|
17122
|
+
/** The new names an older name takes: from each plugin's rename table, and from each owner's `aliases` (R124). */
|
|
17123
|
+
function renameSources(layers) {
|
|
17124
|
+
const table = renameTable(layers);
|
|
17125
|
+
const aliases = aliasIndex(layers);
|
|
17126
|
+
return (name) => {
|
|
17127
|
+
if (aliases.owned.has(name)) return [];
|
|
17128
|
+
const found = /* @__PURE__ */ new Map();
|
|
17129
|
+
const add = (to, by) => found.set(to, (found.get(to) ?? /* @__PURE__ */ new Set()).add(by));
|
|
17130
|
+
for (const entry of table.byOld.get(name) ?? []) add(entry.to, `plugin ${entry.plugin}'s ${renameTableFile}`);
|
|
17131
|
+
for (const owner of aliases.byAlias.get(name) ?? []) add(owner.name, `the aliases of ${owner.tier === "plugin" ? `plugin ${owner.plugin}` : `the default of ${owner.skill}`}`);
|
|
17132
|
+
return [...found].map(([to, by]) => ({
|
|
17133
|
+
to,
|
|
17134
|
+
by: [...by].join(" and ")
|
|
17135
|
+
}));
|
|
16132
17136
|
};
|
|
16133
|
-
|
|
16134
|
-
|
|
16135
|
-
|
|
16136
|
-
const
|
|
16137
|
-
|
|
16138
|
-
|
|
16139
|
-
|
|
16140
|
-
|
|
16141
|
-
|
|
16142
|
-
|
|
16143
|
-
|
|
16144
|
-
|
|
16145
|
-
|
|
16146
|
-
|
|
16147
|
-
|
|
16148
|
-
|
|
16149
|
-
|
|
16150
|
-
|
|
16151
|
-
const
|
|
16152
|
-
|
|
16153
|
-
|
|
16154
|
-
|
|
16155
|
-
|
|
17137
|
+
}
|
|
17138
|
+
/** Whether a folder holds any file answering `name` in its tier (R60's forms). */
|
|
17139
|
+
function holdsName(place, name) {
|
|
17140
|
+
const own = place.tier === "default" ? defaultSuffix : "";
|
|
17141
|
+
return [
|
|
17142
|
+
`${name}${own}.md`,
|
|
17143
|
+
...place.tier === "default" ? [] : [join(name, "README.md")],
|
|
17144
|
+
...dataSuffixes.map((extension) => `${name}${own}${extension}`),
|
|
17145
|
+
`${name}${own}${metadataSuffix}.md`
|
|
17146
|
+
].some((file) => existsSync(join(place.dir, file)));
|
|
17147
|
+
}
|
|
17148
|
+
/**
|
|
17149
|
+
* Finds every file in a legacy form (K37), or under a name a plugin renamed, and returns the plan
|
|
17150
|
+
* (R121). With `write`, it applies each change that is not manual. It reads the `repo` tier, the
|
|
17151
|
+
* `user` tier, and every project section below the root, or only a plugin's or a skill's source
|
|
17152
|
+
* folder in the repository (R123), and never changes a managed folder or an installed copy.
|
|
17153
|
+
*/
|
|
17154
|
+
function migrateReferences(layers, options) {
|
|
17155
|
+
const root = resolve(options.root ?? layers.root ?? process.cwd());
|
|
17156
|
+
const names = options.names?.length ? new Set(options.names.map((name) => bareName(name).name)) : void 0;
|
|
17157
|
+
const places = placesOf(layers, root, options);
|
|
17158
|
+
const targetsOf = renameSources(layers);
|
|
17159
|
+
const installed = new Set(layers.layers.flatMap(({ tier, plugin, skipped }) => tier === "plugin" && plugin && !skipped ? [plugin] : []));
|
|
17160
|
+
const rows = [];
|
|
17161
|
+
const operations = [];
|
|
17162
|
+
for (const place of places) {
|
|
17163
|
+
const where = place.folder === void 0 ? {} : { folder: place.folder };
|
|
17164
|
+
const renames = place.tier === "repo" || place.tier === "user" || place.tier === "project";
|
|
17165
|
+
/** New names this run gives files in the folder, so a second file renamed to one is manual. */
|
|
17166
|
+
const claimed = /* @__PURE__ */ new Set();
|
|
17167
|
+
const held = heldFiles(place);
|
|
17168
|
+
if (place.tier === "default") held.push(...oldDefaults(place, names, held));
|
|
17169
|
+
for (const file of held) {
|
|
17170
|
+
const targets = renames ? targetsOf(file.name) : [];
|
|
17171
|
+
if (names && !names.has(file.name) && !targets.some(({ to }) => names.has(to))) continue;
|
|
17172
|
+
const base = {
|
|
17173
|
+
path: file.path,
|
|
17174
|
+
tier: place.tier,
|
|
17175
|
+
...where,
|
|
17176
|
+
name: file.name
|
|
17177
|
+
};
|
|
17178
|
+
const operation = { path: file.path };
|
|
17179
|
+
if (file.kind === "markdown") {
|
|
17180
|
+
const raw = tryRead$2(file.path) ?? "";
|
|
17181
|
+
const { metadata, body } = parseDocument(raw, () => {});
|
|
17182
|
+
const { findings, keys } = legacyRewrite(metadata, body, place.tier === "project" ? "project" : "contribution", file.name);
|
|
17183
|
+
for (const finding of findings) rows.push(rowOf(base, finding));
|
|
17184
|
+
if (Object.keys(keys).length) operation.content = editFrontmatter(raw, keys);
|
|
17185
|
+
}
|
|
17186
|
+
if (file.suffix === ".md" && place.tier === "default") {
|
|
17187
|
+
const to = join(place.dir, `${file.name}${defaultSuffix}.md`);
|
|
17188
|
+
const finding = defaultLayoutFinding(file.name);
|
|
17189
|
+
if (existsSync(to)) {
|
|
17190
|
+
const { to: _, ...kept } = finding;
|
|
17191
|
+
rows.push(rowOf(base, {
|
|
17192
|
+
...kept,
|
|
17193
|
+
manual: `${file.name}${defaultSuffix}.md is there too and answers the name; the old file is not read, so delete it by hand once nothing in it is needed`
|
|
17194
|
+
}));
|
|
17195
|
+
} else {
|
|
17196
|
+
rows.push(rowOf(base, finding));
|
|
17197
|
+
Object.assign(operation, {
|
|
17198
|
+
moveFrom: file.path,
|
|
17199
|
+
moveTo: to
|
|
17200
|
+
});
|
|
17201
|
+
}
|
|
17202
|
+
}
|
|
17203
|
+
if (targets.length) renameRow(place, file, targets, claimed, base, rows, operation);
|
|
17204
|
+
if (operation.content !== void 0 || operation.moveTo !== void 0) operations.push(operation);
|
|
17205
|
+
}
|
|
17206
|
+
if (renames) for (const found of qualifiedOverrides(place.dir)) {
|
|
17207
|
+
if (names && !names.has(found.name)) continue;
|
|
17208
|
+
const operation = qualifiedRow(place, found, installed, claimed, {
|
|
17209
|
+
path: found.path,
|
|
17210
|
+
tier: place.tier,
|
|
17211
|
+
...where,
|
|
17212
|
+
name: found.name
|
|
17213
|
+
}, rows);
|
|
17214
|
+
if (operation) operations.push(operation);
|
|
16156
17215
|
}
|
|
17216
|
+
}
|
|
17217
|
+
let written = false;
|
|
17218
|
+
if (options.write) for (const operation of operations) {
|
|
17219
|
+
if (operation.content !== void 0) writeFileSync(operation.path, operation.content);
|
|
17220
|
+
if (operation.moveFrom !== void 0 && operation.moveTo !== void 0) renameSync(operation.moveFrom, operation.moveTo);
|
|
17221
|
+
if (operation.prune !== void 0) pruneEmpty(operation.prune);
|
|
17222
|
+
written = true;
|
|
17223
|
+
}
|
|
17224
|
+
return {
|
|
17225
|
+
rows,
|
|
17226
|
+
written,
|
|
17227
|
+
warnings: [...renameTable(layers).warnings]
|
|
16157
17228
|
};
|
|
16158
|
-
|
|
16159
|
-
|
|
16160
|
-
|
|
16161
|
-
|
|
16162
|
-
|
|
16163
|
-
|
|
16164
|
-
const relevant = (name, resolved) => {
|
|
16165
|
-
const declared = readImplements(resolved.metadata ?? {}).entry?.id;
|
|
16166
|
-
if (declared !== void 0 && ids.includes(declared)) return true;
|
|
16167
|
-
return inNamespace(name) && !ids.some((each) => belongsElsewhere(name, declared, each));
|
|
17229
|
+
}
|
|
17230
|
+
function rowOf(base, finding) {
|
|
17231
|
+
const row = {
|
|
17232
|
+
...base,
|
|
17233
|
+
form: finding.form,
|
|
17234
|
+
from: finding.from
|
|
16168
17235
|
};
|
|
16169
|
-
const
|
|
16170
|
-
|
|
16171
|
-
|
|
16172
|
-
|
|
16173
|
-
|
|
16174
|
-
|
|
16175
|
-
|
|
16176
|
-
|
|
16177
|
-
|
|
16178
|
-
|
|
16179
|
-
|
|
16180
|
-
|
|
16181
|
-
|
|
16182
|
-
|
|
16183
|
-
|
|
16184
|
-
warn(checked.warnings);
|
|
16185
|
-
if (checked.failures.length) {
|
|
16186
|
-
report.excluded.push({
|
|
16187
|
-
name: pin,
|
|
16188
|
-
reasons: checked.failures.map(({ check, message }) => `${check}: ${message}`)
|
|
16189
|
-
});
|
|
16190
|
-
return;
|
|
16191
|
-
}
|
|
16192
|
-
report.implementations.push({
|
|
16193
|
-
pin,
|
|
16194
|
-
description: firstLine(resolved.metadata?.["description"]),
|
|
16195
|
-
unsupported: checked.unsupported,
|
|
17236
|
+
const note = finding.note === void 0 ? "" : `; ${finding.reads}, and ${finding.note}`;
|
|
17237
|
+
if (finding.to !== void 0) row.to = `${finding.to}${note}`;
|
|
17238
|
+
if (finding.manual !== void 0) row.manual = `${finding.manual}${note}`;
|
|
17239
|
+
return row;
|
|
17240
|
+
}
|
|
17241
|
+
/**
|
|
17242
|
+
* A skill's defaults in the old layout, `references/<name>.md`, for the names given (UC22.7): the
|
|
17243
|
+
* folder's other files may be the skill's own documents, so no name given recognizes none.
|
|
17244
|
+
*/
|
|
17245
|
+
function oldDefaults(place, names, held) {
|
|
17246
|
+
if (!names) return [];
|
|
17247
|
+
return [...names].flatMap((name) => {
|
|
17248
|
+
const path = join(place.dir, `${name}.md`);
|
|
17249
|
+
if (tryRead$2(path) === void 0 || held.some((file) => file.path === path)) return [];
|
|
17250
|
+
return [{
|
|
16196
17251
|
name,
|
|
16197
|
-
|
|
16198
|
-
|
|
16199
|
-
|
|
17252
|
+
path,
|
|
17253
|
+
suffix: ".md",
|
|
17254
|
+
kind: "markdown",
|
|
17255
|
+
folderForm: false
|
|
17256
|
+
}];
|
|
17257
|
+
});
|
|
17258
|
+
}
|
|
17259
|
+
/** The rename row of a file under an older name (R124), and the move that applies it. */
|
|
17260
|
+
function renameRow(place, file, targets, claimed, base, rows, operation) {
|
|
17261
|
+
const from = file.folderForm ? `${file.name}/` : `${file.name}${file.suffix}`;
|
|
17262
|
+
const row = {
|
|
17263
|
+
...base,
|
|
17264
|
+
form: "renamed",
|
|
17265
|
+
from,
|
|
17266
|
+
by: targets.map(({ by }) => by).join("; ")
|
|
16200
17267
|
};
|
|
16201
|
-
|
|
16202
|
-
|
|
16203
|
-
|
|
16204
|
-
|
|
16205
|
-
const resolved = resolveReference(name, source, resolveOptions);
|
|
16206
|
-
if (resolved.status === "ambiguous") {
|
|
16207
|
-
for (const option of resolved.options ?? []) {
|
|
16208
|
-
if (option === "default") continue;
|
|
16209
|
-
const copy = resolveReference(parseReferenceName(option), source, resolveOptions);
|
|
16210
|
-
if (copy.status !== "found" || !relevant(name, copy)) continue;
|
|
16211
|
-
warn(copy.warnings);
|
|
16212
|
-
report.excluded.push({
|
|
16213
|
-
name: option,
|
|
16214
|
-
reasons: [`ambiguous: ${resolved.message}; each is left out until a customizer picks one`]
|
|
16215
|
-
});
|
|
16216
|
-
}
|
|
16217
|
-
continue;
|
|
16218
|
-
}
|
|
16219
|
-
if (resolved.status !== "found" || !relevant(name, resolved)) continue;
|
|
16220
|
-
const plugins = holders(resolved);
|
|
16221
|
-
const tier = value.cardinality === "many" ? ownTier(resolved) : void 0;
|
|
16222
|
-
if (tier !== void 0) {
|
|
16223
|
-
consider({
|
|
16224
|
-
pin: name,
|
|
16225
|
-
name,
|
|
16226
|
-
resolved,
|
|
16227
|
-
tier
|
|
16228
|
-
});
|
|
16229
|
-
for (const plugin of plugins) report.excluded.push({
|
|
16230
|
-
name: `${plugin}/${name}`,
|
|
16231
|
-
reasons: [`the ${tier} implementation answers ${name}; a consumer loads it by that name`]
|
|
16232
|
-
});
|
|
16233
|
-
continue;
|
|
16234
|
-
}
|
|
16235
|
-
const pick = resolved.trace.stack.filter((target) => target !== "default").at(-1);
|
|
16236
|
-
if (pick === void 0) continue;
|
|
16237
|
-
consider({
|
|
16238
|
-
pin: `${pick}/${name}`,
|
|
16239
|
-
name,
|
|
16240
|
-
resolved: qualified(pick, name),
|
|
16241
|
-
tier: "plugin"
|
|
17268
|
+
if (targets.length > 1) {
|
|
17269
|
+
rows.push({
|
|
17270
|
+
...row,
|
|
17271
|
+
manual: `it is renamed differently: ${targets.map(({ to, by }) => `"${to}" by ${by}`).join(", ")}; rename it by hand to the one that applies`
|
|
16242
17272
|
});
|
|
16243
|
-
|
|
16244
|
-
if (plugin === pick) continue;
|
|
16245
|
-
report.excluded.push({
|
|
16246
|
-
name: `${plugin}/${name}`,
|
|
16247
|
-
reasons: [`${name} loads the stack of ${pick}, the pick; load ${pick}/${name}`]
|
|
16248
|
-
});
|
|
16249
|
-
}
|
|
17273
|
+
return;
|
|
16250
17274
|
}
|
|
16251
|
-
|
|
17275
|
+
const { to } = targets[0];
|
|
17276
|
+
const companion = file.kind === "metadata" || file.kind === "data";
|
|
17277
|
+
if (holdsName(place, to) && !claimed.has(to) || claimed.has(to) && !companion) {
|
|
17278
|
+
rows.push({
|
|
17279
|
+
...row,
|
|
17280
|
+
manual: `"${to}" already has a file in that folder; merge the two by hand, then delete this one`
|
|
17281
|
+
});
|
|
17282
|
+
return;
|
|
17283
|
+
}
|
|
17284
|
+
claimed.add(to);
|
|
17285
|
+
const into = file.folderForm ? `${to}/` : `${to}${file.suffix}`;
|
|
17286
|
+
rows.push({
|
|
17287
|
+
...row,
|
|
17288
|
+
to: into
|
|
17289
|
+
});
|
|
17290
|
+
const moveFrom = file.folderForm ? dirname(file.path) : file.path;
|
|
17291
|
+
Object.assign(operation, {
|
|
17292
|
+
moveFrom,
|
|
17293
|
+
moveTo: join(place.dir, file.folderForm ? to : `${to}${file.suffix}`)
|
|
17294
|
+
});
|
|
16252
17295
|
}
|
|
16253
|
-
|
|
16254
|
-
|
|
16255
|
-
|
|
17296
|
+
function pruneEmpty(dir) {
|
|
17297
|
+
try {
|
|
17298
|
+
if (!readdirSync(dir).length) rmdirSync(dir);
|
|
17299
|
+
} catch {}
|
|
17300
|
+
}
|
|
17301
|
+
/**
|
|
17302
|
+
* The row of a qualified-name override (K37), and the move that applies it: `<plugin>/<name>.md`
|
|
17303
|
+
* becomes `<name>.md` beside the subfolder, extending the plugin. It is manual in a project folder,
|
|
17304
|
+
* for a plugin the plugin tier does not hold, beside a file that already answers the name, and for a
|
|
17305
|
+
* file that writes its own `extends` or `overrides`.
|
|
17306
|
+
*/
|
|
17307
|
+
function qualifiedRow(place, found, installed, claimed, base, rows) {
|
|
17308
|
+
const { plugin, name } = found;
|
|
17309
|
+
const finding = qualifiedOverrideFinding(found, place.tier === "project" ? "project" : "contribution");
|
|
17310
|
+
const { to, ...kept } = finding;
|
|
17311
|
+
const manual = (why) => {
|
|
17312
|
+
rows.push(rowOf(base, {
|
|
17313
|
+
...kept,
|
|
17314
|
+
manual: why
|
|
17315
|
+
}));
|
|
17316
|
+
};
|
|
17317
|
+
if (to === void 0) return manual(finding.manual);
|
|
17318
|
+
if (!installed.has(plugin)) return manual(`plugin ${plugin} is not installed; it would become ${to}, extending a plugin that is not there: install ${plugin} and run migrate again, or delete this file`);
|
|
17319
|
+
if (holdsName(place, name) || claimed.has(name)) return manual(`"${name}" already has a file in that folder; move this file's sections into it and add ${plugin} to its extends, then delete this one`);
|
|
17320
|
+
const raw = tryRead$2(found.path) ?? "";
|
|
17321
|
+
const { metadata, body } = parseDocument(raw, () => {});
|
|
17322
|
+
const own = ["extends", "overrides"].find((key) => key in metadata);
|
|
17323
|
+
if (own !== void 0) return manual(`it also writes \`${own}\`; move it to ${name}.md by hand, with an extends that names ${plugin}`);
|
|
17324
|
+
claimed.add(name);
|
|
17325
|
+
rows.push(rowOf(base, finding));
|
|
17326
|
+
const { findings, keys } = legacyRewrite(metadata, body, "contribution", name);
|
|
17327
|
+
for (const other of findings) rows.push(rowOf(base, other));
|
|
17328
|
+
return {
|
|
17329
|
+
path: found.path,
|
|
17330
|
+
content: editFrontmatter(raw, {
|
|
17331
|
+
...keys,
|
|
17332
|
+
extends: [plugin]
|
|
17333
|
+
}),
|
|
17334
|
+
moveFrom: found.path,
|
|
17335
|
+
moveTo: join(place.dir, `${name}.md`),
|
|
17336
|
+
prune: dirname(found.path)
|
|
17337
|
+
};
|
|
16256
17338
|
}
|
|
16257
17339
|
/**
|
|
16258
17340
|
* The files a customization of `name` is written to, `repo` and `user` and, with `in`, that folder's
|
|
@@ -16309,129 +17391,436 @@ function whereReference(input, source, { root, display = (path) => path, in: fol
|
|
|
16309
17391
|
return report;
|
|
16310
17392
|
}
|
|
16311
17393
|
//#endregion
|
|
16312
|
-
//#region src/
|
|
16313
|
-
/** Generic marketplace names: a second registration under one of these replaces the first, silently. */
|
|
16314
|
-
const GENERIC_NAMES = /* @__PURE__ */ new Set([
|
|
16315
|
-
"deps",
|
|
16316
|
-
"dependencies",
|
|
16317
|
-
"local",
|
|
16318
|
-
"plugins",
|
|
16319
|
-
"marketplace",
|
|
16320
|
-
"plugin",
|
|
16321
|
-
"default"
|
|
16322
|
-
]);
|
|
16323
|
-
function isGenericName(name) {
|
|
16324
|
-
return GENERIC_NAMES.has(name.toLowerCase());
|
|
16325
|
-
}
|
|
17394
|
+
//#region src/harness-registry/harness-registry.ts
|
|
16326
17395
|
/**
|
|
16327
|
-
*
|
|
16328
|
-
*
|
|
17396
|
+
* Throws on a harness `skillsDirectories` does not confirm: treating it as native would silently
|
|
17397
|
+
* drop a projection it may need.
|
|
16329
17398
|
*/
|
|
16330
|
-
function
|
|
16331
|
-
|
|
17399
|
+
function skillsProjection(harness, scope, directories = skillsDirectories(harness)?.[scope]) {
|
|
17400
|
+
const listed = directories ?? [];
|
|
17401
|
+
const [first] = listed;
|
|
17402
|
+
if (first === void 0) throw new Error(`cyber-agentkit records no ${scope} skills directories for ${harness}.`);
|
|
17403
|
+
return listed.includes(".agents/skills") ? {} : { skillsDirectory: first };
|
|
16332
17404
|
}
|
|
16333
17405
|
/**
|
|
16334
|
-
*
|
|
16335
|
-
*
|
|
17406
|
+
* The user-scope counterpart of the root `AGENTS.md`, relative to the home directory. No harness
|
|
17407
|
+
* reads it natively; each loads it only through a user-scope `instructionBridge` (E-CC-18, E-CODEX-05,
|
|
17408
|
+
* E-GEM-03, E-COPILOT-04, E-CUR-08).
|
|
16336
17409
|
*/
|
|
16337
|
-
|
|
16338
|
-
|
|
16339
|
-
|
|
16340
|
-
|
|
16341
|
-
|
|
16342
|
-
|
|
16343
|
-
|
|
16344
|
-
|
|
16345
|
-
|
|
16346
|
-
|
|
16347
|
-
|
|
16348
|
-
|
|
16349
|
-
|
|
16350
|
-
|
|
16351
|
-
|
|
16352
|
-
|
|
16353
|
-
|
|
16354
|
-
|
|
16355
|
-
|
|
16356
|
-
|
|
16357
|
-
|
|
16358
|
-
|
|
16359
|
-
|
|
16360
|
-
|
|
16361
|
-
|
|
16362
|
-
|
|
16363
|
-
|
|
16364
|
-
|
|
16365
|
-
|
|
16366
|
-
|
|
16367
|
-
|
|
16368
|
-
|
|
16369
|
-
|
|
16370
|
-
|
|
16371
|
-
|
|
16372
|
-
|
|
16373
|
-
|
|
16374
|
-
|
|
16375
|
-
|
|
16376
|
-
|
|
16377
|
-
|
|
16378
|
-
|
|
16379
|
-
|
|
16380
|
-
|
|
16381
|
-
|
|
16382
|
-
|
|
16383
|
-
|
|
16384
|
-
|
|
16385
|
-
|
|
16386
|
-
|
|
16387
|
-
const record = manifest;
|
|
16388
|
-
const name = typeof record["name"] === "string" ? record["name"] : void 0;
|
|
16389
|
-
if (!name) return {
|
|
16390
|
-
ok: false,
|
|
16391
|
-
package: pkg,
|
|
16392
|
-
reason: "unnamed"
|
|
16393
|
-
};
|
|
16394
|
-
const installed = readJson$1(join(dir, "package.json"));
|
|
16395
|
-
const version = typeof installed?.version === "string" ? installed.version : void 0;
|
|
16396
|
-
if (!version) return {
|
|
16397
|
-
ok: false,
|
|
16398
|
-
package: pkg,
|
|
16399
|
-
reason: "no-manifest"
|
|
16400
|
-
};
|
|
16401
|
-
const description = typeof record["description"] === "string" ? record["description"] : void 0;
|
|
16402
|
-
return {
|
|
16403
|
-
ok: true,
|
|
16404
|
-
plugin: description ? {
|
|
16405
|
-
package: pkg,
|
|
16406
|
-
plugin: name,
|
|
16407
|
-
version,
|
|
16408
|
-
description
|
|
16409
|
-
} : {
|
|
16410
|
-
package: pkg,
|
|
16411
|
-
plugin: name,
|
|
16412
|
-
version
|
|
17410
|
+
const globalInstructions = ".agents/AGENTS.md";
|
|
17411
|
+
/**
|
|
17412
|
+
* Skills directories come from `cyber-agentkit`, except for the harnesses it does not know
|
|
17413
|
+
* (`devin-desktop`, `windsurf`); see `.research/agentic-configuration-standards/` for the rest.
|
|
17414
|
+
*/
|
|
17415
|
+
const harnessRegistry = [
|
|
17416
|
+
{
|
|
17417
|
+
name: "claude-code",
|
|
17418
|
+
project: {
|
|
17419
|
+
detect: ".claude",
|
|
17420
|
+
...skillsProjection("claude-code", "project"),
|
|
17421
|
+
shadowedBy: [
|
|
17422
|
+
"CLAUDE.md",
|
|
17423
|
+
".claude/CLAUDE.md",
|
|
17424
|
+
"CLAUDE.local.md"
|
|
17425
|
+
],
|
|
17426
|
+
mcpConfig: {
|
|
17427
|
+
path: ".mcp.json",
|
|
17428
|
+
key: "mcpServers",
|
|
17429
|
+
format: "json",
|
|
17430
|
+
dialect: "claude-code"
|
|
17431
|
+
},
|
|
17432
|
+
nonstandard: [
|
|
17433
|
+
{
|
|
17434
|
+
path: ".claude/commands",
|
|
17435
|
+
shape: "directory",
|
|
17436
|
+
kind: "command"
|
|
17437
|
+
},
|
|
17438
|
+
{
|
|
17439
|
+
path: ".claude/rules",
|
|
17440
|
+
shape: "directory",
|
|
17441
|
+
kind: "rule"
|
|
17442
|
+
},
|
|
17443
|
+
{
|
|
17444
|
+
path: ".claude/agents",
|
|
17445
|
+
shape: "directory",
|
|
17446
|
+
kind: "subagent"
|
|
17447
|
+
}
|
|
17448
|
+
]
|
|
17449
|
+
},
|
|
17450
|
+
user: {
|
|
17451
|
+
detect: ".claude",
|
|
17452
|
+
...skillsProjection("claude-code", "user"),
|
|
17453
|
+
relocatedBy: "CLAUDE_CONFIG_DIR",
|
|
17454
|
+
emptyRelocatesToStart: true,
|
|
17455
|
+
instructionBridge: {
|
|
17456
|
+
kind: "import",
|
|
17457
|
+
path: ".claude/CLAUDE.md",
|
|
17458
|
+
line: `@~/${globalInstructions}`
|
|
17459
|
+
}
|
|
16413
17460
|
}
|
|
16414
|
-
}
|
|
17461
|
+
},
|
|
17462
|
+
{
|
|
17463
|
+
name: "cursor",
|
|
17464
|
+
project: {
|
|
17465
|
+
detect: ".cursor",
|
|
17466
|
+
...skillsProjection("cursor", "project"),
|
|
17467
|
+
mcpConfig: {
|
|
17468
|
+
path: ".cursor/mcp.json",
|
|
17469
|
+
key: "mcpServers",
|
|
17470
|
+
format: "json",
|
|
17471
|
+
dialect: "cursor"
|
|
17472
|
+
},
|
|
17473
|
+
nonstandard: [
|
|
17474
|
+
{
|
|
17475
|
+
path: ".cursorrules",
|
|
17476
|
+
shape: "file",
|
|
17477
|
+
kind: "instructions"
|
|
17478
|
+
},
|
|
17479
|
+
{
|
|
17480
|
+
path: ".cursor/rules",
|
|
17481
|
+
shape: "directory",
|
|
17482
|
+
kind: "rule"
|
|
17483
|
+
},
|
|
17484
|
+
{
|
|
17485
|
+
path: ".cursor/commands",
|
|
17486
|
+
shape: "directory",
|
|
17487
|
+
kind: "command"
|
|
17488
|
+
},
|
|
17489
|
+
{
|
|
17490
|
+
path: ".cursor/skills",
|
|
17491
|
+
shape: "directory",
|
|
17492
|
+
kind: "skill"
|
|
17493
|
+
}
|
|
17494
|
+
]
|
|
17495
|
+
},
|
|
17496
|
+
user: {
|
|
17497
|
+
detect: ".cursor",
|
|
17498
|
+
...skillsProjection("cursor", "user")
|
|
17499
|
+
}
|
|
17500
|
+
},
|
|
17501
|
+
{
|
|
17502
|
+
name: "codex",
|
|
17503
|
+
project: {
|
|
17504
|
+
detect: ".codex",
|
|
17505
|
+
...skillsProjection("codex", "project"),
|
|
17506
|
+
mcpConfig: {
|
|
17507
|
+
path: ".codex/config.toml",
|
|
17508
|
+
key: "mcp_servers",
|
|
17509
|
+
format: "toml",
|
|
17510
|
+
dialect: "codex",
|
|
17511
|
+
shared: true
|
|
17512
|
+
},
|
|
17513
|
+
nonstandard: [{
|
|
17514
|
+
path: ".codex/skills",
|
|
17515
|
+
shape: "directory",
|
|
17516
|
+
kind: "skill"
|
|
17517
|
+
}]
|
|
17518
|
+
},
|
|
17519
|
+
user: {
|
|
17520
|
+
detect: ".codex",
|
|
17521
|
+
...skillsProjection("codex", "user"),
|
|
17522
|
+
instructionBridge: {
|
|
17523
|
+
kind: "symlink",
|
|
17524
|
+
path: ".codex/AGENTS.md"
|
|
17525
|
+
},
|
|
17526
|
+
shadowedBy: [".codex/AGENTS.override.md"],
|
|
17527
|
+
relocatedBy: "CODEX_HOME"
|
|
17528
|
+
}
|
|
17529
|
+
},
|
|
17530
|
+
{
|
|
17531
|
+
name: "copilot-cli",
|
|
17532
|
+
project: {
|
|
17533
|
+
detect: ".github/skills",
|
|
17534
|
+
...skillsProjection("copilot-cli", "project"),
|
|
17535
|
+
nonstandard: [
|
|
17536
|
+
{
|
|
17537
|
+
path: ".github/copilot-instructions.md",
|
|
17538
|
+
shape: "file",
|
|
17539
|
+
kind: "instructions"
|
|
17540
|
+
},
|
|
17541
|
+
{
|
|
17542
|
+
path: ".github/instructions",
|
|
17543
|
+
shape: "directory",
|
|
17544
|
+
kind: "instructions"
|
|
17545
|
+
},
|
|
17546
|
+
{
|
|
17547
|
+
path: ".github/skills",
|
|
17548
|
+
shape: "directory",
|
|
17549
|
+
kind: "skill"
|
|
17550
|
+
}
|
|
17551
|
+
]
|
|
17552
|
+
},
|
|
17553
|
+
user: {
|
|
17554
|
+
detect: ".copilot",
|
|
17555
|
+
...skillsProjection("copilot-cli", "user"),
|
|
17556
|
+
relocatedBy: "COPILOT_HOME",
|
|
17557
|
+
instructionBridge: {
|
|
17558
|
+
kind: "symlink",
|
|
17559
|
+
path: ".copilot/copilot-instructions.md"
|
|
17560
|
+
}
|
|
17561
|
+
}
|
|
17562
|
+
},
|
|
17563
|
+
{
|
|
17564
|
+
name: "gemini-cli",
|
|
17565
|
+
project: {
|
|
17566
|
+
detect: ".gemini",
|
|
17567
|
+
...skillsProjection("gemini-cli", "project"),
|
|
17568
|
+
instructionBridge: {
|
|
17569
|
+
kind: "settings-entry",
|
|
17570
|
+
path: ".gemini/settings.json",
|
|
17571
|
+
key: "context.fileName"
|
|
17572
|
+
},
|
|
17573
|
+
mcpConfig: {
|
|
17574
|
+
path: ".gemini/settings.json",
|
|
17575
|
+
key: "mcpServers",
|
|
17576
|
+
format: "json",
|
|
17577
|
+
dialect: "gemini-cli",
|
|
17578
|
+
shared: true
|
|
17579
|
+
},
|
|
17580
|
+
nonstandard: [{
|
|
17581
|
+
path: "GEMINI.md",
|
|
17582
|
+
shape: "file",
|
|
17583
|
+
kind: "instructions"
|
|
17584
|
+
}, {
|
|
17585
|
+
path: ".gemini/skills",
|
|
17586
|
+
shape: "directory",
|
|
17587
|
+
kind: "skill"
|
|
17588
|
+
}]
|
|
17589
|
+
},
|
|
17590
|
+
user: {
|
|
17591
|
+
detect: ".gemini",
|
|
17592
|
+
...skillsProjection("gemini-cli", "user"),
|
|
17593
|
+
instructionBridge: {
|
|
17594
|
+
kind: "symlink",
|
|
17595
|
+
path: ".gemini/GEMINI.md"
|
|
17596
|
+
}
|
|
17597
|
+
}
|
|
17598
|
+
},
|
|
17599
|
+
{
|
|
17600
|
+
name: "devin-desktop",
|
|
17601
|
+
project: { detect: ".devin" }
|
|
17602
|
+
},
|
|
17603
|
+
{
|
|
17604
|
+
name: "windsurf",
|
|
17605
|
+
project: {
|
|
17606
|
+
detect: ".windsurf",
|
|
17607
|
+
skillsDirectory: ".windsurf/skills",
|
|
17608
|
+
nonstandard: [{
|
|
17609
|
+
path: ".windsurfrules",
|
|
17610
|
+
shape: "file",
|
|
17611
|
+
kind: "instructions"
|
|
17612
|
+
}, {
|
|
17613
|
+
path: ".windsurf/rules",
|
|
17614
|
+
shape: "directory",
|
|
17615
|
+
kind: "rule"
|
|
17616
|
+
}]
|
|
17617
|
+
},
|
|
17618
|
+
deprecated: "devin-desktop"
|
|
17619
|
+
}
|
|
17620
|
+
];
|
|
17621
|
+
const harnessNames = new Set(harnessRegistry.map((harness) => harness.name));
|
|
17622
|
+
function isHarnessName(value) {
|
|
17623
|
+
return harnessNames.has(value);
|
|
16415
17624
|
}
|
|
16416
17625
|
/**
|
|
16417
|
-
*
|
|
16418
|
-
*
|
|
17626
|
+
* An unknown name throws rather than being dropped — silently falling back to the defaults would
|
|
17627
|
+
* misreport what was diagnosed.
|
|
16419
17628
|
*/
|
|
16420
|
-
function
|
|
16421
|
-
|
|
17629
|
+
function parseHarnesses(value) {
|
|
17630
|
+
const requested = (value ?? "").split(",").map((name) => name.trim()).filter(Boolean);
|
|
17631
|
+
const unsupported = requested.filter((name) => !isHarnessName(name));
|
|
17632
|
+
if (unsupported.length) throw new Error(`Unsupported harness: ${unsupported.join(", ")}. Supported: ${harnessRegistry.map((harness) => harness.name).join(", ")}.`);
|
|
17633
|
+
return requested.filter(isHarnessName);
|
|
17634
|
+
}
|
|
17635
|
+
/** Enabled whether or not the repository already contains their directories. */
|
|
17636
|
+
const defaultHarnesses = ["claude-code", "cursor"];
|
|
17637
|
+
function directoryExists(path) {
|
|
17638
|
+
try {
|
|
17639
|
+
return lstatSync(path).isDirectory();
|
|
17640
|
+
} catch {
|
|
17641
|
+
return false;
|
|
17642
|
+
}
|
|
17643
|
+
}
|
|
17644
|
+
/** The default harnesses, plus the preferred ones, plus every harness whose directory is present. */
|
|
17645
|
+
function selectHarnesses(root, preferred) {
|
|
17646
|
+
const enabled = /* @__PURE__ */ new Set([...defaultHarnesses, ...preferred]);
|
|
17647
|
+
return harnessRegistry.filter((harness) => enabled.has(harness.name) || directoryExists(join(root, harness.project.detect)));
|
|
16422
17648
|
}
|
|
16423
17649
|
//#endregion
|
|
16424
|
-
//#region src/
|
|
17650
|
+
//#region src/command-output/command-output.ts
|
|
16425
17651
|
/**
|
|
16426
|
-
*
|
|
16427
|
-
*
|
|
17652
|
+
* AXI §6: a failure is a coded error on stdout, in the caller's format, with a `help` line saying
|
|
17653
|
+
* what to do next. A `CliError` the command already built is rethrown untouched; anything else
|
|
17654
|
+
* becomes `command-failed`, exit code 1.
|
|
16428
17655
|
*/
|
|
16429
|
-
|
|
16430
|
-
|
|
16431
|
-
|
|
16432
|
-
|
|
17656
|
+
function fail(error, fallback, help) {
|
|
17657
|
+
if (isCliError(error)) throw error;
|
|
17658
|
+
throw new CliError(error instanceof Error ? error.message : fallback, {
|
|
17659
|
+
code: "command-failed",
|
|
17660
|
+
exitCode: exitCodes.error,
|
|
17661
|
+
...help ? { help } : {},
|
|
17662
|
+
cause: error
|
|
17663
|
+
});
|
|
16433
17664
|
}
|
|
16434
|
-
|
|
17665
|
+
/**
|
|
17666
|
+
* An answer that comes with a failing exit code. The cli prints only an answer a command returns, and
|
|
17667
|
+
* a thrown error replaces the return, so the answer is printed first.
|
|
17668
|
+
*/
|
|
17669
|
+
function answerThenFail(answer, format) {
|
|
17670
|
+
const text = printAnswer(answer, format);
|
|
17671
|
+
if (text !== void 0) process.stdout.write(text);
|
|
17672
|
+
throw CliError.reported(exitCodes.error);
|
|
17673
|
+
}
|
|
17674
|
+
/**
|
|
17675
|
+
* AXI §10: collapses the home directory to `~` so a path in a report is portable to another
|
|
17676
|
+
* machine.
|
|
17677
|
+
*/
|
|
17678
|
+
function collapseHome$1(home, path) {
|
|
17679
|
+
return home && path.startsWith(home + sep) ? `~${path.slice(home.length)}` : path;
|
|
17680
|
+
}
|
|
17681
|
+
function displayBinPath(home, executable) {
|
|
17682
|
+
return executable ? collapseHome$1(home, executable) : "buddy-agent-harness";
|
|
17683
|
+
}
|
|
17684
|
+
/**
|
|
17685
|
+
* `parseHarnesses` for a command's `--harness`: an unsupported name is a usage error, exit code 2.
|
|
17686
|
+
* The registry stays free of clibuilder, which the library entry treats as an optional peer.
|
|
17687
|
+
*/
|
|
17688
|
+
function parseHarnessOption(value) {
|
|
17689
|
+
try {
|
|
17690
|
+
return parseHarnesses(value);
|
|
17691
|
+
} catch (error) {
|
|
17692
|
+
throw new CliError(error.message, {
|
|
17693
|
+
code: "unsupported-harness",
|
|
17694
|
+
exitCode: exitCodes.usage,
|
|
17695
|
+
help: "Pass --harness as a comma-separated list of the supported names.",
|
|
17696
|
+
cause: error
|
|
17697
|
+
});
|
|
17698
|
+
}
|
|
17699
|
+
}
|
|
17700
|
+
//#endregion
|
|
17701
|
+
//#region src/dep-plugins/dep-plugins.ts
|
|
17702
|
+
/** Generic marketplace names: a second registration under one of these replaces the first, silently. */
|
|
17703
|
+
const GENERIC_NAMES = /* @__PURE__ */ new Set([
|
|
17704
|
+
"deps",
|
|
17705
|
+
"dependencies",
|
|
17706
|
+
"local",
|
|
17707
|
+
"plugins",
|
|
17708
|
+
"marketplace",
|
|
17709
|
+
"plugin",
|
|
17710
|
+
"default"
|
|
17711
|
+
]);
|
|
17712
|
+
function isGenericName(name) {
|
|
17713
|
+
return GENERIC_NAMES.has(name.toLowerCase());
|
|
17714
|
+
}
|
|
17715
|
+
/**
|
|
17716
|
+
* Folds a scope into the name (`@acme/web` → `acme-web`) rather than dropping it — the unscoped
|
|
17717
|
+
* part collides more often.
|
|
17718
|
+
*/
|
|
17719
|
+
function marketplaceName(packageName) {
|
|
17720
|
+
return packageName.replace(/^@/, "").replace(/\//g, "-").replace(/[^a-zA-Z0-9-_.]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "") || "agent-plugins";
|
|
17721
|
+
}
|
|
17722
|
+
/**
|
|
17723
|
+
* Derived in full every run, never merged with what's on disk. Entries are sorted so unchanged
|
|
17724
|
+
* inputs serialize identically, which `--check` relies on.
|
|
17725
|
+
*/
|
|
17726
|
+
function buildCatalog({ name, owner, plugins }) {
|
|
17727
|
+
const entries = [...plugins].sort((a, b) => a.plugin.localeCompare(b.plugin)).map(({ plugin, package: pkg, version, description }) => {
|
|
17728
|
+
const source = {
|
|
17729
|
+
source: "npm",
|
|
17730
|
+
package: pkg,
|
|
17731
|
+
version
|
|
17732
|
+
};
|
|
17733
|
+
return description ? {
|
|
17734
|
+
name: plugin,
|
|
17735
|
+
source,
|
|
17736
|
+
description
|
|
17737
|
+
} : {
|
|
17738
|
+
name: plugin,
|
|
17739
|
+
source
|
|
17740
|
+
};
|
|
17741
|
+
});
|
|
17742
|
+
return {
|
|
17743
|
+
name,
|
|
17744
|
+
owner: { name: owner },
|
|
17745
|
+
description: "Plugins provided by this repository’s dependencies. Generated — do not edit.",
|
|
17746
|
+
plugins: entries
|
|
17747
|
+
};
|
|
17748
|
+
}
|
|
17749
|
+
/** Includes the trailing newline. */
|
|
17750
|
+
function serializeCatalog(catalog) {
|
|
17751
|
+
return `${JSON.stringify(catalog, null, " ")}\n`;
|
|
17752
|
+
}
|
|
17753
|
+
//#endregion
|
|
17754
|
+
//#region src/dep-plugins/resolve.ts
|
|
17755
|
+
function readJson$1(path) {
|
|
17756
|
+
try {
|
|
17757
|
+
return JSON.parse(readFileSync(path, "utf8"));
|
|
17758
|
+
} catch {
|
|
17759
|
+
return;
|
|
17760
|
+
}
|
|
17761
|
+
}
|
|
17762
|
+
/** A manifest lives at the package root — the Agent Plugins Specification's pinned location. */
|
|
17763
|
+
function readDepPlugin(pkg, fromManifest) {
|
|
17764
|
+
const dir = packageDir(pkg, fromManifest);
|
|
17765
|
+
if (!dir) return {
|
|
17766
|
+
ok: false,
|
|
17767
|
+
package: pkg,
|
|
17768
|
+
reason: "unresolved"
|
|
17769
|
+
};
|
|
17770
|
+
const manifest = readJson$1(join(dir, "plugin.json"));
|
|
17771
|
+
if (!manifest || typeof manifest !== "object") return {
|
|
17772
|
+
ok: false,
|
|
17773
|
+
package: pkg,
|
|
17774
|
+
reason: "no-manifest"
|
|
17775
|
+
};
|
|
17776
|
+
const record = manifest;
|
|
17777
|
+
const name = typeof record["name"] === "string" ? record["name"] : void 0;
|
|
17778
|
+
if (!name) return {
|
|
17779
|
+
ok: false,
|
|
17780
|
+
package: pkg,
|
|
17781
|
+
reason: "unnamed"
|
|
17782
|
+
};
|
|
17783
|
+
const installed = readJson$1(join(dir, "package.json"));
|
|
17784
|
+
const version = typeof installed?.version === "string" ? installed.version : void 0;
|
|
17785
|
+
if (!version) return {
|
|
17786
|
+
ok: false,
|
|
17787
|
+
package: pkg,
|
|
17788
|
+
reason: "no-manifest"
|
|
17789
|
+
};
|
|
17790
|
+
const description = typeof record["description"] === "string" ? record["description"] : void 0;
|
|
17791
|
+
return {
|
|
17792
|
+
ok: true,
|
|
17793
|
+
plugin: description ? {
|
|
17794
|
+
package: pkg,
|
|
17795
|
+
plugin: name,
|
|
17796
|
+
version,
|
|
17797
|
+
description
|
|
17798
|
+
} : {
|
|
17799
|
+
package: pkg,
|
|
17800
|
+
plugin: name,
|
|
17801
|
+
version
|
|
17802
|
+
}
|
|
17803
|
+
};
|
|
17804
|
+
}
|
|
17805
|
+
/**
|
|
17806
|
+
* True under Yarn PnP, where packages live in zip archives rather than directories — nothing here
|
|
17807
|
+
* has an installable directory.
|
|
17808
|
+
*/
|
|
17809
|
+
function isPnp() {
|
|
17810
|
+
return Boolean(process.versions["pnp"]);
|
|
17811
|
+
}
|
|
17812
|
+
//#endregion
|
|
17813
|
+
//#region src/dep-plugins/generate.ts
|
|
17814
|
+
/**
|
|
17815
|
+
* Not the repository root's `.claude-plugin/` — that's the repo's own published catalog; merging
|
|
17816
|
+
* would leak dependency plugins into it.
|
|
17817
|
+
*/
|
|
17818
|
+
const CATALOG_DIR = join(".agents", "buddy-agent-harness");
|
|
17819
|
+
const CATALOG_PATH = join(CATALOG_DIR, ".claude-plugin", "marketplace.json");
|
|
17820
|
+
function readManifest(path) {
|
|
17821
|
+
return JSON.parse(readFileSync(path, "utf8"));
|
|
17822
|
+
}
|
|
17823
|
+
function deriveCatalog(root) {
|
|
16435
17824
|
const manifestPath = join(root, "package.json");
|
|
16436
17825
|
const manifest = readManifest(manifestPath);
|
|
16437
17826
|
const plugins = [];
|
|
@@ -16685,327 +18074,78 @@ const depPluginsCommand = command({
|
|
|
16685
18074
|
type: t.boolean().optional()
|
|
16686
18075
|
}
|
|
16687
18076
|
},
|
|
18077
|
+
examples: [{
|
|
18078
|
+
command: "buddy-agent-harness dep-plugins",
|
|
18079
|
+
description: "Write the marketplace catalog for dependency plugins"
|
|
18080
|
+
}, {
|
|
18081
|
+
command: "buddy-agent-harness dep-plugins --check",
|
|
18082
|
+
description: "Fail when the catalog is stale (CI)"
|
|
18083
|
+
}],
|
|
16688
18084
|
run(args) {
|
|
16689
18085
|
try {
|
|
16690
18086
|
const root = args.root ?? process.cwd();
|
|
16691
|
-
const derived = deriveCatalog(root);
|
|
16692
|
-
const outcome = args.check ? catalogStatus(root, derived.catalog) === "unchanged" ? "current" : "would-change" : writeCatalog(root, derived.catalog);
|
|
16693
|
-
const home = homedir();
|
|
16694
|
-
const notes = notesFor(derived);
|
|
16695
|
-
const detected = runtimes.filter((candidate) => candidate.installedOnMachine(home));
|
|
16696
|
-
const harness = [];
|
|
16697
|
-
const actions = [];
|
|
16698
|
-
for (const runtime of detected) {
|
|
16699
|
-
const state = runtime.state(home);
|
|
16700
|
-
const delta = reconcile({
|
|
16701
|
-
catalog: derived.catalog,
|
|
16702
|
-
catalogDir: CATALOG_DIR,
|
|
16703
|
-
root: resolve(root),
|
|
16704
|
-
installed: state.installed,
|
|
16705
|
-
marketplaces: state.marketplaces,
|
|
16706
|
-
scoped: runtime.scoped
|
|
16707
|
-
});
|
|
16708
|
-
if (delta.collision) notes.push(`${runtime.name}: marketplace "${derived.catalog.name}" is already registered against ${delta.collision}. Registering over it replaces the other repository's silently, and the install that follows reports "already installed" — rename this repository's marketplace instead.`);
|
|
16709
|
-
harness.push({
|
|
16710
|
-
runtime: runtime.name,
|
|
16711
|
-
status: delta.actions.length ? `${delta.actions.length} to apply` : "in sync"
|
|
16712
|
-
});
|
|
16713
|
-
for (const action of delta.actions) actions.push({
|
|
16714
|
-
runtime: runtime.name,
|
|
16715
|
-
do: commandFor(runtime, action, derived.catalog.name),
|
|
16716
|
-
subject: action.subject,
|
|
16717
|
-
why: action.detail
|
|
16718
|
-
});
|
|
16719
|
-
}
|
|
16720
|
-
if (derived.plugins.length) notes.push(COPILOT_LIMITATION);
|
|
16721
|
-
const answer = result({
|
|
16722
|
-
catalog: CATALOG_PATH,
|
|
16723
|
-
marketplace: derived.catalog.name,
|
|
16724
|
-
outcome,
|
|
16725
|
-
plugins: derived.plugins.length ? derived.plugins.map(({ plugin, package: pkg, version }) => ({
|
|
16726
|
-
name: plugin,
|
|
16727
|
-
package: pkg,
|
|
16728
|
-
version
|
|
16729
|
-
})) : "0 dependencies ship a plugin — nothing to install",
|
|
16730
|
-
harness: harness.length ? harness : "no supported harness detected on this machine",
|
|
16731
|
-
actions: actions.length ? actions : "0 actions — every detected harness matches the catalog",
|
|
16732
|
-
...notes.length ? { notes } : {}
|
|
16733
|
-
});
|
|
16734
|
-
return outcome === "would-change" ? answerThenFail(answer, this.format) : answer;
|
|
16735
|
-
} catch (error) {
|
|
16736
|
-
return fail(error, "Catalog generation failed.");
|
|
16737
|
-
}
|
|
16738
|
-
}
|
|
16739
|
-
});
|
|
16740
|
-
//#endregion
|
|
16741
|
-
//#region src/diagnose-bridges/directory-files.ts
|
|
16742
|
-
/** A directory that does not exist reads as empty: `doctor` never creates one to look at it. */
|
|
16743
|
-
function filesUnder(directory) {
|
|
16744
|
-
try {
|
|
16745
|
-
return readdirSync(directory, {
|
|
16746
|
-
recursive: true,
|
|
16747
|
-
withFileTypes: true
|
|
16748
|
-
}).filter((entry) => entry.isFile()).map((entry) => relative(directory, join(entry.parentPath, entry.name)).split(sep).join(posix.sep)).sort();
|
|
16749
|
-
} catch {
|
|
16750
|
-
return [];
|
|
16751
|
-
}
|
|
16752
|
-
}
|
|
16753
|
-
//#endregion
|
|
16754
|
-
//#region src/harness-registry/harness-registry.ts
|
|
16755
|
-
/**
|
|
16756
|
-
* Throws on a harness `skillsDirectories` does not confirm: treating it as native would silently
|
|
16757
|
-
* drop a projection it may need.
|
|
16758
|
-
*/
|
|
16759
|
-
function skillsProjection(harness, scope, directories = skillsDirectories(harness)?.[scope]) {
|
|
16760
|
-
const listed = directories ?? [];
|
|
16761
|
-
const [first] = listed;
|
|
16762
|
-
if (first === void 0) throw new Error(`cyber-agentkit records no ${scope} skills directories for ${harness}.`);
|
|
16763
|
-
return listed.includes(".agents/skills") ? {} : { skillsDirectory: first };
|
|
16764
|
-
}
|
|
16765
|
-
/**
|
|
16766
|
-
* The user-scope counterpart of the root `AGENTS.md`, relative to the home directory. No harness
|
|
16767
|
-
* reads it natively; each loads it only through a user-scope `instructionBridge` (E-CC-18, E-CODEX-05,
|
|
16768
|
-
* E-GEM-03, E-COPILOT-04, E-CUR-08).
|
|
16769
|
-
*/
|
|
16770
|
-
const globalInstructions = ".agents/AGENTS.md";
|
|
16771
|
-
/**
|
|
16772
|
-
* Skills directories come from `cyber-agentkit`, except for the harnesses it does not know
|
|
16773
|
-
* (`devin-desktop`, `windsurf`); see `.research/agentic-configuration-standards/` for the rest.
|
|
16774
|
-
*/
|
|
16775
|
-
const harnessRegistry = [
|
|
16776
|
-
{
|
|
16777
|
-
name: "claude-code",
|
|
16778
|
-
project: {
|
|
16779
|
-
detect: ".claude",
|
|
16780
|
-
...skillsProjection("claude-code", "project"),
|
|
16781
|
-
shadowedBy: [
|
|
16782
|
-
"CLAUDE.md",
|
|
16783
|
-
".claude/CLAUDE.md",
|
|
16784
|
-
"CLAUDE.local.md"
|
|
16785
|
-
],
|
|
16786
|
-
mcpConfig: {
|
|
16787
|
-
path: ".mcp.json",
|
|
16788
|
-
key: "mcpServers",
|
|
16789
|
-
format: "json",
|
|
16790
|
-
dialect: "claude-code"
|
|
16791
|
-
},
|
|
16792
|
-
nonstandard: [
|
|
16793
|
-
{
|
|
16794
|
-
path: ".claude/commands",
|
|
16795
|
-
shape: "directory",
|
|
16796
|
-
kind: "command"
|
|
16797
|
-
},
|
|
16798
|
-
{
|
|
16799
|
-
path: ".claude/rules",
|
|
16800
|
-
shape: "directory",
|
|
16801
|
-
kind: "rule"
|
|
16802
|
-
},
|
|
16803
|
-
{
|
|
16804
|
-
path: ".claude/agents",
|
|
16805
|
-
shape: "directory",
|
|
16806
|
-
kind: "subagent"
|
|
16807
|
-
}
|
|
16808
|
-
]
|
|
16809
|
-
},
|
|
16810
|
-
user: {
|
|
16811
|
-
detect: ".claude",
|
|
16812
|
-
...skillsProjection("claude-code", "user"),
|
|
16813
|
-
relocatedBy: "CLAUDE_CONFIG_DIR",
|
|
16814
|
-
emptyRelocatesToStart: true,
|
|
16815
|
-
instructionBridge: {
|
|
16816
|
-
kind: "import",
|
|
16817
|
-
path: ".claude/CLAUDE.md",
|
|
16818
|
-
line: `@~/${globalInstructions}`
|
|
16819
|
-
}
|
|
16820
|
-
}
|
|
16821
|
-
},
|
|
16822
|
-
{
|
|
16823
|
-
name: "cursor",
|
|
16824
|
-
project: {
|
|
16825
|
-
detect: ".cursor",
|
|
16826
|
-
...skillsProjection("cursor", "project"),
|
|
16827
|
-
mcpConfig: {
|
|
16828
|
-
path: ".cursor/mcp.json",
|
|
16829
|
-
key: "mcpServers",
|
|
16830
|
-
format: "json",
|
|
16831
|
-
dialect: "cursor"
|
|
16832
|
-
},
|
|
16833
|
-
nonstandard: [
|
|
16834
|
-
{
|
|
16835
|
-
path: ".cursorrules",
|
|
16836
|
-
shape: "file",
|
|
16837
|
-
kind: "instructions"
|
|
16838
|
-
},
|
|
16839
|
-
{
|
|
16840
|
-
path: ".cursor/rules",
|
|
16841
|
-
shape: "directory",
|
|
16842
|
-
kind: "rule"
|
|
16843
|
-
},
|
|
16844
|
-
{
|
|
16845
|
-
path: ".cursor/commands",
|
|
16846
|
-
shape: "directory",
|
|
16847
|
-
kind: "command"
|
|
16848
|
-
},
|
|
16849
|
-
{
|
|
16850
|
-
path: ".cursor/skills",
|
|
16851
|
-
shape: "directory",
|
|
16852
|
-
kind: "skill"
|
|
16853
|
-
}
|
|
16854
|
-
]
|
|
16855
|
-
},
|
|
16856
|
-
user: {
|
|
16857
|
-
detect: ".cursor",
|
|
16858
|
-
...skillsProjection("cursor", "user")
|
|
16859
|
-
}
|
|
16860
|
-
},
|
|
16861
|
-
{
|
|
16862
|
-
name: "codex",
|
|
16863
|
-
project: {
|
|
16864
|
-
detect: ".codex",
|
|
16865
|
-
...skillsProjection("codex", "project"),
|
|
16866
|
-
mcpConfig: {
|
|
16867
|
-
path: ".codex/config.toml",
|
|
16868
|
-
key: "mcp_servers",
|
|
16869
|
-
format: "toml",
|
|
16870
|
-
dialect: "codex",
|
|
16871
|
-
shared: true
|
|
16872
|
-
},
|
|
16873
|
-
nonstandard: [{
|
|
16874
|
-
path: ".codex/skills",
|
|
16875
|
-
shape: "directory",
|
|
16876
|
-
kind: "skill"
|
|
16877
|
-
}]
|
|
16878
|
-
},
|
|
16879
|
-
user: {
|
|
16880
|
-
detect: ".codex",
|
|
16881
|
-
...skillsProjection("codex", "user"),
|
|
16882
|
-
instructionBridge: {
|
|
16883
|
-
kind: "symlink",
|
|
16884
|
-
path: ".codex/AGENTS.md"
|
|
16885
|
-
},
|
|
16886
|
-
shadowedBy: [".codex/AGENTS.override.md"],
|
|
16887
|
-
relocatedBy: "CODEX_HOME"
|
|
16888
|
-
}
|
|
16889
|
-
},
|
|
16890
|
-
{
|
|
16891
|
-
name: "copilot-cli",
|
|
16892
|
-
project: {
|
|
16893
|
-
detect: ".github/skills",
|
|
16894
|
-
...skillsProjection("copilot-cli", "project"),
|
|
16895
|
-
nonstandard: [
|
|
16896
|
-
{
|
|
16897
|
-
path: ".github/copilot-instructions.md",
|
|
16898
|
-
shape: "file",
|
|
16899
|
-
kind: "instructions"
|
|
16900
|
-
},
|
|
16901
|
-
{
|
|
16902
|
-
path: ".github/instructions",
|
|
16903
|
-
shape: "directory",
|
|
16904
|
-
kind: "instructions"
|
|
16905
|
-
},
|
|
16906
|
-
{
|
|
16907
|
-
path: ".github/skills",
|
|
16908
|
-
shape: "directory",
|
|
16909
|
-
kind: "skill"
|
|
16910
|
-
}
|
|
16911
|
-
]
|
|
16912
|
-
},
|
|
16913
|
-
user: {
|
|
16914
|
-
detect: ".copilot",
|
|
16915
|
-
...skillsProjection("copilot-cli", "user"),
|
|
16916
|
-
relocatedBy: "COPILOT_HOME",
|
|
16917
|
-
instructionBridge: {
|
|
16918
|
-
kind: "symlink",
|
|
16919
|
-
path: ".copilot/copilot-instructions.md"
|
|
16920
|
-
}
|
|
16921
|
-
}
|
|
16922
|
-
},
|
|
16923
|
-
{
|
|
16924
|
-
name: "gemini-cli",
|
|
16925
|
-
project: {
|
|
16926
|
-
detect: ".gemini",
|
|
16927
|
-
...skillsProjection("gemini-cli", "project"),
|
|
16928
|
-
instructionBridge: {
|
|
16929
|
-
kind: "settings-entry",
|
|
16930
|
-
path: ".gemini/settings.json",
|
|
16931
|
-
key: "context.fileName"
|
|
16932
|
-
},
|
|
16933
|
-
mcpConfig: {
|
|
16934
|
-
path: ".gemini/settings.json",
|
|
16935
|
-
key: "mcpServers",
|
|
16936
|
-
format: "json",
|
|
16937
|
-
dialect: "gemini-cli",
|
|
16938
|
-
shared: true
|
|
16939
|
-
},
|
|
16940
|
-
nonstandard: [{
|
|
16941
|
-
path: "GEMINI.md",
|
|
16942
|
-
shape: "file",
|
|
16943
|
-
kind: "instructions"
|
|
16944
|
-
}, {
|
|
16945
|
-
path: ".gemini/skills",
|
|
16946
|
-
shape: "directory",
|
|
16947
|
-
kind: "skill"
|
|
16948
|
-
}]
|
|
16949
|
-
},
|
|
16950
|
-
user: {
|
|
16951
|
-
detect: ".gemini",
|
|
16952
|
-
...skillsProjection("gemini-cli", "user"),
|
|
16953
|
-
instructionBridge: {
|
|
16954
|
-
kind: "symlink",
|
|
16955
|
-
path: ".gemini/GEMINI.md"
|
|
18087
|
+
const derived = deriveCatalog(root);
|
|
18088
|
+
const outcome = args.check ? catalogStatus(root, derived.catalog) === "unchanged" ? "current" : "would-change" : writeCatalog(root, derived.catalog);
|
|
18089
|
+
const home = homedir();
|
|
18090
|
+
const notes = notesFor(derived);
|
|
18091
|
+
const detected = runtimes.filter((candidate) => candidate.installedOnMachine(home));
|
|
18092
|
+
const harness = [];
|
|
18093
|
+
const actions = [];
|
|
18094
|
+
for (const runtime of detected) {
|
|
18095
|
+
const state = runtime.state(home);
|
|
18096
|
+
const delta = reconcile({
|
|
18097
|
+
catalog: derived.catalog,
|
|
18098
|
+
catalogDir: CATALOG_DIR,
|
|
18099
|
+
root: resolve(root),
|
|
18100
|
+
installed: state.installed,
|
|
18101
|
+
marketplaces: state.marketplaces,
|
|
18102
|
+
scoped: runtime.scoped
|
|
18103
|
+
});
|
|
18104
|
+
if (delta.collision) notes.push(`${runtime.name}: marketplace "${derived.catalog.name}" is already registered against ${delta.collision}. Registering over it replaces the other repository's silently, and the install that follows reports "already installed" — rename this repository's marketplace instead.`);
|
|
18105
|
+
harness.push({
|
|
18106
|
+
runtime: runtime.name,
|
|
18107
|
+
status: delta.actions.length ? `${delta.actions.length} to apply` : "in sync"
|
|
18108
|
+
});
|
|
18109
|
+
for (const action of delta.actions) actions.push({
|
|
18110
|
+
runtime: runtime.name,
|
|
18111
|
+
do: commandFor(runtime, action, derived.catalog.name),
|
|
18112
|
+
subject: action.subject,
|
|
18113
|
+
why: action.detail
|
|
18114
|
+
});
|
|
16956
18115
|
}
|
|
18116
|
+
if (derived.plugins.length) notes.push(COPILOT_LIMITATION);
|
|
18117
|
+
const answer = result({
|
|
18118
|
+
catalog: CATALOG_PATH,
|
|
18119
|
+
marketplace: derived.catalog.name,
|
|
18120
|
+
outcome,
|
|
18121
|
+
plugins: derived.plugins.length ? derived.plugins.map(({ plugin, package: pkg, version }) => ({
|
|
18122
|
+
name: plugin,
|
|
18123
|
+
package: pkg,
|
|
18124
|
+
version
|
|
18125
|
+
})) : "0 dependencies ship a plugin — nothing to install",
|
|
18126
|
+
harness: harness.length ? harness : "no supported harness detected on this machine",
|
|
18127
|
+
actions: actions.length ? actions : "0 actions — every detected harness matches the catalog",
|
|
18128
|
+
...notes.length ? { notes } : {}
|
|
18129
|
+
});
|
|
18130
|
+
return outcome === "would-change" ? answerThenFail(answer, this.format) : answer;
|
|
18131
|
+
} catch (error) {
|
|
18132
|
+
return fail(error, "Catalog generation failed.");
|
|
16957
18133
|
}
|
|
16958
|
-
},
|
|
16959
|
-
{
|
|
16960
|
-
name: "devin-desktop",
|
|
16961
|
-
project: { detect: ".devin" }
|
|
16962
|
-
},
|
|
16963
|
-
{
|
|
16964
|
-
name: "windsurf",
|
|
16965
|
-
project: {
|
|
16966
|
-
detect: ".windsurf",
|
|
16967
|
-
skillsDirectory: ".windsurf/skills",
|
|
16968
|
-
nonstandard: [{
|
|
16969
|
-
path: ".windsurfrules",
|
|
16970
|
-
shape: "file",
|
|
16971
|
-
kind: "instructions"
|
|
16972
|
-
}, {
|
|
16973
|
-
path: ".windsurf/rules",
|
|
16974
|
-
shape: "directory",
|
|
16975
|
-
kind: "rule"
|
|
16976
|
-
}]
|
|
16977
|
-
},
|
|
16978
|
-
deprecated: "devin-desktop"
|
|
16979
18134
|
}
|
|
16980
|
-
|
|
16981
|
-
|
|
16982
|
-
|
|
16983
|
-
|
|
16984
|
-
|
|
16985
|
-
/**
|
|
16986
|
-
* An unknown name throws rather than being dropped — silently falling back to the defaults would
|
|
16987
|
-
* misreport what was diagnosed.
|
|
16988
|
-
*/
|
|
16989
|
-
function parseHarnesses(value) {
|
|
16990
|
-
const requested = (value ?? "").split(",").map((name) => name.trim()).filter(Boolean);
|
|
16991
|
-
const unsupported = requested.filter((name) => !isHarnessName(name));
|
|
16992
|
-
if (unsupported.length) throw new Error(`Unsupported harness: ${unsupported.join(", ")}. Supported: ${harnessRegistry.map((harness) => harness.name).join(", ")}.`);
|
|
16993
|
-
return requested.filter(isHarnessName);
|
|
16994
|
-
}
|
|
16995
|
-
/** Enabled whether or not the repository already contains their directories. */
|
|
16996
|
-
const defaultHarnesses = ["claude-code", "cursor"];
|
|
16997
|
-
function directoryExists(path) {
|
|
18135
|
+
});
|
|
18136
|
+
//#endregion
|
|
18137
|
+
//#region src/diagnose-bridges/directory-files.ts
|
|
18138
|
+
/** A directory that does not exist reads as empty: `doctor` never creates one to look at it. */
|
|
18139
|
+
function filesUnder(directory) {
|
|
16998
18140
|
try {
|
|
16999
|
-
return
|
|
18141
|
+
return readdirSync(directory, {
|
|
18142
|
+
recursive: true,
|
|
18143
|
+
withFileTypes: true
|
|
18144
|
+
}).filter((entry) => entry.isFile()).map((entry) => relative(directory, join(entry.parentPath, entry.name)).split(sep).join(posix.sep)).sort();
|
|
17000
18145
|
} catch {
|
|
17001
|
-
return
|
|
18146
|
+
return [];
|
|
17002
18147
|
}
|
|
17003
18148
|
}
|
|
17004
|
-
/** The default harnesses, plus the preferred ones, plus every harness whose directory is present. */
|
|
17005
|
-
function selectHarnesses(root, preferred) {
|
|
17006
|
-
const enabled = /* @__PURE__ */ new Set([...defaultHarnesses, ...preferred]);
|
|
17007
|
-
return harnessRegistry.filter((harness) => enabled.has(harness.name) || directoryExists(join(root, harness.project.detect)));
|
|
17008
|
-
}
|
|
17009
18149
|
//#endregion
|
|
17010
18150
|
//#region src/diagnose-bridges/locator.ts
|
|
17011
18151
|
/** The locator as one string: the finding's `path`, and what a repair names. */
|
|
@@ -19201,7 +20341,7 @@ function diagnoseNonstandard({ root, cli }) {
|
|
|
19201
20341
|
return findings.sort((left, right) => left.path.localeCompare(right.path));
|
|
19202
20342
|
}
|
|
19203
20343
|
//#endregion
|
|
19204
|
-
//#region ../../node_modules/.pnpm/cyber-agentkit@2.
|
|
20344
|
+
//#region ../../node_modules/.pnpm/cyber-agentkit@2.2.0_clibuilder@12.3.0_js-yaml@4.3.2_/node_modules/cyber-agentkit/dist/commands.js
|
|
19205
20345
|
/**
|
|
19206
20346
|
* AXI §10: collapses the home directory to `~` so a path in a report is portable to another
|
|
19207
20347
|
* machine.
|
|
@@ -19214,6 +20354,8 @@ t.boolean().optional();
|
|
|
19214
20354
|
t.string().optional();
|
|
19215
20355
|
t.string();
|
|
19216
20356
|
t.string().optional();
|
|
20357
|
+
/** The command line that reaches the reference commands in `cyber-agentkit`'s own CLI. */
|
|
20358
|
+
const defaultReferencePath = "cyber-agentkit reference";
|
|
19217
20359
|
const rootOption$3 = {
|
|
19218
20360
|
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.",
|
|
19219
20361
|
type: t.string().optional()
|
|
@@ -19366,7 +20508,7 @@ const skillOption = {
|
|
|
19366
20508
|
description: "A skill's source folder in this repository: acts on its `references/<name>.default.md`.",
|
|
19367
20509
|
type: t.string().optional()
|
|
19368
20510
|
};
|
|
19369
|
-
function checkCommand(layersFor) {
|
|
20511
|
+
function checkCommand(layersFor, path = defaultReferencePath) {
|
|
19370
20512
|
return command({
|
|
19371
20513
|
name: "check",
|
|
19372
20514
|
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.",
|
|
@@ -19382,6 +20524,10 @@ function checkCommand(layersFor) {
|
|
|
19382
20524
|
description: "Add every entry checked, with its kind and unsupported sections."
|
|
19383
20525
|
}
|
|
19384
20526
|
},
|
|
20527
|
+
examples: [{
|
|
20528
|
+
command: `${path} check`,
|
|
20529
|
+
description: "Check every reference against its interface"
|
|
20530
|
+
}],
|
|
19385
20531
|
async run(args) {
|
|
19386
20532
|
try {
|
|
19387
20533
|
const home = homedir();
|
|
@@ -19423,7 +20569,7 @@ function checkCommand(layersFor) {
|
|
|
19423
20569
|
}
|
|
19424
20570
|
});
|
|
19425
20571
|
}
|
|
19426
|
-
function createCommand(layersFor) {
|
|
20572
|
+
function createCommand(layersFor, path = defaultReferencePath) {
|
|
19427
20573
|
return command({
|
|
19428
20574
|
name: "create",
|
|
19429
20575
|
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.",
|
|
@@ -19459,6 +20605,10 @@ function createCommand(layersFor) {
|
|
|
19459
20605
|
description: "After the write, add the trace, in which the new file applies."
|
|
19460
20606
|
}
|
|
19461
20607
|
},
|
|
20608
|
+
examples: [{
|
|
20609
|
+
command: `${path} create my-topic`,
|
|
20610
|
+
description: "Start a new reference named my-topic"
|
|
20611
|
+
}],
|
|
19462
20612
|
async run(args) {
|
|
19463
20613
|
const format = this.format;
|
|
19464
20614
|
try {
|
|
@@ -19569,13 +20719,32 @@ function text(customized, shown) {
|
|
|
19569
20719
|
const meta = shown.metadataFile ? ` Its record is in ${shown.metadataFile}.` : "";
|
|
19570
20720
|
return `${shown.path}\n\n${shown.hint}${meta}\n`;
|
|
19571
20721
|
}
|
|
19572
|
-
function
|
|
20722
|
+
function familyReport(customized, args, home) {
|
|
20723
|
+
return compact({
|
|
20724
|
+
family: customized.family,
|
|
20725
|
+
interfaces: customized.interfaces.map(({ interface: id, outcome, reason, result: each }) => compact({
|
|
20726
|
+
interface: id,
|
|
20727
|
+
outcome,
|
|
20728
|
+
reason,
|
|
20729
|
+
...each ? report(each, args, home) : {}
|
|
20730
|
+
})),
|
|
20731
|
+
hint: customized.dryRun ? "Nothing was written. Run again without --dry-run to write it." : "Run `load <interface>` for each composed document."
|
|
20732
|
+
});
|
|
20733
|
+
}
|
|
20734
|
+
function familyText(customized, shown) {
|
|
20735
|
+
return `${customized.interfaces.map(({ interface: id, outcome, reason, result: each }, at) => {
|
|
20736
|
+
if (!each) return `${id}: ${outcome}. ${reason}\n`;
|
|
20737
|
+
const own = shown.interfaces[at];
|
|
20738
|
+
return `${id}: ${outcome}.\n${text(each, own)}`;
|
|
20739
|
+
}).join("\n")}`;
|
|
20740
|
+
}
|
|
20741
|
+
function customizeCommand(layersFor, path = defaultReferencePath) {
|
|
19573
20742
|
return command({
|
|
19574
20743
|
name: "customize",
|
|
19575
20744
|
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.",
|
|
19576
20745
|
arguments: [{
|
|
19577
20746
|
name: "name",
|
|
19578
|
-
description: "Reference name, without the `.md` extension.",
|
|
20747
|
+
description: "Reference name, without the `.md` extension; or a family, `<prefix>.*`, to pick for every interface under the prefix at once (takes --extends or --extends-file, no `sections`).",
|
|
19579
20748
|
type: t.string()
|
|
19580
20749
|
}, {
|
|
19581
20750
|
name: "steps",
|
|
@@ -19634,6 +20803,13 @@ function customizeCommand(layersFor) {
|
|
|
19634
20803
|
description: "After the write, add the trace, in which the written file applies."
|
|
19635
20804
|
}
|
|
19636
20805
|
},
|
|
20806
|
+
examples: [{
|
|
20807
|
+
command: `${path} customize my-topic`,
|
|
20808
|
+
description: "Customize a reference for this project"
|
|
20809
|
+
}, {
|
|
20810
|
+
command: `${path} customize my-skill.aspects.* --extends house-style`,
|
|
20811
|
+
description: "Pick one plugin for every interface under a prefix"
|
|
20812
|
+
}],
|
|
19637
20813
|
async run(args) {
|
|
19638
20814
|
const format = this.format;
|
|
19639
20815
|
try {
|
|
@@ -19657,19 +20833,33 @@ function customizeCommand(layersFor) {
|
|
|
19657
20833
|
base: args.base,
|
|
19658
20834
|
dryRun: args["dry-run"]
|
|
19659
20835
|
};
|
|
20836
|
+
const family = isFamilyName(args.name) || args.name.includes("*") ? parseFamily(args.name) : void 0;
|
|
19660
20837
|
validateWrite({
|
|
19661
20838
|
command: "customize",
|
|
19662
|
-
...request
|
|
20839
|
+
...request,
|
|
20840
|
+
name: family === void 0 || family.invalid !== void 0 ? args.name : family.prefix
|
|
19663
20841
|
});
|
|
19664
20842
|
if (args.steps?.length && args.extends === void 0) throw new ReferenceFailure("options-missing", `${args.steps.join(" ")} follow no --extends; pass --extends before the steps.`);
|
|
19665
20843
|
const layers = await layersFor({
|
|
19666
20844
|
root: args.root,
|
|
19667
20845
|
default: args.default
|
|
19668
20846
|
}, home);
|
|
20847
|
+
if (family !== void 0) {
|
|
20848
|
+
const customized = customizeContribution(layers, {
|
|
20849
|
+
...request,
|
|
20850
|
+
root: layers.root ?? request.root
|
|
20851
|
+
}, { display });
|
|
20852
|
+
if (!("family" in customized)) throw new Error(`${args.name} was customized as a single name, not a family.`);
|
|
20853
|
+
const shown = familyReport(customized, args, home);
|
|
20854
|
+
if (format !== "text") return result(shown);
|
|
20855
|
+
for (const { result: each } of customized.interfaces) writeWarnings(each?.warnings ?? []);
|
|
20856
|
+
return document(familyText(customized, shown));
|
|
20857
|
+
}
|
|
19669
20858
|
const customized = customizeContribution(layers, {
|
|
19670
20859
|
...request,
|
|
19671
20860
|
root: layers.root ?? request.root
|
|
19672
20861
|
}, { display });
|
|
20862
|
+
if ("family" in customized) throw new Error(`${args.name} was customized as a family, not a single name.`);
|
|
19673
20863
|
if (customized.action === "base") {
|
|
19674
20864
|
if (format !== "text") return result(baseReport(customized));
|
|
19675
20865
|
return document(baseText(customized));
|
|
@@ -19702,7 +20892,7 @@ function deleteReport(deleted, home) {
|
|
|
19702
20892
|
options: deleted.after.options
|
|
19703
20893
|
});
|
|
19704
20894
|
}
|
|
19705
|
-
function deleteCommand(layersFor) {
|
|
20895
|
+
function deleteCommand(layersFor, path = defaultReferencePath) {
|
|
19706
20896
|
return command({
|
|
19707
20897
|
name: "delete",
|
|
19708
20898
|
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.",
|
|
@@ -19725,6 +20915,10 @@ function deleteCommand(layersFor) {
|
|
|
19725
20915
|
type: t.boolean().optional()
|
|
19726
20916
|
}
|
|
19727
20917
|
},
|
|
20918
|
+
examples: [{
|
|
20919
|
+
command: `${path} delete my-topic`,
|
|
20920
|
+
description: "Delete your contribution to my-topic"
|
|
20921
|
+
}],
|
|
19728
20922
|
async run(args) {
|
|
19729
20923
|
const format = this.format;
|
|
19730
20924
|
try {
|
|
@@ -19772,13 +20966,40 @@ function parseAccept(value) {
|
|
|
19772
20966
|
} });
|
|
19773
20967
|
return versions.map(Number);
|
|
19774
20968
|
}
|
|
19775
|
-
function
|
|
20969
|
+
function rowsOf(found, trace) {
|
|
20970
|
+
return found.implementations.map(({ pin, description, unsupported, name, tier, version }) => compact(trace ? {
|
|
20971
|
+
pin,
|
|
20972
|
+
description,
|
|
20973
|
+
unsupported,
|
|
20974
|
+
name,
|
|
20975
|
+
tier,
|
|
20976
|
+
version
|
|
20977
|
+
} : {
|
|
20978
|
+
pin,
|
|
20979
|
+
...tier === "plugin" ? {} : { tier },
|
|
20980
|
+
description,
|
|
20981
|
+
unsupported
|
|
20982
|
+
}));
|
|
20983
|
+
}
|
|
20984
|
+
function reportOf(found, trace) {
|
|
20985
|
+
const report = { implementations: rowsOf(found, trace) };
|
|
20986
|
+
if (found.inUse !== void 0) report.inUse = found.inUse;
|
|
20987
|
+
if (trace) Object.assign(report, compact({
|
|
20988
|
+
interface: found.interface,
|
|
20989
|
+
cardinality: found.cardinality,
|
|
20990
|
+
accept: found.accept,
|
|
20991
|
+
excluded: found.excluded,
|
|
20992
|
+
warnings: found.warnings
|
|
20993
|
+
}));
|
|
20994
|
+
return report;
|
|
20995
|
+
}
|
|
20996
|
+
function implementationsCommand(layersFor, path = defaultReferencePath) {
|
|
19776
20997
|
return command({
|
|
19777
20998
|
name: "implementations",
|
|
19778
20999
|
description: "List the implementations of an interface a consumer can load, each with its pin, description, and unsupported sections.",
|
|
19779
21000
|
arguments: [{
|
|
19780
21001
|
name: "id",
|
|
19781
|
-
description: "Interface id, the name of its `<id>.interface` document without the suffix.",
|
|
21002
|
+
description: "Interface id, the name of its `<id>.interface` document without the suffix; or a family, `<prefix>.*`, for every interface under the prefix.",
|
|
19782
21003
|
type: t.string()
|
|
19783
21004
|
}],
|
|
19784
21005
|
options: {
|
|
@@ -19792,36 +21013,34 @@ function implementationsCommand(layersFor) {
|
|
|
19792
21013
|
description: "Add each one's name, tier, and version, the interface's cardinality and accepted versions, and the ones left out with why."
|
|
19793
21014
|
}
|
|
19794
21015
|
},
|
|
21016
|
+
examples: [{
|
|
21017
|
+
command: `${path} implementations my-interface`,
|
|
21018
|
+
description: "List what implements an interface"
|
|
21019
|
+
}, {
|
|
21020
|
+
command: `${path} implementations my-skill.aspects.*`,
|
|
21021
|
+
description: "List, for every interface under a prefix, what implements it and which one is in use"
|
|
21022
|
+
}],
|
|
19795
21023
|
async run(args) {
|
|
19796
21024
|
try {
|
|
19797
21025
|
const accept = parseAccept(args.accept);
|
|
19798
21026
|
const home = homedir();
|
|
19799
|
-
const
|
|
21027
|
+
const options = {
|
|
19800
21028
|
accept,
|
|
19801
21029
|
display: (path) => collapseHome(home, path)
|
|
19802
|
-
}
|
|
19803
|
-
const
|
|
19804
|
-
|
|
19805
|
-
|
|
19806
|
-
|
|
19807
|
-
|
|
19808
|
-
|
|
19809
|
-
|
|
19810
|
-
|
|
19811
|
-
|
|
19812
|
-
|
|
19813
|
-
|
|
19814
|
-
|
|
19815
|
-
})
|
|
19816
|
-
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.` });
|
|
19817
|
-
const report = { implementations: rows };
|
|
19818
|
-
if (args.trace) Object.assign(report, compact({
|
|
19819
|
-
interface: found.interface,
|
|
19820
|
-
cardinality: found.cardinality,
|
|
19821
|
-
accept: found.accept,
|
|
19822
|
-
excluded: found.excluded,
|
|
19823
|
-
warnings: found.warnings
|
|
19824
|
-
}));
|
|
21030
|
+
};
|
|
21031
|
+
const found = referenceImplementations(args.id, await layersFor(args, home), options);
|
|
21032
|
+
if ("family" in found) {
|
|
21033
|
+
const family = found;
|
|
21034
|
+
return result({
|
|
21035
|
+
family: family.family,
|
|
21036
|
+
interfaces: family.interfaces.map((each) => ({
|
|
21037
|
+
...reportOf(each, args.trace),
|
|
21038
|
+
interface: each.interface
|
|
21039
|
+
}))
|
|
21040
|
+
});
|
|
21041
|
+
}
|
|
21042
|
+
const report = reportOf(found, args.trace);
|
|
21043
|
+
if (!report.implementations.length && report.inUse === void 0 && !args.trace) return empty(`0 implementations of ${args.id}`, { help: `Run \`reference implementations ${args.id} --trace\` for the ones left out, with why.` });
|
|
19825
21044
|
return result(report);
|
|
19826
21045
|
} catch (error) {
|
|
19827
21046
|
return asCliError(error, "Implementation lookup failed.");
|
|
@@ -19829,7 +21048,7 @@ function implementationsCommand(layersFor) {
|
|
|
19829
21048
|
}
|
|
19830
21049
|
});
|
|
19831
21050
|
}
|
|
19832
|
-
function listCommand(layersFor) {
|
|
21051
|
+
function listCommand(layersFor, path = defaultReferencePath) {
|
|
19833
21052
|
return command({
|
|
19834
21053
|
name: "list",
|
|
19835
21054
|
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.",
|
|
@@ -19845,6 +21064,13 @@ function listCommand(layersFor) {
|
|
|
19845
21064
|
description: "Add one row per name per layer holding it, with its outcome."
|
|
19846
21065
|
}
|
|
19847
21066
|
},
|
|
21067
|
+
examples: [{
|
|
21068
|
+
command: `${path} list`,
|
|
21069
|
+
description: "List every reference name"
|
|
21070
|
+
}, {
|
|
21071
|
+
command: `${path} list 'project-tracker.*'`,
|
|
21072
|
+
description: "List the names matching a pattern"
|
|
21073
|
+
}],
|
|
19848
21074
|
async run(args) {
|
|
19849
21075
|
try {
|
|
19850
21076
|
const home = homedir();
|
|
@@ -19912,7 +21138,7 @@ function loadText(resolved) {
|
|
|
19912
21138
|
}
|
|
19913
21139
|
return document(resolved.map(tag).join("\n\n"));
|
|
19914
21140
|
}
|
|
19915
|
-
function loadCommand(layersFor) {
|
|
21141
|
+
function loadCommand(layersFor, path = defaultReferencePath) {
|
|
19916
21142
|
return command({
|
|
19917
21143
|
name: "load",
|
|
19918
21144
|
description: "Print one or more references, each composed from its contributions as their extends and merge declare.",
|
|
@@ -19930,6 +21156,13 @@ function loadCommand(layersFor) {
|
|
|
19930
21156
|
},
|
|
19931
21157
|
trace: traceOption
|
|
19932
21158
|
},
|
|
21159
|
+
examples: [{
|
|
21160
|
+
command: `${path} load my-topic`,
|
|
21161
|
+
description: "Print one reference"
|
|
21162
|
+
}, {
|
|
21163
|
+
command: `${path} load my-topic other-topic --format json`,
|
|
21164
|
+
description: "Print several as JSON"
|
|
21165
|
+
}],
|
|
19933
21166
|
async run(args) {
|
|
19934
21167
|
const format = this.format;
|
|
19935
21168
|
try {
|
|
@@ -19970,7 +21203,87 @@ function loadCommand(layersFor) {
|
|
|
19970
21203
|
}
|
|
19971
21204
|
});
|
|
19972
21205
|
}
|
|
19973
|
-
function
|
|
21206
|
+
function rowLine(row) {
|
|
21207
|
+
const where = row.folder === void 0 ? row.tier : `${row.tier} ${row.folder}`;
|
|
21208
|
+
const change = row.manual === void 0 ? `${row.from} → ${row.to}` : `${row.from}: manual: ${row.manual}`;
|
|
21209
|
+
const by = row.by === void 0 ? "" : ` (by ${row.by})`;
|
|
21210
|
+
const user = row.tier === "user" ? "; a user file, which no git history restores" : "";
|
|
21211
|
+
return `${row.path} (${where}): ${row.form}: ${change}${by}${user}`;
|
|
21212
|
+
}
|
|
21213
|
+
function summary(rows, written) {
|
|
21214
|
+
const manual = rows.filter((row) => row.manual !== void 0).length;
|
|
21215
|
+
const applied = rows.length - manual;
|
|
21216
|
+
const left = manual ? ` ${manual} ${manual === 1 ? "needs" : "need"} a person, as each says.` : "";
|
|
21217
|
+
if (written) return `migrated ${applied} ${applied === 1 ? "change" : "changes"}.${left}`;
|
|
21218
|
+
if (!applied) return `nothing to apply.${left}`;
|
|
21219
|
+
return `${applied} ${applied === 1 ? "change" : "changes"} to make; run \`reference migrate --write\` with the same arguments to apply ${applied === 1 ? "it" : "them"}.${left}`;
|
|
21220
|
+
}
|
|
21221
|
+
function migrateCommand(layersFor, path = defaultReferencePath) {
|
|
21222
|
+
return command({
|
|
21223
|
+
name: "migrate",
|
|
21224
|
+
description: "Rewrite reference files still in a form version 1 wrote, and rename files under a name a plugin renamed, in the repo and user tiers and every project section, or in one plugin or skill source folder. Prints the plan; `--write` applies it. Never changes a managed or installed file.",
|
|
21225
|
+
arguments: [{
|
|
21226
|
+
name: "names",
|
|
21227
|
+
description: "Reference names to limit the run to, without the `.md` extension; a file under an older name of one is included.",
|
|
21228
|
+
type: t.array(t.string()).optional()
|
|
21229
|
+
}],
|
|
21230
|
+
options: {
|
|
21231
|
+
root: rootOption$3,
|
|
21232
|
+
plugin: {
|
|
21233
|
+
description: "A plugin's source folder in this repository: migrate only its `references/`, in place of the tiers.",
|
|
21234
|
+
type: t.string().optional()
|
|
21235
|
+
},
|
|
21236
|
+
default: {
|
|
21237
|
+
description: "A skill's source folder in this repository: migrate only its `references/`, in place of the tiers. A default at `references/<name>.md` is renamed only for a name given.",
|
|
21238
|
+
type: t.string().optional()
|
|
21239
|
+
},
|
|
21240
|
+
write: {
|
|
21241
|
+
description: "Apply the changes. It edits `user` files too, which no git history restores.",
|
|
21242
|
+
type: t.boolean().optional()
|
|
21243
|
+
}
|
|
21244
|
+
},
|
|
21245
|
+
examples: [{
|
|
21246
|
+
command: `${path} migrate`,
|
|
21247
|
+
description: "Migrate version 1 reference files"
|
|
21248
|
+
}],
|
|
21249
|
+
async run(args) {
|
|
21250
|
+
const format = this.format;
|
|
21251
|
+
try {
|
|
21252
|
+
const home = homedir();
|
|
21253
|
+
if (args.plugin !== void 0 && args.default !== void 0) throw new ReferenceFailure("options-conflict", "pass one of --plugin and --default.");
|
|
21254
|
+
const layers = await layersFor({ root: args.root }, home);
|
|
21255
|
+
const migrated = migrateReferences(layers, {
|
|
21256
|
+
root: layers.root ?? givenRoot(args.root),
|
|
21257
|
+
home,
|
|
21258
|
+
names: args.names,
|
|
21259
|
+
plugin: args.plugin,
|
|
21260
|
+
default: args.default,
|
|
21261
|
+
write: args.write
|
|
21262
|
+
});
|
|
21263
|
+
const changes = migrated.rows.map((row) => ({
|
|
21264
|
+
...row,
|
|
21265
|
+
path: collapseHome(home, row.path)
|
|
21266
|
+
}));
|
|
21267
|
+
if (!changes.length && !migrated.warnings.length) return empty("0 changes — no reference file is in a version 1 form or under a renamed name", { help: ["Run `reference check` for every warning `load` raises."] });
|
|
21268
|
+
const report = compact({
|
|
21269
|
+
changes,
|
|
21270
|
+
written: migrated.written ? true : void 0,
|
|
21271
|
+
warnings: migrated.warnings
|
|
21272
|
+
});
|
|
21273
|
+
if (format !== "text") return result(report);
|
|
21274
|
+
writeWarnings(migrated.warnings);
|
|
21275
|
+
return document(`${[
|
|
21276
|
+
...changes.map(rowLine),
|
|
21277
|
+
"",
|
|
21278
|
+
summary(changes, migrated.written)
|
|
21279
|
+
].join("\n").trimStart()}\n`);
|
|
21280
|
+
} catch (error) {
|
|
21281
|
+
return asCliError(error, "Reference migration failed.");
|
|
21282
|
+
}
|
|
21283
|
+
}
|
|
21284
|
+
});
|
|
21285
|
+
}
|
|
21286
|
+
function searchCommand(layersFor, path = defaultReferencePath) {
|
|
19974
21287
|
return command({
|
|
19975
21288
|
name: "search",
|
|
19976
21289
|
description: "Find references by name, description, tags, headings, or body, best match first.",
|
|
@@ -19984,16 +21297,29 @@ function searchCommand(layersFor) {
|
|
|
19984
21297
|
trace: {
|
|
19985
21298
|
...traceOption,
|
|
19986
21299
|
description: "Add the tier and plugin or skill of each match."
|
|
21300
|
+
},
|
|
21301
|
+
full: {
|
|
21302
|
+
...fullOption,
|
|
21303
|
+
description: `Show every match, not only the best 10.`
|
|
19987
21304
|
}
|
|
19988
21305
|
},
|
|
21306
|
+
examples: [{
|
|
21307
|
+
command: `${path} search "worktree leases"`,
|
|
21308
|
+
description: "Find references by topic"
|
|
21309
|
+
}],
|
|
19989
21310
|
async run(args) {
|
|
19990
21311
|
const query = args.query.trim();
|
|
19991
21312
|
try {
|
|
19992
21313
|
const home = homedir();
|
|
19993
21314
|
const { matches, defaults } = searchReferences(query, await layersFor(args, home), { display: (path) => collapseHome(home, path) });
|
|
19994
21315
|
if (!matches.length) return empty(`0 references match "${query}"`, { help: [...defaults ? [defaults] : [], "Try other words, or run `reference list` for every name."] });
|
|
19995
|
-
|
|
19996
|
-
|
|
21316
|
+
const shown = truncateList(matches, {
|
|
21317
|
+
limit: 10,
|
|
21318
|
+
full: args.full
|
|
21319
|
+
});
|
|
21320
|
+
const report = compact({
|
|
21321
|
+
count: shown.truncated ? shown.count : void 0,
|
|
21322
|
+
references: shown.items.map(({ name, description, match, folder, tier, plugin, skill, loadedBy }) => compact(args.trace ? {
|
|
19997
21323
|
name,
|
|
19998
21324
|
description,
|
|
19999
21325
|
match,
|
|
@@ -20010,14 +21336,15 @@ function searchCommand(layersFor) {
|
|
|
20010
21336
|
loadedBy
|
|
20011
21337
|
})),
|
|
20012
21338
|
defaults
|
|
20013
|
-
})
|
|
21339
|
+
});
|
|
21340
|
+
return shown.truncated ? result(report, { help: [`Run \`reference search ${JSON.stringify(query)} --full\` for every match.`] }) : result(report);
|
|
20014
21341
|
} catch (error) {
|
|
20015
21342
|
return asCliError(error, "Reference search failed.");
|
|
20016
21343
|
}
|
|
20017
21344
|
}
|
|
20018
21345
|
});
|
|
20019
21346
|
}
|
|
20020
|
-
function whereCommand(layersFor) {
|
|
21347
|
+
function whereCommand(layersFor, path = defaultReferencePath) {
|
|
20021
21348
|
return command({
|
|
20022
21349
|
name: "where",
|
|
20023
21350
|
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.",
|
|
@@ -20038,6 +21365,10 @@ function whereCommand(layersFor) {
|
|
|
20038
21365
|
description: "Add every layer checked, with its outcome."
|
|
20039
21366
|
}
|
|
20040
21367
|
},
|
|
21368
|
+
examples: [{
|
|
21369
|
+
command: `${path} where my-topic`,
|
|
21370
|
+
description: "Show which layers hold my-topic"
|
|
21371
|
+
}],
|
|
20041
21372
|
async run(args) {
|
|
20042
21373
|
const format = this.format;
|
|
20043
21374
|
try {
|
|
@@ -20072,25 +21403,26 @@ function whereCommand(layersFor) {
|
|
|
20072
21403
|
});
|
|
20073
21404
|
}
|
|
20074
21405
|
/** A factory, so each host CLI names itself as the plugin whose own references come first. */
|
|
20075
|
-
function createReferenceCommands({ plugin } = {}) {
|
|
21406
|
+
function createReferenceCommands({ plugin, path = defaultReferencePath } = {}) {
|
|
20076
21407
|
const layersFor = layerSource(plugin);
|
|
20077
21408
|
return {
|
|
20078
|
-
load: loadCommand(layersFor),
|
|
20079
|
-
list: listCommand(layersFor),
|
|
20080
|
-
search: searchCommand(layersFor),
|
|
20081
|
-
where: whereCommand(layersFor),
|
|
20082
|
-
create: createCommand(layersFor),
|
|
20083
|
-
customize: customizeCommand(layersFor),
|
|
20084
|
-
delete: deleteCommand(layersFor),
|
|
20085
|
-
|
|
20086
|
-
|
|
21409
|
+
load: loadCommand(layersFor, path),
|
|
21410
|
+
list: listCommand(layersFor, path),
|
|
21411
|
+
search: searchCommand(layersFor, path),
|
|
21412
|
+
where: whereCommand(layersFor, path),
|
|
21413
|
+
create: createCommand(layersFor, path),
|
|
21414
|
+
customize: customizeCommand(layersFor, path),
|
|
21415
|
+
delete: deleteCommand(layersFor, path),
|
|
21416
|
+
migrate: migrateCommand(layersFor, path),
|
|
21417
|
+
check: checkCommand(layersFor, path),
|
|
21418
|
+
implementations: implementationsCommand(layersFor, path)
|
|
20087
21419
|
};
|
|
20088
21420
|
}
|
|
20089
21421
|
function createReferenceCommand(options = {}) {
|
|
20090
|
-
const { load, list, search, where, create, customize, delete: remove, check, implementations } = createReferenceCommands(options);
|
|
21422
|
+
const { load, list, search, where, create, customize, delete: remove, migrate, check, implementations } = createReferenceCommands(options);
|
|
20091
21423
|
return command({
|
|
20092
21424
|
name: "reference",
|
|
20093
|
-
description: "Read reference documents by name, composed from the managed, repo, user, plugin, and default tiers,
|
|
21425
|
+
description: "Read reference documents by name, composed from the managed, repo, user, plugin, and default tiers, create, customize, or delete a contribution, and migrate version 1 files.",
|
|
20094
21426
|
commands: [
|
|
20095
21427
|
load,
|
|
20096
21428
|
list,
|
|
@@ -20099,6 +21431,7 @@ function createReferenceCommand(options = {}) {
|
|
|
20099
21431
|
create,
|
|
20100
21432
|
customize,
|
|
20101
21433
|
remove,
|
|
21434
|
+
migrate,
|
|
20102
21435
|
implementations,
|
|
20103
21436
|
check
|
|
20104
21437
|
]
|
|
@@ -20256,10 +21589,13 @@ function referenceLayers({ packageRoot = dirname(packageGovernancesDir()), ...op
|
|
|
20256
21589
|
}
|
|
20257
21590
|
});
|
|
20258
21591
|
}
|
|
20259
|
-
const referenceCommand = createReferenceCommand({
|
|
20260
|
-
|
|
20261
|
-
|
|
20262
|
-
|
|
21592
|
+
const referenceCommand = createReferenceCommand({
|
|
21593
|
+
plugin: {
|
|
21594
|
+
name: PACKAGE_PLUGIN,
|
|
21595
|
+
root: dirname(packageGovernancesDir())
|
|
21596
|
+
},
|
|
21597
|
+
path: "buddy-agent-harness reference"
|
|
21598
|
+
});
|
|
20263
21599
|
//#endregion
|
|
20264
21600
|
//#region src/skill-projection/skill-projection.ts
|
|
20265
21601
|
function skillNames(skillsDirectory) {
|
|
@@ -20974,64 +22310,77 @@ function buildDoctorReport(bin, result, configuration = [], held = []) {
|
|
|
20974
22310
|
help: [...new Map(findings.map((finding) => [`${finding.repair.command}\u0000${finding.repair.instruction}`, finding.repair])).values()]
|
|
20975
22311
|
};
|
|
20976
22312
|
}
|
|
20977
|
-
const
|
|
20978
|
-
|
|
20979
|
-
|
|
20980
|
-
|
|
20981
|
-
root: {
|
|
20982
|
-
description: "Repository or package directory. Defaults to the current directory.",
|
|
20983
|
-
type: t.string().optional()
|
|
20984
|
-
},
|
|
20985
|
-
harness: {
|
|
20986
|
-
description: "Comma-separated harnesses to check in addition to Claude Code and Cursor, e.g. codex,gemini-cli.",
|
|
20987
|
-
type: t.string().optional()
|
|
20988
|
-
}
|
|
22313
|
+
const doctorOptions = {
|
|
22314
|
+
root: {
|
|
22315
|
+
description: "Repository or package directory. Defaults to the current directory.",
|
|
22316
|
+
type: t.string().optional()
|
|
20989
22317
|
},
|
|
20990
|
-
|
|
20991
|
-
|
|
20992
|
-
|
|
20993
|
-
|
|
20994
|
-
|
|
20995
|
-
|
|
22318
|
+
harness: {
|
|
22319
|
+
description: "Comma-separated harnesses to check in addition to Claude Code and Cursor, e.g. codex,gemini-cli.",
|
|
22320
|
+
type: t.string().optional()
|
|
22321
|
+
}
|
|
22322
|
+
};
|
|
22323
|
+
const doctorDescription = "Report whether this repository's harness bridges still resolve — skills into .agents/skills, instructions into AGENTS.md — and whether each harness's user-scope file loads ~/.agents/AGENTS.md. Read-only.";
|
|
22324
|
+
/** The live report `doctor` answers with, shared with the bare invocation of the CLI. */
|
|
22325
|
+
async function diagnoseReport(args) {
|
|
22326
|
+
try {
|
|
22327
|
+
const harnesses = parseHarnessOption(args.harness);
|
|
22328
|
+
const root = args.root ?? process.cwd();
|
|
22329
|
+
const home = homedir();
|
|
22330
|
+
const diagnosis = diagnoseBridges({
|
|
22331
|
+
root,
|
|
22332
|
+
...harnesses.length ? { harnesses } : {},
|
|
22333
|
+
cli: commandInvocation,
|
|
22334
|
+
home,
|
|
22335
|
+
env: process.env
|
|
22336
|
+
});
|
|
22337
|
+
const git = new GitBridgeState(root);
|
|
22338
|
+
const configuration = [
|
|
22339
|
+
...diagnoseConfiguration({
|
|
20996
22340
|
root,
|
|
20997
|
-
|
|
20998
|
-
cli: commandInvocation
|
|
20999
|
-
|
|
21000
|
-
|
|
21001
|
-
});
|
|
21002
|
-
const git = new GitBridgeState(root);
|
|
21003
|
-
const configuration = [
|
|
21004
|
-
...diagnoseConfiguration({
|
|
21005
|
-
root,
|
|
21006
|
-
git,
|
|
21007
|
-
cli: commandInvocation
|
|
21008
|
-
}),
|
|
21009
|
-
...diagnoseMcp({
|
|
21010
|
-
root,
|
|
21011
|
-
git,
|
|
21012
|
-
cli: commandInvocation
|
|
21013
|
-
}),
|
|
21014
|
-
...diagnoseNonstandard({
|
|
21015
|
-
root,
|
|
21016
|
-
cli: commandInvocation
|
|
21017
|
-
})
|
|
21018
|
-
];
|
|
21019
|
-
const { layers: rows } = listReferences(await referenceLayers({
|
|
22341
|
+
git,
|
|
22342
|
+
cli: commandInvocation
|
|
22343
|
+
}),
|
|
22344
|
+
...diagnoseMcp({
|
|
21020
22345
|
root,
|
|
21021
|
-
|
|
21022
|
-
|
|
21023
|
-
|
|
21024
|
-
|
|
21025
|
-
|
|
21026
|
-
|
|
21027
|
-
|
|
21028
|
-
|
|
21029
|
-
|
|
21030
|
-
|
|
21031
|
-
|
|
21032
|
-
|
|
21033
|
-
|
|
21034
|
-
}
|
|
22346
|
+
git,
|
|
22347
|
+
cli: commandInvocation
|
|
22348
|
+
}),
|
|
22349
|
+
...diagnoseNonstandard({
|
|
22350
|
+
root,
|
|
22351
|
+
cli: commandInvocation
|
|
22352
|
+
})
|
|
22353
|
+
];
|
|
22354
|
+
const { layers: rows } = listReferences(await referenceLayers({
|
|
22355
|
+
root,
|
|
22356
|
+
home,
|
|
22357
|
+
platform: process.platform,
|
|
22358
|
+
env: process.env
|
|
22359
|
+
}));
|
|
22360
|
+
const held = rows.filter(({ tier }) => tier !== "plugin").map(({ name, tier, path, outcome }) => ({
|
|
22361
|
+
name,
|
|
22362
|
+
tier,
|
|
22363
|
+
path: collapseHome$1(home, path),
|
|
22364
|
+
status: outcome
|
|
22365
|
+
}));
|
|
22366
|
+
return buildDoctorReport(displayBinPath(home, process.argv[1]), diagnosis, configuration, held);
|
|
22367
|
+
} catch (error) {
|
|
22368
|
+
return fail(error, "Harness diagnosis failed.");
|
|
22369
|
+
}
|
|
22370
|
+
}
|
|
22371
|
+
const doctorCommand = command({
|
|
22372
|
+
name: "doctor",
|
|
22373
|
+
description: doctorDescription,
|
|
22374
|
+
options: doctorOptions,
|
|
22375
|
+
examples: [{
|
|
22376
|
+
command: "buddy-agent-harness doctor",
|
|
22377
|
+
description: "Check the current repository"
|
|
22378
|
+
}, {
|
|
22379
|
+
command: "buddy-agent-harness doctor --harness codex,gemini-cli",
|
|
22380
|
+
description: "Also check Codex and Gemini CLI"
|
|
22381
|
+
}],
|
|
22382
|
+
async run(args) {
|
|
22383
|
+
return result(await diagnoseReport(args));
|
|
21035
22384
|
}
|
|
21036
22385
|
});
|
|
21037
22386
|
//#endregion
|
|
@@ -21066,6 +22415,10 @@ const governanceCommand = command({
|
|
|
21066
22415
|
root: rootOption,
|
|
21067
22416
|
"overrides-only": overridesOnlyOption
|
|
21068
22417
|
},
|
|
22418
|
+
examples: [{
|
|
22419
|
+
command: "buddy-agent-harness governance list",
|
|
22420
|
+
description: "List the governances the layers hold"
|
|
22421
|
+
}],
|
|
21069
22422
|
run(args) {
|
|
21070
22423
|
process.stderr.write(GOVERNANCE_DEPRECATED);
|
|
21071
22424
|
try {
|
|
@@ -21100,16 +22453,21 @@ const governanceCommand = command({
|
|
|
21100
22453
|
root: rootOption,
|
|
21101
22454
|
"overrides-only": overridesOnlyOption
|
|
21102
22455
|
},
|
|
22456
|
+
examples: [{
|
|
22457
|
+
command: "buddy-agent-harness governance show skill-design",
|
|
22458
|
+
description: "Print one governance document"
|
|
22459
|
+
}],
|
|
21103
22460
|
run(args) {
|
|
21104
22461
|
const format = this.format;
|
|
21105
22462
|
process.stderr.write(GOVERNANCE_DEPRECATED);
|
|
21106
22463
|
try {
|
|
21107
22464
|
const name = parseGovernanceName(args.name);
|
|
21108
22465
|
const found = resolveGovernance(name, layersFor(args));
|
|
21109
|
-
if (!found) {
|
|
21110
|
-
|
|
21111
|
-
|
|
21112
|
-
|
|
22466
|
+
if (!found) throw new CliError(args["overrides-only"] ? `no override for governance "${name}" in the project, user, or machine-wide layer.` : `no governance named "${name}" in any layer.`, {
|
|
22467
|
+
code: "governance-not-found",
|
|
22468
|
+
details: { name },
|
|
22469
|
+
help: ref("reference list", {}, "see what is available")
|
|
22470
|
+
});
|
|
21113
22471
|
const report = {
|
|
21114
22472
|
name: found.name,
|
|
21115
22473
|
scope: found.scope,
|
|
@@ -21185,9 +22543,16 @@ const initCommand = command({
|
|
|
21185
22543
|
type: t.string().optional()
|
|
21186
22544
|
}
|
|
21187
22545
|
},
|
|
22546
|
+
examples: [{
|
|
22547
|
+
command: "buddy-agent-harness init",
|
|
22548
|
+
description: "Set up skills for Claude Code and Cursor"
|
|
22549
|
+
}, {
|
|
22550
|
+
command: "buddy-agent-harness init --harness codex --copy",
|
|
22551
|
+
description: "Also enable Codex, copying skills instead of linking"
|
|
22552
|
+
}],
|
|
21188
22553
|
run(args) {
|
|
21189
22554
|
try {
|
|
21190
|
-
const harnesses =
|
|
22555
|
+
const harnesses = parseHarnessOption(args.harness);
|
|
21191
22556
|
return result(initializeHarnesses({
|
|
21192
22557
|
root: args.root ?? process.cwd(),
|
|
21193
22558
|
...harnesses.length ? { harnesses } : {},
|
|
@@ -23763,6 +25128,13 @@ const mcpReconcileCommand = command({
|
|
|
23763
25128
|
type: t.array(t.string()).optional()
|
|
23764
25129
|
}
|
|
23765
25130
|
},
|
|
25131
|
+
examples: [{
|
|
25132
|
+
command: "buddy-agent-harness mcp reconcile",
|
|
25133
|
+
description: "List harness-side changes the golden set lacks"
|
|
25134
|
+
}, {
|
|
25135
|
+
command: "buddy-agent-harness mcp reconcile --accept <path>",
|
|
25136
|
+
description: "Import one approved field"
|
|
25137
|
+
}],
|
|
23766
25138
|
run(args) {
|
|
23767
25139
|
try {
|
|
23768
25140
|
const root = args.root ?? process.cwd();
|
|
@@ -23774,19 +25146,17 @@ const mcpReconcileCommand = command({
|
|
|
23774
25146
|
});
|
|
23775
25147
|
if (plan.kind === "unreadable") {
|
|
23776
25148
|
const at = plan.position ? ` at line ${plan.position.line}, column ${plan.position.column}` : "";
|
|
23777
|
-
|
|
23778
|
-
|
|
23779
|
-
|
|
23780
|
-
|
|
23781
|
-
|
|
23782
|
-
|
|
23783
|
-
|
|
23784
|
-
|
|
23785
|
-
|
|
23786
|
-
mode: "nothing to reconcile into",
|
|
23787
|
-
fields: "0 fields",
|
|
23788
|
-
record: "not written"
|
|
25149
|
+
throw new CliError(`${goldenSetPath} does not parse${at}.`, {
|
|
25150
|
+
code: "golden-set-unreadable",
|
|
25151
|
+
help: `Fix the TOML syntax in ${goldenSetPath}, then re-run.`
|
|
25152
|
+
});
|
|
25153
|
+
}
|
|
25154
|
+
if (plan.kind === "rejected") throw new CliError("nothing written.", {
|
|
25155
|
+
code: "accept-rejected",
|
|
25156
|
+
details: { reasons: plan.reasons },
|
|
25157
|
+
help: ref("mcp reconcile", {}, "list the fields that can be approved")
|
|
23789
25158
|
});
|
|
25159
|
+
if (plan.kind === "absent") return empty(`0 servers — no golden set at ${goldenSetPath}`, { help: `Create ${goldenSetPath} first; there is nothing to reconcile into.` });
|
|
23790
25160
|
return result({
|
|
23791
25161
|
golden: goldenSetPath,
|
|
23792
25162
|
mode: plan.written ? "written — only the fields named by --accept" : "dry run — nothing written; re-run with --accept <path> for each approved field",
|
|
@@ -24836,6 +26206,13 @@ const mcpCommand = command({
|
|
|
24836
26206
|
type: t.boolean().optional()
|
|
24837
26207
|
}
|
|
24838
26208
|
},
|
|
26209
|
+
examples: [{
|
|
26210
|
+
command: "buddy-agent-harness mcp project",
|
|
26211
|
+
description: "Preview what would be projected"
|
|
26212
|
+
}, {
|
|
26213
|
+
command: "buddy-agent-harness mcp project --write",
|
|
26214
|
+
description: "Project the golden set into each harness"
|
|
26215
|
+
}],
|
|
24839
26216
|
run(args) {
|
|
24840
26217
|
try {
|
|
24841
26218
|
const root = args.root ?? process.cwd();
|
|
@@ -24847,15 +26224,12 @@ const mcpCommand = command({
|
|
|
24847
26224
|
});
|
|
24848
26225
|
if (plan.kind === "unreadable") {
|
|
24849
26226
|
const at = plan.position ? ` at line ${plan.position.line}, column ${plan.position.column}` : "";
|
|
24850
|
-
|
|
24851
|
-
|
|
26227
|
+
throw new CliError(`${goldenSetPath} does not parse${at}.`, {
|
|
26228
|
+
code: "golden-set-unreadable",
|
|
26229
|
+
help: `Fix the TOML syntax in ${goldenSetPath}, then re-run.`
|
|
26230
|
+
});
|
|
24852
26231
|
}
|
|
24853
|
-
if (plan.kind === "absent") return
|
|
24854
|
-
golden: `0 servers — no golden set at ${goldenSetPath}`,
|
|
24855
|
-
mode: "nothing to project",
|
|
24856
|
-
actions: "0 changes",
|
|
24857
|
-
record: "not written"
|
|
24858
|
-
});
|
|
26232
|
+
if (plan.kind === "absent") return empty(`0 servers — no golden set at ${goldenSetPath}`, { help: [`Create ${goldenSetPath} with the MCP servers to project.`, ref("mcp project", {}, "preview the projection")] });
|
|
24859
26233
|
return result({
|
|
24860
26234
|
golden: goldenSetPath,
|
|
24861
26235
|
mode: write ? "written" : "dry run — nothing written; re-run with --write to apply",
|
|
@@ -24892,6 +26266,19 @@ function app(format) {
|
|
|
24892
26266
|
version,
|
|
24893
26267
|
description: rootCommand.description,
|
|
24894
26268
|
format
|
|
26269
|
+
}).default({
|
|
26270
|
+
description: "Report whether this repository's harness bridges resolve (the same as `doctor`).",
|
|
26271
|
+
options: doctorCommand.options,
|
|
26272
|
+
async run(args) {
|
|
26273
|
+
const { bin: _bin, ...report } = await diagnoseReport({
|
|
26274
|
+
root: args["root"],
|
|
26275
|
+
harness: args["harness"]
|
|
26276
|
+
});
|
|
26277
|
+
return result({
|
|
26278
|
+
...homeHeader({ description: rootCommand.description }),
|
|
26279
|
+
...report
|
|
26280
|
+
});
|
|
26281
|
+
}
|
|
24895
26282
|
}).command(first));
|
|
24896
26283
|
}
|
|
24897
26284
|
const textByDefault = {
|