@notis_ai/cli 0.2.0-beta.165.1 → 0.2.0-beta.167.1

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.
@@ -3669,10 +3669,34 @@ async function extractZipToDirectory(zipPath, destinationDir) {
3669
3669
  await fs2.rm(destinationDir, { recursive: true, force: true });
3670
3670
  await fs2.mkdir(path2.dirname(destinationDir), { recursive: true });
3671
3671
  await fs2.cp(sourceDir, destinationDir, { recursive: true });
3672
+ return await listRelativeFiles(destinationDir);
3672
3673
  } finally {
3673
3674
  await fs2.rm(extractRoot, { recursive: true, force: true });
3674
3675
  }
3675
3676
  }
3677
+ async function listRelativeFiles(dirPath) {
3678
+ try {
3679
+ const files = await listFilesRecursive2(dirPath);
3680
+ return files.map((filePath) => toPosixRelativePath(dirPath, filePath)).sort();
3681
+ } catch (error) {
3682
+ if (error?.code === "ENOENT") return [];
3683
+ throw error;
3684
+ }
3685
+ }
3686
+ function toPosixRelativePath(rootDir, filePath) {
3687
+ return path2.relative(rootDir, filePath).split(path2.sep).join("/");
3688
+ }
3689
+ async function preserveUnmanagedFiles(existingDir, stagingDir, previouslyApplied) {
3690
+ const managed = previouslyApplied ? new Set(previouslyApplied) : null;
3691
+ for (const existingFile of await listFilesRecursive2(existingDir)) {
3692
+ const relativePath = toPosixRelativePath(existingDir, existingFile);
3693
+ if (managed?.has(relativePath)) continue;
3694
+ const stagedPath = path2.join(stagingDir, ...relativePath.split("/"));
3695
+ if (await pathExists(stagedPath)) continue;
3696
+ await fs2.mkdir(path2.dirname(stagedPath), { recursive: true });
3697
+ await fs2.copyFile(existingFile, stagedPath);
3698
+ }
3699
+ }
3676
3700
  async function pathExists(targetPath) {
3677
3701
  try {
3678
3702
  await fs2.access(targetPath);
@@ -3681,7 +3705,7 @@ async function pathExists(targetPath) {
3681
3705
  return false;
3682
3706
  }
3683
3707
  }
3684
- async function replaceSkillDirectoryAtomically(skillDir, populateDir) {
3708
+ async function replaceSkillDirectoryAtomically(skillDir, populateDir, preserve) {
3685
3709
  const parentDir = path2.dirname(skillDir);
3686
3710
  const skillName = path2.basename(skillDir);
3687
3711
  await fs2.mkdir(parentDir, { recursive: true });
@@ -3697,6 +3721,9 @@ async function replaceSkillDirectoryAtomically(skillDir, populateDir) {
3697
3721
  let cleanupError = null;
3698
3722
  try {
3699
3723
  await populateDir(stagingDir);
3724
+ if (preserve && await pathExists(skillDir)) {
3725
+ await preserveUnmanagedFiles(skillDir, stagingDir, preserve.previouslyApplied);
3726
+ }
3700
3727
  if (await pathExists(skillDir)) {
3701
3728
  await fs2.rename(skillDir, backupDir);
3702
3729
  movedExisting = true;
@@ -3744,11 +3771,12 @@ async function createSkillBundleBase64(skill) {
3744
3771
  await fs2.rm(zipPath, { force: true });
3745
3772
  }
3746
3773
  }
3747
- async function writeCloudSkillToDisk(skill, bundleBytes, paths = DEFAULT_SYNC_PATHS) {
3774
+ async function writeCloudSkillToDisk(skill, bundleBytes, paths = DEFAULT_SYNC_PATHS, options = {}) {
3748
3775
  const skillDir = path2.join(
3749
3776
  paths.skillsDir,
3750
3777
  safeName(skill.name, paths.skillsDir)
3751
3778
  );
3779
+ const preserve = { previouslyApplied: options.previouslyApplied };
3752
3780
  if (bundleBytes?.length) {
3753
3781
  const bundlePath2 = path2.join(
3754
3782
  os.tmpdir(),
@@ -3756,8 +3784,11 @@ async function writeCloudSkillToDisk(skill, bundleBytes, paths = DEFAULT_SYNC_PA
3756
3784
  );
3757
3785
  try {
3758
3786
  await fs2.writeFile(bundlePath2, bundleBytes);
3759
- await extractZipToDirectory(bundlePath2, skillDir);
3760
- return true;
3787
+ let appliedFiles = [];
3788
+ await replaceSkillDirectoryAtomically(skillDir, async (stagingDir) => {
3789
+ appliedFiles = await extractZipToDirectory(bundlePath2, stagingDir);
3790
+ }, preserve);
3791
+ return { written: true, appliedFiles };
3761
3792
  } finally {
3762
3793
  await fs2.rm(bundlePath2, { force: true });
3763
3794
  }
@@ -3771,6 +3802,7 @@ async function writeCloudSkillToDisk(skill, bundleBytes, paths = DEFAULT_SYNC_PA
3771
3802
  `Synced bundle for "${skill.name}" is missing SKILL.md or SKILLS.md`
3772
3803
  );
3773
3804
  }
3805
+ const appliedFiles = [];
3774
3806
  await replaceSkillDirectoryAtomically(skillDir, async (stagingDir) => {
3775
3807
  for (const bundleFile of skill.bundle_files || []) {
3776
3808
  const filePath = resolveSkillBundleFilePath(
@@ -3782,9 +3814,10 @@ async function writeCloudSkillToDisk(skill, bundleBytes, paths = DEFAULT_SYNC_PA
3782
3814
  filePath,
3783
3815
  Buffer.from(bundleFile.content_b64, "base64")
3784
3816
  );
3817
+ appliedFiles.push(toPosixRelativePath(stagingDir, filePath));
3785
3818
  }
3786
- });
3787
- return true;
3819
+ }, preserve);
3820
+ return { written: true, appliedFiles: appliedFiles.sort() };
3788
3821
  }
3789
3822
  await replaceSkillDirectoryAtomically(skillDir, async (stagingDir) => {
3790
3823
  await fs2.writeFile(
@@ -3792,8 +3825,8 @@ async function writeCloudSkillToDisk(skill, bundleBytes, paths = DEFAULT_SYNC_PA
3792
3825
  skill.skill_md ?? "",
3793
3826
  "utf8"
3794
3827
  );
3795
- });
3796
- return true;
3828
+ }, preserve);
3829
+ return { written: true, appliedFiles: ["SKILL.md"] };
3797
3830
  }
3798
3831
  async function requestJson(url, jwt, options = {}) {
3799
3832
  const response = await fetch(url, {
@@ -4174,7 +4207,7 @@ async function writeCloudSkillWithBundleFallback(skill, dependencies) {
4174
4207
  const bundleBytes = await dependencies.downloadSkillBundle(skill.skill_source_url);
4175
4208
  const wroteBundleToDisk = await dependencies.writeCloudSkillToDisk(skill, bundleBytes);
4176
4209
  if (wroteBundleToDisk) {
4177
- return true;
4210
+ return wroteBundleToDisk;
4178
4211
  }
4179
4212
  dependencies.onWarning?.(
4180
4213
  `Bundle sync for "${skill.name}" produced no local changes, falling back to SKILL.md payload.`,
@@ -4267,12 +4300,20 @@ function collectAppliedCloudRevisions(pullResponse, previousState, writtenSkillN
4267
4300
  }
4268
4301
  return applied;
4269
4302
  }
4270
- function buildSyncState(pullResponse, localSkills, lastSyncedAt, verifiedAgentLinks = {}, failedContentNames = /* @__PURE__ */ new Set(), appliedRevisions = {}) {
4303
+ function collectAppliedFiles(pullResponse, previousState, appliedFilesByName) {
4304
+ const applied = {};
4305
+ for (const skill of selectCloudSkillsToApply(pullResponse.skills)) {
4306
+ applied[skill.name] = appliedFilesByName[skill.name] ?? previousState.skills[skill.name]?.appliedFiles;
4307
+ }
4308
+ return applied;
4309
+ }
4310
+ function buildSyncState(pullResponse, localSkills, lastSyncedAt, verifiedAgentLinks = {}, failedContentNames = /* @__PURE__ */ new Set(), appliedRevisions = {}, appliedFiles = {}) {
4271
4311
  const localSkillMap = toSkillMap(localSkills);
4272
4312
  const skills = Object.fromEntries(
4273
4313
  selectCloudSkillsToApply(pullResponse.skills).map((skill) => {
4274
4314
  const localSkill = localSkillMap.get(skill.name);
4275
4315
  const appliedCloudFolderHash = appliedRevisions[skill.name];
4316
+ const appliedFileList = appliedFiles[skill.name];
4276
4317
  return [
4277
4318
  skill.name,
4278
4319
  {
@@ -4283,6 +4324,7 @@ function buildSyncState(pullResponse, localSkills, lastSyncedAt, verifiedAgentLi
4283
4324
  cloudUpdatedAt: skill.updated_at,
4284
4325
  ...!failedContentNames.has(skill.name) && !skill.skill_source_url ? { cloudContentHash: cloudContentHash(skill) } : {},
4285
4326
  ...appliedCloudFolderHash !== void 0 ? { appliedCloudFolderHash } : {},
4327
+ ...appliedFileList !== void 0 ? { appliedFiles: appliedFileList } : {},
4286
4328
  syncedAt: lastSyncedAt || (/* @__PURE__ */ new Date()).toISOString()
4287
4329
  }
4288
4330
  ];
@@ -4338,7 +4380,7 @@ function applyLegacyFirstRunState(localSkills, scopedState, legacyState) {
4338
4380
  skills: migratedSkills
4339
4381
  };
4340
4382
  }
4341
- async function writePulledSkillsToScopedMirror(pullResponse, localSkills, previousState, syncPaths, deps, failures = [], writtenSkillNames = /* @__PURE__ */ new Set()) {
4383
+ async function writePulledSkillsToScopedMirror(pullResponse, localSkills, previousState, syncPaths, deps, failures = [], writtenSkillNames = /* @__PURE__ */ new Set(), appliedFilesByName = {}) {
4342
4384
  const localSkillMap = toSkillMap(localSkills);
4343
4385
  const warnSkillSync = (message, error) => {
4344
4386
  console.warn(`[Notis] ${message}`, error);
@@ -4348,13 +4390,21 @@ async function writePulledSkillsToScopedMirror(pullResponse, localSkills, previo
4348
4390
  if (!shouldWriteCloudSkill(cloudSkill, localSkillMap, previousState)) {
4349
4391
  continue;
4350
4392
  }
4351
- if (await writeCloudSkillWithBundleFallback(cloudSkill, {
4393
+ const outcome = await writeCloudSkillWithBundleFallback(cloudSkill, {
4352
4394
  downloadSkillBundle: deps.downloadSkillBundle,
4353
- writeCloudSkillToDisk: (skill, bundleBytes) => deps.writeCloudSkillToDisk(skill, bundleBytes, syncPaths),
4395
+ writeCloudSkillToDisk: (skill, bundleBytes) => deps.writeCloudSkillToDisk(skill, bundleBytes, syncPaths, {
4396
+ // What the last write put there. Everything else in the folder was
4397
+ // produced locally and survives this one.
4398
+ previouslyApplied: previousState.skills[skill.name]?.appliedFiles
4399
+ }),
4354
4400
  onWarning: warnSkillSync
4355
- })) {
4401
+ });
4402
+ if (outcome) {
4356
4403
  downloaded += 1;
4357
4404
  writtenSkillNames.add(cloudSkill.name);
4405
+ if (typeof outcome === "object" && Array.isArray(outcome.appliedFiles)) {
4406
+ appliedFilesByName[cloudSkill.name] = outcome.appliedFiles;
4407
+ }
4358
4408
  } else {
4359
4409
  failures.push({ name: cloudSkill.name, error: "Skill content could not be downloaded or written; sync will retry" });
4360
4410
  }
@@ -4386,6 +4436,7 @@ async function materializeCloudSkillsForLocalShell(serverUrl, jwt, dependencies
4386
4436
  const localSkills = await deps.scanLocalSkills(syncPaths);
4387
4437
  const failedDownloads = [];
4388
4438
  const writtenSkillNames = /* @__PURE__ */ new Set();
4439
+ const appliedFilesByName = {};
4389
4440
  const downloaded = await writePulledSkillsToScopedMirror(
4390
4441
  pullResponse,
4391
4442
  localSkills,
@@ -4393,7 +4444,8 @@ async function materializeCloudSkillsForLocalShell(serverUrl, jwt, dependencies
4393
4444
  syncPaths,
4394
4445
  deps,
4395
4446
  failedDownloads,
4396
- writtenSkillNames
4447
+ writtenSkillNames,
4448
+ appliedFilesByName
4397
4449
  );
4398
4450
  const finalLocalSkills = await deps.scanLocalSkills(syncPaths);
4399
4451
  const lastSyncedAt = pullResponse.last_synced_at || (/* @__PURE__ */ new Date()).toISOString();
@@ -4426,7 +4478,8 @@ async function materializeCloudSkillsForLocalShell(serverUrl, jwt, dependencies
4426
4478
  lastSyncedAt,
4427
4479
  verifiedLinks,
4428
4480
  new Set(failedDownloads.map((item) => item.name)),
4429
- collectAppliedCloudRevisions(pullResponse, previousState, writtenSkillNames)
4481
+ collectAppliedCloudRevisions(pullResponse, previousState, writtenSkillNames),
4482
+ collectAppliedFiles(pullResponse, previousState, appliedFilesByName)
4430
4483
  );
4431
4484
  for (const [name, entry] of Object.entries(materializedState.skills)) {
4432
4485
  if (writtenSkillNames.has(name)) continue;
@@ -4607,6 +4660,7 @@ async function runSkillSync(serverUrl, jwt, dependencies = {}, options = {}) {
4607
4660
  }
4608
4661
  const failedDownloads = [];
4609
4662
  const writtenSkillNames = /* @__PURE__ */ new Set();
4663
+ const appliedFilesByName = {};
4610
4664
  const downloaded = await writePulledSkillsToScopedMirror(
4611
4665
  pullResponse,
4612
4666
  localSkills,
@@ -4614,7 +4668,8 @@ async function runSkillSync(serverUrl, jwt, dependencies = {}, options = {}) {
4614
4668
  syncPaths,
4615
4669
  deps,
4616
4670
  failedDownloads,
4617
- writtenSkillNames
4671
+ writtenSkillNames,
4672
+ appliedFilesByName
4618
4673
  );
4619
4674
  const finalLocalSkills = (await deps.scanLocalSkills(syncPaths)).filter((skill) => !BASE_SKILL_NAMES2.has(skill.name));
4620
4675
  const symlinkResult = await deps.syncSymlinks(
@@ -4631,7 +4686,8 @@ async function runSkillSync(serverUrl, jwt, dependencies = {}, options = {}) {
4631
4686
  lastSyncedAt,
4632
4687
  verifiedLinks,
4633
4688
  new Set(failedDownloads.map((item) => item.name)),
4634
- collectAppliedCloudRevisions(pullResponse, previousState, writtenSkillNames)
4689
+ collectAppliedCloudRevisions(pullResponse, previousState, writtenSkillNames),
4690
+ collectAppliedFiles(pullResponse, previousState, appliedFilesByName)
4635
4691
  ),
4636
4692
  syncPaths
4637
4693
  );
@@ -625,10 +625,34 @@ async function extractZipToDirectory(zipPath, destinationDir) {
625
625
  await fs.rm(destinationDir, { recursive: true, force: true });
626
626
  await fs.mkdir(path.dirname(destinationDir), { recursive: true });
627
627
  await fs.cp(sourceDir, destinationDir, { recursive: true });
628
+ return await listRelativeFiles(destinationDir);
628
629
  } finally {
629
630
  await fs.rm(extractRoot, { recursive: true, force: true });
630
631
  }
631
632
  }
633
+ async function listRelativeFiles(dirPath) {
634
+ try {
635
+ const files = await listFilesRecursive(dirPath);
636
+ return files.map((filePath) => toPosixRelativePath(dirPath, filePath)).sort();
637
+ } catch (error) {
638
+ if (error?.code === "ENOENT") return [];
639
+ throw error;
640
+ }
641
+ }
642
+ function toPosixRelativePath(rootDir, filePath) {
643
+ return path.relative(rootDir, filePath).split(path.sep).join("/");
644
+ }
645
+ async function preserveUnmanagedFiles(existingDir, stagingDir, previouslyApplied) {
646
+ const managed = previouslyApplied ? new Set(previouslyApplied) : null;
647
+ for (const existingFile of await listFilesRecursive(existingDir)) {
648
+ const relativePath = toPosixRelativePath(existingDir, existingFile);
649
+ if (managed?.has(relativePath)) continue;
650
+ const stagedPath = path.join(stagingDir, ...relativePath.split("/"));
651
+ if (await pathExists(stagedPath)) continue;
652
+ await fs.mkdir(path.dirname(stagedPath), { recursive: true });
653
+ await fs.copyFile(existingFile, stagedPath);
654
+ }
655
+ }
632
656
  async function pathExists(targetPath) {
633
657
  try {
634
658
  await fs.access(targetPath);
@@ -637,7 +661,7 @@ async function pathExists(targetPath) {
637
661
  return false;
638
662
  }
639
663
  }
640
- async function replaceSkillDirectoryAtomically(skillDir, populateDir) {
664
+ async function replaceSkillDirectoryAtomically(skillDir, populateDir, preserve) {
641
665
  const parentDir = path.dirname(skillDir);
642
666
  const skillName = path.basename(skillDir);
643
667
  await fs.mkdir(parentDir, { recursive: true });
@@ -653,6 +677,9 @@ async function replaceSkillDirectoryAtomically(skillDir, populateDir) {
653
677
  let cleanupError = null;
654
678
  try {
655
679
  await populateDir(stagingDir);
680
+ if (preserve && await pathExists(skillDir)) {
681
+ await preserveUnmanagedFiles(skillDir, stagingDir, preserve.previouslyApplied);
682
+ }
656
683
  if (await pathExists(skillDir)) {
657
684
  await fs.rename(skillDir, backupDir);
658
685
  movedExisting = true;
@@ -700,11 +727,12 @@ async function createSkillBundleBase64(skill) {
700
727
  await fs.rm(zipPath, { force: true });
701
728
  }
702
729
  }
703
- async function writeCloudSkillToDisk(skill, bundleBytes, paths = DEFAULT_SYNC_PATHS) {
730
+ async function writeCloudSkillToDisk(skill, bundleBytes, paths = DEFAULT_SYNC_PATHS, options = {}) {
704
731
  const skillDir = path.join(
705
732
  paths.skillsDir,
706
733
  safeName(skill.name, paths.skillsDir)
707
734
  );
735
+ const preserve = { previouslyApplied: options.previouslyApplied };
708
736
  if (bundleBytes?.length) {
709
737
  const bundlePath = path.join(
710
738
  os.tmpdir(),
@@ -712,8 +740,11 @@ async function writeCloudSkillToDisk(skill, bundleBytes, paths = DEFAULT_SYNC_PA
712
740
  );
713
741
  try {
714
742
  await fs.writeFile(bundlePath, bundleBytes);
715
- await extractZipToDirectory(bundlePath, skillDir);
716
- return true;
743
+ let appliedFiles = [];
744
+ await replaceSkillDirectoryAtomically(skillDir, async (stagingDir) => {
745
+ appliedFiles = await extractZipToDirectory(bundlePath, stagingDir);
746
+ }, preserve);
747
+ return { written: true, appliedFiles };
717
748
  } finally {
718
749
  await fs.rm(bundlePath, { force: true });
719
750
  }
@@ -727,6 +758,7 @@ async function writeCloudSkillToDisk(skill, bundleBytes, paths = DEFAULT_SYNC_PA
727
758
  `Synced bundle for "${skill.name}" is missing SKILL.md or SKILLS.md`
728
759
  );
729
760
  }
761
+ const appliedFiles = [];
730
762
  await replaceSkillDirectoryAtomically(skillDir, async (stagingDir) => {
731
763
  for (const bundleFile of skill.bundle_files || []) {
732
764
  const filePath = resolveSkillBundleFilePath(
@@ -738,9 +770,10 @@ async function writeCloudSkillToDisk(skill, bundleBytes, paths = DEFAULT_SYNC_PA
738
770
  filePath,
739
771
  Buffer.from(bundleFile.content_b64, "base64")
740
772
  );
773
+ appliedFiles.push(toPosixRelativePath(stagingDir, filePath));
741
774
  }
742
- });
743
- return true;
775
+ }, preserve);
776
+ return { written: true, appliedFiles: appliedFiles.sort() };
744
777
  }
745
778
  await replaceSkillDirectoryAtomically(skillDir, async (stagingDir) => {
746
779
  await fs.writeFile(
@@ -748,8 +781,8 @@ async function writeCloudSkillToDisk(skill, bundleBytes, paths = DEFAULT_SYNC_PA
748
781
  skill.skill_md ?? "",
749
782
  "utf8"
750
783
  );
751
- });
752
- return true;
784
+ }, preserve);
785
+ return { written: true, appliedFiles: ["SKILL.md"] };
753
786
  }
754
787
 
755
788
  // src/runtime/skill-sync/cloud-client.ts
@@ -1154,7 +1187,7 @@ async function writeCloudSkillWithBundleFallback(skill, dependencies) {
1154
1187
  const bundleBytes = await dependencies.downloadSkillBundle(skill.skill_source_url);
1155
1188
  const wroteBundleToDisk = await dependencies.writeCloudSkillToDisk(skill, bundleBytes);
1156
1189
  if (wroteBundleToDisk) {
1157
- return true;
1190
+ return wroteBundleToDisk;
1158
1191
  }
1159
1192
  dependencies.onWarning?.(
1160
1193
  `Bundle sync for "${skill.name}" produced no local changes, falling back to SKILL.md payload.`,
@@ -1268,12 +1301,20 @@ function collectAppliedCloudRevisions(pullResponse, previousState, writtenSkillN
1268
1301
  }
1269
1302
  return applied;
1270
1303
  }
1271
- function buildSyncState(pullResponse, localSkills, lastSyncedAt, verifiedAgentLinks = {}, failedContentNames = /* @__PURE__ */ new Set(), appliedRevisions = {}) {
1304
+ function collectAppliedFiles(pullResponse, previousState, appliedFilesByName) {
1305
+ const applied = {};
1306
+ for (const skill of selectCloudSkillsToApply(pullResponse.skills)) {
1307
+ applied[skill.name] = appliedFilesByName[skill.name] ?? previousState.skills[skill.name]?.appliedFiles;
1308
+ }
1309
+ return applied;
1310
+ }
1311
+ function buildSyncState(pullResponse, localSkills, lastSyncedAt, verifiedAgentLinks = {}, failedContentNames = /* @__PURE__ */ new Set(), appliedRevisions = {}, appliedFiles = {}) {
1272
1312
  const localSkillMap = toSkillMap(localSkills);
1273
1313
  const skills = Object.fromEntries(
1274
1314
  selectCloudSkillsToApply(pullResponse.skills).map((skill) => {
1275
1315
  const localSkill = localSkillMap.get(skill.name);
1276
1316
  const appliedCloudFolderHash = appliedRevisions[skill.name];
1317
+ const appliedFileList = appliedFiles[skill.name];
1277
1318
  return [
1278
1319
  skill.name,
1279
1320
  {
@@ -1284,6 +1325,7 @@ function buildSyncState(pullResponse, localSkills, lastSyncedAt, verifiedAgentLi
1284
1325
  cloudUpdatedAt: skill.updated_at,
1285
1326
  ...!failedContentNames.has(skill.name) && !skill.skill_source_url ? { cloudContentHash: cloudContentHash(skill) } : {},
1286
1327
  ...appliedCloudFolderHash !== void 0 ? { appliedCloudFolderHash } : {},
1328
+ ...appliedFileList !== void 0 ? { appliedFiles: appliedFileList } : {},
1287
1329
  syncedAt: lastSyncedAt || (/* @__PURE__ */ new Date()).toISOString()
1288
1330
  }
1289
1331
  ];
@@ -1339,7 +1381,7 @@ function applyLegacyFirstRunState(localSkills, scopedState, legacyState) {
1339
1381
  skills: migratedSkills
1340
1382
  };
1341
1383
  }
1342
- async function writePulledSkillsToScopedMirror(pullResponse, localSkills, previousState, syncPaths, deps, failures = [], writtenSkillNames = /* @__PURE__ */ new Set()) {
1384
+ async function writePulledSkillsToScopedMirror(pullResponse, localSkills, previousState, syncPaths, deps, failures = [], writtenSkillNames = /* @__PURE__ */ new Set(), appliedFilesByName = {}) {
1343
1385
  const localSkillMap = toSkillMap(localSkills);
1344
1386
  const warnSkillSync = (message, error) => {
1345
1387
  console.warn(`[Notis] ${message}`, error);
@@ -1349,13 +1391,21 @@ async function writePulledSkillsToScopedMirror(pullResponse, localSkills, previo
1349
1391
  if (!shouldWriteCloudSkill(cloudSkill, localSkillMap, previousState)) {
1350
1392
  continue;
1351
1393
  }
1352
- if (await writeCloudSkillWithBundleFallback(cloudSkill, {
1394
+ const outcome = await writeCloudSkillWithBundleFallback(cloudSkill, {
1353
1395
  downloadSkillBundle: deps.downloadSkillBundle,
1354
- writeCloudSkillToDisk: (skill, bundleBytes) => deps.writeCloudSkillToDisk(skill, bundleBytes, syncPaths),
1396
+ writeCloudSkillToDisk: (skill, bundleBytes) => deps.writeCloudSkillToDisk(skill, bundleBytes, syncPaths, {
1397
+ // What the last write put there. Everything else in the folder was
1398
+ // produced locally and survives this one.
1399
+ previouslyApplied: previousState.skills[skill.name]?.appliedFiles
1400
+ }),
1355
1401
  onWarning: warnSkillSync
1356
- })) {
1402
+ });
1403
+ if (outcome) {
1357
1404
  downloaded += 1;
1358
1405
  writtenSkillNames.add(cloudSkill.name);
1406
+ if (typeof outcome === "object" && Array.isArray(outcome.appliedFiles)) {
1407
+ appliedFilesByName[cloudSkill.name] = outcome.appliedFiles;
1408
+ }
1359
1409
  } else {
1360
1410
  failures.push({ name: cloudSkill.name, error: "Skill content could not be downloaded or written; sync will retry" });
1361
1411
  }
@@ -1387,6 +1437,7 @@ async function materializeCloudSkillsForLocalShell(serverUrl, jwt, dependencies
1387
1437
  const localSkills = await deps.scanLocalSkills(syncPaths);
1388
1438
  const failedDownloads = [];
1389
1439
  const writtenSkillNames = /* @__PURE__ */ new Set();
1440
+ const appliedFilesByName = {};
1390
1441
  const downloaded = await writePulledSkillsToScopedMirror(
1391
1442
  pullResponse,
1392
1443
  localSkills,
@@ -1394,7 +1445,8 @@ async function materializeCloudSkillsForLocalShell(serverUrl, jwt, dependencies
1394
1445
  syncPaths,
1395
1446
  deps,
1396
1447
  failedDownloads,
1397
- writtenSkillNames
1448
+ writtenSkillNames,
1449
+ appliedFilesByName
1398
1450
  );
1399
1451
  const finalLocalSkills = await deps.scanLocalSkills(syncPaths);
1400
1452
  const lastSyncedAt = pullResponse.last_synced_at || (/* @__PURE__ */ new Date()).toISOString();
@@ -1427,7 +1479,8 @@ async function materializeCloudSkillsForLocalShell(serverUrl, jwt, dependencies
1427
1479
  lastSyncedAt,
1428
1480
  verifiedLinks,
1429
1481
  new Set(failedDownloads.map((item) => item.name)),
1430
- collectAppliedCloudRevisions(pullResponse, previousState, writtenSkillNames)
1482
+ collectAppliedCloudRevisions(pullResponse, previousState, writtenSkillNames),
1483
+ collectAppliedFiles(pullResponse, previousState, appliedFilesByName)
1431
1484
  );
1432
1485
  for (const [name, entry] of Object.entries(materializedState.skills)) {
1433
1486
  if (writtenSkillNames.has(name)) continue;
@@ -1608,6 +1661,7 @@ async function runSkillSync(serverUrl, jwt, dependencies = {}, options = {}) {
1608
1661
  }
1609
1662
  const failedDownloads = [];
1610
1663
  const writtenSkillNames = /* @__PURE__ */ new Set();
1664
+ const appliedFilesByName = {};
1611
1665
  const downloaded = await writePulledSkillsToScopedMirror(
1612
1666
  pullResponse,
1613
1667
  localSkills,
@@ -1615,7 +1669,8 @@ async function runSkillSync(serverUrl, jwt, dependencies = {}, options = {}) {
1615
1669
  syncPaths,
1616
1670
  deps,
1617
1671
  failedDownloads,
1618
- writtenSkillNames
1672
+ writtenSkillNames,
1673
+ appliedFilesByName
1619
1674
  );
1620
1675
  const finalLocalSkills = (await deps.scanLocalSkills(syncPaths)).filter((skill) => !BASE_SKILL_NAMES.has(skill.name));
1621
1676
  const symlinkResult = await deps.syncSymlinks(
@@ -1632,7 +1687,8 @@ async function runSkillSync(serverUrl, jwt, dependencies = {}, options = {}) {
1632
1687
  lastSyncedAt,
1633
1688
  verifiedLinks,
1634
1689
  new Set(failedDownloads.map((item) => item.name)),
1635
- collectAppliedCloudRevisions(pullResponse, previousState, writtenSkillNames)
1690
+ collectAppliedCloudRevisions(pullResponse, previousState, writtenSkillNames),
1691
+ collectAppliedFiles(pullResponse, previousState, appliedFilesByName)
1636
1692
  ),
1637
1693
  syncPaths
1638
1694
  );