@uzysjung/agent-harness 26.175.0 → 26.176.0

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.
package/dist/index.js CHANGED
@@ -126,6 +126,7 @@ import {
126
126
  resolveBundleRoot,
127
127
  resolveRules,
128
128
  resolveScope,
129
+ resolveWriteTarget,
129
130
  retiredMcpNames,
130
131
  scaffoldBanner,
131
132
  selectableMcpServers,
@@ -144,7 +145,7 @@ import {
144
145
  withoutAppleDouble,
145
146
  wrapHarnessBlock,
146
147
  writeInstallLog
147
- } from "./chunk-44H2VEXJ.js";
148
+ } from "./chunk-HB6UJWC6.js";
148
149
 
149
150
  // node_modules/sisteransi/src/index.js
150
151
  var require_src = __commonJS({
@@ -1097,6 +1098,9 @@ var LINES = {
1097
1098
  removed: "removed",
1098
1099
  noChecksumRemoved: "no checksum on record \u2014 saved a copy, removed from live",
1099
1100
  editedRemoved: "you edited it \u2014 saved as <file>.backup-<time>, removed from live",
1101
+ // #902 — 옛 판이 파일 목록 없이 깐 외부 스킬 폴더의 파일: 소유를 몰라 되살리지 않고 백업만(update 마다 같은 판정이라 폴더 밖을 권한다)
1102
+ noFileList: "no file list on record (an older version installed this skill) \u2014 saved as <file>.backup-<time>",
1103
+ noFileListRemoved: "no file list on record (an older version installed this skill) \u2014 saved as <file>.backup-<time>, removed from live (if it was yours, keep it outside this skill folder)",
1100
1104
  sharedCreated: "wrote (harness part only)",
1101
1105
  upsert: "wrote the harness part \u2014 yours stays",
1102
1106
  strip: "removed the harness part \u2014 yours stays",
@@ -1685,7 +1689,14 @@ function withRecordedExclusions(spec, previous) {
1685
1689
  ],
1686
1690
  []
1687
1691
  );
1688
- return { spec: specWithExclusions(spec, excluded), excluded };
1692
+ const keyExclude = [...excluded].filter(isKeyId);
1693
+ return {
1694
+ spec: {
1695
+ ...specWithExclusions(spec, excluded),
1696
+ ...keyExclude.length > 0 ? { keyExclude } : {}
1697
+ },
1698
+ excluded
1699
+ };
1689
1700
  }
1690
1701
  function thisRunExclusions(spec, previous, accepts) {
1691
1702
  const excluded = /* @__PURE__ */ new Set([
@@ -1938,7 +1949,7 @@ function legacyGitignoreSeed(text, render, previous) {
1938
1949
  // src/installer.ts
1939
1950
  init_esm_shims();
1940
1951
  import { chmodSync as chmodSync3, existsSync as existsSync20, readdirSync as readdirSync5, readFileSync as readFileSync21, writeFileSync as writeFileSync7 } from "fs";
1941
- import { basename as basename6, join as join28, relative as relative6, resolve, sep as sep7 } from "path";
1952
+ import { basename as basename6, join as join29, relative as relative7, resolve, sep as sep8 } from "path";
1942
1953
 
1943
1954
  // src/anchor-seed.ts
1944
1955
  init_esm_shims();
@@ -3326,6 +3337,11 @@ function trackDefaultMcpAssetIds(harnessRoot, tracks) {
3326
3337
  return id !== void 0 && anyTrack(tracks, r.pattern) ? [id] : [];
3327
3338
  });
3328
3339
  }
3340
+ function droppedMcpAssetIds(harnessRoot, excluded) {
3341
+ const rows = trackRows(harnessRoot);
3342
+ const assetOf = selectableMcpServers(rows);
3343
+ return droppedMcpServers(rows, excluded).flatMap((name) => assetOf.get(name) ?? []);
3344
+ }
3329
3345
  function trackDefaultMcpNames(harnessRoot, tracks) {
3330
3346
  const { templateMcpPath } = mcpSources(harnessRoot);
3331
3347
  const base = existsSync15(templateMcpPath) ? Object.keys(JSON.parse(readFileSync14(templateMcpPath, "utf8")).mcpServers) : [];
@@ -3535,7 +3551,50 @@ function runCliTransforms(params) {
3535
3551
  // src/displaced.ts
3536
3552
  init_esm_shims();
3537
3553
  import { lstatSync as lstatSync3, mkdirSync as mkdirSync5, readFileSync as readFileSync15, renameSync } from "fs";
3538
- import { basename as basename3, dirname as dirname5, join as join20, posix } from "path";
3554
+ import { basename as basename3, dirname as dirname5, join as join21, posix as posix2 } from "path";
3555
+
3556
+ // src/harness-slot.ts
3557
+ init_esm_shims();
3558
+ import { isAbsolute, join as join20, posix, relative as relative4, sep as sep5 } from "path";
3559
+ var ownedOf = (...kinds) => [
3560
+ ...new Set(
3561
+ Object.values(CLI_OWNERSHIP).flat().filter((o) => kinds.includes(o.kind)).map((o) => o.path)
3562
+ )
3563
+ ];
3564
+ var HARNESS_FILES = {
3565
+ places: [
3566
+ ...ownedOf("recorded", "anchor"),
3567
+ ...buildManifest({ tracks: [] }).map((e2) => e2.target).filter(isCliNeutralTarget)
3568
+ ],
3569
+ except: new Set(Object.keys(SHARED_FILES))
3570
+ };
3571
+ var ANCHOR_FILES = { places: ownedOf("anchor", "import-block") };
3572
+ function skillFolders(roots) {
3573
+ return { places: roots.map((r) => `${r}/`) };
3574
+ }
3575
+ function within(rel, slots) {
3576
+ if (slots.except?.has(rel) === true) return false;
3577
+ return slots.places.some((p2) => p2.endsWith("/") ? rel.startsWith(p2) : rel === p2);
3578
+ }
3579
+ function realRel(projectDir, norm, follow) {
3580
+ const target = follow ? resolveWriteTarget(join20(projectDir, norm)) : (() => {
3581
+ const dir = resolveWriteTarget(join20(projectDir, posix.dirname(norm)));
3582
+ return dir === null ? null : join20(dir, posix.basename(norm));
3583
+ })();
3584
+ if (target === null) return null;
3585
+ const rel = relative4(resolveWriteTarget(projectDir) ?? projectDir, target);
3586
+ if (rel === "" || isAbsolute(rel)) return null;
3587
+ const slash = rel.split(sep5).join("/");
3588
+ return slash === ".." || slash.startsWith("../") ? null : slash;
3589
+ }
3590
+ function inHarnessSlot(projectDir, path, slots = HARNESS_FILES, opts = {}) {
3591
+ const norm = posix.normalize(path);
3592
+ if (!within(norm, slots)) return false;
3593
+ const real = realRel(projectDir, norm, opts.follow === true);
3594
+ return real !== null && within(real, slots);
3595
+ }
3596
+
3597
+ // src/displaced.ts
3539
3598
  function exists(abs) {
3540
3599
  try {
3541
3600
  lstatSync3(abs);
@@ -3545,12 +3604,12 @@ function exists(abs) {
3545
3604
  }
3546
3605
  }
3547
3606
  function outsideBackup(projectDir, backup) {
3548
- const abs = join20(projectDir, backup);
3607
+ const abs = join21(projectDir, backup);
3549
3608
  const hit = outsideProjectTarget(projectDir, dirname5(abs));
3550
- return hit === null ? null : join20(hit.target, basename3(abs));
3609
+ return hit === null ? null : join21(hit.target, basename3(abs));
3551
3610
  }
3552
3611
  function isBackupOf(path, backup) {
3553
- return backupOriginal(posix.normalize(backup)) === posix.normalize(path);
3612
+ return backupOriginal(posix2.normalize(backup)) === posix2.normalize(path);
3554
3613
  }
3555
3614
  function isLegacyDisplaced(log, projectDir, f) {
3556
3615
  if (f.change !== "displaced") return false;
@@ -3558,7 +3617,7 @@ function isLegacyDisplaced(log, projectDir, f) {
3558
3617
  if (outsideBackup(projectDir, at2) !== null) return false;
3559
3618
  let original;
3560
3619
  try {
3561
- original = readFileSync15(join20(projectDir, at2), "utf8");
3620
+ original = readFileSync15(join21(projectDir, at2), "utf8");
3562
3621
  } catch {
3563
3622
  return false;
3564
3623
  }
@@ -3568,8 +3627,18 @@ function restoreDisplaced(log, projectDir, opts) {
3568
3627
  const out = [];
3569
3628
  for (const f of log.rootFiles ?? []) {
3570
3629
  if (f.change !== "displaced" || !opts.has(f.path)) continue;
3571
- const slot = join20(projectDir, f.path);
3630
+ const slot = join21(projectDir, f.path);
3572
3631
  if (outsideProjectTarget(projectDir, slot) !== null) continue;
3632
+ if (!inHarnessSlot(projectDir, f.path)) {
3633
+ out.push({
3634
+ path: f.path,
3635
+ backup: f.notes[0] ?? null,
3636
+ verdict: "advise",
3637
+ line: "not put back \u2014 the install record says this tool moved a file of yours aside here, but this is (or leads) outside the places this tool installs to; left it as is",
3638
+ forget: true
3639
+ });
3640
+ continue;
3641
+ }
3573
3642
  const backup = isLegacyDisplaced(log, projectDir, f) ? null : f.notes[0] ?? null;
3574
3643
  const away = backup === null ? null : outsideBackup(projectDir, backup);
3575
3644
  if (away !== null) {
@@ -3594,12 +3663,12 @@ function restoreDisplaced(log, projectDir, opts) {
3594
3663
  }
3595
3664
  const d2 = judgeDisplaced({
3596
3665
  backup,
3597
- backupExists: backup !== null && exists(join20(projectDir, backup)),
3666
+ backupExists: backup !== null && exists(join21(projectDir, backup)),
3598
3667
  slotEmpty: !exists(slot) || !opts.write && opts.willBeGone?.has(f.path) === true
3599
3668
  });
3600
3669
  if (opts.write && d2.verdict === "restore" && backup !== null) {
3601
3670
  mkdirSync5(dirname5(slot), { recursive: true });
3602
- renameSync(join20(projectDir, backup), slot);
3671
+ renameSync(join21(projectDir, backup), slot);
3603
3672
  }
3604
3673
  out.push({
3605
3674
  path: f.path,
@@ -3619,7 +3688,7 @@ function withoutRestored(rootFiles, outcomes) {
3619
3688
  // src/excluded-still-there.ts
3620
3689
  init_esm_shims();
3621
3690
  import { existsSync as existsSync16 } from "fs";
3622
- import { join as join21 } from "path";
3691
+ import { join as join22 } from "path";
3623
3692
  function excludedStillThere(projectDir, excluded, baselineOnDisk, previousLog) {
3624
3693
  const out = [];
3625
3694
  const add = (id, catalog) => {
@@ -3632,7 +3701,7 @@ function excludedStillThere(projectDir, excluded, baselineOnDisk, previousLog) {
3632
3701
  for (const id of excluded) {
3633
3702
  if (INTERNAL_BUNDLED_SKILL_IDS.includes(id)) {
3634
3703
  const there = [".claude/skills", ".agents/skills"].some(
3635
- (d2) => existsSync16(join21(projectDir, d2, id))
3704
+ (d2) => existsSync16(join22(projectDir, d2, id))
3636
3705
  );
3637
3706
  if (there) add(id, false);
3638
3707
  } else {
@@ -3646,9 +3715,9 @@ function excludedStillThere(projectDir, excluded, baselineOnDisk, previousLog) {
3646
3715
  function assetStillThere(projectDir, asset) {
3647
3716
  if (asset.method !== "skill" || asset.scope === "global") return true;
3648
3717
  if (asset.files !== void 0)
3649
- return asset.files.some((f) => existsSync16(join21(projectDir, f.path)));
3718
+ return asset.files.some((f) => existsSync16(join22(projectDir, f.path)));
3650
3719
  const dir = asset.detail.skill ?? asset.id;
3651
- return [".claude/skills", ".agents/skills"].some((d2) => existsSync16(join21(projectDir, d2, dir)));
3720
+ return [".claude/skills", ".agents/skills"].some((d2) => existsSync16(join22(projectDir, d2, dir)));
3652
3721
  }
3653
3722
 
3654
3723
  // src/external-installer.ts
@@ -3669,7 +3738,7 @@ import {
3669
3738
  rmSync as rmSync3
3670
3739
  } from "fs";
3671
3740
  import { homedir as homedir2 } from "os";
3672
- import { basename as basename4, dirname as dirname6, join as join22, relative as relative4, sep as sep5 } from "path";
3741
+ import { basename as basename4, dirname as dirname6, join as join23, relative as relative5, sep as sep6 } from "path";
3673
3742
  var SKILL_ROOTS = [".claude/skills", ".agents/skills"];
3674
3743
  function skillRootOf(cli) {
3675
3744
  return cli === "claude" ? ".claude/skills" : ".agents/skills";
@@ -3677,11 +3746,11 @@ function skillRootOf(cli) {
3677
3746
  function snapshotSkillRoots(projectDir) {
3678
3747
  const out = /* @__PURE__ */ new Map();
3679
3748
  for (const root of SKILL_ROOTS) {
3680
- if (outsideProjectTarget(projectDir, join22(projectDir, root)) !== null) continue;
3681
- for (const rel of listFilesRecursive(join22(projectDir, root))) {
3749
+ if (outsideProjectTarget(projectDir, join23(projectDir, root)) !== null) continue;
3750
+ for (const rel of listFilesRecursive(join23(projectDir, root))) {
3682
3751
  const path = `${root}/${rel}`;
3683
3752
  try {
3684
- out.set(path, hashContent(readFileSync16(join22(projectDir, path), "utf8")));
3753
+ out.set(path, hashContent(readFileSync16(join23(projectDir, path), "utf8")));
3685
3754
  } catch {
3686
3755
  }
3687
3756
  }
@@ -3699,8 +3768,8 @@ function homeSkillDirs(roots) {
3699
3768
  }
3700
3769
  for (const name of names) {
3701
3770
  try {
3702
- const st2 = lstatSync4(join22(root, name), { bigint: true });
3703
- if (st2.isDirectory()) out.set(join22(root, name), `${st2.ino}:${st2.ctimeNs}`);
3771
+ const st2 = lstatSync4(join23(root, name), { bigint: true });
3772
+ if (st2.isDirectory()) out.set(join23(root, name), `${st2.ino}:${st2.ctimeNs}`);
3704
3773
  } catch {
3705
3774
  }
3706
3775
  }
@@ -3713,7 +3782,7 @@ function writtenSkillNames(before, after) {
3713
3782
  }
3714
3783
  function lockedSkillNames(source) {
3715
3784
  const xdg = process.env.XDG_STATE_HOME;
3716
- const path = xdg ? join22(xdg, "skills", ".skill-lock.json") : join22(homedir2(), ".agents", ".skill-lock.json");
3785
+ const path = xdg ? join23(xdg, "skills", ".skill-lock.json") : join23(homedir2(), ".agents", ".skill-lock.json");
3717
3786
  try {
3718
3787
  const lock = JSON.parse(readFileSync16(path, "utf8"));
3719
3788
  if (typeof lock.version !== "number" || lock.version < 3) return null;
@@ -3816,7 +3885,7 @@ function installOne(asset, ctx) {
3816
3885
  return { ...result2, skills: written.filter((n) => locked.has(n)) };
3817
3886
  }
3818
3887
  const outside = SKILL_ROOTS.flatMap((root) => {
3819
- const hit = outsideProjectTarget(cwd, join22(cwd, root));
3888
+ const hit = outsideProjectTarget(cwd, join23(cwd, root));
3820
3889
  return hit === null ? [] : [{ root, target: hit.target }];
3821
3890
  });
3822
3891
  const outRoots = new Set(outside.map((o) => o.root));
@@ -3898,16 +3967,19 @@ function refreshExternalSkills(projectDir, deps = {}) {
3898
3967
  if (targets.length === 0) {
3899
3968
  return { ...none, notInCatalog, unknown: false, ...copies };
3900
3969
  }
3970
+ const scopeOf = (a) => resolveScope(log.assets.find((r) => r.id === a.id)?.scope ?? log.scope);
3901
3971
  const targetIds = new Set(targets.map((a) => a.id));
3902
3972
  const clis = [...installedClis(log)];
3973
+ const inProject = targets.filter((a) => scopeOf(a) === "project");
3974
+ const slotCtx = { log, scope: "project" };
3903
3975
  const settle = holdSkillSlots(projectDir, {
3904
3976
  recorded: log.assets.filter((a) => targetIds.has(a.id)),
3905
- slots: skillSlotsFor(targets, clis, { log, scope: log.scope }),
3977
+ slots: skillSlotsFor(inProject, clis, slotCtx),
3978
+ unlisted: unlistedSkillSlots(inProject, clis, slotCtx),
3906
3979
  log,
3907
3980
  clis,
3908
- scope: log.scope
3981
+ scope: "project"
3909
3982
  });
3910
- const scopeOf = (a) => resolveScope(log.assets.find((r) => r.id === a.id)?.scope ?? log.scope);
3911
3983
  const run = deps.run ?? runExternalInstall;
3912
3984
  let ran;
3913
3985
  try {
@@ -3975,10 +4047,10 @@ function holdSkillSlots(projectDir, hold) {
3975
4047
  const root = dirname6(f.slot);
3976
4048
  let dir = asides.get(root);
3977
4049
  if (dir === void 0) {
3978
- dir = claimBackupDir(join22(projectDir, root), now);
4050
+ dir = claimBackupDir(join23(projectDir, root), now);
3979
4051
  asides.set(root, dir);
3980
4052
  }
3981
- const aside = join22(dir, f.rel.slice(root.length + 1));
4053
+ const aside = join23(dir, f.rel.slice(root.length + 1));
3982
4054
  mkdirSync6(dirname6(aside), { recursive: true });
3983
4055
  renameSync2(f.abs, aside);
3984
4056
  moved.push({ ...f, aside });
@@ -4001,12 +4073,17 @@ function holdSkillSlots(projectDir, hold) {
4001
4073
  displaced: [],
4002
4074
  judged: []
4003
4075
  };
4076
+ const rewritten = new Set(
4077
+ (report?.attempted ?? []).flatMap(
4078
+ (r) => r.ok ? (r.files?.created ?? []).map((x) => skillFolderOf(x.path)) : []
4079
+ )
4080
+ );
4004
4081
  const back = /* @__PURE__ */ new Set();
4005
4082
  const failed = [];
4006
4083
  for (const f of moved) {
4007
4084
  const restore = failedSlots === null || failedSlots.some((s) => f.rel === s || f.rel.startsWith(`${s}/`));
4008
4085
  try {
4009
- const r = settleAside(projectDir, f, restore);
4086
+ const r = settleAside(projectDir, f, restore, rewritten.has(skillFolderOf(f.rel)));
4010
4087
  if (r === "back") {
4011
4088
  back.add(f.rel);
4012
4089
  continue;
@@ -4014,11 +4091,11 @@ function holdSkillSlots(projectDir, hold) {
4014
4091
  out.backups.push(r.backup);
4015
4092
  if (r.displaced !== null) out.displaced.push(r.displaced);
4016
4093
  const backup = toProjectRel(projectDir, r.backup);
4017
- const line = !f.recorded ? LINES.hadFile : r.gone ? LINES.editedRemoved : LINES.edited;
4094
+ const line = f.unlisted ? r.gone ? LINES.noFileListRemoved : LINES.noFileList : !f.recorded ? LINES.hadFile : r.gone ? LINES.editedRemoved : LINES.edited;
4018
4095
  out.judged.push({
4019
4096
  path: f.rel,
4020
4097
  verdict: r.gone ? "backup+remove" : "backup+overwrite",
4021
- recorded: f.recorded ? "sha" : "none",
4098
+ recorded: f.recorded ? "sha" : f.unlisted ? "no-sha" : "none",
4022
4099
  line: withBackup(line, backup),
4023
4100
  backup,
4024
4101
  backupAbs: r.backup
@@ -4043,13 +4120,18 @@ function skillSlotsFor(assets, clis, ctx) {
4043
4120
  const out = /* @__PURE__ */ new Set();
4044
4121
  for (const a of assets) {
4045
4122
  if (a.method.kind !== "skill") continue;
4046
- const prior = ctx.log?.assets.find((r) => r.id === a.id);
4047
- if (prior !== void 0 && prior.files === void 0) continue;
4048
4123
  const skill = a.method.skill;
4049
4124
  for (const root of roots) out.add(skill === void 0 ? root : `${root}/${skill}`);
4050
4125
  }
4051
4126
  return [...out];
4052
4127
  }
4128
+ function unlistedSkillSlots(assets, clis, ctx) {
4129
+ const unlisted = assets.filter((a) => {
4130
+ const prior = ctx.log?.assets.find((r) => r.id === a.id);
4131
+ return prior !== void 0 && prior.files === void 0;
4132
+ });
4133
+ return skillSlotsFor(unlisted, clis, ctx);
4134
+ }
4053
4135
  function reclaimGlobalSkillCopies(projectDir, log, opts) {
4054
4136
  const clis = installedClis(log);
4055
4137
  const roots = clis.length === 0 ? [...SKILL_ROOTS] : [...new Set(clis.map(skillRootOf))];
@@ -4062,17 +4144,19 @@ function reclaimGlobalSkillCopies(projectDir, log, opts) {
4062
4144
  if (a.method !== "skill" || a.scope !== "global" || excluded.has(a.id)) continue;
4063
4145
  if (opts.ids !== void 0 && !opts.ids.has(a.id)) continue;
4064
4146
  const skill = a.detail.skill;
4065
- const names = skill !== void 0 ? [skill] : (Array.isArray(a.skills) ? a.skills : []).filter(isSkillName);
4147
+ const names = (skill !== void 0 ? [skill] : Array.isArray(a.skills) ? a.skills : []).filter(
4148
+ isSkillName
4149
+ );
4066
4150
  for (const name of names) {
4067
4151
  for (const root of roots) {
4068
4152
  const slot = `${root}/${name}`;
4069
- const real = realOrNull(join22(projectDir, slot));
4153
+ const real = realOrNull(join23(projectDir, slot));
4070
4154
  if (real !== null) {
4071
4155
  if (seen.has(real)) continue;
4072
4156
  seen.add(real);
4073
4157
  }
4074
4158
  const ctx = { log, others, write: opts.write, rm, id: a.id };
4075
- const r = reclaimCopy(projectDir, slot, join22(globalSkillRoot(root), name), ctx);
4159
+ const r = reclaimCopy(projectDir, slot, join23(globalSkillRoot(root), name), ctx);
4076
4160
  if (r !== null) out.push(r);
4077
4161
  }
4078
4162
  }
@@ -4081,15 +4165,15 @@ function reclaimGlobalSkillCopies(projectDir, log, opts) {
4081
4165
  }
4082
4166
  function globalSkillRoot(root) {
4083
4167
  if (root === ".claude/skills") {
4084
- return join22(process.env.CLAUDE_CONFIG_DIR?.trim() || join22(homedir2(), ".claude"), "skills");
4168
+ return join23(process.env.CLAUDE_CONFIG_DIR?.trim() || join23(homedir2(), ".claude"), "skills");
4085
4169
  }
4086
- return join22(homedir2(), ".agents/skills");
4170
+ return join23(homedir2(), ".agents/skills");
4087
4171
  }
4088
4172
  function isSkillName(name) {
4089
4173
  return typeof name === "string" && name !== "" && name !== "." && name !== ".." && !/[\\/]/.test(name);
4090
4174
  }
4091
4175
  function reclaimCopy(projectDir, slot, home, ctx) {
4092
- const abs = join22(projectDir, slot);
4176
+ const abs = join23(projectDir, slot);
4093
4177
  try {
4094
4178
  if (lstatSync4(abs, { throwIfNoEntry: false })?.isDirectory() !== true) return null;
4095
4179
  if (outsideProjectTarget(projectDir, abs) !== null) return null;
@@ -4097,8 +4181,15 @@ function reclaimCopy(projectDir, slot, home, ctx) {
4097
4181
  } catch {
4098
4182
  return null;
4099
4183
  }
4100
- const homeExists = existsSync17(home);
4101
4184
  const res = { id: ctx.id, slot, removed: [], judged: [], left: [] };
4185
+ if (!inHarnessSlot(projectDir, slot, skillFolders(SKILL_ROOTS))) {
4186
+ res.left.push({
4187
+ path: slot,
4188
+ why: "it leads outside the skill folders this tool installs to \u2014 not touched"
4189
+ });
4190
+ return res;
4191
+ }
4192
+ const homeExists = existsSync17(home);
4102
4193
  const now = /* @__PURE__ */ new Date();
4103
4194
  let entries;
4104
4195
  try {
@@ -4115,15 +4206,15 @@ function reclaimCopy(projectDir, slot, home, ctx) {
4115
4206
  res.left.push({ path, why: "a link \u2014 not part of the copy the tool put here" });
4116
4207
  continue;
4117
4208
  }
4118
- const file = join22(abs, rel);
4209
+ const file = join23(abs, rel);
4119
4210
  try {
4120
4211
  const mine = readFileSync16(file);
4121
- const same = homeExists && readOrNull(join22(home, rel))?.equals(mine) === true;
4212
+ const same = homeExists && readOrNull(join23(home, rel))?.equals(mine) === true;
4122
4213
  const backupAbs = !same && ctx.write ? backupFile(file, now) : void 0;
4123
4214
  if (ctx.write) ctx.rm(file);
4124
4215
  res.removed.push(path);
4125
4216
  if (same) continue;
4126
- const line = homeExists ? `not the same as the global copy (${join22(home, rel)}) \u2014 saved as <file>.backup-<time>, removed from live` : `no global copy at ${home} to compare \u2014 saved as <file>.backup-<time>, removed from live`;
4217
+ const line = homeExists ? `not the same as the global copy (${join23(home, rel)}) \u2014 saved as <file>.backup-<time>, removed from live` : `no global copy at ${home} to compare \u2014 saved as <file>.backup-<time>, removed from live`;
4127
4218
  const judged = {
4128
4219
  path,
4129
4220
  verdict: "backup+remove",
@@ -4151,7 +4242,7 @@ function realOrNull(abs) {
4151
4242
  }
4152
4243
  function realFileOrNull(abs) {
4153
4244
  const dir = realOrNull(dirname6(abs));
4154
- return dir === null ? null : join22(dir, basename4(abs));
4245
+ return dir === null ? null : join23(dir, basename4(abs));
4155
4246
  }
4156
4247
  function readOrNull(abs) {
4157
4248
  try {
@@ -4168,7 +4259,7 @@ function slotFilesToKeep(projectDir, hold) {
4168
4259
  const sha = new Map(mine.map((f) => [f.path, f.sha256]));
4169
4260
  const realSha = new Map(
4170
4261
  mine.flatMap((f) => {
4171
- const real = realFileOrNull(join22(projectDir, f.path));
4262
+ const real = realFileOrNull(join23(projectDir, f.path));
4172
4263
  return real === null ? [] : [[real, f.sha256]];
4173
4264
  })
4174
4265
  );
@@ -4180,17 +4271,23 @@ function slotFilesToKeep(projectDir, hold) {
4180
4271
  ...mine.map((f) => f.path.split("/")).filter((parts) => parts.length > 3).map((parts) => parts.slice(0, 3).join("/")),
4181
4272
  ...hold.slots
4182
4273
  ]);
4274
+ const unlisted = new Set(hold.unlisted ?? []);
4275
+ const unlistedAt = (path) => {
4276
+ let owner = "";
4277
+ for (const s of slots) if (path.startsWith(`${s}/`) && s.length > owner.length) owner = s;
4278
+ return unlisted.has(owner);
4279
+ };
4183
4280
  const out = /* @__PURE__ */ new Map();
4184
4281
  const realProject = realpathSync3(projectDir);
4185
4282
  for (const slot of slots) {
4186
- if (outsideProjectTarget(projectDir, join22(projectDir, slot)) !== null) continue;
4187
- for (const { rel, link } of listEntriesRecursive(join22(projectDir, slot))) {
4283
+ if (outsideProjectTarget(projectDir, join23(projectDir, slot)) !== null) continue;
4284
+ for (const { rel, link } of listEntriesRecursive(join23(projectDir, slot))) {
4188
4285
  const path = `${slot}/${rel}`;
4189
4286
  if (link && SKILL_ROOTS.includes(dirname6(path))) continue;
4190
- const abs = join22(projectDir, path);
4191
- const real = join22(realpathSync3(dirname6(abs)), basename4(abs));
4287
+ const abs = join23(projectDir, path);
4288
+ const real = join23(realpathSync3(dirname6(abs)), basename4(abs));
4192
4289
  const prev = out.get(real);
4193
- if (prev !== void 0 && (prev.rel === path || real !== join22(realProject, path))) continue;
4290
+ if (prev !== void 0 && (prev.rel === path || real !== join23(realProject, path))) continue;
4194
4291
  const recordedSha = sha.get(path) ?? realSha.get(real);
4195
4292
  if (recordedSha === void 0 && (others.has(path) || recorded(hold.log, path).state !== "none"))
4196
4293
  continue;
@@ -4203,12 +4300,20 @@ function slotFilesToKeep(projectDir, hold) {
4203
4300
  } catch {
4204
4301
  continue;
4205
4302
  }
4206
- out.set(real, { abs, rel: path, slot, recorded: recordedSha !== void 0, link });
4303
+ const listed = recordedSha !== void 0;
4304
+ out.set(real, {
4305
+ abs,
4306
+ rel: path,
4307
+ slot,
4308
+ recorded: listed,
4309
+ unlisted: !listed && unlistedAt(path) && backupOriginal(path) === null,
4310
+ link
4311
+ });
4207
4312
  }
4208
4313
  }
4209
4314
  return [...out.values()];
4210
4315
  }
4211
- function settleAside(projectDir, f, restore) {
4316
+ function settleAside(projectDir, f, restore, folderRewritten) {
4212
4317
  let now = null;
4213
4318
  let vacant = false;
4214
4319
  try {
@@ -4219,7 +4324,7 @@ function settleAside(projectDir, f, restore) {
4219
4324
  if (code !== "ENOENT" && code !== "ENOTDIR") throw e2;
4220
4325
  vacant = true;
4221
4326
  }
4222
- const keep = vacant ? f.link || !f.recorded : f.link ? lstatSync4(f.abs).isSymbolicLink() && readlinkSync(f.abs) === readlinkSync(f.aside) : now?.equals(readFileSync16(f.aside)) === true;
4327
+ const keep = vacant ? f.unlisted ? !folderRewritten : f.link || !f.recorded : f.link ? lstatSync4(f.abs).isSymbolicLink() && readlinkSync(f.abs) === readlinkSync(f.aside) : now?.equals(readFileSync16(f.aside)) === true;
4223
4328
  if (restore || keep) {
4224
4329
  mkdirSync6(dirname6(f.abs), { recursive: true });
4225
4330
  renameSync2(f.aside, f.abs);
@@ -4227,13 +4332,16 @@ function settleAside(projectDir, f, restore) {
4227
4332
  }
4228
4333
  const backup = f.link ? backupLink(f.abs, f.aside) : backupFromCopy(f.abs, f.aside);
4229
4334
  rmSync3(f.aside, { force: true });
4230
- if (f.recorded || vacant) return { backup, gone: vacant, displaced: null };
4335
+ if (f.recorded || f.unlisted || vacant) return { backup, gone: vacant, displaced: null };
4231
4336
  return {
4232
4337
  backup,
4233
4338
  gone: false,
4234
4339
  displaced: { path: f.rel, change: "displaced", notes: [toProjectRel(projectDir, backup)] }
4235
4340
  };
4236
4341
  }
4342
+ function skillFolderOf(path) {
4343
+ return path.split("/").slice(0, 3).join("/");
4344
+ }
4237
4345
  function withoutCreated(report, paths) {
4238
4346
  if (paths.size === 0) return report;
4239
4347
  return {
@@ -4249,13 +4357,13 @@ function withoutCreated(report, paths) {
4249
4357
  function pruneEmptyDirs(dir) {
4250
4358
  try {
4251
4359
  for (const e2 of readdirSync3(dir, { withFileTypes: true }))
4252
- if (e2.isDirectory()) pruneEmptyDirs(join22(dir, e2.name));
4360
+ if (e2.isDirectory()) pruneEmptyDirs(join23(dir, e2.name));
4253
4361
  rmdirSync(dir);
4254
4362
  } catch {
4255
4363
  }
4256
4364
  }
4257
4365
  function toProjectRel(projectDir, abs) {
4258
- return relative4(projectDir, abs).split(sep5).join("/");
4366
+ return relative5(projectDir, abs).split(sep6).join("/");
4259
4367
  }
4260
4368
  function recordRefreshedFiles(projectDir, log, report, displaced, writeLog) {
4261
4369
  const next = new Map(
@@ -4382,7 +4490,7 @@ function detectVersion(method, spawn, scope, projectDir) {
4382
4490
  case "plugin":
4383
4491
  return detectPluginVersion(method.pluginId, scope, projectDir);
4384
4492
  case "npm": {
4385
- const pkgJson = scope === "global" ? detectGlobalNpmPackageJsonPath(method.pkg, spawn) : join22(projectDir, "node_modules", method.pkg, "package.json");
4493
+ const pkgJson = scope === "global" ? detectGlobalNpmPackageJsonPath(method.pkg, spawn) : join23(projectDir, "node_modules", method.pkg, "package.json");
4386
4494
  if (!pkgJson || !existsSync17(pkgJson)) return void 0;
4387
4495
  const parsed = JSON.parse(readFileSync16(pkgJson, "utf8"));
4388
4496
  return parsed.version;
@@ -4396,7 +4504,7 @@ function detectVersion(method, spawn, scope, projectDir) {
4396
4504
  }
4397
4505
  function detectPluginVersion(pluginId, scope, projectDir) {
4398
4506
  const claudeScope = scope === "global" ? "user" : "project";
4399
- const path = join22(homedir2(), ".claude/plugins/installed_plugins.json");
4507
+ const path = join23(homedir2(), ".claude/plugins/installed_plugins.json");
4400
4508
  if (!existsSync17(path)) return void 0;
4401
4509
  const data = JSON.parse(readFileSync16(path, "utf8"));
4402
4510
  const entries = data.plugins?.[pluginId] ?? [];
@@ -4411,7 +4519,7 @@ function detectPluginVersion(pluginId, scope, projectDir) {
4411
4519
  }
4412
4520
  function detectGlobalNpmPackageJsonPath(pkg, spawn) {
4413
4521
  const npmRoot = getNpmGlobalRoot(spawn);
4414
- return npmRoot ? join22(npmRoot, pkg, "package.json") : void 0;
4522
+ return npmRoot ? join23(npmRoot, pkg, "package.json") : void 0;
4415
4523
  }
4416
4524
  var npmGlobalRootCache;
4417
4525
  function getNpmGlobalRoot(spawn) {
@@ -4431,7 +4539,7 @@ function getNpmGlobalRoot(spawn) {
4431
4539
  // src/key-ids.ts
4432
4540
  init_esm_shims();
4433
4541
  import { readFileSync as readFileSync17 } from "fs";
4434
- import { join as join23 } from "path";
4542
+ import { join as join24 } from "path";
4435
4543
  function renderedKeyIds(harnessRoot, input, clis) {
4436
4544
  const out = /* @__PURE__ */ new Set();
4437
4545
  const add = (path, key) => {
@@ -4454,7 +4562,7 @@ function renderedKeyIds(harnessRoot, input, clis) {
4454
4562
  for (const name of Object.keys(mcp.mcpServers)) add(".mcp.json", `mcpServers.${name}`);
4455
4563
  for (const key of gitignoreRender().keys()) add(".gitignore", key);
4456
4564
  if (clis.includes("claude")) {
4457
- const template = readFileSync17(join23(harnessRoot, "templates/settings.json"), "utf8");
4565
+ const template = readFileSync17(join24(harnessRoot, "templates/settings.json"), "utf8");
4458
4566
  for (const key of renderSettingsPortion(template, () => true).keys()) {
4459
4567
  add(".claude/settings.json", key);
4460
4568
  }
@@ -4495,7 +4603,7 @@ function withoutAccepts(harnessRoot, spec, previous) {
4495
4603
 
4496
4604
  // src/linked-skill-bodies.ts
4497
4605
  init_esm_shims();
4498
- import { join as join24 } from "path";
4606
+ import { join as join25 } from "path";
4499
4607
  function refreshLinkedSkillBodies(params) {
4500
4608
  const { harnessRoot, projectDir, ids, baseline, journal } = params;
4501
4609
  const bodyKey = (id) => `.agents/skills/${id}/SKILL.md`;
@@ -4506,7 +4614,7 @@ function refreshLinkedSkillBodies(params) {
4506
4614
  );
4507
4615
  const result = writer.result();
4508
4616
  return {
4509
- updated: ours.filter((id) => written.has(join24(projectDir, bodyKey(id)))),
4617
+ updated: ours.filter((id) => written.has(join25(projectDir, bodyKey(id)))),
4510
4618
  notOurs: ids.filter((id) => !baseline.has(bodyKey(id))),
4511
4619
  files: result.files,
4512
4620
  backupPaths: result.backupPaths,
@@ -4521,7 +4629,7 @@ function refreshLinkedSkillBodies(params) {
4521
4629
  // src/out-of-track.ts
4522
4630
  init_esm_shims();
4523
4631
  import { lstatSync as lstatSync5, readFileSync as readFileSync18, rmSync as rmSync4 } from "fs";
4524
- import { join as join25 } from "path";
4632
+ import { join as join26 } from "path";
4525
4633
  var AGENTS_RULE = /^\.agents\/rules\/([^/]+)\.md$/;
4526
4634
  var ANCHOR_RULE = "uzys-harness";
4527
4635
  function reclaimOutOfTrack(args) {
@@ -4541,8 +4649,9 @@ function reclaimOutOfTrack(args) {
4541
4649
  const name = AGENTS_RULE.exec(f.path)?.[1];
4542
4650
  if (name === void 0 || name === ANCHOR_RULE || rendered.has(name)) continue;
4543
4651
  if (isBaselineExcluded(`.claude/rules/${name}.md`, args.excluded)) continue;
4544
- const abs = join25(projectDir, f.path);
4652
+ const abs = join26(projectDir, f.path);
4545
4653
  if (args.outside.skip(abs)) continue;
4654
+ if (!inHarnessSlot(projectDir, f.path)) continue;
4546
4655
  const stat = lstatSync5(abs, { throwIfNoEntry: false });
4547
4656
  if (stat !== void 0 && !stat.isFile()) continue;
4548
4657
  if (stat !== void 0 && claudeCopy.has(`rules/${name}.md`)) {
@@ -4583,14 +4692,14 @@ function tracksBringing(rule) {
4583
4692
  // src/state.ts
4584
4693
  init_esm_shims();
4585
4694
  import { existsSync as existsSync18, readFileSync as readFileSync19 } from "fs";
4586
- import { join as join26 } from "path";
4695
+ import { join as join27 } from "path";
4587
4696
  var META_FILE = ".claude/.installed-tracks";
4588
4697
  var LEGACY_ANCHOR_FILE = ".claude/CLAUDE.md";
4589
4698
  var TRACE_RULES = ["htmx", "nextjs", "data-analysis", "pyside6", "cli-development"].map(
4590
4699
  (r) => `.claude/rules/${r}.md`
4591
4700
  );
4592
4701
  function detectInstallState(projectDir) {
4593
- const hasClaudeDir = existsSync18(join26(projectDir, ".claude"));
4702
+ const hasClaudeDir = existsSync18(join27(projectDir, ".claude"));
4594
4703
  const read = readInstallLogStatus(projectDir);
4595
4704
  if (read.status === "ok") {
4596
4705
  return {
@@ -4610,7 +4719,7 @@ function tracksFromLog(tracks) {
4610
4719
  return [...new Set(tracks.filter(isTrack))].sort();
4611
4720
  }
4612
4721
  function collectTraces(projectDir) {
4613
- const at2 = (rel) => join26(projectDir, rel);
4722
+ const at2 = (rel) => join27(projectDir, rel);
4614
4723
  const text = (rel) => {
4615
4724
  try {
4616
4725
  return readFileSync19(at2(rel), "utf8");
@@ -4684,7 +4793,7 @@ import {
4684
4793
  unlinkSync as unlinkSync2,
4685
4794
  writeFileSync as writeFileSync6
4686
4795
  } from "fs";
4687
- import { basename as basename5, dirname as dirname7, join as join27, relative as relative5, sep as sep6 } from "path";
4796
+ import { basename as basename5, dirname as dirname7, join as join28, relative as relative6, sep as sep7 } from "path";
4688
4797
  var LEGACY_ANCHOR_FILE2 = ".claude/CLAUDE.md";
4689
4798
  function buildUpdateSpec(projectDir, tracks, only) {
4690
4799
  const log = readInstallLog(projectDir);
@@ -4706,7 +4815,7 @@ function parseUpdateOnly(raw) {
4706
4815
  return { ok: true, groups: [...new Set(raw)] };
4707
4816
  }
4708
4817
  function runUpdateMode(projectDir, templatesDir, harnessRoot, deps = {}, only) {
4709
- const claudeDir = join27(projectDir, ".claude");
4818
+ const claudeDir = join28(projectDir, ".claude");
4710
4819
  const startedAt = Date.now();
4711
4820
  const backupsAtStart = new Set(
4712
4821
  collectRunBackups(projectDir, Number.NEGATIVE_INFINITY).map((b2) => b2.backup)
@@ -4777,7 +4886,7 @@ function runUpdateMode(projectDir, templatesDir, harnessRoot, deps = {}, only) {
4777
4886
  harnessMcp,
4778
4887
  harnessWrote: harness.wrote
4779
4888
  });
4780
- const settingsPath = join27(claudeDir, "settings.json");
4889
+ const settingsPath = join28(claudeDir, "settings.json");
4781
4890
  if (claudeManaged && wants("hooks") && existsSync19(settingsPath) && !outside.skip(settingsPath)) {
4782
4891
  report.retiredHookRefsRemoved = removeRetiredHookRefs(
4783
4892
  settingsPath,
@@ -4924,7 +5033,7 @@ function runUpdateMode(projectDir, templatesDir, harnessRoot, deps = {}, only) {
4924
5033
  judged.filter((j4) => j4.recorded === "no-sha" && j4.backup !== void 0).map((j4) => j4.path)
4925
5034
  );
4926
5035
  report.backups = collectRunBackups(projectDir, startedAt, backupsAtStart).map(
4927
- (b2) => existsSync19(join27(projectDir, b2.path)) ? b2 : { ...b2, removed: true }
5036
+ (b2) => existsSync19(join28(projectDir, b2.path)) ? b2 : { ...b2, removed: true }
4928
5037
  );
4929
5038
  const noChecksum = report.backups.map((b2) => b2.path).filter((p2) => unmeasured.has(p2));
4930
5039
  if (noChecksum.length > 0) report.noChecksum = noChecksum;
@@ -5007,7 +5116,7 @@ function reclaimOutOfTrackRecorded(projectDir, outside) {
5007
5116
  function deadMcpLeft(projectDir) {
5008
5117
  let servers;
5009
5118
  try {
5010
- servers = JSON.parse(readFileSync20(join27(projectDir, ".mcp.json"), "utf8")).mcpServers;
5119
+ servers = JSON.parse(readFileSync20(join28(projectDir, ".mcp.json"), "utf8")).mcpServers;
5011
5120
  } catch {
5012
5121
  return [];
5013
5122
  }
@@ -5022,7 +5131,7 @@ function retiredFeaturesPart(projectDir, f) {
5022
5131
  }
5023
5132
  function legacyHookOnDisk(projectDir, rel) {
5024
5133
  try {
5025
- return hasLegacyHarnessHook(readFileSync20(join27(projectDir, rel), "utf8"));
5134
+ return hasLegacyHarnessHook(readFileSync20(join28(projectDir, rel), "utf8"));
5026
5135
  } catch {
5027
5136
  return false;
5028
5137
  }
@@ -5048,7 +5157,7 @@ function writeUpdateSharedFiles(args) {
5048
5157
  excluded: adapterExcluded(harnessRoot, excluded)
5049
5158
  });
5050
5159
  const tracks = installedTracks(projectDir);
5051
- const settingsSource = join27(templatesDir, "settings.json");
5160
+ const settingsSource = join28(templatesDir, "settings.json");
5052
5161
  if (args.claudeManaged && wants("hooks") && existsSync19(settingsSource)) {
5053
5162
  const spec = buildAssetSpec({ tracks, options: DEFAULT_OPTIONS });
5054
5163
  const manifest = buildManifest(spec);
@@ -5123,8 +5232,8 @@ function catalogRemovedOnRecord(log) {
5123
5232
  }
5124
5233
  function staleSkillDirs(projectDir, claudeDir, clis) {
5125
5234
  const stale = (id) => RENAMED_SKILL_IDS[id] !== void 0 || RETIRED_SKILL_IDS.includes(id);
5126
- const dirs = [join27(claudeDir, "skills")];
5127
- if (clis.some((cli) => cli !== "claude")) dirs.push(join27(projectDir, ".agents", "skills"));
5235
+ const dirs = [join28(claudeDir, "skills")];
5236
+ if (clis.some((cli) => cli !== "claude")) dirs.push(join28(projectDir, ".agents", "skills"));
5128
5237
  const ids = /* @__PURE__ */ new Set();
5129
5238
  for (const dir of dirs) {
5130
5239
  if (!existsSync19(dir)) continue;
@@ -5135,13 +5244,13 @@ function staleSkillDirs(projectDir, claudeDir, clis) {
5135
5244
  return [...ids];
5136
5245
  }
5137
5246
  function staleAgentFiles(claudeDir) {
5138
- const dir = join27(claudeDir, "agents");
5247
+ const dir = join28(claudeDir, "agents");
5139
5248
  if (!existsSync19(dir)) return [];
5140
5249
  return readdirSync4(dir, { withFileTypes: true }).filter((e2) => e2.isFile() && e2.name.endsWith(".md")).map((e2) => basename5(e2.name, ".md")).filter((id) => RETIRED_AGENT_IDS.includes(id));
5141
5250
  }
5142
5251
  function demotedAgentFiles(claudeDir, tracks) {
5143
5252
  if (tracks.length === 0) return [];
5144
- const dir = join27(claudeDir, "agents");
5253
+ const dir = join28(claudeDir, "agents");
5145
5254
  if (!existsSync19(dir)) return [];
5146
5255
  const demoted = TRACK_AGENTS.filter(([, pattern]) => !anyTrack(tracks, pattern)).map(
5147
5256
  ([id]) => id
@@ -5157,19 +5266,19 @@ function updateHarnessTargets(projectDir, templatesDir, log, wants, report, fore
5157
5266
  if (!wants(e2.target.startsWith(".claude/hooks/") ? "hooks" : "rules")) continue;
5158
5267
  if (e2.target.startsWith(".claude/") && !claude) continue;
5159
5268
  if (isBaselineExcluded(e2.target, excluded)) continue;
5160
- const source = join27(templatesDir, e2.source);
5269
+ const source = join28(templatesDir, e2.source);
5161
5270
  if (!existsSync19(source)) continue;
5162
5271
  const policy = POLICY_DIRS.find((d2) => e2.target.startsWith(`.claude/${d2.dir}/`));
5163
5272
  files.push({
5164
5273
  path: e2.target,
5165
5274
  source,
5166
5275
  unit: e2.target,
5167
- unitExisted: existsSync19(join27(projectDir, e2.target)),
5276
+ unitExisted: existsSync19(join28(projectDir, e2.target)),
5168
5277
  label: policy === void 0 ? e2.target : `.claude/${policy.dir}`
5169
5278
  });
5170
5279
  }
5171
5280
  const refreshedSkills = [];
5172
- const skillsSource = join27(templatesDir, "skills");
5281
+ const skillsSource = join28(templatesDir, "skills");
5173
5282
  if (!claude || !(wants("skills") || wants("new-skills")) || !existsSync19(skillsSource)) {
5174
5283
  return { files, refreshedSkills };
5175
5284
  }
@@ -5179,20 +5288,20 @@ function updateHarnessTargets(projectDir, templatesDir, log, wants, report, fore
5179
5288
  for (const d2 of readdirSync4(skillsSource, { withFileTypes: true })) {
5180
5289
  if (!d2.isDirectory()) continue;
5181
5290
  const unit = `.claude/skills/${d2.name}`;
5182
- if (occupiedByNonDirectory(join27(projectDir, unit))) {
5291
+ if (occupiedByNonDirectory(join28(projectDir, unit))) {
5183
5292
  report.skillsSkippedLinks.push(d2.name);
5184
5293
  continue;
5185
5294
  }
5186
5295
  if (!ids.has(d2.name) || excludedSkill(d2.name)) continue;
5187
- const existed = existsSync19(join27(projectDir, unit));
5296
+ const existed = existsSync19(join28(projectDir, unit));
5188
5297
  if (existed ? !wants("skills") : !wants("new-skills") || !creatable.has(d2.name)) continue;
5189
- for (const rel of listFilesRecursive(join27(skillsSource, d2.name))) {
5298
+ for (const rel of listFilesRecursive(join28(skillsSource, d2.name))) {
5190
5299
  const foreign = foreignOwnedTarget(projectDir, `${unit}/${rel}`);
5191
5300
  if (foreign !== null) {
5192
5301
  if (!foreignOwned.includes(foreign)) foreignOwned.push(foreign);
5193
5302
  continue;
5194
5303
  }
5195
- const source = join27(skillsSource, d2.name, rel);
5304
+ const source = join28(skillsSource, d2.name, rel);
5196
5305
  files.push({
5197
5306
  path: `${unit}/${rel}`,
5198
5307
  source,
@@ -5230,8 +5339,8 @@ function updateHarnessFiles(args) {
5230
5339
  report.updated[label] = 0;
5231
5340
  const ctx = { prefix: dir, outside, recordOf, projectDir, judged };
5232
5341
  report.pruned[label] = pruneOrphans(
5233
- join27(projectDir, label),
5234
- join27(templatesDir, dir),
5342
+ join28(projectDir, label),
5343
+ join28(templatesDir, dir),
5235
5344
  ext,
5236
5345
  ctx,
5237
5346
  now
@@ -5299,7 +5408,7 @@ function updateHarnessFiles(args) {
5299
5408
  function removedRow(projectDir, path, why, judged, backupAbs) {
5300
5409
  const { line, recorded: recorded2 } = judged;
5301
5410
  if (backupAbs === null) return { path, verdict: "remove", recorded: recorded2, line: `${why} \u2014 ${line}` };
5302
- const backup = relative5(projectDir, backupAbs).split(sep6).join("/");
5411
+ const backup = relative6(projectDir, backupAbs).split(sep7).join("/");
5303
5412
  return {
5304
5413
  path,
5305
5414
  verdict: "backup+remove",
@@ -5315,12 +5424,12 @@ function pruneSkillExtras(projectDir, templatesDir, ids, outside, recordOf, now,
5315
5424
  const backedUp = [];
5316
5425
  const failed = [];
5317
5426
  for (const id of ids) {
5318
- const targetSkill = join27(projectDir, ".claude/skills", id);
5319
- const bundled = new Set(listFilesRecursive(join27(templatesDir, "skills", id)));
5427
+ const targetSkill = join28(projectDir, ".claude/skills", id);
5428
+ const bundled = new Set(listFilesRecursive(join28(templatesDir, "skills", id)));
5320
5429
  for (const rel of listFilesRecursive(targetSkill)) {
5321
5430
  if (bundled.has(rel) || basename5(rel).includes(".backup-")) continue;
5322
5431
  if (foreignOwnedTarget(projectDir, `.claude/skills/${id}/${rel}`) !== null) continue;
5323
- const targetFile = join27(targetSkill, rel);
5432
+ const targetFile = join28(targetSkill, rel);
5324
5433
  if (outside.skip(targetFile)) continue;
5325
5434
  const path = `.claude/skills/${id}/${rel}`;
5326
5435
  const rec = recordOf(path);
@@ -5395,20 +5504,20 @@ function collectRunBackups(projectDir, startedAt, exclude) {
5395
5504
  const consider = (rel) => {
5396
5505
  if (!BACKUP_SUFFIX.test(rel)) return;
5397
5506
  if (exclude?.has(rel)) return;
5398
- const abs = join27(projectDir, rel);
5507
+ const abs = join28(projectDir, rel);
5399
5508
  if (lstatSync6(abs).mtimeMs < startedAt - 1e3) return;
5400
5509
  out.push({ path: rel.replace(BACKUP_SUFFIX, ""), backup: rel });
5401
5510
  };
5402
5511
  for (const e2 of withoutAppleDouble(readdirSync4(projectDir, { withFileTypes: true })))
5403
5512
  if (e2.isFile()) consider(e2.name);
5404
5513
  for (const dir of BACKUP_SCAN_DIRS) {
5405
- for (const { rel } of listEntriesRecursive(join27(projectDir, dir))) consider(`${dir}/${rel}`);
5514
+ for (const { rel } of listEntriesRecursive(join28(projectDir, dir))) consider(`${dir}/${rel}`);
5406
5515
  }
5407
5516
  return out.sort((a, b2) => a.path.localeCompare(b2.path));
5408
5517
  }
5409
5518
  function writeBackupList(projectDir, backups, noChecksum = /* @__PURE__ */ new Set()) {
5410
- const dir = join27(projectDir, ".uzys-agent-harness");
5411
- const file = join27(dir, "update-backups.json");
5519
+ const dir = join28(projectDir, ".uzys-agent-harness");
5520
+ const file = join28(dir, "update-backups.json");
5412
5521
  try {
5413
5522
  if (backups.length === 0) {
5414
5523
  if (existsSync19(file)) unlinkSync2(file);
@@ -5440,12 +5549,12 @@ function trackOnlyFileAssets(tracks) {
5440
5549
  );
5441
5550
  }
5442
5551
  function syncHarnessAnchor(projectDir, templatesDir, report, outside, judged, now = /* @__PURE__ */ new Date()) {
5443
- if (existsSync19(join27(projectDir, LEGACY_ANCHOR_FILE2))) {
5552
+ if (existsSync19(join28(projectDir, LEGACY_ANCHOR_FILE2))) {
5444
5553
  report.legacyAnchor = LEGACY_ANCHOR_FILE2;
5445
5554
  }
5446
- const templateMd = join27(templatesDir, "CLAUDE.md");
5555
+ const templateMd = join28(templatesDir, "CLAUDE.md");
5447
5556
  if (!existsSync19(templateMd)) return;
5448
- const anchor = join27(projectDir, HARNESS_ANCHOR_FILE);
5557
+ const anchor = join28(projectDir, HARNESS_ANCHOR_FILE);
5449
5558
  if (outside.skip(anchor)) return;
5450
5559
  const existed = existsSync19(anchor);
5451
5560
  if (existed) {
@@ -5456,7 +5565,7 @@ function syncHarnessAnchor(projectDir, templatesDir, report, outside, judged, no
5456
5565
  if (!owned) {
5457
5566
  const backup = backupFile(anchor, now);
5458
5567
  report.anchorBackedUp = true;
5459
- const rel = relative5(projectDir, backup).split(sep6).join("/");
5568
+ const rel = relative6(projectDir, backup).split(sep7).join("/");
5460
5569
  const rec = recorded2?.path === HARNESS_ANCHOR_FILE ? { state: "sha", sha256: recorded2.sha256 } : { state: "no-sha" };
5461
5570
  const next = readFileSync20(templateMd, "utf8");
5462
5571
  const { line } = judge({ op: "write", kind: "harness", rec, disk: current, next });
@@ -5483,7 +5592,7 @@ function syncHarnessAnchor(projectDir, templatesDir, report, outside, judged, no
5483
5592
  recordAnchorBaseline(projectDir, anchor);
5484
5593
  }
5485
5594
  function upsertRootImport(projectDir, outside) {
5486
- const target = join27(projectDir, "CLAUDE.md");
5595
+ const target = join28(projectDir, "CLAUDE.md");
5487
5596
  if (outside.skip(target)) return false;
5488
5597
  const existing = existsSync19(target) ? readFileSync20(target, "utf8") : null;
5489
5598
  const next = upsertHarnessImport(existing, {
@@ -5491,7 +5600,7 @@ function upsertRootImport(projectDir, outside) {
5491
5600
  // ADR-085 — update 는 선택 목록을 인자로 받지 않는다. 앞 단계(하네스 파일 쓰기)가 지나간 디스크로 판정한다:
5492
5601
  // `.claude/skills/<id>` 가 있으면 그 스킬은 이 설치가 고른 것이다.
5493
5602
  continuousSkills: CONTINUOUS_SKILLS.map((s) => s.id).filter(
5494
- (id) => existsSync19(join27(projectDir, ".claude/skills", id))
5603
+ (id) => existsSync19(join28(projectDir, ".claude/skills", id))
5495
5604
  )
5496
5605
  });
5497
5606
  if (next === existing) return false;
@@ -5526,7 +5635,7 @@ function excludedBaselineOnDisk(projectDir, log) {
5526
5635
  if (excluded.size === 0) return [];
5527
5636
  const spec = buildAssetSpec({ tracks: installedTracks(projectDir), options: DEFAULT_OPTIONS });
5528
5637
  return buildManifest(spec).filter(
5529
- (e2) => e2.applies(spec) && isBaselineExcluded(e2.target, excluded) && existsSync19(join27(projectDir, e2.target))
5638
+ (e2) => e2.applies(spec) && isBaselineExcluded(e2.target, excluded) && existsSync19(join28(projectDir, e2.target))
5530
5639
  ).map((e2) => e2.target);
5531
5640
  }
5532
5641
  function excludedSkillOf(log) {
@@ -5554,7 +5663,7 @@ function recordedRenderInputs(projectDir, log, tracks = installedTracks(projectD
5554
5663
  function recordedBundledSkills(projectDir, log) {
5555
5664
  if (log === null) {
5556
5665
  return INTERNAL_BUNDLED_SKILL_IDS.filter(
5557
- (id) => existsSync19(join27(projectDir, ".claude/skills", id)) || existsSync19(join27(projectDir, ".agents/skills", id))
5666
+ (id) => existsSync19(join28(projectDir, ".claude/skills", id)) || existsSync19(join28(projectDir, ".agents/skills", id))
5558
5667
  );
5559
5668
  }
5560
5669
  const ids = recordedSkillIds(projectDir, log);
@@ -5605,9 +5714,9 @@ var SHARED_SKILLS_DIR = ".agents/skills";
5605
5714
  var SHARED_SKILL_FILE = /^\.agents\/skills\/([^/]+)\//;
5606
5715
  var SHARED_SKILL_MD = /^\.agents\/skills\/([^/]+)\/SKILL\.md$/;
5607
5716
  function sharedSkillIdsOnDisk(projectDir) {
5608
- const root = join27(projectDir, SHARED_SKILLS_DIR);
5717
+ const root = join28(projectDir, SHARED_SKILLS_DIR);
5609
5718
  if (!existsSync19(root)) return /* @__PURE__ */ new Set();
5610
- return new Set(readdirSync4(root).filter((id) => existsSync19(join27(root, id, "SKILL.md"))));
5719
+ return new Set(readdirSync4(root).filter((id) => existsSync19(join28(root, id, "SKILL.md"))));
5611
5720
  }
5612
5721
  var CREATED_SHARED_HOW = {
5613
5722
  "opencode.json": "created from template (harness part + template defaults)",
@@ -5634,14 +5743,14 @@ function refreshExternalCli(projectDir, harnessRoot, droppedKeys, harnessMcp, li
5634
5743
  const log = readInstallLog(projectDir);
5635
5744
  const presentBefore = sharedSkillIdsOnDisk(projectDir);
5636
5745
  const rulesBefore = /* @__PURE__ */ new Set();
5637
- const rulesDir = join27(projectDir, ".agents", "rules");
5746
+ const rulesDir = join28(projectDir, ".agents", "rules");
5638
5747
  if (existsSync19(rulesDir)) {
5639
5748
  try {
5640
5749
  for (const f of readdirSync4(rulesDir)) rulesBefore.add(f);
5641
5750
  } catch {
5642
5751
  }
5643
5752
  }
5644
- const agentsMdBefore = existsSync19(join27(projectDir, "AGENTS.md"));
5753
+ const agentsMdBefore = existsSync19(join28(projectDir, "AGENTS.md"));
5645
5754
  const result = runCliTransforms({
5646
5755
  harnessRoot,
5647
5756
  projectDir,
@@ -5780,7 +5889,7 @@ function refreshExternalCli(projectDir, harnessRoot, droppedKeys, harnessMcp, li
5780
5889
  ],
5781
5890
  anchorRetired: result.anchorRetired,
5782
5891
  retiredCommands: (result.opencode?.retiredCommands ?? []).map(
5783
- (p2) => relative5(projectDir, p2).split(sep6).join("/")
5892
+ (p2) => relative6(projectDir, p2).split(sep7).join("/")
5784
5893
  ),
5785
5894
  anchorDisplaced,
5786
5895
  agentsMd: agentsMdNews(result.agentsMd, agentsMdBefore, priorPaths),
@@ -5818,14 +5927,15 @@ function pruneOrphans(target, source, ext, ctx, now) {
5818
5927
  if (!existsSync19(target) || !existsSync19(source)) return [];
5819
5928
  const removed = [];
5820
5929
  for (const file of readdirSync4(target)) {
5821
- if (!file.endsWith(ext) || existsSync19(join27(source, file))) continue;
5822
- const targetFile = join27(target, file);
5930
+ if (!file.endsWith(ext) || existsSync19(join28(source, file))) continue;
5931
+ const targetFile = join28(target, file);
5823
5932
  const path = `.claude/${ctx.prefix}/${file}`;
5824
5933
  const rec = ctx.recordOf(path);
5825
5934
  try {
5826
5935
  const disk = readFileSync20(targetFile, "utf8");
5827
5936
  const { verdict, line } = judge({ op: "remove", kind: "harness", rec, disk, next: null });
5828
5937
  if (verdict === "leave" || ctx.outside.skip(targetFile)) continue;
5938
+ if (!inHarnessSlot(ctx.projectDir, path, HARNESS_FILES, { follow: true })) continue;
5829
5939
  const backupAbs = verdict === "backup+remove" ? backupFile(targetFile, now) : null;
5830
5940
  unlinkSync2(targetFile);
5831
5941
  removed.push(file);
@@ -5844,7 +5954,7 @@ function pruneOrphans(target, source, ext, ctx, now) {
5844
5954
  return removed;
5845
5955
  }
5846
5956
  function retireMcpAllowlist(projectDir, now = /* @__PURE__ */ new Date()) {
5847
- const target = join27(projectDir, ".mcp-allowlist");
5957
+ const target = join28(projectDir, ".mcp-allowlist");
5848
5958
  if (!existsSync19(target)) return null;
5849
5959
  try {
5850
5960
  const backup = backupFile(target, now);
@@ -5866,11 +5976,11 @@ function recordClaudeCommand(log, tracks) {
5866
5976
  function runInstall(ctx) {
5867
5977
  const { harnessRoot, projectDir } = ctx;
5868
5978
  const mode = ctx.mode ?? "fresh";
5869
- const templatesDir = join28(harnessRoot, "templates");
5979
+ const templatesDir = join29(harnessRoot, "templates");
5870
5980
  if (!existsSync20(templatesDir)) {
5871
5981
  throw new Error(`Templates dir not found: ${templatesDir}`);
5872
5982
  }
5873
- const claudeDir = join28(projectDir, ".claude");
5983
+ const claudeDir = join29(projectDir, ".claude");
5874
5984
  const previousLog = readInstallLog(projectDir);
5875
5985
  const legacyAnchor = mode === "update" ? null : legacyAnchorTrace(projectDir, ctx.spec);
5876
5986
  if (mode === "update" && previousLog === null) {
@@ -5895,13 +6005,13 @@ function runInstall(ctx) {
5895
6005
  const manifestSpec = buildManifestSpec(spec);
5896
6006
  const baselineExcluded = new Set(spec.baselineExclude ?? []);
5897
6007
  if (spec.cli.includes("claude")) {
5898
- const settingsPath = join28(projectDir, ".claude", "settings.json");
6008
+ const settingsPath = join29(projectDir, ".claude", "settings.json");
5899
6009
  if (existsSync20(settingsPath)) {
5900
6010
  try {
5901
6011
  JSON.parse(readFileSync21(settingsPath, "utf8"));
5902
6012
  } catch {
5903
6013
  throw new Error(
5904
- `${relative6(process.cwd(), settingsPath)} is not valid JSON \u2014 left untouched, so no hooks were wired. Fix or delete that file, then run the install again`
6014
+ `${relative7(process.cwd(), settingsPath)} is not valid JSON \u2014 left untouched, so no hooks were wired. Fix or delete that file, then run the install again`
5905
6015
  );
5906
6016
  }
5907
6017
  }
@@ -6195,7 +6305,7 @@ function releasedThisRun(args) {
6195
6305
  }
6196
6306
  if (id.startsWith(BASELINE_PREFIX)) {
6197
6307
  const again2 = manifest.some(
6198
- (e2) => e2.applies(manifestSpec) && classifyBaselineTarget(e2.target)?.id === id && existsSync20(join28(projectDir, e2.target))
6308
+ (e2) => e2.applies(manifestSpec) && classifyBaselineTarget(e2.target)?.id === id && existsSync20(join29(projectDir, e2.target))
6199
6309
  );
6200
6310
  return { id, again: again2, tail: "the harness manages it again" };
6201
6311
  }
@@ -6204,7 +6314,7 @@ function releasedThisRun(args) {
6204
6314
  options: spec.options,
6205
6315
  ...spec.userOverride ? { userOverride: spec.userOverride } : {}
6206
6316
  });
6207
- const recorded2 = (previousLog?.assets.some((a) => a.id === id) ?? false) || [".claude/skills", ".agents/skills"].some((d2) => existsSync20(join28(projectDir, d2, id)));
6317
+ const recorded2 = (previousLog?.assets.some((a) => a.id === id) ?? false) || [".claude/skills", ".agents/skills"].some((d2) => existsSync20(join29(projectDir, d2, id)));
6208
6318
  return {
6209
6319
  id,
6210
6320
  again,
@@ -6216,10 +6326,10 @@ function pendingKeyExcludes(projectDir, keyExclude, touched) {
6216
6326
  const done = new Set(touched);
6217
6327
  return keyExclude.flatMap((id) => {
6218
6328
  const path = sharedPathOfKeyId(id);
6219
- if (path === null || done.has(path) || !existsSync20(join28(projectDir, path))) return [];
6329
+ if (path === null || done.has(path) || !existsSync20(join29(projectDir, path))) return [];
6220
6330
  const name = adapterFor(path);
6221
6331
  const keys = [...excludedKeys(path, [id])];
6222
- const present = name === null ? null : ADAPTERS[name].read(readFileSync21(join28(projectDir, path), "utf8"), keys, projectDir);
6332
+ const present = name === null ? null : ADAPTERS[name].read(readFileSync21(join29(projectDir, path), "utf8"), keys, projectDir);
6223
6333
  return present !== null && present.size === 0 ? [] : [{ id, path }];
6224
6334
  });
6225
6335
  }
@@ -6263,7 +6373,7 @@ function installCliNeutralAssets(manifestSpec, templatesDir, baselineExcluded, w
6263
6373
  continue;
6264
6374
  }
6265
6375
  if (!isCliNeutralTarget(entry.target)) continue;
6266
- const source = join28(templatesDir, entry.source);
6376
+ const source = join29(templatesDir, entry.source);
6267
6377
  if (!existsSync20(source)) {
6268
6378
  result.skipped += 1;
6269
6379
  continue;
@@ -6284,13 +6394,13 @@ function installClaudeBaseline(manifestSpec, projectDir, templatesDir, baselineE
6284
6394
  if (!entry.applies(manifestSpec)) {
6285
6395
  continue;
6286
6396
  }
6287
- const target = join28(projectDir, entry.target);
6397
+ const target = join29(projectDir, entry.target);
6288
6398
  if (isBaselineExcluded(entry.target, baselineExcluded)) {
6289
6399
  result.excluded.push(entry.target);
6290
6400
  if (existsSync20(target)) result.excludedOnDisk.push(entry.target);
6291
6401
  continue;
6292
6402
  }
6293
- const source = join28(templatesDir, entry.source);
6403
+ const source = join29(templatesDir, entry.source);
6294
6404
  if (!existsSync20(source)) {
6295
6405
  result.skipped += 1;
6296
6406
  continue;
@@ -6321,18 +6431,18 @@ function installClaudeBaseline(manifestSpec, projectDir, templatesDir, baselineE
6321
6431
  if (!result.foreignOwned.includes(slot)) result.foreignOwned.push(slot);
6322
6432
  continue;
6323
6433
  }
6324
- if (writer.skipOutside(join28(projectDir, entry.target, rel))) {
6434
+ if (writer.skipOutside(join29(projectDir, entry.target, rel))) {
6325
6435
  leftOutside += 1;
6326
6436
  continue;
6327
6437
  }
6328
- writer.harness(`${entry.target}/${rel}`, { source: join28(source, rel) });
6438
+ writer.harness(`${entry.target}/${rel}`, { source: join29(source, rel) });
6329
6439
  }
6330
6440
  if (leftOutside > 0) continue;
6331
6441
  result.dirsCopied += 1;
6332
6442
  }
6333
6443
  accumulateCategory(result.categories, entry);
6334
6444
  }
6335
- const hookDir = join28(projectDir, ".claude/hooks");
6445
+ const hookDir = join29(projectDir, ".claude/hooks");
6336
6446
  if (existsSync20(hookDir)) {
6337
6447
  chmodHooksSync(hookDir, projectDir);
6338
6448
  }
@@ -6354,8 +6464,8 @@ function installClaudeBaseline(manifestSpec, projectDir, templatesDir, baselineE
6354
6464
  });
6355
6465
  result.keptHookRefs.push(
6356
6466
  ...findStaleHookRefs(
6357
- join28(projectDir, ".claude", "settings.json"),
6358
- join28(projectDir, ".claude")
6467
+ join29(projectDir, ".claude", "settings.json"),
6468
+ join29(projectDir, ".claude")
6359
6469
  )
6360
6470
  );
6361
6471
  }
@@ -6364,7 +6474,7 @@ function installClaudeBaseline(manifestSpec, projectDir, templatesDir, baselineE
6364
6474
  // ADR-085 · update(`upsertRootImport`)와 같은 판정 — 실제로 깔린 스킬만 안내한다. 밖 링크라 쓰지 않은
6365
6475
  // 스킬(ADR-098)을 "installed" 라 적으면 다음 update 가 그 절을 걷어 내 두 동작이 서로 반대로 쓴다.
6366
6476
  (manifestSpec.selectedInternalSkills ?? []).filter(
6367
- (id) => existsSync20(join28(projectDir, ".claude", "skills", id))
6477
+ (id) => existsSync20(join29(projectDir, ".claude", "skills", id))
6368
6478
  ),
6369
6479
  harnessRoot,
6370
6480
  writer
@@ -6430,9 +6540,12 @@ function runExternalPhase(ctx, previousLog) {
6430
6540
  ctx.onProgress?.({ type: "external-start", assetCount: targets.length });
6431
6541
  const skills = targets.filter((a) => a.method.kind === "skill");
6432
6542
  const skillIds = new Set(skills.map((a) => a.id));
6543
+ const slotCtx = { log: previousLog, scope: spec.scope };
6433
6544
  const settle = holdSkillSlots(projectDir, {
6434
6545
  recorded: (previousLog?.assets ?? []).filter((a) => a.method === "skill" && skillIds.has(a.id)),
6435
- slots: skillSlotsFor(skills, spec.cli, { log: previousLog, scope: spec.scope }),
6546
+ slots: skillSlotsFor(skills, spec.cli, slotCtx),
6547
+ // #902 — 옛 판이 파일 목록 없이 깐 자산의 자리 — 그 안 파일은 되살리지 않고 백업만 남긴다
6548
+ unlisted: unlistedSkillSlots(skills, spec.cli, slotCtx),
6436
6549
  log: previousLog,
6437
6550
  clis: spec.cli,
6438
6551
  scope: spec.scope
@@ -6451,7 +6564,7 @@ function runExternalPhase(ctx, previousLog) {
6451
6564
  throw e2;
6452
6565
  }
6453
6566
  const { report: external, backups, displaced } = settle(ran);
6454
- const displacedBackups = new Set(displaced.map((f) => join28(projectDir, f.notes[0] ?? "")));
6567
+ const displacedBackups = new Set(displaced.map((f) => join29(projectDir, f.notes[0] ?? "")));
6455
6568
  const editBackups = backups.filter((b2) => !displacedBackups.has(b2));
6456
6569
  ctx.onProgress?.({
6457
6570
  type: "external-complete",
@@ -6524,7 +6637,7 @@ function recordInterruptedInstall(ctx, error, run) {
6524
6637
  const codexTrust = registeredTrust(journal.codexOptIn, ctx.projectDir);
6525
6638
  const written = writtenSoFar(ledger, cliFiles, rootFiles, stage.rootImportWritten);
6526
6639
  if (codexTrust) written.push(`${codexTrust.configPath} (Codex trust entry for this folder)`);
6527
- const projectRel = (abs) => relative6(ctx.projectDir, abs).split(sep7).join("/");
6640
+ const projectRel = (abs) => relative7(ctx.projectDir, abs).split(sep8).join("/");
6528
6641
  const writtenSet = new Set(written);
6529
6642
  const backups = [...own.backups, ...journal.backups].map((abs) => {
6530
6643
  const original = projectRel(backupOriginal(abs) ?? abs);
@@ -6664,7 +6777,7 @@ function accumulateCategory(cats, entry) {
6664
6777
  }
6665
6778
  }
6666
6779
  function writeRootClaudeMd(projectDir, continuousSkills, harnessRoot, writer) {
6667
- const target = join28(projectDir, "CLAUDE.md");
6780
+ const target = join29(projectDir, "CLAUDE.md");
6668
6781
  if (writer.skipOutside(target)) return null;
6669
6782
  const existing = existsSync20(target) ? readFileSync21(target, "utf-8") : null;
6670
6783
  const seeded = existing === null ? seedRootClaudeProjectContext(projectDir, harnessRoot) : null;
@@ -6699,7 +6812,7 @@ function listHookFiles(hookDir) {
6699
6812
  // src/commands/install-render.ts
6700
6813
  init_esm_shims();
6701
6814
  import { existsSync as existsSync22, readdirSync as readdirSync6, readFileSync as readFileSync23 } from "fs";
6702
- import { join as join30, relative as relative7 } from "path";
6815
+ import { join as join31, relative as relative8 } from "path";
6703
6816
 
6704
6817
  // src/preset-recommend.ts
6705
6818
  init_esm_shims();
@@ -6721,16 +6834,21 @@ function trackDefaultAssets(tracks) {
6721
6834
  ])
6722
6835
  ];
6723
6836
  }
6724
- function finalSelectedAssets(tracks, userOverride) {
6837
+ function finalSelectedAssets(tracks, userOverride, keyExclude = []) {
6725
6838
  const selected = new Set(trackDefaultAssets(tracks));
6726
6839
  if (userOverride) {
6727
6840
  for (const id of userOverride.forceExclude) selected.delete(id);
6728
6841
  for (const id of userOverride.forceInclude) selected.add(id);
6729
6842
  }
6843
+ if (keyExclude.length > 0) {
6844
+ for (const id of droppedMcpAssetIds(resolveBundleRoot(), new Set(keyExclude))) {
6845
+ selected.delete(id);
6846
+ }
6847
+ }
6730
6848
  return [...selected].sort();
6731
6849
  }
6732
6850
  function residentSelectedAssets(spec) {
6733
- return finalSelectedAssets(spec.tracks, spec.userOverride).filter((id) => {
6851
+ return finalSelectedAssets(spec.tracks, spec.userOverride, spec.keyExclude).filter((id) => {
6734
6852
  const asset = EXTERNAL_ASSETS.find((a) => a.id === id);
6735
6853
  return asset === void 0 || assetReachesCli(asset, spec.cli);
6736
6854
  });
@@ -6750,9 +6868,9 @@ function groupAssetsByCategory(assetIds) {
6750
6868
  // src/resident-entries.ts
6751
6869
  init_esm_shims();
6752
6870
  import { existsSync as existsSync21, readFileSync as readFileSync22 } from "fs";
6753
- import { basename as basename7, join as join29 } from "path";
6871
+ import { basename as basename7, join as join30 } from "path";
6754
6872
  function residentEntries(spec) {
6755
- const skillThere = (id) => INTERNAL_BUNDLED_SKILL_IDS.includes(id) && [".claude/skills", ".agents/skills"].some((d2) => existsSync21(join29(spec.projectDir, d2, id)));
6873
+ const skillThere = (id) => INTERNAL_BUNDLED_SKILL_IDS.includes(id) && [".claude/skills", ".agents/skills"].some((d2) => existsSync21(join30(spec.projectDir, d2, id)));
6756
6874
  const forceExclude = (spec.userOverride?.forceExclude ?? []).filter((id) => !skillThere(id));
6757
6875
  const assetSpec = buildManifestSpec(
6758
6876
  spec.userOverride ? { ...spec, userOverride: { ...spec.userOverride, forceExclude } } : spec
@@ -6778,7 +6896,7 @@ function residentEntries(spec) {
6778
6896
  function residentCostFor(spec, entries = residentEntries(spec), contextFiles) {
6779
6897
  if (contextFiles !== void 0) return residentCost(entries, void 0, spec.cli, contextFiles);
6780
6898
  const cost = residentCost(entries, void 0, spec.cli, plannedClaudeContext(spec));
6781
- if (anchorSurface(spec.cli).scaffold === "agents-md" && existsSync21(join29(spec.projectDir, "AGENTS.md"))) {
6899
+ if (anchorSurface(spec.cli).scaffold === "agents-md" && existsSync21(join30(spec.projectDir, "AGENTS.md"))) {
6782
6900
  return makeResidentCost({
6783
6901
  ...cost,
6784
6902
  contextNote: "planned, not counting text already in AGENTS.md"
@@ -6789,7 +6907,7 @@ function residentCostFor(spec, entries = residentEntries(spec), contextFiles) {
6789
6907
  function plannedClaudeContext(spec) {
6790
6908
  if (!spec.cli.includes("claude")) return void 0;
6791
6909
  const root = resolveBundleRoot();
6792
- const rootClaude = join29(spec.projectDir, "CLAUDE.md");
6910
+ const rootClaude = join30(spec.projectDir, "CLAUDE.md");
6793
6911
  const existing = existsSync21(rootClaude) ? readFileSync22(rootClaude, "utf-8") : null;
6794
6912
  const seeded = existing === null ? seedRootClaudeProjectContext(spec.projectDir, root) : null;
6795
6913
  if (existing === null && seeded === null) return void 0;
@@ -6799,14 +6917,14 @@ function plannedClaudeContext(spec) {
6799
6917
  continuousSkills: manifestSpec.selectedInternalSkills ?? [],
6800
6918
  ...seeded === null ? {} : { projectContext: seeded }
6801
6919
  });
6802
- return [join29(root, "templates", "CLAUDE.md"), { text }];
6920
+ return [join30(root, "templates", "CLAUDE.md"), { text }];
6803
6921
  }
6804
6922
  function onDiskTargets(spec, assetSpec) {
6805
6923
  const out = /* @__PURE__ */ new Set();
6806
6924
  for (const e2 of buildManifest(assetSpec)) {
6807
6925
  if (!e2.applies(assetSpec)) continue;
6808
6926
  const t = classifyBaselineTarget(e2.target);
6809
- if (t !== null && existsSync21(join29(spec.projectDir, e2.target))) out.add(t.id);
6927
+ if (t !== null && existsSync21(join30(spec.projectDir, e2.target))) out.add(t.id);
6810
6928
  }
6811
6929
  return out;
6812
6930
  }
@@ -6831,7 +6949,7 @@ function renderInstallHeader(log, spec, mode) {
6831
6949
  );
6832
6950
  }
6833
6951
  log(infoRow("OPTIONS", formatOptions(spec)));
6834
- const finalAssets = finalSelectedAssets(spec.tracks, spec.userOverride);
6952
+ const finalAssets = finalSelectedAssets(spec.tracks, spec.userOverride, spec.keyExclude);
6835
6953
  if (finalAssets.length > 0) {
6836
6954
  const unreachable = finalAssets.filter((id) => {
6837
6955
  const asset = EXTERNAL_ASSETS.find((a) => a.id === id);
@@ -6993,23 +7111,23 @@ function residentEntriesOnDisk(spec) {
6993
7111
  const hasClaude = spec.cli.includes("claude");
6994
7112
  const surface = anchorSurface(spec.cli).scaffold;
6995
7113
  const mdFiles = (dir, kind, skip) => {
6996
- const abs = join30(spec.projectDir, dir);
7114
+ const abs = join31(spec.projectDir, dir);
6997
7115
  return existsSync22(abs) ? readdirSync6(abs, { withFileTypes: true }).filter((e2) => e2.isFile() && e2.name.endsWith(".md") && e2.name !== skip).map((e2) => ({
6998
7116
  source: `${kind}/${e2.name}`,
6999
7117
  // `target` 은 `residentCost` 의 분류 키다 — 실제 자리는 `file` 이 말한다.
7000
7118
  target: `.claude/${kind}/${e2.name}`,
7001
7119
  // 배포판이 아니라 **이 프로젝트의 파일**을 잰다 — 사용자가 고친 본문도,
7002
7120
  // 배포판에 더는 없는 파일도 실제로 상주하는 것은 이쪽이다.
7003
- file: join30(abs, e2.name)
7121
+ file: join31(abs, e2.name)
7004
7122
  })) : [];
7005
7123
  };
7006
- const rules = surface === "claude" ? mdFiles(join30(".claude", "rules"), "rules") : rulesInline(spec.cli) ? planned.filter((e2) => e2.target.startsWith(".claude/rules/")) : mdFiles(join30(".agents", "rules"), "rules", "uzys-harness.md");
7007
- const slot = hasClaude ? join30(".claude", "skills") : join30(".agents", "skills");
7124
+ const rules = surface === "claude" ? mdFiles(join31(".claude", "rules"), "rules") : rulesInline(spec.cli) ? planned.filter((e2) => e2.target.startsWith(".claude/rules/")) : mdFiles(join31(".agents", "rules"), "rules", "uzys-harness.md");
7125
+ const slot = hasClaude ? join31(".claude", "skills") : join31(".agents", "skills");
7008
7126
  const skills = planned.filter((e2) => e2.target.startsWith(".claude/skills/")).map((e2) => ({
7009
7127
  ...e2,
7010
- file: join30(spec.projectDir, slot, e2.target.slice(".claude/skills/".length))
7128
+ file: join31(spec.projectDir, slot, e2.target.slice(".claude/skills/".length))
7011
7129
  })).filter((e2) => existsSync22(e2.file));
7012
- const agents = hasClaude ? mdFiles(join30(".claude", "agents"), "agents") : [];
7130
+ const agents = hasClaude ? mdFiles(join31(".claude", "agents"), "agents") : [];
7013
7131
  return [
7014
7132
  ...planned.filter(
7015
7133
  (e2) => !e2.target.startsWith(".claude/rules/") && !e2.target.startsWith(".claude/agents/") && !e2.target.startsWith(".claude/skills/")
@@ -7021,8 +7139,8 @@ function residentEntriesOnDisk(spec) {
7021
7139
  }
7022
7140
  function contextFilesOnDisk(spec) {
7023
7141
  const surface = anchorSurface(spec.cli).scaffold;
7024
- const files = surface === "claude" ? [HARNESS_ANCHOR_FILE, "CLAUDE.md", join30(".claude", "CLAUDE.md")] : ["AGENTS.md"];
7025
- return files.map((f) => join30(spec.projectDir, f));
7142
+ const files = surface === "claude" ? [HARNESS_ANCHOR_FILE, "CLAUDE.md", join31(".claude", "CLAUDE.md")] : ["AGENTS.md"];
7143
+ return files.map((f) => join31(spec.projectDir, f));
7026
7144
  }
7027
7145
  function rulesInsideAgentsMd(spec, cost) {
7028
7146
  if (anchorSurface(spec.cli).scaffold !== "agents-md" || !rulesInline(spec.cli)) return cost;
@@ -7314,7 +7432,7 @@ function renderFinalSummary(log, spec, report, fromWizard) {
7314
7432
  const remote = (report.shared ?? []).find((f) => f.path === ".mcp.json")?.remote ?? [];
7315
7433
  for (const line of remoteMcpNextLines(remote, spec.cli)) log(`${" ".repeat(16)} ${line}`);
7316
7434
  const scaffoldFiles = scaffoldFilesForCli(spec.cli).filter(
7317
- (f) => f === "CLAUDE.md" && report.rootClaudeMd?.created === true || hasFillPrompt(join30(spec.projectDir, f))
7435
+ (f) => f === "CLAUDE.md" && report.rootClaudeMd?.created === true || hasFillPrompt(join31(spec.projectDir, f))
7318
7436
  );
7319
7437
  if (scaffoldFiles.length > 0) {
7320
7438
  const auditInstalled = buildManifestSpec(spec).selectedInternalSkills?.includes("audit-harness-fit");
@@ -8080,7 +8198,7 @@ function renderInterruptedInstall(err, e2, cwd = process.cwd()) {
8080
8198
  }
8081
8199
  const { path, error } = e2.record;
8082
8200
  err(
8083
- path !== null ? ` Already in place before it stopped \u2014 recorded in ${relative7(cwd, path) || path}:` : " Already in place before it stopped:"
8201
+ path !== null ? ` Already in place before it stopped \u2014 recorded in ${relative8(cwd, path) || path}:` : " Already in place before it stopped:"
8084
8202
  );
8085
8203
  for (const [key, n] of groups) {
8086
8204
  err(` ${key.endsWith("/") ? `${key} (${n} file${n === 1 ? "" : "s"})` : key}`);
@@ -8404,7 +8522,7 @@ function registerInstallCommand(cli) {
8404
8522
  // src/commands/list.ts
8405
8523
  init_esm_shims();
8406
8524
  import { existsSync as existsSync23, readFileSync as readFileSync24 } from "fs";
8407
- import { join as join31, resolve as resolve3 } from "path";
8525
+ import { join as join32, resolve as resolve3 } from "path";
8408
8526
  function listAction(options = {}, deps = {}) {
8409
8527
  const log = deps.log ?? console.log;
8410
8528
  const err = deps.err ?? console.error;
@@ -8508,7 +8626,7 @@ function formatHarnessFileRows(log, projectDir) {
8508
8626
  const rows = [` ${c.dim(tops.length > 0 ? tops.join(" ") : "(none recorded)")}`];
8509
8627
  const rootMd = log.templates.rootClaudeMd;
8510
8628
  if (rootMd) {
8511
- const path = join31(projectDir, rootMd.path);
8629
+ const path = join32(projectDir, rootMd.path);
8512
8630
  const modified = existsSync23(path) && hashContent(readFileSync24(path, "utf8")) !== rootMd.sha256;
8513
8631
  rows.push(
8514
8632
  modified ? ` ${c.dim(rootMd.path)} ${c.yellow("(\uC218\uC815\uB428 \u2014 uninstall \uC2DC \uBCF4\uC874)")}` : ` ${c.dim(rootMd.path)}`
@@ -8523,7 +8641,7 @@ var ROOT_FILE_CHANGE = {
8523
8641
  displaced: "\uBE44\uCF1C \uB460"
8524
8642
  };
8525
8643
  function formatRootFileRows(rootFiles, projectDir) {
8526
- const present = rootFiles.filter((f) => existsSync23(join31(projectDir, f.path)));
8644
+ const present = rootFiles.filter((f) => existsSync23(join32(projectDir, f.path)));
8527
8645
  const width = Math.max(0, ...present.map((f) => f.path.length));
8528
8646
  return present.map(
8529
8647
  (f) => ` ${c.dim(padDisplay(f.path, width))} ${c.dim(ROOT_FILE_CHANGE[f.change])} ${c.dim(f.notes.join(" / "))}`
@@ -8554,7 +8672,7 @@ import {
8554
8672
  statSync as statSync4,
8555
8673
  writeFileSync as writeFileSync8
8556
8674
  } from "fs";
8557
- import { dirname as dirname8, join as join32, relative as relative8, resolve as resolve4, sep as sep8 } from "path";
8675
+ import { dirname as dirname8, join as join33, relative as relative9, resolve as resolve4, sep as sep9 } from "path";
8558
8676
 
8559
8677
  // src/track-removal.ts
8560
8678
  init_esm_shims();
@@ -10493,7 +10611,7 @@ function unwritablePaths(ctx) {
10493
10611
  const project = ctx.targetAssets.filter((a) => a.scope === "project");
10494
10612
  if (project.some((a) => a.method === "npm")) need.add(".");
10495
10613
  const parentOf = (path) => {
10496
- const abs = join32(projectDir, path);
10614
+ const abs = join33(projectDir, path);
10497
10615
  if (!safeExists(abs)) return;
10498
10616
  const real = safeRealpath(abs);
10499
10617
  if (real !== null && isOutsideProject(projectDir, real)) return;
@@ -10505,7 +10623,7 @@ function unwritablePaths(ctx) {
10505
10623
  const recordedShared = new Set((installLog.portions ?? []).map((p2) => p2.path));
10506
10624
  const made = new Set((installLog.externalFiles ?? []).map((f) => f.path));
10507
10625
  for (const p2 of UNINSTALL_SHARED) {
10508
- if (recordedShared.has(p2) && existsSync24(join32(projectDir, p2))) need.add(p2);
10626
+ if (recordedShared.has(p2) && existsSync24(join33(projectDir, p2))) need.add(p2);
10509
10627
  if (made.has(p2)) parentOf(p2);
10510
10628
  }
10511
10629
  for (const p2 of harnessCandidates(installLog)) parentOf(p2);
@@ -10515,7 +10633,7 @@ function unwritablePaths(ctx) {
10515
10633
  }
10516
10634
  return [...need].filter((p2) => {
10517
10635
  try {
10518
- accessSync2(join32(projectDir, p2), fsConstants2.W_OK);
10636
+ accessSync2(join33(projectDir, p2), fsConstants2.W_OK);
10519
10637
  return false;
10520
10638
  } catch (e2) {
10521
10639
  return e2.code !== "ENOENT";
@@ -10618,15 +10736,15 @@ function settleLog(ctx, io) {
10618
10736
  return true;
10619
10737
  }
10620
10738
  } else {
10621
- const recordDir = join32(projectDir, INSTALL_LOG_DIR);
10739
+ const recordDir = join33(projectDir, INSTALL_LOG_DIR);
10622
10740
  if (outsideLinkAt(projectDir, INSTALL_LOG_DIR) !== null) io.rm(recordDir);
10623
10741
  else {
10624
- for (const rel of RECORD_FILES) io.rm(join32(projectDir, rel));
10742
+ for (const rel of RECORD_FILES) io.rm(join33(projectDir, rel));
10625
10743
  pruneEmptyDirsUpward(projectDir, recordDir);
10626
10744
  }
10627
10745
  if (outsideLinkAt(projectDir, LEGACY_INSTALL_LOG_DIR) === null) {
10628
10746
  io.rm(legacyInstallLogPath(projectDir));
10629
- pruneEmptyDirsUpward(projectDir, join32(projectDir, LEGACY_INSTALL_LOG_DIR));
10747
+ pruneEmptyDirsUpward(projectDir, join33(projectDir, LEGACY_INSTALL_LOG_DIR));
10630
10748
  }
10631
10749
  io.log(` ${status.success("install log removed")}`);
10632
10750
  }
@@ -10730,7 +10848,7 @@ function backupArtifactAdvisoryLines(projectDir) {
10730
10848
  }
10731
10849
  }
10732
10850
  for (const dir of BACKUP_SCAN_DIRS) {
10733
- const abs = join32(projectDir, dir);
10851
+ const abs = join33(projectDir, dir);
10734
10852
  if (!existsSync24(abs) || outsideLinkAt(projectDir, dir) !== null) continue;
10735
10853
  for (const rel of listFilesRecursive(abs)) {
10736
10854
  if (BACKUP_SUFFIX.test(rel)) found.push(`${dir}/${rel}`);
@@ -10746,11 +10864,11 @@ function backupArtifactAdvisoryLines(projectDir) {
10746
10864
  }
10747
10865
  function rootFileAdvisoryLines(rootFiles, projectDir) {
10748
10866
  const header = "[ROOT] `.claude/` \uBC16\uC5D0 \uB0A8\uB294 \uAC83 (\uC790\uB3D9\uC73C\uB85C \uC9C0\uC6B0\uC9C0 \uC54A\uB294\uB2E4):";
10749
- const present = rootFiles.filter((f) => existsSync24(join32(projectDir, f.path)));
10867
+ const present = rootFiles.filter((f) => existsSync24(join33(projectDir, f.path)));
10750
10868
  if (present.length === 0) return [];
10751
10869
  const outside = /* @__PURE__ */ new Map();
10752
10870
  const inside = present.filter((f) => {
10753
- const o = outsideProjectTarget(projectDir, join32(projectDir, f.path));
10871
+ const o = outsideProjectTarget(projectDir, join33(projectDir, f.path));
10754
10872
  if (o === null) return true;
10755
10873
  const group = outside.get(o.link) ?? { link: o, files: [] };
10756
10874
  group.files.push(f.path);
@@ -10789,7 +10907,7 @@ function rootFileMeaning(f, projectDir) {
10789
10907
  case "displaced": {
10790
10908
  const backup = f.notes[0];
10791
10909
  if (backup === void 0) return "\uD558\uB124\uC2A4 \uD310\uACFC \uAC19\uC740 \uC124\uCE58\uC790 \uD30C\uC77C\uC774 \uC788\uB358 \uC790\uB9AC (\uADF8\uB300\uB85C \uB450\uC5C8\uB2E4)";
10792
- return existsSync24(join32(projectDir, backup)) ? `\uD558\uB124\uC2A4\uAC00 \uC4F4 \uC790\uB9AC \u2014 \uC6D0\uB798 \uC788\uB358 \uC124\uCE58\uC790 \uD30C\uC77C\uC740 ${backup} \uC5D0 \uC788\uB2E4` : `\uD558\uB124\uC2A4\uAC00 \uC4F4 \uC790\uB9AC \u2014 \uC6D0\uB798 \uC788\uB358 \uC124\uCE58\uC790 \uD30C\uC77C\uC758 \uBC31\uC5C5(${backup})\uC774 \uB354\uB294 \uC5C6\uB2E4`;
10910
+ return existsSync24(join33(projectDir, backup)) ? `\uD558\uB124\uC2A4\uAC00 \uC4F4 \uC790\uB9AC \u2014 \uC6D0\uB798 \uC788\uB358 \uC124\uCE58\uC790 \uD30C\uC77C\uC740 ${backup} \uC5D0 \uC788\uB2E4` : `\uD558\uB124\uC2A4\uAC00 \uC4F4 \uC790\uB9AC \u2014 \uC6D0\uB798 \uC788\uB358 \uC124\uCE58\uC790 \uD30C\uC77C\uC758 \uBC31\uC5C5(${backup})\uC774 \uB354\uB294 \uC5C6\uB2E4`;
10793
10911
  }
10794
10912
  }
10795
10913
  }
@@ -10871,8 +10989,8 @@ function agentsMdRerenderSet(target, remaining, log, projectDir) {
10871
10989
  const set = agentsMdClis(remaining);
10872
10990
  if (set.length === 0) return null;
10873
10991
  const recorded2 = (log.externalFiles ?? []).some((f) => f.path === AGENTS_MD2) || (log.portions ?? []).some((p2) => p2.path === AGENTS_MD2);
10874
- if (!existsSync24(join32(projectDir, AGENTS_MD2))) return null;
10875
- if (!recorded2 && !isLegacyHarnessAgentsMd(readTextOrEmpty(join32(projectDir, AGENTS_MD2)))) {
10992
+ if (!existsSync24(join33(projectDir, AGENTS_MD2))) return null;
10993
+ if (!recorded2 && !isLegacyHarnessAgentsMd(readTextOrEmpty(join33(projectDir, AGENTS_MD2)))) {
10876
10994
  return null;
10877
10995
  }
10878
10996
  return set;
@@ -11003,9 +11121,9 @@ function runScopedRemoval(ctx, io, plan) {
11003
11121
  }
11004
11122
  for (const line of lines) io.log(line);
11005
11123
  const settled = plan.settle(done);
11006
- const agentsBefore = readTextOrEmpty(join32(projectDir, AGENTS_MD2));
11124
+ const agentsBefore = readTextOrEmpty(join33(projectDir, AGENTS_MD2));
11007
11125
  const rerender = plan.rerenderSet === null ? null : rerenderAgentsMd(ctx, plan.rerenderSet, settled);
11008
- const rewrote = rerender !== null && readTextOrEmpty(join32(projectDir, AGENTS_MD2)) !== agentsBefore;
11126
+ const rewrote = rerender !== null && readTextOrEmpty(join33(projectDir, AGENTS_MD2)) !== agentsBefore;
11009
11127
  if (rewrote) for (const line of rerenderLines(rerender)) io.log(line);
11010
11128
  const next = rerender === null ? settled : withRerender(settled, rerender);
11011
11129
  try {
@@ -11078,7 +11196,7 @@ function removeTrackAction(ctx, io) {
11078
11196
  }
11079
11197
  var TRACK_SCOPE = { shared: [], skeleton: [], folders: [], importBlock: false, anchor: false };
11080
11198
  function trackNotes(ctx, r, d2, write) {
11081
- const md = join32(ctx.projectDir, "CLAUDE.md");
11199
+ const md = join33(ctx.projectDir, "CLAUDE.md");
11082
11200
  const block = rootBlockNext(ctx, new Set(d2.removed));
11083
11201
  if (write && block !== null) {
11084
11202
  backupIfLossyUtf8(md);
@@ -11106,11 +11224,11 @@ function previewTrackRemoval(ctx, target) {
11106
11224
  }
11107
11225
  function rootBlockNext({ installLog, projectDir }, gone) {
11108
11226
  const anchor = installLog.templates?.rootClaudeMd?.path;
11109
- const md = join32(projectDir, "CLAUDE.md");
11227
+ const md = join33(projectDir, "CLAUDE.md");
11110
11228
  const text = safeExists(md) && outsideProjectTarget(projectDir, md) === null ? readTextOrEmpty(md) : "";
11111
- if (anchor === void 0 || !safeExists(join32(projectDir, anchor))) return null;
11229
+ if (anchor === void 0 || !safeExists(join33(projectDir, anchor))) return null;
11112
11230
  if (stripHarnessImport(text) === null) return null;
11113
- const stays = (id) => listFilesRecursive(join32(projectDir, SKILLS_DIR3, id)).some(
11231
+ const stays = (id) => listFilesRecursive(join33(projectDir, SKILLS_DIR3, id)).some(
11114
11232
  (f) => !gone.has(`${SKILLS_DIR3}${id}/${f}`)
11115
11233
  );
11116
11234
  const continuousSkills = CONTINUOUS_SKILLS.map((s) => s.id).filter(stays);
@@ -11121,7 +11239,7 @@ function settleTrackLog(ctx, target, remaining, done) {
11121
11239
  const { installLog, projectDir } = ctx;
11122
11240
  const tracks = installLog.spec.tracks.filter((t) => t !== target);
11123
11241
  const next = forgetRemoved({ ...installLog, spec: { ...installLog.spec, tracks } }, done);
11124
- const abs = join32(projectDir, INSTALLED_TRACKS);
11242
+ const abs = join33(projectDir, INSTALLED_TRACKS);
11125
11243
  const key = INSTALLED_TRACKS.slice(CLAUDE_DIR3.length);
11126
11244
  const prior = installLog.policyFiles?.find((f) => f.path === key)?.sha256 ?? hashContent(installedTracksText(installLog.spec.tracks.filter(isTrack)));
11127
11245
  const edited = safeExists(abs) && hashContent(readTextOrEmpty(abs)) !== prior;
@@ -11148,7 +11266,7 @@ var SKILLS_LOCK = "skills-lock.json";
11148
11266
  function withSkillsLock(rootFiles, assets, projectDir) {
11149
11267
  const out = [...rootFiles];
11150
11268
  const skills = assets.some((a) => a.method === "skill" && a.scope === "project");
11151
- if (skills && !out.some((f) => f.path === SKILLS_LOCK) && existsSync24(join32(projectDir, SKILLS_LOCK))) {
11269
+ if (skills && !out.some((f) => f.path === SKILLS_LOCK) && existsSync24(join33(projectDir, SKILLS_LOCK))) {
11152
11270
  out.push({ path: SKILLS_LOCK, change: "advisory", notes: [] });
11153
11271
  }
11154
11272
  return out;
@@ -11159,7 +11277,7 @@ function remnantFor(path, log, harnessRoot, projectDir, wiringGone = []) {
11159
11277
  const gone = (k2) => wiringGone.includes(`hooks/${k2.split("#")[1] ?? ""}`);
11160
11278
  return (disk) => {
11161
11279
  try {
11162
- const template = readFileSync25(join32(harnessRoot, "templates", "settings.json"), "utf8");
11280
+ const template = readFileSync25(join33(harnessRoot, "templates", "settings.json"), "utf8");
11163
11281
  const render = renderSettingsPortion(template, () => true);
11164
11282
  return [...legacySettingsSeed(disk, render, projectDir, { edited: true }).keys()].filter(
11165
11283
  (k2) => !k2.endsWith("{}") && !gone(k2)
@@ -11298,7 +11416,7 @@ function noReverseHints(asset, ctx) {
11298
11416
  }
11299
11417
  if (asset.method !== "skill") return [];
11300
11418
  const name = asset.detail.skill;
11301
- const left = name === void 0 ? [] : [".claude/skills", ".agents/skills"].map((root) => `${root}/${name}/`).filter((p2) => existsSync24(join32(ctx.projectDir, p2)));
11419
+ const left = name === void 0 ? [] : [".claude/skills", ".agents/skills"].map((root) => `${root}/${name}/`).filter((p2) => existsSync24(join33(ctx.projectDir, p2)));
11302
11420
  return [
11303
11421
  left.length > 0 ? `files it put down are not on record (installed by an older version) \u2014 left: ${left.join(" \xB7 ")} (delete by hand if the harness installed it)` : "files it put down are not on record (installed by an older version) \u2014 nothing removed"
11304
11422
  ];
@@ -11408,6 +11526,7 @@ function removeHarnessFiles(log, projectDir, scope, io) {
11408
11526
  legacyAgentsMd: [],
11409
11527
  unjudged: [],
11410
11528
  outside: [],
11529
+ offSlot: [],
11411
11530
  displaced: [],
11412
11531
  shared: [],
11413
11532
  retiredWiring: [],
@@ -11424,7 +11543,7 @@ function removeHarnessFiles(log, projectDir, scope, io) {
11424
11543
  }
11425
11544
  const judged = new Set(candidates);
11426
11545
  for (const path of legacyFileCandidates()) {
11427
- if (judged.has(path) || !scope.has(path) || !safeExists(join32(projectDir, path))) continue;
11546
+ if (judged.has(path) || !scope.has(path) || !safeExists(join33(projectDir, path))) continue;
11428
11547
  if (recorded(log, path, { isTarget }).state !== "none") continue;
11429
11548
  removeRecordedFile(path, { log, projectDir, io, out, isTarget, pruneFrom });
11430
11549
  }
@@ -11450,9 +11569,9 @@ function removeHarnessFiles(log, projectDir, scope, io) {
11450
11569
  write: io.write
11451
11570
  });
11452
11571
  if (io.write) {
11453
- for (const p2 of pruneFrom) pruneEmptyDirsUpward(projectDir, dirname8(join32(projectDir, p2)));
11572
+ for (const p2 of pruneFrom) pruneEmptyDirsUpward(projectDir, dirname8(join33(projectDir, p2)));
11454
11573
  for (const d2 of scope.skeleton) {
11455
- const abs = join32(projectDir, d2);
11574
+ const abs = join33(projectDir, d2);
11456
11575
  if (outsideProjectTarget(projectDir, abs) !== null) continue;
11457
11576
  pruneEmptyDirsUpward(projectDir, abs);
11458
11577
  }
@@ -11491,14 +11610,14 @@ function templateNext(harnessRoot, log, path) {
11491
11610
  if (helper === void 0 && !path.startsWith(CLAUDE_DIR3)) return null;
11492
11611
  try {
11493
11612
  const source = helper ?? path.slice(CLAUDE_DIR3.length);
11494
- return readFileSync25(join32(harnessRoot, "templates", source), "utf8");
11613
+ return readFileSync25(join33(harnessRoot, "templates", source), "utf8");
11495
11614
  } catch {
11496
11615
  return null;
11497
11616
  }
11498
11617
  }
11499
11618
  function removeRecordedFile(path, ctx) {
11500
11619
  const { log, projectDir, io, out } = ctx;
11501
- const abs = join32(projectDir, path);
11620
+ const abs = join33(projectDir, path);
11502
11621
  let foreign;
11503
11622
  try {
11504
11623
  foreign = foreignOwnedTarget(projectDir, path);
@@ -11515,6 +11634,11 @@ function removeRecordedFile(path, ctx) {
11515
11634
  out.outside.push(outside);
11516
11635
  return;
11517
11636
  }
11637
+ if (!inHarnessSlot(projectDir, path, HARNESS_FILES, { follow: true })) {
11638
+ out.offSlot.push(path);
11639
+ out.forgotten.add(path);
11640
+ return;
11641
+ }
11518
11642
  let st2;
11519
11643
  try {
11520
11644
  st2 = lstatSync7(abs, { throwIfNoEntry: false });
@@ -11553,7 +11677,7 @@ function removeRecordedFile(path, ctx) {
11553
11677
  if (verdict.verdict !== "remove" && verdict.verdict !== "backup+remove") return;
11554
11678
  let backup = null;
11555
11679
  if (verdict.verdict === "backup+remove") {
11556
- backup = io.write ? relative8(projectDir, backupFile(effective)).split(sep8).join("/") : `${relative8(projectDir, effective).split(sep8).join("/")}.backup-<time>`;
11680
+ backup = io.write ? relative9(projectDir, backupFile(effective)).split(sep9).join("/") : `${relative9(projectDir, effective).split(sep9).join("/")}.backup-<time>`;
11557
11681
  }
11558
11682
  if (io.write) {
11559
11683
  if (effective !== abs) io.rm(abs);
@@ -11582,11 +11706,11 @@ function mayReclaimLegacy(log, projectDir, path) {
11582
11706
  function removeLegacyHookWiring(log, projectDir, scope, io, removed) {
11583
11707
  if (!scope.has(SETTINGS_TARGET) || !installedClis(log).includes("claude")) return [];
11584
11708
  if ((log.portions ?? []).some((p2) => p2.path === SETTINGS_TARGET)) return [];
11585
- const abs = join32(projectDir, SETTINGS_TARGET);
11709
+ const abs = join33(projectDir, SETTINGS_TARGET);
11586
11710
  if (!safeExists(abs) || outsideProjectTarget(projectDir, abs) !== null) return [];
11587
- const claudeDir = join32(projectDir, ".claude");
11711
+ const claudeDir = join33(projectDir, ".claude");
11588
11712
  const gone = LEGACY_HOOK_REFS.filter(
11589
- (r) => !RETIRED_HOOK_WIRING_REFS.includes(r) && (removed.includes(`${CLAUDE_DIR3}${r}`) || !safeExists(join32(claudeDir, r)))
11713
+ (r) => !RETIRED_HOOK_WIRING_REFS.includes(r) && (removed.includes(`${CLAUDE_DIR3}${r}`) || !safeExists(join33(claudeDir, r)))
11590
11714
  );
11591
11715
  return removeRetiredHookRefs(abs, claudeDir, gone, { evenIfScriptExists: true, write: io.write });
11592
11716
  }
@@ -11607,7 +11731,7 @@ function stripSharedFiles(log, projectDir, paths, io, out, pruneFrom) {
11607
11731
  };
11608
11732
  for (const path of paths) {
11609
11733
  if (!relevant(path)) continue;
11610
- const abs = join32(projectDir, path);
11734
+ const abs = join33(projectDir, path);
11611
11735
  if (safeExists(abs)) {
11612
11736
  const o = outsideProjectTarget(projectDir, abs);
11613
11737
  if (o !== null) {
@@ -11618,7 +11742,7 @@ function stripSharedFiles(log, projectDir, paths, io, out, pruneFrom) {
11618
11742
  if (path === SETTINGS_TARGET && safeExists(abs)) {
11619
11743
  out.retiredWiring = removeRetiredHookRefs(
11620
11744
  abs,
11621
- join32(projectDir, ".claude"),
11745
+ join33(projectDir, ".claude"),
11622
11746
  RETIRED_HOOK_WIRING_REFS,
11623
11747
  { evenIfScriptExists: true, write: io.write }
11624
11748
  );
@@ -11646,7 +11770,7 @@ function stripSharedFiles(log, projectDir, paths, io, out, pruneFrom) {
11646
11770
  }
11647
11771
  }
11648
11772
  function removeHarnessMadeShared(path, sha, projectDir, io, out, pruneFrom) {
11649
- const abs = join32(projectDir, path);
11773
+ const abs = join33(projectDir, path);
11650
11774
  if (!existsSync24(abs)) {
11651
11775
  out.forgotten.add(path);
11652
11776
  return true;
@@ -11817,6 +11941,7 @@ function harnessRemovalLines(r, log, projectDir, scope, preview, alsoGone = [])
11817
11941
  );
11818
11942
  }
11819
11943
  lines.push(...outsideLines(r.outside, log));
11944
+ lines.push(...offSlotLines(r.offSlot, projectDir));
11820
11945
  for (const path of r.kept) {
11821
11946
  lines.push(
11822
11947
  ` ${c.yellow("\u2298")} ${path} kept \u2014 modified since install. Remove manually if intended.`
@@ -11851,7 +11976,7 @@ function leftFolderLines(r, log, projectDir, scope, preview, alsoGone) {
11851
11976
  preview ? [...r.removed, ...r.shared.filter((s) => s.fileRemoved).map((s) => s.path), ...alsoGone] : []
11852
11977
  );
11853
11978
  const back = preview ? r.displaced.filter((d2) => d2.verdict === "restore").map((d2) => d2.path) : [];
11854
- const said = (full) => r.unjudged.includes(full) || r.kept.includes(full) || r.outside.some((o) => o.path === full || full.startsWith(`${o.link}/`));
11979
+ const said = (full) => r.unjudged.includes(full) || r.kept.includes(full) || r.offSlot.includes(full) || r.outside.some((o) => o.path === full || full.startsWith(`${o.link}/`));
11855
11980
  const whose = "not on the install record \u2014 yours, or left by an older harness version; delete by hand any that are not yours";
11856
11981
  const lines = [];
11857
11982
  for (const dir of scope.folders) {
@@ -11884,19 +12009,19 @@ function yoursUnder(projectDir, dir) {
11884
12009
  }
11885
12010
  for (const e2 of entries) {
11886
12011
  const name = `${prefix}${e2.name}`;
11887
- if (e2.isDirectory()) walk(join32(abs2, e2.name), `${name}/`);
12012
+ if (e2.isDirectory()) walk(join33(abs2, e2.name), `${name}/`);
11888
12013
  else if (BACKUP_SUFFIX.test(e2.name)) continue;
11889
12014
  else if (e2.isSymbolicLink()) {
11890
12015
  let target = "?";
11891
12016
  try {
11892
- target = readlinkSync2(join32(abs2, e2.name));
12017
+ target = readlinkSync2(join33(abs2, e2.name));
11893
12018
  } catch {
11894
12019
  }
11895
12020
  out.push({ name, shown: `${name} (your link \u2192 ${target})` });
11896
12021
  } else out.push({ name, shown: name });
11897
12022
  }
11898
12023
  };
11899
- const abs = join32(projectDir, dir);
12024
+ const abs = join33(projectDir, dir);
11900
12025
  try {
11901
12026
  if (!statSync4(abs).isDirectory()) return [];
11902
12027
  } catch {
@@ -11908,7 +12033,7 @@ function yoursUnder(projectDir, dir) {
11908
12033
  function assetsLeftLines(log, projectDir, scope) {
11909
12034
  const lines = [];
11910
12035
  for (const a of log.assets) {
11911
- const here = (a.files ?? []).map((f) => f.path).filter((p2) => scope.has(p2) && safeExists(join32(projectDir, p2)));
12036
+ const here = (a.files ?? []).map((f) => f.path).filter((p2) => scope.has(p2) && safeExists(join33(projectDir, p2)));
11912
12037
  if (here.length === 0) continue;
11913
12038
  const slots = [...new Set(here.map((p2) => p2.split("/").slice(0, 3).join("/")))].sort();
11914
12039
  lines.push(
@@ -11920,10 +12045,13 @@ function assetsLeftLines(log, projectDir, scope) {
11920
12045
  function removeAnchor(log, projectDir, io) {
11921
12046
  const rootMd = log.templates?.rootClaudeMd;
11922
12047
  if (!rootMd) return { removed: false, lines: [] };
11923
- const abs = join32(projectDir, rootMd.path);
12048
+ const abs = join33(projectDir, rootMd.path);
11924
12049
  if (!existsSync24(abs)) return { removed: false, lines: [] };
11925
12050
  const outside = outsideProjectTarget(projectDir, abs);
11926
12051
  if (outside !== null) return { removed: false, lines: outsideLines([outside], log) };
12052
+ if (!inHarnessSlot(projectDir, rootMd.path, ANCHOR_FILES, { follow: true })) {
12053
+ return { removed: false, lines: [offSlotLine(rootMd.path, "names it as the harness anchor")] };
12054
+ }
11927
12055
  let disk;
11928
12056
  try {
11929
12057
  disk = readFileSync25(abs, "utf8");
@@ -11959,7 +12087,7 @@ function removeAnchor(log, projectDir, io) {
11959
12087
  ]
11960
12088
  };
11961
12089
  }
11962
- const backup = edited ? relative8(projectDir, backupFile(abs)).split(sep8).join("/") : null;
12090
+ const backup = edited ? relative9(projectDir, backupFile(abs)).split(sep9).join("/") : null;
11963
12091
  io.rm(abs);
11964
12092
  return {
11965
12093
  removed: true,
@@ -11971,7 +12099,7 @@ function removeAnchor(log, projectDir, io) {
11971
12099
  var AGENTS_MD2 = "AGENTS.md";
11972
12100
  function readAgentsMdTemplate(harnessRoot) {
11973
12101
  try {
11974
- return readFileSync25(join32(harnessRoot, AGENTS_MD_TEMPLATE), "utf8");
12102
+ return readFileSync25(join33(harnessRoot, AGENTS_MD_TEMPLATE), "utf8");
11975
12103
  } catch {
11976
12104
  return null;
11977
12105
  }
@@ -11997,7 +12125,7 @@ function agentsMdRemainder(current, harnessRoot) {
11997
12125
  return { ok: true, remainder: strip.remainder, strip };
11998
12126
  }
11999
12127
  function removeUnrecordedLegacyAgentsMd(path, projectDir, io, out, pruneFrom) {
12000
- const abs = join32(projectDir, path);
12128
+ const abs = join33(projectDir, path);
12001
12129
  let effective = abs;
12002
12130
  let current;
12003
12131
  try {
@@ -12022,7 +12150,7 @@ function removeLegacyAgentsMd(path, abs, effective, strip, ctx) {
12022
12150
  let backup = `${path}.backup-<time>`;
12023
12151
  try {
12024
12152
  if (io.write) {
12025
- backup = relative8(projectDir, backupFile(effective)).split(sep8).join("/");
12153
+ backup = relative9(projectDir, backupFile(effective)).split(sep9).join("/");
12026
12154
  if (strip.remainder !== null) {
12027
12155
  writeFileSync8(effective, neutralizeScaffoldBanner(strip.remainder), "utf8");
12028
12156
  } else {
@@ -12061,14 +12189,52 @@ function globalCopyLines(copies, preview) {
12061
12189
  ];
12062
12190
  });
12063
12191
  }
12192
+ var ASSET_SLOTS = skillFolders(SKILL_ROOTS);
12193
+ function offSlotLines(paths, projectDir) {
12194
+ const lines = [];
12195
+ const byLink = /* @__PURE__ */ new Map();
12196
+ for (const path of paths) {
12197
+ const link = linkOnPath(projectDir, path);
12198
+ if (link === null) {
12199
+ lines.push(offSlotLine(path, "lists it"));
12200
+ continue;
12201
+ }
12202
+ const g = byLink.get(link.link) ?? { target: link.target, n: 0 };
12203
+ g.n += 1;
12204
+ byLink.set(link.link, g);
12205
+ }
12206
+ for (const [link, g] of byLink) {
12207
+ lines.push(
12208
+ ` ${c.yellow("\u2298")} ${link} \u2014 your link (\u2192 ${g.target}) leads outside the places this tool installs to; left ${g.n} file(s) on the install record under it. Not touched \u2014 remove by hand if intended.`
12209
+ );
12210
+ }
12211
+ return lines;
12212
+ }
12213
+ function linkOnPath(projectDir, path) {
12214
+ const parts = path.split("/");
12215
+ for (let i = 1; i <= parts.length; i++) {
12216
+ const rel = parts.slice(0, i).join("/");
12217
+ try {
12218
+ if (lstatSync7(join33(projectDir, rel), { throwIfNoEntry: false })?.isSymbolicLink() === true) {
12219
+ return { link: rel, target: readlinkSync2(join33(projectDir, rel)) };
12220
+ }
12221
+ } catch {
12222
+ return null;
12223
+ }
12224
+ }
12225
+ return null;
12226
+ }
12227
+ function offSlotLine(path, what) {
12228
+ return ` ${c.yellow("\u2298")} ${path} left \u2014 the install record ${what}, but it is (or leads) outside the places this tool installs to. Not touched.`;
12229
+ }
12064
12230
  function removeToolFiles(files, projectDir, rm) {
12065
12231
  const notes = [];
12066
12232
  const removed = [];
12067
12233
  let failed = 0;
12068
- const pairs = new Set(files.filter((f) => isAppleDoublePath(join32(projectDir, f.path))));
12234
+ const pairs = new Set(files.filter((f) => isAppleDoublePath(join33(projectDir, f.path))));
12069
12235
  for (const file of files) {
12070
12236
  const { path, sha256 } = file;
12071
- const abs = join32(projectDir, path);
12237
+ const abs = join33(projectDir, path);
12072
12238
  if (pairs.has(file)) {
12073
12239
  try {
12074
12240
  rm(abs);
@@ -12091,10 +12257,16 @@ function removeToolFiles(files, projectDir, rm) {
12091
12257
  notes.push(`left ${path} \u2014 it is a link now, not the copy that was installed`);
12092
12258
  continue;
12093
12259
  }
12260
+ if (!inHarnessSlot(projectDir, path, ASSET_SLOTS)) {
12261
+ notes.push(
12262
+ `left ${path} \u2014 the install record lists it for this asset, but it is (or leads) outside the skill folders this asset installs to`
12263
+ );
12264
+ continue;
12265
+ }
12094
12266
  try {
12095
12267
  if (hashContent(readFileSync25(abs, "utf8")) !== sha256) {
12096
12268
  const backup = backupFile(abs);
12097
- notes.push(`backed up ${path} \u2192 ${relative8(projectDir, backup)} (changed since install)`);
12269
+ notes.push(`backed up ${path} \u2192 ${relative9(projectDir, backup)} (changed since install)`);
12098
12270
  }
12099
12271
  rm(abs);
12100
12272
  removed.push(path);
@@ -12103,14 +12275,14 @@ function removeToolFiles(files, projectDir, rm) {
12103
12275
  notes.push(`left ${path} \u2014 ${e2 instanceof Error ? e2.message : String(e2)}`);
12104
12276
  }
12105
12277
  }
12106
- for (const path of removed) pruneEmptyDirsUpward(projectDir, dirname8(join32(projectDir, path)));
12278
+ for (const path of removed) pruneEmptyDirsUpward(projectDir, dirname8(join33(projectDir, path)));
12107
12279
  return failed > 0 ? { ok: false, message: `${failed} file(s) could not be removed`, notes } : { ok: true, notes };
12108
12280
  }
12109
12281
  function pruneEmptyDirsUpward(projectDir, startDir) {
12110
12282
  let cursor = startDir;
12111
12283
  while (true) {
12112
- const rel = relative8(projectDir, cursor);
12113
- if (rel === "" || rel.startsWith(`..${sep8}`) || rel === "..") return;
12284
+ const rel = relative9(projectDir, cursor);
12285
+ if (rel === "" || rel.startsWith(`..${sep9}`) || rel === "..") return;
12114
12286
  try {
12115
12287
  if (readdirSync7(cursor).length > 0) return;
12116
12288
  rmdirSync3(cursor);
@@ -12127,8 +12299,8 @@ function stripRootImport(projectDir) {
12127
12299
  if (withoutImport === null) return false;
12128
12300
  const stripped = neutralizeScaffoldBanner(withoutImport);
12129
12301
  try {
12130
- backupIfLossyUtf8(join32(projectDir, "CLAUDE.md"));
12131
- writeFileSync8(join32(projectDir, "CLAUDE.md"), stripped, "utf8");
12302
+ backupIfLossyUtf8(join33(projectDir, "CLAUDE.md"));
12303
+ writeFileSync8(join33(projectDir, "CLAUDE.md"), stripped, "utf8");
12132
12304
  return true;
12133
12305
  } catch {
12134
12306
  return false;
@@ -12150,10 +12322,10 @@ function importOutsideLine(o, anchorGone) {
12150
12322
  return ` ${c.yellow("\u2298")} CLAUDE.md kept \u2014 link target is outside the project (${o.target}). Not touched \u2014 its harness @import block (${HARNESS_IMPORT_LINE}) is left in it${pointing} \u2014 delete that block by hand.`;
12151
12323
  }
12152
12324
  function rootClaudeMdOutside(projectDir) {
12153
- return outsideProjectTarget(projectDir, join32(projectDir, "CLAUDE.md"));
12325
+ return outsideProjectTarget(projectDir, join33(projectDir, "CLAUDE.md"));
12154
12326
  }
12155
12327
  function readRootClaudeMd(projectDir) {
12156
- const path = join32(projectDir, "CLAUDE.md");
12328
+ const path = join33(projectDir, "CLAUDE.md");
12157
12329
  if (!existsSync24(path)) return null;
12158
12330
  try {
12159
12331
  return readFileSync25(path, "utf8");
@@ -12351,7 +12523,8 @@ function classifyUpdateIntent(log, confirmed, legacyTracks = []) {
12351
12523
  const recordClis = recordedClis(log);
12352
12524
  if (!sameSet(confirmed.tracks, recordTracks)) return "add";
12353
12525
  if (!sameSet(confirmed.cli, recordClis)) return "add";
12354
- const out = excludedIds(log);
12526
+ const recordedOut = excludedIds(log);
12527
+ const out = /* @__PURE__ */ new Set([...recordedOut, ...droppedMcpAssetIds(resolveBundleRoot(), recordedOut)]);
12355
12528
  const recorded2 = log?.assets ?? [];
12356
12529
  const covered = coveredByRecord(log, legacyTracks);
12357
12530
  const picked = new Set(confirmed.assetIds);
@@ -12699,7 +12872,8 @@ async function confirmUpdate(input) {
12699
12872
  function unchecked(log, tracks, assetIds, baselineIds) {
12700
12873
  const checked = /* @__PURE__ */ new Set([...assetIds, ...baselineIds]);
12701
12874
  const offered = new Set(listBaselineTargets({ tracks }).map((t) => t.id));
12702
- return [...excludedIds(log)].filter((id) => !checked.has(id)).filter((id) => !id.startsWith(BASELINE_PREFIX) || offered.has(id)).sort();
12875
+ const rechecked = (id) => isKeyId(id) && droppedMcpAssetIds(resolveBundleRoot(), /* @__PURE__ */ new Set([id])).some((a) => checked.has(a));
12876
+ return [...excludedIds(log)].filter((id) => !checked.has(id) && !rechecked(id)).filter((id) => !id.startsWith(BASELINE_PREFIX) || offered.has(id)).sort();
12703
12877
  }
12704
12878
  function annotate(summary, notes) {
12705
12879
  return summary.split("\n").map((line) => {
@@ -12734,7 +12908,7 @@ function formatSummary(spec) {
12734
12908
  `CLI: ${spec.cli.join(" \xB7 ")}`,
12735
12909
  `Target: ${spec.projectDir}`
12736
12910
  ];
12737
- const finalAssets = finalSelectedAssets(spec.tracks, spec.userOverride);
12911
+ const finalAssets = finalSelectedAssets(spec.tracks, spec.userOverride, spec.keyExclude);
12738
12912
  if (finalAssets.length > 0) {
12739
12913
  const unreachable = finalAssets.filter((id) => {
12740
12914
  const asset = EXTERNAL_ASSETS.find((a) => a.id === id);