@agent-native/core 0.122.3 → 0.122.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.
Files changed (46) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/core/CHANGELOG.md +6 -0
  3. package/corpus/core/package.json +1 -1
  4. package/corpus/core/src/cli/index.ts +2 -1
  5. package/corpus/core/src/cli/mcp.ts +13 -3
  6. package/corpus/core/src/cli/skills-content/rewind-skill.ts +36 -4
  7. package/corpus/core/src/cli/skills.ts +250 -37
  8. package/corpus/core/src/cli/telemetry-routing.ts +57 -0
  9. package/corpus/core/src/cli/telemetry.ts +3 -3
  10. package/corpus/core/src/mcp/screen-memory-stdio.ts +21 -2
  11. package/dist/cli/index.js +3 -1
  12. package/dist/cli/index.js.map +1 -1
  13. package/dist/cli/mcp.d.ts.map +1 -1
  14. package/dist/cli/mcp.js +12 -3
  15. package/dist/cli/mcp.js.map +1 -1
  16. package/dist/cli/skills-content/rewind-skill.d.ts +1 -1
  17. package/dist/cli/skills-content/rewind-skill.d.ts.map +1 -1
  18. package/dist/cli/skills-content/rewind-skill.js +36 -4
  19. package/dist/cli/skills-content/rewind-skill.js.map +1 -1
  20. package/dist/cli/skills.d.ts +2 -0
  21. package/dist/cli/skills.d.ts.map +1 -1
  22. package/dist/cli/skills.js +191 -40
  23. package/dist/cli/skills.js.map +1 -1
  24. package/dist/cli/telemetry-routing.d.ts +2 -0
  25. package/dist/cli/telemetry-routing.d.ts.map +1 -0
  26. package/dist/cli/telemetry-routing.js +54 -0
  27. package/dist/cli/telemetry-routing.js.map +1 -0
  28. package/dist/cli/telemetry.js +3 -3
  29. package/dist/cli/telemetry.js.map +1 -1
  30. package/dist/collab/routes.d.ts +1 -1
  31. package/dist/collab/struct-routes.d.ts +1 -1
  32. package/dist/file-upload/actions/upload-image.d.ts +1 -1
  33. package/dist/mcp/screen-memory-stdio.d.ts +2 -0
  34. package/dist/mcp/screen-memory-stdio.d.ts.map +1 -1
  35. package/dist/mcp/screen-memory-stdio.js +12 -2
  36. package/dist/mcp/screen-memory-stdio.js.map +1 -1
  37. package/dist/observability/routes.d.ts +1 -1
  38. package/dist/progress/routes.d.ts +1 -1
  39. package/package.json +1 -1
  40. package/src/cli/index.ts +2 -1
  41. package/src/cli/mcp.ts +13 -3
  42. package/src/cli/skills-content/rewind-skill.ts +36 -4
  43. package/src/cli/skills.ts +250 -37
  44. package/src/cli/telemetry-routing.ts +57 -0
  45. package/src/cli/telemetry.ts +3 -3
  46. package/src/mcp/screen-memory-stdio.ts +21 -2
@@ -13,7 +13,7 @@ import { MCP_LEGACY_ROUTE_PREFIX, MCP_PUBLIC_ROUTE_PREFIX, } from "../mcp/route-
13
13
  import { buildAppSkillPack, ensureAppSkill, loadAppSkillManifest, normalizeAppSkillManifest, } from "./app-skill.js";
14
14
  import { resolveClients, runConnect, writeConnectClientPreferences, } from "./connect.js";
15
15
  import { CONTEXT_XRAY_SKILL_MD, installLocalContextXray, } from "./context-xray-local.js";
16
- import { CLIENTS } from "./mcp-config-writers.js";
16
+ import { CLIENTS, configPathFor } from "./mcp-config-writers.js";
17
17
  import { installScreenMemoryForClient, resolveScreenMemoryStoreDir, } from "./mcp.js";
18
18
  import { PR_VISUAL_RECAP_SETUP, writePrVisualRecapWorkflow } from "./recap.js";
19
19
  import { setupAgentSymlinks } from "./setup-agents.js";
@@ -466,6 +466,34 @@ function normalizeKnownSkillTarget(value) {
466
466
  function isKnownSkill(value) {
467
467
  return Boolean(normalizeKnownSkillTarget(value));
468
468
  }
469
+ function explicitlyTargetsRewind(parsed) {
470
+ return (normalizeKnownSkillTarget(parsed.target ?? "assets") === "rewind" ||
471
+ Boolean(parsed.plainSkillNames?.some((skillName) => normalizeKnownSkillTarget(skillName) === "rewind")));
472
+ }
473
+ const REWIND_MISSING_STORE_ERROR = "No local Clips Screen Memory store was found. Clips Desktop is required for Rewind. Download and launch the signed app from https://clips.agent-native.com/download, turn Rewind on, then run the setup again. Clips Desktop was not installed or enabled automatically.";
474
+ function preflightRewindStore(parsed) {
475
+ if (parsed.command !== "add" || !explicitlyTargetsRewind(parsed))
476
+ return undefined;
477
+ if (!parsed.mcp) {
478
+ throw new Error("Rewind requires the local Clips Screen Memory MCP and cannot be installed with --no-mcp.");
479
+ }
480
+ if (parsed.mcpUrl) {
481
+ throw new Error("Rewind uses the local Clips Screen Memory MCP and does not accept --mcp-url.");
482
+ }
483
+ if (parsed.dryRun)
484
+ return undefined;
485
+ const screenMemoryDir = resolveScreenMemoryStoreDir();
486
+ if (!screenMemoryDir)
487
+ throw new Error(REWIND_MISSING_STORE_ERROR);
488
+ return screenMemoryDir;
489
+ }
490
+ function preflightResolvedRewindTargets(parsed, targets) {
491
+ preflightRewindStore({
492
+ ...parsed,
493
+ target: undefined,
494
+ plainSkillNames: [...(parsed.plainSkillNames ?? []), ...targets],
495
+ });
496
+ }
469
497
  function isLocalOnlyBuiltInSkill(entry) {
470
498
  return Boolean(entry && "localOnly" in entry && entry.localOnly);
471
499
  }
@@ -895,13 +923,7 @@ $ARGUMENTS
895
923
  }
896
924
  return null;
897
925
  }
898
- /**
899
- * Write a built-in skill's instruction folders straight into each client's
900
- * skills directory. Built-in skills ship their SKILL.md inside this package, so
901
- * there is no need to shell out to the separate @agent-native/skills installer
902
- * (which would have to be published to npm first). Returns the written folders.
903
- */
904
- function installBuiltInInstructions(input) {
926
+ function builtInInstructionPaths(input) {
905
927
  const bundles = Object.values(skillFilesForBuiltIn(input.appSkillId, {
906
928
  planMode: input.planMode,
907
929
  mcpUrl: input.mcpUrl,
@@ -912,22 +934,72 @@ function installBuiltInInstructions(input) {
912
934
  const commandsRoot = builtInCommandsRootForAgent(agent, input.scope, input.baseDir);
913
935
  for (const bundle of bundles) {
914
936
  const dir = path.join(root, bundle.skillName);
915
- if (!input.dryRun)
916
- writeSkillFolder(dir, bundle);
917
937
  written.push(dir);
918
938
  const command = slashCommandForBuiltInSkill(bundle.skillName);
919
939
  if (command) {
920
940
  const commandPath = path.join(commandsRoot, `${bundle.skillName}.md`);
921
- if (!input.dryRun) {
922
- fs.mkdirSync(path.dirname(commandPath), { recursive: true });
923
- fs.writeFileSync(commandPath, command, "utf-8");
924
- }
925
941
  written.push(commandPath);
926
942
  }
927
943
  }
928
944
  }
929
945
  return written;
930
946
  }
947
+ function installBuiltInInstructions(input) {
948
+ const bundles = Object.values(skillFilesForBuiltIn(input.appSkillId, {
949
+ planMode: input.planMode,
950
+ mcpUrl: input.mcpUrl,
951
+ })).filter((bundle) => !input.onlySkillNames || input.onlySkillNames.includes(bundle.skillName));
952
+ const written = builtInInstructionPaths(input);
953
+ if (input.dryRun)
954
+ return written;
955
+ for (const agent of input.skillsAgents) {
956
+ const root = builtInSkillsRootForAgent(agent, input.scope, input.baseDir);
957
+ const commandsRoot = builtInCommandsRootForAgent(agent, input.scope, input.baseDir);
958
+ for (const bundle of bundles) {
959
+ writeSkillFolder(path.join(root, bundle.skillName), bundle);
960
+ const command = slashCommandForBuiltInSkill(bundle.skillName);
961
+ if (command) {
962
+ const commandPath = path.join(commandsRoot, `${bundle.skillName}.md`);
963
+ fs.mkdirSync(path.dirname(commandPath), { recursive: true });
964
+ fs.writeFileSync(commandPath, command, "utf-8");
965
+ }
966
+ }
967
+ }
968
+ return written;
969
+ }
970
+ function snapshotInstallPaths(targets, backupRoot) {
971
+ return [...new Set(targets)].map((target, index) => {
972
+ const backup = path.join(backupRoot, `snapshot-${index}`);
973
+ const existed = fs.existsSync(target);
974
+ if (existed)
975
+ fs.cpSync(target, backup, { recursive: true });
976
+ return { target, backup, existed };
977
+ });
978
+ }
979
+ function removeEmptyParents(start, boundary) {
980
+ let current = path.resolve(start);
981
+ const stop = path.resolve(boundary);
982
+ while (current !== stop && current.startsWith(`${stop}${path.sep}`)) {
983
+ if (!fs.existsSync(current) || fs.readdirSync(current).length > 0)
984
+ return;
985
+ fs.rmdirSync(current);
986
+ current = path.dirname(current);
987
+ }
988
+ }
989
+ function restoreInstallPaths(snapshots, boundary) {
990
+ for (const snapshot of snapshots.toReversed()) {
991
+ fs.rmSync(snapshot.target, { recursive: true, force: true });
992
+ if (snapshot.existed) {
993
+ fs.mkdirSync(path.dirname(snapshot.target), { recursive: true });
994
+ fs.cpSync(snapshot.backup, snapshot.target, { recursive: true });
995
+ }
996
+ }
997
+ for (const snapshot of snapshots) {
998
+ if (!snapshot.existed) {
999
+ removeEmptyParents(path.dirname(snapshot.target), boundary);
1000
+ }
1001
+ }
1002
+ }
931
1003
  function listSkillFolderFiles(dir) {
932
1004
  const out = {};
933
1005
  const walk = (current, prefix = "") => {
@@ -2558,6 +2630,9 @@ export async function addAgentNativeSkill(parsed, options = {}) {
2558
2630
  const knownBuiltIn = knownTarget ? BUILT_IN_APP_SKILLS[knownTarget] : null;
2559
2631
  const installsScreenMemoryMcp = isScreenMemoryMcpBuiltInSkill(knownBuiltIn);
2560
2632
  const baseDir = options.baseDir ?? process.cwd();
2633
+ if (installsScreenMemoryMcp && !parsed.mcp) {
2634
+ throw new Error("Rewind requires the local Clips Screen Memory MCP and cannot be installed with --no-mcp.");
2635
+ }
2561
2636
  if (installsScreenMemoryMcp && parsed.mcpUrl) {
2562
2637
  throw new Error("Rewind uses the local Clips Screen Memory MCP and does not accept --mcp-url.");
2563
2638
  }
@@ -2652,7 +2727,7 @@ export async function addAgentNativeSkill(parsed, options = {}) {
2652
2727
  }
2653
2728
  installTarget = preserveMcpUrlAppPathOverride(installTarget, parsed.mcpUrl);
2654
2729
  const skillsAgents = installsScreenMemoryMcp
2655
- ? clients
2730
+ ? clients.filter((client) => client !== "cowork")
2656
2731
  : skillsAgentsForClients(clients);
2657
2732
  if (parsed.dryRun) {
2658
2733
  try {
@@ -2698,6 +2773,7 @@ export async function addAgentNativeSkill(parsed, options = {}) {
2698
2773
  }
2699
2774
  }
2700
2775
  const commands = [];
2776
+ const screenMemoryDir = preflightRewindStore(parsed);
2701
2777
  const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), "an-skills-add-"));
2702
2778
  let instructionSource;
2703
2779
  let instructionsWritten;
@@ -2705,6 +2781,29 @@ export async function addAgentNativeSkill(parsed, options = {}) {
2705
2781
  let connectCommand;
2706
2782
  let registeredMcpClients = shouldRegisterMcp ? mcpClients : [];
2707
2783
  let localManifestPath;
2784
+ const builtInInstructionInput = knownTarget
2785
+ ? {
2786
+ appSkillId: knownTarget,
2787
+ onlySkillNames,
2788
+ skillsAgents,
2789
+ scope: parsed.scope,
2790
+ baseDir,
2791
+ dryRun: parsed.dryRun,
2792
+ planMode,
2793
+ mcpUrl: installTarget.loaded.manifest.hosted.mcpUrl,
2794
+ }
2795
+ : undefined;
2796
+ const rewindSnapshots = installsScreenMemoryMcp
2797
+ ? snapshotInstallPaths([
2798
+ ...(parsed.instructions && builtInInstructionInput
2799
+ ? builtInInstructionPaths(builtInInstructionInput)
2800
+ : []),
2801
+ ...(shouldRegisterMcp
2802
+ ? mcpClients.map((client) => configPathFor(client, baseDir, parsed.scope))
2803
+ : []),
2804
+ ], tmpRoot)
2805
+ : undefined;
2806
+ const rollbackBoundary = parsed.scope === "user" ? os.homedir() : path.resolve(baseDir);
2708
2807
  try {
2709
2808
  if (parsed.instructions) {
2710
2809
  if (skillsAgents.length === 0) {
@@ -2712,21 +2811,12 @@ export async function addAgentNativeSkill(parsed, options = {}) {
2712
2811
  throw new Error("Skill instructions use shared .agents for Codex, Pi, Cursor, OpenCode, Copilot, and similar agents, or Claude Code's native files. Use an MCP-capable client or omit --instructions-only.");
2713
2812
  }
2714
2813
  }
2715
- else if (knownTarget) {
2814
+ else if (knownTarget && builtInInstructionInput) {
2716
2815
  // Built-in skills ship their instructions inside this package, so copy
2717
2816
  // the skill folders straight into each client's skills directory. This
2718
2817
  // avoids shelling out to the separate @agent-native/skills installer
2719
2818
  // (which would need to be published to npm to run via npx).
2720
- instructionsWritten = installBuiltInInstructions({
2721
- appSkillId: knownTarget,
2722
- onlySkillNames,
2723
- skillsAgents,
2724
- scope: parsed.scope,
2725
- baseDir,
2726
- dryRun: parsed.dryRun,
2727
- planMode,
2728
- mcpUrl: installTarget.loaded.manifest.hosted.mcpUrl,
2729
- });
2819
+ instructionsWritten = installBuiltInInstructions(builtInInstructionInput);
2730
2820
  instructionSource = instructionsWritten[0];
2731
2821
  commands.push(...instructionsWritten.map((dir) => `write ${dir}`));
2732
2822
  }
@@ -2767,27 +2857,30 @@ export async function addAgentNativeSkill(parsed, options = {}) {
2767
2857
  localManifestPath = writeContentLocalFilesManifest(baseDir);
2768
2858
  commands.push(`write ${localManifestPath}`);
2769
2859
  }
2770
- // Skill instructions are now on disk (built-in folders copied or external
2771
- // pack materialized) — record the install before MCP registration/connect.
2772
- options.telemetry?.track("skills_cli install completed", {
2773
- skills: installTarget.skillNames.join(","),
2774
- clients: clients.join(","),
2775
- scope: parsed.scope,
2776
- dryRun: Boolean(parsed.dryRun),
2777
- });
2860
+ // Rewind reports completion only after both local writes succeed.
2861
+ if (!installsScreenMemoryMcp) {
2862
+ options.telemetry?.track("skills_cli install completed", {
2863
+ skills: installTarget.skillNames.join(","),
2864
+ clients: clients.join(","),
2865
+ scope: parsed.scope,
2866
+ dryRun: Boolean(parsed.dryRun),
2867
+ });
2868
+ }
2778
2869
  if (shouldRegisterMcp && installsScreenMemoryMcp) {
2779
- const screenMemoryDir = resolveScreenMemoryStoreDir();
2780
- if (!screenMemoryDir) {
2781
- throw new Error("No local Clips Screen Memory store was found. Turn Rewind on in Clips, then run the setup again.");
2782
- }
2783
2870
  registeredMcpClients = mcpClients.map((client) => {
2784
2871
  commands.push(`npx @agent-native/core@latest mcp install-screen-memory --client ${client} --scope ${parsed.scope}`);
2785
- installScreenMemoryForClient(client, screenMemoryDir, baseDir, parsed.scope);
2872
+ (options.installScreenMemory ?? installScreenMemoryForClient)(client, screenMemoryDir, baseDir, parsed.scope);
2786
2873
  return client;
2787
2874
  });
2788
2875
  options.telemetry?.track("skills_cli mcp registered", {
2789
2876
  skills: installTarget.skillNames.join(","),
2790
2877
  });
2878
+ options.telemetry?.track("skills_cli install completed", {
2879
+ skills: installTarget.skillNames.join(","),
2880
+ clients: clients.join(","),
2881
+ scope: parsed.scope,
2882
+ dryRun: false,
2883
+ });
2791
2884
  }
2792
2885
  else if (shouldRegisterMcp) {
2793
2886
  commands.push(`npx @agent-native/core@latest app-skill ensure --manifest ${installTarget.loaded.file} --client ${parsed.client} --scope ${parsed.scope} --yes`);
@@ -2898,6 +2991,17 @@ export async function addAgentNativeSkill(parsed, options = {}) {
2898
2991
  githubActionSuggestedCommand,
2899
2992
  };
2900
2993
  }
2994
+ catch (error) {
2995
+ if (rewindSnapshots) {
2996
+ try {
2997
+ restoreInstallPaths(rewindSnapshots, rollbackBoundary);
2998
+ }
2999
+ catch (rollbackError) {
3000
+ throw new AggregateError([error, rollbackError], "Rewind setup failed and its partial installation could not be fully rolled back.");
3001
+ }
3002
+ }
3003
+ throw error;
3004
+ }
2901
3005
  finally {
2902
3006
  fs.rmSync(tmpRoot, { recursive: true, force: true });
2903
3007
  installTarget.cleanup?.();
@@ -3121,11 +3225,48 @@ function readCliVersion() {
3121
3225
  return "unknown";
3122
3226
  }
3123
3227
  }
3228
+ function deferCliTelemetry(target) {
3229
+ const trackCalls = [];
3230
+ const exceptionCalls = [];
3231
+ let committed = false;
3232
+ return {
3233
+ telemetry: {
3234
+ track(...args) {
3235
+ if (committed)
3236
+ target.track(...args);
3237
+ else
3238
+ trackCalls.push(args);
3239
+ },
3240
+ captureException(...args) {
3241
+ if (committed)
3242
+ target.captureException(...args);
3243
+ else
3244
+ exceptionCalls.push(args);
3245
+ },
3246
+ async flush() {
3247
+ if (committed)
3248
+ await target.flush();
3249
+ },
3250
+ },
3251
+ commit() {
3252
+ if (committed)
3253
+ return;
3254
+ committed = true;
3255
+ for (const args of trackCalls)
3256
+ target.track(...args);
3257
+ for (const args of exceptionCalls)
3258
+ target.captureException(...args);
3259
+ trackCalls.length = 0;
3260
+ exceptionCalls.length = 0;
3261
+ },
3262
+ };
3263
+ }
3124
3264
  export async function runSkills(argv, options = {}) {
3125
3265
  const parsed = parseSkillsArgs(argv);
3126
3266
  if (parsed.baseDir) {
3127
3267
  options = { ...options, baseDir: path.resolve(parsed.baseDir) };
3128
3268
  }
3269
+ preflightRewindStore(parsed);
3129
3270
  const clackForLog = parsed.printJson
3130
3271
  ? undefined
3131
3272
  : await import("@clack/prompts");
@@ -3157,13 +3298,20 @@ export async function runSkills(argv, options = {}) {
3157
3298
  // finally so events send on success, error, and cancellation — the CLI is
3158
3299
  // short-lived, so flushing before exit is essential or the events never send.
3159
3300
  const startedAt = Date.now();
3160
- const telemetry = options.telemetry ??
3301
+ const telemetryTarget = options.telemetry ??
3161
3302
  createCliTelemetry({
3162
3303
  cli: "core",
3163
3304
  cliVersion: readCliVersion(),
3164
3305
  command: parsed.command,
3165
3306
  interactive: shouldPrompt(parsed, options),
3166
3307
  });
3308
+ const deferredTelemetry = deferCliTelemetry(telemetryTarget);
3309
+ const telemetry = deferredTelemetry.telemetry;
3310
+ const deferUntilSkillSelection = parsed.command === "add" &&
3311
+ !parsed.target &&
3312
+ !(parsed.plainSkillNames?.length ?? 0);
3313
+ if (!deferUntilSkillSelection)
3314
+ deferredTelemetry.commit();
3167
3315
  const optionsWithTelemetry = {
3168
3316
  ...options,
3169
3317
  telemetry,
@@ -3198,9 +3346,12 @@ export async function runSkills(argv, options = {}) {
3198
3346
  }
3199
3347
  const targets = await resolveSkillTargets(parsed, optionsWithTelemetry);
3200
3348
  if (!targets) {
3349
+ deferredTelemetry.commit();
3201
3350
  telemetry.track("skills_cli cancelled", { step: "skills" });
3202
3351
  return;
3203
3352
  }
3353
+ preflightResolvedRewindTargets(parsed, targets);
3354
+ deferredTelemetry.commit();
3204
3355
  const preselected = Boolean(parsed.target);
3205
3356
  telemetry.track("skills_cli skills selected", {
3206
3357
  selected: targets.join(","),