@agent-native/skills 0.2.10 → 0.2.12

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/dist/connect.d.ts CHANGED
@@ -9,14 +9,13 @@
9
9
  * SAME device-code/OAuth protocol as core.
10
10
  *
11
11
  * Two client families, exactly as in core:
12
- * - OAuth-capable (claude-code, claude-code-cli): get a URL-only HTTP MCP
13
- * entry (no bearer headers). The user authenticates in-host via standard
14
- * remote MCP OAuth: restart Claude Code, run /mcp, choose Authenticate.
12
+ * - OAuth-capable (Claude Code, Cursor, OpenCode, GitHub Copilot / VS Code):
13
+ * get a URL-only HTTP MCP entry (no bearer headers). The user authenticates
14
+ * in-host via standard remote MCP OAuth.
15
15
  * - Device-code (codex, cowork): run the browser device-code flow against the
16
16
  * descriptor's hosted URL, then write the entry WITH the minted bearer token
17
- * + headers. Non-interactive (or no TTY) skips the flow and writes a
18
- * URL-only entry, surfacing the exact `agent-native connect <url> --token`
19
- * fallback command.
17
+ * + headers. Non-interactive (or no TTY) skips writing device-code configs,
18
+ * surfacing the exact `agent-native connect <url>` fallback command.
20
19
  *
21
20
  * Server contract (identical paths + JSON field names to core):
22
21
  * POST <hostedUrl>/_agent-native/mcp/connect/device/start (no auth)
@@ -1 +1 @@
1
- {"version":3,"file":"connect.d.ts","sourceRoot":"","sources":["../src/connect.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,OAAO,EAAE,QAAQ,EAA2B,MAAM,yBAAyB,CAAC;AAgB5E;;;;;;;GAOG;AACH,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAC;IACvC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,aAAa,CAAC;IAC1B,OAAO,EAAE,QAAQ,EAAE,CAAC;IACpB,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,OAAO,CAAC;IACrB,GAAG,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,IAAI,CAAC,EAAE;QACL,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;QACzB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;QACnB,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;KACvC,CAAC;CACH;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE;QAAE,MAAM,EAAE,QAAQ,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC9C,aAAa,EAAE,OAAO,CAAC;IACvB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AA0CD,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,QAAQ,GAAG,OAAO,CAEhE;AAuUD;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,kBAAkB,GACvB,OAAO,CAAC,iBAAiB,CAAC,CAyH5B"}
1
+ {"version":3,"file":"connect.d.ts","sourceRoot":"","sources":["../src/connect.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,OAAO,EAAE,QAAQ,EAA2B,MAAM,yBAAyB,CAAC;AA6B5E;;;;;;;GAOG;AACH,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAC;IACvC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,aAAa,CAAC;IAC1B,OAAO,EAAE,QAAQ,EAAE,CAAC;IACpB,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,OAAO,CAAC;IACrB,GAAG,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,IAAI,CAAC,EAAE;QACL,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;QACzB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;QACnB,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;KACvC,CAAC;CACH;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE;QAAE,MAAM,EAAE,QAAQ,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC9C,aAAa,EAAE,OAAO,CAAC;IACvB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AA0CD,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,QAAQ,GAAG,OAAO,CAEhE;AAmWD;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,kBAAkB,GACvB,OAAO,CAAC,iBAAiB,CAAC,CAyH5B"}
package/dist/connect.js CHANGED
@@ -9,14 +9,13 @@
9
9
  * SAME device-code/OAuth protocol as core.
10
10
  *
11
11
  * Two client families, exactly as in core:
12
- * - OAuth-capable (claude-code, claude-code-cli): get a URL-only HTTP MCP
13
- * entry (no bearer headers). The user authenticates in-host via standard
14
- * remote MCP OAuth: restart Claude Code, run /mcp, choose Authenticate.
12
+ * - OAuth-capable (Claude Code, Cursor, OpenCode, GitHub Copilot / VS Code):
13
+ * get a URL-only HTTP MCP entry (no bearer headers). The user authenticates
14
+ * in-host via standard remote MCP OAuth.
15
15
  * - Device-code (codex, cowork): run the browser device-code flow against the
16
16
  * descriptor's hosted URL, then write the entry WITH the minted bearer token
17
- * + headers. Non-interactive (or no TTY) skips the flow and writes a
18
- * URL-only entry, surfacing the exact `agent-native connect <url> --token`
19
- * fallback command.
17
+ * + headers. Non-interactive (or no TTY) skips writing device-code configs,
18
+ * surfacing the exact `agent-native connect <url>` fallback command.
20
19
  *
21
20
  * Server contract (identical paths + JSON field names to core):
22
21
  * POST <hostedUrl>/_agent-native/mcp/connect/device/start (no auth)
@@ -40,7 +39,19 @@ const MCP_PATH = "/_agent-native/mcp";
40
39
  const REMOTE_MCP_OAUTH_CLIENTS = new Set([
41
40
  "claude-code",
42
41
  "claude-code-cli",
42
+ "cursor",
43
+ "opencode",
44
+ "github-copilot",
43
45
  ]);
46
+ const CLIENT_LABELS = {
47
+ "claude-code": "Claude Code",
48
+ "claude-code-cli": "Claude Code CLI",
49
+ codex: "Codex",
50
+ cowork: "Claude Cowork",
51
+ cursor: "Cursor",
52
+ opencode: "OpenCode",
53
+ "github-copilot": "GitHub Copilot / VS Code",
54
+ };
44
55
  // ---------------------------------------------------------------------------
45
56
  // Helpers
46
57
  // ---------------------------------------------------------------------------
@@ -153,6 +164,22 @@ function manualConnectGuidance(baseUrl, clients, scope) {
153
164
  return (`To authenticate ${describeClients(clients)}, run: ` +
154
165
  fallbackConnectCommand(baseUrl, clients, scope));
155
166
  }
167
+ function oauthNextStepsForClients(clients, serverName) {
168
+ const lines = [];
169
+ if (clients.includes("claude-code") || clients.includes("claude-code-cli")) {
170
+ lines.push("Claude Code: restart Claude Code, run /mcp, and choose Authenticate.");
171
+ }
172
+ if (clients.includes("cursor")) {
173
+ lines.push("Cursor: restart or reload Cursor, then authenticate the MCP server from Cursor MCP settings if prompted.");
174
+ }
175
+ if (clients.includes("opencode")) {
176
+ lines.push(`OpenCode: run opencode mcp auth ${serverName ?? "<server-name>"} or authenticate on first use.`);
177
+ }
178
+ if (clients.includes("github-copilot")) {
179
+ lines.push("GitHub Copilot / VS Code: reload VS Code, open the MCP config, and use the Auth action above the server if prompted.");
180
+ }
181
+ return lines;
182
+ }
156
183
  /** Write a token+headers entry for every config key, collecting files. */
157
184
  function writeAuthedEntries(clients, keys, mcpUrl, token, headers, scope, baseDir, written, errors) {
158
185
  for (const client of clients) {
@@ -308,7 +335,7 @@ export async function registerMcpServer(opts) {
308
335
  // OAuth clients always get URL-only entries (in-host OAuth, no local bearer).
309
336
  if (oauthClients.length > 0) {
310
337
  writeUrlOnlyEntries(oauthClients, keys, mcpUrl, scope, baseDir, written, errors);
311
- guidance.push(`${describeClients(oauthClients)}: wrote URL-only MCP config (no bearer headers).`, "Next: restart Claude Code, run /mcp, and choose Authenticate.");
338
+ guidance.push(`${describeClients(oauthClients)}: wrote URL-only MCP config (no bearer headers).`, ...oauthNextStepsForClients(oauthClients, descriptor.serverName));
312
339
  }
313
340
  // Device-code clients.
314
341
  if (deviceClients.length > 0) {
@@ -348,7 +375,7 @@ export async function registerMcpServer(opts) {
348
375
  return { written, authenticated, guidance: [...guidance, ...errors] };
349
376
  }
350
377
  function describeClients(clients) {
351
- return clients.join(", ");
378
+ return clients.map((client) => CLIENT_LABELS[client]).join(", ");
352
379
  }
353
380
  /** Derive the `app` slug the device-start endpoint expects. */
354
381
  function appSlugFor(descriptor, baseUrl) {
@@ -1 +1 @@
1
- {"version":3,"file":"connect.js","sourceRoot":"","sources":["../src/connect.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * MCP-server registration + authentication for `@agent-native/skills`.\n *\n * This is a dependency-free port of the MCP-config-writing + device-code/OAuth\n * flow that lives in `@agent-native/core`'s `cli/connect.ts`. The skills package\n * ships standalone (no `@agent-native/core` dependency), so this module\n * re-implements just the registration surface against the shared on-disk\n * writers in `./mcp-config-writers.js`. It writes the SAME config and speaks the\n * SAME device-code/OAuth protocol as core.\n *\n * Two client families, exactly as in core:\n * - OAuth-capable (claude-code, claude-code-cli): get a URL-only HTTP MCP\n * entry (no bearer headers). The user authenticates in-host via standard\n * remote MCP OAuth: restart Claude Code, run /mcp, choose Authenticate.\n * - Device-code (codex, cowork): run the browser device-code flow against the\n * descriptor's hosted URL, then write the entry WITH the minted bearer token\n * + headers. Non-interactive (or no TTY) skips the flow and writes a\n * URL-only entry, surfacing the exact `agent-native connect <url> --token`\n * fallback command.\n *\n * Server contract (identical paths + JSON field names to core):\n * POST <hostedUrl>/_agent-native/mcp/connect/device/start (no auth)\n * body { client?, app? }\n * → { device_code, user_code, verification_uri,\n * verification_uri_complete, interval, expires_in }\n * POST <hostedUrl>/_agent-native/mcp/connect/device/poll (no auth)\n * body { device_code }\n * → { status: \"pending\" }\n * | { status: \"approved\", token, mcpUrl, serverName, mcpServerEntry }\n * | { status: \"expired\" } | { status: \"consumed\" }\n * | { status: \"error\" | \"not_found\", message? }\n *\n * Node-only. Node built-ins + global fetch only; no npm deps.\n */\n\nimport { ClientId, writeHttpEntryForClient } from \"./mcp-config-writers.js\";\n\nconst DEVICE_START_PATH = \"/_agent-native/mcp/connect/device/start\";\nconst DEVICE_POLL_PATH = \"/_agent-native/mcp/connect/device/poll\";\nconst MCP_PATH = \"/_agent-native/mcp\";\n\n/** OAuth-capable clients (in-host remote MCP OAuth, never a local bearer). */\nconst REMOTE_MCP_OAUTH_CLIENTS = new Set<ClientId>([\n \"claude-code\",\n \"claude-code-cli\",\n]);\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\n/**\n * Describes one MCP server to register. `serverName` is the canonical config\n * key; `aliases` are additional config keys that point at the same MCP URL\n * (e.g. `plan` + `agent-native-plans`). `hostedUrl` is the deployed app origin\n * the device-code flow authenticates against; `mcpUrl` is the resolved MCP\n * endpoint written into the config (defaults to `<hostedUrl>/_agent-native/mcp`\n * when only `hostedUrl` is supplied).\n */\nexport interface McpDescriptor {\n serverName: string;\n mcpUrl: string;\n aliases?: string[];\n authMode?: \"oauth\" | \"device\" | \"none\";\n hostedUrl?: string;\n}\n\nexport interface RegisterMcpOptions {\n descriptor: McpDescriptor;\n clients: ClientId[];\n scope: \"user\" | \"project\";\n baseDir: string;\n interactive: boolean;\n log?: (m: string) => void;\n deviceFlowTimeoutMs?: number;\n deps?: {\n fetchImpl?: typeof fetch;\n now?: () => number;\n sleep?: (ms: number) => Promise<void>;\n };\n}\n\nexport interface RegisterMcpResult {\n written: { client: ClientId; file: string }[];\n authenticated: boolean;\n guidance: string[];\n}\n\n// ---------------------------------------------------------------------------\n// Device-code protocol shapes (field names match core EXACTLY).\n// ---------------------------------------------------------------------------\n\ninterface DeviceStartResponse {\n device_code: string;\n user_code: string;\n verification_uri: string;\n verification_uri_complete: string;\n interval?: number;\n expires_in?: number;\n}\n\ninterface DevicePollResponse {\n status:\n | \"pending\"\n | \"approved\"\n | \"expired\"\n | \"consumed\"\n | \"error\"\n | \"not_found\";\n token?: string;\n mcpUrl?: string;\n serverName?: string;\n mcpServerEntry?: Record<string, unknown>;\n message?: string;\n error?: string;\n}\n\ninterface DeviceGrant {\n token?: string;\n mcpUrl: string;\n serverName: string;\n headers?: Record<string, string>;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nexport function supportsRemoteMcpOAuth(client: ClientId): boolean {\n return REMOTE_MCP_OAUTH_CLIENTS.has(client);\n}\n\nconst DEFAULT_DEVICE_FLOW_TIMEOUT_MS = 90_000;\n\nfunction realSleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/** Trailing-slash-stripped origin+path for a hosted app URL. */\nfunction stripTrailingSlash(url: string): string {\n return url.replace(/\\/+$/, \"\");\n}\n\n/**\n * Resolve the MCP endpoint URL for a descriptor. Prefers an explicit `mcpUrl`,\n * otherwise derives `<hostedUrl>/_agent-native/mcp` (mirrors core's\n * `mcpUrlForBaseUrl`). Returns `undefined` when neither is usable.\n */\nfunction resolveMcpUrl(descriptor: McpDescriptor): string | undefined {\n if (descriptor.mcpUrl && descriptor.mcpUrl.trim()) {\n return descriptor.mcpUrl.trim();\n }\n if (descriptor.hostedUrl && descriptor.hostedUrl.trim()) {\n const base = stripTrailingSlash(descriptor.hostedUrl.trim());\n return `${base}${MCP_PATH}`;\n }\n return undefined;\n}\n\n/** Base (origin) URL of the deployed app the device flow runs against. */\nfunction resolveBaseUrl(descriptor: McpDescriptor): string | undefined {\n if (descriptor.hostedUrl && descriptor.hostedUrl.trim()) {\n return stripTrailingSlash(descriptor.hostedUrl.trim());\n }\n // Fall back to stripping the MCP path off an explicit mcpUrl.\n if (descriptor.mcpUrl && descriptor.mcpUrl.trim()) {\n const trimmed = stripTrailingSlash(descriptor.mcpUrl.trim());\n if (trimmed.endsWith(MCP_PATH)) {\n return trimmed.slice(0, -MCP_PATH.length);\n }\n }\n return undefined;\n}\n\n/** All config keys to register: the canonical name plus any aliases (deduped). */\nfunction configKeys(descriptor: McpDescriptor): string[] {\n const keys: string[] = [descriptor.serverName];\n for (const alias of descriptor.aliases ?? []) {\n if (alias && alias !== descriptor.serverName && !keys.includes(alias)) {\n keys.push(alias);\n }\n }\n return keys;\n}\n\nfunction responseMessage(json: any, fallback: string): string {\n const message =\n typeof json?.message === \"string\"\n ? json.message\n : typeof json?.error === \"string\"\n ? json.error\n : \"\";\n return message.trim() || fallback;\n}\n\nasync function postJson(\n fetchImpl: typeof fetch,\n url: string,\n body: unknown,\n): Promise<{ status: number; json: any }> {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), 30_000);\n try {\n const response = await fetchImpl(url, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify(body ?? {}),\n signal: controller.signal,\n });\n let json: any = null;\n try {\n json = await response.json();\n } catch {\n json = null;\n }\n return { status: response.status, json };\n } finally {\n clearTimeout(timeout);\n }\n}\n\n/**\n * The exact no-browser fallback command core prints. Surfaced as guidance when\n * a device-code client is asked to register non-interactively.\n */\nfunction clientArgForClients(clients: ClientId[]): string {\n return clients.length === 1 ? clients[0] : clients.join(\",\");\n}\n\nfunction fallbackConnectCommand(\n baseUrl: string,\n clients: ClientId[],\n scope: string,\n): string {\n return `npx @agent-native/core@latest connect ${baseUrl} --client ${clientArgForClients(\n clients,\n )} --scope ${scope}`;\n}\n\n/** Write a URL-only entry (no bearer) for every config key, collecting files. */\nfunction writeUrlOnlyEntries(\n clients: ClientId[],\n keys: string[],\n mcpUrl: string,\n scope: string,\n baseDir: string,\n written: { client: ClientId; file: string }[],\n errors: string[],\n): void {\n for (const client of clients) {\n for (const key of keys) {\n try {\n const file = writeHttpEntryForClient(\n client,\n key,\n mcpUrl,\n undefined,\n baseDir,\n scope,\n undefined,\n );\n written.push({ client, file });\n } catch (err: any) {\n errors.push(\n `Could not write ${key} for ${client}: ${err?.message ?? err}`,\n );\n }\n }\n }\n}\n\nfunction manualConnectGuidance(\n baseUrl: string,\n clients: ClientId[],\n scope: string,\n): string {\n return (\n `To authenticate ${describeClients(clients)}, run: ` +\n fallbackConnectCommand(baseUrl, clients, scope)\n );\n}\n\n/** Write a token+headers entry for every config key, collecting files. */\nfunction writeAuthedEntries(\n clients: ClientId[],\n keys: string[],\n mcpUrl: string,\n token: string | undefined,\n headers: Record<string, string> | undefined,\n scope: string,\n baseDir: string,\n written: { client: ClientId; file: string }[],\n errors: string[],\n): void {\n for (const client of clients) {\n for (const key of keys) {\n try {\n const file = writeHttpEntryForClient(\n client,\n key,\n mcpUrl,\n token,\n baseDir,\n scope,\n headers,\n );\n written.push({ client, file });\n } catch (err: any) {\n errors.push(\n `Could not write ${key} for ${client}: ${err?.message ?? err}`,\n );\n }\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Device-code flow (dependency-free port of core's runDeviceFlow)\n// ---------------------------------------------------------------------------\n\n/**\n * Run the device-code flow against `baseUrl` and return the approved grant, or\n * `null` (after logging a clear message) on expiry/consumed/error/timeout. Same\n * state machine and field handling as core; the spinner/browser-open are\n * dropped since this runs inside a non-interactive installer context.\n */\nasync function runDeviceFlow(\n baseUrl: string,\n appSlug: string,\n clientArg: string,\n log: (m: string) => void,\n timeoutMs: number | undefined,\n deps: RegisterMcpOptions[\"deps\"] = {},\n): Promise<DeviceGrant | null> {\n const fetchImpl = deps.fetchImpl ?? fetch;\n const sleep = deps.sleep ?? realSleep;\n const now = deps.now ?? (() => Date.now());\n\n let start: DeviceStartResponse;\n try {\n const { status, json } = await postJson(\n fetchImpl,\n `${baseUrl}${DEVICE_START_PATH}`,\n { client: clientArg, app: appSlug },\n );\n if (status < 200 || status >= 300 || !json?.device_code) {\n log(\n ` Could not start the connect flow on ${baseUrl} (HTTP ${status}). ` +\n `Is this an agent-native app, and is it deployed with the connect ` +\n `endpoint enabled?`,\n );\n return null;\n }\n start = json as DeviceStartResponse;\n } catch (err: any) {\n log(\n ` Could not reach ${baseUrl} (${err?.message ?? err}). ` +\n `Check the URL and your network.`,\n );\n return null;\n }\n\n const interval = Math.max(1, Number(start.interval) || 5);\n const expiresIn = Math.max(interval, Number(start.expires_in) || 600);\n const serverDeadlineMs = expiresIn * 1000;\n const installerDeadlineMs = Math.max(\n 0,\n timeoutMs ?? DEFAULT_DEVICE_FLOW_TIMEOUT_MS,\n );\n const waitMs = Math.min(serverDeadlineMs, installerDeadlineMs);\n const waitWasCapped = waitMs < serverDeadlineMs;\n const deadline = now() + waitMs;\n\n log(\"\");\n log(` Connecting to ${baseUrl}`);\n log(\"\");\n log(` Your code: ${start.user_code}`);\n log(` Open: ${start.verification_uri_complete}`);\n log(\"\");\n log(\" Approve in the browser to finish.\");\n\n while (now() < deadline) {\n let poll: DevicePollResponse;\n try {\n const { status, json } = await postJson(\n fetchImpl,\n `${baseUrl}${DEVICE_POLL_PATH}`,\n { device_code: start.device_code },\n );\n if (status < 200 || status >= 300) {\n log(\n ` Connect polling failed (HTTP ${status}): ` +\n responseMessage(json, \"server returned an error.\"),\n );\n return null;\n }\n poll = (json ?? { status: \"pending\" }) as DevicePollResponse;\n } catch {\n // Transient network error — keep polling until the deadline.\n poll = { status: \"pending\" };\n }\n\n if (poll.status === \"approved\") {\n const token = poll.token ?? \"\";\n const mcpUrl = poll.mcpUrl ?? `${baseUrl}${MCP_PATH}`;\n const serverName = poll.serverName ?? appSlug;\n const headers =\n poll.mcpServerEntry &&\n typeof poll.mcpServerEntry === \"object\" &&\n poll.mcpServerEntry.headers &&\n typeof poll.mcpServerEntry.headers === \"object\"\n ? (poll.mcpServerEntry.headers as Record<string, string>)\n : undefined;\n log(\" Approved.\");\n return { token: token || undefined, mcpUrl, serverName, headers };\n }\n if (poll.status === \"expired\") {\n log(\" The connect request expired before it was approved.\");\n log(\" Run the command again to retry.\");\n return null;\n }\n if (poll.status === \"consumed\") {\n log(\" This connect code was already used. Run the command again.\");\n return null;\n }\n if (poll.status === \"error\" || poll.status === \"not_found\") {\n log(\n ` Connect polling failed: ${responseMessage(\n poll,\n poll.status === \"not_found\"\n ? \"device code was not found.\"\n : \"server returned an error.\",\n )}`,\n );\n return null;\n }\n\n const remainingMs = deadline - now();\n if (remainingMs <= 0) break;\n await sleep(Math.min(interval * 1000, remainingMs));\n }\n\n if (waitWasCapped) {\n log(\n ` Stopped waiting after ${Math.round(waitMs / 1000)}s so installation can finish.`,\n );\n log(\" You can authenticate later with the command below.\");\n } else {\n log(\" Timed out waiting for approval. Run the command again to retry.\");\n }\n return null;\n}\n\n// ---------------------------------------------------------------------------\n// Public entry point\n// ---------------------------------------------------------------------------\n\n/**\n * Register an MCP server (plus aliases) into the requested client configs,\n * authenticating device-code clients via the browser flow when interactive.\n *\n * Idempotent: re-running replaces the same named entries. Never throws on a\n * single client/key failing — failures are collected into `guidance`.\n */\nexport async function registerMcpServer(\n opts: RegisterMcpOptions,\n): Promise<RegisterMcpResult> {\n const { descriptor, scope, baseDir, interactive } = opts;\n const log = opts.log ?? (() => {});\n const deps = opts.deps ?? {};\n\n const written: { client: ClientId; file: string }[] = [];\n const guidance: string[] = [];\n const errors: string[] = [];\n let authenticated = false;\n\n const keys = configKeys(descriptor);\n const mcpUrl = resolveMcpUrl(descriptor);\n if (!mcpUrl) {\n return {\n written,\n authenticated,\n guidance: [\n `Cannot register \"${descriptor.serverName}\": no mcpUrl or hostedUrl supplied.`,\n ],\n };\n }\n\n const authMode = descriptor.authMode ?? \"device\";\n\n // authMode \"none\" (e.g. context-xray): URL-only for ALL clients, no auth.\n if (authMode === \"none\") {\n writeUrlOnlyEntries(\n opts.clients,\n keys,\n mcpUrl,\n scope,\n baseDir,\n written,\n errors,\n );\n return { written, authenticated, guidance: [...guidance, ...errors] };\n }\n\n // Split into OAuth-capable and device-code clients, exactly like core.\n const oauthClients = opts.clients.filter((c) => supportsRemoteMcpOAuth(c));\n const deviceClients = opts.clients.filter((c) => !supportsRemoteMcpOAuth(c));\n\n // OAuth clients always get URL-only entries (in-host OAuth, no local bearer).\n if (oauthClients.length > 0) {\n writeUrlOnlyEntries(\n oauthClients,\n keys,\n mcpUrl,\n scope,\n baseDir,\n written,\n errors,\n );\n guidance.push(\n `${describeClients(oauthClients)}: wrote URL-only MCP config (no bearer headers).`,\n \"Next: restart Claude Code, run /mcp, and choose Authenticate.\",\n );\n }\n\n // Device-code clients.\n if (deviceClients.length > 0) {\n const baseUrl = resolveBaseUrl(descriptor);\n\n // We only reach here for authMode \"oauth\"/\"device\" (authMode \"none\" returned\n // earlier). Run the flow only when interactive AND we have a hosted URL to\n // authenticate against. Device-code clients such as Codex cannot use a\n // URL-only hosted entry: writing one would overwrite a working bearer token\n // and leave new sessions unauthenticated. If auth cannot complete, keep the\n // existing config untouched and surface the explicit connect command.\n const canRunFlow = interactive && !!baseUrl;\n\n if (!canRunFlow) {\n if (baseUrl) {\n guidance.push(\n `${describeClients(deviceClients)}: skipped MCP config because this client needs a bearer token.`,\n manualConnectGuidance(baseUrl, deviceClients, scope),\n );\n } else {\n guidance.push(\n `${describeClients(deviceClients)}: skipped MCP config because no hosted URL was available for authentication.`,\n );\n }\n } else {\n const appSlug = appSlugFor(descriptor, baseUrl!);\n const clientArg = deviceClients.length === 1 ? deviceClients[0] : \"all\";\n const grant = await runDeviceFlow(\n baseUrl!,\n appSlug,\n clientArg,\n log,\n opts.deviceFlowTimeoutMs,\n deps,\n );\n\n if (grant && grant.token) {\n // Write authed entries; honour the server's resolved mcpUrl when given.\n const resolvedUrl = grant.mcpUrl || mcpUrl;\n writeAuthedEntries(\n deviceClients,\n keys,\n resolvedUrl,\n grant.token,\n grant.headers,\n scope,\n baseDir,\n written,\n errors,\n );\n authenticated = true;\n } else {\n // Flow failed (or approved with no token). Do not downgrade device-code\n // clients to a URL-only hosted entry; keep any existing bearer config.\n guidance.push(\n `${describeClients(deviceClients)}: authentication did not complete; existing MCP config was left unchanged.`,\n manualConnectGuidance(baseUrl!, deviceClients, scope),\n );\n }\n }\n }\n\n return { written, authenticated, guidance: [...guidance, ...errors] };\n}\n\nfunction describeClients(clients: ClientId[]): string {\n return clients.join(\", \");\n}\n\n/** Derive the `app` slug the device-start endpoint expects. */\nfunction appSlugFor(descriptor: McpDescriptor, baseUrl: string): string {\n // Prefer the descriptor's server name without the agent-native prefix.\n const name = descriptor.serverName.replace(/^agent-native-/, \"\");\n if (name) return name;\n try {\n const host = new URL(baseUrl).hostname;\n const first = host.split(\".\")[0];\n return first && first !== \"www\" ? first : \"app\";\n } catch {\n return \"app\";\n }\n}\n"]}
1
+ {"version":3,"file":"connect.js","sourceRoot":"","sources":["../src/connect.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * MCP-server registration + authentication for `@agent-native/skills`.\n *\n * This is a dependency-free port of the MCP-config-writing + device-code/OAuth\n * flow that lives in `@agent-native/core`'s `cli/connect.ts`. The skills package\n * ships standalone (no `@agent-native/core` dependency), so this module\n * re-implements just the registration surface against the shared on-disk\n * writers in `./mcp-config-writers.js`. It writes the SAME config and speaks the\n * SAME device-code/OAuth protocol as core.\n *\n * Two client families, exactly as in core:\n * - OAuth-capable (Claude Code, Cursor, OpenCode, GitHub Copilot / VS Code):\n * get a URL-only HTTP MCP entry (no bearer headers). The user authenticates\n * in-host via standard remote MCP OAuth.\n * - Device-code (codex, cowork): run the browser device-code flow against the\n * descriptor's hosted URL, then write the entry WITH the minted bearer token\n * + headers. Non-interactive (or no TTY) skips writing device-code configs,\n * surfacing the exact `agent-native connect <url>` fallback command.\n *\n * Server contract (identical paths + JSON field names to core):\n * POST <hostedUrl>/_agent-native/mcp/connect/device/start (no auth)\n * body { client?, app? }\n * → { device_code, user_code, verification_uri,\n * verification_uri_complete, interval, expires_in }\n * POST <hostedUrl>/_agent-native/mcp/connect/device/poll (no auth)\n * body { device_code }\n * → { status: \"pending\" }\n * | { status: \"approved\", token, mcpUrl, serverName, mcpServerEntry }\n * | { status: \"expired\" } | { status: \"consumed\" }\n * | { status: \"error\" | \"not_found\", message? }\n *\n * Node-only. Node built-ins + global fetch only; no npm deps.\n */\n\nimport { ClientId, writeHttpEntryForClient } from \"./mcp-config-writers.js\";\n\nconst DEVICE_START_PATH = \"/_agent-native/mcp/connect/device/start\";\nconst DEVICE_POLL_PATH = \"/_agent-native/mcp/connect/device/poll\";\nconst MCP_PATH = \"/_agent-native/mcp\";\n\n/** OAuth-capable clients (in-host remote MCP OAuth, never a local bearer). */\nconst REMOTE_MCP_OAUTH_CLIENTS = new Set<ClientId>([\n \"claude-code\",\n \"claude-code-cli\",\n \"cursor\",\n \"opencode\",\n \"github-copilot\",\n]);\n\nconst CLIENT_LABELS: Record<ClientId, string> = {\n \"claude-code\": \"Claude Code\",\n \"claude-code-cli\": \"Claude Code CLI\",\n codex: \"Codex\",\n cowork: \"Claude Cowork\",\n cursor: \"Cursor\",\n opencode: \"OpenCode\",\n \"github-copilot\": \"GitHub Copilot / VS Code\",\n};\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\n/**\n * Describes one MCP server to register. `serverName` is the canonical config\n * key; `aliases` are additional config keys that point at the same MCP URL\n * (e.g. `plan` + `agent-native-plans`). `hostedUrl` is the deployed app origin\n * the device-code flow authenticates against; `mcpUrl` is the resolved MCP\n * endpoint written into the config (defaults to `<hostedUrl>/_agent-native/mcp`\n * when only `hostedUrl` is supplied).\n */\nexport interface McpDescriptor {\n serverName: string;\n mcpUrl: string;\n aliases?: string[];\n authMode?: \"oauth\" | \"device\" | \"none\";\n hostedUrl?: string;\n}\n\nexport interface RegisterMcpOptions {\n descriptor: McpDescriptor;\n clients: ClientId[];\n scope: \"user\" | \"project\";\n baseDir: string;\n interactive: boolean;\n log?: (m: string) => void;\n deviceFlowTimeoutMs?: number;\n deps?: {\n fetchImpl?: typeof fetch;\n now?: () => number;\n sleep?: (ms: number) => Promise<void>;\n };\n}\n\nexport interface RegisterMcpResult {\n written: { client: ClientId; file: string }[];\n authenticated: boolean;\n guidance: string[];\n}\n\n// ---------------------------------------------------------------------------\n// Device-code protocol shapes (field names match core EXACTLY).\n// ---------------------------------------------------------------------------\n\ninterface DeviceStartResponse {\n device_code: string;\n user_code: string;\n verification_uri: string;\n verification_uri_complete: string;\n interval?: number;\n expires_in?: number;\n}\n\ninterface DevicePollResponse {\n status:\n | \"pending\"\n | \"approved\"\n | \"expired\"\n | \"consumed\"\n | \"error\"\n | \"not_found\";\n token?: string;\n mcpUrl?: string;\n serverName?: string;\n mcpServerEntry?: Record<string, unknown>;\n message?: string;\n error?: string;\n}\n\ninterface DeviceGrant {\n token?: string;\n mcpUrl: string;\n serverName: string;\n headers?: Record<string, string>;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nexport function supportsRemoteMcpOAuth(client: ClientId): boolean {\n return REMOTE_MCP_OAUTH_CLIENTS.has(client);\n}\n\nconst DEFAULT_DEVICE_FLOW_TIMEOUT_MS = 90_000;\n\nfunction realSleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/** Trailing-slash-stripped origin+path for a hosted app URL. */\nfunction stripTrailingSlash(url: string): string {\n return url.replace(/\\/+$/, \"\");\n}\n\n/**\n * Resolve the MCP endpoint URL for a descriptor. Prefers an explicit `mcpUrl`,\n * otherwise derives `<hostedUrl>/_agent-native/mcp` (mirrors core's\n * `mcpUrlForBaseUrl`). Returns `undefined` when neither is usable.\n */\nfunction resolveMcpUrl(descriptor: McpDescriptor): string | undefined {\n if (descriptor.mcpUrl && descriptor.mcpUrl.trim()) {\n return descriptor.mcpUrl.trim();\n }\n if (descriptor.hostedUrl && descriptor.hostedUrl.trim()) {\n const base = stripTrailingSlash(descriptor.hostedUrl.trim());\n return `${base}${MCP_PATH}`;\n }\n return undefined;\n}\n\n/** Base (origin) URL of the deployed app the device flow runs against. */\nfunction resolveBaseUrl(descriptor: McpDescriptor): string | undefined {\n if (descriptor.hostedUrl && descriptor.hostedUrl.trim()) {\n return stripTrailingSlash(descriptor.hostedUrl.trim());\n }\n // Fall back to stripping the MCP path off an explicit mcpUrl.\n if (descriptor.mcpUrl && descriptor.mcpUrl.trim()) {\n const trimmed = stripTrailingSlash(descriptor.mcpUrl.trim());\n if (trimmed.endsWith(MCP_PATH)) {\n return trimmed.slice(0, -MCP_PATH.length);\n }\n }\n return undefined;\n}\n\n/** All config keys to register: the canonical name plus any aliases (deduped). */\nfunction configKeys(descriptor: McpDescriptor): string[] {\n const keys: string[] = [descriptor.serverName];\n for (const alias of descriptor.aliases ?? []) {\n if (alias && alias !== descriptor.serverName && !keys.includes(alias)) {\n keys.push(alias);\n }\n }\n return keys;\n}\n\nfunction responseMessage(json: any, fallback: string): string {\n const message =\n typeof json?.message === \"string\"\n ? json.message\n : typeof json?.error === \"string\"\n ? json.error\n : \"\";\n return message.trim() || fallback;\n}\n\nasync function postJson(\n fetchImpl: typeof fetch,\n url: string,\n body: unknown,\n): Promise<{ status: number; json: any }> {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), 30_000);\n try {\n const response = await fetchImpl(url, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify(body ?? {}),\n signal: controller.signal,\n });\n let json: any = null;\n try {\n json = await response.json();\n } catch {\n json = null;\n }\n return { status: response.status, json };\n } finally {\n clearTimeout(timeout);\n }\n}\n\n/**\n * The exact no-browser fallback command core prints. Surfaced as guidance when\n * a device-code client is asked to register non-interactively.\n */\nfunction clientArgForClients(clients: ClientId[]): string {\n return clients.length === 1 ? clients[0] : clients.join(\",\");\n}\n\nfunction fallbackConnectCommand(\n baseUrl: string,\n clients: ClientId[],\n scope: string,\n): string {\n return `npx @agent-native/core@latest connect ${baseUrl} --client ${clientArgForClients(\n clients,\n )} --scope ${scope}`;\n}\n\n/** Write a URL-only entry (no bearer) for every config key, collecting files. */\nfunction writeUrlOnlyEntries(\n clients: ClientId[],\n keys: string[],\n mcpUrl: string,\n scope: string,\n baseDir: string,\n written: { client: ClientId; file: string }[],\n errors: string[],\n): void {\n for (const client of clients) {\n for (const key of keys) {\n try {\n const file = writeHttpEntryForClient(\n client,\n key,\n mcpUrl,\n undefined,\n baseDir,\n scope,\n undefined,\n );\n written.push({ client, file });\n } catch (err: any) {\n errors.push(\n `Could not write ${key} for ${client}: ${err?.message ?? err}`,\n );\n }\n }\n }\n}\n\nfunction manualConnectGuidance(\n baseUrl: string,\n clients: ClientId[],\n scope: string,\n): string {\n return (\n `To authenticate ${describeClients(clients)}, run: ` +\n fallbackConnectCommand(baseUrl, clients, scope)\n );\n}\n\nfunction oauthNextStepsForClients(\n clients: ClientId[],\n serverName?: string,\n): string[] {\n const lines: string[] = [];\n if (clients.includes(\"claude-code\") || clients.includes(\"claude-code-cli\")) {\n lines.push(\n \"Claude Code: restart Claude Code, run /mcp, and choose Authenticate.\",\n );\n }\n if (clients.includes(\"cursor\")) {\n lines.push(\n \"Cursor: restart or reload Cursor, then authenticate the MCP server from Cursor MCP settings if prompted.\",\n );\n }\n if (clients.includes(\"opencode\")) {\n lines.push(\n `OpenCode: run opencode mcp auth ${serverName ?? \"<server-name>\"} or authenticate on first use.`,\n );\n }\n if (clients.includes(\"github-copilot\")) {\n lines.push(\n \"GitHub Copilot / VS Code: reload VS Code, open the MCP config, and use the Auth action above the server if prompted.\",\n );\n }\n return lines;\n}\n\n/** Write a token+headers entry for every config key, collecting files. */\nfunction writeAuthedEntries(\n clients: ClientId[],\n keys: string[],\n mcpUrl: string,\n token: string | undefined,\n headers: Record<string, string> | undefined,\n scope: string,\n baseDir: string,\n written: { client: ClientId; file: string }[],\n errors: string[],\n): void {\n for (const client of clients) {\n for (const key of keys) {\n try {\n const file = writeHttpEntryForClient(\n client,\n key,\n mcpUrl,\n token,\n baseDir,\n scope,\n headers,\n );\n written.push({ client, file });\n } catch (err: any) {\n errors.push(\n `Could not write ${key} for ${client}: ${err?.message ?? err}`,\n );\n }\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Device-code flow (dependency-free port of core's runDeviceFlow)\n// ---------------------------------------------------------------------------\n\n/**\n * Run the device-code flow against `baseUrl` and return the approved grant, or\n * `null` (after logging a clear message) on expiry/consumed/error/timeout. Same\n * state machine and field handling as core; the spinner/browser-open are\n * dropped since this runs inside a non-interactive installer context.\n */\nasync function runDeviceFlow(\n baseUrl: string,\n appSlug: string,\n clientArg: string,\n log: (m: string) => void,\n timeoutMs: number | undefined,\n deps: RegisterMcpOptions[\"deps\"] = {},\n): Promise<DeviceGrant | null> {\n const fetchImpl = deps.fetchImpl ?? fetch;\n const sleep = deps.sleep ?? realSleep;\n const now = deps.now ?? (() => Date.now());\n\n let start: DeviceStartResponse;\n try {\n const { status, json } = await postJson(\n fetchImpl,\n `${baseUrl}${DEVICE_START_PATH}`,\n { client: clientArg, app: appSlug },\n );\n if (status < 200 || status >= 300 || !json?.device_code) {\n log(\n ` Could not start the connect flow on ${baseUrl} (HTTP ${status}). ` +\n `Is this an agent-native app, and is it deployed with the connect ` +\n `endpoint enabled?`,\n );\n return null;\n }\n start = json as DeviceStartResponse;\n } catch (err: any) {\n log(\n ` Could not reach ${baseUrl} (${err?.message ?? err}). ` +\n `Check the URL and your network.`,\n );\n return null;\n }\n\n const interval = Math.max(1, Number(start.interval) || 5);\n const expiresIn = Math.max(interval, Number(start.expires_in) || 600);\n const serverDeadlineMs = expiresIn * 1000;\n const installerDeadlineMs = Math.max(\n 0,\n timeoutMs ?? DEFAULT_DEVICE_FLOW_TIMEOUT_MS,\n );\n const waitMs = Math.min(serverDeadlineMs, installerDeadlineMs);\n const waitWasCapped = waitMs < serverDeadlineMs;\n const deadline = now() + waitMs;\n\n log(\"\");\n log(` Connecting to ${baseUrl}`);\n log(\"\");\n log(` Your code: ${start.user_code}`);\n log(` Open: ${start.verification_uri_complete}`);\n log(\"\");\n log(\" Approve in the browser to finish.\");\n\n while (now() < deadline) {\n let poll: DevicePollResponse;\n try {\n const { status, json } = await postJson(\n fetchImpl,\n `${baseUrl}${DEVICE_POLL_PATH}`,\n { device_code: start.device_code },\n );\n if (status < 200 || status >= 300) {\n log(\n ` Connect polling failed (HTTP ${status}): ` +\n responseMessage(json, \"server returned an error.\"),\n );\n return null;\n }\n poll = (json ?? { status: \"pending\" }) as DevicePollResponse;\n } catch {\n // Transient network error — keep polling until the deadline.\n poll = { status: \"pending\" };\n }\n\n if (poll.status === \"approved\") {\n const token = poll.token ?? \"\";\n const mcpUrl = poll.mcpUrl ?? `${baseUrl}${MCP_PATH}`;\n const serverName = poll.serverName ?? appSlug;\n const headers =\n poll.mcpServerEntry &&\n typeof poll.mcpServerEntry === \"object\" &&\n poll.mcpServerEntry.headers &&\n typeof poll.mcpServerEntry.headers === \"object\"\n ? (poll.mcpServerEntry.headers as Record<string, string>)\n : undefined;\n log(\" Approved.\");\n return { token: token || undefined, mcpUrl, serverName, headers };\n }\n if (poll.status === \"expired\") {\n log(\" The connect request expired before it was approved.\");\n log(\" Run the command again to retry.\");\n return null;\n }\n if (poll.status === \"consumed\") {\n log(\" This connect code was already used. Run the command again.\");\n return null;\n }\n if (poll.status === \"error\" || poll.status === \"not_found\") {\n log(\n ` Connect polling failed: ${responseMessage(\n poll,\n poll.status === \"not_found\"\n ? \"device code was not found.\"\n : \"server returned an error.\",\n )}`,\n );\n return null;\n }\n\n const remainingMs = deadline - now();\n if (remainingMs <= 0) break;\n await sleep(Math.min(interval * 1000, remainingMs));\n }\n\n if (waitWasCapped) {\n log(\n ` Stopped waiting after ${Math.round(waitMs / 1000)}s so installation can finish.`,\n );\n log(\" You can authenticate later with the command below.\");\n } else {\n log(\" Timed out waiting for approval. Run the command again to retry.\");\n }\n return null;\n}\n\n// ---------------------------------------------------------------------------\n// Public entry point\n// ---------------------------------------------------------------------------\n\n/**\n * Register an MCP server (plus aliases) into the requested client configs,\n * authenticating device-code clients via the browser flow when interactive.\n *\n * Idempotent: re-running replaces the same named entries. Never throws on a\n * single client/key failing — failures are collected into `guidance`.\n */\nexport async function registerMcpServer(\n opts: RegisterMcpOptions,\n): Promise<RegisterMcpResult> {\n const { descriptor, scope, baseDir, interactive } = opts;\n const log = opts.log ?? (() => {});\n const deps = opts.deps ?? {};\n\n const written: { client: ClientId; file: string }[] = [];\n const guidance: string[] = [];\n const errors: string[] = [];\n let authenticated = false;\n\n const keys = configKeys(descriptor);\n const mcpUrl = resolveMcpUrl(descriptor);\n if (!mcpUrl) {\n return {\n written,\n authenticated,\n guidance: [\n `Cannot register \"${descriptor.serverName}\": no mcpUrl or hostedUrl supplied.`,\n ],\n };\n }\n\n const authMode = descriptor.authMode ?? \"device\";\n\n // authMode \"none\" (e.g. context-xray): URL-only for ALL clients, no auth.\n if (authMode === \"none\") {\n writeUrlOnlyEntries(\n opts.clients,\n keys,\n mcpUrl,\n scope,\n baseDir,\n written,\n errors,\n );\n return { written, authenticated, guidance: [...guidance, ...errors] };\n }\n\n // Split into OAuth-capable and device-code clients, exactly like core.\n const oauthClients = opts.clients.filter((c) => supportsRemoteMcpOAuth(c));\n const deviceClients = opts.clients.filter((c) => !supportsRemoteMcpOAuth(c));\n\n // OAuth clients always get URL-only entries (in-host OAuth, no local bearer).\n if (oauthClients.length > 0) {\n writeUrlOnlyEntries(\n oauthClients,\n keys,\n mcpUrl,\n scope,\n baseDir,\n written,\n errors,\n );\n guidance.push(\n `${describeClients(oauthClients)}: wrote URL-only MCP config (no bearer headers).`,\n ...oauthNextStepsForClients(oauthClients, descriptor.serverName),\n );\n }\n\n // Device-code clients.\n if (deviceClients.length > 0) {\n const baseUrl = resolveBaseUrl(descriptor);\n\n // We only reach here for authMode \"oauth\"/\"device\" (authMode \"none\" returned\n // earlier). Run the flow only when interactive AND we have a hosted URL to\n // authenticate against. Device-code clients such as Codex cannot use a\n // URL-only hosted entry: writing one would overwrite a working bearer token\n // and leave new sessions unauthenticated. If auth cannot complete, keep the\n // existing config untouched and surface the explicit connect command.\n const canRunFlow = interactive && !!baseUrl;\n\n if (!canRunFlow) {\n if (baseUrl) {\n guidance.push(\n `${describeClients(deviceClients)}: skipped MCP config because this client needs a bearer token.`,\n manualConnectGuidance(baseUrl, deviceClients, scope),\n );\n } else {\n guidance.push(\n `${describeClients(deviceClients)}: skipped MCP config because no hosted URL was available for authentication.`,\n );\n }\n } else {\n const appSlug = appSlugFor(descriptor, baseUrl!);\n const clientArg = deviceClients.length === 1 ? deviceClients[0] : \"all\";\n const grant = await runDeviceFlow(\n baseUrl!,\n appSlug,\n clientArg,\n log,\n opts.deviceFlowTimeoutMs,\n deps,\n );\n\n if (grant && grant.token) {\n // Write authed entries; honour the server's resolved mcpUrl when given.\n const resolvedUrl = grant.mcpUrl || mcpUrl;\n writeAuthedEntries(\n deviceClients,\n keys,\n resolvedUrl,\n grant.token,\n grant.headers,\n scope,\n baseDir,\n written,\n errors,\n );\n authenticated = true;\n } else {\n // Flow failed (or approved with no token). Do not downgrade device-code\n // clients to a URL-only hosted entry; keep any existing bearer config.\n guidance.push(\n `${describeClients(deviceClients)}: authentication did not complete; existing MCP config was left unchanged.`,\n manualConnectGuidance(baseUrl!, deviceClients, scope),\n );\n }\n }\n }\n\n return { written, authenticated, guidance: [...guidance, ...errors] };\n}\n\nfunction describeClients(clients: ClientId[]): string {\n return clients.map((client) => CLIENT_LABELS[client]).join(\", \");\n}\n\n/** Derive the `app` slug the device-start endpoint expects. */\nfunction appSlugFor(descriptor: McpDescriptor, baseUrl: string): string {\n // Prefer the descriptor's server name without the agent-native prefix.\n const name = descriptor.serverName.replace(/^agent-native-/, \"\");\n if (name) return name;\n try {\n const host = new URL(baseUrl).hostname;\n const first = host.split(\".\")[0];\n return first && first !== \"www\" ? first : \"app\";\n } catch {\n return \"app\";\n }\n}\n"]}
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { type CliTelemetry } from "./telemetry.js";
2
- export type SkillClient = "codex" | "claude-code";
2
+ export type SkillClient = "codex" | "claude-code" | "pi" | "cursor" | "opencode" | "github-copilot";
3
3
  export type SkillScope = "project" | "user";
4
4
  export type PlanMode = "hosted" | "local-files" | "self-hosted";
5
5
  export interface SkillEntry {
@@ -82,6 +82,7 @@ interface ParsedArgs {
82
82
  mcp: boolean;
83
83
  planMode?: PlanMode;
84
84
  mcpUrl?: string;
85
+ quiet: boolean;
85
86
  }
86
87
  interface PromptOption<T extends string> {
87
88
  value: T;
@@ -101,6 +102,8 @@ export interface ScopePromptContext {
101
102
  }
102
103
  export interface GithubActionPromptContext {
103
104
  workflowPath: string;
105
+ setupCommand: string;
106
+ docsUrl: string;
104
107
  }
105
108
  export interface PlanModePromptContext {
106
109
  initialMode: PlanMode;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -15,7 +15,8 @@ Usage:
15
15
 
16
16
  Options:
17
17
  --skill <name> Install only this skill (repeatable)
18
- --client, -a <client> codex, claude-code, or all (default: all; repeatable or comma-separated)
18
+ --client, -a <client> codex, claude-code, pi, cursor, opencode, github-copilot, or all
19
+ (default: all; repeatable or comma-separated)
19
20
  --scope <user|project> Install globally or into the current project (default: user)
20
21
  -g, --global Alias for --scope user
21
22
  --project Alias for --scope project
@@ -32,9 +33,9 @@ Options:
32
33
  --dry-run Print intended writes without changing files
33
34
  --json Print the result as JSON
34
35
 
35
- App-backed skills (visual-plan, visual-recap, assets, design-exploration)
36
- register their hosted MCP server in your agent config by default so the agent
37
- can actually use them. Use --no-mcp to skip that and copy the files only.
36
+ App-backed skills (visual-plan, visual-recap) register their hosted MCP server
37
+ in your agent config by default so the agent can actually use them. Use
38
+ --no-mcp to skip that and copy the files only.
38
39
  For visual-plan/visual-recap, choose --mode local-files for no sharing and all
39
40
  local files, or --mode self-hosted --mcp-url <url> for your own Plan app.
40
41
 
@@ -43,7 +44,14 @@ Examples:
43
44
  npx @agent-native/skills@latest add --skill quick-recap
44
45
  npx @agent-native/skills@latest add --skill visual-recap --with-github-action
45
46
  `;
46
- const CLIENTS = ["codex", "claude-code"];
47
+ const CLIENTS = [
48
+ "codex",
49
+ "claude-code",
50
+ "pi",
51
+ "cursor",
52
+ "opencode",
53
+ "github-copilot",
54
+ ];
47
55
  const DEFAULT_SKILLS_SOURCE = "BuilderIO/skills";
48
56
  const MANAGED_INSTRUCTIONS_START = "<!-- BEGIN @agent-native/skills -->";
49
57
  const MANAGED_INSTRUCTIONS_END = "<!-- END @agent-native/skills -->";
@@ -110,6 +118,8 @@ export function parseSkillsCliArgs(argv) {
110
118
  out.dryRun = true;
111
119
  else if (arg === "--json")
112
120
  out.printJson = true;
121
+ else if (arg === "--quiet")
122
+ out.quiet = true;
113
123
  else if (arg === "--update-instructions")
114
124
  out.updateInstructions = true;
115
125
  else if (arg === "--no-update-instructions")
@@ -227,6 +237,11 @@ function shouldLoadPublicCatalog(parsed) {
227
237
  return true;
228
238
  return parsed.skillNames.some((name) => !resolveAppForSkill(name));
229
239
  }
240
+ const HIDDEN_STANDALONE_BUILT_INS = [
241
+ "assets",
242
+ "design-exploration",
243
+ "context-xray",
244
+ ];
230
245
  export async function runSkillsCli(argv, options = {}) {
231
246
  const parsed = parseSkillsCliArgs(argv);
232
247
  // `@agent-native/skills` normally uses the exact same core flow as
@@ -255,6 +270,7 @@ export async function runSkillsCli(argv, options = {}) {
255
270
  isInteractive: options.isInteractive,
256
271
  baseDir: parsed.baseDir ?? options.baseDir,
257
272
  catalogMode: "all",
273
+ hiddenBuiltInSkillTargets: HIDDEN_STANDALONE_BUILT_INS,
258
274
  publicSkillSource: loadedSource?.root ?? parsed.source ?? DEFAULT_SKILLS_SOURCE,
259
275
  publicSkillEntries,
260
276
  promptSkills: options.promptSkills
@@ -268,6 +284,8 @@ export async function runSkillsCli(argv, options = {}) {
268
284
  promptGithubAction: options.promptGithubAction
269
285
  ? async (context) => options.promptGithubAction?.({
270
286
  workflowPath: context.workflowPath,
287
+ setupCommand: context.setupCommand,
288
+ docsUrl: context.docsUrl,
271
289
  }) ?? null
272
290
  : undefined,
273
291
  promptPlanMode: options.promptPlanMode,
@@ -333,8 +351,8 @@ export async function runSkillsCli(argv, options = {}) {
333
351
  withGithubAction: parsed.withGithubAction,
334
352
  force: parsed.force,
335
353
  connect: parsed.connect,
336
- quiet: parsed.printJson,
337
- log: parsed.printJson ? undefined : stdoutLog,
354
+ quiet: parsed.printJson || parsed.quiet,
355
+ log: parsed.printJson || parsed.quiet ? undefined : stdoutLog,
338
356
  isInteractive: options.isInteractive,
339
357
  promptSkills: options.promptSkills,
340
358
  promptClients: options.promptClients,
@@ -359,6 +377,8 @@ export async function runSkillsCli(argv, options = {}) {
359
377
  process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
360
378
  return;
361
379
  }
380
+ if (parsed.quiet)
381
+ return;
362
382
  await printInstallResult(result, {
363
383
  baseDir: parsed.baseDir ?? options.baseDir ?? process.cwd(),
364
384
  dryRun: parsed.dryRun,
@@ -426,8 +446,7 @@ export async function installSkills(options) {
426
446
  options.telemetry?.track("skills_cli scope selected", { scope });
427
447
  const instructionBlocks = managedInstructionBlocksForSkills(skillNames);
428
448
  const shouldUpdateInstructions = await shouldUpdateManagedInstructions(instructionBlocks, options);
429
- const shouldWriteGithubAction = selected.some((skill) => skill.name === "visual-recap") &&
430
- shouldWritePrVisualRecapWorkflow(options, baseDir);
449
+ const shouldWriteGithubAction = await shouldWritePrVisualRecapWorkflow(selected.some((skill) => skill.name === "visual-recap"), options, baseDir);
431
450
  const mcpApps = options.mcp === false || planMode === "local-files"
432
451
  ? []
433
452
  : mcpAppsForSkills(skillNames, planMcpOverride);
@@ -441,8 +460,7 @@ export async function installSkills(options) {
441
460
  const mcpServers = [];
442
461
  try {
443
462
  progress?.start("Installing skill files...");
444
- for (const client of clients) {
445
- const root = installRootForClient(client, scope, baseDir);
463
+ for (const root of unique(clients.map((client) => installRootForClient(client, scope, baseDir)))) {
446
464
  for (const skill of selected) {
447
465
  const destination = path.join(root, skill.name);
448
466
  written.push(destination);
@@ -480,12 +498,17 @@ export async function installSkills(options) {
480
498
  }
481
499
  }
482
500
  // Register the hosted MCP server for app-backed skills (visual-plan /
483
- // visual-recap → Agent-Native Plan, assets, design-exploration) so the
484
- // agent can actually call them — not just read the SKILL.md. On by
485
- // default; `--no-mcp` installs the skill files only. One registration per
486
- // app, so visual-plan + visual-recap share a single "plan" server.
501
+ // visual-recap → Agent-Native Plan) so the agent can actually call them,
502
+ // not just read the SKILL.md. On by default; `--no-mcp` installs the
503
+ // skill files only. One registration per app, so visual-plan +
504
+ // visual-recap share a single "plan" server.
487
505
  if (mcpApps.length > 0) {
488
- const mcpClients = clients.map((client) => client === "claude-code" ? "claude-code" : "codex");
506
+ const mcpClients = clients
507
+ .map(skillClientToMcpClient)
508
+ .filter((client) => Boolean(client));
509
+ if (mcpClients.length === 0) {
510
+ throw new Error("MCP setup supports Claude Code, Codex, Cursor, OpenCode, GitHub Copilot / VS Code, or Claude Cowork. Use --mode local-files or --no-mcp for Pi.");
511
+ }
489
512
  for (const app of mcpApps) {
490
513
  progress?.message(`Registering ${app.displayName} MCP server...`);
491
514
  if (!options.dryRun) {
@@ -574,6 +597,7 @@ function defaultArgs(command) {
574
597
  force: false,
575
598
  connect: true,
576
599
  mcp: true,
600
+ quiet: false,
577
601
  };
578
602
  }
579
603
  function selectedPlanApp(skillNames) {
@@ -651,14 +675,34 @@ function normalizeClients(value) {
651
675
  return CLIENTS;
652
676
  if (client === "codex")
653
677
  return ["codex"];
678
+ if (client === "pi")
679
+ return ["pi"];
680
+ if (client === "cursor")
681
+ return ["cursor"];
682
+ if (client === "opencode" || client === "open-code") {
683
+ return ["opencode"];
684
+ }
685
+ if (client === "github-copilot" ||
686
+ client === "copilot" ||
687
+ client === "vscode" ||
688
+ client === "vs-code") {
689
+ return ["github-copilot"];
690
+ }
654
691
  if (client === "claude" ||
655
692
  client === "claude-code" ||
656
693
  client === "claude-code-cli") {
657
694
  return ["claude-code"];
658
695
  }
659
- throw new Error(`Unsupported client "${raw}". Use codex, claude-code, or all.`);
696
+ throw new Error(`Unsupported client "${raw}". Use codex, claude-code, pi, cursor, opencode, github-copilot, or all.`);
660
697
  });
661
698
  }
699
+ function skillClientToMcpClient(client) {
700
+ if (client === "pi")
701
+ return null;
702
+ if (client === "claude-code")
703
+ return "claude-code";
704
+ return client;
705
+ }
662
706
  function normalizeSkillName(value) {
663
707
  const normalized = value.trim().toLowerCase();
664
708
  if (!/^[a-z0-9][a-z0-9._-]*$/.test(normalized)) {
@@ -797,7 +841,7 @@ async function printInstallResult(result, options) {
797
841
  clack.note([
798
842
  "No sharing, all local.",
799
843
  "Run: npx @agent-native/core@latest plan blocks --out plan-blocks.md",
800
- "Preview: npx @agent-native/core@latest plan local preview --dir plans/<slug>",
844
+ "Preview: npx @agent-native/core@latest plan local preview --dir plans/<slug> --open",
801
845
  ].join("\n"), "Local Plan files");
802
846
  }
803
847
  if (!options.dryRun) {
@@ -840,15 +884,24 @@ function isInteractive(options) {
840
884
  function installRootForClient(client, scope, baseDir) {
841
885
  const home = process.env.HOME || os.homedir();
842
886
  if (scope === "project") {
843
- return client === "codex"
844
- ? path.join(baseDir, ".agents", "skills")
845
- : path.join(baseDir, ".claude", "skills");
887
+ if (client !== "claude-code") {
888
+ return path.join(baseDir, ".agents", "skills");
889
+ }
890
+ return path.join(baseDir, ".claude", "skills");
846
891
  }
847
892
  if (client === "codex") {
848
893
  return process.env.CODEX_HOME
849
894
  ? path.join(process.env.CODEX_HOME, "skills")
850
895
  : path.join(home, ".codex", "skills");
851
896
  }
897
+ if (client === "pi") {
898
+ return path.join(home, ".agents", "skills");
899
+ }
900
+ if (client === "cursor" ||
901
+ client === "opencode" ||
902
+ client === "github-copilot") {
903
+ return path.join(home, ".agents", "skills");
904
+ }
852
905
  return path.join(home, ".claude", "skills");
853
906
  }
854
907
  function discoverSkills(root) {
@@ -1083,13 +1136,44 @@ function upsertManagedBlock(file, block) {
1083
1136
  function escapeRegExp(value) {
1084
1137
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1085
1138
  }
1086
- function shouldWritePrVisualRecapWorkflow(options, baseDir) {
1139
+ const PR_VISUAL_RECAP_DOCS_URL = "https://www.agent-native.com/docs/pr-visual-recap";
1140
+ function prVisualRecapWorkflowPath(baseDir) {
1141
+ return path.join(baseDir, ".github", "workflows", "pr-visual-recap.yml");
1142
+ }
1143
+ function prVisualRecapSetupCommand() {
1144
+ return "npx @agent-native/core@latest recap setup";
1145
+ }
1146
+ async function promptForGithubAction(context) {
1147
+ const clack = await import("@clack/prompts");
1148
+ const result = await clack.confirm({
1149
+ message: "Optional: add automatic PR Visual Recaps? (GitHub Action)\n" +
1150
+ " Posts a human-friendly recap on every pull request.\n" +
1151
+ ` Learn more: ${context.docsUrl}\n` +
1152
+ ` Writes ${context.workflowPath}; ${context.setupCommand} finishes the GitHub secrets.`,
1153
+ initialValue: false,
1154
+ });
1155
+ if (clack.isCancel(result)) {
1156
+ clack.cancel("Skipped PR Visual Recap workflow.");
1157
+ return null;
1158
+ }
1159
+ return Boolean(result);
1160
+ }
1161
+ async function shouldWritePrVisualRecapWorkflow(hasVisualRecap, options, baseDir) {
1162
+ if (!hasVisualRecap)
1163
+ return false;
1087
1164
  if (options.withGithubAction)
1088
1165
  return true;
1089
- if (fs.existsSync(path.join(baseDir, ".github", "workflows", "pr-visual-recap.yml"))) {
1166
+ if (fs.existsSync(prVisualRecapWorkflowPath(baseDir)))
1090
1167
  return false;
1091
- }
1092
- return false;
1168
+ if (options.yes || !isInteractive(options))
1169
+ return false;
1170
+ const prompt = options.promptGithubAction ?? promptForGithubAction;
1171
+ const choice = await prompt({
1172
+ workflowPath: path.join(".github", "workflows", "pr-visual-recap.yml"),
1173
+ setupCommand: prVisualRecapSetupCommand(),
1174
+ docsUrl: PR_VISUAL_RECAP_DOCS_URL,
1175
+ });
1176
+ return choice === true;
1093
1177
  }
1094
1178
  const PR_VISUAL_RECAP_REUSABLE_WORKFLOW = `name: PR Visual Recap
1095
1179