@uzysjung/agent-harness 26.170.0 → 26.172.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ko.md +5 -3
- package/README.md +5 -3
- package/dist/{chunk-MGFWOWF4.js → chunk-T2HBTG32.js} +30 -54
- package/dist/chunk-T2HBTG32.js.map +1 -0
- package/dist/index.js +417 -578
- package/dist/index.js.map +1 -1
- package/dist/trust-tier-drift.js +1 -1
- package/package.json +1 -1
- package/templates/CLAUDE.md +3 -1
- package/templates/agents/reviewer.md +10 -62
- package/templates/rules/htmx-fastapi-ui.md +36 -0
- package/templates/rules/htmx-fastify-ui.md +9 -0
- package/templates/rules/ship-checklist.md +2 -2
- package/templates/rules/test-policy.md +7 -4
- package/templates/skills/audit-harness-fit/references/apply.md +6 -1
- package/templates/skills/audit-harness-fit/references/verification.md +5 -0
- package/templates/skills/objective-brief/SKILL.md +3 -1
- package/templates/track-mcp-map.tsv +1 -1
- package/dist/chunk-MGFWOWF4.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -48,10 +48,7 @@ import {
|
|
|
48
48
|
claimBackupDir,
|
|
49
49
|
claudeRecorded,
|
|
50
50
|
claudeTargets,
|
|
51
|
-
collectPolicyHashes,
|
|
52
|
-
collectSkillHashes,
|
|
53
51
|
continuousNoteEnd,
|
|
54
|
-
copyDir,
|
|
55
52
|
copyFile,
|
|
56
53
|
corruptedInstallLogMessage,
|
|
57
54
|
createOutsideGuard,
|
|
@@ -71,7 +68,6 @@ import {
|
|
|
71
68
|
isAssetSelected,
|
|
72
69
|
isCliBase,
|
|
73
70
|
isCliNeutralTarget,
|
|
74
|
-
isHarnessOwned,
|
|
75
71
|
isInstallScope,
|
|
76
72
|
isKeyId,
|
|
77
73
|
isOutsideProject,
|
|
@@ -105,7 +101,7 @@ import {
|
|
|
105
101
|
targetsInclude,
|
|
106
102
|
upsertHarnessImport,
|
|
107
103
|
writeInstallLog
|
|
108
|
-
} from "./chunk-
|
|
104
|
+
} from "./chunk-T2HBTG32.js";
|
|
109
105
|
|
|
110
106
|
// node_modules/sisteransi/src/index.js
|
|
111
107
|
var require_src = __commonJS({
|
|
@@ -1200,6 +1196,13 @@ var FILES = `
|
|
|
1200
1196
|
.claude/skills/verification-loop/SKILL.md 1744f867f7868b83b480341c2e8d08bff3848c2408255871d3aba0c2f7eff2f2
|
|
1201
1197
|
.claude/skills/verification-loop/agents/openai.yaml 1907fd04fea5c83d15faf14acfc978de90076d4351341cf9259639aa0c3b7bd3
|
|
1202
1198
|
.claude/skills/verification-loop/references/tracks.md de11792d447c2e8ea67e8367c6655bd94646b1ba3f29184c4521cae01facc6ec
|
|
1199
|
+
.uzys-agent-harness/check-absence.sh b9bbc7bbdc69d51eac2a4e4748ee7c137dd585b4f3ca1d9bd2404eab9694c9b4
|
|
1200
|
+
.uzys-agent-harness/check-absence.sh c08d3a1df4188e5f37ddf7f7b53901184d87bdbed63c5c99afbddd0ef6a0442d
|
|
1201
|
+
.uzys-agent-harness/check-absence.sh 3655faf030e319e26a6386b316df004f4e2fee963fe0ebacb661030e85cda92e
|
|
1202
|
+
.uzys-agent-harness/protect-branch.sh 3356c4941ef1d5c174b10fd8858887f00b3b06c851788064e0e3cc6a6c8c56fd
|
|
1203
|
+
.uzys-agent-harness/protect-branch.sh ecd46450beae58c085a0d407c34441ae470092d9963bd4a37f09416a22cfad35
|
|
1204
|
+
.uzys-agent-harness/spec-drift-check.sh f35a64dd9954a776c3eabf356c0cda017db30e129d08022341a07c5a9d2b6f9a
|
|
1205
|
+
.uzys-agent-harness/spec-drift-check.sh 3c6925b03775454ec3a81e63dda400511824a9ac6e3f8f30b302ce1edf39d062
|
|
1203
1206
|
`;
|
|
1204
1207
|
var LEGACY_HARNESS_FILES = (() => {
|
|
1205
1208
|
const m = /* @__PURE__ */ new Map();
|
|
@@ -1535,13 +1538,14 @@ function planUpsert(args) {
|
|
|
1535
1538
|
}
|
|
1536
1539
|
function planStrip(args) {
|
|
1537
1540
|
const alwaysStrip2 = args.alwaysStrip ?? (() => false);
|
|
1538
|
-
const plan = { remove: [], kept: [], portions: /* @__PURE__ */ new Map() };
|
|
1541
|
+
const plan = { remove: [], removedEdited: [], kept: [], portions: /* @__PURE__ */ new Map() };
|
|
1539
1542
|
for (const [key, sha] of args.recorded) {
|
|
1540
1543
|
if (args.excluded.has(key)) continue;
|
|
1541
1544
|
const now = args.present.get(key);
|
|
1542
1545
|
if (now === void 0) continue;
|
|
1543
1546
|
if (now === sha || alwaysStrip2(key)) {
|
|
1544
1547
|
plan.remove.push(key);
|
|
1548
|
+
if (now !== sha) plan.removedEdited.push(key);
|
|
1545
1549
|
} else {
|
|
1546
1550
|
plan.kept.push(key);
|
|
1547
1551
|
plan.portions.set(key, sha);
|
|
@@ -1827,6 +1831,7 @@ var jsonKeys = {
|
|
|
1827
1831
|
text: changed ? serialize(root) : existing,
|
|
1828
1832
|
changed,
|
|
1829
1833
|
removed: plan.remove,
|
|
1834
|
+
removedEdited: plan.removedEdited,
|
|
1830
1835
|
kept: plan.kept,
|
|
1831
1836
|
portions: plan.portions,
|
|
1832
1837
|
empty: Object.keys(root).length === 0
|
|
@@ -2018,6 +2023,7 @@ var lines = {
|
|
|
2018
2023
|
text,
|
|
2019
2024
|
changed: text !== existing,
|
|
2020
2025
|
removed: plan.remove,
|
|
2026
|
+
removedEdited: plan.removedEdited,
|
|
2021
2027
|
kept: plan.kept,
|
|
2022
2028
|
portions: plan.portions,
|
|
2023
2029
|
empty: text.trim() === ""
|
|
@@ -2472,6 +2478,7 @@ var markerMd = {
|
|
|
2472
2478
|
text,
|
|
2473
2479
|
changed: text !== existing,
|
|
2474
2480
|
removed: plan.remove,
|
|
2481
|
+
removedEdited: plan.removedEdited,
|
|
2475
2482
|
kept: plan.kept,
|
|
2476
2483
|
portions: plan.portions,
|
|
2477
2484
|
empty: text.trim() === ""
|
|
@@ -3508,6 +3515,7 @@ var tomlRegion = {
|
|
|
3508
3515
|
text,
|
|
3509
3516
|
changed: text !== existing,
|
|
3510
3517
|
removed: plan.remove,
|
|
3518
|
+
removedEdited: plan.removedEdited,
|
|
3511
3519
|
kept: plan.kept,
|
|
3512
3520
|
portions: plan.portions,
|
|
3513
3521
|
empty: text.trim() === ""
|
|
@@ -3840,7 +3848,7 @@ function harnessWrite(input) {
|
|
|
3840
3848
|
const update = input.run === "update";
|
|
3841
3849
|
if (input.excluded) return j("leave", "", "keep");
|
|
3842
3850
|
if (disk === null) {
|
|
3843
|
-
if (rec.state
|
|
3851
|
+
if (rec.state !== "sha") return j("create", update ? LINES.newInRelease : LINES.wrote, "sha");
|
|
3844
3852
|
return j("create", update ? LINES.restored : LINES.wrote, "sha");
|
|
3845
3853
|
}
|
|
3846
3854
|
if (rec.state === "none") {
|
|
@@ -4182,6 +4190,9 @@ function ownersOf(path) {
|
|
|
4182
4190
|
)
|
|
4183
4191
|
);
|
|
4184
4192
|
}
|
|
4193
|
+
function bundlePath(path) {
|
|
4194
|
+
return path.startsWith(".agents/skills/") ? `${SKILLS_DIR}${path.slice(".agents/skills/".length)}` : path;
|
|
4195
|
+
}
|
|
4185
4196
|
function isRecordedTrackTarget(log, path) {
|
|
4186
4197
|
return recordedTrackTargets(log)(path);
|
|
4187
4198
|
}
|
|
@@ -4260,7 +4271,11 @@ function scanFieldOf(log, path) {
|
|
|
4260
4271
|
({ dir, ext }) => path.startsWith(`${CLAUDE_DIR}${dir}/`) && path.endsWith(ext)
|
|
4261
4272
|
);
|
|
4262
4273
|
if (policy === void 0) return "outside";
|
|
4263
|
-
return present2(
|
|
4274
|
+
return present2(
|
|
4275
|
+
(log.policyFiles ?? []).some(
|
|
4276
|
+
(f) => f.path.startsWith(`${policy.dir}/`) && !RETIRED_PATHS.includes(f.path)
|
|
4277
|
+
)
|
|
4278
|
+
);
|
|
4264
4279
|
}
|
|
4265
4280
|
function isExternalOnly(path) {
|
|
4266
4281
|
const owners = ownersOf(path);
|
|
@@ -4296,6 +4311,7 @@ function createInstallWriter(args) {
|
|
|
4296
4311
|
const portionPaths = [];
|
|
4297
4312
|
const backups = [];
|
|
4298
4313
|
const judged = [];
|
|
4314
|
+
const writes = [];
|
|
4299
4315
|
const shared2 = [];
|
|
4300
4316
|
const outside = createOutsideGuard(projectDir);
|
|
4301
4317
|
const rel = (abs) => relative(projectDir, abs).split(sep).join("/");
|
|
@@ -4310,12 +4326,15 @@ function createInstallWriter(args) {
|
|
|
4310
4326
|
if (outside.skip(abs)) return null;
|
|
4311
4327
|
const next = "source" in from ? readFileSync4(from.source, "utf8") : from.content;
|
|
4312
4328
|
const disk = existsSync6(abs) ? readFileSync4(abs, "utf8") : null;
|
|
4329
|
+
const rec = recorded(previousLog, path, opts);
|
|
4330
|
+
const diskSha = rec.state === "no-sha" && disk !== null ? hashContent(disk) : null;
|
|
4313
4331
|
const j4 = judge({
|
|
4314
4332
|
op: "write",
|
|
4315
4333
|
kind: "harness",
|
|
4316
|
-
rec:
|
|
4334
|
+
rec: diskSha !== null && isLegacyHarnessFile(path, diskSha) ? { state: "sha", sha256: diskSha } : rec,
|
|
4317
4335
|
disk,
|
|
4318
|
-
next
|
|
4336
|
+
next,
|
|
4337
|
+
...args.run === void 0 ? {} : { run: args.run }
|
|
4319
4338
|
});
|
|
4320
4339
|
let backupAbs;
|
|
4321
4340
|
const write = () => {
|
|
@@ -4357,13 +4376,11 @@ function createInstallWriter(args) {
|
|
|
4357
4376
|
default:
|
|
4358
4377
|
throw new Error(`install: '${j4.record}' is not a write record (${path})`);
|
|
4359
4378
|
}
|
|
4379
|
+
const line = backup === void 0 ? j4.line : j4.line.replace(BACKUP_PLACEHOLDER, backup);
|
|
4380
|
+
const backupPair = backup === void 0 || backupAbs === void 0 ? {} : { backup, backupAbs };
|
|
4381
|
+
writes.push({ path, verdict: j4.verdict, record: j4.record, line, ...backupPair });
|
|
4360
4382
|
if (j4.line === "" || j4.verdict === "create" || j4.verdict === "overwrite") return null;
|
|
4361
|
-
const out = {
|
|
4362
|
-
path,
|
|
4363
|
-
verdict: j4.verdict,
|
|
4364
|
-
line: backup === void 0 ? j4.line : j4.line.replace(BACKUP_PLACEHOLDER, backup),
|
|
4365
|
-
...backup === void 0 || backupAbs === void 0 ? {} : { backup, backupAbs }
|
|
4366
|
-
};
|
|
4383
|
+
const out = { path, verdict: j4.verdict, line, ...backupPair };
|
|
4367
4384
|
judged.push(out);
|
|
4368
4385
|
return out;
|
|
4369
4386
|
}
|
|
@@ -4424,7 +4441,12 @@ function createInstallWriter(args) {
|
|
|
4424
4441
|
if (disk === null && ![...res.portions.keys()].some((k2) => !isContainerKey(k2))) {
|
|
4425
4442
|
return result("leave", "");
|
|
4426
4443
|
}
|
|
4444
|
+
let backupAbs;
|
|
4427
4445
|
if (res.changed) {
|
|
4446
|
+
if (disk !== null && res.removedEdited.length > 0) {
|
|
4447
|
+
backupAbs = backupFile(abs, now);
|
|
4448
|
+
backups.push(backupAbs);
|
|
4449
|
+
}
|
|
4428
4450
|
mkdirSync(dirname(abs), { recursive: true });
|
|
4429
4451
|
writeFileSync3(abs, res.text);
|
|
4430
4452
|
}
|
|
@@ -4446,7 +4468,7 @@ function createInstallWriter(args) {
|
|
|
4446
4468
|
const ids = (keys) => [...keys].flatMap((k2) => keyId(path, k2) ?? []);
|
|
4447
4469
|
const out = excludedKeys(path, excluded);
|
|
4448
4470
|
const removedOut = res.removed.filter((k2) => out.has(k2));
|
|
4449
|
-
const removedEdited = res.removedEdited
|
|
4471
|
+
const removedEdited = res.removedEdited;
|
|
4450
4472
|
const keptOut = res.kept.filter((k2) => out.has(k2));
|
|
4451
4473
|
const onlyRemoved = disk !== null && res.removed.length > 0 && res.replaced.length === 0 && addedKeys.length === 0 && restoredKeys.size === 0;
|
|
4452
4474
|
const wrote = onlyRemoved ? "removed the harness part" : j4.line;
|
|
@@ -4454,6 +4476,7 @@ function createInstallWriter(args) {
|
|
|
4454
4476
|
const retired = names(res.removed.filter((k2) => !out.has(k2) && !render.has(k2)));
|
|
4455
4477
|
const replaced = names(res.replaced);
|
|
4456
4478
|
return result(j4.verdict, line, {
|
|
4479
|
+
...backupAbs === void 0 ? {} : { backup: rel(backupAbs), backupAbs },
|
|
4457
4480
|
...retired.length > 0 ? { retired } : {},
|
|
4458
4481
|
...replaced.length > 0 ? { replaced } : {},
|
|
4459
4482
|
changed: res.changed,
|
|
@@ -4481,6 +4504,7 @@ function createInstallWriter(args) {
|
|
|
4481
4504
|
portionPaths: [...portionPaths],
|
|
4482
4505
|
backups: [...backups],
|
|
4483
4506
|
judged: [...judged],
|
|
4507
|
+
writes: [...writes],
|
|
4484
4508
|
shared: [...shared2],
|
|
4485
4509
|
outside: outside.list()
|
|
4486
4510
|
})
|
|
@@ -4769,8 +4793,8 @@ function legacyGitignoreSeed(text, render, previous) {
|
|
|
4769
4793
|
|
|
4770
4794
|
// src/installer.ts
|
|
4771
4795
|
init_esm_shims();
|
|
4772
|
-
import { chmodSync as chmodSync3, existsSync as existsSync22, readdirSync as readdirSync5, readFileSync as
|
|
4773
|
-
import { basename as basename4, join as join31, relative as
|
|
4796
|
+
import { chmodSync as chmodSync3, existsSync as existsSync22, readdirSync as readdirSync5, readFileSync as readFileSync23, writeFileSync as writeFileSync8 } from "fs";
|
|
4797
|
+
import { basename as basename4, join as join31, relative as relative5, resolve, sep as sep6 } from "path";
|
|
4774
4798
|
|
|
4775
4799
|
// src/anchor-seed.ts
|
|
4776
4800
|
init_esm_shims();
|
|
@@ -5049,41 +5073,34 @@ function createOwnedWriter(projectDir, baseline, options = {}) {
|
|
|
5049
5073
|
return false;
|
|
5050
5074
|
}
|
|
5051
5075
|
if (outside.skip(absPath)) return false;
|
|
5052
|
-
const
|
|
5053
|
-
|
|
5054
|
-
|
|
5055
|
-
|
|
5076
|
+
const recordedSha = baseline.get(rel);
|
|
5077
|
+
const rec = recordedSha !== void 0 ? { state: "sha", sha256: recordedSha } : { state: owned(rel) ? "no-sha" : "none" };
|
|
5078
|
+
const disk = existsSync9(absPath) ? readFileSync6(absPath, "utf8") : null;
|
|
5079
|
+
if (disk === null && refreshOnly && !opts?.createInRefresh && rec.state !== "sha")
|
|
5080
|
+
return false;
|
|
5081
|
+
const { verdict, record } = judge({ op: "write", kind: "harness", rec, disk, next: content });
|
|
5082
|
+
let backupRel = null;
|
|
5083
|
+
if (verdict === "backup+overwrite" || verdict === "overwrite" && opts?.backupFirst === true) {
|
|
5084
|
+
const backup = backupFile(absPath, now);
|
|
5085
|
+
backupPaths.push(backup);
|
|
5086
|
+
journal?.backup(backup);
|
|
5087
|
+
backupRel = relative2(projectDir, backup).split(sep3).join("/");
|
|
5088
|
+
if (verdict === "backup+overwrite" && rec.state !== "none") backedUp.push(rel);
|
|
5089
|
+
}
|
|
5090
|
+
if (record === "displaced" || record === "displaced+sha") {
|
|
5091
|
+
displace({ path: rel, change: "displaced", notes: backupRel === null ? [] : [backupRel] });
|
|
5092
|
+
}
|
|
5093
|
+
if (verdict !== "leave") {
|
|
5056
5094
|
mkdirSync2(dirname2(absPath), { recursive: true });
|
|
5057
5095
|
writeFileSync4(absPath, content);
|
|
5058
5096
|
updated++;
|
|
5059
|
-
if (refreshOnly &&
|
|
5060
|
-
}
|
|
5061
|
-
|
|
5062
|
-
const
|
|
5063
|
-
|
|
5064
|
-
|
|
5065
|
-
return true;
|
|
5066
|
-
}
|
|
5067
|
-
if (current !== content) {
|
|
5068
|
-
if (!isHarnessOwned(baseline, rel, current)) {
|
|
5069
|
-
const backup = backupFile(absPath, now);
|
|
5070
|
-
backupPaths.push(backup);
|
|
5071
|
-
journal?.backup(backup);
|
|
5072
|
-
if (firstContact) {
|
|
5073
|
-
const backupRel = relative2(projectDir, backup).split(sep3).join("/");
|
|
5074
|
-
displace({ path: rel, change: "displaced", notes: [backupRel] });
|
|
5075
|
-
} else backedUp.push(rel);
|
|
5076
|
-
} else if (opts?.backupFirst === true) {
|
|
5077
|
-
const backup = backupFile(absPath, now);
|
|
5078
|
-
backupPaths.push(backup);
|
|
5079
|
-
journal?.backup(backup);
|
|
5080
|
-
}
|
|
5081
|
-
writeFileSync4(absPath, content);
|
|
5082
|
-
updated++;
|
|
5083
|
-
}
|
|
5097
|
+
if (refreshOnly && verdict === "create" && rec.state === "sha") restored.push(rel);
|
|
5098
|
+
}
|
|
5099
|
+
if (record === "sha" || record === "displaced+sha") {
|
|
5100
|
+
const digest = hashContent(content);
|
|
5101
|
+
written.set(rel, digest);
|
|
5102
|
+
journal?.file({ path: rel, sha256: digest });
|
|
5084
5103
|
}
|
|
5085
|
-
written.set(rel, digest);
|
|
5086
|
-
journal?.file({ path: rel, sha256: digest });
|
|
5087
5104
|
return true;
|
|
5088
5105
|
},
|
|
5089
5106
|
...journal === void 0 ? {} : { journal },
|
|
@@ -5158,7 +5175,7 @@ function renderRulesBlock(rules) {
|
|
|
5158
5175
|
// src/shared-write.ts
|
|
5159
5176
|
init_esm_shims();
|
|
5160
5177
|
import { existsSync as existsSync11, mkdirSync as mkdirSync3, readFileSync as readFileSync8, rmSync, statSync as statSync3, writeFileSync as writeFileSync5 } from "fs";
|
|
5161
|
-
import { dirname as dirname3, join as join13 } from "path";
|
|
5178
|
+
import { dirname as dirname3, join as join13, relative as relative3, sep as sep4 } from "path";
|
|
5162
5179
|
function toPortions(path, portions) {
|
|
5163
5180
|
const adapter = adapterFor(path);
|
|
5164
5181
|
if (adapter === null) throw new Error(`shared-write: ${path} is not a shared file`);
|
|
@@ -5396,6 +5413,10 @@ function stripShared(params) {
|
|
|
5396
5413
|
});
|
|
5397
5414
|
}
|
|
5398
5415
|
const fileRemoved = params.removeIfEmpty === true && res.removed.length > 0 && res.empty;
|
|
5416
|
+
const edited = res.removedEdited.length === 0 ? void 0 : {
|
|
5417
|
+
keys: res.removedEdited,
|
|
5418
|
+
backup: params.write ? relative3(projectDir, backupFile(abs)).split(sep4).join("/") : `${path}.backup-<time>`
|
|
5419
|
+
};
|
|
5399
5420
|
if (params.write && fileRemoved) rmSync(abs);
|
|
5400
5421
|
else if (params.write && res.changed) writeFileSync5(abs, res.text);
|
|
5401
5422
|
const action = res.removed.length > 0 ? "removed" : res.kept.length > 0 ? "left" : "none";
|
|
@@ -5403,6 +5424,7 @@ function stripShared(params) {
|
|
|
5403
5424
|
removed: res.removed,
|
|
5404
5425
|
kept: res.kept,
|
|
5405
5426
|
portions: toPortions(path, res.portions),
|
|
5427
|
+
...edited === void 0 ? {} : { edited },
|
|
5406
5428
|
...fileRemoved ? { fileRemoved } : {}
|
|
5407
5429
|
});
|
|
5408
5430
|
}
|
|
@@ -6362,7 +6384,7 @@ function runCliTransforms(params) {
|
|
|
6362
6384
|
|
|
6363
6385
|
// src/displaced.ts
|
|
6364
6386
|
init_esm_shims();
|
|
6365
|
-
import { lstatSync as lstatSync3, mkdirSync as mkdirSync5, renameSync } from "fs";
|
|
6387
|
+
import { lstatSync as lstatSync3, mkdirSync as mkdirSync5, readFileSync as readFileSync17, renameSync } from "fs";
|
|
6366
6388
|
import { dirname as dirname5, join as join23 } from "path";
|
|
6367
6389
|
function exists(abs) {
|
|
6368
6390
|
try {
|
|
@@ -6372,13 +6394,23 @@ function exists(abs) {
|
|
|
6372
6394
|
return false;
|
|
6373
6395
|
}
|
|
6374
6396
|
}
|
|
6397
|
+
function isLegacyDisplaced(log, projectDir, f) {
|
|
6398
|
+
if (f.change !== "displaced") return false;
|
|
6399
|
+
let original;
|
|
6400
|
+
try {
|
|
6401
|
+
original = readFileSync17(join23(projectDir, f.notes[0] ?? f.path), "utf8");
|
|
6402
|
+
} catch {
|
|
6403
|
+
return false;
|
|
6404
|
+
}
|
|
6405
|
+
return isLegacyHarnessFile(f.path, hashContent(original)) && !isRecordedTrackTarget(log, bundlePath(f.path));
|
|
6406
|
+
}
|
|
6375
6407
|
function restoreDisplaced(log, projectDir, opts) {
|
|
6376
6408
|
const out = [];
|
|
6377
6409
|
for (const f of log.rootFiles ?? []) {
|
|
6378
6410
|
if (f.change !== "displaced" || !opts.has(f.path)) continue;
|
|
6379
6411
|
const slot = join23(projectDir, f.path);
|
|
6380
6412
|
if (outsideProjectTarget(projectDir, slot) !== null) continue;
|
|
6381
|
-
const backup = f.notes[0] ?? null;
|
|
6413
|
+
const backup = isLegacyDisplaced(log, projectDir, f) ? null : f.notes[0] ?? null;
|
|
6382
6414
|
const d2 = judgeDisplaced({
|
|
6383
6415
|
backup,
|
|
6384
6416
|
backupExists: backup !== null && exists(join23(projectDir, backup)),
|
|
@@ -6447,13 +6479,13 @@ import {
|
|
|
6447
6479
|
constants as fsConstants,
|
|
6448
6480
|
mkdirSync as mkdirSync6,
|
|
6449
6481
|
readdirSync as readdirSync3,
|
|
6450
|
-
readFileSync as
|
|
6482
|
+
readFileSync as readFileSync18,
|
|
6451
6483
|
renameSync as renameSync2,
|
|
6452
6484
|
rmdirSync,
|
|
6453
6485
|
rmSync as rmSync3
|
|
6454
6486
|
} from "fs";
|
|
6455
6487
|
import { homedir as homedir2 } from "os";
|
|
6456
|
-
import { dirname as dirname6, join as join25, relative as
|
|
6488
|
+
import { dirname as dirname6, join as join25, relative as relative4, sep as sep5 } from "path";
|
|
6457
6489
|
var SKILL_ROOTS = [".claude/skills", ".agents/skills"];
|
|
6458
6490
|
function skillRootOf(cli) {
|
|
6459
6491
|
return cli === "claude" ? ".claude/skills" : ".agents/skills";
|
|
@@ -6465,7 +6497,7 @@ function snapshotSkillRoots(projectDir) {
|
|
|
6465
6497
|
for (const rel of listFilesRecursive(join25(projectDir, root))) {
|
|
6466
6498
|
const path = `${root}/${rel}`;
|
|
6467
6499
|
try {
|
|
6468
|
-
out.set(path, hashContent(
|
|
6500
|
+
out.set(path, hashContent(readFileSync18(join25(projectDir, path), "utf8")));
|
|
6469
6501
|
} catch {
|
|
6470
6502
|
}
|
|
6471
6503
|
}
|
|
@@ -6762,7 +6794,7 @@ function slotFilesToKeep(projectDir, hold) {
|
|
|
6762
6794
|
continue;
|
|
6763
6795
|
const abs = join25(projectDir, path);
|
|
6764
6796
|
try {
|
|
6765
|
-
if (recordedSha !== void 0 && hashContent(
|
|
6797
|
+
if (recordedSha !== void 0 && hashContent(readFileSync18(abs, "utf8")) === recordedSha)
|
|
6766
6798
|
continue;
|
|
6767
6799
|
accessSync(abs, fsConstants.R_OK);
|
|
6768
6800
|
} catch {
|
|
@@ -6776,12 +6808,12 @@ function slotFilesToKeep(projectDir, hold) {
|
|
|
6776
6808
|
function settleAside(projectDir, f, restore) {
|
|
6777
6809
|
let now = null;
|
|
6778
6810
|
try {
|
|
6779
|
-
now =
|
|
6811
|
+
now = readFileSync18(f.abs);
|
|
6780
6812
|
} catch (e2) {
|
|
6781
6813
|
const code = e2.code;
|
|
6782
6814
|
if (code !== "ENOENT" && code !== "ENOTDIR") throw e2;
|
|
6783
6815
|
}
|
|
6784
|
-
if (restore || (now === null ? !f.recorded : now.equals(
|
|
6816
|
+
if (restore || (now === null ? !f.recorded : now.equals(readFileSync18(f.aside)))) {
|
|
6785
6817
|
mkdirSync6(dirname6(f.abs), { recursive: true });
|
|
6786
6818
|
renameSync2(f.aside, f.abs);
|
|
6787
6819
|
return "back";
|
|
@@ -6815,7 +6847,7 @@ function pruneEmptyDirs(dir) {
|
|
|
6815
6847
|
}
|
|
6816
6848
|
}
|
|
6817
6849
|
function toProjectRel(projectDir, abs) {
|
|
6818
|
-
return
|
|
6850
|
+
return relative4(projectDir, abs).split(sep5).join("/");
|
|
6819
6851
|
}
|
|
6820
6852
|
function recordRefreshedFiles(projectDir, log, report, displaced, writeLog) {
|
|
6821
6853
|
const next = new Map(
|
|
@@ -6916,7 +6948,7 @@ function detectVersion(method, spawn, scope, projectDir) {
|
|
|
6916
6948
|
case "npm": {
|
|
6917
6949
|
const pkgJson = scope === "global" ? detectGlobalNpmPackageJsonPath(method.pkg, spawn) : join25(projectDir, "node_modules", method.pkg, "package.json");
|
|
6918
6950
|
if (!pkgJson || !existsSync19(pkgJson)) return void 0;
|
|
6919
|
-
const parsed = JSON.parse(
|
|
6951
|
+
const parsed = JSON.parse(readFileSync18(pkgJson, "utf8"));
|
|
6920
6952
|
return parsed.version;
|
|
6921
6953
|
}
|
|
6922
6954
|
default:
|
|
@@ -6930,7 +6962,7 @@ function detectPluginVersion(pluginId, scope, projectDir) {
|
|
|
6930
6962
|
const claudeScope = scope === "global" ? "user" : "project";
|
|
6931
6963
|
const path = join25(homedir2(), ".claude/plugins/installed_plugins.json");
|
|
6932
6964
|
if (!existsSync19(path)) return void 0;
|
|
6933
|
-
const data = JSON.parse(
|
|
6965
|
+
const data = JSON.parse(readFileSync18(path, "utf8"));
|
|
6934
6966
|
const entries = data.plugins?.[pluginId] ?? [];
|
|
6935
6967
|
const matches = entries.filter(
|
|
6936
6968
|
(e2) => claudeScope === "user" ? e2.scope === "user" : e2.scope === "project" && e2.projectPath === projectDir
|
|
@@ -6962,7 +6994,7 @@ function getNpmGlobalRoot(spawn) {
|
|
|
6962
6994
|
|
|
6963
6995
|
// src/key-ids.ts
|
|
6964
6996
|
init_esm_shims();
|
|
6965
|
-
import { readFileSync as
|
|
6997
|
+
import { readFileSync as readFileSync19 } from "fs";
|
|
6966
6998
|
import { join as join26 } from "path";
|
|
6967
6999
|
function renderedKeyIds(harnessRoot, input, clis) {
|
|
6968
7000
|
const out = /* @__PURE__ */ new Set();
|
|
@@ -6986,7 +7018,7 @@ function renderedKeyIds(harnessRoot, input, clis) {
|
|
|
6986
7018
|
for (const name of Object.keys(mcp.mcpServers)) add(".mcp.json", `mcpServers.${name}`);
|
|
6987
7019
|
for (const key of gitignoreRender().keys()) add(".gitignore", key);
|
|
6988
7020
|
if (clis.includes("claude")) {
|
|
6989
|
-
const template =
|
|
7021
|
+
const template = readFileSync19(join26(harnessRoot, "templates/settings.json"), "utf8");
|
|
6990
7022
|
for (const key of renderSettingsPortion(template, () => true).keys()) {
|
|
6991
7023
|
add(".claude/settings.json", key);
|
|
6992
7024
|
}
|
|
@@ -7050,7 +7082,7 @@ function refreshLinkedSkillBodies(params) {
|
|
|
7050
7082
|
|
|
7051
7083
|
// src/out-of-track.ts
|
|
7052
7084
|
init_esm_shims();
|
|
7053
|
-
import { lstatSync as lstatSync4, readFileSync as
|
|
7085
|
+
import { lstatSync as lstatSync4, readFileSync as readFileSync20, rmSync as rmSync4 } from "fs";
|
|
7054
7086
|
import { join as join28 } from "path";
|
|
7055
7087
|
var AGENTS_RULE = /^\.agents\/rules\/([^/]+)\.md$/;
|
|
7056
7088
|
var ANCHOR_RULE = "uzys-harness";
|
|
@@ -7080,7 +7112,7 @@ function reclaimOutOfTrack(args) {
|
|
|
7080
7112
|
out.bringBack[f.path] = tracksBringing(name);
|
|
7081
7113
|
continue;
|
|
7082
7114
|
}
|
|
7083
|
-
const disk = stat === void 0 ? null :
|
|
7115
|
+
const disk = stat === void 0 ? null : readFileSync20(abs, "utf8");
|
|
7084
7116
|
const verdict = judge({
|
|
7085
7117
|
op: "remove",
|
|
7086
7118
|
kind: "harness",
|
|
@@ -7112,7 +7144,7 @@ function tracksBringing(rule) {
|
|
|
7112
7144
|
|
|
7113
7145
|
// src/state.ts
|
|
7114
7146
|
init_esm_shims();
|
|
7115
|
-
import { existsSync as existsSync20, readFileSync as
|
|
7147
|
+
import { existsSync as existsSync20, readFileSync as readFileSync21 } from "fs";
|
|
7116
7148
|
import { join as join29 } from "path";
|
|
7117
7149
|
var META_FILE = ".claude/.installed-tracks";
|
|
7118
7150
|
var LEGACY_ANCHOR_FILE = ".claude/CLAUDE.md";
|
|
@@ -7141,7 +7173,7 @@ function tracksFromLog(tracks) {
|
|
|
7141
7173
|
}
|
|
7142
7174
|
function collectTraces(projectDir) {
|
|
7143
7175
|
const at5 = (rel) => join29(projectDir, rel);
|
|
7144
|
-
const contains = (rel, needle) => existsSync20(at5(rel)) &&
|
|
7176
|
+
const contains = (rel, needle) => existsSync20(at5(rel)) && readFileSync21(at5(rel), "utf8").includes(needle);
|
|
7145
7177
|
const traces = [];
|
|
7146
7178
|
if (existsSync20(at5(META_FILE)))
|
|
7147
7179
|
traces.push({ path: META_FILE, tracks: readMetafile(at5(META_FILE)) });
|
|
@@ -7156,7 +7188,7 @@ function collectTraces(projectDir) {
|
|
|
7156
7188
|
}
|
|
7157
7189
|
function readMetafile(path) {
|
|
7158
7190
|
const seen = /* @__PURE__ */ new Set();
|
|
7159
|
-
for (const word of
|
|
7191
|
+
for (const word of readFileSync21(path, "utf8").split(/\s+/)) {
|
|
7160
7192
|
if (isTrack(word)) seen.add(word);
|
|
7161
7193
|
}
|
|
7162
7194
|
return [...seen].sort();
|
|
@@ -7200,10 +7232,8 @@ init_esm_shims();
|
|
|
7200
7232
|
import {
|
|
7201
7233
|
copyFileSync,
|
|
7202
7234
|
existsSync as existsSync21,
|
|
7203
|
-
lstatSync as lstatSync5,
|
|
7204
|
-
mkdirSync as mkdirSync7,
|
|
7205
7235
|
readdirSync as readdirSync4,
|
|
7206
|
-
readFileSync as
|
|
7236
|
+
readFileSync as readFileSync22,
|
|
7207
7237
|
rmdirSync as rmdirSync2,
|
|
7208
7238
|
statSync as statSync4,
|
|
7209
7239
|
unlinkSync as unlinkSync2,
|
|
@@ -7268,91 +7298,12 @@ function runUpdateMode(projectDir, templatesDir, harnessRoot, deps = {}, only) {
|
|
|
7268
7298
|
demotedAgents: [],
|
|
7269
7299
|
mcpAllowlistRetired: null
|
|
7270
7300
|
};
|
|
7271
|
-
const fresh = installNewAssets(projectDir, templatesDir, wants, outside);
|
|
7272
|
-
report.installedNew = fresh.installed;
|
|
7273
|
-
report.restored = fresh.restored;
|
|
7274
7301
|
const logAtStart = readInstallLog(projectDir);
|
|
7275
7302
|
const claudeManaged = logAtStart === null || installedClis(logAtStart).includes("claude");
|
|
7276
|
-
const recordOf = recordedStateOf(logAtStart);
|
|
7277
|
-
const claudeDisplaced = [];
|
|
7278
7303
|
report.claudeUnrecorded = !claudeManaged && logAtStart !== null && existsSync21(claudeDir) ? recordClaudeCommand(logAtStart, installedTracks(projectDir)) : null;
|
|
7279
|
-
const
|
|
7280
|
-
const
|
|
7281
|
-
|
|
7282
|
-
if (!claudeManaged || !wants(dir === "hooks" ? "hooks" : "rules")) continue;
|
|
7283
|
-
const target = join30(claudeDir, dir);
|
|
7284
|
-
const source = join30(templatesDir, dir);
|
|
7285
|
-
const label = `.claude/${dir}`;
|
|
7286
|
-
const ctx = {
|
|
7287
|
-
prefix: dir,
|
|
7288
|
-
baseline: policyBase,
|
|
7289
|
-
outside,
|
|
7290
|
-
excluded: excludedIds(logAtStart),
|
|
7291
|
-
// 0단계가 방금 깐 파일은 기록 전이지만 하네스 것이다 — 첫 접촉으로 읽지 않는다
|
|
7292
|
-
justWritten: new Set(fresh.installed),
|
|
7293
|
-
...recordOf === void 0 ? {} : { recordOf }
|
|
7294
|
-
};
|
|
7295
|
-
const synced = updateDir(target, source, ext, ctx);
|
|
7296
|
-
report.updated[label] = synced.updated;
|
|
7297
|
-
report.policyBackedUp.push(...synced.backedUp);
|
|
7298
|
-
claudeDisplaced.push(...synced.displaced);
|
|
7299
|
-
report.pruned[label] = pruneOrphans(target, source, ext, ctx);
|
|
7300
|
-
for (const f of report.pruned[label]) prunedPaths.add(`${label}/${f}`);
|
|
7301
|
-
}
|
|
7302
|
-
const syncedDirs = POLICY_DIRS.filter((d2) => wants(d2.dir === "hooks" ? "hooks" : "rules")).map(
|
|
7303
|
-
(d2) => d2.dir
|
|
7304
|
-
);
|
|
7305
|
-
if (claudeManaged && syncedDirs.length > 0)
|
|
7306
|
-
refreshPolicyBaseline(
|
|
7307
|
-
projectDir,
|
|
7308
|
-
templatesDir,
|
|
7309
|
-
syncedDirs,
|
|
7310
|
-
outsidePaths(outside, ".claude/"),
|
|
7311
|
-
notOursUnder(claudeDisplaced, ".claude/")
|
|
7312
|
-
);
|
|
7313
|
-
const prunedRestored = logAtStart === null || prunedPaths.size === 0 ? [] : restoreDisplaced(logAtStart, projectDir, { has: (p2) => prunedPaths.has(p2), write: true });
|
|
7314
|
-
const skillTargets = logAtStart === null ? void 0 : recordedSkillIds(projectDir, logAtStart);
|
|
7315
|
-
const skillSync = claudeManaged && wants("skills") ? syncSkills(
|
|
7316
|
-
join30(claudeDir, "skills"),
|
|
7317
|
-
join30(templatesDir, "skills"),
|
|
7318
|
-
skillBaseline(projectDir),
|
|
7319
|
-
/* @__PURE__ */ new Date(),
|
|
7320
|
-
(relInSkills) => foreignOwnedTarget(projectDir, `.claude/skills/${relInSkills}`),
|
|
7321
|
-
outside,
|
|
7322
|
-
excludedSkillOf(logAtStart),
|
|
7323
|
-
{
|
|
7324
|
-
...recordOf === void 0 ? {} : { recordOf },
|
|
7325
|
-
...skillTargets === void 0 ? {} : { targets: skillTargets }
|
|
7326
|
-
}
|
|
7327
|
-
) : {
|
|
7328
|
-
updated: 0,
|
|
7329
|
-
backedUp: [],
|
|
7330
|
-
skippedLinks: [],
|
|
7331
|
-
foreignOwned: [],
|
|
7332
|
-
pruned: [],
|
|
7333
|
-
displaced: []
|
|
7334
|
-
};
|
|
7335
|
-
claudeDisplaced.push(...skillSync.displaced);
|
|
7336
|
-
if (wants("skills")) report.updated[".claude/skills"] = skillSync.updated;
|
|
7337
|
-
report.skillsBackedUp = skillSync.backedUp;
|
|
7338
|
-
report.skillsSkippedLinks = skillSync.skippedLinks;
|
|
7339
|
-
report.skillsPruned = skillSync.pruned;
|
|
7340
|
-
const newSkills = claudeManaged && wants("new-skills") ? installNewSkillDirs(projectDir, templatesDir, installedTracks(projectDir), outside) : { installed: [], restored: [], foreignOwned: [] };
|
|
7341
|
-
report.installedNew.push(...newSkills.installed);
|
|
7342
|
-
report.restored.push(...newSkills.restored);
|
|
7343
|
-
const copiedSkills = [...newSkills.installed, ...newSkills.restored];
|
|
7344
|
-
if (claudeManaged && wants("skills"))
|
|
7345
|
-
refreshSkillBaseline(
|
|
7346
|
-
projectDir,
|
|
7347
|
-
templatesDir,
|
|
7348
|
-
outsidePaths(outside, ".claude/skills/"),
|
|
7349
|
-
skillTargets,
|
|
7350
|
-
notOursUnder(claudeDisplaced, ".claude/skills/")
|
|
7351
|
-
);
|
|
7352
|
-
else if (claudeManaged && copiedSkills.length > 0)
|
|
7353
|
-
recordNewSkillBaseline(projectDir, templatesDir, copiedSkills);
|
|
7354
|
-
if (claudeDisplaced.length > 0 || prunedRestored.length > 0)
|
|
7355
|
-
recordDisplaced(projectDir, claudeDisplaced, prunedRestored);
|
|
7304
|
+
const harness = logAtStart === null ? { displaced: [], restored: [], foreignOwned: [], outside: [] } : updateHarnessFiles({ projectDir, templatesDir, log: logAtStart, wants, outside, report });
|
|
7305
|
+
const claudeDisplaced = harness.displaced;
|
|
7306
|
+
const prunedRestored = harness.restored;
|
|
7356
7307
|
if (existsSync21(claudeDir) && claudeManaged) {
|
|
7357
7308
|
if (wants("anchor")) syncHarnessAnchor(projectDir, templatesDir, report, outside);
|
|
7358
7309
|
}
|
|
@@ -7388,8 +7339,6 @@ function runUpdateMode(projectDir, templatesDir, harnessRoot, deps = {}, only) {
|
|
|
7388
7339
|
if (wants("hooks")) report.mcpAllowlistRetired = retireMcpAllowlist(projectDir);
|
|
7389
7340
|
report.retiredAgents = staleAgentFiles(claudeDir);
|
|
7390
7341
|
report.demotedAgents = demotedAgentFiles(claudeDir, installedTracks(projectDir));
|
|
7391
|
-
if (logAtStart !== null && wants("rules"))
|
|
7392
|
-
refreshNeutralHelpers(projectDir, templatesDir, report);
|
|
7393
7342
|
const external = wants("external") ? refreshExternalCli(projectDir, harnessRoot, legacyDroppedKeys(logAtStart), harnessMcp) : {
|
|
7394
7343
|
externalUpdated: 0,
|
|
7395
7344
|
externalBackedUp: [],
|
|
@@ -7461,12 +7410,7 @@ function runUpdateMode(projectDir, templatesDir, harnessRoot, deps = {}, only) {
|
|
|
7461
7410
|
report.restoredWithout = restoredWithoutArgs(report.restored, installedTracks(projectDir));
|
|
7462
7411
|
const saidBySlotRow = new Set(report.skillsSkippedLinks.map((id) => `.claude/skills/${id}`));
|
|
7463
7412
|
const merged = [];
|
|
7464
|
-
for (const f of [
|
|
7465
|
-
...skillSync.foreignOwned,
|
|
7466
|
-
...fresh.foreignOwned,
|
|
7467
|
-
...newSkills.foreignOwned,
|
|
7468
|
-
...external.externalForeignOwned
|
|
7469
|
-
]) {
|
|
7413
|
+
for (const f of [...harness.foreignOwned, ...external.externalForeignOwned]) {
|
|
7470
7414
|
if (!saidBySlotRow.has(f) && !merged.includes(f)) merged.push(f);
|
|
7471
7415
|
}
|
|
7472
7416
|
report.foreignOwned = merged;
|
|
@@ -7496,7 +7440,12 @@ function runUpdateMode(projectDir, templatesDir, harnessRoot, deps = {}, only) {
|
|
|
7496
7440
|
).filter((id) => !skillRefresh.notInCatalog.includes(id))
|
|
7497
7441
|
].filter((id) => !saidAsRemoved.has(id));
|
|
7498
7442
|
report.externalSkillsUnknown = skillRefresh.unknown;
|
|
7499
|
-
const outsideLinks = mergeOutside(
|
|
7443
|
+
const outsideLinks = mergeOutside(
|
|
7444
|
+
harness.outside,
|
|
7445
|
+
outside.list(),
|
|
7446
|
+
sharedWrites.outside,
|
|
7447
|
+
external.externalOutside
|
|
7448
|
+
);
|
|
7500
7449
|
if (outsideLinks.length > 0) report.outsideLinks = outsideLinks;
|
|
7501
7450
|
const displacedAll = [...claudeDisplaced, ...external.displaced];
|
|
7502
7451
|
const displacedMoved = displacedAll.filter((f) => f.notes.length > 0).map((f) => f.path);
|
|
@@ -7590,7 +7539,9 @@ function checksumOnRecord(log) {
|
|
|
7590
7539
|
...(log.policyFiles ?? []).map((f) => `.claude/${f.path}`),
|
|
7591
7540
|
...(log.skillFiles ?? []).map((f) => `.claude/skills/${f.path}`),
|
|
7592
7541
|
...(log.externalFiles ?? []).map((f) => f.path),
|
|
7593
|
-
...log.assets.flatMap((a) => a.files ?? []).map((f) => f.path)
|
|
7542
|
+
...log.assets.flatMap((a) => a.files ?? []).map((f) => f.path),
|
|
7543
|
+
// #800 — 함께 쓰는 파일은 몫마다 sha 가 있다 — 그 백업은 설치자 편집이 확실하다
|
|
7544
|
+
...(log.portions ?? []).map((p2) => p2.path)
|
|
7594
7545
|
]);
|
|
7595
7546
|
const anchor = log.templates.rootClaudeMd;
|
|
7596
7547
|
if (anchor !== void 0) paths.add(anchor.path);
|
|
@@ -7603,7 +7554,7 @@ function retiredFeaturesPart(projectDir, f) {
|
|
|
7603
7554
|
}
|
|
7604
7555
|
function legacyHookOnDisk(projectDir, rel) {
|
|
7605
7556
|
try {
|
|
7606
|
-
return hasLegacyHarnessHook(
|
|
7557
|
+
return hasLegacyHarnessHook(readFileSync22(join30(projectDir, rel), "utf8"));
|
|
7607
7558
|
} catch {
|
|
7608
7559
|
return false;
|
|
7609
7560
|
}
|
|
@@ -7635,7 +7586,7 @@ function writeUpdateSharedFiles(args) {
|
|
|
7635
7586
|
const manifest = buildManifest(spec);
|
|
7636
7587
|
writeSettingsShared(
|
|
7637
7588
|
writer,
|
|
7638
|
-
|
|
7589
|
+
readFileSync22(settingsSource, "utf8"),
|
|
7639
7590
|
projectDir,
|
|
7640
7591
|
log,
|
|
7641
7592
|
(script) => {
|
|
@@ -7729,44 +7680,197 @@ function demotedAgentFiles(claudeDir, tracks) {
|
|
|
7729
7680
|
);
|
|
7730
7681
|
return readdirSync4(dir, { withFileTypes: true }).filter((e2) => e2.isFile() && e2.name.endsWith(".md")).map((e2) => basename3(e2.name, ".md")).filter((id) => demoted.includes(id));
|
|
7731
7682
|
}
|
|
7732
|
-
function
|
|
7733
|
-
const
|
|
7734
|
-
const
|
|
7683
|
+
function updateHarnessTargets(projectDir, templatesDir, log, wants, report, foreignOwned) {
|
|
7684
|
+
const files = [];
|
|
7685
|
+
const claude = installedClis(log).includes("claude");
|
|
7686
|
+
const excluded = excludedIds(log);
|
|
7687
|
+
for (const e2 of trackOnlyFileAssets(installedTracks(projectDir))) {
|
|
7688
|
+
if (e2.target === HARNESS_ANCHOR_FILE || e2.target === SETTINGS_TARGET) continue;
|
|
7689
|
+
if (!wants(e2.target.startsWith(".claude/hooks/") ? "hooks" : "rules")) continue;
|
|
7690
|
+
if (e2.target.startsWith(".claude/") && !claude) continue;
|
|
7691
|
+
if (isBaselineExcluded(e2.target, excluded)) continue;
|
|
7692
|
+
const source = join30(templatesDir, e2.source);
|
|
7693
|
+
if (!existsSync21(source)) continue;
|
|
7694
|
+
const policy = POLICY_DIRS.find((d2) => e2.target.startsWith(`.claude/${d2.dir}/`));
|
|
7695
|
+
files.push({
|
|
7696
|
+
path: e2.target,
|
|
7697
|
+
source,
|
|
7698
|
+
unit: e2.target,
|
|
7699
|
+
unitExisted: existsSync21(join30(projectDir, e2.target)),
|
|
7700
|
+
label: policy === void 0 ? e2.target : `.claude/${policy.dir}`
|
|
7701
|
+
});
|
|
7702
|
+
}
|
|
7703
|
+
const refreshedSkills = [];
|
|
7704
|
+
const skillsSource = join30(templatesDir, "skills");
|
|
7705
|
+
if (!claude || !(wants("skills") || wants("new-skills")) || !existsSync21(skillsSource)) {
|
|
7706
|
+
return { files, refreshedSkills };
|
|
7707
|
+
}
|
|
7708
|
+
const ids = recordedSkillIds(projectDir, log);
|
|
7709
|
+
const creatable = claudeSkillIds(projectDir, log);
|
|
7710
|
+
const excludedSkill = excludedSkillOf(log);
|
|
7711
|
+
for (const d2 of readdirSync4(skillsSource, { withFileTypes: true })) {
|
|
7712
|
+
if (!d2.isDirectory()) continue;
|
|
7713
|
+
const unit = `.claude/skills/${d2.name}`;
|
|
7714
|
+
if (occupiedByNonDirectory(join30(projectDir, unit))) {
|
|
7715
|
+
report.skillsSkippedLinks.push(d2.name);
|
|
7716
|
+
continue;
|
|
7717
|
+
}
|
|
7718
|
+
if (!ids.has(d2.name) || excludedSkill(d2.name)) continue;
|
|
7719
|
+
const existed = existsSync21(join30(projectDir, unit));
|
|
7720
|
+
if (existed ? !wants("skills") : !wants("new-skills") || !creatable.has(d2.name)) continue;
|
|
7721
|
+
for (const rel of listFilesRecursive(join30(skillsSource, d2.name))) {
|
|
7722
|
+
const foreign = foreignOwnedTarget(projectDir, `${unit}/${rel}`);
|
|
7723
|
+
if (foreign !== null) {
|
|
7724
|
+
if (!foreignOwned.includes(foreign)) foreignOwned.push(foreign);
|
|
7725
|
+
continue;
|
|
7726
|
+
}
|
|
7727
|
+
const source = join30(skillsSource, d2.name, rel);
|
|
7728
|
+
files.push({
|
|
7729
|
+
path: `${unit}/${rel}`,
|
|
7730
|
+
source,
|
|
7731
|
+
unit,
|
|
7732
|
+
unitExisted: existed,
|
|
7733
|
+
label: ".claude/skills"
|
|
7734
|
+
});
|
|
7735
|
+
}
|
|
7736
|
+
if (existed) refreshedSkills.push(d2.name);
|
|
7737
|
+
}
|
|
7738
|
+
return { files, refreshedSkills };
|
|
7739
|
+
}
|
|
7740
|
+
function updateHarnessFiles(args) {
|
|
7741
|
+
const { projectDir, templatesDir, log, wants, outside, report } = args;
|
|
7742
|
+
const now = /* @__PURE__ */ new Date();
|
|
7743
|
+
const claude = installedClis(log).includes("claude");
|
|
7744
|
+
const claims = retiredClaims(log, projectDir, log.policyFiles ?? []);
|
|
7745
|
+
const isTarget = recordedTrackTargets(log);
|
|
7746
|
+
const recordOf = (path) => recorded(log, path, { isTarget });
|
|
7747
|
+
const writer = createInstallWriter({
|
|
7748
|
+
projectDir,
|
|
7749
|
+
previousLog: log,
|
|
7750
|
+
excluded: excludedIds(log),
|
|
7751
|
+
now,
|
|
7752
|
+
run: "update"
|
|
7753
|
+
});
|
|
7735
7754
|
const foreignOwned = [];
|
|
7736
|
-
const
|
|
7737
|
-
const
|
|
7738
|
-
const
|
|
7739
|
-
const
|
|
7740
|
-
|
|
7741
|
-
|
|
7755
|
+
const targets = updateHarnessTargets(projectDir, templatesDir, log, wants, report, foreignOwned);
|
|
7756
|
+
for (const t of targets.files) writer.harness(t.path, { source: t.source }, { isTarget });
|
|
7757
|
+
const prunedPolicy = /* @__PURE__ */ new Set();
|
|
7758
|
+
for (const { dir, ext } of POLICY_DIRS) {
|
|
7759
|
+
if (!claude || !wants(dir === "hooks" ? "hooks" : "rules")) continue;
|
|
7760
|
+
const label = `.claude/${dir}`;
|
|
7761
|
+
report.updated[label] = 0;
|
|
7762
|
+
const ctx = { prefix: dir, outside, recordOf };
|
|
7763
|
+
report.pruned[label] = pruneOrphans(
|
|
7764
|
+
join30(projectDir, label),
|
|
7765
|
+
join30(templatesDir, dir),
|
|
7766
|
+
ext,
|
|
7767
|
+
ctx,
|
|
7768
|
+
now
|
|
7769
|
+
);
|
|
7770
|
+
for (const f of report.pruned[label]) prunedPolicy.add(`${dir}/${f}`);
|
|
7771
|
+
}
|
|
7772
|
+
if (claude && wants("skills")) report.updated[".claude/skills"] = 0;
|
|
7773
|
+
const skillPrune = pruneSkillExtras(
|
|
7774
|
+
projectDir,
|
|
7775
|
+
templatesDir,
|
|
7776
|
+
targets.refreshedSkills,
|
|
7777
|
+
outside,
|
|
7778
|
+
recordOf,
|
|
7779
|
+
now
|
|
7742
7780
|
);
|
|
7743
|
-
|
|
7744
|
-
|
|
7745
|
-
|
|
7746
|
-
|
|
7747
|
-
|
|
7748
|
-
|
|
7749
|
-
|
|
7750
|
-
|
|
7751
|
-
|
|
7781
|
+
report.skillsPruned = skillPrune.pruned;
|
|
7782
|
+
report.skillsBackedUp.push(...skillPrune.backedUp);
|
|
7783
|
+
const restored = prunedPolicy.size === 0 ? [] : restoreDisplaced(log, projectDir, {
|
|
7784
|
+
has: (p2) => p2.startsWith(".claude/") && prunedPolicy.has(p2.slice(".claude/".length)),
|
|
7785
|
+
write: true
|
|
7786
|
+
});
|
|
7787
|
+
const ledger = writer.ledger();
|
|
7788
|
+
recordHarnessWrites(projectDir, ledger, {
|
|
7789
|
+
claims,
|
|
7790
|
+
prunedPolicy,
|
|
7791
|
+
prunedSkill: new Set(skillPrune.pruned),
|
|
7792
|
+
restored,
|
|
7793
|
+
walkedSkills: wants("skills")
|
|
7794
|
+
});
|
|
7795
|
+
const byPath = new Map(targets.files.map((t) => [t.path, t]));
|
|
7796
|
+
const created = /* @__PURE__ */ new Map();
|
|
7797
|
+
for (const w3 of ledger.writes) {
|
|
7798
|
+
const t = byPath.get(w3.path);
|
|
7799
|
+
if (t === void 0) continue;
|
|
7800
|
+
if (!t.unitExisted) {
|
|
7801
|
+
if (w3.verdict === "create")
|
|
7802
|
+
created.set(t.unit, created.get(t.unit) === true || w3.line === LINES.restored);
|
|
7752
7803
|
continue;
|
|
7753
7804
|
}
|
|
7754
|
-
if (
|
|
7755
|
-
|
|
7756
|
-
if (
|
|
7757
|
-
|
|
7758
|
-
if (
|
|
7759
|
-
|
|
7760
|
-
|
|
7805
|
+
if (w3.verdict !== "leave") report.updated[t.label] = (report.updated[t.label] ?? 0) + 1;
|
|
7806
|
+
if (w3.verdict !== "backup+overwrite" || w3.record !== "sha") continue;
|
|
7807
|
+
if (w3.path.startsWith(".claude/skills/"))
|
|
7808
|
+
report.skillsBackedUp.push(w3.path.slice(".claude/skills/".length));
|
|
7809
|
+
else if (w3.path.startsWith(".claude/"))
|
|
7810
|
+
report.policyBackedUp.push(w3.path.slice(".claude/".length));
|
|
7811
|
+
}
|
|
7812
|
+
for (const [unit, wasRestored] of created)
|
|
7813
|
+
(wasRestored ? report.restored : report.installedNew).push(unit);
|
|
7814
|
+
return {
|
|
7815
|
+
displaced: ledger.rootFiles.filter((f) => f.change === "displaced"),
|
|
7816
|
+
restored,
|
|
7817
|
+
foreignOwned,
|
|
7818
|
+
outside: ledger.outside
|
|
7819
|
+
};
|
|
7820
|
+
}
|
|
7821
|
+
function pruneSkillExtras(projectDir, templatesDir, ids, outside, recordOf, now) {
|
|
7822
|
+
const pruned = [];
|
|
7823
|
+
const backedUp = [];
|
|
7824
|
+
for (const id of ids) {
|
|
7825
|
+
const targetSkill = join30(projectDir, ".claude/skills", id);
|
|
7826
|
+
const bundled = new Set(listFilesRecursive(join30(templatesDir, "skills", id)));
|
|
7827
|
+
for (const rel of listFilesRecursive(targetSkill)) {
|
|
7828
|
+
if (bundled.has(rel) || basename3(rel).includes(".backup-")) continue;
|
|
7829
|
+
if (foreignOwnedTarget(projectDir, `.claude/skills/${id}/${rel}`) !== null) continue;
|
|
7830
|
+
const targetFile = join30(targetSkill, rel);
|
|
7831
|
+
if (outside.skip(targetFile)) continue;
|
|
7832
|
+
const rec = recordOf(`.claude/skills/${id}/${rel}`);
|
|
7833
|
+
if (rec.state !== "sha" || hashContent(readFileSync22(targetFile, "utf8")) !== rec.sha256) {
|
|
7834
|
+
backupFile(targetFile, now);
|
|
7835
|
+
backedUp.push(`${id}/${rel}`);
|
|
7836
|
+
}
|
|
7837
|
+
unlinkSync2(targetFile);
|
|
7838
|
+
pruned.push(`${id}/${rel}`);
|
|
7839
|
+
for (let d2 = dirname7(targetFile); d2 !== targetSkill && readdirSync4(d2).length === 0; d2 = dirname7(d2))
|
|
7840
|
+
rmdirSync2(d2);
|
|
7761
7841
|
}
|
|
7762
|
-
mkdirSync7(dirname7(target), { recursive: true });
|
|
7763
|
-
copyFileSync(source, target);
|
|
7764
|
-
const recordedAs = entry.target.startsWith(".claude/") ? entry.target.slice(8) : entry.target;
|
|
7765
|
-
const wired = entry.target.startsWith(".claude/hooks/") && wiredBefore.has(entry.target.slice(".claude/hooks/".length));
|
|
7766
|
-
if (priorBaseline.has(recordedAs) || wired) restored.push(entry.target);
|
|
7767
|
-
else installed.push(entry.target);
|
|
7768
7842
|
}
|
|
7769
|
-
return {
|
|
7843
|
+
return { pruned, backedUp };
|
|
7844
|
+
}
|
|
7845
|
+
function recordHarnessWrites(projectDir, ledger, x) {
|
|
7846
|
+
const log = readInstallLog(projectDir);
|
|
7847
|
+
if (!log) return;
|
|
7848
|
+
const skillLedger = log.skillFiles === void 0 && !x.walkedSkills ? [] : ledger.skillFiles;
|
|
7849
|
+
const policy = mergeExternalFiles(log.policyFiles, ledger.policyFiles).filter(
|
|
7850
|
+
(f) => !x.prunedPolicy.has(f.path)
|
|
7851
|
+
);
|
|
7852
|
+
const have = new Set(policy.map((f) => f.path));
|
|
7853
|
+
const next = {
|
|
7854
|
+
...log,
|
|
7855
|
+
policyFiles: [
|
|
7856
|
+
...policy,
|
|
7857
|
+
...x.claims.filter((c3) => !x.prunedPolicy.has(c3.path) && !have.has(c3.path))
|
|
7858
|
+
],
|
|
7859
|
+
skillFiles: mergeExternalFiles(log.skillFiles, skillLedger).filter(
|
|
7860
|
+
(f) => !x.prunedSkill.has(f.path)
|
|
7861
|
+
),
|
|
7862
|
+
externalFiles: mergeExternalFiles(log.externalFiles, ledger.externalFiles),
|
|
7863
|
+
rootFiles: withoutRestored(
|
|
7864
|
+
mergeRootFiles(log.rootFiles, withoutLostBackups(log.rootFiles, ledger.rootFiles)),
|
|
7865
|
+
x.restored
|
|
7866
|
+
)
|
|
7867
|
+
};
|
|
7868
|
+
for (const key of ["policyFiles", "skillFiles", "externalFiles", "rootFiles"])
|
|
7869
|
+
if (next[key]?.length === 0) delete next[key];
|
|
7870
|
+
try {
|
|
7871
|
+
writeInstallLog(projectDir, next);
|
|
7872
|
+
} catch {
|
|
7873
|
+
}
|
|
7770
7874
|
}
|
|
7771
7875
|
var BACKUP_SUFFIX = /\.backup-\d{8}T\d{6}(-\d+)?$/;
|
|
7772
7876
|
var BACKUP_SCAN_DIRS = [
|
|
@@ -7815,51 +7919,6 @@ function writeBackupList(projectDir, backups, noChecksum = /* @__PURE__ */ new S
|
|
|
7815
7919
|
} catch {
|
|
7816
7920
|
}
|
|
7817
7921
|
}
|
|
7818
|
-
function installNewSkillDirs(projectDir, templatesDir, tracks, outside) {
|
|
7819
|
-
const installed = [];
|
|
7820
|
-
const restored = [];
|
|
7821
|
-
const foreignOwned = [];
|
|
7822
|
-
const log = readInstallLog(projectDir);
|
|
7823
|
-
const priorIds = new Set((log?.skillFiles ?? []).map((f) => f.path.split("/")[0] ?? ""));
|
|
7824
|
-
const excluded = excludedIds(log);
|
|
7825
|
-
const skillExcluded = excluded;
|
|
7826
|
-
if (log !== null && !installedClis(log).includes("claude"))
|
|
7827
|
-
return { installed, restored, foreignOwned };
|
|
7828
|
-
const spec = buildAssetSpec({ tracks, options: DEFAULT_OPTIONS });
|
|
7829
|
-
for (const entry of buildManifest(spec)) {
|
|
7830
|
-
if (entry.type !== "dir" || !entry.target.startsWith(".claude/skills/")) continue;
|
|
7831
|
-
if (!entry.applies(spec) || isBaselineExcluded(entry.target, excluded)) continue;
|
|
7832
|
-
if (skillExcluded.has(entry.target.slice(".claude/skills/".length))) continue;
|
|
7833
|
-
const target = join30(projectDir, entry.target);
|
|
7834
|
-
const foreign = foreignOwnedTarget(projectDir, entry.target);
|
|
7835
|
-
if (foreign !== null) {
|
|
7836
|
-
if (!foreignOwned.includes(foreign)) foreignOwned.push(foreign);
|
|
7837
|
-
continue;
|
|
7838
|
-
}
|
|
7839
|
-
if (existsSync21(target)) continue;
|
|
7840
|
-
const source = join30(templatesDir, entry.source);
|
|
7841
|
-
if (!existsSync21(source)) continue;
|
|
7842
|
-
if (outside.skip(target)) continue;
|
|
7843
|
-
copyDir(source, target, (rel) => foreignOwnedTarget(projectDir, `${entry.target}/${rel}`));
|
|
7844
|
-
if (priorIds.has(entry.target.slice(".claude/skills/".length))) restored.push(entry.target);
|
|
7845
|
-
else installed.push(entry.target);
|
|
7846
|
-
}
|
|
7847
|
-
return { installed, restored, foreignOwned };
|
|
7848
|
-
}
|
|
7849
|
-
function recordNewSkillBaseline(projectDir, templatesDir, targets) {
|
|
7850
|
-
const log = readInstallLog(projectDir);
|
|
7851
|
-
if (!log) return;
|
|
7852
|
-
if (log.skillFiles === void 0) return;
|
|
7853
|
-
const ids = new Set(targets.map((t) => t.slice(".claude/skills/".length)));
|
|
7854
|
-
const added = collectSkillHashes(projectDir, templatesDir).filter(
|
|
7855
|
-
(f) => ids.has(f.path.split("/")[0] ?? "")
|
|
7856
|
-
);
|
|
7857
|
-
const kept = (log.skillFiles ?? []).filter((f) => !ids.has(f.path.split("/")[0] ?? ""));
|
|
7858
|
-
try {
|
|
7859
|
-
writeInstallLog(projectDir, { ...log, skillFiles: [...kept, ...added] });
|
|
7860
|
-
} catch {
|
|
7861
|
-
}
|
|
7862
|
-
}
|
|
7863
7922
|
function trackOnlyFileAssets(tracks) {
|
|
7864
7923
|
const base = { tracks, selectedInternalSkills: [] };
|
|
7865
7924
|
const optedOut = { ...base, withTauri: false };
|
|
@@ -7878,8 +7937,8 @@ function syncHarnessAnchor(projectDir, templatesDir, report, outside, now = /* @
|
|
|
7878
7937
|
if (outside.skip(anchor)) return;
|
|
7879
7938
|
const existed = existsSync21(anchor);
|
|
7880
7939
|
if (existed) {
|
|
7881
|
-
const current =
|
|
7882
|
-
if (current !==
|
|
7940
|
+
const current = readFileSync22(anchor, "utf8");
|
|
7941
|
+
if (current !== readFileSync22(templateMd, "utf8")) {
|
|
7883
7942
|
const recorded2 = readInstallLog(projectDir)?.templates.rootClaudeMd;
|
|
7884
7943
|
const owned = recorded2?.path === HARNESS_ANCHOR_FILE && hashContent(current) === recorded2.sha256;
|
|
7885
7944
|
if (!owned) {
|
|
@@ -7902,11 +7961,11 @@ function syncHarnessAnchor(projectDir, templatesDir, report, outside, now = /* @
|
|
|
7902
7961
|
function upsertRootImport(projectDir, outside) {
|
|
7903
7962
|
const target = join30(projectDir, "CLAUDE.md");
|
|
7904
7963
|
if (outside.skip(target)) return false;
|
|
7905
|
-
const existing = existsSync21(target) ?
|
|
7964
|
+
const existing = existsSync21(target) ? readFileSync22(target, "utf8") : null;
|
|
7906
7965
|
const next = upsertHarnessImport(existing, {
|
|
7907
7966
|
projectName: basename3(projectDir),
|
|
7908
7967
|
tracks: installedTracks(projectDir),
|
|
7909
|
-
// ADR-085 — update 는 선택 목록을 인자로 받지 않는다.
|
|
7968
|
+
// ADR-085 — update 는 선택 목록을 인자로 받지 않는다. 앞 단계(하네스 파일 쓰기)가 지나간 디스크로 판정한다:
|
|
7910
7969
|
// `.claude/skills/<id>` 가 있으면 그 스킬은 이 설치가 고른 것이다.
|
|
7911
7970
|
continuousSkills: CONTINUOUS_SKILLS.map((s) => s.id).filter(
|
|
7912
7971
|
(id) => existsSync21(join30(projectDir, ".claude/skills", id))
|
|
@@ -7930,7 +7989,7 @@ function recordAnchorBaseline(projectDir, anchor) {
|
|
|
7930
7989
|
...log.templates,
|
|
7931
7990
|
rootClaudeMd: {
|
|
7932
7991
|
path: HARNESS_ANCHOR_FILE,
|
|
7933
|
-
sha256: hashContent(
|
|
7992
|
+
sha256: hashContent(readFileSync22(anchor, "utf8"))
|
|
7934
7993
|
}
|
|
7935
7994
|
}
|
|
7936
7995
|
};
|
|
@@ -7979,16 +8038,20 @@ function recordedBundledSkills(projectDir, log) {
|
|
|
7979
8038
|
return INTERNAL_BUNDLED_SKILL_IDS.filter((id) => ids.has(id));
|
|
7980
8039
|
}
|
|
7981
8040
|
function recordedSkillIds(projectDir, log) {
|
|
8041
|
+
const ids = claudeSkillIds(projectDir, log);
|
|
8042
|
+
for (const f of log.externalFiles ?? []) {
|
|
8043
|
+
const id = SHARED_SKILL_ID.exec(f.path)?.[1];
|
|
8044
|
+
if (id !== void 0) ids.add(id);
|
|
8045
|
+
}
|
|
8046
|
+
return ids;
|
|
8047
|
+
}
|
|
8048
|
+
function claudeSkillIds(projectDir, log) {
|
|
7982
8049
|
const isTarget = recordedTrackTargets(log);
|
|
7983
8050
|
const ids = new Set(bundledSkillTargets(projectDir, log));
|
|
7984
8051
|
for (const f of log.skillFiles ?? []) {
|
|
7985
8052
|
const id = f.path.split("/")[0] ?? "";
|
|
7986
8053
|
if (recorded(log, `.claude/skills/${f.path}`, { isTarget }).state !== "none") ids.add(id);
|
|
7987
8054
|
}
|
|
7988
|
-
for (const f of log.externalFiles ?? []) {
|
|
7989
|
-
const id = SHARED_SKILL_ID.exec(f.path)?.[1];
|
|
7990
|
-
if (id !== void 0) ids.add(id);
|
|
7991
|
-
}
|
|
7992
8055
|
return ids;
|
|
7993
8056
|
}
|
|
7994
8057
|
var SHARED_SKILL_ID = /^\.agents\/skills\/([^/]+)\//;
|
|
@@ -8073,7 +8136,7 @@ function refreshExternalCli(projectDir, harnessRoot, droppedKeys, harnessMcp) {
|
|
|
8073
8136
|
// 기록 없음: CLI 경로로는 도달 불가 — 진입 판정(#699)이 먼저 거절. 엔진 단위 방어(빈 집합).
|
|
8074
8137
|
recordedClis: log === null ? [] : installedClis(log),
|
|
8075
8138
|
// #761 B-1 — 외부 기록이 아예 없던 옛 판의 산출물은 첫 접촉이 아니라 체크섬 없는 하네스 파일이다(install 과 같은 판정).
|
|
8076
|
-
// 앞 단계(
|
|
8139
|
+
// 앞 단계(하네스 파일 쓰기)가 헬퍼만 든 `externalFiles` 를 적어도 같은 답이다 — 헬퍼는 외부 자리로 세지 않는다(B-2)
|
|
8077
8140
|
owned: recordedOwner(log)
|
|
8078
8141
|
});
|
|
8079
8142
|
const anchorPath = result.anchorRetired?.action === "removed" ? result.anchorRetired.path : null;
|
|
@@ -8200,229 +8263,27 @@ function refreshExternalCli(projectDir, harnessRoot, droppedKeys, harnessMcp) {
|
|
|
8200
8263
|
})
|
|
8201
8264
|
};
|
|
8202
8265
|
}
|
|
8203
|
-
function updateDir(target, source, ext, ctx, now = /* @__PURE__ */ new Date()) {
|
|
8204
|
-
if (!existsSync21(target) || !existsSync21(source))
|
|
8205
|
-
return { updated: 0, backedUp: [], displaced: [] };
|
|
8206
|
-
let updated = 0;
|
|
8207
|
-
const backedUp = [];
|
|
8208
|
-
const displaced = [];
|
|
8209
|
-
for (const file of readdirSync4(target)) {
|
|
8210
|
-
if (!file.endsWith(ext)) continue;
|
|
8211
|
-
const targetFile = join30(target, file);
|
|
8212
|
-
const sourceFile = join30(source, file);
|
|
8213
|
-
if (!existsSync21(sourceFile)) continue;
|
|
8214
|
-
if (ctx.outside?.skip(targetFile)) continue;
|
|
8215
|
-
const projectRel = `.claude/${ctx.prefix}/${file}`;
|
|
8216
|
-
if (ctx.excluded && isBaselineExcluded(projectRel, ctx.excluded)) continue;
|
|
8217
|
-
if (ctx.justWritten?.has(projectRel)) continue;
|
|
8218
|
-
const next = readFileSync21(sourceFile, "utf8");
|
|
8219
|
-
const rec = ctx.recordOf?.(projectRel) ?? baselineRecord(ctx.baseline, `${ctx.prefix}/${file}`);
|
|
8220
|
-
const w3 = judgedUpdateWrite(targetFile, rec, next, now);
|
|
8221
|
-
if (w3.changed) updated++;
|
|
8222
|
-
if (w3.kind === "edited") backedUp.push(`${ctx.prefix}/${file}`);
|
|
8223
|
-
const d2 = displacedEntry(w3, projectRel, targetFile);
|
|
8224
|
-
if (d2 !== null) displaced.push(d2);
|
|
8225
|
-
}
|
|
8226
|
-
return { updated, backedUp, displaced };
|
|
8227
|
-
}
|
|
8228
|
-
function syncSkills(targetDir, sourceDir, baseline, now = /* @__PURE__ */ new Date(), foreignOf, outside, excludedSkill, opts = {}) {
|
|
8229
|
-
if (!existsSync21(targetDir) || !existsSync21(sourceDir))
|
|
8230
|
-
return {
|
|
8231
|
-
updated: 0,
|
|
8232
|
-
backedUp: [],
|
|
8233
|
-
skippedLinks: [],
|
|
8234
|
-
foreignOwned: [],
|
|
8235
|
-
pruned: [],
|
|
8236
|
-
displaced: []
|
|
8237
|
-
};
|
|
8238
|
-
let updated = 0;
|
|
8239
|
-
const backedUp = [];
|
|
8240
|
-
const skippedLinks = [];
|
|
8241
|
-
const foreignOwned = [];
|
|
8242
|
-
const pruned = [];
|
|
8243
|
-
const displaced = [];
|
|
8244
|
-
for (const skill of readdirSync4(sourceDir, { withFileTypes: true })) {
|
|
8245
|
-
if (!skill.isDirectory()) continue;
|
|
8246
|
-
const targetSkill = join30(targetDir, skill.name);
|
|
8247
|
-
if (occupiedByNonDirectory(targetSkill)) {
|
|
8248
|
-
skippedLinks.push(skill.name);
|
|
8249
|
-
continue;
|
|
8250
|
-
}
|
|
8251
|
-
if (opts.targets !== void 0 && !opts.targets.has(skill.name)) continue;
|
|
8252
|
-
if (!existsSync21(targetSkill)) continue;
|
|
8253
|
-
if (excludedSkill?.(skill.name)) continue;
|
|
8254
|
-
for (const rel of listFilesRecursive(join30(sourceDir, skill.name))) {
|
|
8255
|
-
const targetFile = join30(targetSkill, rel);
|
|
8256
|
-
const foreign = foreignOf(`${skill.name}/${rel}`);
|
|
8257
|
-
if (foreign !== null) {
|
|
8258
|
-
if (!foreignOwned.includes(foreign)) foreignOwned.push(foreign);
|
|
8259
|
-
continue;
|
|
8260
|
-
}
|
|
8261
|
-
if (outside?.skip(targetFile)) continue;
|
|
8262
|
-
const next = readFileSync21(join30(sourceDir, skill.name, rel), "utf8");
|
|
8263
|
-
const projectRel = `.claude/skills/${skill.name}/${rel}`;
|
|
8264
|
-
const rec = opts.recordOf?.(projectRel) ?? baselineRecord(baseline, `${skill.name}/${rel}`);
|
|
8265
|
-
const w3 = judgedUpdateWrite(targetFile, rec, next, now);
|
|
8266
|
-
if (w3.changed) updated++;
|
|
8267
|
-
if (w3.kind === "edited") backedUp.push(`${skill.name}/${rel}`);
|
|
8268
|
-
const d2 = displacedEntry(w3, projectRel, targetFile);
|
|
8269
|
-
if (d2 !== null) displaced.push(d2);
|
|
8270
|
-
}
|
|
8271
|
-
const bundled = new Set(listFilesRecursive(join30(sourceDir, skill.name)));
|
|
8272
|
-
for (const rel of listFilesRecursive(targetSkill)) {
|
|
8273
|
-
if (bundled.has(rel) || basename3(rel).includes(".backup-")) continue;
|
|
8274
|
-
if (foreignOf(`${skill.name}/${rel}`) !== null) continue;
|
|
8275
|
-
const targetFile = join30(targetSkill, rel);
|
|
8276
|
-
if (outside?.skip(targetFile)) continue;
|
|
8277
|
-
const rec = opts.recordOf?.(`.claude/skills/${skill.name}/${rel}`) ?? baselineRecord(baseline, `${skill.name}/${rel}`);
|
|
8278
|
-
if (rec.state !== "sha" || hashContent(readFileSync21(targetFile, "utf8")) !== rec.sha256) {
|
|
8279
|
-
backupFile(targetFile, now);
|
|
8280
|
-
backedUp.push(`${skill.name}/${rel}`);
|
|
8281
|
-
}
|
|
8282
|
-
unlinkSync2(targetFile);
|
|
8283
|
-
pruned.push(`${skill.name}/${rel}`);
|
|
8284
|
-
for (let d2 = dirname7(targetFile); d2 !== targetSkill && readdirSync4(d2).length === 0; d2 = dirname7(d2))
|
|
8285
|
-
rmdirSync2(d2);
|
|
8286
|
-
}
|
|
8287
|
-
}
|
|
8288
|
-
return { updated, backedUp, skippedLinks, foreignOwned, pruned, displaced };
|
|
8289
|
-
}
|
|
8290
|
-
function judgedUpdateWrite(abs, rec, next, now) {
|
|
8291
|
-
const disk = existsSync21(abs) ? readFileSync21(abs, "utf8") : null;
|
|
8292
|
-
const j4 = judge({ op: "write", kind: "harness", rec, disk, next, run: "update" });
|
|
8293
|
-
let backup;
|
|
8294
|
-
switch (j4.verdict) {
|
|
8295
|
-
case "leave":
|
|
8296
|
-
break;
|
|
8297
|
-
case "create":
|
|
8298
|
-
mkdirSync7(dirname7(abs), { recursive: true });
|
|
8299
|
-
writeFileSync7(abs, next);
|
|
8300
|
-
break;
|
|
8301
|
-
case "overwrite":
|
|
8302
|
-
writeFileSync7(abs, next);
|
|
8303
|
-
break;
|
|
8304
|
-
case "backup+overwrite":
|
|
8305
|
-
backup = backupFile(abs, now);
|
|
8306
|
-
writeFileSync7(abs, next);
|
|
8307
|
-
break;
|
|
8308
|
-
default:
|
|
8309
|
-
throw new Error(`update: '${j4.verdict}' is not a write verdict (${abs})`);
|
|
8310
|
-
}
|
|
8311
|
-
const changed = j4.verdict !== "leave";
|
|
8312
|
-
if (j4.record === "displaced") return { kind: "displacedSame", changed };
|
|
8313
|
-
if (backup === void 0) return { kind: changed ? "written" : "same", changed };
|
|
8314
|
-
return { kind: j4.record === "displaced+sha" ? "displaced" : "edited", changed, backup };
|
|
8315
|
-
}
|
|
8316
|
-
function displacedEntry(w3, projectRel, abs) {
|
|
8317
|
-
if (w3.kind === "displacedSame") return { path: projectRel, change: "displaced", notes: [] };
|
|
8318
|
-
if (w3.kind !== "displaced" || w3.backup === void 0) return null;
|
|
8319
|
-
const backupRel = `${projectRel}${w3.backup.slice(abs.length)}`;
|
|
8320
|
-
return { path: projectRel, change: "displaced", notes: [backupRel] };
|
|
8321
|
-
}
|
|
8322
|
-
function baselineRecord(baseline, key) {
|
|
8323
|
-
const sha256 = baseline.get(key);
|
|
8324
|
-
return sha256 === void 0 ? { state: "no-sha" } : { state: "sha", sha256 };
|
|
8325
|
-
}
|
|
8326
|
-
function recordedStateOf(log) {
|
|
8327
|
-
if (log === null) return void 0;
|
|
8328
|
-
const isTarget = recordedTrackTargets(log);
|
|
8329
|
-
return (projectRel) => recorded(log, projectRel, { isTarget });
|
|
8330
|
-
}
|
|
8331
|
-
function notOursUnder(displaced, prefix) {
|
|
8332
|
-
return new Set(
|
|
8333
|
-
displaced.filter((f) => f.notes.length === 0 && f.path.startsWith(prefix)).map((f) => f.path.slice(prefix.length))
|
|
8334
|
-
);
|
|
8335
|
-
}
|
|
8336
8266
|
function withoutLostBackups(prior, add) {
|
|
8337
8267
|
const backedUp = new Set(
|
|
8338
8268
|
(prior ?? []).filter((f) => f.change === "displaced" && f.notes.length > 0).map((f) => f.path)
|
|
8339
8269
|
);
|
|
8340
8270
|
return add.filter((f) => f.notes.length > 0 || !backedUp.has(f.path));
|
|
8341
8271
|
}
|
|
8342
|
-
function
|
|
8343
|
-
const log = readInstallLog(projectDir);
|
|
8344
|
-
if (!log) return;
|
|
8345
|
-
const add = withoutLostBackups(log.rootFiles, displaced);
|
|
8346
|
-
const settles = restored.some((d2) => d2.forget);
|
|
8347
|
-
if (add.length === 0 && !settles) return;
|
|
8348
|
-
const merged = add.length === 0 ? [...log.rootFiles ?? []] : mergeRootFiles(log.rootFiles, add);
|
|
8349
|
-
try {
|
|
8350
|
-
writeInstallLog(projectDir, { ...log, rootFiles: withoutRestored(merged, restored) });
|
|
8351
|
-
} catch {
|
|
8352
|
-
}
|
|
8353
|
-
}
|
|
8354
|
-
function skillBaseline(projectDir) {
|
|
8355
|
-
const log = readInstallLog(projectDir);
|
|
8356
|
-
return new Map((log?.skillFiles ?? []).map((f) => [f.path, f.sha256]));
|
|
8357
|
-
}
|
|
8358
|
-
function policyBaseline(projectDir) {
|
|
8359
|
-
const log = readInstallLog(projectDir);
|
|
8360
|
-
return new Map((log?.policyFiles ?? []).map((f) => [f.path, f.sha256]));
|
|
8361
|
-
}
|
|
8362
|
-
function refreshPolicyBaseline(projectDir, templatesDir, dirs, outside, notOurs = /* @__PURE__ */ new Set()) {
|
|
8363
|
-
const log = readInstallLog(projectDir);
|
|
8364
|
-
if (!log) return;
|
|
8365
|
-
const excluded = excludedIds(log);
|
|
8366
|
-
const inDirs = (path) => (dirs === void 0 || dirs.some((d2) => path.startsWith(`${d2}/`))) && !outside.has(path) && !isBaselineExcluded(`.claude/${path}`, excluded);
|
|
8367
|
-
const rewritten = [
|
|
8368
|
-
...(log.policyFiles ?? []).filter((f) => !inDirs(f.path)),
|
|
8369
|
-
...collectPolicyHashes(projectDir, templatesDir).filter(
|
|
8370
|
-
(f) => inDirs(f.path) && !notOurs.has(f.path)
|
|
8371
|
-
)
|
|
8372
|
-
];
|
|
8373
|
-
const policyFiles = [...rewritten, ...retiredClaims(log, projectDir, rewritten)];
|
|
8374
|
-
const next = { ...log };
|
|
8375
|
-
if (policyFiles.length > 0) next.policyFiles = policyFiles;
|
|
8376
|
-
else delete next.policyFiles;
|
|
8377
|
-
try {
|
|
8378
|
-
writeInstallLog(projectDir, next);
|
|
8379
|
-
} catch {
|
|
8380
|
-
}
|
|
8381
|
-
}
|
|
8382
|
-
function outsidePaths(outside, prefix) {
|
|
8383
|
-
return new Set(
|
|
8384
|
-
outside.list().filter((o) => o.path.startsWith(prefix)).map((o) => o.path.slice(prefix.length))
|
|
8385
|
-
);
|
|
8386
|
-
}
|
|
8387
|
-
function refreshSkillBaseline(projectDir, templatesDir, outside, targets, notOurs = /* @__PURE__ */ new Set()) {
|
|
8388
|
-
const log = readInstallLog(projectDir);
|
|
8389
|
-
if (!log) return;
|
|
8390
|
-
const excludedSkill = excludedSkillOf(log);
|
|
8391
|
-
const keepPrior = (path) => outside.has(path) || excludedSkill(path.split("/")[0] ?? "");
|
|
8392
|
-
const ours = (path) => (targets === void 0 || targets.has(path.split("/")[0] ?? "")) && !notOurs.has(path);
|
|
8393
|
-
const skillFiles = [
|
|
8394
|
-
...collectSkillHashes(projectDir, templatesDir).filter(
|
|
8395
|
-
(f) => !keepPrior(f.path) && ours(f.path)
|
|
8396
|
-
),
|
|
8397
|
-
...(log.skillFiles ?? []).filter((f) => keepPrior(f.path))
|
|
8398
|
-
];
|
|
8399
|
-
const next = { ...log };
|
|
8400
|
-
if (skillFiles.length > 0) next.skillFiles = skillFiles;
|
|
8401
|
-
else delete next.skillFiles;
|
|
8402
|
-
try {
|
|
8403
|
-
writeInstallLog(projectDir, next);
|
|
8404
|
-
} catch {
|
|
8405
|
-
}
|
|
8406
|
-
}
|
|
8407
|
-
function pruneOrphans(target, source, ext, ctx, now = /* @__PURE__ */ new Date()) {
|
|
8272
|
+
function pruneOrphans(target, source, ext, ctx, now) {
|
|
8408
8273
|
if (!existsSync21(target) || !existsSync21(source)) return [];
|
|
8409
8274
|
const removed = [];
|
|
8410
8275
|
for (const file of readdirSync4(target)) {
|
|
8411
|
-
if (!file.endsWith(ext)) continue;
|
|
8412
|
-
const
|
|
8413
|
-
|
|
8414
|
-
|
|
8415
|
-
const
|
|
8416
|
-
|
|
8417
|
-
if (ctx.outside
|
|
8418
|
-
|
|
8419
|
-
|
|
8420
|
-
|
|
8421
|
-
|
|
8422
|
-
unlinkSync2(targetFile);
|
|
8423
|
-
removed.push(file);
|
|
8424
|
-
} catch {
|
|
8425
|
-
}
|
|
8276
|
+
if (!file.endsWith(ext) || existsSync21(join30(source, file))) continue;
|
|
8277
|
+
const targetFile = join30(target, file);
|
|
8278
|
+
const rec = ctx.recordOf(`.claude/${ctx.prefix}/${file}`);
|
|
8279
|
+
try {
|
|
8280
|
+
const disk = readFileSync22(targetFile, "utf8");
|
|
8281
|
+
const { verdict } = judge({ op: "remove", kind: "harness", rec, disk, next: null });
|
|
8282
|
+
if (verdict === "leave" || ctx.outside.skip(targetFile)) continue;
|
|
8283
|
+
if (verdict === "backup+remove") backupFile(targetFile, now);
|
|
8284
|
+
unlinkSync2(targetFile);
|
|
8285
|
+
removed.push(file);
|
|
8286
|
+
} catch {
|
|
8426
8287
|
}
|
|
8427
8288
|
}
|
|
8428
8289
|
return removed;
|
|
@@ -8438,53 +8299,6 @@ function retireMcpAllowlist(projectDir, now = /* @__PURE__ */ new Date()) {
|
|
|
8438
8299
|
return null;
|
|
8439
8300
|
}
|
|
8440
8301
|
}
|
|
8441
|
-
function refreshNeutralHelpers(projectDir, templatesDir, report) {
|
|
8442
|
-
const spec = buildAssetSpec({ tracks: installedTracks(projectDir), options: DEFAULT_OPTIONS });
|
|
8443
|
-
const refreshed = /* @__PURE__ */ new Map();
|
|
8444
|
-
for (const entry of buildManifest(spec)) {
|
|
8445
|
-
if (entry.type !== "file" || !isCliNeutralTarget(entry.target)) continue;
|
|
8446
|
-
if (!entry.applies(spec)) continue;
|
|
8447
|
-
const abs = join30(projectDir, entry.target);
|
|
8448
|
-
const source = join30(templatesDir, entry.source);
|
|
8449
|
-
if (!existsSync21(source)) continue;
|
|
8450
|
-
let st2;
|
|
8451
|
-
try {
|
|
8452
|
-
st2 = lstatSync5(abs);
|
|
8453
|
-
} catch {
|
|
8454
|
-
continue;
|
|
8455
|
-
}
|
|
8456
|
-
if (st2.isSymbolicLink()) {
|
|
8457
|
-
report.helpersKept = [...report.helpersKept ?? [], entry.target];
|
|
8458
|
-
continue;
|
|
8459
|
-
}
|
|
8460
|
-
if (!st2.isFile()) continue;
|
|
8461
|
-
const log2 = readInstallLog(projectDir);
|
|
8462
|
-
const recorded2 = (log2?.externalFiles ?? []).find((f) => f.path === entry.target);
|
|
8463
|
-
const diskSha = hashContent(readFileSync21(abs, "utf8"));
|
|
8464
|
-
const freshContent = readFileSync21(source, "utf8");
|
|
8465
|
-
const freshSha = hashContent(freshContent);
|
|
8466
|
-
if (diskSha === freshSha) {
|
|
8467
|
-
if (recorded2?.sha256 !== freshSha) refreshed.set(entry.target, freshSha);
|
|
8468
|
-
continue;
|
|
8469
|
-
}
|
|
8470
|
-
if (recorded2 === void 0 || recorded2.sha256 !== diskSha) backupFile(abs);
|
|
8471
|
-
writeFileSync7(abs, freshContent, "utf8");
|
|
8472
|
-
refreshed.set(entry.target, freshSha);
|
|
8473
|
-
report.updated[entry.target] = (report.updated[entry.target] ?? 0) + 1;
|
|
8474
|
-
}
|
|
8475
|
-
if (refreshed.size === 0) return;
|
|
8476
|
-
const log = readInstallLog(projectDir);
|
|
8477
|
-
if (log === null) return;
|
|
8478
|
-
const next = (log.externalFiles ?? []).map(
|
|
8479
|
-
(f) => refreshed.has(f.path) ? { ...f, sha256: refreshed.get(f.path) } : f
|
|
8480
|
-
);
|
|
8481
|
-
for (const [path, sha256] of refreshed)
|
|
8482
|
-
if (!next.some((f) => f.path === path)) next.push({ path, sha256 });
|
|
8483
|
-
try {
|
|
8484
|
-
writeInstallLog(projectDir, { ...log, externalFiles: next });
|
|
8485
|
-
} catch {
|
|
8486
|
-
}
|
|
8487
|
-
}
|
|
8488
8302
|
function recordClaudeCommand(log, tracks) {
|
|
8489
8303
|
const trackArgs = (tracks.length > 0 ? tracks : log.spec.tracks).map((t) => `--track ${t}`);
|
|
8490
8304
|
return `agent-harness install ${trackArgs.join(" ")} --cli claude --scope ${log.scope}`.replace(
|
|
@@ -8529,10 +8343,10 @@ function runInstall(ctx) {
|
|
|
8529
8343
|
const settingsPath = join31(projectDir, ".claude", "settings.json");
|
|
8530
8344
|
if (existsSync22(settingsPath)) {
|
|
8531
8345
|
try {
|
|
8532
|
-
JSON.parse(
|
|
8346
|
+
JSON.parse(readFileSync23(settingsPath, "utf8"));
|
|
8533
8347
|
} catch {
|
|
8534
8348
|
throw new Error(
|
|
8535
|
-
`${
|
|
8349
|
+
`${relative5(process.cwd(), settingsPath)} is not valid JSON \u2014 left untouched, so no hooks were wired. Fix or delete that file, then run the install again`
|
|
8536
8350
|
);
|
|
8537
8351
|
}
|
|
8538
8352
|
}
|
|
@@ -8848,7 +8662,7 @@ function pendingKeyExcludes(projectDir, keyExclude, touched) {
|
|
|
8848
8662
|
if (path === null || done.has(path) || !existsSync22(join31(projectDir, path))) return [];
|
|
8849
8663
|
const name = adapterFor(path);
|
|
8850
8664
|
const keys = [...excludedKeys(path, [id])];
|
|
8851
|
-
const present2 = name === null ? null : ADAPTERS[name].read(
|
|
8665
|
+
const present2 = name === null ? null : ADAPTERS[name].read(readFileSync23(join31(projectDir, path), "utf8"), keys, projectDir);
|
|
8852
8666
|
return present2 !== null && present2.size === 0 ? [] : [{ id, path }];
|
|
8853
8667
|
});
|
|
8854
8668
|
}
|
|
@@ -9009,7 +8823,7 @@ function installClaudeBaseline(manifestSpec, projectDir, templatesDir, baselineE
|
|
|
9009
8823
|
function writeSettingsPortion(writer, source, projectDir, previousLog, hookInstalled) {
|
|
9010
8824
|
return writeSettingsShared(
|
|
9011
8825
|
writer,
|
|
9012
|
-
|
|
8826
|
+
readFileSync23(source, "utf8"),
|
|
9013
8827
|
projectDir,
|
|
9014
8828
|
previousLog,
|
|
9015
8829
|
hookInstalled
|
|
@@ -9153,7 +8967,7 @@ function recordInterruptedInstall(ctx, error, run) {
|
|
|
9153
8967
|
const codexTrust = registeredTrust(journal.codexOptIn, ctx.projectDir);
|
|
9154
8968
|
const written = writtenSoFar(ledger, cliFiles, rootFiles, stage.rootImportWritten);
|
|
9155
8969
|
if (codexTrust) written.push(`${codexTrust.configPath} (Codex trust entry for this folder)`);
|
|
9156
|
-
const projectRel = (abs) =>
|
|
8970
|
+
const projectRel = (abs) => relative5(ctx.projectDir, abs).split(sep6).join("/");
|
|
9157
8971
|
const writtenSet = new Set(written);
|
|
9158
8972
|
const backups = [...own.backups, ...journal.backups].map((abs) => {
|
|
9159
8973
|
const original = projectRel(backupOriginal(abs) ?? abs);
|
|
@@ -9295,7 +9109,7 @@ function accumulateCategory(cats, entry) {
|
|
|
9295
9109
|
function writeRootClaudeMd(projectDir, tracks, continuousSkills, harnessRoot, writer) {
|
|
9296
9110
|
const target = join31(projectDir, "CLAUDE.md");
|
|
9297
9111
|
if (writer.skipOutside(target)) return null;
|
|
9298
|
-
const existing = existsSync22(target) ?
|
|
9112
|
+
const existing = existsSync22(target) ? readFileSync23(target, "utf-8") : null;
|
|
9299
9113
|
const seeded = existing === null ? seedRootClaudeProjectContext(projectDir, harnessRoot) : null;
|
|
9300
9114
|
const content = upsertHarnessImport(existing, {
|
|
9301
9115
|
projectName: basename4(projectDir),
|
|
@@ -9329,8 +9143,8 @@ function listHookFiles(hookDir) {
|
|
|
9329
9143
|
// src/commands/install-render.ts
|
|
9330
9144
|
init_esm_shims();
|
|
9331
9145
|
import { homedir as homedir3 } from "os";
|
|
9332
|
-
import { existsSync as existsSync24, readdirSync as readdirSync6, readFileSync as
|
|
9333
|
-
import { join as join33, relative as
|
|
9146
|
+
import { existsSync as existsSync24, readdirSync as readdirSync6, readFileSync as readFileSync25 } from "fs";
|
|
9147
|
+
import { join as join33, relative as relative6 } from "path";
|
|
9334
9148
|
|
|
9335
9149
|
// src/preset-recommend.ts
|
|
9336
9150
|
init_esm_shims();
|
|
@@ -9366,7 +9180,7 @@ function groupAssetsByCategory(assetIds) {
|
|
|
9366
9180
|
|
|
9367
9181
|
// src/resident-entries.ts
|
|
9368
9182
|
init_esm_shims();
|
|
9369
|
-
import { existsSync as existsSync23, readFileSync as
|
|
9183
|
+
import { existsSync as existsSync23, readFileSync as readFileSync24 } from "fs";
|
|
9370
9184
|
import { basename as basename5, join as join32 } from "path";
|
|
9371
9185
|
function residentEntries(spec) {
|
|
9372
9186
|
const skillThere = (id) => INTERNAL_BUNDLED_SKILL_IDS.includes(id) && [".claude/skills", ".agents/skills"].some((d2) => existsSync23(join32(spec.projectDir, d2, id)));
|
|
@@ -9407,7 +9221,7 @@ function plannedClaudeContext(spec) {
|
|
|
9407
9221
|
if (!spec.cli.includes("claude")) return void 0;
|
|
9408
9222
|
const root = resolveBundleRoot();
|
|
9409
9223
|
const rootClaude = join32(spec.projectDir, "CLAUDE.md");
|
|
9410
|
-
const existing = existsSync23(rootClaude) ?
|
|
9224
|
+
const existing = existsSync23(rootClaude) ? readFileSync24(rootClaude, "utf-8") : null;
|
|
9411
9225
|
const seeded = existing === null ? seedRootClaudeProjectContext(spec.projectDir, root) : null;
|
|
9412
9226
|
if (existing === null && seeded === null) return void 0;
|
|
9413
9227
|
const manifestSpec = buildManifestSpec(spec);
|
|
@@ -9573,7 +9387,7 @@ function renderUpdateSummary(log, spec, report) {
|
|
|
9573
9387
|
log(
|
|
9574
9388
|
infoRow(
|
|
9575
9389
|
"NEXT",
|
|
9576
|
-
unmeasured === backups.length ? 'nothing to
|
|
9390
|
+
unmeasured === backups.length ? 'nothing to carry over unless you remember editing one of them \u2014 then ask audit-harness-fit: "update \uBC31\uC5C5\uBCF8\uC758 \uB0B4 \uD3B8\uC9D1\uC744 \uB0B4 \uD30C\uC77C\uB85C \uC62E\uACA8\uC918"' : 'backups are not loaded \u2014 to keep your edits across updates, ask audit-harness-fit: "update \uBC31\uC5C5\uBCF8\uC758 \uB0B4 \uD3B8\uC9D1\uC744 \uB0B4 \uD30C\uC77C\uB85C \uC62E\uACA8\uC918" (update replaces harness files every time, so edits put back inside them return as backups)'
|
|
9577
9391
|
)
|
|
9578
9392
|
);
|
|
9579
9393
|
}
|
|
@@ -9870,7 +9684,7 @@ function renderFinalSummary(log, spec, report, fromWizard) {
|
|
|
9870
9684
|
const home = process.env.CODEX_HOME ?? join33(homedir3(), ".codex");
|
|
9871
9685
|
const configPath = join33(home, "config.toml");
|
|
9872
9686
|
if (!existsSync24(configPath)) return false;
|
|
9873
|
-
return hasTrustEntry(
|
|
9687
|
+
return hasTrustEntry(readFileSync25(configPath, "utf8"), spec.projectDir);
|
|
9874
9688
|
} catch {
|
|
9875
9689
|
return false;
|
|
9876
9690
|
}
|
|
@@ -9911,7 +9725,7 @@ function renderFinalSummary(log, spec, report, fromWizard) {
|
|
|
9911
9725
|
}
|
|
9912
9726
|
function hasFillPrompt(path) {
|
|
9913
9727
|
try {
|
|
9914
|
-
return
|
|
9728
|
+
return readFileSync25(path, "utf8").includes("<!-- FILL:");
|
|
9915
9729
|
} catch {
|
|
9916
9730
|
return false;
|
|
9917
9731
|
}
|
|
@@ -10142,15 +9956,6 @@ function renderPhase1Rows(log, baseline, verbose = false) {
|
|
|
10142
9956
|
)
|
|
10143
9957
|
);
|
|
10144
9958
|
}
|
|
10145
|
-
if (baseline.updateMode.helpersKept?.length) {
|
|
10146
|
-
log(
|
|
10147
|
-
assetRow(
|
|
10148
|
-
"skip",
|
|
10149
|
-
"helper is a link",
|
|
10150
|
-
`${baseline.updateMode.helpersKept.join(", ")} \xB7 \uB9C1\uD06C\uB77C \uAC31\uC2E0\uD558\uC9C0 \uC54A\uACE0 \uB0A8\uACBC\uB2E4`
|
|
10151
|
-
)
|
|
10152
|
-
);
|
|
10153
|
-
}
|
|
10154
9959
|
for (const row of outsideLinkRows(baseline.updateMode.outsideLinks ?? [])) log(row);
|
|
10155
9960
|
if (baseline.updateMode.foreignOwned.length > 0) {
|
|
10156
9961
|
log(
|
|
@@ -10348,6 +10153,9 @@ function renderPhase1Rows(log, baseline, verbose = false) {
|
|
|
10348
10153
|
if (j4.backupAbs !== void 0) judgedBackups.add(j4.backupAbs);
|
|
10349
10154
|
log(judgedRow(j4));
|
|
10350
10155
|
}
|
|
10156
|
+
for (const f of baseline.shared ?? []) {
|
|
10157
|
+
if (f.backupAbs !== void 0 && f.removedEdited.length > 0) judgedBackups.add(f.backupAbs);
|
|
10158
|
+
}
|
|
10351
10159
|
for (const row of outsideLinkRows(baseline.outsideLinks ?? [])) log(row);
|
|
10352
10160
|
for (const row of outOfTrackRows(baseline.outOfTrack)) log(row);
|
|
10353
10161
|
for (const row of displacedRows(baseline.displacedRestored)) log(row);
|
|
@@ -10608,8 +10416,9 @@ function excludedKeyParts(f) {
|
|
|
10608
10416
|
parts.push(`removed the harness part: ${f.removedOut.join(", ")} (you asked: ${asked})`);
|
|
10609
10417
|
}
|
|
10610
10418
|
if (f.removedEdited.length > 0) {
|
|
10419
|
+
const where = f.backup === void 0 ? "went with it" : `are in the copy: ${f.backup}`;
|
|
10611
10420
|
parts.push(
|
|
10612
|
-
`your edits to ${f.removedEdited.join(", ")}
|
|
10421
|
+
`your edits to ${f.removedEdited.join(", ")} ${where} (a hook entry calls a harness script \u2014 left alone it would call a script that is no longer wired)`
|
|
10613
10422
|
);
|
|
10614
10423
|
}
|
|
10615
10424
|
if (f.keptOut.length > 0) {
|
|
@@ -10729,7 +10538,7 @@ function renderInterruptedInstall(err, e2, cwd = process.cwd()) {
|
|
|
10729
10538
|
}
|
|
10730
10539
|
const { path, error } = e2.record;
|
|
10731
10540
|
err(
|
|
10732
|
-
path !== null ? ` Already in place before it stopped \u2014 recorded in ${
|
|
10541
|
+
path !== null ? ` Already in place before it stopped \u2014 recorded in ${relative6(cwd, path) || path}:` : " Already in place before it stopped:"
|
|
10733
10542
|
);
|
|
10734
10543
|
for (const [key, n] of groups) {
|
|
10735
10544
|
err(` ${key.endsWith("/") ? `${key} (${n} file${n === 1 ? "" : "s"})` : key}`);
|
|
@@ -11052,7 +10861,7 @@ function registerInstallCommand(cli) {
|
|
|
11052
10861
|
|
|
11053
10862
|
// src/commands/list.ts
|
|
11054
10863
|
init_esm_shims();
|
|
11055
|
-
import { existsSync as existsSync25, readFileSync as
|
|
10864
|
+
import { existsSync as existsSync25, readFileSync as readFileSync26 } from "fs";
|
|
11056
10865
|
import { join as join34, resolve as resolve3 } from "path";
|
|
11057
10866
|
function listAction(options = {}, deps = {}) {
|
|
11058
10867
|
const log = deps.log ?? console.log;
|
|
@@ -11157,7 +10966,7 @@ function formatHarnessFileRows(log, projectDir) {
|
|
|
11157
10966
|
const rootMd = log.templates.rootClaudeMd;
|
|
11158
10967
|
if (rootMd) {
|
|
11159
10968
|
const path = join34(projectDir, rootMd.path);
|
|
11160
|
-
const modified = existsSync25(path) && hashContent(
|
|
10969
|
+
const modified = existsSync25(path) && hashContent(readFileSync26(path, "utf8")) !== rootMd.sha256;
|
|
11161
10970
|
rows2.push(
|
|
11162
10971
|
modified ? ` ${c.dim(rootMd.path)} ${c.yellow("(\uC218\uC815\uB428 \u2014 uninstall \uC2DC \uBCF4\uC874)")}` : ` ${c.dim(rootMd.path)}`
|
|
11163
10972
|
);
|
|
@@ -11192,9 +11001,9 @@ import {
|
|
|
11192
11001
|
accessSync as accessSync2,
|
|
11193
11002
|
existsSync as existsSync26,
|
|
11194
11003
|
constants as fsConstants2,
|
|
11195
|
-
lstatSync as
|
|
11004
|
+
lstatSync as lstatSync5,
|
|
11196
11005
|
readdirSync as readdirSync7,
|
|
11197
|
-
readFileSync as
|
|
11006
|
+
readFileSync as readFileSync27,
|
|
11198
11007
|
readlinkSync,
|
|
11199
11008
|
realpathSync as realpathSync3,
|
|
11200
11009
|
rmdirSync as rmdirSync3,
|
|
@@ -11202,7 +11011,7 @@ import {
|
|
|
11202
11011
|
statSync as statSync5,
|
|
11203
11012
|
writeFileSync as writeFileSync9
|
|
11204
11013
|
} from "fs";
|
|
11205
|
-
import { dirname as dirname8, join as join35, relative as
|
|
11014
|
+
import { dirname as dirname8, join as join35, relative as relative7, resolve as resolve4, sep as sep7 } from "path";
|
|
11206
11015
|
|
|
11207
11016
|
// src/uninstall-interactive.ts
|
|
11208
11017
|
init_esm_shims();
|
|
@@ -12291,7 +12100,8 @@ var TRACK_LABELS = {
|
|
|
12291
12100
|
"csr-supabase": "csr-supabase \u2014 Vite + React + Supabase",
|
|
12292
12101
|
"csr-fastify": "csr-fastify \u2014 Vite + React + Fastify",
|
|
12293
12102
|
"csr-fastapi": "csr-fastapi \u2014 Vite + React + FastAPI",
|
|
12294
|
-
"ssr-htmx": "ssr-htmx \u2014 htmx + FastAPI",
|
|
12103
|
+
"ssr-htmx": "ssr-htmx \u2014 htmx + FastAPI (Python) + daisyUI",
|
|
12104
|
+
"ssr-htmx-fastify": "ssr-htmx-fastify \u2014 htmx + Fastify (Node) + daisyUI",
|
|
12295
12105
|
"ssr-nextjs": "ssr-nextjs \u2014 Next.js (App Router)",
|
|
12296
12106
|
data: "data \u2014 Python data / DuckDB / PySide6",
|
|
12297
12107
|
executive: "executive \u2014 proposals / DD / pitch (no agent-skills)",
|
|
@@ -13051,7 +12861,7 @@ function unwritablePaths(ctx) {
|
|
|
13051
12861
|
}
|
|
13052
12862
|
function safeExists(abs) {
|
|
13053
12863
|
try {
|
|
13054
|
-
|
|
12864
|
+
lstatSync5(abs);
|
|
13055
12865
|
return true;
|
|
13056
12866
|
} catch {
|
|
13057
12867
|
return false;
|
|
@@ -13127,7 +12937,17 @@ function settleLog(ctx, io) {
|
|
|
13127
12937
|
return true;
|
|
13128
12938
|
}
|
|
13129
12939
|
} else {
|
|
13130
|
-
|
|
12940
|
+
const recordDir = join35(projectDir, INSTALL_LOG_DIR);
|
|
12941
|
+
if (outsideLinkAt(projectDir, INSTALL_LOG_DIR) !== null) io.rm(recordDir);
|
|
12942
|
+
else {
|
|
12943
|
+
for (const rel of [
|
|
12944
|
+
`${INSTALL_LOG_DIR}/${INSTALL_LOG_FILENAME}`,
|
|
12945
|
+
...HARNESS_RUNTIME_IGNORES
|
|
12946
|
+
]) {
|
|
12947
|
+
io.rm(join35(projectDir, rel));
|
|
12948
|
+
}
|
|
12949
|
+
pruneEmptyDirsUpward(projectDir, recordDir);
|
|
12950
|
+
}
|
|
13131
12951
|
if (outsideLinkAt(projectDir, LEGACY_INSTALL_LOG_DIR) === null) {
|
|
13132
12952
|
io.rm(legacyInstallLogPath(projectDir));
|
|
13133
12953
|
pruneEmptyDirsUpward(projectDir, join35(projectDir, LEGACY_INSTALL_LOG_DIR));
|
|
@@ -13142,7 +12962,9 @@ function dryRunLines(plan, installLog, projectDir, assetsOnly, rootFiles, harnes
|
|
|
13142
12962
|
lines2.push(c.dim(" (no project-scope assets to reverse)"));
|
|
13143
12963
|
}
|
|
13144
12964
|
lines2.push(...plan.reverseSteps.map((s) => ` \u25CB ${s.label}`));
|
|
13145
|
-
lines2.push(
|
|
12965
|
+
lines2.push(
|
|
12966
|
+
" \u25CB remove install record (.uzys-agent-harness/ \u2014 \uC124\uCE58 \uAE30\uB85D\xB7\uCC28\uB2E8 \uB85C\uADF8\xB7update \uBC31\uC5C5 \uBAA9\uB85D)"
|
|
12967
|
+
);
|
|
13146
12968
|
lines2.push(
|
|
13147
12969
|
...plan.noReversePath.flatMap((a) => [
|
|
13148
12970
|
c.dim(` \u2298 ${a.id} (${a.method}) \u2014 \uC790\uB3D9 \uB418\uB3CC\uB9AC\uAE30 \uACBD\uB85C \uC5C6\uC74C, \uAE30\uB85D \uC720\uC9C0`),
|
|
@@ -13204,7 +13026,6 @@ function backupArtifactAdvisoryLines(projectDir) {
|
|
|
13204
13026
|
}
|
|
13205
13027
|
}
|
|
13206
13028
|
for (const dir of BACKUP_SCAN_DIRS) {
|
|
13207
|
-
if (dir === INSTALL_LOG_DIR) continue;
|
|
13208
13029
|
const abs = join35(projectDir, dir);
|
|
13209
13030
|
if (!existsSync26(abs) || outsideLinkAt(projectDir, dir) !== null) continue;
|
|
13210
13031
|
for (const rel of listFilesRecursive(abs)) {
|
|
@@ -13375,7 +13196,7 @@ function agentsMdRerenderSet(target, remaining, log, projectDir) {
|
|
|
13375
13196
|
}
|
|
13376
13197
|
function readTextOrEmpty(abs) {
|
|
13377
13198
|
try {
|
|
13378
|
-
return
|
|
13199
|
+
return readFileSync27(abs, "utf8");
|
|
13379
13200
|
} catch {
|
|
13380
13201
|
return "";
|
|
13381
13202
|
}
|
|
@@ -13516,7 +13337,7 @@ function remnantFor(path, log, harnessRoot, projectDir, wiringGone = []) {
|
|
|
13516
13337
|
const gone = (k2) => wiringGone.includes(`hooks/${k2.split("#")[1] ?? ""}`);
|
|
13517
13338
|
return (disk) => {
|
|
13518
13339
|
try {
|
|
13519
|
-
const template =
|
|
13340
|
+
const template = readFileSync27(join35(harnessRoot, "templates", "settings.json"), "utf8");
|
|
13520
13341
|
const render = renderSettingsPortion(template, () => true);
|
|
13521
13342
|
return [...legacySettingsSeed(disk, render, projectDir, { edited: true }).keys()].filter(
|
|
13522
13343
|
(k2) => !k2.endsWith("{}") && !gone(k2)
|
|
@@ -13601,15 +13422,24 @@ function sharedStripLines(results, preview) {
|
|
|
13601
13422
|
return id === null ? [] : [id.slice(id.indexOf(":") + 1)];
|
|
13602
13423
|
};
|
|
13603
13424
|
const names = (keys) => keys.flatMap(display).join(" \xB7 ");
|
|
13425
|
+
const stays = r.edited === void 0 ? " (yours stays)" : "";
|
|
13604
13426
|
if (r.fileRemoved) {
|
|
13427
|
+
const only = (verb) => r.edited === void 0 ? ` and only its part ${verb} in it` : "";
|
|
13605
13428
|
lines2.push(
|
|
13606
|
-
preview ? ` \u25CB remove ${r.path} (the harness created it
|
|
13429
|
+
preview ? ` \u25CB remove ${r.path} (the harness created it${only("is")})` : ` ${status.success(`${r.path} \u2014 removed (the harness created it${only("was")})`)}`
|
|
13607
13430
|
);
|
|
13608
13431
|
} else if (r.removed.length > 0) {
|
|
13609
13432
|
const which = r.path === AGENTS_MD2 ? "" : `: ${names(r.removed)}`;
|
|
13610
13433
|
const what = r.path === AGENTS_MD2 ? "the harness block" : "the harness part";
|
|
13611
13434
|
lines2.push(
|
|
13612
|
-
preview ? ` \u25CB remove ${what} from ${r.path}${which}
|
|
13435
|
+
preview ? ` \u25CB remove ${what} from ${r.path}${which}${stays}` : ` ${status.success(`${r.path} \u2014 removed ${what}${which}${stays}`)}`
|
|
13436
|
+
);
|
|
13437
|
+
}
|
|
13438
|
+
if (r.edited !== void 0) {
|
|
13439
|
+
const n = r.edited.keys.length;
|
|
13440
|
+
const edited = `you had edited ${n} harness hook line${n === 1 ? "" : "s"} (${names(r.edited.keys)})`;
|
|
13441
|
+
lines2.push(
|
|
13442
|
+
preview ? ` \u25CB ${r.path} \u2014 ${edited} \u2014 a copy is saved first as ${r.edited.backup}` : ` ${c.yellow("\u2298")} ${r.path} \u2014 ${edited} \u2014 your edit is in the copy: ${r.edited.backup}`
|
|
13613
13443
|
);
|
|
13614
13444
|
}
|
|
13615
13445
|
if (r.kept.length > 0) {
|
|
@@ -13802,7 +13632,10 @@ function harnessCandidates(log) {
|
|
|
13802
13632
|
const paths = [
|
|
13803
13633
|
...(log.policyFiles ?? []).map((f) => `${CLAUDE_DIR3}${f.path}`),
|
|
13804
13634
|
...(log.skillFiles ?? []).map((f) => `${SKILLS_DIR3}${f.path}`),
|
|
13805
|
-
...(log.externalFiles ?? []).map((f) => f.path)
|
|
13635
|
+
...(log.externalFiles ?? []).map((f) => f.path),
|
|
13636
|
+
// 헬퍼 — writer 기록은 `externalFiles` 에 sha 로 적지만 옛 기록(≤v26.161.0)엔 항목이 없다. `recorded()` 가 가른다
|
|
13637
|
+
// (writer 기록에 없으면 `none` = 설치자 파일 · 옛 기록이면 `no-sha` = 번들 판과 같을 때만 백업 없이)
|
|
13638
|
+
...helperSources().keys(),
|
|
13806
13639
|
// 어느 필드에도 적힌 적 없는 옛 판의 하네스 파일 — `isTarget` 이 알려 준다(A8 · install 과 같은 방식)
|
|
13807
13640
|
INSTALLED_TRACKS,
|
|
13808
13641
|
// 스캔 필드가 아예 없는 옛 로그(v26.125.0 이하)는 필드가 하네스 파일을 열거하지 못한다 — manifest 의 하네스 자리 ·
|
|
@@ -13816,11 +13649,18 @@ function harnessTargets(log) {
|
|
|
13816
13649
|
const tracked = recordedTrackTargets(log);
|
|
13817
13650
|
return (p2) => p2 === INSTALLED_TRACKS || tracked(p2);
|
|
13818
13651
|
}
|
|
13652
|
+
function helperSources() {
|
|
13653
|
+
return new Map(
|
|
13654
|
+
buildManifest({ tracks: [] }).filter((e2) => isCliNeutralTarget(e2.target)).map((e2) => [e2.target, e2.source])
|
|
13655
|
+
);
|
|
13656
|
+
}
|
|
13819
13657
|
function templateNext(harnessRoot, log, path) {
|
|
13820
13658
|
if (path === INSTALLED_TRACKS) return installedTracksText(log.spec.tracks.filter(isTrack));
|
|
13821
|
-
|
|
13659
|
+
const helper = helperSources().get(path);
|
|
13660
|
+
if (helper === void 0 && !path.startsWith(CLAUDE_DIR3)) return null;
|
|
13822
13661
|
try {
|
|
13823
|
-
|
|
13662
|
+
const source = helper ?? path.slice(CLAUDE_DIR3.length);
|
|
13663
|
+
return readFileSync27(join35(harnessRoot, "templates", source), "utf8");
|
|
13824
13664
|
} catch {
|
|
13825
13665
|
return null;
|
|
13826
13666
|
}
|
|
@@ -13846,7 +13686,7 @@ function removeRecordedFile(path, ctx) {
|
|
|
13846
13686
|
}
|
|
13847
13687
|
let st2;
|
|
13848
13688
|
try {
|
|
13849
|
-
st2 =
|
|
13689
|
+
st2 = lstatSync5(abs, { throwIfNoEntry: false });
|
|
13850
13690
|
} catch {
|
|
13851
13691
|
out.unjudged.push(path);
|
|
13852
13692
|
return;
|
|
@@ -13864,13 +13704,13 @@ function removeRecordedFile(path, ctx) {
|
|
|
13864
13704
|
return;
|
|
13865
13705
|
}
|
|
13866
13706
|
if (st2.isSymbolicLink()) effective = real;
|
|
13867
|
-
disk =
|
|
13707
|
+
disk = readFileSync27(effective, "utf8");
|
|
13868
13708
|
} catch {
|
|
13869
13709
|
out.unjudged.push(path);
|
|
13870
13710
|
return;
|
|
13871
13711
|
}
|
|
13872
13712
|
const rec = recorded(log, path, { isTarget: ctx.isTarget });
|
|
13873
|
-
const legacy = rec.state !== "sha" && isLegacyHarnessFile(path, hashContent(disk)) && (rec.state === "no-sha" || mayReclaimLegacy(log, path));
|
|
13713
|
+
const legacy = rec.state !== "sha" && isLegacyHarnessFile(path, hashContent(disk)) && (rec.state === "no-sha" || mayReclaimLegacy(log, projectDir, path));
|
|
13874
13714
|
const inBundle = legacy && ctx.isTarget(bundlePath(path));
|
|
13875
13715
|
const verdict = judge({
|
|
13876
13716
|
op: "remove",
|
|
@@ -13882,7 +13722,7 @@ function removeRecordedFile(path, ctx) {
|
|
|
13882
13722
|
if (verdict.verdict !== "remove" && verdict.verdict !== "backup+remove") return;
|
|
13883
13723
|
let backup = null;
|
|
13884
13724
|
if (verdict.verdict === "backup+remove") {
|
|
13885
|
-
backup = io.write ?
|
|
13725
|
+
backup = io.write ? relative7(projectDir, backupFile(effective)).split(sep7).join("/") : `${relative7(projectDir, effective).split(sep7).join("/")}.backup-<time>`;
|
|
13886
13726
|
}
|
|
13887
13727
|
if (io.write) {
|
|
13888
13728
|
if (effective !== abs) io.rm(abs);
|
|
@@ -13900,14 +13740,13 @@ function legacyFileCandidates() {
|
|
|
13900
13740
|
(p2) => p2.startsWith(SKILLS_DIR3) ? [p2, `.agents/skills/${p2.slice(SKILLS_DIR3.length)}`] : [p2]
|
|
13901
13741
|
);
|
|
13902
13742
|
}
|
|
13903
|
-
function
|
|
13904
|
-
return path.startsWith(".agents/skills/") ? `${SKILLS_DIR3}${path.slice(".agents/skills/".length)}` : path;
|
|
13905
|
-
}
|
|
13906
|
-
function mayReclaimLegacy(log, path) {
|
|
13743
|
+
function mayReclaimLegacy(log, projectDir, path) {
|
|
13907
13744
|
const clis = installedClis(log);
|
|
13908
13745
|
if (!ownersOf(path).some((c3) => clis.includes(c3))) return false;
|
|
13909
13746
|
if (isExcludedPath(bundlePath(path), excludedIds(log))) return false;
|
|
13910
|
-
return !(log.rootFiles ?? []).some(
|
|
13747
|
+
return !(log.rootFiles ?? []).some(
|
|
13748
|
+
(f) => f.path === path && f.change === "displaced" && !isLegacyDisplaced(log, projectDir, f)
|
|
13749
|
+
);
|
|
13911
13750
|
}
|
|
13912
13751
|
function removeLegacyHookWiring(log, projectDir, scope, io, removed) {
|
|
13913
13752
|
if (!scope.has(SETTINGS_TARGET) || !installedClis(log).includes("claude")) return [];
|
|
@@ -13989,8 +13828,8 @@ function removeHarnessMadeShared(path, sha, projectDir, io, out, pruneFrom) {
|
|
|
13989
13828
|
out.unjudged.push(path);
|
|
13990
13829
|
return true;
|
|
13991
13830
|
}
|
|
13992
|
-
if (
|
|
13993
|
-
current =
|
|
13831
|
+
if (lstatSync5(abs).isSymbolicLink()) effective = real;
|
|
13832
|
+
current = readFileSync27(effective, "utf8");
|
|
13994
13833
|
} catch {
|
|
13995
13834
|
out.unjudged.push(path);
|
|
13996
13835
|
return true;
|
|
@@ -14253,7 +14092,7 @@ function removeAnchor(log, projectDir, io) {
|
|
|
14253
14092
|
if (outside !== null) return { removed: false, lines: outsideLines([outside], log) };
|
|
14254
14093
|
let disk;
|
|
14255
14094
|
try {
|
|
14256
|
-
disk =
|
|
14095
|
+
disk = readFileSync27(abs, "utf8");
|
|
14257
14096
|
} catch {
|
|
14258
14097
|
return {
|
|
14259
14098
|
removed: false,
|
|
@@ -14286,7 +14125,7 @@ function removeAnchor(log, projectDir, io) {
|
|
|
14286
14125
|
]
|
|
14287
14126
|
};
|
|
14288
14127
|
}
|
|
14289
|
-
const backup = edited ?
|
|
14128
|
+
const backup = edited ? relative7(projectDir, backupFile(abs)).split(sep7).join("/") : null;
|
|
14290
14129
|
io.rm(abs);
|
|
14291
14130
|
return {
|
|
14292
14131
|
removed: true,
|
|
@@ -14298,7 +14137,7 @@ function removeAnchor(log, projectDir, io) {
|
|
|
14298
14137
|
var AGENTS_MD2 = "AGENTS.md";
|
|
14299
14138
|
function readAgentsMdTemplate(harnessRoot) {
|
|
14300
14139
|
try {
|
|
14301
|
-
return
|
|
14140
|
+
return readFileSync27(join35(harnessRoot, AGENTS_MD_TEMPLATE), "utf8");
|
|
14302
14141
|
} catch {
|
|
14303
14142
|
return null;
|
|
14304
14143
|
}
|
|
@@ -14330,8 +14169,8 @@ function removeUnrecordedLegacyAgentsMd(path, projectDir, io, out, pruneFrom) {
|
|
|
14330
14169
|
try {
|
|
14331
14170
|
const real = realpathSync3(abs);
|
|
14332
14171
|
if (!statSync5(real).isFile()) return false;
|
|
14333
|
-
if (
|
|
14334
|
-
current =
|
|
14172
|
+
if (lstatSync5(abs).isSymbolicLink()) effective = real;
|
|
14173
|
+
current = readFileSync27(effective, "utf8");
|
|
14335
14174
|
} catch {
|
|
14336
14175
|
return false;
|
|
14337
14176
|
}
|
|
@@ -14349,7 +14188,7 @@ function removeLegacyAgentsMd(path, abs, effective, strip, ctx) {
|
|
|
14349
14188
|
let backup = `${path}.backup-<time>`;
|
|
14350
14189
|
try {
|
|
14351
14190
|
if (io.write) {
|
|
14352
|
-
backup =
|
|
14191
|
+
backup = relative7(projectDir, backupFile(effective)).split(sep7).join("/");
|
|
14353
14192
|
if (strip.remainder !== null) {
|
|
14354
14193
|
writeFileSync9(effective, neutralizeScaffoldBanner(strip.remainder), "utf8");
|
|
14355
14194
|
} else {
|
|
@@ -14379,7 +14218,7 @@ function removeToolFiles(files, projectDir, rm) {
|
|
|
14379
14218
|
const abs = join35(projectDir, path);
|
|
14380
14219
|
let isLink;
|
|
14381
14220
|
try {
|
|
14382
|
-
isLink =
|
|
14221
|
+
isLink = lstatSync5(abs).isSymbolicLink();
|
|
14383
14222
|
} catch {
|
|
14384
14223
|
continue;
|
|
14385
14224
|
}
|
|
@@ -14393,9 +14232,9 @@ function removeToolFiles(files, projectDir, rm) {
|
|
|
14393
14232
|
continue;
|
|
14394
14233
|
}
|
|
14395
14234
|
try {
|
|
14396
|
-
if (hashContent(
|
|
14235
|
+
if (hashContent(readFileSync27(abs, "utf8")) !== sha256) {
|
|
14397
14236
|
const backup = backupFile(abs);
|
|
14398
|
-
notes.push(`backed up ${path} \u2192 ${
|
|
14237
|
+
notes.push(`backed up ${path} \u2192 ${relative7(projectDir, backup)} (changed since install)`);
|
|
14399
14238
|
}
|
|
14400
14239
|
rm(abs);
|
|
14401
14240
|
removed.push(path);
|
|
@@ -14410,8 +14249,8 @@ function removeToolFiles(files, projectDir, rm) {
|
|
|
14410
14249
|
function pruneEmptyDirsUpward(projectDir, startDir) {
|
|
14411
14250
|
let cursor = startDir;
|
|
14412
14251
|
while (true) {
|
|
14413
|
-
const rel =
|
|
14414
|
-
if (rel === "" || rel.startsWith(`..${
|
|
14252
|
+
const rel = relative7(projectDir, cursor);
|
|
14253
|
+
if (rel === "" || rel.startsWith(`..${sep7}`) || rel === "..") return;
|
|
14415
14254
|
try {
|
|
14416
14255
|
if (readdirSync7(cursor).length > 0) return;
|
|
14417
14256
|
rmdirSync3(cursor);
|
|
@@ -14441,7 +14280,7 @@ function readRootClaudeMd(projectDir) {
|
|
|
14441
14280
|
const path = join35(projectDir, "CLAUDE.md");
|
|
14442
14281
|
if (!existsSync26(path)) return null;
|
|
14443
14282
|
try {
|
|
14444
|
-
return
|
|
14283
|
+
return readFileSync27(path, "utf8");
|
|
14445
14284
|
} catch {
|
|
14446
14285
|
return null;
|
|
14447
14286
|
}
|