@agent-native/core 0.51.7 → 0.51.9

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.
@@ -21,7 +21,7 @@ Usage:
21
21
  npx @agent-native/core@latest skills list
22
22
  npx @agent-native/core@latest skills status [assets|design-exploration|visual-plan|visual-recap|context-xray] [--client codex|claude-code|all] [--scope user|project] [--json]
23
23
  npx @agent-native/core@latest skills update [assets|design-exploration|visual-plan|visual-recap|context-xray] [--client codex|claude-code|all] [--scope user|project] [--dry-run] [--json]
24
- npx @agent-native/core@latest skills add assets|design-exploration|visual-plan|visual-recap|context-xray [--client codex|claude-code|claude-code-cli|cowork|all] [--scope user|project] [--mcp-url <url>] [--no-connect] [--with-github-action] [--yes] [--dry-run] [--json]
24
+ npx @agent-native/core@latest skills add assets|design-exploration|visual-plan|visual-recap|context-xray [--client codex|claude-code|claude-code-cli|cowork|all] [--scope user|project] [--mode hosted|local-files|self-hosted] [--mcp-url <url>] [--no-connect] [--with-github-action] [--yes] [--dry-run] [--json]
25
25
  npx @agent-native/core@latest skills add <manifest-or-app-dir|skill-repo> [--skill <name>] [--client ...] [--yes]
26
26
 
27
27
  Examples:
@@ -30,6 +30,8 @@ Examples:
30
30
  npx @agent-native/core@latest skills add visual-plan
31
31
  npx @agent-native/core@latest skills add visual-recap
32
32
  npx @agent-native/core@latest skills add visual-recap --with-github-action
33
+ npx @agent-native/core@latest skills add visual-plan --mode local-files
34
+ npx @agent-native/core@latest skills add visual-plan --mode self-hosted --mcp-url https://my-plan-app.example.com
33
35
  npx @agent-native/core@latest skills status visual-plan
34
36
  npx @agent-native/core@latest skills update visual-plan
35
37
  npx @agent-native/core@latest skills add visual-plan --no-connect
@@ -59,6 +61,13 @@ deployment) instead of the built-in hosted default — a bare origin gets the
59
61
  standard /_agent-native/mcp path appended. Use app-skill pack for marketplace
60
62
  bundles and custom adapter output.
61
63
 
64
+ When installing visual-plan or visual-recap interactively, the CLI asks where
65
+ plans and recaps should live: hosted Plans for shareable links/comments, local
66
+ files for "No sharing, all local.", or a self-hosted/custom Plan app URL.
67
+ Pass --mode to choose directly. Local-files mode skips MCP registration and
68
+ auth and installs instructions that default to a no-auth block catalog fetch,
69
+ MDX folders, and local preview.
70
+
62
71
  When installing visual-recap interactively, the CLI offers to add the optional PR
63
72
  Visual Recap GitHub Action. Pass --with-github-action to write it directly, then
64
73
  run "npx @agent-native/core@latest recap setup" / "npx @agent-native/core@latest recap doctor" to configure and
@@ -252,6 +261,22 @@ the connector without authenticating, then run
252
261
  whenever you are ready, or choose a narrower \`--client\`. Auth and MCP tool
253
262
  loading are per client config/session.
254
263
 
264
+ **Local-only / text installs.** If the user wants no sharing and all local files,
265
+ install with \`--mode local-files\`:
266
+
267
+ \`\`\`bash
268
+ npx @agent-native/core@latest skills add visual-plan --mode local-files
269
+ \`\`\`
270
+
271
+ This mode does not register the Plan MCP connector. Before authoring structured
272
+ MDX, fetch the no-auth, schema-only block catalog with
273
+ \`npx @agent-native/core@latest plan blocks --out plan-blocks.md\`, read that file,
274
+ write \`plans/<slug>/plan.mdx\` locally, then run \`plan local preview\`. Plain
275
+ text skill installs (Vercel Skills CLI, copied GitHub files, etc.) can follow
276
+ that same local flow if \`@agent-native/core\` is available. Text alone cannot
277
+ register MCP tools; hosted/shareable Plans still need the Agent-Native CLI
278
+ install/reconnect step above.
279
+
255
280
  **Browser (people you share with).** Open the Plans editor and create & edit
256
281
  with no sign-up — you work as a guest. Sign in only when you want to save or
257
282
  share; signing in claims the plans you made as a guest into your account.
@@ -259,9 +284,10 @@ share; signing in claims the plans you made as a guest into your account.
259
284
  Sharing and commenting require an account: public/shared plans are viewable by
260
285
  anyone with the link, but commenting on them needs an agent-native account.
261
286
 
262
- For fully offline, no-account use, run the Plans app locally and sync plans to
263
- your repo as MDX. This local mode is a separate advanced path, not the default
264
- hosted flow.
287
+ For fully offline, no-account use, use local-files mode and the local preview
288
+ command. The optional \`plan blocks\` lookup reads only public schema metadata; if
289
+ network access is unavailable, use the bundled references and validate with
290
+ \`plan local preview\`.
265
291
 
266
292
  If a Plans tool returns \`needs auth\`, \`Unauthorized\`, or \`Session terminated\`,
267
293
  do not keep retrying the tool. Stop and give the user the reconnect step for the
@@ -1291,11 +1317,20 @@ planning, repo-owned/source-controlled planning artifacts, or when
1291
1317
  \`AGENT_NATIVE_PLANS_MODE=local-files\` is set. Also use it when a user or repo
1292
1318
  policy says a plan must stay under their own brand, domain, source control, or
1293
1319
  infrastructure. In this mode the plan data must never be sent to the Plan MCP
1294
- server or Plan app action surface.
1320
+ server or Plan app action surface. Schema-only block catalog lookup is allowed
1321
+ because it sends no plan content: use the MCP \`get-plan-blocks\` tool if it is
1322
+ already available, or run
1323
+ \`npx @agent-native/core@latest plan blocks --out plan-blocks.md\` and read that
1324
+ file before authoring MDX.
1295
1325
 
1296
1326
  The local-files contract is:
1297
1327
 
1298
1328
  - Read source context from local files and shell commands only.
1329
+ - Fetch/read the block catalog before writing structured MDX. The
1330
+ \`plan blocks\` command calls the public no-auth \`get-plan-blocks\` route and
1331
+ writes only registry metadata to disk; use \`--format schema\` if exact nested
1332
+ fields are needed. If network access is unavailable, use the bundled
1333
+ references and rely on \`plan local preview\` to catch invalid tags.
1299
1334
  - Write the plan as a local MDX folder under \`plans/<slug>/\`: \`plan.mdx\`,
1300
1335
  optional \`canvas.mdx\`, optional \`prototype.mdx\`, and optional
1301
1336
  \`.plan-state.json\`.
@@ -1306,7 +1341,8 @@ The local-files contract is:
1306
1341
  - Do **not** call \`create-visual-plan\`, \`create-ui-plan\`,
1307
1342
  \`create-prototype-plan\`, \`create-plan-design\`, \`import-visual-plan-source\`,
1308
1343
  \`update-visual-plan\`, \`patch-visual-plan-source\`, \`get-plan-feedback\`,
1309
- \`export-visual-plan\`, or any hosted Plan tool for that plan.
1344
+ \`export-visual-plan\`, or any hosted Plan tool for that plan except the
1345
+ schema-only block catalog lookup above.
1310
1346
  - Treat feedback as file or chat feedback: update the MDX files directly, rerun
1311
1347
  the local preview command, and summarize the new local URL/path. Hosted
1312
1348
  comments, sharing, history, and publish/export receipts are unavailable until
@@ -1437,6 +1473,12 @@ In local-files mode:
1437
1473
  The existing \`npx @agent-native/core@latest recap collect-diff\`, \`scan\`, and
1438
1474
  \`build-prompt --local-files\` helpers are safe to use because they operate on
1439
1475
  local files and do not write to the Plan database.
1476
+ - Fetch/read the block catalog before writing structured MDX. Use
1477
+ \`npx @agent-native/core@latest plan blocks --out plan-blocks.md\` when the Plan
1478
+ MCP connector is not registered; it calls the public no-auth
1479
+ \`get-plan-blocks\` route and sends no recap content. If network access is
1480
+ unavailable, use the bundled references and validate with
1481
+ \`plan local preview\`.
1440
1482
  - Write the recap as a local MDX folder under \`plans/<slug>/\`: \`plan.mdx\`,
1441
1483
  optional \`canvas.mdx\`, optional \`prototype.mdx\`, and optional
1442
1484
  \`.plan-state.json\`. Set \`kind: "recap"\` and \`localOnly: true\` in
@@ -1448,7 +1490,8 @@ In local-files mode:
1448
1490
  - Do **not** call \`create-visual-recap\`, \`create-visual-plan\`,
1449
1491
  \`import-visual-plan-source\`, \`update-visual-plan\`,
1450
1492
  \`patch-visual-plan-source\`, \`get-plan-feedback\`, \`export-visual-plan\`,
1451
- \`set-resource-visibility\`, or any hosted Plan tool for that recap.
1493
+ \`set-resource-visibility\`, or any hosted Plan tool for that recap except the
1494
+ schema-only block catalog lookup above.
1452
1495
  - Treat review feedback as file or chat feedback: update the MDX files directly,
1453
1496
  rerun the local preview command, and summarize the new local URL/path.
1454
1497
  Hosted comments, sharing, screenshots, usage attachment, and PR sticky comment
@@ -1811,12 +1854,19 @@ memorized tags — they drift and silently produce a wrong tag (\`ApiEndpoint\`
1811
1854
  instead of \`Endpoint\`, \`JsonExplorer\` instead of \`Json\`, \`Tabs\` instead of
1812
1855
  \`TabsBlock\`) that errors on import.
1813
1856
 
1814
- **Before writing any structured plan content, call \`get-plan-blocks\` on the Plan
1815
- MCP connector (\`plan\` or legacy \`agent-native-plans\`).** It returns the
1816
- authoritative, always-current block
1817
- vocabulary generated live from the app's own block registry the same config
1818
- the renderer and MDX round-trip use so it can never be stale even if this
1819
- SKILL.md is an old installed copy:
1857
+ **Before writing any structured plan content, fetch/read the block catalog.** In
1858
+ hosted or self-hosted mode, call \`get-plan-blocks\` on the Plan MCP connector
1859
+ (\`plan\` or legacy \`agent-native-plans\`). In local-files mode, or when the skill
1860
+ was installed as plain text and no MCP tools are registered, run
1861
+ \`npx @agent-native/core@latest plan blocks --out plan-blocks.md\` and read that
1862
+ file first. The CLI command calls the public no-auth \`get-plan-blocks\` route and
1863
+ sends no plan/recap content. If network access is unavailable, use the bundled
1864
+ references and validate with \`plan local preview\`.
1865
+
1866
+ The catalog returns the authoritative, always-current block vocabulary generated
1867
+ live from the app's own block registry — the same config the renderer and MDX
1868
+ round-trip use — so it can never be stale even if this SKILL.md is an old
1869
+ installed copy:
1820
1870
 
1821
1871
  - \`get-plan-blocks\` (default \`format: "reference"\`) → a compact table of every
1822
1872
  block's runtime \`type\`, exact MDX \`<Tag>\`, placement, and key data fields.
@@ -2244,12 +2294,62 @@ function stableSkillHash(files) {
2244
2294
  }
2245
2295
  return hash.digest("hex").slice(0, 16);
2246
2296
  }
2247
- function skillFilesForBuiltIn(appSkillId) {
2297
+ function insertAfterFrontmatter(markdown, block) {
2298
+ if (!block.trim())
2299
+ return markdown;
2300
+ const match = markdown.match(/^---\n[\s\S]*?\n---\n/);
2301
+ if (!match)
2302
+ return `${block}\n\n${markdown}`;
2303
+ return `${match[0]}\n${block.trim()}\n\n${markdown.slice(match[0].length)}`;
2304
+ }
2305
+ function planModeInstructionBlock(input) {
2306
+ if (input.mode === "local-files") {
2307
+ return `## Installed Mode
2308
+
2309
+ Default storage for this installation: local files. Create and update plans and
2310
+ recaps as MDX folders under \`plans/<slug>/\`. Before authoring structured MDX,
2311
+ run \`npx @agent-native/core@latest plan blocks --out plan-blocks.md\` and read
2312
+ the no-auth block catalog; it sends no plan content. Then run
2313
+ \`npx @agent-native/core@latest plan local preview --dir plans/<slug> --kind plan|recap\`,
2314
+ and report the local preview URL or path. No sharing, all local. Use a hosted
2315
+ or self-hosted Plan MCP connector only if the user explicitly asks to publish or
2316
+ share.`;
2317
+ }
2318
+ if (input.mode === "self-hosted") {
2319
+ return `## Installed Mode
2320
+
2321
+ Default storage for this installation: the configured self-hosted/custom Plan
2322
+ app${input.mcpUrl ? ` at \`${input.mcpUrl}\`` : ""}. Use that Plan MCP connector
2323
+ for plans and recaps instead of assuming \`https://plan.agent-native.com\`.`;
2324
+ }
2325
+ return "";
2326
+ }
2327
+ function applyPlanModeToSkillMarkdown(markdown, input) {
2328
+ if (input.appSkillId !== "visual-plans")
2329
+ return markdown;
2330
+ const block = planModeInstructionBlock({
2331
+ mode: input.mode,
2332
+ mcpUrl: input.mcpUrl,
2333
+ });
2334
+ return insertAfterFrontmatter(markdown, block);
2335
+ }
2336
+ function skillFilesForBuiltIn(appSkillId, options = {}) {
2248
2337
  const entry = BUILT_IN_APP_SKILLS[appSkillId];
2249
2338
  const skills = {
2250
- [entry.skillName]: entry.skillMarkdown,
2339
+ [entry.skillName]: applyPlanModeToSkillMarkdown(entry.skillMarkdown, {
2340
+ appSkillId,
2341
+ mode: options.planMode,
2342
+ mcpUrl: options.mcpUrl,
2343
+ }),
2251
2344
  ...builtInExtraSkills(entry),
2252
2345
  };
2346
+ for (const [skillName, skillMarkdown] of Object.entries(builtInExtraSkills(entry))) {
2347
+ skills[skillName] = applyPlanModeToSkillMarkdown(skillMarkdown, {
2348
+ appSkillId,
2349
+ mode: options.planMode,
2350
+ mcpUrl: options.mcpUrl,
2351
+ });
2352
+ }
2253
2353
  const extraFiles = builtInExtraFiles(entry);
2254
2354
  const out = {};
2255
2355
  for (const [skillName, skillMarkdown] of Object.entries(skills)) {
@@ -2261,11 +2361,12 @@ function skillFilesForBuiltIn(appSkillId) {
2261
2361
  appSkillId,
2262
2362
  displayName: entry.manifest.displayName,
2263
2363
  skillName,
2264
- mcpUrl: isLocalOnlyBuiltInSkill(entry)
2364
+ mcpUrl: isLocalOnlyBuiltInSkill(entry) || options.planMode === "local-files"
2265
2365
  ? ""
2266
- : entry.manifest.hosted.mcpUrl,
2366
+ : (options.mcpUrl ?? entry.manifest.hosted.mcpUrl),
2267
2367
  files,
2268
2368
  contentHash: stableSkillHash(files),
2369
+ planMode: options.planMode,
2269
2370
  };
2270
2371
  }
2271
2372
  return out;
@@ -2295,6 +2396,7 @@ function writeSkillFolder(dir, bundle, installedAt = new Date().toISOString()) {
2295
2396
  mcpUrl: bundle.mcpUrl,
2296
2397
  installedAt,
2297
2398
  updateCommand: `npx @agent-native/core@latest skills update ${bundle.skillName}`,
2399
+ ...(bundle.planMode ? { planMode: bundle.planMode } : {}),
2298
2400
  };
2299
2401
  fs.writeFileSync(path.join(dir, AGENT_NATIVE_SKILL_METADATA_FILE), `${JSON.stringify(metadata, null, 2)}\n`, "utf-8");
2300
2402
  }
@@ -2324,7 +2426,10 @@ function builtInSkillsRootForAgent(agent, scope, baseDir) {
2324
2426
  * (which would have to be published to npm first). Returns the written folders.
2325
2427
  */
2326
2428
  function installBuiltInInstructions(input) {
2327
- const bundles = Object.values(skillFilesForBuiltIn(input.appSkillId)).filter((bundle) => !input.onlySkillNames || input.onlySkillNames.includes(bundle.skillName));
2429
+ const bundles = Object.values(skillFilesForBuiltIn(input.appSkillId, {
2430
+ planMode: input.planMode,
2431
+ mcpUrl: input.mcpUrl,
2432
+ })).filter((bundle) => !input.onlySkillNames || input.onlySkillNames.includes(bundle.skillName));
2328
2433
  const written = [];
2329
2434
  for (const agent of input.skillsAgents) {
2330
2435
  const root = builtInSkillsRootForAgent(agent, input.scope, input.baseDir);
@@ -2438,7 +2543,7 @@ function clientFilterForStatus(parsed) {
2438
2543
  }
2439
2544
  function collectSkillInstallStates(parsed, options) {
2440
2545
  const appSkillIds = targetIdsForStatus(parsed);
2441
- const latest = latestSkillBundlesForTargets(appSkillIds);
2546
+ const defaultLatest = latestSkillBundlesForTargets(appSkillIds);
2442
2547
  const roots = skillSearchRoots({
2443
2548
  baseDir: options.baseDir ?? process.cwd(),
2444
2549
  clients: clientFilterForStatus(parsed),
@@ -2447,7 +2552,7 @@ function collectSkillInstallStates(parsed, options) {
2447
2552
  const states = [];
2448
2553
  const seenDirs = new Set();
2449
2554
  for (const root of roots) {
2450
- for (const bundle of Object.values(latest)) {
2555
+ for (const bundle of Object.values(defaultLatest)) {
2451
2556
  const dir = path.join(root.root, bundle.skillName);
2452
2557
  const resolvedDir = path.resolve(dir);
2453
2558
  if (seenDirs.has(resolvedDir) || !fs.existsSync(dir))
@@ -2456,35 +2561,41 @@ function collectSkillInstallStates(parsed, options) {
2456
2561
  continue;
2457
2562
  seenDirs.add(resolvedDir);
2458
2563
  const files = listSkillFolderFiles(dir);
2459
- const installedHash = Object.keys(files).length > 0 ? stableSkillHash(files) : null;
2460
2564
  const metadata = readSkillInstallMetadata(dir);
2565
+ const stateBundle = skillFilesForBuiltIn(bundle.appSkillId, {
2566
+ planMode: metadata?.planMode,
2567
+ mcpUrl: metadata?.mcpUrl,
2568
+ })[bundle.skillName] ?? bundle;
2569
+ const installedHash = Object.keys(files).length > 0 ? stableSkillHash(files) : null;
2461
2570
  states.push({
2462
- appSkillId: bundle.appSkillId,
2463
- displayName: bundle.displayName,
2464
- skillName: bundle.skillName,
2571
+ appSkillId: stateBundle.appSkillId,
2572
+ displayName: stateBundle.displayName,
2573
+ skillName: stateBundle.skillName,
2465
2574
  path: dir,
2466
2575
  root: root.root,
2467
2576
  scope: root.scope,
2468
2577
  client: root.client,
2469
- latestHash: bundle.contentHash,
2578
+ latestHash: stateBundle.contentHash,
2470
2579
  installedHash,
2471
2580
  metadataHash: metadata?.contentHash,
2472
- current: installedHash === bundle.contentHash,
2581
+ current: installedHash === stateBundle.contentHash,
2473
2582
  managed: metadata?.source === "agent-native",
2583
+ planMode: metadata?.planMode,
2584
+ mcpUrl: metadata?.mcpUrl,
2474
2585
  });
2475
2586
  }
2476
2587
  }
2477
2588
  return states.sort((a, b) => `${a.skillName}:${a.path}`.localeCompare(`${b.skillName}:${b.path}`));
2478
2589
  }
2479
2590
  function updateSkillInstallStates(states, dryRun) {
2480
- const latest = latestSkillBundlesForTargets([
2481
- ...new Set(states.map((state) => state.appSkillId)),
2482
- ]);
2483
2591
  const updated = [];
2484
2592
  for (const state of states) {
2485
2593
  if (state.current && state.managed)
2486
2594
  continue;
2487
- const bundle = latest[state.skillName];
2595
+ const bundle = skillFilesForBuiltIn(state.appSkillId, {
2596
+ planMode: state.planMode,
2597
+ mcpUrl: state.mcpUrl,
2598
+ })[state.skillName];
2488
2599
  if (!bundle)
2489
2600
  continue;
2490
2601
  if (!dryRun)
@@ -2622,6 +2733,56 @@ async function promptForScope(context) {
2622
2733
  }
2623
2734
  return result === "project" ? "project" : "user";
2624
2735
  }
2736
+ async function promptForPlanMode(context) {
2737
+ const clack = await import("@clack/prompts");
2738
+ const result = await clack.select({
2739
+ message: "Where should visual plans and recaps live?",
2740
+ options: [
2741
+ {
2742
+ value: "hosted",
2743
+ label: "Hosted Plans, shareable links",
2744
+ hint: "Stores plans at plan.agent-native.com. Enables sharing, comments, browser editor. Requires one-time browser sign-in.",
2745
+ },
2746
+ {
2747
+ value: "local-files",
2748
+ label: "Local files only",
2749
+ hint: "Writes plans/<name>/plan.mdx in this repo and opens a local preview. No sharing, all local.",
2750
+ },
2751
+ {
2752
+ value: "self-hosted",
2753
+ label: "Self-hosted/custom URL",
2754
+ hint: "Connect to your own Plan app or local dev tunnel.",
2755
+ },
2756
+ ],
2757
+ initialValue: context.initialMode,
2758
+ });
2759
+ if (clack.isCancel(result)) {
2760
+ clack.cancel("Cancelled.");
2761
+ return null;
2762
+ }
2763
+ return normalizePlanInstallMode(String(result));
2764
+ }
2765
+ async function promptForPlanMcpUrl() {
2766
+ const clack = await import("@clack/prompts");
2767
+ const result = await clack.text({
2768
+ message: "Plan app URL or MCP URL",
2769
+ placeholder: "https://my-plan-app.example.com",
2770
+ validate(value) {
2771
+ try {
2772
+ resolveMcpUrlOverride(value);
2773
+ return undefined;
2774
+ }
2775
+ catch (err) {
2776
+ return err?.message ?? "Enter a valid http:// or https:// URL.";
2777
+ }
2778
+ },
2779
+ });
2780
+ if (clack.isCancel(result)) {
2781
+ clack.cancel("Cancelled.");
2782
+ return null;
2783
+ }
2784
+ return String(result).trim();
2785
+ }
2625
2786
  async function promptForSkills(context) {
2626
2787
  const clack = await import("@clack/prompts");
2627
2788
  const result = await clack.multiselect({
@@ -2659,6 +2820,30 @@ async function resolveSkillsClients(parsed, options) {
2659
2820
  }
2660
2821
  return selected;
2661
2822
  }
2823
+ function normalizePlanInstallMode(value) {
2824
+ const normalized = value?.trim().toLowerCase();
2825
+ if (!normalized || normalized === "hosted")
2826
+ return "hosted";
2827
+ if (normalized === "local" ||
2828
+ normalized === "local-file" ||
2829
+ normalized === "local-files" ||
2830
+ normalized === "files") {
2831
+ return "local-files";
2832
+ }
2833
+ if (normalized === "self-hosted" ||
2834
+ normalized === "selfhosted" ||
2835
+ normalized === "custom" ||
2836
+ normalized === "custom-url") {
2837
+ return "self-hosted";
2838
+ }
2839
+ throw new Error('--mode must be one of "hosted", "local-files", or "self-hosted".');
2840
+ }
2841
+ function targetIncludesPlans(target) {
2842
+ return normalizeKnownSkillTarget(target) === "visual-plans";
2843
+ }
2844
+ function targetsIncludePlans(targets) {
2845
+ return targets.some(targetIncludesPlans);
2846
+ }
2662
2847
  async function resolveSkillTargets(parsed, options) {
2663
2848
  if (parsed.target || !shouldPrompt(parsed, options)) {
2664
2849
  return [parsed.target ?? "assets"];
@@ -2755,6 +2940,14 @@ export function parseSkillsArgs(argv) {
2755
2940
  }
2756
2941
  else if ((value = eat("--mcp-url")) !== undefined)
2757
2942
  out.mcpUrl = value;
2943
+ else if ((value = eat("--mode")) !== undefined)
2944
+ out.planMode = normalizePlanInstallMode(value);
2945
+ else if (arg === "--hosted")
2946
+ out.planMode = "hosted";
2947
+ else if (arg === "--local" || arg === "--local-files")
2948
+ out.planMode = "local-files";
2949
+ else if (arg === "--self-hosted" || arg === "--custom-url")
2950
+ out.planMode = "self-hosted";
2758
2951
  else if (arg === "--yes" || arg === "-y")
2759
2952
  out.yes = true;
2760
2953
  else if (arg === "--dry-run")
@@ -2785,6 +2978,9 @@ export function parseSkillsArgs(argv) {
2785
2978
  if (out.scope !== "user" && out.scope !== "project") {
2786
2979
  throw new Error("--scope must be either user or project.");
2787
2980
  }
2981
+ if (out.planMode === "local-files" && out.mcpUrl) {
2982
+ throw new Error("--mode local-files cannot be combined with --mcp-url.");
2983
+ }
2788
2984
  return out;
2789
2985
  }
2790
2986
  function loadSkillTarget(target, onlySkillNames) {
@@ -2898,6 +3094,8 @@ function dryRunInstallCommand(parsed, target) {
2898
3094
  "--scope",
2899
3095
  parsed.scope,
2900
3096
  ];
3097
+ if (parsed.planMode)
3098
+ args.push("--mode", parsed.planMode);
2901
3099
  if (parsed.mcpUrl)
2902
3100
  args.push("--mcp-url", parsed.mcpUrl);
2903
3101
  if (parsed.instructions && !parsed.mcp)
@@ -3159,6 +3357,20 @@ async function connectAfterEnsure(installTarget, clients, parsed, options) {
3159
3357
  export async function addAgentNativeSkill(parsed, options = {}) {
3160
3358
  const target = parsed.target ?? "assets";
3161
3359
  const knownTarget = normalizeKnownSkillTarget(target);
3360
+ const planMode = knownTarget === "visual-plans"
3361
+ ? (parsed.planMode ?? (parsed.mcpUrl ? "self-hosted" : "hosted"))
3362
+ : undefined;
3363
+ if (parsed.planMode && knownTarget !== "visual-plans") {
3364
+ throw new Error("--mode only applies to visual-plan / visual-recap.");
3365
+ }
3366
+ if (planMode === "local-files" && parsed.mcpUrl) {
3367
+ throw new Error("--mode local-files cannot be combined with --mcp-url.");
3368
+ }
3369
+ if (planMode === "self-hosted" && !parsed.mcpUrl) {
3370
+ throw new Error("--mode self-hosted requires --mcp-url <url>.");
3371
+ }
3372
+ const shouldRegisterMcp = parsed.mcp &&
3373
+ !(knownTarget === "visual-plans" && planMode === "local-files");
3162
3374
  // For multi-skill bundles (the plan bundle), a single-skill target installs
3163
3375
  // only that skill. `installsRecap` controls the PR Visual Recap github-action
3164
3376
  // offer, which is only relevant when the recap skill is part of the install.
@@ -3258,12 +3470,15 @@ export async function addAgentNativeSkill(parsed, options = {}) {
3258
3470
  displayName: installTarget.displayName,
3259
3471
  skillNames: installTarget.skillNames,
3260
3472
  skillsAgents,
3261
- mcpUrl: installTarget.loaded.manifest.hosted.mcpUrl,
3262
- mcpClients: clients,
3473
+ mcpUrl: knownTarget === "visual-plans" && planMode === "local-files"
3474
+ ? ""
3475
+ : installTarget.loaded.manifest.hosted.mcpUrl,
3476
+ mcpClients: shouldRegisterMcp ? clients : [],
3263
3477
  dryRun: true,
3264
3478
  commands: [dryRunInstallCommand(parsed, target)],
3265
3479
  githubActionPath,
3266
3480
  githubActionSuggestedCommand,
3481
+ planMode,
3267
3482
  };
3268
3483
  }
3269
3484
  finally {
@@ -3279,7 +3494,7 @@ export async function addAgentNativeSkill(parsed, options = {}) {
3279
3494
  try {
3280
3495
  if (parsed.instructions) {
3281
3496
  if (skillsAgents.length === 0) {
3282
- if (!parsed.mcp) {
3497
+ if (!shouldRegisterMcp) {
3283
3498
  throw new Error("Skill instructions can only be installed for Codex or Claude Code clients. Use an MCP-capable client or omit --instructions-only.");
3284
3499
  }
3285
3500
  }
@@ -3295,6 +3510,8 @@ export async function addAgentNativeSkill(parsed, options = {}) {
3295
3510
  scope: parsed.scope,
3296
3511
  baseDir: options.baseDir ?? process.cwd(),
3297
3512
  dryRun: parsed.dryRun,
3513
+ planMode,
3514
+ mcpUrl: installTarget.loaded.manifest.hosted.mcpUrl,
3298
3515
  });
3299
3516
  instructionSource = instructionsWritten[0];
3300
3517
  commands.push(...instructionsWritten.map((dir) => `write ${dir}`));
@@ -3333,7 +3550,7 @@ export async function addAgentNativeSkill(parsed, options = {}) {
3333
3550
  scope: parsed.scope,
3334
3551
  dryRun: Boolean(parsed.dryRun),
3335
3552
  });
3336
- if (parsed.mcp) {
3553
+ if (shouldRegisterMcp) {
3337
3554
  commands.push(`npx @agent-native/core@latest app-skill ensure --manifest ${installTarget.loaded.file} --client ${parsed.client} --scope ${parsed.scope} --yes`);
3338
3555
  if (!parsed.dryRun) {
3339
3556
  await ensureAppSkill(installTarget.loaded, {
@@ -3414,13 +3631,16 @@ export async function addAgentNativeSkill(parsed, options = {}) {
3414
3631
  instructionSource,
3415
3632
  skillNames: installTarget.skillNames,
3416
3633
  skillsAgents,
3417
- mcpUrl: installTarget.loaded.manifest.hosted.mcpUrl,
3418
- mcpClients: clients,
3634
+ mcpUrl: knownTarget === "visual-plans" && planMode === "local-files"
3635
+ ? ""
3636
+ : installTarget.loaded.manifest.hosted.mcpUrl,
3637
+ mcpClients: shouldRegisterMcp ? clients : [],
3419
3638
  dryRun: parsed.dryRun,
3420
3639
  commands,
3421
3640
  written: instructionsWritten,
3422
3641
  connected,
3423
3642
  connectCommand,
3643
+ planMode,
3424
3644
  githubActionPath,
3425
3645
  githubActionExisted,
3426
3646
  githubActionSuggestedCommand,
@@ -3454,6 +3674,7 @@ function skillStateJson(state) {
3454
3674
  installedHash: state.installedHash,
3455
3675
  latestHash: state.latestHash,
3456
3676
  metadataHash: state.metadataHash,
3677
+ planMode: state.planMode,
3457
3678
  };
3458
3679
  }
3459
3680
  function formatSkillState(state) {
@@ -3464,6 +3685,13 @@ function formatSkillState(state) {
3464
3685
  : "";
3465
3686
  return `${state.skillName.padEnd(22)} ${status.padEnd(7)} ${state.scope}/${state.client} ${managed}${hashes}\n ${state.path}`;
3466
3687
  }
3688
+ function planModeSummary(mode) {
3689
+ if (mode === "local-files")
3690
+ return "Local files - No sharing, all local.";
3691
+ if (mode === "self-hosted")
3692
+ return "Self-hosted/custom Plan app";
3693
+ return "Hosted Plans - shareable links and comments";
3694
+ }
3467
3695
  function runSkillsStatusOrUpdate(parsed, options, update) {
3468
3696
  const before = collectSkillInstallStates(parsed, options);
3469
3697
  const changed = update ? updateSkillInstallStates(before, parsed.dryRun) : [];
@@ -3586,6 +3814,45 @@ export async function runSkills(argv, options = {}) {
3586
3814
  selectedAll: targets.length === skillPromptOptions().length,
3587
3815
  preselected,
3588
3816
  });
3817
+ const includesPlans = targetsIncludePlans(targets);
3818
+ if (parsed.planMode && !includesPlans) {
3819
+ throw new Error("--mode only applies to visual-plan / visual-recap.");
3820
+ }
3821
+ if (includesPlans) {
3822
+ if (!parsed.planMode && parsed.mcpUrl) {
3823
+ parsed.planMode = "self-hosted";
3824
+ }
3825
+ if (!parsed.planMode && shouldPrompt(parsed, options)) {
3826
+ const prompt = options.promptPlanMode ?? promptForPlanMode;
3827
+ const mode = await prompt({ initialMode: "hosted" });
3828
+ if (!mode) {
3829
+ telemetry.track("skills_cli cancelled", { step: "plan-mode" });
3830
+ return;
3831
+ }
3832
+ parsed.planMode = mode;
3833
+ }
3834
+ if (!parsed.planMode)
3835
+ parsed.planMode = "hosted";
3836
+ if (parsed.planMode === "self-hosted" && !parsed.mcpUrl) {
3837
+ if (shouldPrompt(parsed, options)) {
3838
+ const prompt = options.promptPlanMcpUrl ?? promptForPlanMcpUrl;
3839
+ const mcpUrl = await prompt();
3840
+ if (!mcpUrl) {
3841
+ telemetry.track("skills_cli cancelled", {
3842
+ step: "plan-mcp-url",
3843
+ });
3844
+ return;
3845
+ }
3846
+ parsed.mcpUrl = mcpUrl;
3847
+ }
3848
+ else {
3849
+ throw new Error("--mode self-hosted requires --mcp-url <url> in non-interactive mode.");
3850
+ }
3851
+ }
3852
+ telemetry.track("skills_cli plan mode selected", {
3853
+ mode: parsed.planMode,
3854
+ });
3855
+ }
3589
3856
  const clients = await resolveSkillsClients(parsed, optionsWithTelemetry);
3590
3857
  if (!clients) {
3591
3858
  telemetry.track("skills_cli cancelled", { step: "clients" });
@@ -3684,6 +3951,11 @@ export async function runSkills(argv, options = {}) {
3684
3951
  .filter((result) => result.local)
3685
3952
  .flatMap((result) => result.commands)),
3686
3953
  ];
3954
+ const planModes = [
3955
+ ...new Set(results
3956
+ .map((result) => result.planMode)
3957
+ .filter((mode) => Boolean(mode))),
3958
+ ];
3687
3959
  const authConnected = results.some((result) => result.connected);
3688
3960
  const pendingConnectCommands = [
3689
3961
  ...new Set(results
@@ -3720,6 +3992,9 @@ export async function runSkills(argv, options = {}) {
3720
3992
  ? `MCP config ${mcpClients.join(", ")}`
3721
3993
  : "MCP config not required",
3722
3994
  mcpUrls.length ? `MCP URL ${mcpUrls.join(", ")}` : "",
3995
+ planModes.length
3996
+ ? `Plan mode ${planModes.map(planModeSummary).join(", ")}`
3997
+ : "",
3723
3998
  authConnected
3724
3999
  ? "Authentication completed"
3725
4000
  : pendingConnectCommands.length