@agent-native/core 0.51.10 → 0.51.13

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.
@@ -45,16 +45,16 @@ Examples:
45
45
  The add command installs the SKILL.md instructions, registers the app-backed
46
46
  MCP connector, and then authenticates it in one step so you do not hit an OAuth
47
47
  wall on the first tool call. By default, add targets every supported local
48
- client this CLI can configure (Claude Code, Claude Code CLI, Codex, and Cowork);
48
+ client this CLI can configure (Claude Code, Codex, and Cowork);
49
49
  pass --client to narrow it. Authentication reuses "npx @agent-native/core@latest connect":
50
50
  OAuth-capable clients (Claude Code) get a URL-only entry and a /mcp authenticate
51
51
  prompt, while Codex / Cowork run the browser device-code flow. In a
52
52
  non-interactive shell or CI the auth step is skipped and the exact
53
53
  "npx @agent-native/core@latest connect <url> --client all" command is printed instead.
54
54
 
55
- Running "npx @agent-native/skills@latest add ..." directly installs instructions only;
56
- use this Agent Native CLI path when you want MCP setup and auth too. Pass --no-connect to
57
- register the connector without authenticating (leave auth to the host or run
55
+ Running "npx @agent-native/skills@latest add ..." uses this same shared install
56
+ flow with the broader BuilderIO skills catalog enabled. Pass --no-connect to
57
+ register MCP where possible without authenticating (leave auth to the host or run
58
58
  "npx @agent-native/core@latest connect" later). Pass --mcp-url to register that connector against
59
59
  a custom origin (an ngrok tunnel, a local dev server, or a self-hosted
60
60
  deployment) instead of the built-in hosted default — a bare origin gets the
@@ -2246,6 +2246,14 @@ const CLIENT_HINTS = {
2246
2246
  codex: "$CODEX_HOME/config.toml or ~/.codex/config.toml",
2247
2247
  cowork: "~/.cowork/mcp.json",
2248
2248
  };
2249
+ const SKILLS_CLIENTS = ["claude-code", "codex", "cowork"];
2250
+ const SKILL_INSTRUCTION_CLIENTS = ["claude-code", "codex"];
2251
+ const SKILL_INSTRUCTION_CLIENT_HINTS = {
2252
+ "claude-code": ".claude/skills or .claude/commands",
2253
+ "claude-code-cli": ".claude/skills or .claude/commands",
2254
+ codex: ".agents/skills or ~/.codex/skills",
2255
+ cowork: "MCP only",
2256
+ };
2249
2257
  function normalizeKnownSkillTarget(value) {
2250
2258
  const key = value?.trim().toLowerCase();
2251
2259
  if (!key)
@@ -2632,19 +2640,51 @@ function normalizeClientIds(values) {
2632
2640
  }
2633
2641
  return out;
2634
2642
  }
2635
- function clientPromptOptions() {
2636
- return CLIENTS.map((client) => ({
2643
+ function normalizeSkillsClientIds(values) {
2644
+ const seen = new Set();
2645
+ const out = [];
2646
+ for (const client of normalizeClientIds(values)) {
2647
+ const normalized = client === "claude-code-cli" ? "claude-code" : client;
2648
+ if (seen.has(normalized))
2649
+ continue;
2650
+ seen.add(normalized);
2651
+ out.push(normalized);
2652
+ }
2653
+ return out;
2654
+ }
2655
+ function resolveSkillsClientArg(client) {
2656
+ return normalizeSkillsClientIds(resolveClients(client));
2657
+ }
2658
+ function skillsClients(installsMcp) {
2659
+ return installsMcp ? SKILLS_CLIENTS : SKILL_INSTRUCTION_CLIENTS;
2660
+ }
2661
+ function filterSkillsClients(clients, installsMcp) {
2662
+ if (installsMcp)
2663
+ return clients;
2664
+ return clients.filter((client) => SKILL_INSTRUCTION_CLIENTS.includes(client));
2665
+ }
2666
+ function clientPromptOptions(installsMcp) {
2667
+ return skillsClients(installsMcp).map((client) => ({
2637
2668
  value: client,
2638
2669
  label: CLIENT_LABELS[client],
2639
- hint: CLIENT_HINTS[client],
2670
+ hint: installsMcp
2671
+ ? CLIENT_HINTS[client]
2672
+ : SKILL_INSTRUCTION_CLIENT_HINTS[client],
2640
2673
  }));
2641
2674
  }
2642
- // For now the interactive installer offers only the two plan skills, each as
2643
- // an independently selectable entry (uncheck one to install just the other).
2644
- // The other built-in skills stay installable via `agent-native skills add
2645
- // <name>` but are hidden from the default checklist. The values are the real
2646
- // slash-command names so users see exactly what they are installing.
2647
- const PLAN_SKILL_PROMPT_OPTIONS = [
2675
+ const DEFAULT_PUBLIC_SKILLS_SOURCE = "BuilderIO/skills";
2676
+ const PUBLIC_SKILL_TARGET_PREFIX = "public-skills:";
2677
+ const BUILT_IN_SKILL_PROMPT_OPTIONS = [
2678
+ {
2679
+ value: "assets",
2680
+ label: "assets",
2681
+ hint: BUILT_IN_APP_SKILLS.assets.manifest.description,
2682
+ },
2683
+ {
2684
+ value: "design-exploration",
2685
+ label: "design-exploration",
2686
+ hint: BUILT_IN_APP_SKILLS.design.manifest.description,
2687
+ },
2648
2688
  {
2649
2689
  value: "visual-plan",
2650
2690
  label: "visual-plan",
@@ -2655,9 +2695,64 @@ const PLAN_SKILL_PROMPT_OPTIONS = [
2655
2695
  label: "visual-recap",
2656
2696
  hint: "Interactive visual recap that maps PRs/diffs with diagrams, annotated diffs, API/schema summaries, and review notes.",
2657
2697
  },
2698
+ {
2699
+ value: "context-xray",
2700
+ label: "context-xray",
2701
+ hint: BUILT_IN_APP_SKILLS["context-xray"].manifest.description,
2702
+ },
2658
2703
  ];
2659
- function skillPromptOptions() {
2660
- return PLAN_SKILL_PROMPT_OPTIONS;
2704
+ const DEFAULT_SKILL_PROMPT_TARGETS = ["visual-plan", "visual-recap"];
2705
+ function publicSkillEntries(options) {
2706
+ if (options.catalogMode !== "all")
2707
+ return [];
2708
+ const seen = new Set();
2709
+ return (options.publicSkillEntries ?? [])
2710
+ .map((entry) => ({
2711
+ name: entry.name.trim().toLowerCase(),
2712
+ description: entry.description,
2713
+ }))
2714
+ .filter((entry) => {
2715
+ if (!entry.name || isKnownSkill(entry.name) || seen.has(entry.name)) {
2716
+ return false;
2717
+ }
2718
+ seen.add(entry.name);
2719
+ return true;
2720
+ })
2721
+ .sort((a, b) => a.name.localeCompare(b.name));
2722
+ }
2723
+ function publicSkillNames(options) {
2724
+ return new Set(publicSkillEntries(options).map((entry) => entry.name));
2725
+ }
2726
+ function publicSkillPromptOptions(options) {
2727
+ return publicSkillEntries(options).map((entry) => ({
2728
+ value: entry.name,
2729
+ label: entry.name,
2730
+ hint: entry.description ?? "Public skill from the BuilderIO skills catalog.",
2731
+ }));
2732
+ }
2733
+ function skillPromptOptions(options = {}) {
2734
+ return [
2735
+ ...BUILT_IN_SKILL_PROMPT_OPTIONS,
2736
+ ...publicSkillPromptOptions(options),
2737
+ ];
2738
+ }
2739
+ function defaultSkillPromptTargets(options) {
2740
+ return [
2741
+ ...DEFAULT_SKILL_PROMPT_TARGETS,
2742
+ ...publicSkillEntries(options).map((entry) => entry.name),
2743
+ ];
2744
+ }
2745
+ function publicSkillSelectionTarget(skillNames) {
2746
+ return `${PUBLIC_SKILL_TARGET_PREFIX}${skillNames.join(",")}`;
2747
+ }
2748
+ function publicSkillSelectionNames(target) {
2749
+ if (!target.startsWith(PUBLIC_SKILL_TARGET_PREFIX))
2750
+ return null;
2751
+ return target
2752
+ .slice(PUBLIC_SKILL_TARGET_PREFIX.length)
2753
+ .split(",")
2754
+ .map((name) => name.trim().toLowerCase())
2755
+ .filter(Boolean);
2661
2756
  }
2662
2757
  function prVisualRecapWorkflowPath(baseDir) {
2663
2758
  return path.join(baseDir, ".github", "workflows", "pr-visual-recap.yml");
@@ -2700,9 +2795,13 @@ function shouldPrompt(parsed, options) {
2700
2795
  }
2701
2796
  async function promptForClients(context) {
2702
2797
  const clack = await import("@clack/prompts");
2798
+ const message = context.installsMcp
2799
+ ? "Install the MCP connector and skills for which local agents?\n" +
2800
+ " (space toggles, enter confirms; saved for next time)"
2801
+ : "Install skill instructions for which local agents?\n" +
2802
+ " (space toggles, enter confirms; saved for next time)";
2703
2803
  const result = await clack.multiselect({
2704
- message: "Install the MCP connector for which local agents?\n" +
2705
- " (space toggles, enter confirms; saved for next time)",
2804
+ message,
2706
2805
  options: context.options,
2707
2806
  initialValues: context.initialClients,
2708
2807
  required: true,
@@ -2745,7 +2844,7 @@ async function promptForPlanMode(context) {
2745
2844
  {
2746
2845
  value: "hosted",
2747
2846
  label: "Hosted Plans, shareable links",
2748
- hint: "Stores plans at plan.agent-native.com. Enables sharing, comments, browser editor. Requires one-time browser sign-in.",
2847
+ hint: "100% free and open source. Stores plans at plan.agent-native.com with sharing, comments, and browser editor. Requires one-time browser sign-in.",
2749
2848
  },
2750
2849
  {
2751
2850
  value: "local-files",
@@ -2787,6 +2886,18 @@ async function promptForPlanMcpUrl() {
2787
2886
  }
2788
2887
  return String(result).trim();
2789
2888
  }
2889
+ async function promptForUpdateInstructions() {
2890
+ const clack = await import("@clack/prompts");
2891
+ const result = await clack.confirm({
2892
+ message: "Add managed AGENTS.md / CLAUDE.md instructions for always-on skill behavior?",
2893
+ initialValue: true,
2894
+ });
2895
+ if (clack.isCancel(result)) {
2896
+ clack.cancel("Skipped managed instruction updates.");
2897
+ return null;
2898
+ }
2899
+ return Boolean(result);
2900
+ }
2790
2901
  async function promptForSkills(context) {
2791
2902
  const clack = await import("@clack/prompts");
2792
2903
  const result = await clack.multiselect({
@@ -2804,15 +2915,20 @@ async function promptForSkills(context) {
2804
2915
  return [];
2805
2916
  return result.filter((value) => typeof value === "string");
2806
2917
  }
2807
- async function resolveSkillsClients(parsed, options) {
2918
+ async function resolveSkillsClients(parsed, options, installsMcp) {
2808
2919
  if (parsed.clientExplicit || !shouldPrompt(parsed, options)) {
2809
- return resolveClients(parsed.client);
2920
+ const clients = filterSkillsClients(resolveSkillsClientArg(parsed.client), installsMcp);
2921
+ if (clients.length === 0) {
2922
+ throw new Error("Local-file skill instructions only support Codex or Claude Code clients.");
2923
+ }
2924
+ return clients;
2810
2925
  }
2811
- const initialClients = resolveClients("all");
2926
+ const initialClients = skillsClients(installsMcp);
2812
2927
  const prompt = options.promptClients ?? promptForClients;
2813
- const selected = normalizeClientIds(await prompt({
2928
+ const selected = normalizeSkillsClientIds(await prompt({
2814
2929
  initialClients,
2815
- options: clientPromptOptions(),
2930
+ options: clientPromptOptions(installsMcp),
2931
+ installsMcp,
2816
2932
  }));
2817
2933
  if (selected.length === 0)
2818
2934
  return null;
@@ -2848,12 +2964,66 @@ function targetIncludesPlans(target) {
2848
2964
  function targetsIncludePlans(targets) {
2849
2965
  return targets.some(targetIncludesPlans);
2850
2966
  }
2967
+ function planSkillNamesSelected(skillNames) {
2968
+ return Boolean(skillNames?.some((name) => normalizeKnownSkillTarget(name) === "visual-plans"));
2969
+ }
2970
+ function recapSkillNamesSelected(skillNames) {
2971
+ return Boolean(skillNames?.some((name) => {
2972
+ const normalized = name.trim().toLowerCase();
2973
+ return (normalized === "visual-recap" ||
2974
+ normalized === "visual-recaps" ||
2975
+ normalizeKnownSkillTarget(normalized) === "visual-plans");
2976
+ }));
2977
+ }
2978
+ function resolveSelectedSkillTargets(selected, options) {
2979
+ const publicNames = publicSkillNames(options);
2980
+ const builtInSelections = [];
2981
+ const publicSelections = [];
2982
+ for (const raw of selected) {
2983
+ const skill = raw.trim().toLowerCase();
2984
+ if (!skill)
2985
+ continue;
2986
+ if (isKnownSkill(skill)) {
2987
+ builtInSelections.push(skill);
2988
+ continue;
2989
+ }
2990
+ if (publicNames.has(skill)) {
2991
+ publicSelections.push(skill);
2992
+ continue;
2993
+ }
2994
+ throw new Error(`Unknown skill: ${raw}. Run "npx @agent-native/core@latest skills list".`);
2995
+ }
2996
+ const out = [];
2997
+ const planSubskills = ["visual-plan", "visual-recap"];
2998
+ const selectedPlanSubskills = planSubskills.filter((skill) => builtInSelections.includes(skill));
2999
+ if (selectedPlanSubskills.length === planSubskills.length) {
3000
+ out.push("visual-plans");
3001
+ }
3002
+ else {
3003
+ out.push(...selectedPlanSubskills);
3004
+ }
3005
+ out.push(...builtInSelections.filter((skill) => !planSubskills.includes(skill) && !out.includes(skill)));
3006
+ if (publicSelections.length > 0) {
3007
+ out.push(publicSkillSelectionTarget([...new Set(publicSelections)]));
3008
+ }
3009
+ return out;
3010
+ }
2851
3011
  async function resolveSkillTargets(parsed, options) {
3012
+ if (!parsed.target && parsed.plainSkillNames?.length) {
3013
+ return resolveSelectedSkillTargets(parsed.plainSkillNames, options);
3014
+ }
2852
3015
  if (parsed.target || !shouldPrompt(parsed, options)) {
2853
- return [parsed.target ?? "assets"];
3016
+ const target = parsed.target ?? "assets";
3017
+ if (!parsed.target)
3018
+ return [target];
3019
+ const normalizedTarget = target.trim().toLowerCase();
3020
+ if (publicSkillNames(options).has(normalizedTarget)) {
3021
+ return [publicSkillSelectionTarget([normalizedTarget])];
3022
+ }
3023
+ return [target];
2854
3024
  }
2855
3025
  const prompt = options.promptSkills ?? promptForSkills;
2856
- const promptOptions = skillPromptOptions();
3026
+ const promptOptions = skillPromptOptions(options);
2857
3027
  // The interactive multiselect skill picker is about to be shown (no --skill /
2858
3028
  // target passed and we are interactive) — record the funnel "prompted" step.
2859
3029
  options.telemetry?.track("skills_cli skills prompted", {
@@ -2861,22 +3031,12 @@ async function resolveSkillTargets(parsed, options) {
2861
3031
  available: promptOptions.map((option) => option.value).join(","),
2862
3032
  });
2863
3033
  const selected = await prompt({
2864
- initialTargets: ["visual-plan", "visual-recap"],
3034
+ initialTargets: defaultSkillPromptTargets(options),
2865
3035
  options: promptOptions,
2866
3036
  });
2867
3037
  if (!selected || selected.length === 0)
2868
3038
  return null;
2869
- // Both plan skills share one MCP connector, so when both are selected install
2870
- // them through the bundle target — that registers/authenticates the connector
2871
- // once instead of twice.
2872
- const planSubskills = ["visual-plan", "visual-recap"];
2873
- if (planSubskills.every((skill) => selected.includes(skill))) {
2874
- return [
2875
- "visual-plans",
2876
- ...selected.filter((s) => !planSubskills.includes(s)),
2877
- ];
2878
- }
2879
- return selected;
3039
+ return resolveSelectedSkillTargets(selected, options);
2880
3040
  }
2881
3041
  export function parseSkillsArgs(argv) {
2882
3042
  const first = argv[0];
@@ -2932,6 +3092,14 @@ export function parseSkillsArgs(argv) {
2932
3092
  out.client = value;
2933
3093
  out.clientExplicit = true;
2934
3094
  }
3095
+ else if ((value = eat("--agent")) !== undefined) {
3096
+ out.client = value;
3097
+ out.clientExplicit = true;
3098
+ }
3099
+ else if ((value = eat("-a")) !== undefined) {
3100
+ out.client = value;
3101
+ out.clientExplicit = true;
3102
+ }
2935
3103
  else if ((value = eat("--skill")) !== undefined) {
2936
3104
  out.plainSkillNames = [...(out.plainSkillNames ?? []), value];
2937
3105
  }
@@ -2942,6 +3110,8 @@ export function parseSkillsArgs(argv) {
2942
3110
  out.scope = value;
2943
3111
  out.scopeExplicit = true;
2944
3112
  }
3113
+ else if ((value = eat("--cwd")) !== undefined)
3114
+ out.baseDir = value;
2945
3115
  else if ((value = eat("--mcp-url")) !== undefined)
2946
3116
  out.mcpUrl = value;
2947
3117
  else if ((value = eat("--mode")) !== undefined)
@@ -2958,6 +3128,18 @@ export function parseSkillsArgs(argv) {
2958
3128
  out.dryRun = true;
2959
3129
  else if (arg === "--json")
2960
3130
  out.printJson = true;
3131
+ else if (arg === "-g" || arg === "--global") {
3132
+ out.scope = "user";
3133
+ out.scopeExplicit = true;
3134
+ }
3135
+ else if (arg === "--project") {
3136
+ out.scope = "project";
3137
+ out.scopeExplicit = true;
3138
+ }
3139
+ else if (arg === "--copy") {
3140
+ // Compatibility with @agent-native/skills. Core always copies skill
3141
+ // instructions instead of linking to the source repo.
3142
+ }
2961
3143
  else if (arg === "--mcp-only")
2962
3144
  out.instructions = false;
2963
3145
  else if (arg === "--instructions-only" || arg === "--no-mcp")
@@ -3087,7 +3269,7 @@ function preserveMcpUrlAppPathOverride(target, input) {
3087
3269
  };
3088
3270
  }
3089
3271
  function dryRunInstallCommand(parsed, target) {
3090
- const clients = parsed.clients ?? resolveClients(parsed.client);
3272
+ const clients = parsed.clients ?? resolveSkillsClientArg(parsed.client);
3091
3273
  const args = [
3092
3274
  "@agent-native/core@latest",
3093
3275
  "skills",
@@ -3219,21 +3401,30 @@ function isGithubSkillRepoTarget(target) {
3219
3401
  function isPlainSkillRepoTarget(target) {
3220
3402
  return isPlainSkillRepoPath(target) || isGithubSkillRepoTarget(target);
3221
3403
  }
3222
- function agentNativeSkillsInstallArgs(parsed, target, clients) {
3404
+ function agentNativeSkillsInstallArgs(parsed, target, clients, baseDir) {
3223
3405
  const args = [
3224
3406
  "--yes",
3225
3407
  "@agent-native/skills@latest",
3226
3408
  "add",
3409
+ "--copy",
3227
3410
  target,
3228
3411
  "--client",
3229
3412
  clientArgForClients(clients),
3230
3413
  "--scope",
3231
3414
  parsed.scope,
3232
3415
  ];
3416
+ if (baseDir)
3417
+ args.push("--cwd", baseDir);
3233
3418
  if (parsed.withGithubAction)
3234
3419
  args.push("--with-github-action");
3235
3420
  if (parsed.force)
3236
3421
  args.push("--force");
3422
+ if (parsed.planMode)
3423
+ args.push("--mode", parsed.planMode);
3424
+ if (parsed.mcpUrl)
3425
+ args.push("--mcp-url", parsed.mcpUrl);
3426
+ if (!parsed.mcp)
3427
+ args.push("--no-mcp");
3237
3428
  if (!parsed.connect)
3238
3429
  args.push("--no-connect");
3239
3430
  for (const skill of parsed.plainSkillNames ?? []) {
@@ -3245,6 +3436,8 @@ function agentNativeSkillsInstallArgs(parsed, target, clients) {
3245
3436
  args.push("--no-update-instructions");
3246
3437
  if (parsed.yes)
3247
3438
  args.push("--yes");
3439
+ if (parsed.dryRun)
3440
+ args.push("--dry-run");
3248
3441
  return args;
3249
3442
  }
3250
3443
  async function addPlainSkillRepo(parsed, options) {
@@ -3252,15 +3445,16 @@ async function addPlainSkillRepo(parsed, options) {
3252
3445
  if (!parsed.instructions && parsed.mcp) {
3253
3446
  throw new Error("Plain skill repositories only install skill instructions. Run without --mcp-only.");
3254
3447
  }
3255
- if (parsed.mcpUrl) {
3448
+ if (parsed.mcpUrl && !planSkillNamesSelected(parsed.plainSkillNames)) {
3256
3449
  throw new Error("--mcp-url only applies to app-backed Agent Native skills.");
3257
3450
  }
3258
- const clients = parsed.clients ?? resolveClients(parsed.client);
3451
+ const clients = parsed.clients ?? resolveSkillsClientArg(parsed.client);
3259
3452
  const skillsAgents = skillsAgentsForClients(clients);
3453
+ const selectedSkillNames = parsed.plainSkillNames ?? [];
3260
3454
  if (skillsAgents.length === 0) {
3261
3455
  throw new Error("Plain skill repositories can only install instructions for Codex or Claude Code clients.");
3262
3456
  }
3263
- const args = agentNativeSkillsInstallArgs(parsed, target, clients);
3457
+ const args = agentNativeSkillsInstallArgs(parsed, target, clients, options.baseDir);
3264
3458
  if (!parsed.dryRun) {
3265
3459
  const code = await (options.runCommand ?? runCommand)("npx", args, {
3266
3460
  stdio: parsed.yes ? "silent" : "inherit",
@@ -3269,15 +3463,17 @@ async function addPlainSkillRepo(parsed, options) {
3269
3463
  throw new Error(`npx @agent-native/skills@latest add exited with ${code}.`);
3270
3464
  }
3271
3465
  options.telemetry?.track("skills_cli install completed", {
3272
- skills: target,
3466
+ skills: selectedSkillNames.length ? selectedSkillNames.join(",") : target,
3273
3467
  clients: clients.join(","),
3274
3468
  scope: parsed.scope,
3275
3469
  dryRun: Boolean(parsed.dryRun),
3276
3470
  });
3277
3471
  return {
3278
3472
  id: target,
3279
- displayName: target,
3280
- skillNames: [],
3473
+ displayName: selectedSkillNames.length
3474
+ ? selectedSkillNames.join(", ")
3475
+ : target,
3476
+ skillNames: selectedSkillNames,
3281
3477
  skillsAgents,
3282
3478
  mcpUrl: "",
3283
3479
  mcpClients: [],
@@ -3338,13 +3534,29 @@ async function connectAfterEnsure(installTarget, clients, parsed, options) {
3338
3534
  options.log?.(`Authenticating ${installTarget.displayName}…`);
3339
3535
  options.telemetry?.track("skills_cli connect started");
3340
3536
  try {
3341
- await (options.runConnect ?? runConnect)([
3537
+ const connectArgs = [
3342
3538
  hostedUrl,
3343
3539
  "--client",
3344
3540
  clientArgForClients(clients),
3345
3541
  "--scope",
3346
3542
  parsed.scope,
3347
- ]);
3543
+ ];
3544
+ if (options.runConnect) {
3545
+ await options.runConnect(connectArgs);
3546
+ }
3547
+ else {
3548
+ await runConnect(connectArgs, {
3549
+ isInteractive: options.isInteractive,
3550
+ logOut: (message) => {
3551
+ if (message.trim())
3552
+ options.log?.(message);
3553
+ },
3554
+ logErr: (message) => {
3555
+ if (message.trim())
3556
+ options.log?.(message);
3557
+ },
3558
+ });
3559
+ }
3348
3560
  options.telemetry?.track("skills_cli connect completed");
3349
3561
  return { connected: true, connectCommand: "" };
3350
3562
  }
@@ -3360,6 +3572,14 @@ async function connectAfterEnsure(installTarget, clients, parsed, options) {
3360
3572
  }
3361
3573
  export async function addAgentNativeSkill(parsed, options = {}) {
3362
3574
  const target = parsed.target ?? "assets";
3575
+ const publicSelection = publicSkillSelectionNames(target);
3576
+ if (publicSelection) {
3577
+ return addPlainSkillRepo({
3578
+ ...parsed,
3579
+ target: options.publicSkillSource ?? DEFAULT_PUBLIC_SKILLS_SOURCE,
3580
+ plainSkillNames: publicSelection,
3581
+ }, options);
3582
+ }
3363
3583
  const knownTarget = normalizeKnownSkillTarget(target);
3364
3584
  const planMode = knownTarget === "visual-plans"
3365
3585
  ? (parsed.planMode ?? (parsed.mcpUrl ? "self-hosted" : "hosted"))
@@ -3397,7 +3617,7 @@ export async function addAgentNativeSkill(parsed, options = {}) {
3397
3617
  if (!parsed.instructions && parsed.mcp) {
3398
3618
  throw new Error("Context X-Ray does not need MCP config yet. Run without --mcp-only.");
3399
3619
  }
3400
- const clients = parsed.clients ?? resolveClients(parsed.client);
3620
+ const clients = parsed.clients ?? resolveSkillsClientArg(parsed.client);
3401
3621
  const skillsAgents = skillsAgentsForClients(clients);
3402
3622
  if (parsed.dryRun) {
3403
3623
  const githubActionPath = parsed.withGithubAction && knownTarget === "visual-plans"
@@ -3452,7 +3672,7 @@ export async function addAgentNativeSkill(parsed, options = {}) {
3452
3672
  if (parsed.mcpUrl) {
3453
3673
  installTarget = withMcpUrlOverride(installTarget, parsed.mcpUrl);
3454
3674
  }
3455
- const clients = parsed.clients ?? resolveClients(parsed.client);
3675
+ const clients = parsed.clients ?? resolveSkillsClientArg(parsed.client);
3456
3676
  installTarget = preserveMcpUrlAppPathOverride(installTarget, parsed.mcpUrl);
3457
3677
  const skillsAgents = skillsAgentsForClients(clients);
3458
3678
  if (parsed.dryRun) {
@@ -3495,6 +3715,7 @@ export async function addAgentNativeSkill(parsed, options = {}) {
3495
3715
  let instructionsWritten;
3496
3716
  let connected = false;
3497
3717
  let connectCommand;
3718
+ let registeredMcpClients = shouldRegisterMcp ? clients : [];
3498
3719
  try {
3499
3720
  if (parsed.instructions) {
3500
3721
  if (skillsAgents.length === 0) {
@@ -3557,7 +3778,7 @@ export async function addAgentNativeSkill(parsed, options = {}) {
3557
3778
  if (shouldRegisterMcp) {
3558
3779
  commands.push(`npx @agent-native/core@latest app-skill ensure --manifest ${installTarget.loaded.file} --client ${parsed.client} --scope ${parsed.scope} --yes`);
3559
3780
  if (!parsed.dryRun) {
3560
- await ensureAppSkill(installTarget.loaded, {
3781
+ const ensureResult = await ensureAppSkill(installTarget.loaded, {
3561
3782
  clients,
3562
3783
  scope: parsed.scope,
3563
3784
  baseDir: options.baseDir,
@@ -3565,6 +3786,7 @@ export async function addAgentNativeSkill(parsed, options = {}) {
3565
3786
  confirm: true,
3566
3787
  log: options.log,
3567
3788
  });
3789
+ registeredMcpClients = ensureResult.written.map((written) => written.client);
3568
3790
  options.telemetry?.track("skills_cli mcp registered", {
3569
3791
  skills: installTarget.skillNames.join(","),
3570
3792
  });
@@ -3576,9 +3798,18 @@ export async function addAgentNativeSkill(parsed, options = {}) {
3576
3798
  const result = await connectAfterEnsure(installTarget, clients, parsed, options);
3577
3799
  connected = result.connected;
3578
3800
  connectCommand = result.connectCommand || undefined;
3801
+ if (connected)
3802
+ registeredMcpClients = clients;
3579
3803
  if (connectCommand)
3580
3804
  commands.push(connectCommand);
3581
3805
  }
3806
+ else {
3807
+ const pendingClients = clients.filter((client) => !registeredMcpClients.includes(client));
3808
+ if (pendingClients.length > 0) {
3809
+ connectCommand = connectCommandFor(installTarget.loaded.manifest.hosted.url, pendingClients, parsed.scope);
3810
+ commands.push(connectCommand);
3811
+ }
3812
+ }
3582
3813
  }
3583
3814
  }
3584
3815
  // `--with-github-action`: also drop the PR Visual Recap workflow into the
@@ -3638,7 +3869,7 @@ export async function addAgentNativeSkill(parsed, options = {}) {
3638
3869
  mcpUrl: knownTarget === "visual-plans" && planMode === "local-files"
3639
3870
  ? ""
3640
3871
  : installTarget.loaded.manifest.hosted.mcpUrl,
3641
- mcpClients: shouldRegisterMcp ? clients : [],
3872
+ mcpClients: registeredMcpClients,
3642
3873
  dryRun: parsed.dryRun,
3643
3874
  commands,
3644
3875
  written: instructionsWritten,
@@ -3655,15 +3886,29 @@ export async function addAgentNativeSkill(parsed, options = {}) {
3655
3886
  installTarget.cleanup?.();
3656
3887
  }
3657
3888
  }
3658
- function listSkills() {
3659
- return Object.values(BUILT_IN_APP_SKILLS).map((entry) => ({
3660
- id: entry.manifest.id,
3661
- aliases: BUILT_IN_APP_SKILL_DISPLAY_ALIASES[entry.manifest.id] ?? [],
3662
- name: entry.manifest.displayName,
3663
- description: entry.manifest.description,
3664
- mcpUrl: isLocalOnlyBuiltInSkill(entry) ? "" : entry.manifest.hosted.mcpUrl,
3665
- local: isLocalOnlyBuiltInSkill(entry),
3666
- }));
3889
+ function listSkills(options = {}) {
3890
+ return [
3891
+ ...Object.values(BUILT_IN_APP_SKILLS).map((entry) => ({
3892
+ id: entry.manifest.id,
3893
+ aliases: BUILT_IN_APP_SKILL_DISPLAY_ALIASES[entry.manifest.id] ?? [],
3894
+ name: entry.manifest.displayName,
3895
+ description: entry.manifest.description,
3896
+ mcpUrl: isLocalOnlyBuiltInSkill(entry)
3897
+ ? ""
3898
+ : entry.manifest.hosted.mcpUrl,
3899
+ local: isLocalOnlyBuiltInSkill(entry),
3900
+ source: "agent-native",
3901
+ })),
3902
+ ...publicSkillEntries(options).map((entry) => ({
3903
+ id: entry.name,
3904
+ aliases: [],
3905
+ name: entry.name,
3906
+ description: entry.description ?? "Public skill from the BuilderIO skills catalog.",
3907
+ mcpUrl: "",
3908
+ local: true,
3909
+ source: options.publicSkillSource ?? DEFAULT_PUBLIC_SKILLS_SOURCE,
3910
+ })),
3911
+ ];
3667
3912
  }
3668
3913
  function skillStateJson(state) {
3669
3914
  return {
@@ -3696,6 +3941,55 @@ function planModeSummary(mode) {
3696
3941
  return "Self-hosted/custom Plan app";
3697
3942
  return "Hosted Plans - shareable links and comments";
3698
3943
  }
3944
+ function targetInstallsMcp(target, parsed) {
3945
+ if (!parsed.mcp)
3946
+ return false;
3947
+ if (publicSkillSelectionNames(target))
3948
+ return false;
3949
+ const knownTarget = normalizeKnownSkillTarget(target);
3950
+ if (knownTarget === "visual-plans")
3951
+ return parsed.planMode !== "local-files";
3952
+ if (knownTarget) {
3953
+ return !isLocalOnlyBuiltInSkill(BUILT_IN_APP_SKILLS[knownTarget]);
3954
+ }
3955
+ return true;
3956
+ }
3957
+ function targetsInstallMcp(targets, parsed) {
3958
+ return targets.some((target) => targetInstallsMcp(target, parsed));
3959
+ }
3960
+ function instructionContentForSkill(skillName) {
3961
+ if (skillName === "quick-recap") {
3962
+ return "When writing final response status indicators, use the /quick-recap skill always.";
3963
+ }
3964
+ if (skillName === "efficient-fable") {
3965
+ return "When operating as Claude Fable, use the /efficient-fable skill always.";
3966
+ }
3967
+ if (skillName === "efficient-frontier") {
3968
+ return "When using a high-cost frontier model for codebase-heavy work, use the /efficient-frontier skill always.";
3969
+ }
3970
+ if (skillName === "stay-within-limits") {
3971
+ return "When long-running or parallel work needs usage-limit checks, use the /stay-within-limits skill always.";
3972
+ }
3973
+ if (skillName === "visual-plan") {
3974
+ return "When work needs an interactive visual plan before implementation, use the /visual-plan skill always.";
3975
+ }
3976
+ if (skillName === "visual-recap") {
3977
+ return "When a PR, branch, commit, or diff needs an interactive visual recap, use the /visual-recap skill always.";
3978
+ }
3979
+ return null;
3980
+ }
3981
+ function selectedPlainSkillNamesForInstructionPrompt(targets, parsed) {
3982
+ const names = new Set(parsed.plainSkillNames ?? []);
3983
+ for (const target of targets) {
3984
+ for (const name of publicSkillSelectionNames(target) ?? []) {
3985
+ names.add(name);
3986
+ }
3987
+ }
3988
+ return [...names];
3989
+ }
3990
+ function hasManagedInstructionBlock(skillNames) {
3991
+ return skillNames.some((name) => Boolean(instructionContentForSkill(name)));
3992
+ }
3699
3993
  function runSkillsStatusOrUpdate(parsed, options, update) {
3700
3994
  const before = collectSkillInstallStates(parsed, options);
3701
3995
  const changed = update ? updateSkillInstallStates(before, parsed.dryRun) : [];
@@ -3752,9 +4046,19 @@ function readCliVersion() {
3752
4046
  }
3753
4047
  export async function runSkills(argv, options = {}) {
3754
4048
  const parsed = parseSkillsArgs(argv);
4049
+ if (parsed.baseDir) {
4050
+ options = { ...options, baseDir: path.resolve(parsed.baseDir) };
4051
+ }
4052
+ const clackForLog = parsed.printJson
4053
+ ? undefined
4054
+ : await import("@clack/prompts");
3755
4055
  const log = parsed.printJson
3756
4056
  ? undefined
3757
- : (message) => process.stdout.write(`${message}\n`);
4057
+ : (message) => {
4058
+ if (!message.trim())
4059
+ return;
4060
+ clackForLog?.log.info(message);
4061
+ };
3758
4062
  if (parsed.command === "help") {
3759
4063
  process.stdout.write(`${HELP}\n`);
3760
4064
  return;
@@ -3764,6 +4068,7 @@ export async function runSkills(argv, options = {}) {
3764
4068
  // `npx @agent-native/skills@latest add …`; this env guard tells that child process
3765
4069
  // to run its OWN headless installer instead of bouncing back into core,
3766
4070
  // which would otherwise be an infinite skills → core → skills loop.
4071
+ const previousDirect = process.env.AGENT_NATIVE_SKILLS_DIRECT;
3767
4072
  process.env.AGENT_NATIVE_SKILLS_DIRECT = "1";
3768
4073
  // Best-effort install-funnel telemetry. Created once per run and flushed in a
3769
4074
  // finally so events send on success, error, and cancellation — the CLI is
@@ -3780,7 +4085,7 @@ export async function runSkills(argv, options = {}) {
3780
4085
  try {
3781
4086
  telemetry.track("skills_cli started");
3782
4087
  if (parsed.command === "list") {
3783
- const skills = listSkills();
4088
+ const skills = listSkills(optionsWithTelemetry);
3784
4089
  telemetry.track("skills_cli skills listed", {
3785
4090
  availableCount: skills.length,
3786
4091
  available: skills.map((skill) => skill.id).join(","),
@@ -3815,10 +4120,11 @@ export async function runSkills(argv, options = {}) {
3815
4120
  // Best-effort "took everything offered" signal: compare against the
3816
4121
  // interactive picker's option count (the plan sub-skills collapse into a
3817
4122
  // single bundle target, so this is approximate, like the standalone CLI).
3818
- selectedAll: targets.length === skillPromptOptions().length,
4123
+ selectedAll: targets.length === skillPromptOptions(options).length,
3819
4124
  preselected,
3820
4125
  });
3821
- const includesPlans = targetsIncludePlans(targets);
4126
+ const includesPlans = targetsIncludePlans(targets) ||
4127
+ planSkillNamesSelected(parsed.plainSkillNames);
3822
4128
  if (parsed.planMode && !includesPlans) {
3823
4129
  throw new Error("--mode only applies to visual-plan / visual-recap.");
3824
4130
  }
@@ -3857,7 +4163,8 @@ export async function runSkills(argv, options = {}) {
3857
4163
  mode: parsed.planMode,
3858
4164
  });
3859
4165
  }
3860
- const clients = await resolveSkillsClients(parsed, optionsWithTelemetry);
4166
+ const installsMcp = targetsInstallMcp(targets, parsed);
4167
+ const clients = await resolveSkillsClients(parsed, optionsWithTelemetry, installsMcp);
3861
4168
  if (!clients) {
3862
4169
  telemetry.track("skills_cli cancelled", { step: "clients" });
3863
4170
  return;
@@ -3878,6 +4185,20 @@ export async function runSkills(argv, options = {}) {
3878
4185
  parsed.scope = scope;
3879
4186
  }
3880
4187
  telemetry.track("skills_cli scope selected", { scope: parsed.scope });
4188
+ const instructionSkillNames = selectedPlainSkillNamesForInstructionPrompt(targets, parsed);
4189
+ if (parsed.updateInstructions === undefined &&
4190
+ hasManagedInstructionBlock(instructionSkillNames) &&
4191
+ shouldPrompt(parsed, options)) {
4192
+ const prompt = options.promptUpdateInstructions ?? promptForUpdateInstructions;
4193
+ const choice = await prompt();
4194
+ if (choice === null) {
4195
+ telemetry.track("skills_cli cancelled", {
4196
+ step: "managed-instructions",
4197
+ });
4198
+ return;
4199
+ }
4200
+ parsed.updateInstructions = choice === true;
4201
+ }
3881
4202
  // Decide the optional PR Visual Recap GitHub Action UP FRONT — before any
3882
4203
  // install or MCP registration — so every prompt is answered before we touch
3883
4204
  // disk. The choice is threaded into each install via `withGithubAction` +
@@ -3888,7 +4209,7 @@ export async function runSkills(argv, options = {}) {
3888
4209
  return false;
3889
4210
  const only = builtInOnlySkillNames(target);
3890
4211
  return !only || only.includes("visual-recap");
3891
- });
4212
+ }) || recapSkillNamesSelected(parsed.plainSkillNames);
3892
4213
  if (anyRecapTarget &&
3893
4214
  !parsed.withGithubAction &&
3894
4215
  !fs.existsSync(prVisualRecapWorkflowPath(recapBaseDir)) &&
@@ -4011,15 +4332,16 @@ export async function runSkills(argv, options = {}) {
4011
4332
  clack.note(summary.join("\n"), `Installed ${installedNames} skill${results.length === 1 ? "" : "s"}`);
4012
4333
  // GitHub Action follow-ups — kept as exact, copy-pasteable command lines.
4013
4334
  for (const line of [githubActionLine, githubActionSuggestionLine].filter(Boolean)) {
4014
- process.stdout.write(`${line}\n`);
4335
+ clack.log.info(line);
4015
4336
  }
4016
4337
  const slashCommands = completedSkills.map((name) => `/${name}`).join(" ");
4017
- const configuredEveryClient = CLIENTS.every((client) => clients.includes(client));
4338
+ const configuredEveryClient = SKILLS_CLIENTS.every((client) => clients.includes(client));
4018
4339
  const clientHint = configuredEveryClient
4019
4340
  ? ""
4020
4341
  : "\n Add another client later with --client <client> (e.g. --client claude-code).";
4342
+ const reloadTarget = mcpClients.length > 0 ? "skill + MCP server" : "skill";
4021
4343
  clack.outro(`✅ All set! Start using ${slashCommands || "your new skills"} in your agent client.` +
4022
- `\n You may need to reload the client for the skill + MCP server to appear.` +
4344
+ `\n You may need to reload the client for the ${reloadTarget} to appear.` +
4023
4345
  clientHint);
4024
4346
  }
4025
4347
  catch (error) {
@@ -4032,6 +4354,12 @@ export async function runSkills(argv, options = {}) {
4032
4354
  }
4033
4355
  finally {
4034
4356
  await telemetry.flush();
4357
+ if (previousDirect === undefined) {
4358
+ delete process.env.AGENT_NATIVE_SKILLS_DIRECT;
4359
+ }
4360
+ else {
4361
+ process.env.AGENT_NATIVE_SKILLS_DIRECT = previousDirect;
4362
+ }
4035
4363
  }
4036
4364
  }
4037
4365
  //# sourceMappingURL=skills.js.map