@yagni-app/code 1.0.5 → 1.0.7

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.
Files changed (111) hide show
  1. package/README.md +89 -27
  2. package/dist/claudeCompat.d.ts +59 -0
  3. package/dist/claudeCompat.js +109 -2
  4. package/dist/claudePlugins.d.ts +45 -3
  5. package/dist/claudePlugins.js +129 -19
  6. package/dist/cli.js +28 -4
  7. package/dist/doctor.d.ts +42 -4
  8. package/dist/doctor.js +150 -15
  9. package/dist/extension/condensedTools.d.ts +12 -1
  10. package/dist/extension/condensedTools.js +17 -9
  11. package/dist/extension/index.d.ts +18 -5
  12. package/dist/extension/index.js +319 -72
  13. package/dist/extension/mcp/approval.d.ts +45 -0
  14. package/dist/extension/mcp/approval.js +164 -0
  15. package/dist/extension/mcp/auth.d.ts +124 -0
  16. package/dist/extension/mcp/auth.js +560 -0
  17. package/dist/extension/mcp/authStore.d.ts +61 -0
  18. package/dist/extension/mcp/authStore.js +105 -0
  19. package/dist/extension/mcp/callbackPage.d.ts +31 -0
  20. package/dist/extension/mcp/callbackPage.js +222 -0
  21. package/dist/extension/mcp/cliConfig.d.ts +12 -0
  22. package/dist/extension/mcp/cliConfig.js +12 -0
  23. package/dist/extension/mcp/config.d.ts +153 -0
  24. package/dist/extension/mcp/config.js +381 -0
  25. package/dist/extension/mcp/log.d.ts +28 -0
  26. package/dist/extension/mcp/log.js +82 -0
  27. package/dist/extension/mcp/manager.d.ts +100 -0
  28. package/dist/extension/mcp/manager.js +273 -0
  29. package/dist/extension/mcp/names.d.ts +25 -0
  30. package/dist/extension/mcp/names.js +40 -0
  31. package/dist/extension/mcp/panel.d.ts +33 -0
  32. package/dist/extension/mcp/panel.js +268 -0
  33. package/dist/extension/mcp/prompts.d.ts +23 -0
  34. package/dist/extension/mcp/prompts.js +93 -0
  35. package/dist/extension/mcp/startup.d.ts +55 -0
  36. package/dist/extension/mcp/startup.js +152 -0
  37. package/dist/extension/mcp/tools.d.ts +31 -0
  38. package/dist/extension/mcp/tools.js +117 -0
  39. package/dist/extension/mcp/transports.d.ts +17 -0
  40. package/dist/extension/mcp/transports.js +44 -0
  41. package/dist/extension/permission/gate.d.ts +104 -2
  42. package/dist/extension/permission/gate.js +387 -31
  43. package/dist/extension/permission/guardian.d.ts +24 -5
  44. package/dist/extension/permission/guardian.js +162 -24
  45. package/dist/extension/permissionRules/bashFileArgs.d.ts +39 -0
  46. package/dist/extension/permissionRules/bashFileArgs.js +236 -0
  47. package/dist/extension/permissionRules/engine.d.ts +50 -0
  48. package/dist/extension/permissionRules/engine.js +238 -0
  49. package/dist/extension/permissionRules/loadConfig.d.ts +53 -0
  50. package/dist/extension/permissionRules/loadConfig.js +90 -0
  51. package/dist/extension/permissionRules/parser.d.ts +38 -0
  52. package/dist/extension/permissionRules/parser.js +136 -0
  53. package/dist/extension/permissionRules/pathRules.d.ts +58 -0
  54. package/dist/extension/permissionRules/pathRules.js +120 -0
  55. package/dist/extension/permissionRules/shellRules.d.ts +52 -0
  56. package/dist/extension/permissionRules/shellRules.js +221 -0
  57. package/dist/extension/pipeline/invocation.d.ts +3 -6
  58. package/dist/extension/pipeline/invocation.js +3 -6
  59. package/dist/extension/pipeline/personas.js +5 -0
  60. package/dist/extension/pipeline/runner.d.ts +0 -1
  61. package/dist/extension/pipeline/runner.js +6 -14
  62. package/dist/extension/plugins/inventory.d.ts +88 -0
  63. package/dist/extension/plugins/inventory.js +144 -0
  64. package/dist/extension/plugins/panel.d.ts +45 -0
  65. package/dist/extension/plugins/panel.js +293 -0
  66. package/dist/extension/sandbox/bash.d.ts +99 -0
  67. package/dist/extension/sandbox/bash.js +190 -0
  68. package/dist/extension/sandbox/config.d.ts +114 -0
  69. package/dist/extension/sandbox/config.js +366 -0
  70. package/dist/extension/sandbox/manager.d.ts +98 -0
  71. package/dist/extension/sandbox/manager.js +216 -0
  72. package/dist/extension/sandbox/panel.d.ts +111 -0
  73. package/dist/extension/sandbox/panel.js +342 -0
  74. package/dist/extension/sandbox/session.d.ts +85 -0
  75. package/dist/extension/sandbox/session.js +775 -0
  76. package/dist/extension/telemetry/attrs.d.ts +96 -0
  77. package/dist/extension/telemetry/attrs.js +149 -0
  78. package/dist/extension/telemetry/config.d.ts +99 -0
  79. package/dist/extension/telemetry/config.js +193 -0
  80. package/dist/extension/telemetry/index.d.ts +7 -0
  81. package/dist/extension/telemetry/index.js +7 -0
  82. package/dist/extension/telemetry/probe.d.ts +29 -0
  83. package/dist/extension/telemetry/probe.js +122 -0
  84. package/dist/extension/telemetry/register.d.ts +40 -0
  85. package/dist/extension/telemetry/register.js +192 -0
  86. package/dist/extension/telemetry/sdk.d.ts +63 -0
  87. package/dist/extension/telemetry/sdk.js +207 -0
  88. package/dist/extension/telemetry/tracker.d.ts +131 -0
  89. package/dist/extension/telemetry/tracker.js +551 -0
  90. package/dist/extension/vendor/IGNORE-LICENSE-MIT +21 -0
  91. package/dist/extension/vendor/ignore.d.ts +86 -0
  92. package/dist/extension/vendor/ignore.js +788 -0
  93. package/dist/goHeadless.d.ts +1 -1
  94. package/dist/goHeadless.js +2 -2
  95. package/dist/launch.d.ts +4 -3
  96. package/dist/launch.js +7 -4
  97. package/dist/mcpCommand.d.ts +122 -0
  98. package/dist/mcpCommand.js +787 -0
  99. package/dist/otel.d.ts +69 -63
  100. package/dist/otel.js +154 -119
  101. package/dist/paths.d.ts +13 -0
  102. package/dist/paths.js +18 -0
  103. package/dist/pluginCommand.d.ts +43 -0
  104. package/dist/pluginCommand.js +499 -0
  105. package/dist/pluginStore.d.ts +170 -0
  106. package/dist/pluginStore.js +554 -0
  107. package/dist/upgrade.d.ts +11 -2
  108. package/dist/upgrade.js +48 -8
  109. package/package.json +20 -3
  110. package/dist/extension/mcpTools.d.ts +0 -57
  111. package/dist/extension/mcpTools.js +0 -132
package/dist/upgrade.js CHANGED
@@ -15,7 +15,7 @@
15
15
  * `yagni upgrade` still works there.
16
16
  */
17
17
  import { spawn } from "node:child_process";
18
- import { readFileSync } from "node:fs";
18
+ import { readFileSync, realpathSync } from "node:fs";
19
19
  import { mkdir, readFile, writeFile } from "node:fs/promises";
20
20
  import { dirname, join } from "node:path";
21
21
  import { fileURLToPath } from "node:url";
@@ -26,7 +26,9 @@ export const UPDATE_CHECK_DISABLE_ENV = "YAGNI_DISABLE_UPDATE_CHECK";
26
26
  const DEFAULT_REGISTRY = "https://registry.npmjs.org";
27
27
  const FETCH_TIMEOUT_MS = 5_000;
28
28
  const SEMVER_RE = /^(0|[1-9]\d{0,5})\.(0|[1-9]\d{0,5})\.(0|[1-9]\d{0,5})(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?$/;
29
- const INSTALL_METHODS = ["npm", "pnpm", "bun"];
29
+ const INSTALL_METHODS = ["npm", "pnpm", "bun", "brew"];
30
+ /** Fully qualified so `brew upgrade` can never resolve someone else's `yagni`. */
31
+ export const BREW_FORMULA = "yagni-app/tap/yagni";
30
32
  /**
31
33
  * The CLI's own version, read from this package's package.json. Resolved
32
34
  * relative to the module (src/ and dist/ both sit one level below the package
@@ -188,18 +190,34 @@ export async function maybeNudgeAndRefresh(deps) {
188
190
  // ── `yagni upgrade` ─────────────────────────────────────────────────────────
189
191
  /**
190
192
  * Which global installer owns the running bin, from its (real) path. The
191
- * heuristic covers the three installers the package supports; `--method` is
193
+ * heuristic covers the four installers the package supports; `--method` is
192
194
  * the escape hatch when a layout defeats it.
195
+ *
196
+ * The path is realpath'd first because Homebrew is invisible in argv[1]:
197
+ * `/opt/homebrew/bin/yagni` is a symlink chain into the Cellar, and only the
198
+ * resolved path contains the `/Cellar/` segment (macOS, Intel /usr/local,
199
+ * and Linuxbrew all share it). Detecting brew matters: running `npm install
200
+ * -g` over a brew install creates a shadowing parallel copy — or the exact
201
+ * managed-Mac EACCES the brew channel exists to avoid.
193
202
  */
194
203
  export function detectInstallMethod(binPath) {
195
- const normalized = binPath.replaceAll("\\", "/").toLowerCase();
204
+ let resolved = binPath;
205
+ try {
206
+ resolved = realpathSync(binPath);
207
+ }
208
+ catch {
209
+ // Nonexistent or unreadable (tests, exotic layouts): judge the raw path.
210
+ }
211
+ const normalized = resolved.replaceAll("\\", "/").toLowerCase();
212
+ if (normalized.includes("/cellar/"))
213
+ return "brew";
196
214
  if (normalized.includes("/.bun/"))
197
215
  return "bun";
198
216
  if (normalized.includes("pnpm"))
199
217
  return "pnpm";
200
218
  return "npm";
201
219
  }
202
- const UPGRADE_USAGE = `Usage: ${DISTRIBUTION.commandName} upgrade [version] [--method npm|pnpm|bun]`;
220
+ const UPGRADE_USAGE = `Usage: ${DISTRIBUTION.commandName} upgrade [version] [--method npm|pnpm|bun|brew]`;
203
221
  export function parseUpgradeArgs(args) {
204
222
  let target;
205
223
  let method;
@@ -232,9 +250,22 @@ export function parseUpgradeArgs(args) {
232
250
  return { ok: true, target, method };
233
251
  }
234
252
  function installArgv(method, target) {
253
+ // brew upgrades to whatever the tap formula publishes; it cannot pin a
254
+ // version, so `target` only tells us an upgrade is worthwhile.
255
+ if (method === "brew")
256
+ return ["upgrade", BREW_FORMULA];
235
257
  const spec = `${PACKAGE_NAME}@${target}`;
236
258
  return method === "npm" ? ["install", "-g", spec] : ["add", "-g", spec];
237
259
  }
260
+ function failureHint(method) {
261
+ if (method === "brew") {
262
+ return `Try \`brew update\` and \`brew reinstall ${BREW_FORMULA}\`, or \`brew doctor\` if that fails too.`;
263
+ }
264
+ // Never suggest sudo: on managed machines the fix is a user-writable
265
+ // prefix, which the installer sets up automatically.
266
+ return ("If it was a permissions error, the installer repairs the global prefix without sudo: " +
267
+ "curl -fsSL https://yagni.app/install.sh | sh");
268
+ }
238
269
  function defaultRunInstall(command, args) {
239
270
  return new Promise((resolve) => {
240
271
  // npm/pnpm/bun are .cmd shims on Windows; a shell is required to run them.
@@ -256,6 +287,11 @@ export async function upgradeCommand(args, deps) {
256
287
  }
257
288
  const env = deps.env ?? process.env;
258
289
  const method = parsed.method ?? detectInstallMethod(deps.binPath ?? process.argv[1] ?? "");
290
+ if (method === "brew" && parsed.target !== undefined) {
291
+ logError("Homebrew installs track the tap formula and cannot pin a version. " +
292
+ `Run \`${DISTRIBUTION.commandName} upgrade\` for the newest release.`);
293
+ return 1;
294
+ }
259
295
  const fetchLatest = deps.fetchLatest ?? (() => fetchLatestVersion({ env }));
260
296
  const target = parsed.target ?? (await fetchLatest());
261
297
  if (!target) {
@@ -272,13 +308,17 @@ export async function upgradeCommand(args, deps) {
272
308
  const runInstall = deps.runInstall ?? defaultRunInstall;
273
309
  const code = await runInstall(method, installArgv(method, target));
274
310
  if (code !== 0) {
275
- logError(`Upgrade failed (${method} exited ${code}). ` +
276
- `If it was a permissions error, fix your ${method} global prefix or retry with elevated permissions.`);
311
+ logError(`Upgrade failed (${method} exited ${code}). ${failureHint(method)}`);
277
312
  return 1;
278
313
  }
279
314
  // Keep the nudge honest: the freshly installed version is the known latest.
280
315
  await writeUpdateCache(target).catch(() => undefined);
281
- log(`${DISTRIBUTION.displayName} ${target} installed. Run \`${DISTRIBUTION.commandName} version\` to confirm.`);
316
+ // brew lands on whatever the formula publishes, which can trail the
317
+ // registry briefly, so only the package managers that pin get the
318
+ // version-specific claim.
319
+ log(method === "brew"
320
+ ? `${DISTRIBUTION.displayName} upgraded via Homebrew. Run \`${DISTRIBUTION.commandName} version\` to confirm.`
321
+ : `${DISTRIBUTION.displayName} ${target} installed. Run \`${DISTRIBUTION.commandName} version\` to confirm.`);
282
322
  return 0;
283
323
  }
284
324
  //# sourceMappingURL=upgrade.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yagni-app/code",
3
- "version": "1.0.5",
3
+ "version": "1.0.7",
4
4
  "description": "YAGNI Code: a terminal coding agent that already knows your company. One YAGNI login routes the model and grounds the agent in your team's context.",
5
5
  "license": "SEE LICENSE IN LICENSE.md",
6
6
  "author": "YAGNI, Inc. <jack@yagni.app> (https://yagni.app)",
@@ -34,12 +34,29 @@
34
34
  "main": "./dist/cli.js",
35
35
  "types": "./dist/cli.d.ts",
36
36
  "dependencies": {
37
+ "@anthropic-ai/sandbox-runtime": "0.0.74",
37
38
  "@earendil-works/pi-coding-agent": "0.84.1",
38
39
  "@earendil-works/pi-tui": "0.84.1",
39
- "pi-otel": "0.1.0",
40
+ "@grpc/grpc-js": "^1.14.4",
41
+ "@modelcontextprotocol/sdk": "^1.30.0",
42
+ "@opentelemetry/api": "^1.9.1",
43
+ "@opentelemetry/api-logs": "^0.222.0",
44
+ "@opentelemetry/exporter-logs-otlp-grpc": "^0.222.0",
45
+ "@opentelemetry/exporter-logs-otlp-http": "^0.222.0",
46
+ "@opentelemetry/exporter-logs-otlp-proto": "^0.222.0",
47
+ "@opentelemetry/exporter-metrics-otlp-grpc": "^0.222.0",
48
+ "@opentelemetry/exporter-metrics-otlp-http": "^0.222.0",
49
+ "@opentelemetry/exporter-metrics-otlp-proto": "^0.222.0",
50
+ "@opentelemetry/exporter-trace-otlp-grpc": "^0.222.0",
51
+ "@opentelemetry/exporter-trace-otlp-http": "^0.222.0",
52
+ "@opentelemetry/exporter-trace-otlp-proto": "^0.222.0",
53
+ "@opentelemetry/resources": "^2.11.0",
54
+ "@opentelemetry/sdk-logs": "^0.222.0",
55
+ "@opentelemetry/sdk-metrics": "^2.11.0",
56
+ "@opentelemetry/sdk-trace": "^2.11.0",
40
57
  "smol-toml": "^1.8.0",
41
58
  "turndown": "^7.2.4",
42
59
  "typebox": "^1.3.15"
43
60
  },
44
- "yagniSourceSha": "5bd359dcd98d8104d425f698a65ed60d780df0af"
61
+ "yagniSourceSha": "49b4f1be0c81923619b1dd54a977da9653deee1d"
45
62
  }
@@ -1,57 +0,0 @@
1
- /**
2
- * Workspace MCP servers in YAGNI Code (YAG-446).
3
- *
4
- * The workspace's registered MCP servers (Connections → MCP Servers) are
5
- * the config source: at session start we fetch the list from the backend
6
- * and register one pi tool per enabled server tool, named with the same
7
- * `mcp_<slug>__<tool>` wire shape the backend executor parses. Calls
8
- * execute THROUGH the backend (`POST /api/yagni-code/mcp/call`), so
9
- * credentials never reach this machine and the backend's egress guard,
10
- * autonomy gate, per-user credential policy, and audit trail all apply.
11
- *
12
- * Everything here is fail-soft against an older backend or an
13
- * un-rescoped token: a 403/404/network failure on the list fetch means
14
- * no MCP tools and no startup error.
15
- */
16
- import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
17
- export interface McpToolInfo {
18
- name: string;
19
- description: string | null;
20
- inputSchema: unknown;
21
- riskClass: string;
22
- }
23
- export interface McpServerInfo {
24
- slug: string;
25
- name: string;
26
- tools: McpToolInfo[];
27
- viewerCredential: {
28
- source: "user" | "workspace";
29
- };
30
- }
31
- export interface McpClientOpts {
32
- baseUrl: string;
33
- getToken: () => string | undefined;
34
- fetchImpl?: typeof fetch;
35
- }
36
- export declare function mcpWireToolName(slug: string, tool: string): string;
37
- /**
38
- * Fetch the workspace's MCP servers. Fail-soft by design: any non-OK
39
- * response (older backend without the endpoints, token minted before the
40
- * `yagni_code:mcp` scope existed) or transport error returns `[]`.
41
- */
42
- export declare function fetchMcpServers(opts: McpClientOpts): Promise<McpServerInfo[]>;
43
- /**
44
- * Register every reachable MCP tool. Returns the wire names of MUTATING
45
- * tools so the caller can extend the permission-gate policy — plan mode
46
- * holds them, review mode confirms them, exactly like write/edit/bash.
47
- */
48
- export declare function registerMcpTools(pi: ExtensionAPI, servers: McpServerInfo[], opts: McpClientOpts): {
49
- mutatingToolNames: string[];
50
- };
51
- /** Render the /mcp listing (pure, for tests). */
52
- export declare function renderMcpListing(servers: McpServerInfo[], baseUrl: string): string;
53
- /** Wire the `/mcp` command: list servers, tools, and credential status. */
54
- export declare function registerMcpCommand(pi: ExtensionAPI, servers: McpServerInfo[], opts: {
55
- baseUrl: string;
56
- }): void;
57
- //# sourceMappingURL=mcpTools.d.ts.map
@@ -1,132 +0,0 @@
1
- import { Type } from "typebox";
2
- import { friendlyFetchError, METERED_POST_FETCH_POLICY, resilientFetch } from "./resilientFetch.js";
3
- export function mcpWireToolName(slug, tool) {
4
- return `mcp_${slug}__${tool}`;
5
- }
6
- /**
7
- * Fetch the workspace's MCP servers. Fail-soft by design: any non-OK
8
- * response (older backend without the endpoints, token minted before the
9
- * `yagni_code:mcp` scope existed) or transport error returns `[]`.
10
- */
11
- export async function fetchMcpServers(opts) {
12
- try {
13
- const res = await resilientFetch(`${opts.baseUrl}/api/yagni-code/mcp/servers`, {
14
- method: "GET",
15
- headers: { authorization: `Bearer ${opts.getToken() ?? ""}` },
16
- }, { fetchImpl: opts.fetchImpl });
17
- if (!res.ok)
18
- return [];
19
- const data = (await res.json());
20
- if (!Array.isArray(data.servers))
21
- return [];
22
- return data.servers;
23
- }
24
- catch {
25
- return [];
26
- }
27
- }
28
- function schemaFor(tool) {
29
- const schema = tool.inputSchema;
30
- if (schema !== null &&
31
- typeof schema === "object" &&
32
- !Array.isArray(schema) &&
33
- Object.keys(schema).length > 0) {
34
- // MCP inputSchemas are plain JSON Schema objects, which is exactly what
35
- // TypeBox schemas are at runtime — pass the server's schema through so
36
- // the model sees real parameter shapes.
37
- return schema;
38
- }
39
- return Type.Object({}, { additionalProperties: true });
40
- }
41
- function makeMcpTool(server, tool, opts) {
42
- const wireName = mcpWireToolName(server.slug, tool.name);
43
- return {
44
- name: wireName,
45
- label: `${server.name}: ${tool.name}`,
46
- description: `${tool.description ?? `MCP tool ${tool.name}`} ` +
47
- `(MCP server "${server.name}", risk: ${tool.riskClass}; runs through YAGNI)`,
48
- parameters: schemaFor(tool),
49
- async execute(_toolCallId, params, signal) {
50
- const res = await resilientFetch(`${opts.baseUrl}/api/yagni-code/mcp/call`, {
51
- method: "POST",
52
- headers: {
53
- "content-type": "application/json",
54
- authorization: `Bearer ${opts.getToken() ?? ""}`,
55
- },
56
- body: JSON.stringify({
57
- slug: server.slug,
58
- tool: tool.name,
59
- args: params ?? {},
60
- }),
61
- }, { fetchImpl: opts.fetchImpl, signal, policy: METERED_POST_FETCH_POLICY });
62
- if (!res.ok) {
63
- throw new Error(await friendlyFetchError(wireName, res));
64
- }
65
- const result = (await res.json());
66
- if (!result.success) {
67
- const suffix = result.errorCode === "user_credential_required"
68
- ? ` Bind your token at ${opts.baseUrl}/services/mcp.`
69
- : "";
70
- throw new Error(`${result.error ?? "MCP tool call failed"}${suffix}`);
71
- }
72
- const text = typeof result.result === "string"
73
- ? result.result
74
- : JSON.stringify(result.result ?? null, null, 2);
75
- return {
76
- content: [{ type: "text", text }],
77
- details: { riskClass: tool.riskClass },
78
- };
79
- },
80
- };
81
- }
82
- /**
83
- * Register every reachable MCP tool. Returns the wire names of MUTATING
84
- * tools so the caller can extend the permission-gate policy — plan mode
85
- * holds them, review mode confirms them, exactly like write/edit/bash.
86
- */
87
- export function registerMcpTools(pi, servers, opts) {
88
- const mutatingToolNames = [];
89
- for (const server of servers) {
90
- for (const tool of server.tools) {
91
- pi.registerTool(makeMcpTool(server, tool, opts));
92
- if (tool.riskClass !== "read_only") {
93
- mutatingToolNames.push(mcpWireToolName(server.slug, tool.name));
94
- }
95
- }
96
- }
97
- return { mutatingToolNames };
98
- }
99
- /** Render the /mcp listing (pure, for tests). */
100
- export function renderMcpListing(servers, baseUrl) {
101
- if (servers.length === 0) {
102
- return [
103
- "No MCP servers connected for this workspace.",
104
- `An admin can register one under Connections: ${baseUrl}/services/mcp`,
105
- ].join("\n");
106
- }
107
- const lines = ["Workspace MCP servers:"];
108
- for (const server of servers) {
109
- const mutating = server.tools.filter((t) => t.riskClass !== "read_only").length;
110
- const credential = server.viewerCredential.source === "user"
111
- ? "connected as you"
112
- : "workspace credential";
113
- lines.push(` ${server.name} (${server.slug}) — ${server.tools.length} tool${server.tools.length === 1 ? "" : "s"}` +
114
- `${mutating > 0 ? ` (${mutating} mutating)` : ""} · ${credential}`);
115
- for (const tool of server.tools) {
116
- lines.push(` ${mcpWireToolName(server.slug, tool.name)} [${tool.riskClass}]`);
117
- }
118
- }
119
- lines.push(`Bind a personal token (writes carry your authority): ${baseUrl}/services/mcp`);
120
- return lines.join("\n");
121
- }
122
- /** Wire the `/mcp` command: list servers, tools, and credential status. */
123
- export function registerMcpCommand(pi, servers, opts) {
124
- pi.registerCommand("mcp", {
125
- description: "List this workspace's MCP servers, their tools, and your credential status.",
126
- handler: async (_args, ctx) => {
127
- if (ctx.hasUI)
128
- ctx.ui.notify(renderMcpListing(servers, opts.baseUrl), "info");
129
- },
130
- });
131
- }
132
- //# sourceMappingURL=mcpTools.js.map