buddy-agent-harness 0.9.1 → 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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "buddy-agent-harness",
3
- "version": "0.9.1",
3
+ "version": "0.10.0",
4
4
  "description": "Initialize standards-based agent configuration across agent harnesses",
5
5
  "author": {
6
6
  "name": "repobuddy"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "buddy-agent-harness",
3
- "version": "0.9.1",
3
+ "version": "0.10.0",
4
4
  "description": "Initialize standards-based agent configuration across agent harnesses",
5
5
  "author": {
6
6
  "name": "repobuddy"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "buddy-agent-harness",
3
- "version": "0.9.1",
3
+ "version": "0.10.0",
4
4
  "description": "Initialize standards-based agent configuration across agent harnesses",
5
5
  "author": {
6
6
  "name": "repobuddy"
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
- 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, McpConfig, McpDirection, McpField, McpProblem, McpServer, McpTransport, Repair };
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
@@ -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":";;;;;;;;;;;;;wBAiDsB,IAAI,iBAAiB;;;;;;;;;;;;;;;KCrC/B;;;;;;EAOT;;EAEA;;;EAIA;;EAEA;;EAEA;;;;;;;;;;;;;;;;;;;;KCbS;;EAEX;;;;;;EAMA;;;;;EAKA;;;;;;EAMA;;;;;;;;;;;;;;;;;KCtBW;KAgBA;;;;;EAKX;;EAEA;EACA,MAAM;;;;KC/BK;;KAUA;;;;;;;;;;;;;;;;;;;;;;;;;KA0BA;EACX;EACA;EACA,oBAAoB;EACpB,YAAY;EACZ,uBAAuB;;KAGZ;EACX,MAAM;;EAEN,SAAS;;;;;EAKT,OAAO;;EAEP,aAAa;;;;;;;;;;;;;;;;;;;;;;qBAuBD,0BAA0B;;;;KCzE3B;;;;;;KAMA;KAEA;EACX,SAAS;;EAET;EACA,MAAM;EACN,QAAQ;;;;;;;;;;wBAkFO,qBACf,cACA,oBAAoB,WACpB;EACI,cAAc;EAAqB,UAAU;;;;;;;;;;;;;;;;;;;;KC5FtC;EAAa;EAAc;;KAE3B;;EAEX;;EAEA;;EAEA;;EAEA,WAAW;;;;;;;;;;KCbA;;;;;;;;;;;KAqBA;;;;;;;;;KAUA;;;;;;;;;KAoBA;KAOA;;KAOA,gBAAgB,gBAAgB,qBAAqB,qBAAqB,aAAa;;KAGvF,uBAAuB;;;;;;;;;;;;;;;;;;KAmBvB;;EAEX;;EAEA;;KAGW;EACX,SAAS;;EAET;;;;;;;EAOA,OAAO,IAAI,SAAS,cAAc;;;;;;EAMlC,YAAY,IAAI;;qBAyLJ,wBAAwB;qBA4CxB,6BAA6B;qBAuM7B,wBAAwB;qBAOxB;WACZ;WACA;;;;;;;;;;wBAmCe,kBAAkB;;;;KC/kBtB;;KAGA;;;;;;cAwBC;mBAIA;;mBAFK;EAEjB,YAAY;;;;;;EASZ,WAAW,eAAe;;;;;;;EAa1B,UAAU;;;;;;;;;EAaV,gBAAgB;;EAMhB,UAAU,gBAAgB;;;;;EAS1B,YAAY,gBAAgB,oBAAoB;;UAYxC;;;;;UAmBA;;;;KC5GG;KACA;KAEA;EACX,SAAS;;EAET;EACA,MAAM;EACN,QAAQ;;KAGG;EACX;;;;;EAKA,SAAS;EACT;;EAEA,QAAQ;;KAGG;EACX;EACA,WAAW;;KAGA;EACX;EACA,YAAY;;EAEZ;;KAGW;EACX,SAAS;;;;;EAKT,cAAc;EACd,YAAY;EACZ,UAAU;;;;;;;wBA2EK,kBAAkB,MAAM,WAAW,WAAgB,OAAO,kBAAkB;;;;;;;;;;;;;;;;;KC1GhF;;EAEX;EACA,SAAS;EACT;;EAEA,QAAQ;;;;KCfG;EACX;EACA,SAAS;EACT,cAAc;EACd,aAAa;;;;;EAKb;IAAY;IAAc,SAAS;IAAe;;;;;;;;;;EASlD,OAAO;;;;;;wBAOQ,kBACf,aACA,QAAQ,gBACR,gBAAe,yBACb;qBAgCU,eAAe,MAAI;;;;;;;;;;;;;;;;;;KC7CpB;EACX;EACA,KAAK;;EAEL;;wBAgCe,cAAc,MAAM,KAAK,OAAO,qBAAqB;;;;;;;;;;;;;;;;KClDzD;KAEA;EACX,YAAY;EACZ;EACA;EACA,MAAM,SAAS;EACf;EACA,UAAU,SAAS;EACnB;EACA;EACA;EACA;;;cAIY;KAaD,mBAAmB;;;;KCnCnB;;;;qBCAC;;;qBCAA,aAAa,MAAI;qBAoDjB,gBAAgB,MAAI;wBAMjB,WAAW;EAAgB,WAAW,gBAAgB;;;;;;;;;KCV1D;;;KCpDA;EACX;EACA,YAAY;EACZ;EACA,QAAQ;;KAEG;EACX;EACA,WAAW;EACX,QAAQ;EACR,QAAQ;;EAER,SAAS;;EAET;IAAc,MAAM;IAAa,YAAY;;EAC7C;EACA;;wBAGe,sBACf,MACA,WAAW,WACX,MACA,SACE,oBAAoB"}
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";
@@ -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);
@@ -2723,6 +2739,135 @@ async function run(argv) {
2723
2739
  }
2724
2740
  }
2725
2741
  //#endregion
2726
- export { activate, bridgeRepairs, buildDoctorReport, diagnoseBridges, diagnoseInstructions, diagnoseMcp, doctorCommand, doctorRepairs, doctorSkill, goldenSetPath, harnessCommand, harnessRegistry, initCommand, initializeHarnesses, instructionRepairs, renderDoctorSkill, run };
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 };
2727
2872
 
2728
2873
  //# sourceMappingURL=index.mjs.map