buddy-agent-harness 0.9.0 → 0.10.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/dist/cli.mjs +1 -8
- package/dist/index.d.mts +39 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +155 -17
- package/dist/index.mjs.map +1 -1
- package/dist/skill-scripts/doctor.mjs +300 -0
- package/dist/skill-scripts/init.mjs +300 -0
- package/package.json +3 -2
- package/plugin.json +1 -1
- package/skills/doctor/SKILL.md +1 -1
- package/skills/doctor/scripts/doctor.mjs +299 -13
- package/skills/init/SKILL.md +1 -1
- package/skills/init/scripts/doctor.mjs +299 -13
- package/skills/init/scripts/init.mjs +299 -13
- package/skills/repair/SKILL.md +1 -1
- package/skills/repair/scripts/doctor.mjs +299 -13
- package/src/cli.ts +10 -2
- package/src/diagnose-bridges/doctor-guidance.ts +45 -26
- package/src/diagnose-mcp/mcp-secrets.ts +17 -0
- package/src/index.ts +2 -0
- package/src/mcp-inventory/mcp-inventory.ts +165 -0
- package/src/skill-scripts/doctor.ts +11 -0
- package/src/skill-scripts/init.ts +10 -0
- package/src/skill-scripts/launchers.ts +15 -0
package/dist/cli.mjs
CHANGED
|
@@ -18994,14 +18994,7 @@ const initCommand = command({
|
|
|
18994
18994
|
});
|
|
18995
18995
|
//#endregion
|
|
18996
18996
|
//#region src/cli.ts
|
|
18997
|
-
|
|
18998
|
-
* Read from the manifest rather than restated here: a version written into this call is a second
|
|
18999
|
-
* home for a number that already has one, and it stayed at `0.1.0` for five minor releases.
|
|
19000
|
-
*
|
|
19001
|
-
* `../package.json` resolves from `src/cli.ts` and from the bundled `dist/cli.mjs` alike — both sit
|
|
19002
|
-
* one directory under the package root.
|
|
19003
|
-
*/
|
|
19004
|
-
const version = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8")).version;
|
|
18997
|
+
const version = typeof __PACKAGE_VERSION__ !== "undefined" ? __PACKAGE_VERSION__ : JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8")).version;
|
|
19005
18998
|
/**
|
|
19006
18999
|
* A factory rather than a module-level constant: `cli()` builds state, and state built at import
|
|
19007
19000
|
* time is shared by every later call in the process.
|
package/dist/index.d.mts
CHANGED
|
@@ -598,5 +598,43 @@ type InitializeResult = {
|
|
|
598
598
|
};
|
|
599
599
|
export declare function initializeHarnesses({ root, harnesses: preferred, copy, force }: InitializeOptions): InitializeResult;
|
|
600
600
|
//#endregion
|
|
601
|
-
|
|
601
|
+
//#region src/mcp-inventory/mcp-inventory.d.ts
|
|
602
|
+
/** One configured MCP server, redacted of everything that could carry a credential. */
|
|
603
|
+
type McpServerEntry = {
|
|
604
|
+
harness: HarnessName;
|
|
605
|
+
scope: HarnessScopeName;
|
|
606
|
+
/** The absolute path of the config file this entry was read from. */
|
|
607
|
+
configFile: string;
|
|
608
|
+
/** The name the server is declared under. */
|
|
609
|
+
name: string;
|
|
610
|
+
transport: McpTransport | undefined;
|
|
611
|
+
/** The command's basename, never its full path. */
|
|
612
|
+
command: string | undefined;
|
|
613
|
+
/** `args`, with any credential-bearing `--flag=value` pair dropped. */
|
|
614
|
+
args: readonly string[];
|
|
615
|
+
/** The URL's origin and path, never its query string. */
|
|
616
|
+
url: string | undefined;
|
|
617
|
+
enabled: boolean;
|
|
618
|
+
};
|
|
619
|
+
type ListMcpServersOptions = {
|
|
620
|
+
/** The project directory to read project-scope configuration from. */
|
|
621
|
+
projectDir: string;
|
|
622
|
+
/** Overrides `os.homedir()`, for a test that must not touch the real one. */
|
|
623
|
+
homeDir?: string;
|
|
624
|
+
/** Overrides `process.env`, for a test — read only for `CODEX_HOME` and `COPILOT_HOME`. */
|
|
625
|
+
env?: Readonly<Record<string, string | undefined>>;
|
|
626
|
+
};
|
|
627
|
+
/**
|
|
628
|
+
* Every MCP server configured for a project, across every supported harness and every scope that
|
|
629
|
+
* harness documents — without ever surfacing a credential.
|
|
630
|
+
*
|
|
631
|
+
* Reuses `diagnoseMcp`'s own parsing (`parseTarget`), model (`McpServer`), and redaction
|
|
632
|
+
* (`nonSecretArgs`, and the URL/command trimming this module adds) rather than re-reading or
|
|
633
|
+
* re-parsing any of the supported harnesses' MCP files. A harness with no file present, or one whose
|
|
634
|
+
* file does not parse, contributes no entries; this function reports an inventory, not a diagnosis,
|
|
635
|
+
* so it has nothing to say about why a file is missing or broken.
|
|
636
|
+
*/
|
|
637
|
+
export declare function listMcpServers(options: ListMcpServersOptions): McpServerEntry[];
|
|
638
|
+
//#endregion
|
|
639
|
+
export type { BridgeFinding, BridgeKind, BridgeProblem, BridgeReport, BridgeStatus, ConfigurationFault, ConfigurationProblem, DiagnoseMcpOptions, DiagnoseOptions, DiagnoseResult, DivergenceDirection, DivergenceReport, DoctorProblem, DoctorReport, Harness, HarnessName, HarnessScope, HarnessScopeName, InitializeOptions, InitializeResult, InstructionBridge, InstructionKind, InstructionProblem, InstructionReport, InstructionStatus, ListMcpServersOptions, McpConfig, McpDirection, McpField, McpProblem, McpServer, McpServerEntry, McpTransport, Repair };
|
|
602
640
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/cli.ts","../src/harness-registry/instruction-bridge.ts","../src/harness-registry/mcp-config.ts","../src/harness-registry/nonstandard-artifact.ts","../src/harness-registry/harness-registry.ts","../src/diagnose-bridges/diagnose-instructions.ts","../src/diagnose-bridges/locator.ts","../src/diagnose-bridges/doctor-guidance.ts","../src/diagnose-bridges/git-bridge-state.ts","../src/diagnose-bridges/diagnose-bridges.ts","../src/diagnose-configuration/diagnose-configuration.ts","../src/diagnose-bridges/doctor.command.ts","../src/diagnose-mcp/diagnose-mcp.ts","../src/diagnose-mcp/mcp-model.ts","../src/diagnose-mcp/mcp-baseline.ts","../src/diagnose-mcp/mcp-sources.ts","../src/initialize-harnesses/init.command.ts","../src/skill-projection/skill-projection.ts","../src/initialize-harnesses/initialize-harnesses.ts"],"mappings":";;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/cli.ts","../src/harness-registry/instruction-bridge.ts","../src/harness-registry/mcp-config.ts","../src/harness-registry/nonstandard-artifact.ts","../src/harness-registry/harness-registry.ts","../src/diagnose-bridges/diagnose-instructions.ts","../src/diagnose-bridges/locator.ts","../src/diagnose-bridges/doctor-guidance.ts","../src/diagnose-bridges/git-bridge-state.ts","../src/diagnose-bridges/diagnose-bridges.ts","../src/diagnose-configuration/diagnose-configuration.ts","../src/diagnose-bridges/doctor.command.ts","../src/diagnose-mcp/diagnose-mcp.ts","../src/diagnose-mcp/mcp-model.ts","../src/diagnose-mcp/mcp-baseline.ts","../src/diagnose-mcp/mcp-sources.ts","../src/initialize-harnesses/init.command.ts","../src/skill-projection/skill-projection.ts","../src/initialize-harnesses/initialize-harnesses.ts","../src/mcp-inventory/mcp-inventory.ts"],"mappings":"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"}
|
package/dist/index.mjs
CHANGED
|
@@ -2,7 +2,7 @@ import { createRequire } from "node:module";
|
|
|
2
2
|
import { cpSync, existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, readlinkSync, realpathSync, rmSync, statSync, symlinkSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { cli, command, exitCodes, z } from "clibuilder";
|
|
4
4
|
import { homedir, platform } from "node:os";
|
|
5
|
-
import { dirname, join, posix, relative, resolve, sep } from "node:path";
|
|
5
|
+
import { basename, dirname, join, posix, relative, resolve, sep } from "node:path";
|
|
6
6
|
import { encode } from "@toon-format/toon";
|
|
7
7
|
import { parse } from "smol-toml";
|
|
8
8
|
import { execFileSync } from "node:child_process";
|
|
@@ -955,10 +955,10 @@ const skillInvocation = (version) => `npx -y ${commandInvocation}@^${version}`;
|
|
|
955
955
|
*/
|
|
956
956
|
const launcherFor = (subcommand) => `scripts/${subcommand}.mjs`;
|
|
957
957
|
/**
|
|
958
|
-
* How a skill names its
|
|
958
|
+
* How a skill names its script: the path as the skill sees it, resolved by the agent against the
|
|
959
959
|
* directory it read the `SKILL.md` from.
|
|
960
960
|
*
|
|
961
|
-
* `node` stays in front. The
|
|
961
|
+
* `node` stays in front. The script ships without an executable bit, and its shebang does nothing
|
|
962
962
|
* on Windows, so naming the file alone would not run it.
|
|
963
963
|
*/
|
|
964
964
|
const launcherInvocation = (subcommand) => `node ${launcherFor(subcommand)}`;
|
|
@@ -1323,7 +1323,7 @@ Diagnose it:
|
|
|
1323
1323
|
${launcherInvocation("doctor")}
|
|
1324
1324
|
\`\`\`
|
|
1325
1325
|
|
|
1326
|
-
That path is relative to this skill's own directory.
|
|
1326
|
+
That path is relative to this skill's own directory: \`scripts/doctor.mjs\` is a self-contained bundle built from this package's source and shipped inside the skill folder through the npm package, so it runs against the current working directory with nothing downloaded and no \`node_modules\` needed. Fall back to \`${skillInvocation(version)} doctor\` when \`scripts/doctor.mjs\` is missing or cannot be run — the case for a skill installed from git rather than from the npm package, which does not carry the bundle.
|
|
1327
1327
|
|
|
1328
1328
|
The command is read-only. It never repairs anything, so it is safe to run at any point, including from a session-start hook.
|
|
1329
1329
|
|
|
@@ -1831,9 +1831,25 @@ function credentialFields(server) {
|
|
|
1831
1831
|
...server.url && urlHoldsCredential(server.url) ? ["url"] : []
|
|
1832
1832
|
];
|
|
1833
1833
|
}
|
|
1834
|
+
/**
|
|
1835
|
+
* `args` with any `--flag=value` pair dropped whose flag names a credential and whose value is a
|
|
1836
|
+
* literal rather than a reference — the same test `credentialKey` and `holdsLiteral` apply to `env`
|
|
1837
|
+
* and `headers`, applied here to the one field that carries names and values in a single string
|
|
1838
|
+
* instead of a map.
|
|
1839
|
+
*
|
|
1840
|
+
* A positional argument, or a flag with no `=`, passes through unfiltered: `-y` and a package name
|
|
1841
|
+
* are not a place a credential's value is written, and there is no key in either to test.
|
|
1842
|
+
*/
|
|
1843
|
+
function nonSecretArgs(args) {
|
|
1844
|
+
return (args ?? []).filter((arg) => {
|
|
1845
|
+
const at = arg.indexOf("=");
|
|
1846
|
+
if (at === -1) return true;
|
|
1847
|
+
return !(credentialKey(arg.slice(0, at)) && holdsLiteral(arg.slice(at + 1)));
|
|
1848
|
+
});
|
|
1849
|
+
}
|
|
1834
1850
|
//#endregion
|
|
1835
1851
|
//#region src/diagnose-mcp/diagnose-mcp.ts
|
|
1836
|
-
function read(root, path) {
|
|
1852
|
+
function read$1(root, path) {
|
|
1837
1853
|
const absolute = join(root, path);
|
|
1838
1854
|
return existsSync(absolute) ? readFileSync(absolute, "utf8") : void 0;
|
|
1839
1855
|
}
|
|
@@ -1884,7 +1900,7 @@ function diagnoseMcp({ root, git, cli }) {
|
|
|
1884
1900
|
const targets = /* @__PURE__ */ new Map();
|
|
1885
1901
|
const configs = [];
|
|
1886
1902
|
for (const config of targetsOf(root)) {
|
|
1887
|
-
const parsed = parseTarget(config, read(root, config.path));
|
|
1903
|
+
const parsed = parseTarget(config, read$1(root, config.path));
|
|
1888
1904
|
if (parsed.kind === "unreadable") {
|
|
1889
1905
|
add({ file: config.path }, "mcp-target-unreadable");
|
|
1890
1906
|
continue;
|
|
@@ -1894,7 +1910,7 @@ function diagnoseMcp({ root, git, cli }) {
|
|
|
1894
1910
|
targets.set(config.path, parsed.servers);
|
|
1895
1911
|
configs.push(config);
|
|
1896
1912
|
}
|
|
1897
|
-
const golden = parseGoldenSet(read(root, goldenSetPath));
|
|
1913
|
+
const golden = parseGoldenSet(read$1(root, goldenSetPath));
|
|
1898
1914
|
if (golden.kind === "unreadable") {
|
|
1899
1915
|
add({
|
|
1900
1916
|
file: goldenSetPath,
|
|
@@ -1906,7 +1922,7 @@ function diagnoseMcp({ root, git, cli }) {
|
|
|
1906
1922
|
reportSecrets(goldenSetPath, golden.servers);
|
|
1907
1923
|
const baseline = new McpBaseline({
|
|
1908
1924
|
git,
|
|
1909
|
-
record: parseProjectionRecord(read(root, projectionRecordPath))
|
|
1925
|
+
record: parseProjectionRecord(read$1(root, projectionRecordPath))
|
|
1910
1926
|
});
|
|
1911
1927
|
for (const config of configs) {
|
|
1912
1928
|
const servers = targets.get(config.path);
|
|
@@ -2690,14 +2706,7 @@ function activate({ addCommand }) {
|
|
|
2690
2706
|
}
|
|
2691
2707
|
//#endregion
|
|
2692
2708
|
//#region src/cli.ts
|
|
2693
|
-
|
|
2694
|
-
* Read from the manifest rather than restated here: a version written into this call is a second
|
|
2695
|
-
* home for a number that already has one, and it stayed at `0.1.0` for five minor releases.
|
|
2696
|
-
*
|
|
2697
|
-
* `../package.json` resolves from `src/cli.ts` and from the bundled `dist/cli.mjs` alike — both sit
|
|
2698
|
-
* one directory under the package root.
|
|
2699
|
-
*/
|
|
2700
|
-
const version = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8")).version;
|
|
2709
|
+
const version = typeof __PACKAGE_VERSION__ !== "undefined" ? __PACKAGE_VERSION__ : JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8")).version;
|
|
2701
2710
|
/**
|
|
2702
2711
|
* A factory rather than a module-level constant: `cli()` builds state, and state built at import
|
|
2703
2712
|
* time is shared by every later call in the process.
|
|
@@ -2730,6 +2739,135 @@ async function run(argv) {
|
|
|
2730
2739
|
}
|
|
2731
2740
|
}
|
|
2732
2741
|
//#endregion
|
|
2733
|
-
|
|
2742
|
+
//#region src/mcp-inventory/mcp-inventory.ts
|
|
2743
|
+
/**
|
|
2744
|
+
* A public, redaction-safe inventory of configured MCP servers: what `diagnoseMcp` already knows how
|
|
2745
|
+
* to read, reshaped for a consumer that wants a list rather than a drift diagnosis.
|
|
2746
|
+
*
|
|
2747
|
+
* This is a **different question** than `doctor` answers, not a wider version of it. `doctor`
|
|
2748
|
+
* compares project-scope configuration against a golden set the user authors, and stays
|
|
2749
|
+
* project-scope only by design — see `apps/web/src/content/docs/agent-configuration/mcp-servers.md`
|
|
2750
|
+
* for why reading a user's home directory is a wider blast radius than diagnosis needs. An inventory
|
|
2751
|
+
* has no golden set to compare against and nothing to diagnose; it answers "what MCP servers does
|
|
2752
|
+
* this project, and this machine, already have configured", which is a question a consumer can only
|
|
2753
|
+
* answer by reading both scopes. Adding user scope here changes nothing about what `doctor` reads:
|
|
2754
|
+
* `diagnoseMcp` still touches `harness.project.mcpConfig` alone.
|
|
2755
|
+
*
|
|
2756
|
+
* Every field that could carry a credential is reduced before it leaves this module, the same
|
|
2757
|
+
* discipline `mcp-secrets.ts` documents for `doctor`'s own findings: `command` is trimmed to its
|
|
2758
|
+
* basename, `args` drops any `--flag=value` pair `nonSecretArgs` flags, and `url` is trimmed to its
|
|
2759
|
+
* origin and path. `env` and `headers` are not returned at all — a name alone does not say whether
|
|
2760
|
+
* the value behind it is a literal or a reference, and the safe answer is not to carry the map.
|
|
2761
|
+
*/
|
|
2762
|
+
/** Where a harness keeps its **user-scope** MCP configuration, as that harness documents it.
|
|
2763
|
+
*
|
|
2764
|
+
* Unlike `harnessRegistry`, this table is read only from here: it is not wired into `doctor`, and
|
|
2765
|
+
* adding a harness to it never changes what `doctor` reads. Codex and Copilot CLI each let their
|
|
2766
|
+
* whole configuration directory move via an environment variable — `CODEX_HOME` and `COPILOT_HOME`
|
|
2767
|
+
* — so those two resolve through `env` before falling back to the home directory; the rest do not
|
|
2768
|
+
* document one.
|
|
2769
|
+
*/
|
|
2770
|
+
function userScopeConfig(harness, home, env) {
|
|
2771
|
+
switch (harness) {
|
|
2772
|
+
case "claude-code": return {
|
|
2773
|
+
file: join(home, ".claude.json"),
|
|
2774
|
+
config: {
|
|
2775
|
+
path: ".claude.json",
|
|
2776
|
+
key: "mcpServers",
|
|
2777
|
+
format: "json"
|
|
2778
|
+
}
|
|
2779
|
+
};
|
|
2780
|
+
case "cursor": return {
|
|
2781
|
+
file: join(home, ".cursor", "mcp.json"),
|
|
2782
|
+
config: {
|
|
2783
|
+
path: ".cursor/mcp.json",
|
|
2784
|
+
key: "mcpServers",
|
|
2785
|
+
format: "json"
|
|
2786
|
+
}
|
|
2787
|
+
};
|
|
2788
|
+
case "codex": return {
|
|
2789
|
+
file: join(env["CODEX_HOME"] || join(home, ".codex"), "config.toml"),
|
|
2790
|
+
config: {
|
|
2791
|
+
path: "config.toml",
|
|
2792
|
+
key: "mcp_servers",
|
|
2793
|
+
format: "toml"
|
|
2794
|
+
}
|
|
2795
|
+
};
|
|
2796
|
+
case "copilot-cli": return {
|
|
2797
|
+
file: join(env["COPILOT_HOME"] || join(home, ".copilot"), "mcp-config.json"),
|
|
2798
|
+
config: {
|
|
2799
|
+
path: "mcp-config.json",
|
|
2800
|
+
key: "mcpServers",
|
|
2801
|
+
format: "json"
|
|
2802
|
+
}
|
|
2803
|
+
};
|
|
2804
|
+
case "gemini-cli": return {
|
|
2805
|
+
file: join(home, ".gemini", "settings.json"),
|
|
2806
|
+
config: {
|
|
2807
|
+
path: ".gemini/settings.json",
|
|
2808
|
+
key: "mcpServers",
|
|
2809
|
+
format: "json",
|
|
2810
|
+
shared: true
|
|
2811
|
+
}
|
|
2812
|
+
};
|
|
2813
|
+
default: return;
|
|
2814
|
+
}
|
|
2815
|
+
}
|
|
2816
|
+
function read(file) {
|
|
2817
|
+
return existsSync(file) ? readFileSync(file, "utf8") : void 0;
|
|
2818
|
+
}
|
|
2819
|
+
/** A URL trimmed to its origin and path, dropping any query string a token could ride in on. */
|
|
2820
|
+
function safeUrl(value) {
|
|
2821
|
+
if (!value) return void 0;
|
|
2822
|
+
try {
|
|
2823
|
+
const url = new URL(value);
|
|
2824
|
+
return `${url.origin}${url.pathname}`;
|
|
2825
|
+
} catch {
|
|
2826
|
+
return;
|
|
2827
|
+
}
|
|
2828
|
+
}
|
|
2829
|
+
function toEntry(harness, scope, file, name, server) {
|
|
2830
|
+
return {
|
|
2831
|
+
harness,
|
|
2832
|
+
scope,
|
|
2833
|
+
configFile: file,
|
|
2834
|
+
name,
|
|
2835
|
+
transport: server.transport,
|
|
2836
|
+
command: server.command ? basename(server.command) : void 0,
|
|
2837
|
+
args: nonSecretArgs(server.args),
|
|
2838
|
+
url: safeUrl(server.url),
|
|
2839
|
+
enabled: server.enabled !== false
|
|
2840
|
+
};
|
|
2841
|
+
}
|
|
2842
|
+
function entriesFrom(harness, scope, file, config) {
|
|
2843
|
+
const parsed = parseTarget(config, read(file));
|
|
2844
|
+
if (parsed.kind !== "servers") return [];
|
|
2845
|
+
return [...parsed.servers].map(([name, server]) => toEntry(harness, scope, file, name, server));
|
|
2846
|
+
}
|
|
2847
|
+
/**
|
|
2848
|
+
* Every MCP server configured for a project, across every supported harness and every scope that
|
|
2849
|
+
* harness documents — without ever surfacing a credential.
|
|
2850
|
+
*
|
|
2851
|
+
* Reuses `diagnoseMcp`'s own parsing (`parseTarget`), model (`McpServer`), and redaction
|
|
2852
|
+
* (`nonSecretArgs`, and the URL/command trimming this module adds) rather than re-reading or
|
|
2853
|
+
* re-parsing any of the supported harnesses' MCP files. A harness with no file present, or one whose
|
|
2854
|
+
* file does not parse, contributes no entries; this function reports an inventory, not a diagnosis,
|
|
2855
|
+
* so it has nothing to say about why a file is missing or broken.
|
|
2856
|
+
*/
|
|
2857
|
+
function listMcpServers(options) {
|
|
2858
|
+
const home = options.homeDir ?? homedir();
|
|
2859
|
+
const env = options.env ?? process.env;
|
|
2860
|
+
const entries = [];
|
|
2861
|
+
for (const harness of harnessRegistry) {
|
|
2862
|
+
if (harness.deprecated) continue;
|
|
2863
|
+
const project = harness.project.mcpConfig;
|
|
2864
|
+
if (project) entries.push(...entriesFrom(harness.name, "project", join(options.projectDir, project.path), project));
|
|
2865
|
+
const user = userScopeConfig(harness.name, home, env);
|
|
2866
|
+
if (user) entries.push(...entriesFrom(harness.name, "user", user.file, user.config));
|
|
2867
|
+
}
|
|
2868
|
+
return entries;
|
|
2869
|
+
}
|
|
2870
|
+
//#endregion
|
|
2871
|
+
export { activate, bridgeRepairs, buildDoctorReport, diagnoseBridges, diagnoseInstructions, diagnoseMcp, doctorCommand, doctorRepairs, doctorSkill, goldenSetPath, harnessCommand, harnessRegistry, initCommand, initializeHarnesses, instructionRepairs, listMcpServers, renderDoctorSkill, run };
|
|
2734
2872
|
|
|
2735
2873
|
//# sourceMappingURL=index.mjs.map
|