@uzysjung/agent-harness 26.147.0 → 26.147.1

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