@uzysjung/agent-harness 26.172.0 → 26.173.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js
CHANGED
|
@@ -101,7 +101,7 @@ import {
|
|
|
101
101
|
targetsInclude,
|
|
102
102
|
upsertHarnessImport,
|
|
103
103
|
writeInstallLog
|
|
104
|
-
} from "./chunk-
|
|
104
|
+
} from "./chunk-JABFWW6X.js";
|
|
105
105
|
|
|
106
106
|
// node_modules/sisteransi/src/index.js
|
|
107
107
|
var require_src = __commonJS({
|
|
@@ -3713,8 +3713,8 @@ function padDisplay(s, width) {
|
|
|
3713
3713
|
|
|
3714
3714
|
// src/install-writes.ts
|
|
3715
3715
|
init_esm_shims();
|
|
3716
|
-
import { existsSync as existsSync6, mkdirSync, readFileSync as readFileSync4, statSync, writeFileSync as writeFileSync3 } from "fs";
|
|
3717
|
-
import { dirname, join as
|
|
3716
|
+
import { existsSync as existsSync6, mkdirSync, readFileSync as readFileSync4, statSync as statSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
3717
|
+
import { dirname, join as join8, relative, sep } from "path";
|
|
3718
3718
|
|
|
3719
3719
|
// src/env-files.ts
|
|
3720
3720
|
init_esm_shims();
|
|
@@ -3804,7 +3804,8 @@ var LINES = {
|
|
|
3804
3804
|
newInRelease: "wrote (new in this release)",
|
|
3805
3805
|
refreshed: "refreshed",
|
|
3806
3806
|
sameAsHarness: "kept \u2014 already the same as the harness version (left yours in place)",
|
|
3807
|
-
|
|
3807
|
+
// 비켜 둔 설치자 파일은 하네스가 그 자리를 떠날 때(uninstall · 회수) 되돌린다(`displaced` · #782) — 그 약속을 같은 줄에
|
|
3808
|
+
hadFile: "had a file with this name \u2014 saved as <file>.backup-<time> \u2014 yours, put back on uninstall",
|
|
3808
3809
|
noChecksum: "no checksum on record \u2014 saved a copy once",
|
|
3809
3810
|
edited: "you edited it \u2014 saved as <file>.backup-<time>",
|
|
3810
3811
|
removed: "removed",
|
|
@@ -3817,6 +3818,9 @@ var LINES = {
|
|
|
3817
3818
|
scaffoldKept: "kept \u2014 already there (yours)",
|
|
3818
3819
|
leftForYou: "left for you (yours now)"
|
|
3819
3820
|
};
|
|
3821
|
+
function withBackup(line, backup) {
|
|
3822
|
+
return line.replace("<file>.backup-<time>", backup);
|
|
3823
|
+
}
|
|
3820
3824
|
function unreadable(adapter, op) {
|
|
3821
3825
|
const what = op === "write" ? "harness part not added" : "harness part not removed";
|
|
3822
3826
|
return `could not read it (${ADAPTERS[adapter].unreadable}) \u2014 ${what}`;
|
|
@@ -3935,7 +3939,8 @@ var PRIOR_MCP_SERVERS = [
|
|
|
3935
3939
|
// #766 — `@supabase/mcp-server` 는 npm 에 없는 이름이라(404) 서버가 뜬 적이 없다
|
|
3936
3940
|
name: "supabase",
|
|
3937
3941
|
value: { type: "stdio", command: "npx", args: ["-y", "@supabase/mcp-server"] },
|
|
3938
|
-
updated: "now Supabase's remote server \u2014 sign in on first use, see NEXT"
|
|
3942
|
+
updated: "now Supabase's remote server \u2014 sign in on first use, see NEXT",
|
|
3943
|
+
dead: `runs @supabase/mcp-server, which is not on npm \u2014 this server never started. To use Supabase's official remote server, set it to { "type": "http", "url": "https://mcp.supabase.com/mcp" } (signs in on first use)`
|
|
3939
3944
|
},
|
|
3940
3945
|
{
|
|
3941
3946
|
name: "chrome-devtools",
|
|
@@ -4116,7 +4121,7 @@ function recordedMcpAssetIds(log) {
|
|
|
4116
4121
|
// src/recorded.ts
|
|
4117
4122
|
init_esm_shims();
|
|
4118
4123
|
import { existsSync as existsSync5 } from "fs";
|
|
4119
|
-
import { join as
|
|
4124
|
+
import { join as join7 } from "path";
|
|
4120
4125
|
|
|
4121
4126
|
// src/cli-ownership.ts
|
|
4122
4127
|
init_esm_shims();
|
|
@@ -4171,6 +4176,42 @@ function removableFor(cli, remaining) {
|
|
|
4171
4176
|
return { exclusive, sharedNowUnowned };
|
|
4172
4177
|
}
|
|
4173
4178
|
|
|
4179
|
+
// src/foreign-slot.ts
|
|
4180
|
+
init_esm_shims();
|
|
4181
|
+
import { lstatSync, realpathSync, statSync } from "fs";
|
|
4182
|
+
import { join as join6 } from "path";
|
|
4183
|
+
var SKILL_SLOT = /^((?:\.claude|\.agents)\/skills\/[^/]+)(?:\/|$)/;
|
|
4184
|
+
function occupiedByNonDirectory(path) {
|
|
4185
|
+
const stat = lstatSync(path, { throwIfNoEntry: false });
|
|
4186
|
+
return stat !== void 0 && !stat.isDirectory();
|
|
4187
|
+
}
|
|
4188
|
+
function linksToProjectSharedSkill(projectDir, id) {
|
|
4189
|
+
const slot = join6(projectDir, ".claude/skills", id);
|
|
4190
|
+
if (lstatSync(slot, { throwIfNoEntry: false })?.isSymbolicLink() !== true) return false;
|
|
4191
|
+
try {
|
|
4192
|
+
const real = realpathSync(slot);
|
|
4193
|
+
return real === join6(realpathSync(projectDir), ".agents/skills", id) && statSync(real).isDirectory();
|
|
4194
|
+
} catch {
|
|
4195
|
+
return false;
|
|
4196
|
+
}
|
|
4197
|
+
}
|
|
4198
|
+
function foreignOwnedTarget(projectDir, rel) {
|
|
4199
|
+
const slot = SKILL_SLOT.exec(rel)?.[1];
|
|
4200
|
+
if (slot === void 0) return null;
|
|
4201
|
+
if (occupiedByNonDirectory(join6(projectDir, slot))) return slot;
|
|
4202
|
+
if (rel !== slot) {
|
|
4203
|
+
const parts = rel.slice(slot.length + 1).split("/");
|
|
4204
|
+
let cursor = slot;
|
|
4205
|
+
for (const part of parts.slice(0, -1)) {
|
|
4206
|
+
cursor = `${cursor}/${part}`;
|
|
4207
|
+
if (occupiedByNonDirectory(join6(projectDir, cursor))) return cursor;
|
|
4208
|
+
}
|
|
4209
|
+
const leaf = lstatSync(join6(projectDir, rel), { throwIfNoEntry: false });
|
|
4210
|
+
if (leaf !== void 0 && !leaf.isFile()) return rel;
|
|
4211
|
+
}
|
|
4212
|
+
return null;
|
|
4213
|
+
}
|
|
4214
|
+
|
|
4174
4215
|
// src/recorded.ts
|
|
4175
4216
|
var CLAUDE_DIR = ".claude/";
|
|
4176
4217
|
var ADVISORY_PATHS = [".env.example", ".github/workflows/", "skills-lock.json"];
|
|
@@ -4190,6 +4231,12 @@ function ownersOf(path) {
|
|
|
4190
4231
|
)
|
|
4191
4232
|
);
|
|
4192
4233
|
}
|
|
4234
|
+
function usersOf(path, projectDir) {
|
|
4235
|
+
const owners = ownersOf(path);
|
|
4236
|
+
const id = /^\.agents\/skills\/([^/]+)\//.exec(path)?.[1];
|
|
4237
|
+
const linked = id !== void 0 && linksToProjectSharedSkill(projectDir, id);
|
|
4238
|
+
return linked && !owners.includes("claude") ? [...owners, "claude"] : owners;
|
|
4239
|
+
}
|
|
4193
4240
|
function bundlePath(path) {
|
|
4194
4241
|
return path.startsWith(".agents/skills/") ? `${SKILLS_DIR}${path.slice(".agents/skills/".length)}` : path;
|
|
4195
4242
|
}
|
|
@@ -4202,9 +4249,9 @@ function recordedTrackTargets(log) {
|
|
|
4202
4249
|
selectedInternalSkills: INTERNAL_BUNDLED_SKILL_IDS
|
|
4203
4250
|
};
|
|
4204
4251
|
const entries = buildManifest(spec).filter((e2) => e2.applies(spec));
|
|
4205
|
-
const templates =
|
|
4252
|
+
const templates = join7(resolveBundleRoot(), "templates");
|
|
4206
4253
|
return (path) => entries.some(
|
|
4207
|
-
(e2) => e2.type === "dir" ? path.startsWith(`${e2.target}/`) && existsSync5(
|
|
4254
|
+
(e2) => e2.type === "dir" ? path.startsWith(`${e2.target}/`) && existsSync5(join7(templates, e2.source, path.slice(e2.target.length + 1))) : e2.target === path
|
|
4208
4255
|
);
|
|
4209
4256
|
}
|
|
4210
4257
|
function recordedTrackSpots(log) {
|
|
@@ -4212,13 +4259,13 @@ function recordedTrackSpots(log) {
|
|
|
4212
4259
|
tracks: log.spec.tracks.filter(isTrack),
|
|
4213
4260
|
selectedInternalSkills: INTERNAL_BUNDLED_SKILL_IDS
|
|
4214
4261
|
};
|
|
4215
|
-
const templates =
|
|
4262
|
+
const templates = join7(resolveBundleRoot(), "templates");
|
|
4216
4263
|
const out = [];
|
|
4217
4264
|
for (const e2 of buildManifest(spec)) {
|
|
4218
4265
|
if (!e2.applies(spec) || !e2.target.startsWith(CLAUDE_DIR)) continue;
|
|
4219
4266
|
if (e2.type === "file") out.push(e2.target);
|
|
4220
4267
|
else
|
|
4221
|
-
for (const rel of listFilesRecursive(
|
|
4268
|
+
for (const rel of listFilesRecursive(join7(templates, e2.source)))
|
|
4222
4269
|
out.push(`${e2.target}/${rel}`);
|
|
4223
4270
|
}
|
|
4224
4271
|
for (const p2 of RETIRED_PATHS) out.push(`${CLAUDE_DIR}${p2}`);
|
|
@@ -4297,7 +4344,6 @@ function isScaffoldPath(path) {
|
|
|
4297
4344
|
// src/install-writes.ts
|
|
4298
4345
|
var CLAUDE_DIR2 = ".claude/";
|
|
4299
4346
|
var SKILLS_DIR2 = ".claude/skills/";
|
|
4300
|
-
var BACKUP_PLACEHOLDER = "<file>.backup-<time>";
|
|
4301
4347
|
function createInstallWriter(args) {
|
|
4302
4348
|
const { projectDir, previousLog, excluded } = args;
|
|
4303
4349
|
const now = args.now ?? /* @__PURE__ */ new Date();
|
|
@@ -4322,16 +4368,17 @@ function createInstallWriter(args) {
|
|
|
4322
4368
|
else external.set(path, sha256);
|
|
4323
4369
|
}
|
|
4324
4370
|
function harness(path, from, opts = {}) {
|
|
4325
|
-
const abs =
|
|
4371
|
+
const abs = join8(projectDir, path);
|
|
4326
4372
|
if (outside.skip(abs)) return null;
|
|
4327
4373
|
const next = "source" in from ? readFileSync4(from.source, "utf8") : from.content;
|
|
4328
4374
|
const disk = existsSync6(abs) ? readFileSync4(abs, "utf8") : null;
|
|
4329
4375
|
const rec = recorded(previousLog, path, opts);
|
|
4330
4376
|
const diskSha = rec.state === "no-sha" && disk !== null ? hashContent(disk) : null;
|
|
4377
|
+
const judgedRec = diskSha !== null && isLegacyHarnessFile(path, diskSha) ? { state: "sha", sha256: diskSha } : rec;
|
|
4331
4378
|
const j4 = judge({
|
|
4332
4379
|
op: "write",
|
|
4333
4380
|
kind: "harness",
|
|
4334
|
-
rec:
|
|
4381
|
+
rec: judgedRec,
|
|
4335
4382
|
disk,
|
|
4336
4383
|
next,
|
|
4337
4384
|
...args.run === void 0 ? {} : { run: args.run }
|
|
@@ -4376,11 +4423,19 @@ function createInstallWriter(args) {
|
|
|
4376
4423
|
default:
|
|
4377
4424
|
throw new Error(`install: '${j4.record}' is not a write record (${path})`);
|
|
4378
4425
|
}
|
|
4379
|
-
const line = backup === void 0 ? j4.line : j4.line
|
|
4426
|
+
const line = backup === void 0 ? j4.line : withBackup(j4.line, backup);
|
|
4380
4427
|
const backupPair = backup === void 0 || backupAbs === void 0 ? {} : { backup, backupAbs };
|
|
4381
|
-
|
|
4428
|
+
const state = judgedRec.state;
|
|
4429
|
+
writes.push({
|
|
4430
|
+
path,
|
|
4431
|
+
verdict: j4.verdict,
|
|
4432
|
+
record: j4.record,
|
|
4433
|
+
recorded: state,
|
|
4434
|
+
line,
|
|
4435
|
+
...backupPair
|
|
4436
|
+
});
|
|
4382
4437
|
if (j4.line === "" || j4.verdict === "create" || j4.verdict === "overwrite") return null;
|
|
4383
|
-
const out = { path, verdict: j4.verdict, line, ...backupPair };
|
|
4438
|
+
const out = { path, verdict: j4.verdict, recorded: state, line, ...backupPair };
|
|
4384
4439
|
judged.push(out);
|
|
4385
4440
|
return out;
|
|
4386
4441
|
}
|
|
@@ -4388,7 +4443,7 @@ function createInstallWriter(args) {
|
|
|
4388
4443
|
const file = SHARED_FILES[path];
|
|
4389
4444
|
if (file === void 0) throw new Error(`install: ${path} is not a shared file`);
|
|
4390
4445
|
const adapter = ADAPTERS[file.adapter];
|
|
4391
|
-
const abs =
|
|
4446
|
+
const abs = join8(projectDir, path);
|
|
4392
4447
|
const disk = existsSync6(abs) ? readFileSync4(abs, "utf8") : null;
|
|
4393
4448
|
const own = new Map(
|
|
4394
4449
|
(previousLog?.portions ?? []).filter((p2) => p2.path === path).map((p2) => [p2.key, p2.sha256])
|
|
@@ -4570,9 +4625,9 @@ function retiredClaims(previous, projectDir, kept) {
|
|
|
4570
4625
|
const out = [];
|
|
4571
4626
|
for (const p2 of RETIRED_PATHS) {
|
|
4572
4627
|
if (have.has(p2) || recorded(previous, `${CLAUDE_DIR2}${p2}`).state === "none") continue;
|
|
4573
|
-
const abs =
|
|
4628
|
+
const abs = join8(projectDir, CLAUDE_DIR2, p2);
|
|
4574
4629
|
try {
|
|
4575
|
-
if (!
|
|
4630
|
+
if (!statSync2(abs).isFile()) continue;
|
|
4576
4631
|
out.push({ path: p2, sha256: hashContent(readFileSync4(abs, "utf8")) });
|
|
4577
4632
|
} catch {
|
|
4578
4633
|
}
|
|
@@ -4677,7 +4732,7 @@ function legacySettingsSeed(text, render, projectDir, opts = {}) {
|
|
|
4677
4732
|
} catch {
|
|
4678
4733
|
return seed;
|
|
4679
4734
|
}
|
|
4680
|
-
const claudeDir =
|
|
4735
|
+
const claudeDir = join8(projectDir, ".claude");
|
|
4681
4736
|
const candidates = new Set([...render.keys()].filter((k2) => k2.startsWith("hooks.")));
|
|
4682
4737
|
const legacyLines = /* @__PURE__ */ new Set();
|
|
4683
4738
|
const hooks = isObject2(root) && isObject2(root.hooks) ? root.hooks : {};
|
|
@@ -4794,12 +4849,12 @@ function legacyGitignoreSeed(text, render, previous) {
|
|
|
4794
4849
|
// src/installer.ts
|
|
4795
4850
|
init_esm_shims();
|
|
4796
4851
|
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
|
|
4852
|
+
import { basename as basename4, join as join31, relative as relative6, resolve, sep as sep7 } from "path";
|
|
4798
4853
|
|
|
4799
4854
|
// src/anchor-seed.ts
|
|
4800
4855
|
init_esm_shims();
|
|
4801
4856
|
import { existsSync as existsSync7, readFileSync as readFileSync5 } from "fs";
|
|
4802
|
-
import { join as
|
|
4857
|
+
import { join as join9 } from "path";
|
|
4803
4858
|
|
|
4804
4859
|
// src/agents-md-render.ts
|
|
4805
4860
|
init_esm_shims();
|
|
@@ -4906,7 +4961,7 @@ function compose(body, into) {
|
|
|
4906
4961
|
${trimmed.join("\n")}`;
|
|
4907
4962
|
}
|
|
4908
4963
|
function seedAgentsMdProjectContext(projectDir) {
|
|
4909
|
-
const root = readIfExists(
|
|
4964
|
+
const root = readIfExists(join9(projectDir, "CLAUDE.md"));
|
|
4910
4965
|
if (root === null) return null;
|
|
4911
4966
|
const withoutImport = stripHarnessImport(root) ?? root;
|
|
4912
4967
|
const body = dropHarnessQuoteBlocks(dropTitle(withoutImport.split("\n")));
|
|
@@ -4914,9 +4969,9 @@ function seedAgentsMdProjectContext(projectDir) {
|
|
|
4914
4969
|
return compose(body, "agents-md");
|
|
4915
4970
|
}
|
|
4916
4971
|
function seedRootClaudeProjectContext(projectDir, harnessRoot) {
|
|
4917
|
-
const agents = readIfExists(
|
|
4972
|
+
const agents = readIfExists(join9(projectDir, "AGENTS.md"));
|
|
4918
4973
|
if (agents === null) return null;
|
|
4919
|
-
const template = readIfExists(
|
|
4974
|
+
const template = readIfExists(join9(harnessRoot, AGENTS_MD_TEMPLATE));
|
|
4920
4975
|
if (template === null) return null;
|
|
4921
4976
|
const installer = projectContextInstallerLines({ existing: agents, template });
|
|
4922
4977
|
if (installer === null) return null;
|
|
@@ -4928,7 +4983,7 @@ function seedRootClaudeProjectContext(projectDir, harnessRoot) {
|
|
|
4928
4983
|
// src/ci-scaffold.ts
|
|
4929
4984
|
init_esm_shims();
|
|
4930
4985
|
import { existsSync as existsSync8 } from "fs";
|
|
4931
|
-
import { join as
|
|
4986
|
+
import { join as join10 } from "path";
|
|
4932
4987
|
var PYTHON_TRACKS = "data|csr-fastapi|full";
|
|
4933
4988
|
var VARIANTS = [
|
|
4934
4989
|
{
|
|
@@ -4952,12 +5007,12 @@ function installCiScaffold(ctx) {
|
|
|
4952
5007
|
for (const v2 of VARIANTS) {
|
|
4953
5008
|
if (!v2.applies(ctx.tracks)) continue;
|
|
4954
5009
|
const relTarget = `.github/workflows/${v2.target}`;
|
|
4955
|
-
const target =
|
|
5010
|
+
const target = join10(ctx.projectDir, relTarget);
|
|
4956
5011
|
if (existsSync8(target)) {
|
|
4957
5012
|
report.skippedExisting.push(relTarget);
|
|
4958
5013
|
continue;
|
|
4959
5014
|
}
|
|
4960
|
-
copyFile(
|
|
5015
|
+
copyFile(join10(ctx.harnessRoot, "templates/github-workflows", v2.source), target);
|
|
4961
5016
|
report.written.push(relTarget);
|
|
4962
5017
|
}
|
|
4963
5018
|
return report;
|
|
@@ -4975,7 +5030,7 @@ import { basename, join as join14 } from "path";
|
|
|
4975
5030
|
|
|
4976
5031
|
// src/agents-skill-targets.ts
|
|
4977
5032
|
init_esm_shims();
|
|
4978
|
-
import { join as
|
|
5033
|
+
import { join as join11, sep as sep2 } from "path";
|
|
4979
5034
|
function bundledSkillTargets(projectDir, log = readInstallLog(projectDir)) {
|
|
4980
5035
|
if (log === null) return [];
|
|
4981
5036
|
const tracks = log.spec.tracks.filter(
|
|
@@ -4989,7 +5044,7 @@ function bundledSkillTargets(projectDir, log = readInstallLog(projectDir)) {
|
|
|
4989
5044
|
}
|
|
4990
5045
|
function allowSkillCreation(writer, projectDir, ids) {
|
|
4991
5046
|
if (ids.length === 0) return writer;
|
|
4992
|
-
const roots = ids.map((id) => `${
|
|
5047
|
+
const roots = ids.map((id) => `${join11(projectDir, ".agents", "skills", id)}${sep2}`);
|
|
4993
5048
|
return {
|
|
4994
5049
|
write(absPath, content, opts) {
|
|
4995
5050
|
const inTarget = roots.some((root) => absPath.startsWith(root));
|
|
@@ -5012,48 +5067,11 @@ function agentsSkillSlot(params) {
|
|
|
5012
5067
|
init_esm_shims();
|
|
5013
5068
|
import { existsSync as existsSync9, mkdirSync as mkdirSync2, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "fs";
|
|
5014
5069
|
import { dirname as dirname2, relative as relative2, sep as sep3 } from "path";
|
|
5015
|
-
|
|
5016
|
-
// src/foreign-slot.ts
|
|
5017
|
-
init_esm_shims();
|
|
5018
|
-
import { lstatSync, realpathSync, statSync as statSync2 } from "fs";
|
|
5019
|
-
import { join as join11 } from "path";
|
|
5020
|
-
var SKILL_SLOT = /^((?:\.claude|\.agents)\/skills\/[^/]+)(?:\/|$)/;
|
|
5021
|
-
function occupiedByNonDirectory(path) {
|
|
5022
|
-
const stat = lstatSync(path, { throwIfNoEntry: false });
|
|
5023
|
-
return stat !== void 0 && !stat.isDirectory();
|
|
5024
|
-
}
|
|
5025
|
-
function linksToProjectSharedSkill(projectDir, id) {
|
|
5026
|
-
const slot = join11(projectDir, ".claude/skills", id);
|
|
5027
|
-
if (lstatSync(slot, { throwIfNoEntry: false })?.isSymbolicLink() !== true) return false;
|
|
5028
|
-
try {
|
|
5029
|
-
const real = realpathSync(slot);
|
|
5030
|
-
return real === join11(realpathSync(projectDir), ".agents/skills", id) && statSync2(real).isDirectory();
|
|
5031
|
-
} catch {
|
|
5032
|
-
return false;
|
|
5033
|
-
}
|
|
5034
|
-
}
|
|
5035
|
-
function foreignOwnedTarget(projectDir, rel) {
|
|
5036
|
-
const slot = SKILL_SLOT.exec(rel)?.[1];
|
|
5037
|
-
if (slot === void 0) return null;
|
|
5038
|
-
if (occupiedByNonDirectory(join11(projectDir, slot))) return slot;
|
|
5039
|
-
if (rel !== slot) {
|
|
5040
|
-
const parts = rel.slice(slot.length + 1).split("/");
|
|
5041
|
-
let cursor = slot;
|
|
5042
|
-
for (const part of parts.slice(0, -1)) {
|
|
5043
|
-
cursor = `${cursor}/${part}`;
|
|
5044
|
-
if (occupiedByNonDirectory(join11(projectDir, cursor))) return cursor;
|
|
5045
|
-
}
|
|
5046
|
-
const leaf = lstatSync(join11(projectDir, rel), { throwIfNoEntry: false });
|
|
5047
|
-
if (leaf !== void 0 && !leaf.isFile()) return rel;
|
|
5048
|
-
}
|
|
5049
|
-
return null;
|
|
5050
|
-
}
|
|
5051
|
-
|
|
5052
|
-
// src/owned-write.ts
|
|
5053
5070
|
function createOwnedWriter(projectDir, baseline, options = {}) {
|
|
5054
5071
|
const { now = /* @__PURE__ */ new Date(), refreshOnly = false, journal, owned = () => false } = options;
|
|
5055
5072
|
const written = /* @__PURE__ */ new Map();
|
|
5056
5073
|
const backedUp = [];
|
|
5074
|
+
const judged = [];
|
|
5057
5075
|
const backupPaths = [];
|
|
5058
5076
|
const foreignOwned = [];
|
|
5059
5077
|
const restored = [];
|
|
@@ -5078,14 +5096,30 @@ function createOwnedWriter(projectDir, baseline, options = {}) {
|
|
|
5078
5096
|
const disk = existsSync9(absPath) ? readFileSync6(absPath, "utf8") : null;
|
|
5079
5097
|
if (disk === null && refreshOnly && !opts?.createInRefresh && rec.state !== "sha")
|
|
5080
5098
|
return false;
|
|
5081
|
-
const { verdict, record } = judge({
|
|
5099
|
+
const { verdict, record, line } = judge({
|
|
5100
|
+
op: "write",
|
|
5101
|
+
kind: "harness",
|
|
5102
|
+
rec,
|
|
5103
|
+
disk,
|
|
5104
|
+
next: content
|
|
5105
|
+
});
|
|
5082
5106
|
let backupRel = null;
|
|
5083
5107
|
if (verdict === "backup+overwrite" || verdict === "overwrite" && opts?.backupFirst === true) {
|
|
5084
5108
|
const backup = backupFile(absPath, now);
|
|
5085
5109
|
backupPaths.push(backup);
|
|
5086
5110
|
journal?.backup(backup);
|
|
5087
5111
|
backupRel = relative2(projectDir, backup).split(sep3).join("/");
|
|
5088
|
-
if (verdict === "backup+overwrite"
|
|
5112
|
+
if (verdict === "backup+overwrite") {
|
|
5113
|
+
if (rec.state !== "none") backedUp.push(rel);
|
|
5114
|
+
judged.push({
|
|
5115
|
+
path: rel,
|
|
5116
|
+
verdict,
|
|
5117
|
+
recorded: rec.state,
|
|
5118
|
+
line: withBackup(line, backupRel),
|
|
5119
|
+
backup: backupRel,
|
|
5120
|
+
backupAbs: backup
|
|
5121
|
+
});
|
|
5122
|
+
}
|
|
5089
5123
|
}
|
|
5090
5124
|
if (record === "displaced" || record === "displaced+sha") {
|
|
5091
5125
|
displace({ path: rel, change: "displaced", notes: backupRel === null ? [] : [backupRel] });
|
|
@@ -5111,6 +5145,7 @@ function createOwnedWriter(projectDir, baseline, options = {}) {
|
|
|
5111
5145
|
outside: outside.list(),
|
|
5112
5146
|
files: [...written].map(([path, sha256]) => ({ path, sha256 })),
|
|
5113
5147
|
backedUp: [...backedUp],
|
|
5148
|
+
judged: [...judged],
|
|
5114
5149
|
backupPaths: [...backupPaths],
|
|
5115
5150
|
updated,
|
|
5116
5151
|
restored: [...restored],
|
|
@@ -6240,6 +6275,7 @@ function runCliTransforms(params) {
|
|
|
6240
6275
|
const externalFiles = [];
|
|
6241
6276
|
const externalBackups = [];
|
|
6242
6277
|
const externalBackedUp = [];
|
|
6278
|
+
const externalJudged = [];
|
|
6243
6279
|
const externalForeignOwned = [];
|
|
6244
6280
|
const displaced = /* @__PURE__ */ new Map();
|
|
6245
6281
|
let externalOutside = [];
|
|
@@ -6261,6 +6297,7 @@ function runCliTransforms(params) {
|
|
|
6261
6297
|
}
|
|
6262
6298
|
externalBackups.push(...report.ownership.backupPaths);
|
|
6263
6299
|
externalBackedUp.push(...report.ownership.backedUp);
|
|
6300
|
+
externalJudged.push(...report.ownership.judged ?? []);
|
|
6264
6301
|
for (const f of report.ownership.displaced ?? [])
|
|
6265
6302
|
if (!displaced.has(f.path)) displaced.set(f.path, f);
|
|
6266
6303
|
for (const f of report.ownership.foreignOwned) {
|
|
@@ -6368,6 +6405,7 @@ function runCliTransforms(params) {
|
|
|
6368
6405
|
externalBackups,
|
|
6369
6406
|
externalUpdated,
|
|
6370
6407
|
externalBackedUp,
|
|
6408
|
+
externalJudged,
|
|
6371
6409
|
displaced: [...displaced.values()],
|
|
6372
6410
|
externalForeignOwned,
|
|
6373
6411
|
externalOutside,
|
|
@@ -6690,14 +6728,15 @@ function refreshExternalSkills(projectDir, deps = {}) {
|
|
|
6690
6728
|
}
|
|
6691
6729
|
throw e2;
|
|
6692
6730
|
}
|
|
6693
|
-
const { report, displaced } = settle(ran);
|
|
6731
|
+
const { report, displaced, judged } = settle(ran);
|
|
6694
6732
|
recordRefreshedFiles(projectDir, log, report, displaced, deps.writeLog ?? writeInstallLog);
|
|
6695
6733
|
return {
|
|
6696
6734
|
attempted: report.attempted.length,
|
|
6697
6735
|
refreshed: report.succeeded,
|
|
6698
6736
|
failed: report.attempted.filter((r) => !r.ok).map((r) => ({ id: r.asset.id, message: r.message ?? "failed" })),
|
|
6699
6737
|
notInCatalog,
|
|
6700
|
-
unknown: false
|
|
6738
|
+
unknown: false,
|
|
6739
|
+
judged
|
|
6701
6740
|
};
|
|
6702
6741
|
}
|
|
6703
6742
|
function holdSkillSlots(projectDir, hold) {
|
|
@@ -6730,7 +6769,11 @@ function holdSkillSlots(projectDir, hold) {
|
|
|
6730
6769
|
hold.clis,
|
|
6731
6770
|
{ log: hold.log, scope: hold.scope }
|
|
6732
6771
|
);
|
|
6733
|
-
const out = {
|
|
6772
|
+
const out = {
|
|
6773
|
+
backups: [],
|
|
6774
|
+
displaced: [],
|
|
6775
|
+
judged: []
|
|
6776
|
+
};
|
|
6734
6777
|
const back = /* @__PURE__ */ new Set();
|
|
6735
6778
|
const failed = [];
|
|
6736
6779
|
for (const f of moved) {
|
|
@@ -6743,6 +6786,16 @@ function holdSkillSlots(projectDir, hold) {
|
|
|
6743
6786
|
}
|
|
6744
6787
|
out.backups.push(r.backup);
|
|
6745
6788
|
if (r.displaced !== null) out.displaced.push(r.displaced);
|
|
6789
|
+
const backup = toProjectRel(projectDir, r.backup);
|
|
6790
|
+
const line = !f.recorded ? LINES.hadFile : r.gone ? LINES.editedRemoved : LINES.edited;
|
|
6791
|
+
out.judged.push({
|
|
6792
|
+
path: f.rel,
|
|
6793
|
+
verdict: r.gone ? "backup+remove" : "backup+overwrite",
|
|
6794
|
+
recorded: f.recorded ? "sha" : "none",
|
|
6795
|
+
line: withBackup(line, backup),
|
|
6796
|
+
backup,
|
|
6797
|
+
backupAbs: r.backup
|
|
6798
|
+
});
|
|
6746
6799
|
} catch (e2) {
|
|
6747
6800
|
failed.push(`${f.rel} (${e2 instanceof Error ? e2.message : String(e2)})`);
|
|
6748
6801
|
}
|
|
@@ -6820,9 +6873,10 @@ function settleAside(projectDir, f, restore) {
|
|
|
6820
6873
|
}
|
|
6821
6874
|
const backup = backupFromCopy(f.abs, f.aside);
|
|
6822
6875
|
rmSync3(f.aside, { force: true });
|
|
6823
|
-
if (f.recorded || now === null) return { backup, displaced: null };
|
|
6876
|
+
if (f.recorded || now === null) return { backup, gone: now === null, displaced: null };
|
|
6824
6877
|
return {
|
|
6825
6878
|
backup,
|
|
6879
|
+
gone: false,
|
|
6826
6880
|
displaced: { path: f.rel, change: "displaced", notes: [toProjectRel(projectDir, backup)] }
|
|
6827
6881
|
};
|
|
6828
6882
|
}
|
|
@@ -7074,6 +7128,8 @@ function refreshLinkedSkillBodies(params) {
|
|
|
7074
7128
|
notOurs: ids.filter((id) => !baseline.has(bodyKey(id))),
|
|
7075
7129
|
files: result.files,
|
|
7076
7130
|
backupPaths: result.backupPaths,
|
|
7131
|
+
backedUp: result.backedUp,
|
|
7132
|
+
judged: result.judged ?? [],
|
|
7077
7133
|
displaced: result.displaced ?? [],
|
|
7078
7134
|
foreignOwned: result.foreignOwned,
|
|
7079
7135
|
outside: result.outside ?? []
|
|
@@ -7239,7 +7295,7 @@ import {
|
|
|
7239
7295
|
unlinkSync as unlinkSync2,
|
|
7240
7296
|
writeFileSync as writeFileSync7
|
|
7241
7297
|
} from "fs";
|
|
7242
|
-
import { basename as basename3, dirname as dirname7, join as join30 } from "path";
|
|
7298
|
+
import { basename as basename3, dirname as dirname7, join as join30, relative as relative5, sep as sep6 } from "path";
|
|
7243
7299
|
var LEGACY_ANCHOR_FILE2 = ".claude/CLAUDE.md";
|
|
7244
7300
|
function buildUpdateSpec(projectDir, tracks, only) {
|
|
7245
7301
|
const log = readInstallLog(projectDir);
|
|
@@ -7301,11 +7357,12 @@ function runUpdateMode(projectDir, templatesDir, harnessRoot, deps = {}, only) {
|
|
|
7301
7357
|
const logAtStart = readInstallLog(projectDir);
|
|
7302
7358
|
const claudeManaged = logAtStart === null || installedClis(logAtStart).includes("claude");
|
|
7303
7359
|
report.claudeUnrecorded = !claudeManaged && logAtStart !== null && existsSync21(claudeDir) ? recordClaudeCommand(logAtStart, installedTracks(projectDir)) : null;
|
|
7304
|
-
const harness = logAtStart === null ? { displaced: [], restored: [], foreignOwned: [], outside: [] } : updateHarnessFiles({ projectDir, templatesDir, log: logAtStart, wants, outside, report });
|
|
7360
|
+
const harness = logAtStart === null ? { displaced: [], restored: [], foreignOwned: [], outside: [], judged: [] } : updateHarnessFiles({ projectDir, templatesDir, log: logAtStart, wants, outside, report });
|
|
7305
7361
|
const claudeDisplaced = harness.displaced;
|
|
7362
|
+
const judged = [...harness.judged];
|
|
7306
7363
|
const prunedRestored = harness.restored;
|
|
7307
7364
|
if (existsSync21(claudeDir) && claudeManaged) {
|
|
7308
|
-
if (wants("anchor")) syncHarnessAnchor(projectDir, templatesDir, report, outside);
|
|
7365
|
+
if (wants("anchor")) syncHarnessAnchor(projectDir, templatesDir, report, outside, judged);
|
|
7309
7366
|
}
|
|
7310
7367
|
const harnessMcp = renderHarnessMcp(harnessRoot, {
|
|
7311
7368
|
spec: { tracks: installedTracks(projectDir), options: DEFAULT_OPTIONS },
|
|
@@ -7339,9 +7396,16 @@ function runUpdateMode(projectDir, templatesDir, harnessRoot, deps = {}, only) {
|
|
|
7339
7396
|
if (wants("hooks")) report.mcpAllowlistRetired = retireMcpAllowlist(projectDir);
|
|
7340
7397
|
report.retiredAgents = staleAgentFiles(claudeDir);
|
|
7341
7398
|
report.demotedAgents = demotedAgentFiles(claudeDir, installedTracks(projectDir));
|
|
7342
|
-
const external = wants("external") ? refreshExternalCli(
|
|
7399
|
+
const external = wants("external") ? refreshExternalCli(
|
|
7400
|
+
projectDir,
|
|
7401
|
+
harnessRoot,
|
|
7402
|
+
legacyDroppedKeys(logAtStart),
|
|
7403
|
+
harnessMcp,
|
|
7404
|
+
report.skillsSkippedLinks.filter((id) => linksToProjectSharedSkill(projectDir, id))
|
|
7405
|
+
) : {
|
|
7343
7406
|
externalUpdated: 0,
|
|
7344
7407
|
externalBackedUp: [],
|
|
7408
|
+
externalJudged: [],
|
|
7345
7409
|
displaced: [],
|
|
7346
7410
|
externalForeignOwned: [],
|
|
7347
7411
|
externalOutside: [],
|
|
@@ -7452,10 +7516,17 @@ function runUpdateMode(projectDir, templatesDir, harnessRoot, deps = {}, only) {
|
|
|
7452
7516
|
const displacedSame = displacedAll.filter((f) => f.notes.length === 0).map((f) => f.path);
|
|
7453
7517
|
if (displacedMoved.length > 0) report.displaced = displacedMoved;
|
|
7454
7518
|
if (displacedSame.length > 0) report.displacedSame = displacedSame;
|
|
7455
|
-
|
|
7456
|
-
|
|
7457
|
-
|
|
7458
|
-
|
|
7519
|
+
const deadMcp = claudeManaged ? deadMcpLeft(projectDir) : [];
|
|
7520
|
+
if (deadMcp.length > 0) report.deadMcp = deadMcp;
|
|
7521
|
+
judged.push(...external.externalJudged, ...skillRefresh.judged ?? []);
|
|
7522
|
+
if (judged.length > 0) report.judged = judged;
|
|
7523
|
+
const unmeasured = new Set(
|
|
7524
|
+
judged.filter((j4) => j4.recorded === "no-sha" && j4.backup !== void 0).map((j4) => j4.path)
|
|
7525
|
+
);
|
|
7526
|
+
report.backups = collectRunBackups(projectDir, startedAt, backupsAtStart).map(
|
|
7527
|
+
(b2) => existsSync21(join30(projectDir, b2.path)) ? b2 : { ...b2, removed: true }
|
|
7528
|
+
);
|
|
7529
|
+
const noChecksum = report.backups.map((b2) => b2.path).filter((p2) => unmeasured.has(p2));
|
|
7459
7530
|
if (noChecksum.length > 0) report.noChecksum = noChecksum;
|
|
7460
7531
|
writeBackupList(projectDir, report.backups, new Set(noChecksum));
|
|
7461
7532
|
return report;
|
|
@@ -7533,19 +7604,16 @@ function reclaimOutOfTrackRecorded(projectDir, outside) {
|
|
|
7533
7604
|
}
|
|
7534
7605
|
return { reclaim: result, displaced };
|
|
7535
7606
|
}
|
|
7536
|
-
function
|
|
7537
|
-
|
|
7538
|
-
|
|
7539
|
-
|
|
7540
|
-
|
|
7541
|
-
|
|
7542
|
-
|
|
7543
|
-
|
|
7544
|
-
|
|
7545
|
-
|
|
7546
|
-
const anchor = log.templates.rootClaudeMd;
|
|
7547
|
-
if (anchor !== void 0) paths.add(anchor.path);
|
|
7548
|
-
return (path) => paths.has(path);
|
|
7607
|
+
function deadMcpLeft(projectDir) {
|
|
7608
|
+
let servers;
|
|
7609
|
+
try {
|
|
7610
|
+
servers = JSON.parse(readFileSync22(join30(projectDir, ".mcp.json"), "utf8")).mcpServers;
|
|
7611
|
+
} catch {
|
|
7612
|
+
return [];
|
|
7613
|
+
}
|
|
7614
|
+
return PRIOR_MCP_SERVERS.flatMap(
|
|
7615
|
+
(p2) => p2.dead !== void 0 && servers?.[p2.name] !== void 0 && jsonSha(servers[p2.name]) === jsonSha(toClaudeServer(p2.value)) ? [{ name: p2.name, note: p2.dead }] : []
|
|
7616
|
+
);
|
|
7549
7617
|
}
|
|
7550
7618
|
function retiredFeaturesPart(projectDir, f) {
|
|
7551
7619
|
if (f.path !== ".codex/config.toml") return {};
|
|
@@ -7739,6 +7807,7 @@ function updateHarnessTargets(projectDir, templatesDir, log, wants, report, fore
|
|
|
7739
7807
|
}
|
|
7740
7808
|
function updateHarnessFiles(args) {
|
|
7741
7809
|
const { projectDir, templatesDir, log, wants, outside, report } = args;
|
|
7810
|
+
const judged = [];
|
|
7742
7811
|
const now = /* @__PURE__ */ new Date();
|
|
7743
7812
|
const claude = installedClis(log).includes("claude");
|
|
7744
7813
|
const claims = retiredClaims(log, projectDir, log.policyFiles ?? []);
|
|
@@ -7759,7 +7828,7 @@ function updateHarnessFiles(args) {
|
|
|
7759
7828
|
if (!claude || !wants(dir === "hooks" ? "hooks" : "rules")) continue;
|
|
7760
7829
|
const label = `.claude/${dir}`;
|
|
7761
7830
|
report.updated[label] = 0;
|
|
7762
|
-
const ctx = { prefix: dir, outside, recordOf };
|
|
7831
|
+
const ctx = { prefix: dir, outside, recordOf, projectDir, judged };
|
|
7763
7832
|
report.pruned[label] = pruneOrphans(
|
|
7764
7833
|
join30(projectDir, label),
|
|
7765
7834
|
join30(templatesDir, dir),
|
|
@@ -7776,7 +7845,8 @@ function updateHarnessFiles(args) {
|
|
|
7776
7845
|
targets.refreshedSkills,
|
|
7777
7846
|
outside,
|
|
7778
7847
|
recordOf,
|
|
7779
|
-
now
|
|
7848
|
+
now,
|
|
7849
|
+
judged
|
|
7780
7850
|
);
|
|
7781
7851
|
report.skillsPruned = skillPrune.pruned;
|
|
7782
7852
|
report.skillsBackedUp.push(...skillPrune.backedUp);
|
|
@@ -7785,6 +7855,11 @@ function updateHarnessFiles(args) {
|
|
|
7785
7855
|
write: true
|
|
7786
7856
|
});
|
|
7787
7857
|
const ledger = writer.ledger();
|
|
7858
|
+
judged.unshift(
|
|
7859
|
+
...ledger.writes.flatMap(
|
|
7860
|
+
({ path, verdict, recorded: recorded2, line, backup, backupAbs }) => backup === void 0 || backupAbs === void 0 ? [] : [{ path, verdict, recorded: recorded2, line, backup, backupAbs }]
|
|
7861
|
+
)
|
|
7862
|
+
);
|
|
7788
7863
|
recordHarnessWrites(projectDir, ledger, {
|
|
7789
7864
|
claims,
|
|
7790
7865
|
prunedPolicy,
|
|
@@ -7815,10 +7890,25 @@ function updateHarnessFiles(args) {
|
|
|
7815
7890
|
displaced: ledger.rootFiles.filter((f) => f.change === "displaced"),
|
|
7816
7891
|
restored,
|
|
7817
7892
|
foreignOwned,
|
|
7818
|
-
outside: ledger.outside
|
|
7893
|
+
outside: ledger.outside,
|
|
7894
|
+
judged
|
|
7895
|
+
};
|
|
7896
|
+
}
|
|
7897
|
+
function removedRow(projectDir, path, why, judged, backupAbs) {
|
|
7898
|
+
const { line, recorded: recorded2 } = judged;
|
|
7899
|
+
if (backupAbs === null) return { path, verdict: "remove", recorded: recorded2, line: `${why} \u2014 ${line}` };
|
|
7900
|
+
const backup = relative5(projectDir, backupAbs).split(sep6).join("/");
|
|
7901
|
+
return {
|
|
7902
|
+
path,
|
|
7903
|
+
verdict: "backup+remove",
|
|
7904
|
+
recorded: recorded2,
|
|
7905
|
+
line: `${why} \u2014 ${withBackup(line, backup)}`,
|
|
7906
|
+
backup,
|
|
7907
|
+
backupAbs
|
|
7819
7908
|
};
|
|
7820
7909
|
}
|
|
7821
|
-
|
|
7910
|
+
var NOT_RECORDED_REMOVED = "not on the install record \u2014 saved as <file>.backup-<time>, removed from live";
|
|
7911
|
+
function pruneSkillExtras(projectDir, templatesDir, ids, outside, recordOf, now, judged) {
|
|
7822
7912
|
const pruned = [];
|
|
7823
7913
|
const backedUp = [];
|
|
7824
7914
|
for (const id of ids) {
|
|
@@ -7829,11 +7919,18 @@ function pruneSkillExtras(projectDir, templatesDir, ids, outside, recordOf, now)
|
|
|
7829
7919
|
if (foreignOwnedTarget(projectDir, `.claude/skills/${id}/${rel}`) !== null) continue;
|
|
7830
7920
|
const targetFile = join30(targetSkill, rel);
|
|
7831
7921
|
if (outside.skip(targetFile)) continue;
|
|
7832
|
-
const
|
|
7833
|
-
|
|
7834
|
-
|
|
7922
|
+
const path = `.claude/skills/${id}/${rel}`;
|
|
7923
|
+
const rec = recordOf(path);
|
|
7924
|
+
const disk = rec.state === "sha" ? readFileSync22(targetFile, "utf8") : "";
|
|
7925
|
+
const line = rec.state === "none" ? NOT_RECORDED_REMOVED : judge({ op: "remove", kind: "harness", rec, disk, next: null }).line;
|
|
7926
|
+
let backupAbs = null;
|
|
7927
|
+
if (rec.state !== "sha" || hashContent(disk) !== rec.sha256) {
|
|
7928
|
+
backupAbs = backupFile(targetFile, now);
|
|
7835
7929
|
backedUp.push(`${id}/${rel}`);
|
|
7836
7930
|
}
|
|
7931
|
+
judged.push(
|
|
7932
|
+
removedRow(projectDir, path, "not in bundle", { line, recorded: rec.state }, backupAbs)
|
|
7933
|
+
);
|
|
7837
7934
|
unlinkSync2(targetFile);
|
|
7838
7935
|
pruned.push(`${id}/${rel}`);
|
|
7839
7936
|
for (let d2 = dirname7(targetFile); d2 !== targetSkill && readdirSync4(d2).length === 0; d2 = dirname7(d2))
|
|
@@ -7908,7 +8005,7 @@ function writeBackupList(projectDir, backups, noChecksum = /* @__PURE__ */ new S
|
|
|
7908
8005
|
`${JSON.stringify(
|
|
7909
8006
|
{
|
|
7910
8007
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7911
|
-
note: "update \uAC00 \uB0A8\uAE34 \uBC31\uC5C5 \uC30D. \uC6D0\uBCF8(path)\uC740 \uCD5C\uC2E0 \uBC88\uB4E4\uD310, \uBC31\uC5C5(backup)\uC740 \uBC14\uAFB8\uAE30 \uC804 \uD310. noChecksum \uC774 \uC5C6\uB294 \uC30D\uC740 \uAE30\uB85D\uACFC \uB2EC\uB77C\uC9C4 \uD30C\uC77C = \uB2F9\uC2E0\uC758 \uD3B8\uC9D1\uBD84\uC774\uB2E4 \u2014 \uC0C8 \uD310\uC5D0 \uB2E4\uC2DC \uC5B9\uC73C\uB824\uBA74 \uB450 \uD30C\uC77C\uC758 diff \uB97C \uBCF4\uB77C. noChecksum: true \uC778 \uC30D\uC740 \uC61B \uD310\uC774 \uCCB4\uD06C\uC12C\uC744 \uAE30\uB85D\uD558\uC9C0 \uC54A\uC544 \uBE44\uAD50 \uC5C6\uC774 \uD55C \uBC88 \uBC31\uC5C5\uD55C \uAC83 \u2014 \uB300\uAC1C \uB9B4\uB9AC\uC988 \uC0AC\uC774 \uD15C\uD50C\uB9BF \uBCC0\uACBD\uBD84\uC774\uB77C \uB2F9\uC2E0\uC758 \uD3B8\uC9D1\uC774 \uC544\uB2D0 \uC218 \uC788\uB2E4.",
|
|
8008
|
+
note: "update \uAC00 \uB0A8\uAE34 \uBC31\uC5C5 \uC30D. \uC6D0\uBCF8(path)\uC740 \uCD5C\uC2E0 \uBC88\uB4E4\uD310, \uBC31\uC5C5(backup)\uC740 \uBC14\uAFB8\uAE30 \uC804 \uD310. noChecksum \uC774 \uC5C6\uB294 \uC30D\uC740 \uAE30\uB85D\uACFC \uB2EC\uB77C\uC9C4 \uD30C\uC77C = \uB2F9\uC2E0\uC758 \uD3B8\uC9D1\uBD84\uC774\uB2E4 \u2014 \uC0C8 \uD310\uC5D0 \uB2E4\uC2DC \uC5B9\uC73C\uB824\uBA74 \uB450 \uD30C\uC77C\uC758 diff \uB97C \uBCF4\uB77C. noChecksum: true \uC778 \uC30D\uC740 \uC61B \uD310\uC774 \uCCB4\uD06C\uC12C\uC744 \uAE30\uB85D\uD558\uC9C0 \uC54A\uC544 \uBE44\uAD50 \uC5C6\uC774 \uD55C \uBC88 \uBC31\uC5C5\uD55C \uAC83 \u2014 \uB300\uAC1C \uB9B4\uB9AC\uC988 \uC0AC\uC774 \uD15C\uD50C\uB9BF \uBCC0\uACBD\uBD84\uC774\uB77C \uB2F9\uC2E0\uC758 \uD3B8\uC9D1\uC774 \uC544\uB2D0 \uC218 \uC788\uB2E4. removed: true \uC778 \uC30D\uC740 \uADF8 \uD30C\uC77C\uC744 \uC9C0\uC6B0\uAE30 \uC804\uC5D0 \uB0A8\uAE34 \uAC83(\uC740\uD1F4\uD55C \uD558\uB124\uC2A4 \uD30C\uC77C \xB7 \uBC88\uB4E4\uC5D0 \uC5C6\uB294 \uC2A4\uD0AC \uD30C\uC77C \uB4F1) \u2014 \uC6D0\uBCF8 \uC790\uB9AC\uC5D0 \uC0C8 \uD310\uC774 \uC5C6\uB2E4. \uB2F9\uC2E0 \uD30C\uC77C\uC774\uC5C8\uB2E4\uBA74 \uBC31\uC5C5\uC744 \uC6D0\uB798 \uC774\uB984\uC73C\uB85C \uB418\uB3CC\uB9AC\uBA74 \uB41C\uB2E4.",
|
|
7912
8009
|
backups: backups.map((b2) => noChecksum.has(b2.path) ? { ...b2, noChecksum: true } : b2)
|
|
7913
8010
|
},
|
|
7914
8011
|
null,
|
|
@@ -7927,7 +8024,7 @@ function trackOnlyFileAssets(tracks) {
|
|
|
7927
8024
|
(e2) => e2.type === "file" && e2.applies(optedOut) && e2.applies(optedIn)
|
|
7928
8025
|
);
|
|
7929
8026
|
}
|
|
7930
|
-
function syncHarnessAnchor(projectDir, templatesDir, report, outside, now = /* @__PURE__ */ new Date()) {
|
|
8027
|
+
function syncHarnessAnchor(projectDir, templatesDir, report, outside, judged, now = /* @__PURE__ */ new Date()) {
|
|
7931
8028
|
if (existsSync21(join30(projectDir, LEGACY_ANCHOR_FILE2))) {
|
|
7932
8029
|
report.legacyAnchor = LEGACY_ANCHOR_FILE2;
|
|
7933
8030
|
}
|
|
@@ -7942,8 +8039,20 @@ function syncHarnessAnchor(projectDir, templatesDir, report, outside, now = /* @
|
|
|
7942
8039
|
const recorded2 = readInstallLog(projectDir)?.templates.rootClaudeMd;
|
|
7943
8040
|
const owned = recorded2?.path === HARNESS_ANCHOR_FILE && hashContent(current) === recorded2.sha256;
|
|
7944
8041
|
if (!owned) {
|
|
7945
|
-
backupFile(anchor, now);
|
|
8042
|
+
const backup = backupFile(anchor, now);
|
|
7946
8043
|
report.anchorBackedUp = true;
|
|
8044
|
+
const rel = relative5(projectDir, backup).split(sep6).join("/");
|
|
8045
|
+
const rec = recorded2?.path === HARNESS_ANCHOR_FILE ? { state: "sha", sha256: recorded2.sha256 } : { state: "no-sha" };
|
|
8046
|
+
const next = readFileSync22(templateMd, "utf8");
|
|
8047
|
+
const { line } = judge({ op: "write", kind: "harness", rec, disk: current, next });
|
|
8048
|
+
judged.push({
|
|
8049
|
+
path: HARNESS_ANCHOR_FILE,
|
|
8050
|
+
verdict: "backup+overwrite",
|
|
8051
|
+
recorded: rec.state,
|
|
8052
|
+
line: withBackup(line, rel),
|
|
8053
|
+
backup: rel,
|
|
8054
|
+
backupAbs: backup
|
|
8055
|
+
});
|
|
7947
8056
|
}
|
|
7948
8057
|
copyFileSync(templateMd, anchor);
|
|
7949
8058
|
report.claudeMdUpdated = true;
|
|
@@ -8107,7 +8216,7 @@ function changedRemoteMcp(writes) {
|
|
|
8107
8216
|
const changed = /* @__PURE__ */ new Set([...mcp.added, ...mcp.replaced ?? []]);
|
|
8108
8217
|
return (mcp.remote ?? []).filter((r) => changed.has(r.name));
|
|
8109
8218
|
}
|
|
8110
|
-
function refreshExternalCli(projectDir, harnessRoot, droppedKeys, harnessMcp) {
|
|
8219
|
+
function refreshExternalCli(projectDir, harnessRoot, droppedKeys, harnessMcp, linkedIds) {
|
|
8111
8220
|
const log = readInstallLog(projectDir);
|
|
8112
8221
|
const presentBefore = sharedSkillIdsOnDisk(projectDir);
|
|
8113
8222
|
const rulesBefore = /* @__PURE__ */ new Set();
|
|
@@ -8139,11 +8248,23 @@ function refreshExternalCli(projectDir, harnessRoot, droppedKeys, harnessMcp) {
|
|
|
8139
8248
|
// 앞 단계(하네스 파일 쓰기)가 헬퍼만 든 `externalFiles` 를 적어도 같은 답이다 — 헬퍼는 외부 자리로 세지 않는다(B-2)
|
|
8140
8249
|
owned: recordedOwner(log)
|
|
8141
8250
|
});
|
|
8251
|
+
const linked = refreshLinkedSkillBodies({
|
|
8252
|
+
harnessRoot,
|
|
8253
|
+
projectDir,
|
|
8254
|
+
ids: linkedIds.filter(
|
|
8255
|
+
(id) => !result.externalFiles.some((f) => f.path === `${SHARED_SKILLS_DIR}/${id}/SKILL.md`)
|
|
8256
|
+
),
|
|
8257
|
+
baseline: new Map(
|
|
8258
|
+
mergeExternalFiles(log?.externalFiles, result.externalFiles).map((f) => [f.path, f.sha256])
|
|
8259
|
+
)
|
|
8260
|
+
});
|
|
8261
|
+
const written = [...result.externalFiles, ...linked.files];
|
|
8262
|
+
const displaced = [...result.displaced ?? [], ...linked.displaced];
|
|
8142
8263
|
const anchorPath = result.anchorRetired?.action === "removed" ? result.anchorRetired.path : null;
|
|
8143
8264
|
const anchorDisplaced = log === null || anchorPath === null ? [] : restoreDisplaced(log, projectDir, { has: (p2) => p2 === anchorPath, write: true });
|
|
8144
8265
|
if (log) {
|
|
8145
8266
|
const retiredPath = result.anchorRetired?.forget === true ? result.anchorRetired.path : null;
|
|
8146
|
-
const merged = mergeExternalFiles(log.externalFiles,
|
|
8267
|
+
const merged = mergeExternalFiles(log.externalFiles, written).filter(
|
|
8147
8268
|
(f) => f.path !== retiredPath
|
|
8148
8269
|
);
|
|
8149
8270
|
const next = retiredPath === null ? { ...log } : { ...log, spec: { ...log.spec, clis: [...installedClis(log)] } };
|
|
@@ -8157,7 +8278,7 @@ function refreshExternalCli(projectDir, harnessRoot, droppedKeys, harnessMcp) {
|
|
|
8157
8278
|
if (portions.length > 0) next.portions = portions;
|
|
8158
8279
|
else delete next.portions;
|
|
8159
8280
|
const created = result.sharedFiles.filter((f) => f.createdAsRoot === true).map((f) => ({ path: f.path, change: "created", notes: ["\uD558\uB124\uC2A4 \uBAAB\uB9CC(\uBE14\uB85D)"] }));
|
|
8160
|
-
const roots = [...created, ...withoutLostBackups(log.rootFiles,
|
|
8281
|
+
const roots = [...created, ...withoutLostBackups(log.rootFiles, displaced)];
|
|
8161
8282
|
if (roots.length > 0) next.rootFiles = mergeRootFiles(log.rootFiles, roots);
|
|
8162
8283
|
if (anchorDisplaced.some((d2) => d2.forget))
|
|
8163
8284
|
next.rootFiles = withoutRestored(next.rootFiles, anchorDisplaced);
|
|
@@ -8216,11 +8337,12 @@ function refreshExternalCli(projectDir, harnessRoot, droppedKeys, harnessMcp) {
|
|
|
8216
8337
|
const dropped = new Set(droppedKeys);
|
|
8217
8338
|
return {
|
|
8218
8339
|
externalUpdated: result.externalUpdated,
|
|
8219
|
-
externalBackedUp: result.externalBackedUp,
|
|
8220
|
-
|
|
8221
|
-
|
|
8222
|
-
|
|
8223
|
-
|
|
8340
|
+
externalBackedUp: [...result.externalBackedUp, ...linked.backedUp],
|
|
8341
|
+
externalJudged: [...result.externalJudged, ...linked.judged],
|
|
8342
|
+
displaced,
|
|
8343
|
+
externalForeignOwned: [...result.externalForeignOwned, ...linked.foreignOwned],
|
|
8344
|
+
externalOutside: [...result.externalOutside, ...linked.outside],
|
|
8345
|
+
written: written.map((f) => f.path),
|
|
8224
8346
|
skillsInstalled,
|
|
8225
8347
|
skillsRestored,
|
|
8226
8348
|
rulesRestored,
|
|
@@ -8275,14 +8397,24 @@ function pruneOrphans(target, source, ext, ctx, now) {
|
|
|
8275
8397
|
for (const file of readdirSync4(target)) {
|
|
8276
8398
|
if (!file.endsWith(ext) || existsSync21(join30(source, file))) continue;
|
|
8277
8399
|
const targetFile = join30(target, file);
|
|
8278
|
-
const
|
|
8400
|
+
const path = `.claude/${ctx.prefix}/${file}`;
|
|
8401
|
+
const rec = ctx.recordOf(path);
|
|
8279
8402
|
try {
|
|
8280
8403
|
const disk = readFileSync22(targetFile, "utf8");
|
|
8281
|
-
const { verdict } = judge({ op: "remove", kind: "harness", rec, disk, next: null });
|
|
8404
|
+
const { verdict, line } = judge({ op: "remove", kind: "harness", rec, disk, next: null });
|
|
8282
8405
|
if (verdict === "leave" || ctx.outside.skip(targetFile)) continue;
|
|
8283
|
-
|
|
8406
|
+
const backupAbs = verdict === "backup+remove" ? backupFile(targetFile, now) : null;
|
|
8284
8407
|
unlinkSync2(targetFile);
|
|
8285
8408
|
removed.push(file);
|
|
8409
|
+
ctx.judged.push(
|
|
8410
|
+
removedRow(
|
|
8411
|
+
ctx.projectDir,
|
|
8412
|
+
path,
|
|
8413
|
+
"retired from the harness",
|
|
8414
|
+
{ line, recorded: rec.state },
|
|
8415
|
+
backupAbs
|
|
8416
|
+
)
|
|
8417
|
+
);
|
|
8286
8418
|
} catch {
|
|
8287
8419
|
}
|
|
8288
8420
|
}
|
|
@@ -8346,7 +8478,7 @@ function runInstall(ctx) {
|
|
|
8346
8478
|
JSON.parse(readFileSync23(settingsPath, "utf8"));
|
|
8347
8479
|
} catch {
|
|
8348
8480
|
throw new Error(
|
|
8349
|
-
`${
|
|
8481
|
+
`${relative6(process.cwd(), settingsPath)} is not valid JSON \u2014 left untouched, so no hooks were wired. Fix or delete that file, then run the install again`
|
|
8350
8482
|
);
|
|
8351
8483
|
}
|
|
8352
8484
|
}
|
|
@@ -8415,6 +8547,7 @@ function runInstallStages(ctx, args) {
|
|
|
8415
8547
|
externalBackups,
|
|
8416
8548
|
externalUpdated: _externalUpdated,
|
|
8417
8549
|
externalBackedUp: _externalBackedUp,
|
|
8550
|
+
externalJudged: _externalJudged,
|
|
8418
8551
|
displaced: cliDisplaced = [],
|
|
8419
8552
|
externalForeignOwned,
|
|
8420
8553
|
externalOutside,
|
|
@@ -8967,7 +9100,7 @@ function recordInterruptedInstall(ctx, error, run) {
|
|
|
8967
9100
|
const codexTrust = registeredTrust(journal.codexOptIn, ctx.projectDir);
|
|
8968
9101
|
const written = writtenSoFar(ledger, cliFiles, rootFiles, stage.rootImportWritten);
|
|
8969
9102
|
if (codexTrust) written.push(`${codexTrust.configPath} (Codex trust entry for this folder)`);
|
|
8970
|
-
const projectRel = (abs) =>
|
|
9103
|
+
const projectRel = (abs) => relative6(ctx.projectDir, abs).split(sep7).join("/");
|
|
8971
9104
|
const writtenSet = new Set(written);
|
|
8972
9105
|
const backups = [...own.backups, ...journal.backups].map((abs) => {
|
|
8973
9106
|
const original = projectRel(backupOriginal(abs) ?? abs);
|
|
@@ -9144,7 +9277,7 @@ function listHookFiles(hookDir) {
|
|
|
9144
9277
|
init_esm_shims();
|
|
9145
9278
|
import { homedir as homedir3 } from "os";
|
|
9146
9279
|
import { existsSync as existsSync24, readdirSync as readdirSync6, readFileSync as readFileSync25 } from "fs";
|
|
9147
|
-
import { join as join33, relative as
|
|
9280
|
+
import { join as join33, relative as relative7 } from "path";
|
|
9148
9281
|
|
|
9149
9282
|
// src/preset-recommend.ts
|
|
9150
9283
|
init_esm_shims();
|
|
@@ -9372,9 +9505,11 @@ function renderUpdateSummary(log, spec, report) {
|
|
|
9372
9505
|
});
|
|
9373
9506
|
const backups = report.updateMode?.backups ?? [];
|
|
9374
9507
|
if (backups.length > 0) {
|
|
9375
|
-
const
|
|
9376
|
-
const
|
|
9377
|
-
const
|
|
9508
|
+
const replaced = backups.filter((b2) => b2.removed !== true);
|
|
9509
|
+
const removedBefore = backups.length - replaced.length;
|
|
9510
|
+
const noChecksum = new Set(report.updateMode?.noChecksum ?? []);
|
|
9511
|
+
const unmeasured = replaced.filter((b2) => noChecksum.has(b2.path)).length;
|
|
9512
|
+
const unmeasuredPart = unmeasured === 0 ? "" : unmeasured === backups.length ? " \u2014 no checksum on record for any of them (installed before checksums were kept), so they may not be your edits" : ` \u2014 ${unmeasured} ${removedBefore > 0 ? "replaced " : ""}had no checksum on record (marked noChecksum in the list), so those may not be your edits`;
|
|
9378
9513
|
const how = removedBefore === 0 ? "saved before replacing" : removedBefore === backups.length ? "saved before removing" : `saved before replacing (${removedBefore} before removing)`;
|
|
9379
9514
|
log(
|
|
9380
9515
|
infoRow(
|
|
@@ -9382,12 +9517,13 @@ function renderUpdateSummary(log, spec, report) {
|
|
|
9382
9517
|
`${backups.length} file(s) ${how}, as *.backup-<time>${unmeasuredPart} \xB7 list: .uzys-agent-harness/update-backups.json`
|
|
9383
9518
|
)
|
|
9384
9519
|
);
|
|
9385
|
-
const
|
|
9386
|
-
|
|
9520
|
+
const displaced = new Set(report.updateMode?.displaced ?? []);
|
|
9521
|
+
const carry = replaced.filter((b2) => !displaced.has(b2.path)).length;
|
|
9522
|
+
if (carry > 0)
|
|
9387
9523
|
log(
|
|
9388
9524
|
infoRow(
|
|
9389
9525
|
"NEXT",
|
|
9390
|
-
unmeasured ===
|
|
9526
|
+
unmeasured === carry ? '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)'
|
|
9391
9527
|
)
|
|
9392
9528
|
);
|
|
9393
9529
|
}
|
|
@@ -9779,25 +9915,8 @@ function linkedSkillRows(log, updated, notOurs) {
|
|
|
9779
9915
|
);
|
|
9780
9916
|
}
|
|
9781
9917
|
}
|
|
9782
|
-
function hadFileRow(log, area, paths) {
|
|
9783
|
-
if (paths.length === 0) return;
|
|
9784
|
-
log(
|
|
9785
|
-
assetRow(
|
|
9786
|
-
"skip",
|
|
9787
|
-
`${area} had files with these names`,
|
|
9788
|
-
`${paths.length} backed up as *.backup-<time> \u2014 yours, put back on uninstall \xB7 ${paths.join(", ")}`
|
|
9789
|
-
)
|
|
9790
|
-
);
|
|
9791
|
-
}
|
|
9792
9918
|
function renderPhase1Rows(log, baseline, verbose = false) {
|
|
9793
9919
|
if (baseline.updateMode) {
|
|
9794
|
-
const noChecksum = new Set(baseline.updateMode.noChecksum ?? []);
|
|
9795
|
-
const displaced = baseline.updateMode.displaced ?? [];
|
|
9796
|
-
const displacedSkills = displaced.filter((p2) => p2.startsWith(".claude/skills/"));
|
|
9797
|
-
const displacedPolicy = displaced.filter(
|
|
9798
|
-
(p2) => p2.startsWith(".claude/") && !p2.startsWith(".claude/skills/")
|
|
9799
|
-
);
|
|
9800
|
-
const displacedExternal = displaced.filter((p2) => !p2.startsWith(".claude/"));
|
|
9801
9920
|
for (const [dir, count] of Object.entries(baseline.updateMode.updated)) {
|
|
9802
9921
|
if (count > 0) log(assetRow("success", dir, `${count} files updated`));
|
|
9803
9922
|
}
|
|
@@ -9854,6 +9973,8 @@ function renderPhase1Rows(log, baseline, verbose = false) {
|
|
|
9854
9973
|
const row = sharedFileRow(f);
|
|
9855
9974
|
if (row !== null) log(row);
|
|
9856
9975
|
}
|
|
9976
|
+
for (const d2 of baseline.updateMode.deadMcp ?? [])
|
|
9977
|
+
log(assetRow("skip", ".mcp.json", `${d2.name} \u2014 ${d2.note}`));
|
|
9857
9978
|
for (const f of baseline.updateMode.sharedLeft ?? []) {
|
|
9858
9979
|
log(sharedLeftRow(f));
|
|
9859
9980
|
if (f.retiredFeatures !== void 0) log(retiredFeaturesRow(f.path, f.retiredFeatures));
|
|
@@ -9867,11 +9988,7 @@ function renderPhase1Rows(log, baseline, verbose = false) {
|
|
|
9867
9988
|
if (legacy2 !== null) log(legacy2);
|
|
9868
9989
|
for (const row of legacyReleasedCatalogRows(baseline.updateMode.legacyReleasedCatalog ?? []))
|
|
9869
9990
|
log(row);
|
|
9870
|
-
for (const
|
|
9871
|
-
if (removed.length > 0) {
|
|
9872
|
-
log(assetRow("skip", `${dir} orphan prune`, `${removed.length} removed`));
|
|
9873
|
-
}
|
|
9874
|
-
}
|
|
9991
|
+
for (const j4 of baseline.updateMode.judged ?? []) log(judgedRow(j4));
|
|
9875
9992
|
if (baseline.updateMode.skippedGroups.length > 0) {
|
|
9876
9993
|
log(
|
|
9877
9994
|
assetRow(
|
|
@@ -9884,15 +10001,6 @@ function renderPhase1Rows(log, baseline, verbose = false) {
|
|
|
9884
10001
|
if (baseline.updateMode.claudeMdUpdated) {
|
|
9885
10002
|
log(assetRow("success", HARNESS_ANCHOR_FILE, "refreshed from template"));
|
|
9886
10003
|
}
|
|
9887
|
-
if (baseline.updateMode.anchorBackedUp && !noChecksum.has(HARNESS_ANCHOR_FILE)) {
|
|
9888
|
-
log(
|
|
9889
|
-
assetRow(
|
|
9890
|
-
"skip",
|
|
9891
|
-
`${HARNESS_ANCHOR_FILE} edited`,
|
|
9892
|
-
"your edits backed up as *.backup-<time> \u2014 latest template is now active"
|
|
9893
|
-
)
|
|
9894
|
-
);
|
|
9895
|
-
}
|
|
9896
10004
|
if (baseline.updateMode.anchorCreated) {
|
|
9897
10005
|
log(assetRow("success", HARNESS_ANCHOR_FILE, "created from template (anchor migration)"));
|
|
9898
10006
|
}
|
|
@@ -9920,28 +10028,6 @@ function renderPhase1Rows(log, baseline, verbose = false) {
|
|
|
9920
10028
|
if (baseline.updateMode.legacyAnchor) {
|
|
9921
10029
|
log(assetRow("skip", baseline.updateMode.legacyAnchor, LEGACY_ANCHOR_NOTE));
|
|
9922
10030
|
}
|
|
9923
|
-
const skillsEdited = baseline.updateMode.skillsBackedUp.filter(
|
|
9924
|
-
(p2) => !noChecksum.has(`.claude/skills/${p2}`)
|
|
9925
|
-
);
|
|
9926
|
-
if (skillsEdited.length > 0) {
|
|
9927
|
-
log(
|
|
9928
|
-
assetRow(
|
|
9929
|
-
"skip",
|
|
9930
|
-
".claude/skills edited files",
|
|
9931
|
-
`${skillsEdited.length} backed up as *.backup-<time>`
|
|
9932
|
-
)
|
|
9933
|
-
);
|
|
9934
|
-
}
|
|
9935
|
-
hadFileRow(log, ".claude/skills", displacedSkills);
|
|
9936
|
-
if (baseline.updateMode.skillsPruned.length > 0) {
|
|
9937
|
-
log(
|
|
9938
|
-
assetRow(
|
|
9939
|
-
"success",
|
|
9940
|
-
".claude/skills not in bundle",
|
|
9941
|
-
`${baseline.updateMode.skillsPruned.length} files deleted \xB7 ${baseline.updateMode.skillsPruned.join(", ")}`
|
|
9942
|
-
)
|
|
9943
|
-
);
|
|
9944
|
-
}
|
|
9945
10031
|
linkedSkillRows(
|
|
9946
10032
|
log,
|
|
9947
10033
|
baseline.updateMode.skillsLinked ?? [],
|
|
@@ -9995,29 +10081,6 @@ function renderPhase1Rows(log, baseline, verbose = false) {
|
|
|
9995
10081
|
)
|
|
9996
10082
|
);
|
|
9997
10083
|
}
|
|
9998
|
-
const policyEdited = baseline.updateMode.policyBackedUp.filter(
|
|
9999
|
-
(p2) => !noChecksum.has(`.claude/${p2}`)
|
|
10000
|
-
);
|
|
10001
|
-
if (policyEdited.length > 0) {
|
|
10002
|
-
log(
|
|
10003
|
-
assetRow(
|
|
10004
|
-
"skip",
|
|
10005
|
-
"edited policy files",
|
|
10006
|
-
`${policyEdited.length} backed up as *.backup-<time>`
|
|
10007
|
-
)
|
|
10008
|
-
);
|
|
10009
|
-
}
|
|
10010
|
-
hadFileRow(log, "policy files", displacedPolicy);
|
|
10011
|
-
const unmeasured = [...noChecksum].filter((p2) => p2 !== ".mcp-allowlist");
|
|
10012
|
-
if (unmeasured.length > 0) {
|
|
10013
|
-
log(
|
|
10014
|
-
assetRow(
|
|
10015
|
-
"skip",
|
|
10016
|
-
"no checksum on record",
|
|
10017
|
-
`${unmeasured.length} backed up once as *.backup-<time> \u2014 installed before checksums were kept, so these may not be your edits (usually what changed between releases); later updates compare precisely`
|
|
10018
|
-
)
|
|
10019
|
-
);
|
|
10020
|
-
}
|
|
10021
10084
|
const retiredRefs = baseline.updateMode.retiredHookRefsRemoved ?? [];
|
|
10022
10085
|
if (retiredRefs.length > 0) {
|
|
10023
10086
|
log(
|
|
@@ -10067,17 +10130,6 @@ function renderPhase1Rows(log, baseline, verbose = false) {
|
|
|
10067
10130
|
)
|
|
10068
10131
|
);
|
|
10069
10132
|
}
|
|
10070
|
-
const externalEdited = baseline.updateMode.externalBackedUp.filter((p2) => !noChecksum.has(p2));
|
|
10071
|
-
if (externalEdited.length > 0) {
|
|
10072
|
-
log(
|
|
10073
|
-
assetRow(
|
|
10074
|
-
"skip",
|
|
10075
|
-
"edited external CLI files",
|
|
10076
|
-
`${externalEdited.length} backed up as *.backup-<time>`
|
|
10077
|
-
)
|
|
10078
|
-
);
|
|
10079
|
-
}
|
|
10080
|
-
hadFileRow(log, "external CLI files", displacedExternal);
|
|
10081
10133
|
const displacedSame = baseline.updateMode.displacedSame ?? [];
|
|
10082
10134
|
if (displacedSame.length > 0) {
|
|
10083
10135
|
log(
|
|
@@ -10538,7 +10590,7 @@ function renderInterruptedInstall(err, e2, cwd = process.cwd()) {
|
|
|
10538
10590
|
}
|
|
10539
10591
|
const { path, error } = e2.record;
|
|
10540
10592
|
err(
|
|
10541
|
-
path !== null ? ` Already in place before it stopped \u2014 recorded in ${
|
|
10593
|
+
path !== null ? ` Already in place before it stopped \u2014 recorded in ${relative7(cwd, path) || path}:` : " Already in place before it stopped:"
|
|
10542
10594
|
);
|
|
10543
10595
|
for (const [key, n] of groups) {
|
|
10544
10596
|
err(` ${key.endsWith("/") ? `${key} (${n} file${n === 1 ? "" : "s"})` : key}`);
|
|
@@ -11011,7 +11063,7 @@ import {
|
|
|
11011
11063
|
statSync as statSync5,
|
|
11012
11064
|
writeFileSync as writeFileSync9
|
|
11013
11065
|
} from "fs";
|
|
11014
|
-
import { dirname as dirname8, join as join35, relative as
|
|
11066
|
+
import { dirname as dirname8, join as join35, relative as relative8, resolve as resolve4, sep as sep8 } from "path";
|
|
11015
11067
|
|
|
11016
11068
|
// src/uninstall-interactive.ts
|
|
11017
11069
|
init_esm_shims();
|
|
@@ -12940,12 +12992,7 @@ function settleLog(ctx, io) {
|
|
|
12940
12992
|
const recordDir = join35(projectDir, INSTALL_LOG_DIR);
|
|
12941
12993
|
if (outsideLinkAt(projectDir, INSTALL_LOG_DIR) !== null) io.rm(recordDir);
|
|
12942
12994
|
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
|
-
}
|
|
12995
|
+
for (const rel of RECORD_FILES) io.rm(join35(projectDir, rel));
|
|
12949
12996
|
pruneEmptyDirsUpward(projectDir, recordDir);
|
|
12950
12997
|
}
|
|
12951
12998
|
if (outsideLinkAt(projectDir, LEGACY_INSTALL_LOG_DIR) === null) {
|
|
@@ -13131,13 +13178,16 @@ function resolveCliTarget(ctx, io) {
|
|
|
13131
13178
|
function underAny(path, owned) {
|
|
13132
13179
|
return owned.some((o) => o.endsWith("/") ? path.startsWith(o) : path === o);
|
|
13133
13180
|
}
|
|
13134
|
-
function planCliRemoval(target, remaining) {
|
|
13181
|
+
function planCliRemoval(target, remaining, log, projectDir) {
|
|
13135
13182
|
const { exclusive, sharedNowUnowned } = removableFor(target, remaining);
|
|
13136
13183
|
const owned = [...exclusive, ...sharedNowUnowned];
|
|
13137
13184
|
const prefixes = owned.filter((o) => o.kind === "recorded" || o.kind === "anchor").map((o) => o.path);
|
|
13138
|
-
const
|
|
13185
|
+
const used = (path) => usersOf(path, projectDir).some((cli) => remaining.includes(cli));
|
|
13186
|
+
const has = (path) => underAny(path, prefixes) && !used(path);
|
|
13187
|
+
const kept = harnessCandidates(log).filter((p2) => underAny(p2, prefixes) && used(p2)).map((p2) => p2.split("/").slice(0, 3).join("/"));
|
|
13139
13188
|
return {
|
|
13140
13189
|
target,
|
|
13190
|
+
kept: [...new Set(kept)].sort(),
|
|
13141
13191
|
scope: {
|
|
13142
13192
|
has,
|
|
13143
13193
|
// `.claude/settings.json` 은 claude 의 `.claude/` 아래 · `.codex/config.toml` 은 codex 의 `.codex/` 아래 · `AGENTS.md` ·
|
|
@@ -13151,6 +13201,11 @@ function planCliRemoval(target, remaining) {
|
|
|
13151
13201
|
}
|
|
13152
13202
|
};
|
|
13153
13203
|
}
|
|
13204
|
+
function keptForRemainingLines(plan, preview) {
|
|
13205
|
+
if (plan.kept.length === 0) return [];
|
|
13206
|
+
const what = `${plan.kept.join(" \xB7 ")} \u2014 still used by Claude Code through its .claude/skills/ link(s)`;
|
|
13207
|
+
return [preview ? ` \u25CB leave ${what}` : ` ${c.yellow("\u2298")} left ${what}`];
|
|
13208
|
+
}
|
|
13154
13209
|
function removeCliHeader(target, installed, remaining) {
|
|
13155
13210
|
return [
|
|
13156
13211
|
"",
|
|
@@ -13172,6 +13227,7 @@ function removeCliDryRunLines(plan, ctx, remaining) {
|
|
|
13172
13227
|
harnessRoot
|
|
13173
13228
|
});
|
|
13174
13229
|
lines2.push(...harnessRemovalLines(preview, installLog, projectDir, plan.scope, true));
|
|
13230
|
+
lines2.push(...keptForRemainingLines(plan, true));
|
|
13175
13231
|
lines2.push(...assetsLeftLines(installLog, projectDir, plan.scope));
|
|
13176
13232
|
const rerender = agentsMdRerenderSet(plan.target, remaining, installLog, projectDir);
|
|
13177
13233
|
if (rerender !== null) {
|
|
@@ -13281,7 +13337,7 @@ function removeCliAction(ctx, io) {
|
|
|
13281
13337
|
const { options, installLog, projectDir, harnessRoot } = ctx;
|
|
13282
13338
|
const installed = installedClis(installLog);
|
|
13283
13339
|
const remaining = installed.filter((cli) => cli !== target);
|
|
13284
|
-
const plan = planCliRemoval(target, remaining);
|
|
13340
|
+
const plan = planCliRemoval(target, remaining, installLog, projectDir);
|
|
13285
13341
|
for (const line of removeCliHeader(target, installed, remaining)) io.log(line);
|
|
13286
13342
|
if (options.dryRun) {
|
|
13287
13343
|
for (const line of removeCliDryRunLines(plan, ctx, remaining)) io.log(line);
|
|
@@ -13296,6 +13352,7 @@ function removeCliAction(ctx, io) {
|
|
|
13296
13352
|
for (const line of harnessRemovalLines(done, installLog, projectDir, plan.scope, false)) {
|
|
13297
13353
|
io.log(line);
|
|
13298
13354
|
}
|
|
13355
|
+
for (const line of keptForRemainingLines(plan, false)) io.log(line);
|
|
13299
13356
|
for (const line of assetsLeftLines(installLog, projectDir, plan.scope)) io.log(line);
|
|
13300
13357
|
const rerenderSet = agentsMdRerenderSet(target, remaining, installLog, projectDir);
|
|
13301
13358
|
const rerender = rerenderSet === null ? null : rerenderAgentsMd(ctx, rerenderSet);
|
|
@@ -13561,11 +13618,16 @@ var LEGACY_LOG = `${LEGACY_INSTALL_LOG_DIR}/${INSTALL_LOG_FILENAME}`;
|
|
|
13561
13618
|
var SKILLS_DIR3 = ".claude/skills/";
|
|
13562
13619
|
var CLI_FOLDERS2 = [".claude", ".codex", ".opencode"];
|
|
13563
13620
|
var LEFT_FILES_SHOWN = 3;
|
|
13621
|
+
var RECORD_FILES = [
|
|
13622
|
+
`${INSTALL_LOG_DIR}/${INSTALL_LOG_FILENAME}`,
|
|
13623
|
+
...HARNESS_RUNTIME_IGNORES
|
|
13624
|
+
];
|
|
13564
13625
|
var FULL_SCOPE = {
|
|
13565
13626
|
has: () => true,
|
|
13566
13627
|
shared: UNINSTALL_SHARED,
|
|
13567
13628
|
skeleton: PROJECT_SKELETON_DIRS,
|
|
13568
|
-
|
|
13629
|
+
// #817 — 기록 폴더도 — 헬퍼는 파일 하나씩 회수하고(#812) 설치자 파일은 남으니, 폴더가 왜 남았는지 같은 줄로 말한다
|
|
13630
|
+
folders: [...CLI_FOLDERS2, INSTALL_LOG_DIR],
|
|
13569
13631
|
importBlock: true,
|
|
13570
13632
|
anchor: true
|
|
13571
13633
|
};
|
|
@@ -13722,7 +13784,7 @@ function removeRecordedFile(path, ctx) {
|
|
|
13722
13784
|
if (verdict.verdict !== "remove" && verdict.verdict !== "backup+remove") return;
|
|
13723
13785
|
let backup = null;
|
|
13724
13786
|
if (verdict.verdict === "backup+remove") {
|
|
13725
|
-
backup = io.write ?
|
|
13787
|
+
backup = io.write ? relative8(projectDir, backupFile(effective)).split(sep8).join("/") : `${relative8(projectDir, effective).split(sep8).join("/")}.backup-<time>`;
|
|
13726
13788
|
}
|
|
13727
13789
|
if (io.write) {
|
|
13728
13790
|
if (effective !== abs) io.rm(abs);
|
|
@@ -13742,7 +13804,7 @@ function legacyFileCandidates() {
|
|
|
13742
13804
|
}
|
|
13743
13805
|
function mayReclaimLegacy(log, projectDir, path) {
|
|
13744
13806
|
const clis = installedClis(log);
|
|
13745
|
-
if (!
|
|
13807
|
+
if (!usersOf(path, projectDir).some((c3) => clis.includes(c3))) return false;
|
|
13746
13808
|
if (isExcludedPath(bundlePath(path), excludedIds(log))) return false;
|
|
13747
13809
|
return !(log.rootFiles ?? []).some(
|
|
13748
13810
|
(f) => f.path === path && f.change === "displaced" && !isLegacyDisplaced(log, projectDir, f)
|
|
@@ -14025,7 +14087,7 @@ function leftFolderLines(r, log, projectDir, scope, preview) {
|
|
|
14025
14087
|
const entries = /* @__PURE__ */ new Map();
|
|
14026
14088
|
for (const n of yoursUnder(projectDir, dir)) {
|
|
14027
14089
|
const full = `${dir}/${n.name}`;
|
|
14028
|
-
if (full === LEGACY_LOG) continue;
|
|
14090
|
+
if (full === LEGACY_LOG || RECORD_FILES.includes(full)) continue;
|
|
14029
14091
|
if (!gone.has(full) && !assetFiles.has(full) && !said(full)) entries.set(n.name, n.shown);
|
|
14030
14092
|
}
|
|
14031
14093
|
for (const p2 of back)
|
|
@@ -14125,7 +14187,7 @@ function removeAnchor(log, projectDir, io) {
|
|
|
14125
14187
|
]
|
|
14126
14188
|
};
|
|
14127
14189
|
}
|
|
14128
|
-
const backup = edited ?
|
|
14190
|
+
const backup = edited ? relative8(projectDir, backupFile(abs)).split(sep8).join("/") : null;
|
|
14129
14191
|
io.rm(abs);
|
|
14130
14192
|
return {
|
|
14131
14193
|
removed: true,
|
|
@@ -14188,7 +14250,7 @@ function removeLegacyAgentsMd(path, abs, effective, strip, ctx) {
|
|
|
14188
14250
|
let backup = `${path}.backup-<time>`;
|
|
14189
14251
|
try {
|
|
14190
14252
|
if (io.write) {
|
|
14191
|
-
backup =
|
|
14253
|
+
backup = relative8(projectDir, backupFile(effective)).split(sep8).join("/");
|
|
14192
14254
|
if (strip.remainder !== null) {
|
|
14193
14255
|
writeFileSync9(effective, neutralizeScaffoldBanner(strip.remainder), "utf8");
|
|
14194
14256
|
} else {
|
|
@@ -14234,7 +14296,7 @@ function removeToolFiles(files, projectDir, rm) {
|
|
|
14234
14296
|
try {
|
|
14235
14297
|
if (hashContent(readFileSync27(abs, "utf8")) !== sha256) {
|
|
14236
14298
|
const backup = backupFile(abs);
|
|
14237
|
-
notes.push(`backed up ${path} \u2192 ${
|
|
14299
|
+
notes.push(`backed up ${path} \u2192 ${relative8(projectDir, backup)} (changed since install)`);
|
|
14238
14300
|
}
|
|
14239
14301
|
rm(abs);
|
|
14240
14302
|
removed.push(path);
|
|
@@ -14249,8 +14311,8 @@ function removeToolFiles(files, projectDir, rm) {
|
|
|
14249
14311
|
function pruneEmptyDirsUpward(projectDir, startDir) {
|
|
14250
14312
|
let cursor = startDir;
|
|
14251
14313
|
while (true) {
|
|
14252
|
-
const rel =
|
|
14253
|
-
if (rel === "" || rel.startsWith(`..${
|
|
14314
|
+
const rel = relative8(projectDir, cursor);
|
|
14315
|
+
if (rel === "" || rel.startsWith(`..${sep8}`) || rel === "..") return;
|
|
14254
14316
|
try {
|
|
14255
14317
|
if (readdirSync7(cursor).length > 0) return;
|
|
14256
14318
|
rmdirSync3(cursor);
|