@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
package/src/cli/skills.ts CHANGED
@@ -32,7 +32,7 @@ import {
32
32
  CONTEXT_XRAY_SKILL_MD,
33
33
  installLocalContextXray,
34
34
  } from "./context-xray-local.js";
35
- import { CLIENTS, type ClientId } from "./mcp-config-writers.js";
35
+ import { CLIENTS, configPathFor, type ClientId } from "./mcp-config-writers.js";
36
36
  import {
37
37
  installScreenMemoryForClient,
38
38
  resolveScreenMemoryStoreDir,
@@ -800,6 +800,7 @@ export interface RunSkillsOptions {
800
800
  * browser/device OAuth round-trip.
801
801
  */
802
802
  runConnect?: (args: string[]) => Promise<void>;
803
+ installScreenMemory?: typeof installScreenMemoryForClient;
803
804
  /**
804
805
  * Best-effort install-funnel telemetry. Created once per `runSkills` run and
805
806
  * threaded through resolution/install/connect so each `track` is fire-and-
@@ -850,6 +851,50 @@ function isKnownSkill(value: string | undefined): boolean {
850
851
  return Boolean(normalizeKnownSkillTarget(value));
851
852
  }
852
853
 
854
+ function explicitlyTargetsRewind(parsed: ParsedSkillsArgs): boolean {
855
+ return (
856
+ normalizeKnownSkillTarget(parsed.target ?? "assets") === "rewind" ||
857
+ Boolean(
858
+ parsed.plainSkillNames?.some(
859
+ (skillName) => normalizeKnownSkillTarget(skillName) === "rewind",
860
+ ),
861
+ )
862
+ );
863
+ }
864
+
865
+ const REWIND_MISSING_STORE_ERROR =
866
+ "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.";
867
+
868
+ function preflightRewindStore(parsed: ParsedSkillsArgs): string | undefined {
869
+ if (parsed.command !== "add" || !explicitlyTargetsRewind(parsed))
870
+ return undefined;
871
+ if (!parsed.mcp) {
872
+ throw new Error(
873
+ "Rewind requires the local Clips Screen Memory MCP and cannot be installed with --no-mcp.",
874
+ );
875
+ }
876
+ if (parsed.mcpUrl) {
877
+ throw new Error(
878
+ "Rewind uses the local Clips Screen Memory MCP and does not accept --mcp-url.",
879
+ );
880
+ }
881
+ if (parsed.dryRun) return undefined;
882
+ const screenMemoryDir = resolveScreenMemoryStoreDir();
883
+ if (!screenMemoryDir) throw new Error(REWIND_MISSING_STORE_ERROR);
884
+ return screenMemoryDir;
885
+ }
886
+
887
+ function preflightResolvedRewindTargets(
888
+ parsed: ParsedSkillsArgs,
889
+ targets: string[],
890
+ ): void {
891
+ preflightRewindStore({
892
+ ...parsed,
893
+ target: undefined,
894
+ plainSkillNames: [...(parsed.plainSkillNames ?? []), ...targets],
895
+ });
896
+ }
897
+
853
898
  function isLocalOnlyBuiltInSkill(
854
899
  entry: (typeof BUILT_IN_APP_SKILLS)[BuiltInAppSkillId] | null | undefined,
855
900
  ): boolean {
@@ -1382,7 +1427,7 @@ $ARGUMENTS
1382
1427
  * there is no need to shell out to the separate @agent-native/skills installer
1383
1428
  * (which would have to be published to npm first). Returns the written folders.
1384
1429
  */
1385
- function installBuiltInInstructions(input: {
1430
+ type BuiltInInstructionInstallInput = {
1386
1431
  appSkillId: BuiltInAppSkillId;
1387
1432
  onlySkillNames?: string[];
1388
1433
  skillsAgents: string[];
@@ -1391,7 +1436,11 @@ function installBuiltInInstructions(input: {
1391
1436
  dryRun?: boolean;
1392
1437
  planMode?: PlanInstallMode;
1393
1438
  mcpUrl?: string;
1394
- }): string[] {
1439
+ };
1440
+
1441
+ function builtInInstructionPaths(
1442
+ input: BuiltInInstructionInstallInput,
1443
+ ): string[] {
1395
1444
  const bundles = Object.values(
1396
1445
  skillFilesForBuiltIn(input.appSkillId, {
1397
1446
  planMode: input.planMode,
@@ -1411,15 +1460,10 @@ function installBuiltInInstructions(input: {
1411
1460
  );
1412
1461
  for (const bundle of bundles) {
1413
1462
  const dir = path.join(root, bundle.skillName);
1414
- if (!input.dryRun) writeSkillFolder(dir, bundle);
1415
1463
  written.push(dir);
1416
1464
  const command = slashCommandForBuiltInSkill(bundle.skillName);
1417
1465
  if (command) {
1418
1466
  const commandPath = path.join(commandsRoot, `${bundle.skillName}.md`);
1419
- if (!input.dryRun) {
1420
- fs.mkdirSync(path.dirname(commandPath), { recursive: true });
1421
- fs.writeFileSync(commandPath, command, "utf-8");
1422
- }
1423
1467
  written.push(commandPath);
1424
1468
  }
1425
1469
  }
@@ -1427,6 +1471,87 @@ function installBuiltInInstructions(input: {
1427
1471
  return written;
1428
1472
  }
1429
1473
 
1474
+ function installBuiltInInstructions(
1475
+ input: BuiltInInstructionInstallInput,
1476
+ ): string[] {
1477
+ const bundles = Object.values(
1478
+ skillFilesForBuiltIn(input.appSkillId, {
1479
+ planMode: input.planMode,
1480
+ mcpUrl: input.mcpUrl,
1481
+ }),
1482
+ ).filter(
1483
+ (bundle) =>
1484
+ !input.onlySkillNames || input.onlySkillNames.includes(bundle.skillName),
1485
+ );
1486
+ const written = builtInInstructionPaths(input);
1487
+ if (input.dryRun) return written;
1488
+
1489
+ for (const agent of input.skillsAgents) {
1490
+ const root = builtInSkillsRootForAgent(agent, input.scope, input.baseDir);
1491
+ const commandsRoot = builtInCommandsRootForAgent(
1492
+ agent,
1493
+ input.scope,
1494
+ input.baseDir,
1495
+ );
1496
+ for (const bundle of bundles) {
1497
+ writeSkillFolder(path.join(root, bundle.skillName), bundle);
1498
+ const command = slashCommandForBuiltInSkill(bundle.skillName);
1499
+ if (command) {
1500
+ const commandPath = path.join(commandsRoot, `${bundle.skillName}.md`);
1501
+ fs.mkdirSync(path.dirname(commandPath), { recursive: true });
1502
+ fs.writeFileSync(commandPath, command, "utf-8");
1503
+ }
1504
+ }
1505
+ }
1506
+ return written;
1507
+ }
1508
+
1509
+ interface InstallPathSnapshot {
1510
+ target: string;
1511
+ backup: string;
1512
+ existed: boolean;
1513
+ }
1514
+
1515
+ function snapshotInstallPaths(
1516
+ targets: string[],
1517
+ backupRoot: string,
1518
+ ): InstallPathSnapshot[] {
1519
+ return [...new Set(targets)].map((target, index) => {
1520
+ const backup = path.join(backupRoot, `snapshot-${index}`);
1521
+ const existed = fs.existsSync(target);
1522
+ if (existed) fs.cpSync(target, backup, { recursive: true });
1523
+ return { target, backup, existed };
1524
+ });
1525
+ }
1526
+
1527
+ function removeEmptyParents(start: string, boundary: string): void {
1528
+ let current = path.resolve(start);
1529
+ const stop = path.resolve(boundary);
1530
+ while (current !== stop && current.startsWith(`${stop}${path.sep}`)) {
1531
+ if (!fs.existsSync(current) || fs.readdirSync(current).length > 0) return;
1532
+ fs.rmdirSync(current);
1533
+ current = path.dirname(current);
1534
+ }
1535
+ }
1536
+
1537
+ function restoreInstallPaths(
1538
+ snapshots: InstallPathSnapshot[],
1539
+ boundary: string,
1540
+ ): void {
1541
+ for (const snapshot of snapshots.toReversed()) {
1542
+ fs.rmSync(snapshot.target, { recursive: true, force: true });
1543
+ if (snapshot.existed) {
1544
+ fs.mkdirSync(path.dirname(snapshot.target), { recursive: true });
1545
+ fs.cpSync(snapshot.backup, snapshot.target, { recursive: true });
1546
+ }
1547
+ }
1548
+ for (const snapshot of snapshots) {
1549
+ if (!snapshot.existed) {
1550
+ removeEmptyParents(path.dirname(snapshot.target), boundary);
1551
+ }
1552
+ }
1553
+ }
1554
+
1430
1555
  function listSkillFolderFiles(dir: string): Record<string, string> {
1431
1556
  const out: Record<string, string> = {};
1432
1557
  const walk = (current: string, prefix = "") => {
@@ -3376,6 +3501,11 @@ export async function addAgentNativeSkill(
3376
3501
  const knownBuiltIn = knownTarget ? BUILT_IN_APP_SKILLS[knownTarget] : null;
3377
3502
  const installsScreenMemoryMcp = isScreenMemoryMcpBuiltInSkill(knownBuiltIn);
3378
3503
  const baseDir = options.baseDir ?? process.cwd();
3504
+ if (installsScreenMemoryMcp && !parsed.mcp) {
3505
+ throw new Error(
3506
+ "Rewind requires the local Clips Screen Memory MCP and cannot be installed with --no-mcp.",
3507
+ );
3508
+ }
3379
3509
  if (installsScreenMemoryMcp && parsed.mcpUrl) {
3380
3510
  throw new Error(
3381
3511
  "Rewind uses the local Clips Screen Memory MCP and does not accept --mcp-url.",
@@ -3487,7 +3617,7 @@ export async function addAgentNativeSkill(
3487
3617
  }
3488
3618
  installTarget = preserveMcpUrlAppPathOverride(installTarget, parsed.mcpUrl);
3489
3619
  const skillsAgents = installsScreenMemoryMcp
3490
- ? clients
3620
+ ? clients.filter((client) => client !== "cowork")
3491
3621
  : skillsAgentsForClients(clients);
3492
3622
  if (parsed.dryRun) {
3493
3623
  try {
@@ -3537,6 +3667,7 @@ export async function addAgentNativeSkill(
3537
3667
  }
3538
3668
  }
3539
3669
  const commands: string[] = [];
3670
+ const screenMemoryDir = preflightRewindStore(parsed);
3540
3671
  const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), "an-skills-add-"));
3541
3672
  let instructionSource: string | undefined;
3542
3673
  let instructionsWritten: string[] | undefined;
@@ -3544,6 +3675,36 @@ export async function addAgentNativeSkill(
3544
3675
  let connectCommand: string | undefined;
3545
3676
  let registeredMcpClients: ClientId[] = shouldRegisterMcp ? mcpClients : [];
3546
3677
  let localManifestPath: string | undefined;
3678
+ const builtInInstructionInput: BuiltInInstructionInstallInput | undefined =
3679
+ knownTarget
3680
+ ? {
3681
+ appSkillId: knownTarget,
3682
+ onlySkillNames,
3683
+ skillsAgents,
3684
+ scope: parsed.scope as "project" | "user",
3685
+ baseDir,
3686
+ dryRun: parsed.dryRun,
3687
+ planMode,
3688
+ mcpUrl: installTarget.loaded.manifest.hosted.mcpUrl,
3689
+ }
3690
+ : undefined;
3691
+ const rewindSnapshots = installsScreenMemoryMcp
3692
+ ? snapshotInstallPaths(
3693
+ [
3694
+ ...(parsed.instructions && builtInInstructionInput
3695
+ ? builtInInstructionPaths(builtInInstructionInput)
3696
+ : []),
3697
+ ...(shouldRegisterMcp
3698
+ ? mcpClients.map((client) =>
3699
+ configPathFor(client, baseDir, parsed.scope),
3700
+ )
3701
+ : []),
3702
+ ],
3703
+ tmpRoot,
3704
+ )
3705
+ : undefined;
3706
+ const rollbackBoundary =
3707
+ parsed.scope === "user" ? os.homedir() : path.resolve(baseDir);
3547
3708
 
3548
3709
  try {
3549
3710
  if (parsed.instructions) {
@@ -3553,21 +3714,14 @@ export async function addAgentNativeSkill(
3553
3714
  "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.",
3554
3715
  );
3555
3716
  }
3556
- } else if (knownTarget) {
3717
+ } else if (knownTarget && builtInInstructionInput) {
3557
3718
  // Built-in skills ship their instructions inside this package, so copy
3558
3719
  // the skill folders straight into each client's skills directory. This
3559
3720
  // avoids shelling out to the separate @agent-native/skills installer
3560
3721
  // (which would need to be published to npm to run via npx).
3561
- instructionsWritten = installBuiltInInstructions({
3562
- appSkillId: knownTarget,
3563
- onlySkillNames,
3564
- skillsAgents,
3565
- scope: parsed.scope as "project" | "user",
3566
- baseDir,
3567
- dryRun: parsed.dryRun,
3568
- planMode,
3569
- mcpUrl: installTarget.loaded.manifest.hosted.mcpUrl,
3570
- });
3722
+ instructionsWritten = installBuiltInInstructions(
3723
+ builtInInstructionInput,
3724
+ );
3571
3725
  instructionSource = instructionsWritten[0];
3572
3726
  commands.push(...instructionsWritten.map((dir) => `write ${dir}`));
3573
3727
  } else {
@@ -3611,29 +3765,24 @@ export async function addAgentNativeSkill(
3611
3765
  commands.push(`write ${localManifestPath}`);
3612
3766
  }
3613
3767
 
3614
- // Skill instructions are now on disk (built-in folders copied or external
3615
- // pack materialized) — record the install before MCP registration/connect.
3616
- options.telemetry?.track("skills_cli install completed", {
3617
- skills: installTarget.skillNames.join(","),
3618
- clients: clients.join(","),
3619
- scope: parsed.scope,
3620
- dryRun: Boolean(parsed.dryRun),
3621
- });
3768
+ // Rewind reports completion only after both local writes succeed.
3769
+ if (!installsScreenMemoryMcp) {
3770
+ options.telemetry?.track("skills_cli install completed", {
3771
+ skills: installTarget.skillNames.join(","),
3772
+ clients: clients.join(","),
3773
+ scope: parsed.scope,
3774
+ dryRun: Boolean(parsed.dryRun),
3775
+ });
3776
+ }
3622
3777
 
3623
3778
  if (shouldRegisterMcp && installsScreenMemoryMcp) {
3624
- const screenMemoryDir = resolveScreenMemoryStoreDir();
3625
- if (!screenMemoryDir) {
3626
- throw new Error(
3627
- "No local Clips Screen Memory store was found. Turn Rewind on in Clips, then run the setup again.",
3628
- );
3629
- }
3630
3779
  registeredMcpClients = mcpClients.map((client) => {
3631
3780
  commands.push(
3632
3781
  `npx @agent-native/core@latest mcp install-screen-memory --client ${client} --scope ${parsed.scope}`,
3633
3782
  );
3634
- installScreenMemoryForClient(
3783
+ (options.installScreenMemory ?? installScreenMemoryForClient)(
3635
3784
  client,
3636
- screenMemoryDir,
3785
+ screenMemoryDir!,
3637
3786
  baseDir,
3638
3787
  parsed.scope,
3639
3788
  );
@@ -3642,6 +3791,12 @@ export async function addAgentNativeSkill(
3642
3791
  options.telemetry?.track("skills_cli mcp registered", {
3643
3792
  skills: installTarget.skillNames.join(","),
3644
3793
  });
3794
+ options.telemetry?.track("skills_cli install completed", {
3795
+ skills: installTarget.skillNames.join(","),
3796
+ clients: clients.join(","),
3797
+ scope: parsed.scope,
3798
+ dryRun: false,
3799
+ });
3645
3800
  } else if (shouldRegisterMcp) {
3646
3801
  commands.push(
3647
3802
  `npx @agent-native/core@latest app-skill ensure --manifest ${installTarget.loaded.file} --client ${parsed.client} --scope ${parsed.scope} --yes`,
@@ -3769,6 +3924,18 @@ export async function addAgentNativeSkill(
3769
3924
  githubActionExisted,
3770
3925
  githubActionSuggestedCommand,
3771
3926
  };
3927
+ } catch (error) {
3928
+ if (rewindSnapshots) {
3929
+ try {
3930
+ restoreInstallPaths(rewindSnapshots, rollbackBoundary);
3931
+ } catch (rollbackError) {
3932
+ throw new AggregateError(
3933
+ [error, rollbackError],
3934
+ "Rewind setup failed and its partial installation could not be fully rolled back.",
3935
+ );
3936
+ }
3937
+ }
3938
+ throw error;
3772
3939
  } finally {
3773
3940
  fs.rmSync(tmpRoot, { recursive: true, force: true });
3774
3941
  installTarget.cleanup?.();
@@ -4046,6 +4213,41 @@ function readCliVersion(): string {
4046
4213
  }
4047
4214
  }
4048
4215
 
4216
+ function deferCliTelemetry(target: CliTelemetry): {
4217
+ telemetry: CliTelemetry;
4218
+ commit: () => void;
4219
+ } {
4220
+ type TrackCall = Parameters<CliTelemetry["track"]>;
4221
+ type ExceptionCall = Parameters<CliTelemetry["captureException"]>;
4222
+ const trackCalls: TrackCall[] = [];
4223
+ const exceptionCalls: ExceptionCall[] = [];
4224
+ let committed = false;
4225
+
4226
+ return {
4227
+ telemetry: {
4228
+ track(...args) {
4229
+ if (committed) target.track(...args);
4230
+ else trackCalls.push(args);
4231
+ },
4232
+ captureException(...args) {
4233
+ if (committed) target.captureException(...args);
4234
+ else exceptionCalls.push(args);
4235
+ },
4236
+ async flush() {
4237
+ if (committed) await target.flush();
4238
+ },
4239
+ },
4240
+ commit() {
4241
+ if (committed) return;
4242
+ committed = true;
4243
+ for (const args of trackCalls) target.track(...args);
4244
+ for (const args of exceptionCalls) target.captureException(...args);
4245
+ trackCalls.length = 0;
4246
+ exceptionCalls.length = 0;
4247
+ },
4248
+ };
4249
+ }
4250
+
4049
4251
  export async function runSkills(
4050
4252
  argv: string[],
4051
4253
  options: RunSkillsOptions = {},
@@ -4054,6 +4256,7 @@ export async function runSkills(
4054
4256
  if (parsed.baseDir) {
4055
4257
  options = { ...options, baseDir: path.resolve(parsed.baseDir) };
4056
4258
  }
4259
+ preflightRewindStore(parsed);
4057
4260
  const clackForLog = parsed.printJson
4058
4261
  ? undefined
4059
4262
  : await import("@clack/prompts");
@@ -4089,7 +4292,7 @@ export async function runSkills(
4089
4292
  // finally so events send on success, error, and cancellation — the CLI is
4090
4293
  // short-lived, so flushing before exit is essential or the events never send.
4091
4294
  const startedAt = Date.now();
4092
- const telemetry =
4295
+ const telemetryTarget =
4093
4296
  options.telemetry ??
4094
4297
  createCliTelemetry({
4095
4298
  cli: "core",
@@ -4097,6 +4300,13 @@ export async function runSkills(
4097
4300
  command: parsed.command,
4098
4301
  interactive: shouldPrompt(parsed, options),
4099
4302
  });
4303
+ const deferredTelemetry = deferCliTelemetry(telemetryTarget);
4304
+ const telemetry = deferredTelemetry.telemetry;
4305
+ const deferUntilSkillSelection =
4306
+ parsed.command === "add" &&
4307
+ !parsed.target &&
4308
+ !(parsed.plainSkillNames?.length ?? 0);
4309
+ if (!deferUntilSkillSelection) deferredTelemetry.commit();
4100
4310
  const optionsWithTelemetry: RunSkillsOptions = {
4101
4311
  ...options,
4102
4312
  telemetry,
@@ -4137,9 +4347,12 @@ export async function runSkills(
4137
4347
 
4138
4348
  const targets = await resolveSkillTargets(parsed, optionsWithTelemetry);
4139
4349
  if (!targets) {
4350
+ deferredTelemetry.commit();
4140
4351
  telemetry.track("skills_cli cancelled", { step: "skills" });
4141
4352
  return;
4142
4353
  }
4354
+ preflightResolvedRewindTargets(parsed, targets);
4355
+ deferredTelemetry.commit();
4143
4356
  const preselected = Boolean(parsed.target);
4144
4357
  telemetry.track("skills_cli skills selected", {
4145
4358
  selected: targets.join(","),
@@ -0,0 +1,57 @@
1
+ const REWIND_SKILL_TARGETS = new Set([
2
+ "rewind",
3
+ "screen-memory",
4
+ "clips-rewind",
5
+ "agent-native-rewind",
6
+ ]);
7
+
8
+ function isRewindSkillTarget(value: string | undefined): boolean {
9
+ return REWIND_SKILL_TARGETS.has(value?.trim().toLowerCase() ?? "");
10
+ }
11
+
12
+ const SKILLS_VALUE_FLAGS = new Set([
13
+ "--client",
14
+ "--agent",
15
+ "-a",
16
+ "--scope",
17
+ "--cwd",
18
+ "--mcp-url",
19
+ "--mode",
20
+ ]);
21
+
22
+ function explicitSkillTargets(args: string[]): string[] {
23
+ const targets: string[] = [];
24
+ const start = args[0] === "add" ? 1 : 0;
25
+ for (let index = start; index < args.length; index++) {
26
+ const arg = args[index];
27
+ if (arg === "--skill" || arg === "-s") {
28
+ if (args[index + 1]) targets.push(args[++index]);
29
+ continue;
30
+ }
31
+ if (arg.startsWith("--skill=")) {
32
+ targets.push(arg.slice("--skill=".length));
33
+ continue;
34
+ }
35
+ if (SKILLS_VALUE_FLAGS.has(arg)) {
36
+ index++;
37
+ continue;
38
+ }
39
+ if (arg.startsWith("-")) continue;
40
+ targets.push(arg);
41
+ }
42
+ return targets;
43
+ }
44
+
45
+ export function shouldTrackCliRun(command: string | undefined, args: string[]) {
46
+ if (command !== "skills") return true;
47
+ if (
48
+ ["list", "status", "update", "help", "--help", "-h"].includes(args[0] ?? "")
49
+ )
50
+ return true;
51
+
52
+ const targets = explicitSkillTargets(args);
53
+ if (targets.length === 0) return false;
54
+
55
+ const explicitlyTargetsRewind = targets.some(isRewindSkillTarget);
56
+ return !explicitlyTargetsRewind;
57
+ }
@@ -198,7 +198,7 @@ export function createCliTelemetry(options: CliTelemetryOptions): CliTelemetry {
198
198
  const publicKey = resolvePublicKey();
199
199
  const disabled = telemetryDisabled() || !publicKey;
200
200
  const endpoint = resolveEndpoint();
201
- const installId = disabled ? "" : resolveInstallId();
201
+ let installId: string | undefined = disabled ? "" : undefined;
202
202
  const runId = crypto.randomUUID();
203
203
  const inFlight = new Set<Promise<void>>();
204
204
 
@@ -211,18 +211,18 @@ export function createCliTelemetry(options: CliTelemetryOptions): CliTelemetry {
211
211
  ci: process.env.CI === "true",
212
212
  interactive: options.interactive,
213
213
  runId,
214
- installId,
215
214
  };
216
215
 
217
216
  function track(event: string, properties?: Record<string, unknown>): void {
218
217
  if (disabled) return;
218
+ installId ??= resolveInstallId();
219
219
  const body = JSON.stringify({
220
220
  publicKey,
221
221
  event,
222
222
  anonymousId: installId,
223
223
  sessionId: runId,
224
224
  timestamp: new Date().toISOString(),
225
- properties: { ...base, ...properties },
225
+ properties: { ...base, installId, ...properties },
226
226
  });
227
227
  const promise = fetch(endpoint, {
228
228
  method: "POST",
@@ -1146,6 +1146,24 @@ export function screenMemoryMcpToolDefinitions() {
1146
1146
  ];
1147
1147
  }
1148
1148
 
1149
+ export async function queryScreenMemoryContextForStore(
1150
+ args: Parameters<typeof queryScreenMemoryContext>[0],
1151
+ storeDir: string,
1152
+ env: NodeJS.ProcessEnv,
1153
+ ) {
1154
+ return queryScreenMemoryContext(args, {
1155
+ homeDir: env.HOME,
1156
+ env: {
1157
+ ...env,
1158
+ AGENT_NATIVE_SCREEN_MEMORY_DIR: storeDir,
1159
+ AGENT_NATIVE_SCREEN_MEMORY_CONFIG: path.join(
1160
+ path.dirname(storeDir),
1161
+ "feature-config.json",
1162
+ ),
1163
+ },
1164
+ });
1165
+ }
1166
+
1149
1167
  export async function runScreenMemoryMCPStdio(
1150
1168
  opts: RunScreenMemoryMCPStdioOptions = {},
1151
1169
  ): Promise<void> {
@@ -1187,14 +1205,15 @@ export async function runScreenMemoryMCPStdio(
1187
1205
  });
1188
1206
  }
1189
1207
  if (name === "screen_memory_recent_context") {
1190
- const result = await queryScreenMemoryContext(
1208
+ const result = await queryScreenMemoryContextForStore(
1191
1209
  {
1192
1210
  query: typeof args.query === "string" ? args.query : undefined,
1193
1211
  sinceMinutes:
1194
1212
  typeof args.minutes === "number" ? args.minutes : undefined,
1195
1213
  limit: typeof args.limit === "number" ? args.limit : undefined,
1196
1214
  },
1197
- { env: { ...env, AGENT_NATIVE_SCREEN_MEMORY_DIR: storeDir } },
1215
+ storeDir,
1216
+ env,
1198
1217
  );
1199
1218
  const sanitizedEvidence = result.evidence.map((item) => ({
1200
1219
  ...item,