@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/README.ko.md +5 -5
- package/README.md +3 -3
- package/dist/{chunk-GPH5AWLX.js → chunk-B27AJIWG.js} +12 -3
- package/dist/chunk-B27AJIWG.js.map +1 -0
- package/dist/index.js +223 -123
- package/dist/index.js.map +1 -1
- package/dist/trust-tier-drift.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-GPH5AWLX.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -56,7 +56,7 @@ import {
|
|
|
56
56
|
summarizeContextCost,
|
|
57
57
|
upsertHarnessImport,
|
|
58
58
|
writeInstallLog
|
|
59
|
-
} from "./chunk-
|
|
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.
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
1204
|
+
const src = join4(harnessRoot, "templates/skills", id, "SKILL.md");
|
|
1170
1205
|
if (!existsSync4(src)) {
|
|
1171
1206
|
continue;
|
|
1172
1207
|
}
|
|
1173
|
-
const target =
|
|
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 =
|
|
1186
|
-
const templatePath =
|
|
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 =
|
|
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
|
|
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 ??
|
|
1241
|
-
const configPath =
|
|
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
|
|
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(
|
|
1333
|
-
const agentsTemplate = readRequired(
|
|
1334
|
-
const configTemplate = readRequired(
|
|
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(
|
|
1337
|
-
const agentsMdPath =
|
|
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 =
|
|
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 =
|
|
1393
|
+
const hookDir = join6(projectDir, ".codex/hooks");
|
|
1359
1394
|
const hookFiles = [];
|
|
1360
1395
|
for (const hook of HOOK_NAMES) {
|
|
1361
|
-
const src =
|
|
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 =
|
|
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 =
|
|
1408
|
+
const src = join6(harnessRoot, "templates/skills", id, "SKILL.md");
|
|
1374
1409
|
if (!existsSync6(src)) {
|
|
1375
1410
|
continue;
|
|
1376
1411
|
}
|
|
1377
|
-
const target =
|
|
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
|
|
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(
|
|
1519
|
-
const agentsTemplate = readRequired2(
|
|
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
|
-
|
|
1556
|
+
join7(harnessRoot, "templates/opencode/opencode.json.template")
|
|
1522
1557
|
);
|
|
1523
1558
|
const projectName = basename3(projectDir);
|
|
1524
|
-
const mcp = readOptionalJson2(
|
|
1559
|
+
const mcp = readOptionalJson2(join7(harnessRoot, ".mcp.json"));
|
|
1525
1560
|
ensureDir(projectDir);
|
|
1526
|
-
const agentsMdPath =
|
|
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 =
|
|
1573
|
+
const opencodeJsonPath = join7(projectDir, "opencode.json");
|
|
1539
1574
|
writer.write(opencodeJsonPath, renderOpencodeJson({ template: opencodeTemplate, mcp }));
|
|
1540
|
-
const cmdDir =
|
|
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 =
|
|
1579
|
+
const src = join7(harnessRoot, "templates/skills", id, "SKILL.md");
|
|
1545
1580
|
if (!existsSync7(src)) {
|
|
1546
1581
|
continue;
|
|
1547
1582
|
}
|
|
1548
|
-
const target =
|
|
1549
|
-
const shellDependent = existsSync7(
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
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 =
|
|
2112
|
-
const source =
|
|
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
|
-
|
|
2123
|
-
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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(
|
|
2241
|
+
if (existsSync12(join11(projectDir, LEGACY_ANCHOR_FILE))) {
|
|
2184
2242
|
report.legacyAnchor = LEGACY_ANCHOR_FILE;
|
|
2185
2243
|
}
|
|
2186
|
-
const templateMd =
|
|
2244
|
+
const templateMd = join11(templatesDir, "CLAUDE.md");
|
|
2187
2245
|
if (!existsSync12(templateMd)) return;
|
|
2188
|
-
const anchor =
|
|
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 =
|
|
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 =
|
|
2270
|
-
const sourceFile =
|
|
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 =
|
|
2293
|
-
if (
|
|
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
|
-
|
|
2299
|
-
|
|
2300
|
-
const
|
|
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 =
|
|
2422
|
+
const sourceFile = join11(source, file);
|
|
2358
2423
|
if (!existsSync12(sourceFile)) {
|
|
2359
|
-
const targetFile =
|
|
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 =
|
|
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(
|
|
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 =
|
|
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 =
|
|
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(
|
|
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 =
|
|
2616
|
+
const settingsPath = join12(projectDir, ".claude/settings.json");
|
|
2546
2617
|
if (!existsSync13(settingsPath)) return [];
|
|
2547
|
-
return cleanStaleHookRefs(settingsPath,
|
|
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 =
|
|
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,
|
|
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 =
|
|
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 =
|
|
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(
|
|
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 =
|
|
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 =
|
|
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,
|
|
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 =
|
|
2863
|
+
const mcpPath = join12(projectDir, ".mcp.json");
|
|
2780
2864
|
const created = !existsSync13(mcpPath);
|
|
2781
2865
|
const composed = composeMcpJson({
|
|
2782
|
-
templateMcpPath:
|
|
2783
|
-
trackMapPath:
|
|
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 =
|
|
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 =
|
|
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
|
|
3209
|
-
`${baseline.updateMode.skillsSkippedLinks.join(", ")} \xB7
|
|
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
|
|
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 =
|
|
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(
|
|
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
|
|
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(
|
|
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(
|
|
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(
|
|
5105
|
-
if (log.templates.codexDir) rm(
|
|
5106
|
-
if (log.templates.opencodeDir) rm(
|
|
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(
|
|
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(
|
|
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 =
|
|
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 =
|
|
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
|
|
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 =
|
|
5315
|
+
const claudeDir = join15(projectDir, ".claude");
|
|
5216
5316
|
const hasClaudeDir = existsSync16(claudeDir);
|
|
5217
5317
|
if (hasClaudeDir) {
|
|
5218
|
-
const metaPath =
|
|
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 =
|
|
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(
|
|
5358
|
+
if (existsSync16(join15(rulesDir, sig.rule))) {
|
|
5259
5359
|
found.add(sig.track);
|
|
5260
5360
|
}
|
|
5261
5361
|
}
|