@uzysjung/agent-harness 26.147.0 → 26.148.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
@@ -56,7 +56,7 @@ import {
56
56
  summarizeContextCost,
57
57
  upsertHarnessImport,
58
58
  writeInstallLog
59
- } from "./chunk-GPH5AWLX.js";
59
+ } from "./chunk-PTUM2NI3.js";
60
60
 
61
61
  // node_modules/sisteransi/src/index.js
62
62
  var require_src = __commonJS({
@@ -741,7 +741,7 @@ var cac = (name = "") => new CAC(name);
741
741
  // package.json
742
742
  var package_default = {
743
743
  name: "@uzysjung/agent-harness",
744
- version: "26.147.0",
744
+ version: "26.148.0",
745
745
  description: "Curate vetted AI-coding skills & plugins by your tech stack \u2014 install only what you need, across Claude Code, Codex, OpenCode & Antigravity",
746
746
  type: "module",
747
747
  publishConfig: {
@@ -775,6 +775,7 @@ var package_default = {
775
775
  prepare: "[ -d dist ] || npm run build",
776
776
  "cost:report": "npm run build && node scripts/context-cost-report.mjs",
777
777
  "cost:baseline": "npm run build && node scripts/context-cost-baseline.mjs",
778
+ "assets:history": "bash scripts/asset-history.sh",
778
779
  security: "npm audit --omit=dev --audit-level=high && npx --yes ecc-agentshield@1.4.0 scan --baseline security-baseline.json --gate",
779
780
  "security:baseline": "npx --yes ecc-agentshield@1.4.0 scan --save-baseline security-baseline.json"
780
781
  },
@@ -985,7 +986,7 @@ import {
985
986
  unlinkSync as unlinkSync2,
986
987
  writeFileSync as writeFileSync6
987
988
  } from "fs";
988
- import { basename as basename5, dirname as dirname4, join as join11, resolve } from "path";
989
+ import { basename as basename5, dirname as dirname4, join as join12, resolve } from "path";
989
990
 
990
991
  // src/ci-scaffold.ts
991
992
  init_esm_shims();
@@ -1031,7 +1032,7 @@ init_esm_shims();
1031
1032
  // src/antigravity/transform.ts
1032
1033
  init_esm_shims();
1033
1034
  import { existsSync as existsSync4, readFileSync as readFileSync3 } from "fs";
1034
- import { basename, join as join3 } from "path";
1035
+ import { basename, join as join4 } from "path";
1035
1036
 
1036
1037
  // src/codex/agents-md.ts
1037
1038
  init_esm_shims();
@@ -1078,15 +1079,49 @@ function portBody(body) {
1078
1079
  init_esm_shims();
1079
1080
  import { existsSync as existsSync2, mkdirSync, readFileSync, writeFileSync } from "fs";
1080
1081
  import { dirname, relative, sep } from "path";
1082
+
1083
+ // src/foreign-slot.ts
1084
+ init_esm_shims();
1085
+ import { lstatSync } from "fs";
1086
+ import { join as join2 } from "path";
1087
+ var SKILL_SLOT = /^((?:\.claude|\.agents)\/skills\/[^/]+)(?:\/|$)/;
1088
+ function occupiedByNonDirectory(path) {
1089
+ const stat = lstatSync(path, { throwIfNoEntry: false });
1090
+ return stat !== void 0 && !stat.isDirectory();
1091
+ }
1092
+ function foreignOwnedTarget(projectDir, rel) {
1093
+ const slot = SKILL_SLOT.exec(rel)?.[1];
1094
+ if (slot === void 0) return null;
1095
+ if (occupiedByNonDirectory(join2(projectDir, slot))) return slot;
1096
+ if (rel !== slot) {
1097
+ const parts = rel.slice(slot.length + 1).split("/");
1098
+ let cursor = slot;
1099
+ for (const part of parts.slice(0, -1)) {
1100
+ cursor = `${cursor}/${part}`;
1101
+ if (occupiedByNonDirectory(join2(projectDir, cursor))) return cursor;
1102
+ }
1103
+ const leaf = lstatSync(join2(projectDir, rel), { throwIfNoEntry: false });
1104
+ if (leaf !== void 0 && !leaf.isFile()) return rel;
1105
+ }
1106
+ return null;
1107
+ }
1108
+
1109
+ // src/owned-write.ts
1081
1110
  function createOwnedWriter(projectDir, baseline, options = {}) {
1082
1111
  const { now = /* @__PURE__ */ new Date(), refreshOnly = false } = options;
1083
1112
  const written = /* @__PURE__ */ new Map();
1084
1113
  const backedUp = [];
1085
1114
  const backupPaths = [];
1115
+ const foreignOwned = [];
1086
1116
  let updated = 0;
1087
1117
  return {
1088
1118
  write(absPath, content, opts) {
1089
1119
  const rel = relative(projectDir, absPath).split(sep).join("/");
1120
+ const foreign = foreignOwnedTarget(projectDir, rel);
1121
+ if (foreign !== null) {
1122
+ if (!foreignOwned.includes(foreign)) foreignOwned.push(foreign);
1123
+ return false;
1124
+ }
1090
1125
  const digest = hashContent(content);
1091
1126
  if (!existsSync2(absPath)) {
1092
1127
  if (refreshOnly && !opts?.createInRefresh) return false;
@@ -1109,6 +1144,7 @@ function createOwnedWriter(projectDir, baseline, options = {}) {
1109
1144
  },
1110
1145
  result() {
1111
1146
  return {
1147
+ foreignOwned: [...foreignOwned],
1112
1148
  files: [...written].map(([path, sha256]) => ({ path, sha256 })),
1113
1149
  backedUp: [...backedUp],
1114
1150
  backupPaths: [...backupPaths],
@@ -1121,7 +1157,7 @@ function createOwnedWriter(projectDir, baseline, options = {}) {
1121
1157
  // src/rules-port.ts
1122
1158
  init_esm_shims();
1123
1159
  import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
1124
- import { join as join2 } from "path";
1160
+ import { join as join3 } from "path";
1125
1161
  function stripClaudeFrontmatter(source) {
1126
1162
  if (!source.startsWith("---\n")) return source.trim();
1127
1163
  const end = source.indexOf("\n---", 4);
@@ -1131,7 +1167,7 @@ function stripClaudeFrontmatter(source) {
1131
1167
  function portRules(harnessRoot, ruleNames) {
1132
1168
  const ported = [];
1133
1169
  for (const name of ruleNames) {
1134
- const src = join2(harnessRoot, "templates/rules", `${name}.md`);
1170
+ const src = join3(harnessRoot, "templates/rules", `${name}.md`);
1135
1171
  if (!existsSync3(src)) continue;
1136
1172
  ported.push({ name, body: stripClaudeFrontmatter(readFileSync2(src, "utf8")) });
1137
1173
  }
@@ -1157,7 +1193,7 @@ function runAntigravityTransform(params) {
1157
1193
  const antigravityInstalled = rulesFile !== null;
1158
1194
  const harnessRuleFiles = [];
1159
1195
  for (const rule of portRules(harnessRoot, rules)) {
1160
- const target = join3(projectDir, ".agents", "rules", `${rule.name}.md`);
1196
+ const target = join4(projectDir, ".agents", "rules", `${rule.name}.md`);
1161
1197
  if (!writer.write(target, `${rule.body}
1162
1198
  `, { createInRefresh: antigravityInstalled })) {
1163
1199
  continue;
@@ -1166,11 +1202,11 @@ function runAntigravityTransform(params) {
1166
1202
  }
1167
1203
  const skillFiles = [];
1168
1204
  for (const id of selectedInternalSkills) {
1169
- const src = join3(harnessRoot, "templates/skills", id, "SKILL.md");
1205
+ const src = join4(harnessRoot, "templates/skills", id, "SKILL.md");
1170
1206
  if (!existsSync4(src)) {
1171
1207
  continue;
1172
1208
  }
1173
- const target = join3(projectDir, ".agents", "skills", id, "SKILL.md");
1209
+ const target = join4(projectDir, ".agents", "skills", id, "SKILL.md");
1174
1210
  if (!writer.write(target, renderBundledSkill(readFileSync3(src, "utf8")))) continue;
1175
1211
  skillFiles.push(target);
1176
1212
  }
@@ -1182,14 +1218,14 @@ function runAntigravityTransform(params) {
1182
1218
  };
1183
1219
  }
1184
1220
  function writeRules(harnessRoot, projectDir, writer) {
1185
- const claudeMdPath = join3(harnessRoot, "templates/CLAUDE.md");
1186
- const templatePath = join3(harnessRoot, "templates/antigravity/AGENTS.md.template");
1221
+ const claudeMdPath = join4(harnessRoot, "templates/CLAUDE.md");
1222
+ const templatePath = join4(harnessRoot, "templates/antigravity/AGENTS.md.template");
1187
1223
  if (!existsSync4(claudeMdPath) || !existsSync4(templatePath)) {
1188
1224
  return null;
1189
1225
  }
1190
1226
  const claudeMd = readFileSync3(claudeMdPath, "utf8");
1191
1227
  const template = readFileSync3(templatePath, "utf8");
1192
- const target = join3(projectDir, ".agents", "rules", "uzys-harness.md");
1228
+ const target = join4(projectDir, ".agents", "rules", "uzys-harness.md");
1193
1229
  const rulesOut = renderAgentsMd({
1194
1230
  template,
1195
1231
  claudeMd,
@@ -1202,7 +1238,7 @@ function writeRules(harnessRoot, projectDir, writer) {
1202
1238
  // src/codex/opt-in.ts
1203
1239
  init_esm_shims();
1204
1240
  import { homedir } from "os";
1205
- import { join as join4 } from "path";
1241
+ import { join as join5 } from "path";
1206
1242
 
1207
1243
  // src/codex/trust-entry.ts
1208
1244
  init_esm_shims();
@@ -1237,8 +1273,8 @@ function hasTrustEntry(configContent, projectDir) {
1237
1273
 
1238
1274
  // src/codex/opt-in.ts
1239
1275
  function runCodexOptIn(ctx) {
1240
- const codexHome = ctx.codexHome ?? join4(homedir(), ".codex");
1241
- const configPath = join4(codexHome, "config.toml");
1276
+ const codexHome = ctx.codexHome ?? join5(homedir(), ".codex");
1277
+ const configPath = join5(codexHome, "config.toml");
1242
1278
  const result = registerTrustEntry({ configPath, projectDir: ctx.projectDir });
1243
1279
  return {
1244
1280
  trustEntry: {
@@ -1252,7 +1288,7 @@ function runCodexOptIn(ctx) {
1252
1288
  // src/codex/transform.ts
1253
1289
  init_esm_shims();
1254
1290
  import { chmodSync, existsSync as existsSync6, readFileSync as readFileSync5 } from "fs";
1255
- import { basename as basename2, join as join5 } from "path";
1291
+ import { basename as basename2, join as join6 } from "path";
1256
1292
 
1257
1293
  // src/codex/config-toml.ts
1258
1294
  init_esm_shims();
@@ -1329,12 +1365,12 @@ function runCodexTransform(params) {
1329
1365
  refreshOnly
1330
1366
  } = params;
1331
1367
  const writer = createOwnedWriter(projectDir, baseline, { refreshOnly: refreshOnly ?? false });
1332
- const claudeMd = readRequired(join5(harnessRoot, "templates/CLAUDE.md"));
1333
- const agentsTemplate = readRequired(join5(harnessRoot, "templates/codex/AGENTS.md.template"));
1334
- const configTemplate = readRequired(join5(harnessRoot, "templates/codex/config.toml.template"));
1368
+ const claudeMd = readRequired(join6(harnessRoot, "templates/CLAUDE.md"));
1369
+ const agentsTemplate = readRequired(join6(harnessRoot, "templates/codex/AGENTS.md.template"));
1370
+ const configTemplate = readRequired(join6(harnessRoot, "templates/codex/config.toml.template"));
1335
1371
  const projectName = basename2(projectDir);
1336
- const mcp = readOptionalJson(join5(harnessRoot, ".mcp.json"));
1337
- const agentsMdPath = join5(projectDir, "AGENTS.md");
1372
+ const mcp = readOptionalJson(join6(harnessRoot, ".mcp.json"));
1373
+ const agentsMdPath = join6(projectDir, "AGENTS.md");
1338
1374
  ensureDir(projectDir);
1339
1375
  const agentsMdOut = renderAgentsMd({
1340
1376
  template: agentsTemplate,
@@ -1345,7 +1381,7 @@ function runCodexTransform(params) {
1345
1381
  harnessRules: renderRulesBlock(portRules(harnessRoot, rules))
1346
1382
  });
1347
1383
  writer.write(agentsMdPath, agentsMdOut);
1348
- const configTomlPath = join5(projectDir, ".codex/config.toml");
1384
+ const configTomlPath = join6(projectDir, ".codex/config.toml");
1349
1385
  writer.write(
1350
1386
  configTomlPath,
1351
1387
  renderConfigToml({
@@ -1355,26 +1391,26 @@ function runCodexTransform(params) {
1355
1391
  mcp
1356
1392
  })
1357
1393
  );
1358
- const hookDir = join5(projectDir, ".codex/hooks");
1394
+ const hookDir = join6(projectDir, ".codex/hooks");
1359
1395
  const hookFiles = [];
1360
1396
  for (const hook of HOOK_NAMES) {
1361
- const src = join5(harnessRoot, "templates/hooks", `${hook}.sh`);
1397
+ const src = join6(harnessRoot, "templates/hooks", `${hook}.sh`);
1362
1398
  if (!existsSync6(src)) {
1363
1399
  continue;
1364
1400
  }
1365
1401
  const ported = readFileSync5(src, "utf8").replace(ENV_VAR_RENAME, "CODEX_PROJECT_DIR");
1366
- const target = join5(hookDir, `${hook}.sh`);
1402
+ const target = join6(hookDir, `${hook}.sh`);
1367
1403
  if (!writer.write(target, ported)) continue;
1368
1404
  chmodSync(target, 493);
1369
1405
  hookFiles.push(target);
1370
1406
  }
1371
1407
  const skillFiles = [];
1372
1408
  for (const id of selectedInternalSkills) {
1373
- const src = join5(harnessRoot, "templates/skills", id, "SKILL.md");
1409
+ const src = join6(harnessRoot, "templates/skills", id, "SKILL.md");
1374
1410
  if (!existsSync6(src)) {
1375
1411
  continue;
1376
1412
  }
1377
- const target = join5(projectDir, ".agents", "skills", id, "SKILL.md");
1413
+ const target = join6(projectDir, ".agents", "skills", id, "SKILL.md");
1378
1414
  if (!writer.write(target, renderBundledSkill(readFileSync5(src, "utf8")))) continue;
1379
1415
  skillFiles.push(target);
1380
1416
  }
@@ -1406,7 +1442,7 @@ function readOptionalJson(path) {
1406
1442
  // src/opencode/transform.ts
1407
1443
  init_esm_shims();
1408
1444
  import { existsSync as existsSync7, readFileSync as readFileSync6 } from "fs";
1409
- import { basename as basename3, join as join6 } from "path";
1445
+ import { basename as basename3, join as join7 } from "path";
1410
1446
 
1411
1447
  // src/opencode/agents-md.ts
1412
1448
  init_esm_shims();
@@ -1515,15 +1551,15 @@ function runOpencodeTransform(params) {
1515
1551
  refreshOnly
1516
1552
  } = params;
1517
1553
  const writer = createOwnedWriter(projectDir, baseline, { refreshOnly: refreshOnly ?? false });
1518
- const claudeMd = readRequired2(join6(harnessRoot, "templates/CLAUDE.md"));
1519
- const agentsTemplate = readRequired2(join6(harnessRoot, "templates/opencode/AGENTS.md.template"));
1554
+ const claudeMd = readRequired2(join7(harnessRoot, "templates/CLAUDE.md"));
1555
+ const agentsTemplate = readRequired2(join7(harnessRoot, "templates/opencode/AGENTS.md.template"));
1520
1556
  const opencodeTemplate = readRequired2(
1521
- join6(harnessRoot, "templates/opencode/opencode.json.template")
1557
+ join7(harnessRoot, "templates/opencode/opencode.json.template")
1522
1558
  );
1523
1559
  const projectName = basename3(projectDir);
1524
- const mcp = readOptionalJson2(join6(harnessRoot, ".mcp.json"));
1560
+ const mcp = readOptionalJson2(join7(harnessRoot, ".mcp.json"));
1525
1561
  ensureDir(projectDir);
1526
- const agentsMdPath = join6(projectDir, "AGENTS.md");
1562
+ const agentsMdPath = join7(projectDir, "AGENTS.md");
1527
1563
  const agentsMdOut = renderAgentsMd2({
1528
1564
  template: agentsTemplate,
1529
1565
  claudeMd,
@@ -1535,18 +1571,18 @@ function runOpencodeTransform(params) {
1535
1571
  harnessRules: renderRulesBlock(portRules(harnessRoot, rules))
1536
1572
  });
1537
1573
  writer.write(agentsMdPath, agentsMdOut);
1538
- const opencodeJsonPath = join6(projectDir, "opencode.json");
1574
+ const opencodeJsonPath = join7(projectDir, "opencode.json");
1539
1575
  writer.write(opencodeJsonPath, renderOpencodeJson({ template: opencodeTemplate, mcp }));
1540
- const cmdDir = join6(projectDir, ".opencode/commands");
1576
+ const cmdDir = join7(projectDir, ".opencode/commands");
1541
1577
  if (!refreshOnly) ensureDir(cmdDir);
1542
1578
  const commandFiles = [];
1543
1579
  for (const id of selectedInternalSkills) {
1544
- const src = join6(harnessRoot, "templates/skills", id, "SKILL.md");
1580
+ const src = join7(harnessRoot, "templates/skills", id, "SKILL.md");
1545
1581
  if (!existsSync7(src)) {
1546
1582
  continue;
1547
1583
  }
1548
- const target = join6(cmdDir, `${id}.md`);
1549
- const shellDependent = existsSync7(join6(harnessRoot, "templates/skills", id, "scripts"));
1584
+ const target = join7(cmdDir, `${id}.md`);
1585
+ const shellDependent = existsSync7(join7(harnessRoot, "templates/skills", id, "scripts"));
1550
1586
  const wrote = writer.write(
1551
1587
  target,
1552
1588
  renderCommandFromSkill(readFileSync6(src, "utf8"), id, { shellDependent })
@@ -1595,6 +1631,7 @@ function runCliTransforms(params) {
1595
1631
  const externalFiles = [];
1596
1632
  const externalBackups = [];
1597
1633
  const externalBackedUp = [];
1634
+ const externalForeignOwned = [];
1598
1635
  let externalUpdated = 0;
1599
1636
  const absorb = (report) => {
1600
1637
  for (const f of report.ownership.files) {
@@ -1603,6 +1640,9 @@ function runCliTransforms(params) {
1603
1640
  }
1604
1641
  externalBackups.push(...report.ownership.backupPaths);
1605
1642
  externalBackedUp.push(...report.ownership.backedUp);
1643
+ for (const f of report.ownership.foreignOwned) {
1644
+ if (!externalForeignOwned.includes(f)) externalForeignOwned.push(f);
1645
+ }
1606
1646
  externalUpdated += report.ownership.updated;
1607
1647
  };
1608
1648
  let codex = null;
@@ -1653,14 +1693,15 @@ function runCliTransforms(params) {
1653
1693
  externalFiles,
1654
1694
  externalBackups,
1655
1695
  externalUpdated,
1656
- externalBackedUp
1696
+ externalBackedUp,
1697
+ externalForeignOwned
1657
1698
  };
1658
1699
  }
1659
1700
 
1660
1701
  // src/env-files.ts
1661
1702
  init_esm_shims();
1662
1703
  import { appendFileSync, existsSync as existsSync8, readFileSync as readFileSync7, writeFileSync as writeFileSync3 } from "fs";
1663
- import { join as join7 } from "path";
1704
+ import { join as join8 } from "path";
1664
1705
  var ENV_EXAMPLE_BODY = `# .env.example \u2014 csr-supabase Track
1665
1706
  # Copy to .env (gitignored) and fill in values: cp .env.example .env
1666
1707
 
@@ -1697,7 +1738,7 @@ function writeEnvExample(projectDir, tracks) {
1697
1738
  if (!tracks.some((t) => ENV_EXAMPLE_TRACKS.includes(t))) {
1698
1739
  return false;
1699
1740
  }
1700
- const path = join7(projectDir, ".env.example");
1741
+ const path = join8(projectDir, ".env.example");
1701
1742
  if (existsSync8(path)) {
1702
1743
  return false;
1703
1744
  }
@@ -1705,7 +1746,7 @@ function writeEnvExample(projectDir, tracks) {
1705
1746
  return true;
1706
1747
  }
1707
1748
  function addGitignoreEnv(projectDir) {
1708
- const path = join7(projectDir, ".gitignore");
1749
+ const path = join8(projectDir, ".gitignore");
1709
1750
  if (!existsSync8(path)) {
1710
1751
  return false;
1711
1752
  }
@@ -1723,7 +1764,7 @@ function addGitignoreEnv(projectDir) {
1723
1764
  var AGENT_ARTIFACT_DIRS = [".factory/", ".goose/", ".uzys-agent-harness/"];
1724
1765
  var GITIGNORE_AGENT_ARTIFACT_HEADER = "# agent CLI / harness \uC790\uB3D9 \uC0DD\uC131\uBB3C (auto-added by agent-harness)";
1725
1766
  function addGitignoreAgentArtifacts(projectDir) {
1726
- const path = join7(projectDir, ".gitignore");
1767
+ const path = join8(projectDir, ".gitignore");
1727
1768
  if (!existsSync8(path)) {
1728
1769
  return [];
1729
1770
  }
@@ -1748,7 +1789,7 @@ init_esm_shims();
1748
1789
  import { spawnSync } from "child_process";
1749
1790
  import { existsSync as existsSync9, readdirSync, readFileSync as readFileSync8 } from "fs";
1750
1791
  import { homedir as homedir2 } from "os";
1751
- import { join as join8 } from "path";
1792
+ import { join as join9 } from "path";
1752
1793
  var DEFAULT_SPAWN_TIMEOUT_MS = 12e4;
1753
1794
  function selectExternalTargets(assets, ctx) {
1754
1795
  const conditionPassed = filterApplicableAssets(assets, ctx).filter(
@@ -1824,7 +1865,7 @@ function installOne(asset, ctx) {
1824
1865
  cwd
1825
1866
  );
1826
1867
  case "shell-script": {
1827
- const scriptPath = join8(ctx.harnessRoot, method.script);
1868
+ const scriptPath = join9(ctx.harnessRoot, method.script);
1828
1869
  if (!existsSync9(scriptPath)) {
1829
1870
  return {
1830
1871
  asset,
@@ -1915,7 +1956,7 @@ function detectVersion(method, spawn) {
1915
1956
  if (at2 <= 0) return void 0;
1916
1957
  const plugin = method.pluginId.slice(0, at2);
1917
1958
  const marketplaceShort = method.pluginId.slice(at2 + 1);
1918
- const cacheBase = join8(homedir2(), ".claude/plugins/cache", marketplaceShort, plugin);
1959
+ const cacheBase = join9(homedir2(), ".claude/plugins/cache", marketplaceShort, plugin);
1919
1960
  if (!existsSync9(cacheBase)) return void 0;
1920
1961
  const versions = readdirSync(cacheBase).filter((v2) => /^\d/.test(v2)).sort();
1921
1962
  return versions.at(-1);
@@ -1923,7 +1964,7 @@ function detectVersion(method, spawn) {
1923
1964
  case "npm": {
1924
1965
  const npmRoot = getNpmGlobalRoot(spawn);
1925
1966
  if (!npmRoot) return void 0;
1926
- const pkgJson = join8(npmRoot, method.pkg, "package.json");
1967
+ const pkgJson = join9(npmRoot, method.pkg, "package.json");
1927
1968
  if (!existsSync9(pkgJson)) return void 0;
1928
1969
  const parsed = JSON.parse(readFileSync8(pkgJson, "utf8"));
1929
1970
  return parsed.version;
@@ -2028,7 +2069,7 @@ function writeMcpJson(path, mcp) {
2028
2069
  // src/superseded.ts
2029
2070
  init_esm_shims();
2030
2071
  import { existsSync as existsSync11, readFileSync as readFileSync10 } from "fs";
2031
- import { join as join9 } from "path";
2072
+ import { join as join10 } from "path";
2032
2073
  function sameTracks(a, b2) {
2033
2074
  if (a.length !== b2.length) return false;
2034
2075
  const sa = [...a].sort();
@@ -2052,7 +2093,7 @@ function findSuperseded(projectDir, spec, previous) {
2052
2093
  if (!id) continue;
2053
2094
  const key = policyKey(entry.target.slice(".claude/".length));
2054
2095
  if (!key) continue;
2055
- const abs = join9(projectDir, entry.target);
2096
+ const abs = join10(projectDir, entry.target);
2056
2097
  if (!existsSync11(abs)) continue;
2057
2098
  if (!isHarnessOwned(baseline, key, readFileSync10(abs, "utf-8"))) continue;
2058
2099
  out.push({ target: entry.target, id, source: entry.source });
@@ -2065,14 +2106,13 @@ init_esm_shims();
2065
2106
  import {
2066
2107
  copyFileSync,
2067
2108
  existsSync as existsSync12,
2068
- lstatSync,
2069
2109
  mkdirSync as mkdirSync3,
2070
2110
  readdirSync as readdirSync2,
2071
2111
  readFileSync as readFileSync11,
2072
2112
  unlinkSync,
2073
2113
  writeFileSync as writeFileSync5
2074
2114
  } from "fs";
2075
- import { basename as basename4, dirname as dirname3, join as join10 } from "path";
2115
+ import { basename as basename4, dirname as dirname3, join as join11 } from "path";
2076
2116
  var LEGACY_ANCHOR_FILE = ".claude/CLAUDE.md";
2077
2117
  function buildUpdateSpec(projectDir, tracks) {
2078
2118
  return {
@@ -2083,7 +2123,7 @@ function buildUpdateSpec(projectDir, tracks) {
2083
2123
  };
2084
2124
  }
2085
2125
  function runUpdateMode(projectDir, templatesDir, harnessRoot) {
2086
- const claudeDir = join10(projectDir, ".claude");
2126
+ const claudeDir = join11(projectDir, ".claude");
2087
2127
  const report = {
2088
2128
  updated: {},
2089
2129
  pruned: {},
@@ -2097,6 +2137,7 @@ function runUpdateMode(projectDir, templatesDir, harnessRoot) {
2097
2137
  policyBackedUp: [],
2098
2138
  externalUpdated: 0,
2099
2139
  externalBackedUp: [],
2140
+ foreignOwned: [],
2100
2141
  installedNew: [],
2101
2142
  restored: [],
2102
2143
  needsReinstall: [],
@@ -2108,8 +2149,8 @@ function runUpdateMode(projectDir, templatesDir, harnessRoot) {
2108
2149
  report.needsReinstall = fresh.needsReinstall;
2109
2150
  const policyBase = policyBaseline(projectDir);
2110
2151
  for (const { dir, ext } of POLICY_DIRS) {
2111
- const target = join10(claudeDir, dir);
2112
- const source = join10(templatesDir, dir);
2152
+ const target = join11(claudeDir, dir);
2153
+ const source = join11(templatesDir, dir);
2113
2154
  const label = `.claude/${dir}`;
2114
2155
  const ctx = { prefix: dir, baseline: policyBase };
2115
2156
  const synced = updateDir(target, source, ext, ctx);
@@ -2119,9 +2160,11 @@ function runUpdateMode(projectDir, templatesDir, harnessRoot) {
2119
2160
  }
2120
2161
  refreshPolicyBaseline(projectDir, templatesDir);
2121
2162
  const skillSync = syncSkills(
2122
- join10(claudeDir, "skills"),
2123
- join10(templatesDir, "skills"),
2124
- skillBaseline(projectDir)
2163
+ join11(claudeDir, "skills"),
2164
+ join11(templatesDir, "skills"),
2165
+ skillBaseline(projectDir),
2166
+ /* @__PURE__ */ new Date(),
2167
+ (relInSkills) => foreignOwnedTarget(projectDir, `.claude/skills/${relInSkills}`)
2125
2168
  );
2126
2169
  report.updated[".claude/skills"] = skillSync.updated;
2127
2170
  report.skillsBackedUp = skillSync.backedUp;
@@ -2131,7 +2174,7 @@ function runUpdateMode(projectDir, templatesDir, harnessRoot) {
2131
2174
  if (existsSync12(claudeDir) && (installLog === null || installLog.spec.cli.includes("claude"))) {
2132
2175
  syncHarnessAnchor(projectDir, templatesDir, report);
2133
2176
  }
2134
- const settingsPath = join10(claudeDir, "settings.json");
2177
+ const settingsPath = join11(claudeDir, "settings.json");
2135
2178
  if (existsSync12(settingsPath)) {
2136
2179
  report.staleHookRefs = cleanStaleHookRefs(settingsPath, claudeDir);
2137
2180
  }
@@ -2139,12 +2182,23 @@ function runUpdateMode(projectDir, templatesDir, harnessRoot) {
2139
2182
  const external = refreshExternalCli(projectDir, harnessRoot);
2140
2183
  report.externalUpdated = external.externalUpdated;
2141
2184
  report.externalBackedUp = external.externalBackedUp;
2185
+ const saidBySlotRow = new Set(report.skillsSkippedLinks.map((id) => `.claude/skills/${id}`));
2186
+ const merged = [];
2187
+ for (const f of [
2188
+ ...skillSync.foreignOwned,
2189
+ ...fresh.foreignOwned,
2190
+ ...external.externalForeignOwned
2191
+ ]) {
2192
+ if (!saidBySlotRow.has(f) && !merged.includes(f)) merged.push(f);
2193
+ }
2194
+ report.foreignOwned = merged;
2142
2195
  return report;
2143
2196
  }
2144
2197
  function installNewAssets(projectDir, templatesDir) {
2145
2198
  const installed = [];
2146
2199
  const restored = [];
2147
2200
  const needsReinstall = [];
2201
+ const foreignOwned = [];
2148
2202
  const log = readInstallLog(projectDir);
2149
2203
  const claudeSelected = log?.spec.cli.includes("claude") ?? false;
2150
2204
  const baselineExcluded = new Set(log?.spec.baselineExclude ?? []);
@@ -2152,9 +2206,14 @@ function installNewAssets(projectDir, templatesDir) {
2152
2206
  for (const entry of trackOnlyFileAssets(installedTracks(projectDir))) {
2153
2207
  if (entry.target === HARNESS_ANCHOR_FILE) continue;
2154
2208
  if (isBaselineExcluded(entry.target, baselineExcluded)) continue;
2155
- const target = join10(projectDir, entry.target);
2209
+ const target = join11(projectDir, entry.target);
2210
+ const foreign = foreignOwnedTarget(projectDir, entry.target);
2211
+ if (foreign !== null) {
2212
+ if (!foreignOwned.includes(foreign)) foreignOwned.push(foreign);
2213
+ continue;
2214
+ }
2156
2215
  if (existsSync12(target)) continue;
2157
- const source = join10(templatesDir, entry.source);
2216
+ const source = join11(templatesDir, entry.source);
2158
2217
  if (!existsSync12(source)) continue;
2159
2218
  if (entry.target.startsWith(".claude/")) {
2160
2219
  if (!claudeSelected) continue;
@@ -2169,7 +2228,7 @@ function installNewAssets(projectDir, templatesDir) {
2169
2228
  if (priorBaseline.has(recordedAs)) restored.push(entry.target);
2170
2229
  else installed.push(entry.target);
2171
2230
  }
2172
- return { installed, restored, needsReinstall };
2231
+ return { installed, restored, needsReinstall, foreignOwned };
2173
2232
  }
2174
2233
  function trackOnlyFileAssets(tracks) {
2175
2234
  const base = { tracks, selectedInternalSkills: [] };
@@ -2180,12 +2239,12 @@ function trackOnlyFileAssets(tracks) {
2180
2239
  );
2181
2240
  }
2182
2241
  function syncHarnessAnchor(projectDir, templatesDir, report) {
2183
- if (existsSync12(join10(projectDir, LEGACY_ANCHOR_FILE))) {
2242
+ if (existsSync12(join11(projectDir, LEGACY_ANCHOR_FILE))) {
2184
2243
  report.legacyAnchor = LEGACY_ANCHOR_FILE;
2185
2244
  }
2186
- const templateMd = join10(templatesDir, "CLAUDE.md");
2245
+ const templateMd = join11(templatesDir, "CLAUDE.md");
2187
2246
  if (!existsSync12(templateMd)) return;
2188
- const anchor = join10(projectDir, HARNESS_ANCHOR_FILE);
2247
+ const anchor = join11(projectDir, HARNESS_ANCHOR_FILE);
2189
2248
  const existed = existsSync12(anchor);
2190
2249
  copyFileSync(templateMd, anchor);
2191
2250
  if (existed) {
@@ -2197,7 +2256,7 @@ function syncHarnessAnchor(projectDir, templatesDir, report) {
2197
2256
  recordAnchorBaseline(projectDir, anchor);
2198
2257
  }
2199
2258
  function upsertRootImport(projectDir) {
2200
- const target = join10(projectDir, "CLAUDE.md");
2259
+ const target = join11(projectDir, "CLAUDE.md");
2201
2260
  const existing = existsSync12(target) ? readFileSync11(target, "utf8") : null;
2202
2261
  const next = upsertHarnessImport(existing, {
2203
2262
  projectName: basename4(projectDir),
@@ -2254,7 +2313,8 @@ function refreshExternalCli(projectDir, harnessRoot) {
2254
2313
  }
2255
2314
  return {
2256
2315
  externalUpdated: result.externalUpdated,
2257
- externalBackedUp: result.externalBackedUp
2316
+ externalBackedUp: result.externalBackedUp,
2317
+ externalForeignOwned: result.externalForeignOwned
2258
2318
  };
2259
2319
  }
2260
2320
  function isHarnessOwned2(ctx, rel, current) {
@@ -2266,8 +2326,8 @@ function updateDir(target, source, ext, ctx, now = /* @__PURE__ */ new Date()) {
2266
2326
  const backedUp = [];
2267
2327
  for (const file of readdirSync2(target)) {
2268
2328
  if (!file.endsWith(ext)) continue;
2269
- const targetFile = join10(target, file);
2270
- const sourceFile = join10(source, file);
2329
+ const targetFile = join11(target, file);
2330
+ const sourceFile = join11(source, file);
2271
2331
  if (!existsSync12(sourceFile)) continue;
2272
2332
  const next = readFileSync11(sourceFile, "utf8");
2273
2333
  const current = readFileSync11(targetFile, "utf8");
@@ -2281,23 +2341,29 @@ function updateDir(target, source, ext, ctx, now = /* @__PURE__ */ new Date()) {
2281
2341
  }
2282
2342
  return { updated, backedUp };
2283
2343
  }
2284
- function syncSkills(targetDir, sourceDir, baseline, now = /* @__PURE__ */ new Date()) {
2344
+ function syncSkills(targetDir, sourceDir, baseline, now = /* @__PURE__ */ new Date(), foreignOf) {
2285
2345
  if (!existsSync12(targetDir) || !existsSync12(sourceDir))
2286
- return { updated: 0, backedUp: [], skippedLinks: [] };
2346
+ return { updated: 0, backedUp: [], skippedLinks: [], foreignOwned: [] };
2287
2347
  let updated = 0;
2288
2348
  const backedUp = [];
2289
2349
  const skippedLinks = [];
2350
+ const foreignOwned = [];
2290
2351
  for (const skill of readdirSync2(sourceDir, { withFileTypes: true })) {
2291
2352
  if (!skill.isDirectory()) continue;
2292
- const targetSkill = join10(targetDir, skill.name);
2293
- if (!existsSync12(targetSkill)) continue;
2294
- if (lstatSync(targetSkill).isSymbolicLink()) {
2353
+ const targetSkill = join11(targetDir, skill.name);
2354
+ if (occupiedByNonDirectory(targetSkill)) {
2295
2355
  skippedLinks.push(skill.name);
2296
2356
  continue;
2297
2357
  }
2298
- for (const rel of listFilesRecursive(join10(sourceDir, skill.name))) {
2299
- const targetFile = join10(targetSkill, rel);
2300
- const next = readFileSync11(join10(sourceDir, skill.name, rel), "utf8");
2358
+ if (!existsSync12(targetSkill)) continue;
2359
+ for (const rel of listFilesRecursive(join11(sourceDir, skill.name))) {
2360
+ const targetFile = join11(targetSkill, rel);
2361
+ const foreign = foreignOf(`${skill.name}/${rel}`);
2362
+ if (foreign !== null) {
2363
+ if (!foreignOwned.includes(foreign)) foreignOwned.push(foreign);
2364
+ continue;
2365
+ }
2366
+ const next = readFileSync11(join11(sourceDir, skill.name, rel), "utf8");
2301
2367
  if (!existsSync12(targetFile)) {
2302
2368
  mkdirSync3(dirname3(targetFile), { recursive: true });
2303
2369
  writeFileSync5(targetFile, next);
@@ -2315,7 +2381,7 @@ function syncSkills(targetDir, sourceDir, baseline, now = /* @__PURE__ */ new Da
2315
2381
  updated++;
2316
2382
  }
2317
2383
  }
2318
- return { updated, backedUp, skippedLinks };
2384
+ return { updated, backedUp, skippedLinks, foreignOwned };
2319
2385
  }
2320
2386
  function skillBaseline(projectDir) {
2321
2387
  const log = readInstallLog(projectDir);
@@ -2354,9 +2420,9 @@ function pruneOrphans(target, source, ext, ctx, now = /* @__PURE__ */ new Date()
2354
2420
  const removed = [];
2355
2421
  for (const file of readdirSync2(target)) {
2356
2422
  if (!file.endsWith(ext)) continue;
2357
- const sourceFile = join10(source, file);
2423
+ const sourceFile = join11(source, file);
2358
2424
  if (!existsSync12(sourceFile)) {
2359
- const targetFile = join10(target, file);
2425
+ const targetFile = join11(target, file);
2360
2426
  if (!ctx.baseline.has(`${ctx.prefix}/${file}`)) continue;
2361
2427
  try {
2362
2428
  const current = readFileSync11(targetFile, "utf8");
@@ -2370,7 +2436,7 @@ function pruneOrphans(target, source, ext, ctx, now = /* @__PURE__ */ new Date()
2370
2436
  return removed;
2371
2437
  }
2372
2438
  function retireMcpAllowlist(projectDir, now = /* @__PURE__ */ new Date()) {
2373
- const target = join10(projectDir, ".mcp-allowlist");
2439
+ const target = join11(projectDir, ".mcp-allowlist");
2374
2440
  if (!existsSync12(target)) return null;
2375
2441
  try {
2376
2442
  const backup = backupFile(target, now);
@@ -2410,7 +2476,7 @@ function cleanStaleHookRefs(settingsPath, claudeDir) {
2410
2476
  function keepHookRef(hook, claudeDir, removed) {
2411
2477
  const relPath = projectAnchoredRef(hook?.command ?? "", claudeDir);
2412
2478
  if (relPath === void 0) return true;
2413
- const exists = existsSync12(join10(claudeDir, relPath));
2479
+ const exists = existsSync12(join11(claudeDir, relPath));
2414
2480
  if (!exists && !removed.includes(relPath)) removed.push(relPath);
2415
2481
  return exists;
2416
2482
  }
@@ -2437,11 +2503,11 @@ function projectAnchoredRef(command, claudeDir) {
2437
2503
  function runInstall(ctx) {
2438
2504
  const { harnessRoot, projectDir, spec } = ctx;
2439
2505
  const mode = ctx.mode ?? "fresh";
2440
- const templatesDir = join11(harnessRoot, "templates");
2506
+ const templatesDir = join12(harnessRoot, "templates");
2441
2507
  if (!existsSync13(templatesDir)) {
2442
2508
  throw new Error(`Templates dir not found: ${templatesDir}`);
2443
2509
  }
2444
- const claudeDir = join11(projectDir, ".claude");
2510
+ const claudeDir = join12(projectDir, ".claude");
2445
2511
  const previousLog = readInstallLog(projectDir);
2446
2512
  const claudeWasSelected = previousLog?.spec.cli.includes("claude") ?? false;
2447
2513
  if (mode === "update" && !existsSync13(claudeDir) && (previousLog === null || claudeWasSelected)) {
@@ -2474,6 +2540,7 @@ function runInstall(ctx) {
2474
2540
  externalBackups,
2475
2541
  externalUpdated: _externalUpdated,
2476
2542
  externalBackedUp: _externalBackedUp,
2543
+ externalForeignOwned,
2477
2544
  ...cliTransforms
2478
2545
  } = runCliTransforms({
2479
2546
  harnessRoot,
@@ -2507,6 +2574,11 @@ function runInstall(ctx) {
2507
2574
  backups: [...base.backups, ...externalBackups],
2508
2575
  baselineExcluded: base.excluded,
2509
2576
  baselineExcludedOnDisk: base.excludedOnDisk,
2577
+ // `.claude/` baseline 과 외부 CLI 산출물의 같은 판정을 **한 목록으로** 낸다.
2578
+ baselineForeignOwned: [
2579
+ ...base.foreignOwned,
2580
+ ...externalForeignOwned.filter((f) => !base.foreignOwned.includes(f))
2581
+ ],
2510
2582
  // 밀려난 자산 정리는 **manifest 복사가 끝난 뒤**에 한다. 앞에서 지우면 이번 설치가 다시
2511
2583
  // 깔 수도 있는 파일을 지웠다 되돌리는 셈이라, 무엇이 최종 상태인지 보고가 흐려진다.
2512
2584
  superseded: applySupersededCleanup(spec, projectDir, supersededFound)
@@ -2532,7 +2604,7 @@ function applySupersededCleanup(spec, projectDir, found) {
2532
2604
  const kept = [];
2533
2605
  for (const asset of found) {
2534
2606
  try {
2535
- unlinkSync2(join11(projectDir, asset.target));
2607
+ unlinkSync2(join12(projectDir, asset.target));
2536
2608
  removed.push(asset.target);
2537
2609
  } catch {
2538
2610
  kept.push(asset.target);
@@ -2542,9 +2614,9 @@ function applySupersededCleanup(spec, projectDir, found) {
2542
2614
  }
2543
2615
  function healStaleHookRefs(spec, projectDir) {
2544
2616
  if (!spec.cli.includes("claude")) return [];
2545
- const settingsPath = join11(projectDir, ".claude/settings.json");
2617
+ const settingsPath = join12(projectDir, ".claude/settings.json");
2546
2618
  if (!existsSync13(settingsPath)) return [];
2547
- return cleanStaleHookRefs(settingsPath, join11(projectDir, ".claude"));
2619
+ return cleanStaleHookRefs(settingsPath, join12(projectDir, ".claude"));
2548
2620
  }
2549
2621
  function resolveBackupPath(ctx, mode, claudeDir) {
2550
2622
  const wantBackup = ctx.backup ?? (mode === "update" || mode === "reinstall");
@@ -2559,6 +2631,7 @@ function runUpdateInstall(ctx, templatesDir, backupPath) {
2559
2631
  skipped: 0,
2560
2632
  baselineExcluded: [],
2561
2633
  baselineExcludedOnDisk: [],
2634
+ baselineForeignOwned: [],
2562
2635
  // update 는 밀려난 자산을 다루지 않는다 — 그 판정은 사용자가 옵션을 고르는 설치 경로의
2563
2636
  // 것이고, update 는 무엇을 골랐는지 다시 묻지 않는다.
2564
2637
  superseded: { removed: [], kept: [] },
@@ -2613,7 +2686,8 @@ function emptyClaudeBaseline() {
2613
2686
  rootClaudeMdLog: null,
2614
2687
  backups: [],
2615
2688
  excluded: [],
2616
- excludedOnDisk: []
2689
+ excludedOnDisk: [],
2690
+ foreignOwned: []
2617
2691
  };
2618
2692
  }
2619
2693
  function installCliNeutralAssets(manifestSpec, projectDir, templatesDir, baselineExcluded) {
@@ -2625,12 +2699,12 @@ function installCliNeutralAssets(manifestSpec, projectDir, templatesDir, baselin
2625
2699
  continue;
2626
2700
  }
2627
2701
  if (!isCliNeutralTarget(entry.target)) continue;
2628
- const source = join11(templatesDir, entry.source);
2702
+ const source = join12(templatesDir, entry.source);
2629
2703
  if (!existsSync13(source)) {
2630
2704
  result.skipped += 1;
2631
2705
  continue;
2632
2706
  }
2633
- copyFile(source, join11(projectDir, entry.target));
2707
+ copyFile(source, join12(projectDir, entry.target));
2634
2708
  result.filesCopied += 1;
2635
2709
  }
2636
2710
  return result;
@@ -2657,17 +2731,22 @@ function installClaudeBaseline(manifestSpec, projectDir, templatesDir, policyBas
2657
2731
  if (!entry.applies(manifestSpec)) {
2658
2732
  continue;
2659
2733
  }
2660
- const target = join11(projectDir, entry.target);
2734
+ const target = join12(projectDir, entry.target);
2661
2735
  if (isBaselineExcluded(entry.target, baselineExcluded)) {
2662
2736
  result.excluded.push(entry.target);
2663
2737
  if (existsSync13(target)) result.excludedOnDisk.push(entry.target);
2664
2738
  continue;
2665
2739
  }
2666
- const source = join11(templatesDir, entry.source);
2740
+ const source = join12(templatesDir, entry.source);
2667
2741
  if (!existsSync13(source)) {
2668
2742
  result.skipped += 1;
2669
2743
  continue;
2670
2744
  }
2745
+ const foreign = foreignOwnedTarget(projectDir, entry.target);
2746
+ if (foreign !== null) {
2747
+ if (!result.foreignOwned.includes(foreign)) result.foreignOwned.push(foreign);
2748
+ continue;
2749
+ }
2671
2750
  if (entry.type === "file") {
2672
2751
  if (entry.target === ".claude/settings.json") {
2673
2752
  const backup = backupFileIfChanged(target, readFileSync12(source, "utf-8"));
@@ -2683,12 +2762,18 @@ function installClaudeBaseline(manifestSpec, projectDir, templatesDir, policyBas
2683
2762
  copyFile(source, target);
2684
2763
  result.filesCopied += 1;
2685
2764
  } else {
2686
- copyDir(source, target);
2765
+ for (const foreign2 of copyDir(
2766
+ source,
2767
+ target,
2768
+ (relFile) => foreignOwnedTarget(projectDir, `${entry.target}/${relFile}`)
2769
+ )) {
2770
+ if (!result.foreignOwned.includes(foreign2)) result.foreignOwned.push(foreign2);
2771
+ }
2687
2772
  result.dirsCopied += 1;
2688
2773
  }
2689
2774
  accumulateCategory(result.categories, entry);
2690
2775
  }
2691
- const hookDir = join11(projectDir, ".claude/hooks");
2776
+ const hookDir = join12(projectDir, ".claude/hooks");
2692
2777
  if (existsSync13(hookDir)) {
2693
2778
  chmodHooksSync(hookDir);
2694
2779
  }
@@ -2699,7 +2784,7 @@ function installClaudeBaseline(manifestSpec, projectDir, templatesDir, policyBas
2699
2784
  return result;
2700
2785
  }
2701
2786
  function harnessAnchorLog(projectDir) {
2702
- const anchor = join11(projectDir, HARNESS_ANCHOR_FILE);
2787
+ const anchor = join12(projectDir, HARNESS_ANCHOR_FILE);
2703
2788
  if (!existsSync13(anchor)) return null;
2704
2789
  return { path: HARNESS_ANCHOR_FILE, sha256: hashContent(readFileSync12(anchor, "utf-8")) };
2705
2790
  }
@@ -2760,7 +2845,7 @@ function writeInstallLogSafe(ctx, externalFiles, external, rootClaudeMdLog, prev
2760
2845
  rootFiles
2761
2846
  );
2762
2847
  const skillFiles = collectSkillHashes(ctx.projectDir);
2763
- const policyFiles = collectPolicyHashes(ctx.projectDir, join11(ctx.harnessRoot, "templates"));
2848
+ const policyFiles = collectPolicyHashes(ctx.projectDir, join12(ctx.harnessRoot, "templates"));
2764
2849
  const merged = mergeExternalFiles(ctx.projectDir, previousLog?.externalFiles, externalFiles);
2765
2850
  writeInstallLog(ctx.projectDir, {
2766
2851
  ...log,
@@ -2776,11 +2861,11 @@ function writeInstallLogSafe(ctx, externalFiles, external, rootClaudeMdLog, prev
2776
2861
  }
2777
2862
  }
2778
2863
  function composeAndWriteMcp(harnessRoot, projectDir, spec) {
2779
- const mcpPath = join11(projectDir, ".mcp.json");
2864
+ const mcpPath = join12(projectDir, ".mcp.json");
2780
2865
  const created = !existsSync13(mcpPath);
2781
2866
  const composed = composeMcpJson({
2782
- templateMcpPath: join11(harnessRoot, "templates/mcp.json"),
2783
- trackMapPath: join11(harnessRoot, "templates/track-mcp-map.tsv"),
2867
+ templateMcpPath: join12(harnessRoot, "templates/mcp.json"),
2868
+ trackMapPath: join12(harnessRoot, "templates/track-mcp-map.tsv"),
2784
2869
  existingPath: mcpPath,
2785
2870
  tracks: spec.tracks
2786
2871
  });
@@ -2833,14 +2918,14 @@ function accumulateCategory(cats, entry) {
2833
2918
  }
2834
2919
  }
2835
2920
  function writeInstalledTracks(projectDir, tracks) {
2836
- const path = join11(projectDir, ".claude/.installed-tracks");
2921
+ const path = join12(projectDir, ".claude/.installed-tracks");
2837
2922
  mkdirSync4(dirname4(path), { recursive: true });
2838
2923
  const sorted = [...new Set(tracks)].sort().join("\n");
2839
2924
  writeFileSync6(path, `${sorted}
2840
2925
  `);
2841
2926
  }
2842
2927
  function writeRootClaudeMd(projectDir, tracks) {
2843
- const target = join11(projectDir, "CLAUDE.md");
2928
+ const target = join12(projectDir, "CLAUDE.md");
2844
2929
  const existing = existsSync13(target) ? readFileSync12(target, "utf-8") : null;
2845
2930
  const content = upsertHarnessImport(existing, { projectName: basename5(projectDir), tracks });
2846
2931
  if (content !== existing) {
@@ -3205,8 +3290,17 @@ function renderPhase1Rows(log, baseline, verbose = false, withEcc = false, claud
3205
3290
  log(
3206
3291
  assetRow(
3207
3292
  "skip",
3208
- ".claude/skills linked",
3209
- `${baseline.updateMode.skillsSkippedLinks.join(", ")} \xB7 owned by another tool (symlink) \u2014 not updated`
3293
+ ".claude/skills owned by another tool",
3294
+ `${baseline.updateMode.skillsSkippedLinks.join(", ")} \xB7 \uADF8 \uC790\uB9AC\uAC00 \uC6B0\uB9AC \uB514\uB809\uD130\uB9AC\uAC00 \uC544\uB2C8\uB77C \uAC31\uC2E0\uD558\uC9C0 \uC54A\uC558\uB2E4`
3295
+ )
3296
+ );
3297
+ }
3298
+ if (baseline.updateMode.foreignOwned.length > 0) {
3299
+ log(
3300
+ assetRow(
3301
+ "skip",
3302
+ "owned by another tool",
3303
+ `${baseline.updateMode.foreignOwned.join(", ")} \xB7 \uADF8 \uC790\uB9AC\uAC00 \uC6B0\uB9AC \uAC83\uC774 \uC544\uB2C8\uB77C \uAC74\uB4DC\uB9AC\uC9C0 \uC54A\uC558\uB2E4`
3210
3304
  )
3211
3305
  );
3212
3306
  }
@@ -3326,6 +3420,15 @@ function renderPhase1Rows(log, baseline, verbose = false, withEcc = false, claud
3326
3420
  )
3327
3421
  );
3328
3422
  }
3423
+ if (baseline.baselineForeignOwned.length > 0) {
3424
+ log(
3425
+ assetRow(
3426
+ "skip",
3427
+ "owned by another tool",
3428
+ `${baseline.baselineForeignOwned.length} \u2014 ${baseline.baselineForeignOwned.join(", ")} \xB7 \uADF8 \uC790\uB9AC\uAC00 \uC6B0\uB9AC \uAC83\uC774 \uC544\uB2C8\uB77C \uAC74\uB4DC\uB9AC\uC9C0 \uC54A\uC558\uB2E4 \xB7 \uD558\uB124\uC2A4 \uD310\uBCF8\uC744 \uBC1B\uC73C\uB824\uBA74 \uADF8 \uC790\uB9AC\uB97C \uC62E\uAE30\uACE0 \uC7AC\uC124\uCE58`
3429
+ )
3430
+ );
3431
+ }
3329
3432
  if (baseline.superseded.removed.length > 0) {
3330
3433
  log(
3331
3434
  assetRow(
@@ -3381,9 +3484,7 @@ function renderPhase1Rows(log, baseline, verbose = false, withEcc = false, claud
3381
3484
  log(assetRow("success", ".mcp.json", mcpList, TEMPLATES_COL));
3382
3485
  if (!withEcc && baseline.categories) {
3383
3486
  log("");
3384
- log(
3385
- ` ${c.dim("\xB7")} ${c.dim("ECC plugin not selected \u2014 cherry-pick fallback active (up to 4 agents + 8 skills + 3 commands)")}`
3386
- );
3487
+ log(` ${c.dim("\xB7")} ${c.dim("ECC plugin not selected \u2014 cherry-pick fallback active")}`);
3387
3488
  if (claudeSelected) {
3388
3489
  log(` ${c.dim("\xB7")} ${c.dim("Use --with ecc-plugin to install ECC plugin instead")}`);
3389
3490
  }
@@ -3626,7 +3727,7 @@ function registerInstallCommand(cli2) {
3626
3727
  // src/commands/list.ts
3627
3728
  init_esm_shims();
3628
3729
  import { existsSync as existsSync14, readFileSync as readFileSync13 } from "fs";
3629
- import { join as join12, resolve as resolve3 } from "path";
3730
+ import { join as join13, resolve as resolve3 } from "path";
3630
3731
  function listAction(options = {}, deps = {}) {
3631
3732
  const log = deps.log ?? console.log;
3632
3733
  const err = deps.err ?? console.error;
@@ -3689,7 +3790,7 @@ function formatTemplateRows(log, projectDir) {
3689
3790
  const rows = [` ${c.dim(dirs.join(" "))}`];
3690
3791
  const rootMd = log.templates.rootClaudeMd;
3691
3792
  if (rootMd) {
3692
- const path = join12(projectDir, rootMd.path);
3793
+ const path = join13(projectDir, rootMd.path);
3693
3794
  const modified = existsSync14(path) && hashContent(readFileSync13(path, "utf8")) !== rootMd.sha256;
3694
3795
  rows.push(
3695
3796
  modified ? ` ${c.dim(rootMd.path)} ${c.yellow("(\uC218\uC815\uB428 \u2014 uninstall \uC2DC \uBCF4\uC874)")}` : ` ${c.dim(rootMd.path)}`
@@ -3698,7 +3799,7 @@ function formatTemplateRows(log, projectDir) {
3698
3799
  return rows;
3699
3800
  }
3700
3801
  function formatRootFileRows(rootFiles, projectDir) {
3701
- const present = rootFiles.filter((f) => existsSync14(join12(projectDir, f.path)));
3802
+ const present = rootFiles.filter((f) => existsSync14(join13(projectDir, f.path)));
3702
3803
  const width = Math.max(0, ...present.map((f) => f.path.length));
3703
3804
  return present.map(
3704
3805
  (f) => ` ${c.dim(padDisplay(f.path, width))} ${c.dim(f.change === "created" ? "\uC0DD\uC131" : "\uBCD1\uD569")} ${c.dim(f.notes.join(" / "))}`
@@ -3716,7 +3817,7 @@ function registerListCommand(cli2) {
3716
3817
  init_esm_shims();
3717
3818
  import { spawnSync as spawnSync2 } from "child_process";
3718
3819
  import { existsSync as existsSync15, readFileSync as readFileSync14, rmSync, writeFileSync as writeFileSync7 } from "fs";
3719
- import { join as join13, resolve as resolve4 } from "path";
3820
+ import { join as join14, resolve as resolve4 } from "path";
3720
3821
 
3721
3822
  // src/uninstall-interactive.ts
3722
3823
  init_esm_shims();
@@ -4951,7 +5052,7 @@ function settleLog(ctx, io) {
4951
5052
  return true;
4952
5053
  }
4953
5054
  } else {
4954
- io.rm(join13(projectDir, INSTALL_LOG_DIR));
5055
+ io.rm(join14(projectDir, INSTALL_LOG_DIR));
4955
5056
  io.rm(legacyInstallLogPath(projectDir));
4956
5057
  io.log(` ${status.success("install log removed")}`);
4957
5058
  }
@@ -5000,7 +5101,7 @@ function advisoryLines(plan, projectDir, rootFiles) {
5000
5101
  return lines;
5001
5102
  }
5002
5103
  function rootFileAdvisoryLines(rootFiles, projectDir) {
5003
- const present = rootFiles.filter((f) => existsSync15(join13(projectDir, f.path)));
5104
+ const present = rootFiles.filter((f) => existsSync15(join14(projectDir, f.path)));
5004
5105
  if (present.length === 0) return [];
5005
5106
  return [
5006
5107
  "",
@@ -5101,14 +5202,14 @@ function buildGlobalAdvisoryCmd(asset) {
5101
5202
  }
5102
5203
  }
5103
5204
  function removeTemplates(log, projectDir, rm) {
5104
- rm(join13(projectDir, log.templates.claudeDir));
5105
- if (log.templates.codexDir) rm(join13(projectDir, log.templates.codexDir));
5106
- if (log.templates.opencodeDir) rm(join13(projectDir, log.templates.opencodeDir));
5205
+ rm(join14(projectDir, log.templates.claudeDir));
5206
+ if (log.templates.codexDir) rm(join14(projectDir, log.templates.codexDir));
5207
+ if (log.templates.opencodeDir) rm(join14(projectDir, log.templates.opencodeDir));
5107
5208
  const importStripped = stripRootImport(projectDir);
5108
5209
  const rootMd = log.templates.rootClaudeMd;
5109
5210
  if (rootMd) {
5110
5211
  if (rootClaudeMdModified(log, projectDir)) return { rootClaudeMdKept: true, importStripped };
5111
- rm(join13(projectDir, rootMd.path));
5212
+ rm(join14(projectDir, rootMd.path));
5112
5213
  }
5113
5214
  return { rootClaudeMdKept: false, importStripped };
5114
5215
  }
@@ -5117,7 +5218,7 @@ function stripRootImport(projectDir) {
5117
5218
  const stripped = current === null ? null : stripHarnessImport(current);
5118
5219
  if (stripped === null) return false;
5119
5220
  try {
5120
- writeFileSync7(join13(projectDir, "CLAUDE.md"), stripped, "utf8");
5221
+ writeFileSync7(join14(projectDir, "CLAUDE.md"), stripped, "utf8");
5121
5222
  return true;
5122
5223
  } catch {
5123
5224
  return false;
@@ -5128,7 +5229,7 @@ function hasRootImport(projectDir) {
5128
5229
  return current !== null && stripHarnessImport(current) !== null;
5129
5230
  }
5130
5231
  function readRootClaudeMd(projectDir) {
5131
- const path = join13(projectDir, "CLAUDE.md");
5232
+ const path = join14(projectDir, "CLAUDE.md");
5132
5233
  if (!existsSync15(path)) return null;
5133
5234
  try {
5134
5235
  return readFileSync14(path, "utf8");
@@ -5139,7 +5240,7 @@ function readRootClaudeMd(projectDir) {
5139
5240
  function rootClaudeMdModified(log, projectDir) {
5140
5241
  const rootMd = log.templates.rootClaudeMd;
5141
5242
  if (!rootMd) return false;
5142
- const path = join13(projectDir, rootMd.path);
5243
+ const path = join14(projectDir, rootMd.path);
5143
5244
  if (!existsSync15(path)) return false;
5144
5245
  return hashContent(readFileSync14(path, "utf8")) !== rootMd.sha256;
5145
5246
  }
@@ -5202,7 +5303,7 @@ import { resolve as resolve5 } from "path";
5202
5303
  // src/state.ts
5203
5304
  init_esm_shims();
5204
5305
  import { existsSync as existsSync16, readFileSync as readFileSync15 } from "fs";
5205
- import { join as join14 } from "path";
5306
+ import { join as join15 } from "path";
5206
5307
  var META_FILE = ".claude/.installed-tracks";
5207
5308
  var LEGACY_SIGNATURES = [
5208
5309
  { rule: "htmx.md", track: "ssr-htmx" },
@@ -5212,10 +5313,10 @@ var LEGACY_SIGNATURES = [
5212
5313
  { rule: "cli-development.md", track: "tooling" }
5213
5314
  ];
5214
5315
  function detectInstallState(projectDir) {
5215
- const claudeDir = join14(projectDir, ".claude");
5316
+ const claudeDir = join15(projectDir, ".claude");
5216
5317
  const hasClaudeDir = existsSync16(claudeDir);
5217
5318
  if (hasClaudeDir) {
5218
- const metaPath = join14(projectDir, META_FILE);
5319
+ const metaPath = join15(projectDir, META_FILE);
5219
5320
  if (existsSync16(metaPath)) {
5220
5321
  const tracks2 = readMetafile(metaPath);
5221
5322
  return { state: "existing", tracks: tracks2, source: "metafile", hasClaudeDir: true };
@@ -5249,13 +5350,13 @@ function readMetafile(path) {
5249
5350
  return [...seen].sort();
5250
5351
  }
5251
5352
  function inferFromLegacySignatures(projectDir) {
5252
- const rulesDir = join14(projectDir, ".claude/rules");
5353
+ const rulesDir = join15(projectDir, ".claude/rules");
5253
5354
  if (!existsSync16(rulesDir)) {
5254
5355
  return [];
5255
5356
  }
5256
5357
  const found = /* @__PURE__ */ new Set();
5257
5358
  for (const sig of LEGACY_SIGNATURES) {
5258
- if (existsSync16(join14(rulesDir, sig.rule))) {
5359
+ if (existsSync16(join15(rulesDir, sig.rule))) {
5259
5360
  found.add(sig.track);
5260
5361
  }
5261
5362
  }