buddy-agent-harness 1.0.1 → 1.2.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 +44 -0
- package/dist/cli.mjs +2166 -702
- package/dist/index.d.mts +10 -3
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +597 -431
- 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 +8 -6
- package/package.json +5 -4
- package/plugin.json +1 -1
- package/skills/doctor-buddy-agent-harness/README.md +1 -1
- package/skills/doctor-buddy-agent-harness/SKILL.md +9 -2
- package/skills/doctor-buddy-agent-harness/references/worktrees.md +17 -0
- package/skills/doctor-buddy-agent-harness/scripts/doctor.mjs +74 -67
- package/skills/enhance/SKILL.md +18 -10
- package/skills/enhance/references/agent-worktrees.md +59 -0
- package/skills/enhance/references/list-identifiers.history.md +9 -1
- package/skills/enhance/references/list-identifiers.md +6 -9
- package/skills/enhance/references/references-as-links.history.md +9 -0
- package/skills/enhance/references/references-as-links.md +76 -0
- package/skills/enhance/scripts/doctor.mjs +74 -67
- package/skills/init-buddy-agent-harness/SKILL.md +8 -5
- package/skills/init-buddy-agent-harness/references/agent-worktrees.md +59 -0
- 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/README.md +2 -2
- package/skills/repair/SKILL.md +3 -3
- 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-guidance.ts +66 -1
- package/src/diagnose-bridges/doctor.command.ts +69 -49
- package/src/diagnose-bridges/git-bridge-state.ts +5 -0
- package/src/diagnose-worktrees/diagnose-worktrees.ts +69 -0
- package/src/governance-overrides/governance.command.ts +13 -8
- package/src/index.ts +1 -0
- 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("");
|
|
@@ -13590,7 +14110,7 @@ function gitIgnores(root, path) {
|
|
|
13590
14110
|
}
|
|
13591
14111
|
/** One answer per discovery, so a run resolving many names reads each `package.json` once. */
|
|
13592
14112
|
const found = /* @__PURE__ */ new WeakMap();
|
|
13593
|
-
function readJson$
|
|
14113
|
+
function readJson$3(path) {
|
|
13594
14114
|
try {
|
|
13595
14115
|
return JSON.parse(readFileSync(path, "utf8"));
|
|
13596
14116
|
} catch {
|
|
@@ -13601,7 +14121,7 @@ function declared(top, folders) {
|
|
|
13601
14121
|
const plugins = [];
|
|
13602
14122
|
for (const folder of folders) {
|
|
13603
14123
|
const manifestPath = join(top, ...folder.split("/"), "package.json");
|
|
13604
|
-
const manifest = readJson$
|
|
14124
|
+
const manifest = readJson$3(manifestPath);
|
|
13605
14125
|
for (const pkg of declaredDependencies(manifest)) {
|
|
13606
14126
|
const dir = packageDir(pkg, manifestPath);
|
|
13607
14127
|
const plugin = dir && pluginManifestName(dir);
|
|
@@ -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,7 +15099,19 @@ 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
|
-
/**
|
|
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. */
|
|
14479
15115
|
function skillHoldings(source) {
|
|
14480
15116
|
return (Array.isArray(source) ? [] : source.skills?.skills ?? []).flatMap((skill) => layerNames(defaultLayer(skill.folder)).map((name) => ({
|
|
14481
15117
|
name,
|
|
@@ -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);
|
|
16156
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);
|
|
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
|
|
@@ -16306,7 +17388,314 @@ function whereReference(input, source, { root, display = (path) => path, in: fol
|
|
|
16306
17388
|
if (hider) file.hiddenBy = hiderLabel(hider, display);
|
|
16307
17389
|
report.files.push(file);
|
|
16308
17390
|
}
|
|
16309
|
-
return report;
|
|
17391
|
+
return report;
|
|
17392
|
+
}
|
|
17393
|
+
//#endregion
|
|
17394
|
+
//#region src/harness-registry/harness-registry.ts
|
|
17395
|
+
/**
|
|
17396
|
+
* Throws on a harness `skillsDirectories` does not confirm: treating it as native would silently
|
|
17397
|
+
* drop a projection it may need.
|
|
17398
|
+
*/
|
|
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 };
|
|
17404
|
+
}
|
|
17405
|
+
/**
|
|
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).
|
|
17409
|
+
*/
|
|
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
|
+
}
|
|
17460
|
+
}
|
|
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);
|
|
17624
|
+
}
|
|
17625
|
+
/**
|
|
17626
|
+
* An unknown name throws rather than being dropped — silently falling back to the defaults would
|
|
17627
|
+
* misreport what was diagnosed.
|
|
17628
|
+
*/
|
|
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)));
|
|
17648
|
+
}
|
|
17649
|
+
//#endregion
|
|
17650
|
+
//#region src/command-output/command-output.ts
|
|
17651
|
+
/**
|
|
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.
|
|
17655
|
+
*/
|
|
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
|
+
});
|
|
17664
|
+
}
|
|
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
|
+
}
|
|
16310
17699
|
}
|
|
16311
17700
|
//#endregion
|
|
16312
17701
|
//#region src/dep-plugins/dep-plugins.ts
|
|
@@ -16363,7 +17752,7 @@ function serializeCatalog(catalog) {
|
|
|
16363
17752
|
}
|
|
16364
17753
|
//#endregion
|
|
16365
17754
|
//#region src/dep-plugins/resolve.ts
|
|
16366
|
-
function readJson$
|
|
17755
|
+
function readJson$2(path) {
|
|
16367
17756
|
try {
|
|
16368
17757
|
return JSON.parse(readFileSync(path, "utf8"));
|
|
16369
17758
|
} catch {
|
|
@@ -16378,7 +17767,7 @@ function readDepPlugin(pkg, fromManifest) {
|
|
|
16378
17767
|
package: pkg,
|
|
16379
17768
|
reason: "unresolved"
|
|
16380
17769
|
};
|
|
16381
|
-
const manifest = readJson$
|
|
17770
|
+
const manifest = readJson$2(join(dir, "plugin.json"));
|
|
16382
17771
|
if (!manifest || typeof manifest !== "object") return {
|
|
16383
17772
|
ok: false,
|
|
16384
17773
|
package: pkg,
|
|
@@ -16391,7 +17780,7 @@ function readDepPlugin(pkg, fromManifest) {
|
|
|
16391
17780
|
package: pkg,
|
|
16392
17781
|
reason: "unnamed"
|
|
16393
17782
|
};
|
|
16394
|
-
const installed = readJson$
|
|
17783
|
+
const installed = readJson$2(join(dir, "package.json"));
|
|
16395
17784
|
const version = typeof installed?.version === "string" ? installed.version : void 0;
|
|
16396
17785
|
if (!version) return {
|
|
16397
17786
|
ok: false,
|
|
@@ -16484,7 +17873,7 @@ function writeCatalog(root, catalog) {
|
|
|
16484
17873
|
//#endregion
|
|
16485
17874
|
//#region src/dep-plugins/harness-plugins.ts
|
|
16486
17875
|
const COPILOT_LIMITATION = "Copilot CLI rejects an npm marketplace source, so it cannot install from this catalog. It needs a path-sourced catalog, which cannot reach node_modules from a subdirectory.";
|
|
16487
|
-
function readJson(path) {
|
|
17876
|
+
function readJson$1(path) {
|
|
16488
17877
|
try {
|
|
16489
17878
|
return JSON.parse(readFileSync(path, "utf8"));
|
|
16490
17879
|
} catch {
|
|
@@ -16502,7 +17891,7 @@ function claudeConfigDir(home, env = process.env) {
|
|
|
16502
17891
|
return env["CLAUDE_CONFIG_DIR"] || join(home, ".claude");
|
|
16503
17892
|
}
|
|
16504
17893
|
function readClaudeState(configDir) {
|
|
16505
|
-
const installedFile = readJson(join(configDir, "plugins", "installed_plugins.json"));
|
|
17894
|
+
const installedFile = readJson$1(join(configDir, "plugins", "installed_plugins.json"));
|
|
16506
17895
|
const installed = [];
|
|
16507
17896
|
for (const [id, entries] of Object.entries(installedFile?.plugins ?? {})) {
|
|
16508
17897
|
if (!Array.isArray(entries)) continue;
|
|
@@ -16525,7 +17914,7 @@ function readClaudeState(configDir) {
|
|
|
16525
17914
|
});
|
|
16526
17915
|
}
|
|
16527
17916
|
}
|
|
16528
|
-
const knownFile = readJson(join(configDir, "plugins", "known_marketplaces.json"));
|
|
17917
|
+
const knownFile = readJson$1(join(configDir, "plugins", "known_marketplaces.json"));
|
|
16529
17918
|
const marketplaces = [];
|
|
16530
17919
|
for (const [name, value] of Object.entries(knownFile ?? {})) marketplaces.push({
|
|
16531
17920
|
name,
|
|
@@ -16685,6 +18074,13 @@ 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();
|
|
@@ -16727,285 +18123,29 @@ const depPluginsCommand = command({
|
|
|
16727
18123
|
package: pkg,
|
|
16728
18124
|
version
|
|
16729
18125
|
})) : "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"
|
|
16956
|
-
}
|
|
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. */
|
|
@@ -17036,6 +18176,7 @@ const initSkillInvocation = `/buddy-agent-harness:init-buddy-agent-harness`;
|
|
|
17036
18176
|
*/
|
|
17037
18177
|
const pnpmSkillRef = (invocation) => `\`${invocation}\` (pnpm install: \`pnpm-${commandInvocation}-${invocation.slice(invocation.indexOf(":") + 1)}\`)`;
|
|
17038
18178
|
const initSkillRef = pnpmSkillRef(initSkillInvocation);
|
|
18179
|
+
const enhanceSkillRef = pnpmSkillRef("/buddy-agent-harness:enhance");
|
|
17039
18180
|
const repairSkillRef = pnpmSkillRef(repairSkillInvocation);
|
|
17040
18181
|
/** Where the user authors the golden MCP server set, named in every MCP repair that points at it. */
|
|
17041
18182
|
const goldenSet = ".agents/buddy-agent-harness/mcp.toml";
|
|
@@ -17401,6 +18542,35 @@ const nonstandardTable = {
|
|
|
17401
18542
|
}
|
|
17402
18543
|
};
|
|
17403
18544
|
repairsOf(nonstandardTable);
|
|
18545
|
+
/** Who offers every worktree repair, named the same way in each row. */
|
|
18546
|
+
const worktreeOffer = `${initSkillRef} offers it, or ${enhanceSkillRef} where the repository already has an AGENTS.md`;
|
|
18547
|
+
const worktreeTable = {
|
|
18548
|
+
"worktrees-untracked": {
|
|
18549
|
+
detail: ".claude/worktrees/ is not git-ignored, so every agent worktree Claude Code creates there shows up as untracked in git status",
|
|
18550
|
+
repair: ({ file }) => ({
|
|
18551
|
+
command: "",
|
|
18552
|
+
instruction: `add \`.claude/worktrees/\` to ${file} — ${worktreeOffer}`
|
|
18553
|
+
}),
|
|
18554
|
+
skillRepair: () => `run ${initSkillRef} or ${enhanceSkillRef}, which offers the .gitignore line`
|
|
18555
|
+
},
|
|
18556
|
+
"worktrees-scanned": {
|
|
18557
|
+
detail: "this config does not exclude .claude/worktrees, so the tool walks into each agent worktree, a whole checkout with its own config, and Biome stops on the nested root configuration",
|
|
18558
|
+
repair: ({ file }) => ({
|
|
18559
|
+
command: "",
|
|
18560
|
+
instruction: `add \`"!**/.claude/worktrees"\` to \`files.includes\` in ${file} — ${worktreeOffer}`
|
|
18561
|
+
}),
|
|
18562
|
+
skillRepair: () => `run ${initSkillRef} or ${enhanceSkillRef}, which offers the exclusion`
|
|
18563
|
+
},
|
|
18564
|
+
"worktree-base-unset": {
|
|
18565
|
+
detail: "worktree.baseRef is unset, so agent worktrees branch from the default branch rather than the current HEAD — work on a feature branch starts without its commits",
|
|
18566
|
+
repair: ({ file }) => ({
|
|
18567
|
+
command: "",
|
|
18568
|
+
instruction: `decide whether agent worktrees should branch from HEAD, and if so set \`"worktree": { "baseRef": "head" }\` in ${file} — ${worktreeOffer}`
|
|
18569
|
+
}),
|
|
18570
|
+
skillRepair: () => `run ${initSkillRef} or ${enhanceSkillRef}, which explains the trade-off and offers the setting`
|
|
18571
|
+
}
|
|
18572
|
+
};
|
|
18573
|
+
repairsOf(worktreeTable);
|
|
17404
18574
|
/**
|
|
17405
18575
|
* Annotated over the whole union rather than inferred, so an added problem fails to compile until
|
|
17406
18576
|
* its section carries it.
|
|
@@ -17411,7 +18581,8 @@ const doctorTable = {
|
|
|
17411
18581
|
...globalInstructionTable,
|
|
17412
18582
|
...configurationTable,
|
|
17413
18583
|
...mcpTable,
|
|
17414
|
-
...nonstandardTable
|
|
18584
|
+
...nonstandardTable,
|
|
18585
|
+
...worktreeTable
|
|
17415
18586
|
};
|
|
17416
18587
|
repairsOf(doctorTable);
|
|
17417
18588
|
/** The repair for one problem. Total by construction: the table is keyed by the union. */
|
|
@@ -19200,8 +20371,45 @@ function diagnoseNonstandard({ root, cli }) {
|
|
|
19200
20371
|
}
|
|
19201
20372
|
return findings.sort((left, right) => left.path.localeCompare(right.path));
|
|
19202
20373
|
}
|
|
20374
|
+
const biomeConfigs = ["biome.json", "biome.jsonc"];
|
|
20375
|
+
const claudeSettings = ".claude/settings.json";
|
|
20376
|
+
/** `undefined` when the file is missing or does not parse; either way Claude Code reads no base from it. */
|
|
20377
|
+
function readJson(path) {
|
|
20378
|
+
try {
|
|
20379
|
+
return JSON.parse(readFileSync(path, "utf8"));
|
|
20380
|
+
} catch {
|
|
20381
|
+
return;
|
|
20382
|
+
}
|
|
20383
|
+
}
|
|
20384
|
+
/**
|
|
20385
|
+
* Checked only where `.claude/` exists, the sign the repository is worked in with Claude Code.
|
|
20386
|
+
* Biome is the one tool checked: it is the confirmed failure, and the others are offered by the
|
|
20387
|
+
* skills without a finding.
|
|
20388
|
+
*/
|
|
20389
|
+
function diagnoseWorktrees({ root, git, cli }) {
|
|
20390
|
+
if (!existsSync(join(root, ".claude"))) return [];
|
|
20391
|
+
const findings = [];
|
|
20392
|
+
const add = (path, problem) => {
|
|
20393
|
+
const { detail, repair } = repairFor(problem);
|
|
20394
|
+
findings.push({
|
|
20395
|
+
path,
|
|
20396
|
+
problem,
|
|
20397
|
+
detail,
|
|
20398
|
+
repair: repair({ file: path }, cli)
|
|
20399
|
+
});
|
|
20400
|
+
};
|
|
20401
|
+
if (git.isRepository() && !git.isIgnored(".claude/worktrees/")) add(".gitignore", "worktrees-untracked");
|
|
20402
|
+
for (const config of biomeConfigs) {
|
|
20403
|
+
const path = join(root, config);
|
|
20404
|
+
if (existsSync(path) && !readFileSync(path, "utf8").includes(".claude/worktrees")) add(config, "worktrees-scanned");
|
|
20405
|
+
}
|
|
20406
|
+
const settings = readJson(join(root, claudeSettings));
|
|
20407
|
+
const worktree = isRecord(settings) ? settings["worktree"] : void 0;
|
|
20408
|
+
if (!isRecord(worktree) || worktree["baseRef"] === void 0) add(claudeSettings, "worktree-base-unset");
|
|
20409
|
+
return findings;
|
|
20410
|
+
}
|
|
19203
20411
|
//#endregion
|
|
19204
|
-
//#region ../../node_modules/.pnpm/cyber-agentkit@2.
|
|
20412
|
+
//#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
20413
|
/**
|
|
19206
20414
|
* AXI §10: collapses the home directory to `~` so a path in a report is portable to another
|
|
19207
20415
|
* machine.
|
|
@@ -19214,6 +20422,8 @@ t.boolean().optional();
|
|
|
19214
20422
|
t.string().optional();
|
|
19215
20423
|
t.string();
|
|
19216
20424
|
t.string().optional();
|
|
20425
|
+
/** The command line that reaches the reference commands in `cyber-agentkit`'s own CLI. */
|
|
20426
|
+
const defaultReferencePath = "cyber-agentkit reference";
|
|
19217
20427
|
const rootOption$3 = {
|
|
19218
20428
|
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
20429
|
type: t.string().optional()
|
|
@@ -19366,7 +20576,7 @@ const skillOption = {
|
|
|
19366
20576
|
description: "A skill's source folder in this repository: acts on its `references/<name>.default.md`.",
|
|
19367
20577
|
type: t.string().optional()
|
|
19368
20578
|
};
|
|
19369
|
-
function checkCommand(layersFor) {
|
|
20579
|
+
function checkCommand(layersFor, path = defaultReferencePath) {
|
|
19370
20580
|
return command({
|
|
19371
20581
|
name: "check",
|
|
19372
20582
|
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 +20592,10 @@ function checkCommand(layersFor) {
|
|
|
19382
20592
|
description: "Add every entry checked, with its kind and unsupported sections."
|
|
19383
20593
|
}
|
|
19384
20594
|
},
|
|
20595
|
+
examples: [{
|
|
20596
|
+
command: `${path} check`,
|
|
20597
|
+
description: "Check every reference against its interface"
|
|
20598
|
+
}],
|
|
19385
20599
|
async run(args) {
|
|
19386
20600
|
try {
|
|
19387
20601
|
const home = homedir();
|
|
@@ -19423,7 +20637,7 @@ function checkCommand(layersFor) {
|
|
|
19423
20637
|
}
|
|
19424
20638
|
});
|
|
19425
20639
|
}
|
|
19426
|
-
function createCommand(layersFor) {
|
|
20640
|
+
function createCommand(layersFor, path = defaultReferencePath) {
|
|
19427
20641
|
return command({
|
|
19428
20642
|
name: "create",
|
|
19429
20643
|
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 +20673,10 @@ function createCommand(layersFor) {
|
|
|
19459
20673
|
description: "After the write, add the trace, in which the new file applies."
|
|
19460
20674
|
}
|
|
19461
20675
|
},
|
|
20676
|
+
examples: [{
|
|
20677
|
+
command: `${path} create my-topic`,
|
|
20678
|
+
description: "Start a new reference named my-topic"
|
|
20679
|
+
}],
|
|
19462
20680
|
async run(args) {
|
|
19463
20681
|
const format = this.format;
|
|
19464
20682
|
try {
|
|
@@ -19569,13 +20787,32 @@ function text(customized, shown) {
|
|
|
19569
20787
|
const meta = shown.metadataFile ? ` Its record is in ${shown.metadataFile}.` : "";
|
|
19570
20788
|
return `${shown.path}\n\n${shown.hint}${meta}\n`;
|
|
19571
20789
|
}
|
|
19572
|
-
function
|
|
20790
|
+
function familyReport(customized, args, home) {
|
|
20791
|
+
return compact({
|
|
20792
|
+
family: customized.family,
|
|
20793
|
+
interfaces: customized.interfaces.map(({ interface: id, outcome, reason, result: each }) => compact({
|
|
20794
|
+
interface: id,
|
|
20795
|
+
outcome,
|
|
20796
|
+
reason,
|
|
20797
|
+
...each ? report(each, args, home) : {}
|
|
20798
|
+
})),
|
|
20799
|
+
hint: customized.dryRun ? "Nothing was written. Run again without --dry-run to write it." : "Run `load <interface>` for each composed document."
|
|
20800
|
+
});
|
|
20801
|
+
}
|
|
20802
|
+
function familyText(customized, shown) {
|
|
20803
|
+
return `${customized.interfaces.map(({ interface: id, outcome, reason, result: each }, at) => {
|
|
20804
|
+
if (!each) return `${id}: ${outcome}. ${reason}\n`;
|
|
20805
|
+
const own = shown.interfaces[at];
|
|
20806
|
+
return `${id}: ${outcome}.\n${text(each, own)}`;
|
|
20807
|
+
}).join("\n")}`;
|
|
20808
|
+
}
|
|
20809
|
+
function customizeCommand(layersFor, path = defaultReferencePath) {
|
|
19573
20810
|
return command({
|
|
19574
20811
|
name: "customize",
|
|
19575
20812
|
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
20813
|
arguments: [{
|
|
19577
20814
|
name: "name",
|
|
19578
|
-
description: "Reference name, without the `.md` extension.",
|
|
20815
|
+
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
20816
|
type: t.string()
|
|
19580
20817
|
}, {
|
|
19581
20818
|
name: "steps",
|
|
@@ -19634,6 +20871,13 @@ function customizeCommand(layersFor) {
|
|
|
19634
20871
|
description: "After the write, add the trace, in which the written file applies."
|
|
19635
20872
|
}
|
|
19636
20873
|
},
|
|
20874
|
+
examples: [{
|
|
20875
|
+
command: `${path} customize my-topic`,
|
|
20876
|
+
description: "Customize a reference for this project"
|
|
20877
|
+
}, {
|
|
20878
|
+
command: `${path} customize my-skill.aspects.* --extends house-style`,
|
|
20879
|
+
description: "Pick one plugin for every interface under a prefix"
|
|
20880
|
+
}],
|
|
19637
20881
|
async run(args) {
|
|
19638
20882
|
const format = this.format;
|
|
19639
20883
|
try {
|
|
@@ -19657,19 +20901,33 @@ function customizeCommand(layersFor) {
|
|
|
19657
20901
|
base: args.base,
|
|
19658
20902
|
dryRun: args["dry-run"]
|
|
19659
20903
|
};
|
|
20904
|
+
const family = isFamilyName(args.name) || args.name.includes("*") ? parseFamily(args.name) : void 0;
|
|
19660
20905
|
validateWrite({
|
|
19661
20906
|
command: "customize",
|
|
19662
|
-
...request
|
|
20907
|
+
...request,
|
|
20908
|
+
name: family === void 0 || family.invalid !== void 0 ? args.name : family.prefix
|
|
19663
20909
|
});
|
|
19664
20910
|
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
20911
|
const layers = await layersFor({
|
|
19666
20912
|
root: args.root,
|
|
19667
20913
|
default: args.default
|
|
19668
20914
|
}, home);
|
|
20915
|
+
if (family !== void 0) {
|
|
20916
|
+
const customized = customizeContribution(layers, {
|
|
20917
|
+
...request,
|
|
20918
|
+
root: layers.root ?? request.root
|
|
20919
|
+
}, { display });
|
|
20920
|
+
if (!("family" in customized)) throw new Error(`${args.name} was customized as a single name, not a family.`);
|
|
20921
|
+
const shown = familyReport(customized, args, home);
|
|
20922
|
+
if (format !== "text") return result(shown);
|
|
20923
|
+
for (const { result: each } of customized.interfaces) writeWarnings(each?.warnings ?? []);
|
|
20924
|
+
return document(familyText(customized, shown));
|
|
20925
|
+
}
|
|
19669
20926
|
const customized = customizeContribution(layers, {
|
|
19670
20927
|
...request,
|
|
19671
20928
|
root: layers.root ?? request.root
|
|
19672
20929
|
}, { display });
|
|
20930
|
+
if ("family" in customized) throw new Error(`${args.name} was customized as a family, not a single name.`);
|
|
19673
20931
|
if (customized.action === "base") {
|
|
19674
20932
|
if (format !== "text") return result(baseReport(customized));
|
|
19675
20933
|
return document(baseText(customized));
|
|
@@ -19702,7 +20960,7 @@ function deleteReport(deleted, home) {
|
|
|
19702
20960
|
options: deleted.after.options
|
|
19703
20961
|
});
|
|
19704
20962
|
}
|
|
19705
|
-
function deleteCommand(layersFor) {
|
|
20963
|
+
function deleteCommand(layersFor, path = defaultReferencePath) {
|
|
19706
20964
|
return command({
|
|
19707
20965
|
name: "delete",
|
|
19708
20966
|
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 +20983,10 @@ function deleteCommand(layersFor) {
|
|
|
19725
20983
|
type: t.boolean().optional()
|
|
19726
20984
|
}
|
|
19727
20985
|
},
|
|
20986
|
+
examples: [{
|
|
20987
|
+
command: `${path} delete my-topic`,
|
|
20988
|
+
description: "Delete your contribution to my-topic"
|
|
20989
|
+
}],
|
|
19728
20990
|
async run(args) {
|
|
19729
20991
|
const format = this.format;
|
|
19730
20992
|
try {
|
|
@@ -19772,13 +21034,40 @@ function parseAccept(value) {
|
|
|
19772
21034
|
} });
|
|
19773
21035
|
return versions.map(Number);
|
|
19774
21036
|
}
|
|
19775
|
-
function
|
|
21037
|
+
function rowsOf(found, trace) {
|
|
21038
|
+
return found.implementations.map(({ pin, description, unsupported, name, tier, version }) => compact(trace ? {
|
|
21039
|
+
pin,
|
|
21040
|
+
description,
|
|
21041
|
+
unsupported,
|
|
21042
|
+
name,
|
|
21043
|
+
tier,
|
|
21044
|
+
version
|
|
21045
|
+
} : {
|
|
21046
|
+
pin,
|
|
21047
|
+
...tier === "plugin" ? {} : { tier },
|
|
21048
|
+
description,
|
|
21049
|
+
unsupported
|
|
21050
|
+
}));
|
|
21051
|
+
}
|
|
21052
|
+
function reportOf(found, trace) {
|
|
21053
|
+
const report = { implementations: rowsOf(found, trace) };
|
|
21054
|
+
if (found.inUse !== void 0) report.inUse = found.inUse;
|
|
21055
|
+
if (trace) Object.assign(report, compact({
|
|
21056
|
+
interface: found.interface,
|
|
21057
|
+
cardinality: found.cardinality,
|
|
21058
|
+
accept: found.accept,
|
|
21059
|
+
excluded: found.excluded,
|
|
21060
|
+
warnings: found.warnings
|
|
21061
|
+
}));
|
|
21062
|
+
return report;
|
|
21063
|
+
}
|
|
21064
|
+
function implementationsCommand(layersFor, path = defaultReferencePath) {
|
|
19776
21065
|
return command({
|
|
19777
21066
|
name: "implementations",
|
|
19778
21067
|
description: "List the implementations of an interface a consumer can load, each with its pin, description, and unsupported sections.",
|
|
19779
21068
|
arguments: [{
|
|
19780
21069
|
name: "id",
|
|
19781
|
-
description: "Interface id, the name of its `<id>.interface` document without the suffix.",
|
|
21070
|
+
description: "Interface id, the name of its `<id>.interface` document without the suffix; or a family, `<prefix>.*`, for every interface under the prefix.",
|
|
19782
21071
|
type: t.string()
|
|
19783
21072
|
}],
|
|
19784
21073
|
options: {
|
|
@@ -19792,36 +21081,34 @@ function implementationsCommand(layersFor) {
|
|
|
19792
21081
|
description: "Add each one's name, tier, and version, the interface's cardinality and accepted versions, and the ones left out with why."
|
|
19793
21082
|
}
|
|
19794
21083
|
},
|
|
21084
|
+
examples: [{
|
|
21085
|
+
command: `${path} implementations my-interface`,
|
|
21086
|
+
description: "List what implements an interface"
|
|
21087
|
+
}, {
|
|
21088
|
+
command: `${path} implementations my-skill.aspects.*`,
|
|
21089
|
+
description: "List, for every interface under a prefix, what implements it and which one is in use"
|
|
21090
|
+
}],
|
|
19795
21091
|
async run(args) {
|
|
19796
21092
|
try {
|
|
19797
21093
|
const accept = parseAccept(args.accept);
|
|
19798
21094
|
const home = homedir();
|
|
19799
|
-
const
|
|
21095
|
+
const options = {
|
|
19800
21096
|
accept,
|
|
19801
21097
|
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
|
-
}));
|
|
21098
|
+
};
|
|
21099
|
+
const found = referenceImplementations(args.id, await layersFor(args, home), options);
|
|
21100
|
+
if ("family" in found) {
|
|
21101
|
+
const family = found;
|
|
21102
|
+
return result({
|
|
21103
|
+
family: family.family,
|
|
21104
|
+
interfaces: family.interfaces.map((each) => ({
|
|
21105
|
+
...reportOf(each, args.trace),
|
|
21106
|
+
interface: each.interface
|
|
21107
|
+
}))
|
|
21108
|
+
});
|
|
21109
|
+
}
|
|
21110
|
+
const report = reportOf(found, args.trace);
|
|
21111
|
+
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
21112
|
return result(report);
|
|
19826
21113
|
} catch (error) {
|
|
19827
21114
|
return asCliError(error, "Implementation lookup failed.");
|
|
@@ -19829,7 +21116,7 @@ function implementationsCommand(layersFor) {
|
|
|
19829
21116
|
}
|
|
19830
21117
|
});
|
|
19831
21118
|
}
|
|
19832
|
-
function listCommand(layersFor) {
|
|
21119
|
+
function listCommand(layersFor, path = defaultReferencePath) {
|
|
19833
21120
|
return command({
|
|
19834
21121
|
name: "list",
|
|
19835
21122
|
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 +21132,13 @@ function listCommand(layersFor) {
|
|
|
19845
21132
|
description: "Add one row per name per layer holding it, with its outcome."
|
|
19846
21133
|
}
|
|
19847
21134
|
},
|
|
21135
|
+
examples: [{
|
|
21136
|
+
command: `${path} list`,
|
|
21137
|
+
description: "List every reference name"
|
|
21138
|
+
}, {
|
|
21139
|
+
command: `${path} list 'project-tracker.*'`,
|
|
21140
|
+
description: "List the names matching a pattern"
|
|
21141
|
+
}],
|
|
19848
21142
|
async run(args) {
|
|
19849
21143
|
try {
|
|
19850
21144
|
const home = homedir();
|
|
@@ -19912,7 +21206,7 @@ function loadText(resolved) {
|
|
|
19912
21206
|
}
|
|
19913
21207
|
return document(resolved.map(tag).join("\n\n"));
|
|
19914
21208
|
}
|
|
19915
|
-
function loadCommand(layersFor) {
|
|
21209
|
+
function loadCommand(layersFor, path = defaultReferencePath) {
|
|
19916
21210
|
return command({
|
|
19917
21211
|
name: "load",
|
|
19918
21212
|
description: "Print one or more references, each composed from its contributions as their extends and merge declare.",
|
|
@@ -19930,6 +21224,13 @@ function loadCommand(layersFor) {
|
|
|
19930
21224
|
},
|
|
19931
21225
|
trace: traceOption
|
|
19932
21226
|
},
|
|
21227
|
+
examples: [{
|
|
21228
|
+
command: `${path} load my-topic`,
|
|
21229
|
+
description: "Print one reference"
|
|
21230
|
+
}, {
|
|
21231
|
+
command: `${path} load my-topic other-topic --format json`,
|
|
21232
|
+
description: "Print several as JSON"
|
|
21233
|
+
}],
|
|
19933
21234
|
async run(args) {
|
|
19934
21235
|
const format = this.format;
|
|
19935
21236
|
try {
|
|
@@ -19970,7 +21271,87 @@ function loadCommand(layersFor) {
|
|
|
19970
21271
|
}
|
|
19971
21272
|
});
|
|
19972
21273
|
}
|
|
19973
|
-
function
|
|
21274
|
+
function rowLine(row) {
|
|
21275
|
+
const where = row.folder === void 0 ? row.tier : `${row.tier} ${row.folder}`;
|
|
21276
|
+
const change = row.manual === void 0 ? `${row.from} → ${row.to}` : `${row.from}: manual: ${row.manual}`;
|
|
21277
|
+
const by = row.by === void 0 ? "" : ` (by ${row.by})`;
|
|
21278
|
+
const user = row.tier === "user" ? "; a user file, which no git history restores" : "";
|
|
21279
|
+
return `${row.path} (${where}): ${row.form}: ${change}${by}${user}`;
|
|
21280
|
+
}
|
|
21281
|
+
function summary(rows, written) {
|
|
21282
|
+
const manual = rows.filter((row) => row.manual !== void 0).length;
|
|
21283
|
+
const applied = rows.length - manual;
|
|
21284
|
+
const left = manual ? ` ${manual} ${manual === 1 ? "needs" : "need"} a person, as each says.` : "";
|
|
21285
|
+
if (written) return `migrated ${applied} ${applied === 1 ? "change" : "changes"}.${left}`;
|
|
21286
|
+
if (!applied) return `nothing to apply.${left}`;
|
|
21287
|
+
return `${applied} ${applied === 1 ? "change" : "changes"} to make; run \`reference migrate --write\` with the same arguments to apply ${applied === 1 ? "it" : "them"}.${left}`;
|
|
21288
|
+
}
|
|
21289
|
+
function migrateCommand(layersFor, path = defaultReferencePath) {
|
|
21290
|
+
return command({
|
|
21291
|
+
name: "migrate",
|
|
21292
|
+
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.",
|
|
21293
|
+
arguments: [{
|
|
21294
|
+
name: "names",
|
|
21295
|
+
description: "Reference names to limit the run to, without the `.md` extension; a file under an older name of one is included.",
|
|
21296
|
+
type: t.array(t.string()).optional()
|
|
21297
|
+
}],
|
|
21298
|
+
options: {
|
|
21299
|
+
root: rootOption$3,
|
|
21300
|
+
plugin: {
|
|
21301
|
+
description: "A plugin's source folder in this repository: migrate only its `references/`, in place of the tiers.",
|
|
21302
|
+
type: t.string().optional()
|
|
21303
|
+
},
|
|
21304
|
+
default: {
|
|
21305
|
+
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.",
|
|
21306
|
+
type: t.string().optional()
|
|
21307
|
+
},
|
|
21308
|
+
write: {
|
|
21309
|
+
description: "Apply the changes. It edits `user` files too, which no git history restores.",
|
|
21310
|
+
type: t.boolean().optional()
|
|
21311
|
+
}
|
|
21312
|
+
},
|
|
21313
|
+
examples: [{
|
|
21314
|
+
command: `${path} migrate`,
|
|
21315
|
+
description: "Migrate version 1 reference files"
|
|
21316
|
+
}],
|
|
21317
|
+
async run(args) {
|
|
21318
|
+
const format = this.format;
|
|
21319
|
+
try {
|
|
21320
|
+
const home = homedir();
|
|
21321
|
+
if (args.plugin !== void 0 && args.default !== void 0) throw new ReferenceFailure("options-conflict", "pass one of --plugin and --default.");
|
|
21322
|
+
const layers = await layersFor({ root: args.root }, home);
|
|
21323
|
+
const migrated = migrateReferences(layers, {
|
|
21324
|
+
root: layers.root ?? givenRoot(args.root),
|
|
21325
|
+
home,
|
|
21326
|
+
names: args.names,
|
|
21327
|
+
plugin: args.plugin,
|
|
21328
|
+
default: args.default,
|
|
21329
|
+
write: args.write
|
|
21330
|
+
});
|
|
21331
|
+
const changes = migrated.rows.map((row) => ({
|
|
21332
|
+
...row,
|
|
21333
|
+
path: collapseHome(home, row.path)
|
|
21334
|
+
}));
|
|
21335
|
+
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."] });
|
|
21336
|
+
const report = compact({
|
|
21337
|
+
changes,
|
|
21338
|
+
written: migrated.written ? true : void 0,
|
|
21339
|
+
warnings: migrated.warnings
|
|
21340
|
+
});
|
|
21341
|
+
if (format !== "text") return result(report);
|
|
21342
|
+
writeWarnings(migrated.warnings);
|
|
21343
|
+
return document(`${[
|
|
21344
|
+
...changes.map(rowLine),
|
|
21345
|
+
"",
|
|
21346
|
+
summary(changes, migrated.written)
|
|
21347
|
+
].join("\n").trimStart()}\n`);
|
|
21348
|
+
} catch (error) {
|
|
21349
|
+
return asCliError(error, "Reference migration failed.");
|
|
21350
|
+
}
|
|
21351
|
+
}
|
|
21352
|
+
});
|
|
21353
|
+
}
|
|
21354
|
+
function searchCommand(layersFor, path = defaultReferencePath) {
|
|
19974
21355
|
return command({
|
|
19975
21356
|
name: "search",
|
|
19976
21357
|
description: "Find references by name, description, tags, headings, or body, best match first.",
|
|
@@ -19984,16 +21365,29 @@ function searchCommand(layersFor) {
|
|
|
19984
21365
|
trace: {
|
|
19985
21366
|
...traceOption,
|
|
19986
21367
|
description: "Add the tier and plugin or skill of each match."
|
|
21368
|
+
},
|
|
21369
|
+
full: {
|
|
21370
|
+
...fullOption,
|
|
21371
|
+
description: `Show every match, not only the best 10.`
|
|
19987
21372
|
}
|
|
19988
21373
|
},
|
|
21374
|
+
examples: [{
|
|
21375
|
+
command: `${path} search "worktree leases"`,
|
|
21376
|
+
description: "Find references by topic"
|
|
21377
|
+
}],
|
|
19989
21378
|
async run(args) {
|
|
19990
21379
|
const query = args.query.trim();
|
|
19991
21380
|
try {
|
|
19992
21381
|
const home = homedir();
|
|
19993
21382
|
const { matches, defaults } = searchReferences(query, await layersFor(args, home), { display: (path) => collapseHome(home, path) });
|
|
19994
21383
|
if (!matches.length) return empty(`0 references match "${query}"`, { help: [...defaults ? [defaults] : [], "Try other words, or run `reference list` for every name."] });
|
|
19995
|
-
|
|
19996
|
-
|
|
21384
|
+
const shown = truncateList(matches, {
|
|
21385
|
+
limit: 10,
|
|
21386
|
+
full: args.full
|
|
21387
|
+
});
|
|
21388
|
+
const report = compact({
|
|
21389
|
+
count: shown.truncated ? shown.count : void 0,
|
|
21390
|
+
references: shown.items.map(({ name, description, match, folder, tier, plugin, skill, loadedBy }) => compact(args.trace ? {
|
|
19997
21391
|
name,
|
|
19998
21392
|
description,
|
|
19999
21393
|
match,
|
|
@@ -20010,14 +21404,15 @@ function searchCommand(layersFor) {
|
|
|
20010
21404
|
loadedBy
|
|
20011
21405
|
})),
|
|
20012
21406
|
defaults
|
|
20013
|
-
})
|
|
21407
|
+
});
|
|
21408
|
+
return shown.truncated ? result(report, { help: [`Run \`reference search ${JSON.stringify(query)} --full\` for every match.`] }) : result(report);
|
|
20014
21409
|
} catch (error) {
|
|
20015
21410
|
return asCliError(error, "Reference search failed.");
|
|
20016
21411
|
}
|
|
20017
21412
|
}
|
|
20018
21413
|
});
|
|
20019
21414
|
}
|
|
20020
|
-
function whereCommand(layersFor) {
|
|
21415
|
+
function whereCommand(layersFor, path = defaultReferencePath) {
|
|
20021
21416
|
return command({
|
|
20022
21417
|
name: "where",
|
|
20023
21418
|
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 +21433,10 @@ function whereCommand(layersFor) {
|
|
|
20038
21433
|
description: "Add every layer checked, with its outcome."
|
|
20039
21434
|
}
|
|
20040
21435
|
},
|
|
21436
|
+
examples: [{
|
|
21437
|
+
command: `${path} where my-topic`,
|
|
21438
|
+
description: "Show which layers hold my-topic"
|
|
21439
|
+
}],
|
|
20041
21440
|
async run(args) {
|
|
20042
21441
|
const format = this.format;
|
|
20043
21442
|
try {
|
|
@@ -20072,25 +21471,26 @@ function whereCommand(layersFor) {
|
|
|
20072
21471
|
});
|
|
20073
21472
|
}
|
|
20074
21473
|
/** A factory, so each host CLI names itself as the plugin whose own references come first. */
|
|
20075
|
-
function createReferenceCommands({ plugin } = {}) {
|
|
21474
|
+
function createReferenceCommands({ plugin, path = defaultReferencePath } = {}) {
|
|
20076
21475
|
const layersFor = layerSource(plugin);
|
|
20077
21476
|
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
|
-
|
|
21477
|
+
load: loadCommand(layersFor, path),
|
|
21478
|
+
list: listCommand(layersFor, path),
|
|
21479
|
+
search: searchCommand(layersFor, path),
|
|
21480
|
+
where: whereCommand(layersFor, path),
|
|
21481
|
+
create: createCommand(layersFor, path),
|
|
21482
|
+
customize: customizeCommand(layersFor, path),
|
|
21483
|
+
delete: deleteCommand(layersFor, path),
|
|
21484
|
+
migrate: migrateCommand(layersFor, path),
|
|
21485
|
+
check: checkCommand(layersFor, path),
|
|
21486
|
+
implementations: implementationsCommand(layersFor, path)
|
|
20087
21487
|
};
|
|
20088
21488
|
}
|
|
20089
21489
|
function createReferenceCommand(options = {}) {
|
|
20090
|
-
const { load, list, search, where, create, customize, delete: remove, check, implementations } = createReferenceCommands(options);
|
|
21490
|
+
const { load, list, search, where, create, customize, delete: remove, migrate, check, implementations } = createReferenceCommands(options);
|
|
20091
21491
|
return command({
|
|
20092
21492
|
name: "reference",
|
|
20093
|
-
description: "Read reference documents by name, composed from the managed, repo, user, plugin, and default tiers,
|
|
21493
|
+
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
21494
|
commands: [
|
|
20095
21495
|
load,
|
|
20096
21496
|
list,
|
|
@@ -20099,6 +21499,7 @@ function createReferenceCommand(options = {}) {
|
|
|
20099
21499
|
create,
|
|
20100
21500
|
customize,
|
|
20101
21501
|
remove,
|
|
21502
|
+
migrate,
|
|
20102
21503
|
implementations,
|
|
20103
21504
|
check
|
|
20104
21505
|
]
|
|
@@ -20256,10 +21657,13 @@ function referenceLayers({ packageRoot = dirname(packageGovernancesDir()), ...op
|
|
|
20256
21657
|
}
|
|
20257
21658
|
});
|
|
20258
21659
|
}
|
|
20259
|
-
const referenceCommand = createReferenceCommand({
|
|
20260
|
-
|
|
20261
|
-
|
|
20262
|
-
|
|
21660
|
+
const referenceCommand = createReferenceCommand({
|
|
21661
|
+
plugin: {
|
|
21662
|
+
name: PACKAGE_PLUGIN,
|
|
21663
|
+
root: dirname(packageGovernancesDir())
|
|
21664
|
+
},
|
|
21665
|
+
path: "buddy-agent-harness reference"
|
|
21666
|
+
});
|
|
20263
21667
|
//#endregion
|
|
20264
21668
|
//#region src/skill-projection/skill-projection.ts
|
|
20265
21669
|
function skillNames(skillsDirectory) {
|
|
@@ -20712,6 +22116,10 @@ var GitBridgeState = class {
|
|
|
20712
22116
|
this.root = root;
|
|
20713
22117
|
this.repoRootPrefix = git(root, ["rev-parse", "--show-prefix"])?.trim();
|
|
20714
22118
|
}
|
|
22119
|
+
/** Whether `root` is inside a git work tree at all; a check that needs git's answer skips when not. */
|
|
22120
|
+
isRepository() {
|
|
22121
|
+
return this.repoRootPrefix !== void 0;
|
|
22122
|
+
}
|
|
20715
22123
|
/** The skip-worktree bit is a hint some checkouts clear, so it is verified rather than assumed. */
|
|
20716
22124
|
trackingOf(path) {
|
|
20717
22125
|
if (this.repoRootPrefix === void 0) return "untracked";
|
|
@@ -20974,64 +22382,82 @@ function buildDoctorReport(bin, result, configuration = [], held = []) {
|
|
|
20974
22382
|
help: [...new Map(findings.map((finding) => [`${finding.repair.command}\u0000${finding.repair.instruction}`, finding.repair])).values()]
|
|
20975
22383
|
};
|
|
20976
22384
|
}
|
|
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
|
-
}
|
|
22385
|
+
const doctorOptions = {
|
|
22386
|
+
root: {
|
|
22387
|
+
description: "Repository or package directory. Defaults to the current directory.",
|
|
22388
|
+
type: t.string().optional()
|
|
20989
22389
|
},
|
|
20990
|
-
|
|
20991
|
-
|
|
20992
|
-
|
|
20993
|
-
|
|
20994
|
-
|
|
20995
|
-
|
|
22390
|
+
harness: {
|
|
22391
|
+
description: "Comma-separated harnesses to check in addition to Claude Code and Cursor, e.g. codex,gemini-cli.",
|
|
22392
|
+
type: t.string().optional()
|
|
22393
|
+
}
|
|
22394
|
+
};
|
|
22395
|
+
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.";
|
|
22396
|
+
/** The live report `doctor` answers with, shared with the bare invocation of the CLI. */
|
|
22397
|
+
async function diagnoseReport(args) {
|
|
22398
|
+
try {
|
|
22399
|
+
const harnesses = parseHarnessOption(args.harness);
|
|
22400
|
+
const root = args.root ?? process.cwd();
|
|
22401
|
+
const home = homedir();
|
|
22402
|
+
const diagnosis = diagnoseBridges({
|
|
22403
|
+
root,
|
|
22404
|
+
...harnesses.length ? { harnesses } : {},
|
|
22405
|
+
cli: commandInvocation,
|
|
22406
|
+
home,
|
|
22407
|
+
env: process.env
|
|
22408
|
+
});
|
|
22409
|
+
const git = new GitBridgeState(root);
|
|
22410
|
+
const configuration = [
|
|
22411
|
+
...diagnoseConfiguration({
|
|
20996
22412
|
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({
|
|
22413
|
+
git,
|
|
22414
|
+
cli: commandInvocation
|
|
22415
|
+
}),
|
|
22416
|
+
...diagnoseMcp({
|
|
21020
22417
|
root,
|
|
21021
|
-
|
|
21022
|
-
|
|
21023
|
-
|
|
21024
|
-
|
|
21025
|
-
|
|
21026
|
-
|
|
21027
|
-
|
|
21028
|
-
|
|
21029
|
-
|
|
21030
|
-
|
|
21031
|
-
|
|
21032
|
-
|
|
21033
|
-
|
|
21034
|
-
}
|
|
22418
|
+
git,
|
|
22419
|
+
cli: commandInvocation
|
|
22420
|
+
}),
|
|
22421
|
+
...diagnoseNonstandard({
|
|
22422
|
+
root,
|
|
22423
|
+
cli: commandInvocation
|
|
22424
|
+
}),
|
|
22425
|
+
...diagnoseWorktrees({
|
|
22426
|
+
root,
|
|
22427
|
+
git,
|
|
22428
|
+
cli: commandInvocation
|
|
22429
|
+
})
|
|
22430
|
+
];
|
|
22431
|
+
const { layers: rows } = listReferences(await referenceLayers({
|
|
22432
|
+
root,
|
|
22433
|
+
home,
|
|
22434
|
+
platform: process.platform,
|
|
22435
|
+
env: process.env
|
|
22436
|
+
}));
|
|
22437
|
+
const held = rows.filter(({ tier }) => tier !== "plugin").map(({ name, tier, path, outcome }) => ({
|
|
22438
|
+
name,
|
|
22439
|
+
tier,
|
|
22440
|
+
path: collapseHome$1(home, path),
|
|
22441
|
+
status: outcome
|
|
22442
|
+
}));
|
|
22443
|
+
return buildDoctorReport(displayBinPath(home, process.argv[1]), diagnosis, configuration, held);
|
|
22444
|
+
} catch (error) {
|
|
22445
|
+
return fail(error, "Harness diagnosis failed.");
|
|
22446
|
+
}
|
|
22447
|
+
}
|
|
22448
|
+
const doctorCommand = command({
|
|
22449
|
+
name: "doctor",
|
|
22450
|
+
description: doctorDescription,
|
|
22451
|
+
options: doctorOptions,
|
|
22452
|
+
examples: [{
|
|
22453
|
+
command: "buddy-agent-harness doctor",
|
|
22454
|
+
description: "Check the current repository"
|
|
22455
|
+
}, {
|
|
22456
|
+
command: "buddy-agent-harness doctor --harness codex,gemini-cli",
|
|
22457
|
+
description: "Also check Codex and Gemini CLI"
|
|
22458
|
+
}],
|
|
22459
|
+
async run(args) {
|
|
22460
|
+
return result(await diagnoseReport(args));
|
|
21035
22461
|
}
|
|
21036
22462
|
});
|
|
21037
22463
|
//#endregion
|
|
@@ -21066,6 +22492,10 @@ const governanceCommand = command({
|
|
|
21066
22492
|
root: rootOption,
|
|
21067
22493
|
"overrides-only": overridesOnlyOption
|
|
21068
22494
|
},
|
|
22495
|
+
examples: [{
|
|
22496
|
+
command: "buddy-agent-harness governance list",
|
|
22497
|
+
description: "List the governances the layers hold"
|
|
22498
|
+
}],
|
|
21069
22499
|
run(args) {
|
|
21070
22500
|
process.stderr.write(GOVERNANCE_DEPRECATED);
|
|
21071
22501
|
try {
|
|
@@ -21100,16 +22530,21 @@ const governanceCommand = command({
|
|
|
21100
22530
|
root: rootOption,
|
|
21101
22531
|
"overrides-only": overridesOnlyOption
|
|
21102
22532
|
},
|
|
22533
|
+
examples: [{
|
|
22534
|
+
command: "buddy-agent-harness governance show skill-design",
|
|
22535
|
+
description: "Print one governance document"
|
|
22536
|
+
}],
|
|
21103
22537
|
run(args) {
|
|
21104
22538
|
const format = this.format;
|
|
21105
22539
|
process.stderr.write(GOVERNANCE_DEPRECATED);
|
|
21106
22540
|
try {
|
|
21107
22541
|
const name = parseGovernanceName(args.name);
|
|
21108
22542
|
const found = resolveGovernance(name, layersFor(args));
|
|
21109
|
-
if (!found) {
|
|
21110
|
-
|
|
21111
|
-
|
|
21112
|
-
|
|
22543
|
+
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.`, {
|
|
22544
|
+
code: "governance-not-found",
|
|
22545
|
+
details: { name },
|
|
22546
|
+
help: ref("reference list", {}, "see what is available")
|
|
22547
|
+
});
|
|
21113
22548
|
const report = {
|
|
21114
22549
|
name: found.name,
|
|
21115
22550
|
scope: found.scope,
|
|
@@ -21185,9 +22620,16 @@ const initCommand = command({
|
|
|
21185
22620
|
type: t.string().optional()
|
|
21186
22621
|
}
|
|
21187
22622
|
},
|
|
22623
|
+
examples: [{
|
|
22624
|
+
command: "buddy-agent-harness init",
|
|
22625
|
+
description: "Set up skills for Claude Code and Cursor"
|
|
22626
|
+
}, {
|
|
22627
|
+
command: "buddy-agent-harness init --harness codex --copy",
|
|
22628
|
+
description: "Also enable Codex, copying skills instead of linking"
|
|
22629
|
+
}],
|
|
21188
22630
|
run(args) {
|
|
21189
22631
|
try {
|
|
21190
|
-
const harnesses =
|
|
22632
|
+
const harnesses = parseHarnessOption(args.harness);
|
|
21191
22633
|
return result(initializeHarnesses({
|
|
21192
22634
|
root: args.root ?? process.cwd(),
|
|
21193
22635
|
...harnesses.length ? { harnesses } : {},
|
|
@@ -23763,6 +25205,13 @@ const mcpReconcileCommand = command({
|
|
|
23763
25205
|
type: t.array(t.string()).optional()
|
|
23764
25206
|
}
|
|
23765
25207
|
},
|
|
25208
|
+
examples: [{
|
|
25209
|
+
command: "buddy-agent-harness mcp reconcile",
|
|
25210
|
+
description: "List harness-side changes the golden set lacks"
|
|
25211
|
+
}, {
|
|
25212
|
+
command: "buddy-agent-harness mcp reconcile --accept <path>",
|
|
25213
|
+
description: "Import one approved field"
|
|
25214
|
+
}],
|
|
23766
25215
|
run(args) {
|
|
23767
25216
|
try {
|
|
23768
25217
|
const root = args.root ?? process.cwd();
|
|
@@ -23774,19 +25223,17 @@ const mcpReconcileCommand = command({
|
|
|
23774
25223
|
});
|
|
23775
25224
|
if (plan.kind === "unreadable") {
|
|
23776
25225
|
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"
|
|
25226
|
+
throw new CliError(`${goldenSetPath} does not parse${at}.`, {
|
|
25227
|
+
code: "golden-set-unreadable",
|
|
25228
|
+
help: `Fix the TOML syntax in ${goldenSetPath}, then re-run.`
|
|
25229
|
+
});
|
|
25230
|
+
}
|
|
25231
|
+
if (plan.kind === "rejected") throw new CliError("nothing written.", {
|
|
25232
|
+
code: "accept-rejected",
|
|
25233
|
+
details: { reasons: plan.reasons },
|
|
25234
|
+
help: ref("mcp reconcile", {}, "list the fields that can be approved")
|
|
23789
25235
|
});
|
|
25236
|
+
if (plan.kind === "absent") return empty(`0 servers — no golden set at ${goldenSetPath}`, { help: `Create ${goldenSetPath} first; there is nothing to reconcile into.` });
|
|
23790
25237
|
return result({
|
|
23791
25238
|
golden: goldenSetPath,
|
|
23792
25239
|
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 +26283,13 @@ const mcpCommand = command({
|
|
|
24836
26283
|
type: t.boolean().optional()
|
|
24837
26284
|
}
|
|
24838
26285
|
},
|
|
26286
|
+
examples: [{
|
|
26287
|
+
command: "buddy-agent-harness mcp project",
|
|
26288
|
+
description: "Preview what would be projected"
|
|
26289
|
+
}, {
|
|
26290
|
+
command: "buddy-agent-harness mcp project --write",
|
|
26291
|
+
description: "Project the golden set into each harness"
|
|
26292
|
+
}],
|
|
24839
26293
|
run(args) {
|
|
24840
26294
|
try {
|
|
24841
26295
|
const root = args.root ?? process.cwd();
|
|
@@ -24847,15 +26301,12 @@ const mcpCommand = command({
|
|
|
24847
26301
|
});
|
|
24848
26302
|
if (plan.kind === "unreadable") {
|
|
24849
26303
|
const at = plan.position ? ` at line ${plan.position.line}, column ${plan.position.column}` : "";
|
|
24850
|
-
|
|
24851
|
-
|
|
26304
|
+
throw new CliError(`${goldenSetPath} does not parse${at}.`, {
|
|
26305
|
+
code: "golden-set-unreadable",
|
|
26306
|
+
help: `Fix the TOML syntax in ${goldenSetPath}, then re-run.`
|
|
26307
|
+
});
|
|
24852
26308
|
}
|
|
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
|
-
});
|
|
26309
|
+
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
26310
|
return result({
|
|
24860
26311
|
golden: goldenSetPath,
|
|
24861
26312
|
mode: write ? "written" : "dry run — nothing written; re-run with --write to apply",
|
|
@@ -24892,6 +26343,19 @@ function app(format) {
|
|
|
24892
26343
|
version,
|
|
24893
26344
|
description: rootCommand.description,
|
|
24894
26345
|
format
|
|
26346
|
+
}).default({
|
|
26347
|
+
description: "Report whether this repository's harness bridges resolve (the same as `doctor`).",
|
|
26348
|
+
options: doctorCommand.options,
|
|
26349
|
+
async run(args) {
|
|
26350
|
+
const { bin: _bin, ...report } = await diagnoseReport({
|
|
26351
|
+
root: args["root"],
|
|
26352
|
+
harness: args["harness"]
|
|
26353
|
+
});
|
|
26354
|
+
return result({
|
|
26355
|
+
...homeHeader({ description: rootCommand.description }),
|
|
26356
|
+
...report
|
|
26357
|
+
});
|
|
26358
|
+
}
|
|
24895
26359
|
}).command(first));
|
|
24896
26360
|
}
|
|
24897
26361
|
const textByDefault = {
|