@agent-native/core 0.48.3 → 0.48.4

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.
@@ -26,7 +26,8 @@ Examples:
26
26
  agent-native skills add assets
27
27
  agent-native skills add design-exploration
28
28
  agent-native skills add visual-plan
29
- agent-native skills add visual-plan --with-github-action
29
+ agent-native skills add visual-recap
30
+ agent-native skills add visual-recap --with-github-action
30
31
  agent-native skills status visual-plan
31
32
  agent-native skills update visual-plan
32
33
  agent-native skills add visual-plan --no-connect
@@ -54,7 +55,7 @@ deployment) instead of the built-in hosted default — a bare origin gets the
54
55
  standard /_agent-native/mcp path appended. Use app-skill pack for marketplace
55
56
  bundles and custom adapter output.
56
57
 
57
- When installing visual-plan interactively, the CLI offers to add the optional PR
58
+ When installing visual-recap interactively, the CLI offers to add the optional PR
58
59
  Visual Recap GitHub Action. Pass --with-github-action to write it directly, then
59
60
  run "agent-native recap setup" / "agent-native recap doctor" to configure and
60
61
  verify GitHub Actions.
@@ -1947,6 +1948,21 @@ function builtInExtraFiles(entry) {
1947
1948
  function builtInSkillNames(entry) {
1948
1949
  return [entry.skillName, ...Object.keys(builtInExtraSkills(entry))];
1949
1950
  }
1951
+ /**
1952
+ * When a target names a single skill that lives inside a multi-skill bundle
1953
+ * (the plan bundle ships both `visual-plan` and `visual-recap`), restrict the
1954
+ * install to just that skill. The bundle aliases (`visual-plans`, `plannotate`,
1955
+ * …) return undefined so they install every skill in the bundle.
1956
+ */
1957
+ function builtInOnlySkillNames(target) {
1958
+ const normalized = target.trim().toLowerCase();
1959
+ if (normalized === "visual-plan")
1960
+ return ["visual-plan"];
1961
+ if (normalized === "visual-recap" || normalized === "visual-recaps") {
1962
+ return ["visual-recap"];
1963
+ }
1964
+ return undefined;
1965
+ }
1950
1966
  function stableSkillHash(files) {
1951
1967
  const hash = createHash("sha256");
1952
1968
  for (const rel of Object.keys(files).sort()) {
@@ -2013,6 +2029,45 @@ function writeSkillFolder(dir, bundle, installedAt = new Date().toISOString()) {
2013
2029
  };
2014
2030
  fs.writeFileSync(path.join(dir, AGENT_NATIVE_SKILL_METADATA_FILE), `${JSON.stringify(metadata, null, 2)}\n`, "utf-8");
2015
2031
  }
2032
+ /**
2033
+ * The skills directory a built-in skill's instructions are copied into for a
2034
+ * given agent + scope. Mirrors the layout the skills installer uses so
2035
+ * `skills status` / `skills update` find the folders again.
2036
+ */
2037
+ function builtInSkillsRootForAgent(agent, scope, baseDir) {
2038
+ const home = homeDir() ?? baseDir;
2039
+ if (scope === "project") {
2040
+ return agent === "codex"
2041
+ ? path.join(baseDir, ".agents", "skills")
2042
+ : path.join(baseDir, ".claude", "skills");
2043
+ }
2044
+ if (agent === "codex") {
2045
+ return process.env.CODEX_HOME
2046
+ ? path.join(process.env.CODEX_HOME, "skills")
2047
+ : path.join(home, ".codex", "skills");
2048
+ }
2049
+ return path.join(home, ".claude", "skills");
2050
+ }
2051
+ /**
2052
+ * Write a built-in skill's instruction folders straight into each client's
2053
+ * skills directory. Built-in skills ship their SKILL.md inside this package, so
2054
+ * there is no need to shell out to the separate @agent-native/skills installer
2055
+ * (which would have to be published to npm first). Returns the written folders.
2056
+ */
2057
+ function installBuiltInInstructions(input) {
2058
+ const bundles = Object.values(skillFilesForBuiltIn(input.appSkillId)).filter((bundle) => !input.onlySkillNames || input.onlySkillNames.includes(bundle.skillName));
2059
+ const written = [];
2060
+ for (const agent of input.skillsAgents) {
2061
+ const root = builtInSkillsRootForAgent(agent, input.scope, input.baseDir);
2062
+ for (const bundle of bundles) {
2063
+ const dir = path.join(root, bundle.skillName);
2064
+ if (!input.dryRun)
2065
+ writeSkillFolder(dir, bundle);
2066
+ written.push(dir);
2067
+ }
2068
+ }
2069
+ return written;
2070
+ }
2016
2071
  function listSkillFolderFiles(dir) {
2017
2072
  const out = {};
2018
2073
  const walk = (current, prefix = "") => {
@@ -2200,12 +2255,25 @@ function clientPromptOptions() {
2200
2255
  hint: CLIENT_HINTS[client],
2201
2256
  }));
2202
2257
  }
2258
+ // For now the interactive installer offers only the two plan skills, each as
2259
+ // an independently selectable entry (uncheck one to install just the other).
2260
+ // The other built-in skills stay installable via `agent-native skills add
2261
+ // <name>` but are hidden from the default checklist. The values are the real
2262
+ // slash-command names so users see exactly what they are installing.
2263
+ const PLAN_SKILL_PROMPT_OPTIONS = [
2264
+ {
2265
+ value: "visual-plan",
2266
+ label: "visual-plan",
2267
+ hint: "Reviewable coding-agent plan: diagrams, annotated code, file trees, open questions.",
2268
+ },
2269
+ {
2270
+ value: "visual-recap",
2271
+ label: "visual-recap",
2272
+ hint: "Turn a PR, commit, branch, or git diff into a high-altitude visual recap.",
2273
+ },
2274
+ ];
2203
2275
  function skillPromptOptions() {
2204
- return Object.values(BUILT_IN_APP_SKILLS).map((entry) => ({
2205
- value: entry.skillName,
2206
- label: entry.manifest.displayName,
2207
- hint: entry.manifest.description,
2208
- }));
2276
+ return PLAN_SKILL_PROMPT_OPTIONS;
2209
2277
  }
2210
2278
  function prVisualRecapWorkflowPath(baseDir) {
2211
2279
  return path.join(baseDir, ".github", "workflows", "pr-visual-recap.yml");
@@ -2214,7 +2282,7 @@ function prVisualRecapWorkflowDisplayPath() {
2214
2282
  return path.join(".github", "workflows", "pr-visual-recap.yml");
2215
2283
  }
2216
2284
  function prVisualRecapInstallCommand() {
2217
- return "npx @agent-native/core@latest skills add visual-plan --with-github-action";
2285
+ return "npx @agent-native/core@latest skills add visual-recap --with-github-action";
2218
2286
  }
2219
2287
  function prVisualRecapSetupCommand() {
2220
2288
  return "npx @agent-native/core@latest recap setup";
@@ -2258,6 +2326,30 @@ async function promptForClients(context) {
2258
2326
  }
2259
2327
  return normalizeClientIds(result);
2260
2328
  }
2329
+ async function promptForScope(context) {
2330
+ const clack = await import("@clack/prompts");
2331
+ const result = await clack.select({
2332
+ message: "Where do you want to install these skills?",
2333
+ options: [
2334
+ {
2335
+ value: "project",
2336
+ label: "Project",
2337
+ hint: "This repo only (.agents / .claude in the current directory) — committed with your project",
2338
+ },
2339
+ {
2340
+ value: "user",
2341
+ label: "User",
2342
+ hint: "Your home directory (~/.codex, ~/.claude) — available across all projects",
2343
+ },
2344
+ ],
2345
+ initialValue: context.initialScope,
2346
+ });
2347
+ if (clack.isCancel(result)) {
2348
+ clack.cancel("Cancelled.");
2349
+ return null;
2350
+ }
2351
+ return result === "project" ? "project" : "user";
2352
+ }
2261
2353
  async function promptForSkills(context) {
2262
2354
  const clack = await import("@clack/prompts");
2263
2355
  const result = await clack.multiselect({
@@ -2301,11 +2393,21 @@ async function resolveSkillTargets(parsed, options) {
2301
2393
  }
2302
2394
  const prompt = options.promptSkills ?? promptForSkills;
2303
2395
  const selected = await prompt({
2304
- initialTargets: ["assets"],
2396
+ initialTargets: ["visual-plan", "visual-recap"],
2305
2397
  options: skillPromptOptions(),
2306
2398
  });
2307
2399
  if (!selected || selected.length === 0)
2308
2400
  return null;
2401
+ // Both plan skills share one MCP connector, so when both are selected install
2402
+ // them through the bundle target — that registers/authenticates the connector
2403
+ // once instead of twice.
2404
+ const planSubskills = ["visual-plan", "visual-recap"];
2405
+ if (planSubskills.every((skill) => selected.includes(skill))) {
2406
+ return [
2407
+ "visual-plans",
2408
+ ...selected.filter((s) => !planSubskills.includes(s)),
2409
+ ];
2410
+ }
2309
2411
  return selected;
2310
2412
  }
2311
2413
  export function parseSkillsArgs(argv) {
@@ -2406,11 +2508,11 @@ export function parseSkillsArgs(argv) {
2406
2508
  }
2407
2509
  return out;
2408
2510
  }
2409
- function loadSkillTarget(target) {
2511
+ function loadSkillTarget(target, onlySkillNames) {
2410
2512
  const knownTarget = normalizeKnownSkillTarget(target);
2411
2513
  if (knownTarget) {
2412
2514
  const builtIn = BUILT_IN_APP_SKILLS[knownTarget];
2413
- const skillNames = builtInSkillNames(builtIn);
2515
+ const skillNames = builtInSkillNames(builtIn).filter((name) => !onlySkillNames || onlySkillNames.includes(name));
2414
2516
  return {
2415
2517
  id: builtIn.manifest.id,
2416
2518
  displayName: builtIn.manifest.displayName,
@@ -2423,6 +2525,9 @@ function loadSkillTarget(target) {
2423
2525
  materializeInstructions(outDir) {
2424
2526
  const bundles = skillFilesForBuiltIn(knownTarget);
2425
2527
  for (const bundle of Object.values(bundles)) {
2528
+ if (onlySkillNames && !onlySkillNames.includes(bundle.skillName)) {
2529
+ continue;
2530
+ }
2426
2531
  writeSkillFolder(path.join(outDir, "skills", bundle.skillName), bundle);
2427
2532
  }
2428
2533
  return outDir;
@@ -2760,6 +2865,14 @@ async function connectAfterEnsure(installTarget, clients, parsed, options) {
2760
2865
  export async function addAgentNativeSkill(parsed, options = {}) {
2761
2866
  const target = parsed.target ?? "assets";
2762
2867
  const knownTarget = normalizeKnownSkillTarget(target);
2868
+ // For multi-skill bundles (the plan bundle), a single-skill target installs
2869
+ // only that skill. `installsRecap` controls the PR Visual Recap github-action
2870
+ // offer, which is only relevant when the recap skill is part of the install.
2871
+ const onlySkillNames = knownTarget
2872
+ ? builtInOnlySkillNames(target)
2873
+ : undefined;
2874
+ const installsRecap = knownTarget === "visual-plans" &&
2875
+ (!onlySkillNames || onlySkillNames.includes("visual-recap"));
2763
2876
  if (!knownTarget && isPlainSkillRepoTarget(target)) {
2764
2877
  return addPlainSkillRepo({ ...parsed, target }, options);
2765
2878
  }
@@ -2813,7 +2926,7 @@ export async function addAgentNativeSkill(parsed, options = {}) {
2813
2926
  commands: localInstall.commands,
2814
2927
  };
2815
2928
  }
2816
- let installTarget = loadSkillTarget(target);
2929
+ let installTarget = loadSkillTarget(target, onlySkillNames);
2817
2930
  if (parsed.mcpUrl) {
2818
2931
  installTarget = withMcpUrlOverride(installTarget, parsed.mcpUrl);
2819
2932
  }
@@ -2822,10 +2935,10 @@ export async function addAgentNativeSkill(parsed, options = {}) {
2822
2935
  const skillsAgents = skillsAgentsForClients(clients);
2823
2936
  if (parsed.dryRun) {
2824
2937
  try {
2825
- const githubActionPath = parsed.withGithubAction && knownTarget === "visual-plans"
2938
+ const githubActionPath = parsed.withGithubAction && installsRecap
2826
2939
  ? prVisualRecapWorkflowDisplayPath()
2827
2940
  : undefined;
2828
- const githubActionSuggestedCommand = knownTarget === "visual-plans" && !parsed.withGithubAction
2941
+ const githubActionSuggestedCommand = installsRecap && !parsed.withGithubAction
2829
2942
  ? prVisualRecapInstallCommand()
2830
2943
  : undefined;
2831
2944
  return {
@@ -2848,6 +2961,7 @@ export async function addAgentNativeSkill(parsed, options = {}) {
2848
2961
  const commands = [];
2849
2962
  const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), "an-skills-add-"));
2850
2963
  let instructionSource;
2964
+ let instructionsWritten;
2851
2965
  let connected = false;
2852
2966
  let connectCommand;
2853
2967
  try {
@@ -2857,7 +2971,26 @@ export async function addAgentNativeSkill(parsed, options = {}) {
2857
2971
  throw new Error("Skill instructions can only be installed for Codex or Claude Code clients. Use an MCP-capable client or omit --instructions-only.");
2858
2972
  }
2859
2973
  }
2974
+ else if (knownTarget) {
2975
+ // Built-in skills ship their instructions inside this package, so copy
2976
+ // the skill folders straight into each client's skills directory. This
2977
+ // avoids shelling out to the separate @agent-native/skills installer
2978
+ // (which would need to be published to npm to run via npx).
2979
+ instructionsWritten = installBuiltInInstructions({
2980
+ appSkillId: knownTarget,
2981
+ onlySkillNames,
2982
+ skillsAgents,
2983
+ scope: parsed.scope,
2984
+ baseDir: options.baseDir ?? process.cwd(),
2985
+ dryRun: parsed.dryRun,
2986
+ });
2987
+ instructionSource = instructionsWritten[0];
2988
+ commands.push(...instructionsWritten.map((dir) => `write ${dir}`));
2989
+ }
2860
2990
  else {
2991
+ // External app-skill manifests / plain skill repos still go through the
2992
+ // standalone installer, which knows how to pack adapters and fetch
2993
+ // remote skill collections.
2861
2994
  instructionSource = installTarget.materializeInstructions(tmpRoot);
2862
2995
  const args = [
2863
2996
  "--yes",
@@ -2911,7 +3044,7 @@ export async function addAgentNativeSkill(parsed, options = {}) {
2911
3044
  let githubActionPath;
2912
3045
  let githubActionExisted;
2913
3046
  let githubActionSuggestedCommand;
2914
- if (knownTarget === "visual-plans" &&
3047
+ if (installsRecap &&
2915
3048
  !withGithubAction &&
2916
3049
  !fs.existsSync(prVisualRecapWorkflowPath(baseDir))) {
2917
3050
  if (shouldPrompt(parsed, options)) {
@@ -2927,8 +3060,8 @@ export async function addAgentNativeSkill(parsed, options = {}) {
2927
3060
  }
2928
3061
  }
2929
3062
  if (withGithubAction) {
2930
- if (knownTarget !== "visual-plans") {
2931
- options.log?.("--with-github-action only applies to the visual-plan skill; skipping the workflow.");
3063
+ if (!installsRecap) {
3064
+ options.log?.("--with-github-action only applies to the visual-recap skill; skipping the workflow.");
2932
3065
  }
2933
3066
  else {
2934
3067
  const writeResult = writePrVisualRecapWorkflow(baseDir, {
@@ -2953,6 +3086,7 @@ export async function addAgentNativeSkill(parsed, options = {}) {
2953
3086
  mcpClients: clients,
2954
3087
  dryRun: parsed.dryRun,
2955
3088
  commands,
3089
+ written: instructionsWritten,
2956
3090
  connected,
2957
3091
  connectCommand,
2958
3092
  githubActionPath,
@@ -3072,6 +3206,15 @@ export async function runSkills(argv, options = {}) {
3072
3206
  const clients = await resolveSkillsClients(parsed, options);
3073
3207
  if (!clients)
3074
3208
  return;
3209
+ // Ask where to install (project vs user) unless an explicit --scope was
3210
+ // passed or we are running non-interactively.
3211
+ if (!parsed.scopeExplicit && shouldPrompt(parsed, options)) {
3212
+ const promptScope = options.promptScope ?? promptForScope;
3213
+ const scope = await promptScope({ initialScope: "project" });
3214
+ if (!scope)
3215
+ return;
3216
+ parsed.scope = scope;
3217
+ }
3075
3218
  const results = [];
3076
3219
  for (const target of targets) {
3077
3220
  results.push(await addAgentNativeSkill({