@uzysjung/agent-harness 26.173.0 → 26.174.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
|
@@ -96,12 +96,13 @@ import {
|
|
|
96
96
|
resolveScope,
|
|
97
97
|
scaffoldBanner,
|
|
98
98
|
sharedPathOfKeyId,
|
|
99
|
+
shouldInstallAsset,
|
|
99
100
|
stripHarnessImport,
|
|
100
101
|
summarizeContextCost,
|
|
101
102
|
targetsInclude,
|
|
102
103
|
upsertHarnessImport,
|
|
103
104
|
writeInstallLog
|
|
104
|
-
} from "./chunk-
|
|
105
|
+
} from "./chunk-LAWKDFKZ.js";
|
|
105
106
|
|
|
106
107
|
// node_modules/sisteransi/src/index.js
|
|
107
108
|
var require_src = __commonJS({
|
|
@@ -5056,7 +5057,7 @@ function allowSkillCreation(writer, projectDir, ids) {
|
|
|
5056
5057
|
}
|
|
5057
5058
|
function agentsSkillSlot(params) {
|
|
5058
5059
|
const { projectDir, selectedInternalSkills, refreshOnly, writer } = params;
|
|
5059
|
-
const creatable = refreshOnly ? bundledSkillTargets(projectDir) : [];
|
|
5060
|
+
const creatable = refreshOnly ? bundledSkillTargets(projectDir, params.log) : [];
|
|
5060
5061
|
return {
|
|
5061
5062
|
skillIds: creatable.length === 0 ? selectedInternalSkills : [.../* @__PURE__ */ new Set([...selectedInternalSkills, ...creatable])],
|
|
5062
5063
|
skillWriter: allowSkillCreation(writer, projectDir, creatable)
|
|
@@ -5486,7 +5487,8 @@ function runAgentsMdTransform(params) {
|
|
|
5486
5487
|
projectDir,
|
|
5487
5488
|
selectedInternalSkills: params.selectedInternalSkills,
|
|
5488
5489
|
refreshOnly,
|
|
5489
|
-
writer
|
|
5490
|
+
writer,
|
|
5491
|
+
...params.log === void 0 ? {} : { log: params.log }
|
|
5490
5492
|
});
|
|
5491
5493
|
const creates = !exists2 && (!refreshOnly || params.installedByRecord === true);
|
|
5492
5494
|
const legacy = params.legacyContext ?? null;
|
|
@@ -8119,7 +8121,7 @@ function excludedSkillOf(log) {
|
|
|
8119
8121
|
const excluded = excludedIds(log);
|
|
8120
8122
|
return (id) => excluded.has(id) || isBaselineExcluded(`.claude/skills/${id}`, excluded);
|
|
8121
8123
|
}
|
|
8122
|
-
function recordedRenderInputs(projectDir, log) {
|
|
8124
|
+
function recordedRenderInputs(projectDir, log, tracks = installedTracks(projectDir)) {
|
|
8123
8125
|
const excluded = excludedIds(log);
|
|
8124
8126
|
return {
|
|
8125
8127
|
// 스킬은 **기록 + 이번 릴리즈 기본**만 넘긴다(#761 — `recordedBundledSkills`). 스킬 *파일*은 전체 목록을 넘겨도
|
|
@@ -8132,7 +8134,7 @@ function recordedRenderInputs(projectDir, log) {
|
|
|
8132
8134
|
),
|
|
8133
8135
|
// #601 — install 과 같은 SSOT(resolveRules)를 설치된 트랙으로. 렌더 경로(AGENTS.md 절 · antigravity 룰 생성)는
|
|
8134
8136
|
// 디스크 게이트가 없어, data 트랙 설치에서 update 만으로 cli-development(제6룰)가 생겼다.
|
|
8135
|
-
rules: resolveRules({ tracks
|
|
8137
|
+
rules: resolveRules({ tracks }).filter(
|
|
8136
8138
|
(r) => !isBaselineExcluded(`.claude/rules/${r}.md`, excluded)
|
|
8137
8139
|
)
|
|
8138
8140
|
};
|
|
@@ -11065,6 +11067,55 @@ import {
|
|
|
11065
11067
|
} from "fs";
|
|
11066
11068
|
import { dirname as dirname8, join as join35, relative as relative8, resolve as resolve4, sep as sep8 } from "path";
|
|
11067
11069
|
|
|
11070
|
+
// src/track-removal.ts
|
|
11071
|
+
init_esm_shims();
|
|
11072
|
+
function trackRemoval(log, target) {
|
|
11073
|
+
const known = log.spec.tracks.filter(isTrack);
|
|
11074
|
+
const remaining = known.filter((t) => t !== target);
|
|
11075
|
+
const stays = claudeTargets(remaining);
|
|
11076
|
+
const only = [...claudeTargets(known)].filter((p2) => !stays.has(p2));
|
|
11077
|
+
const has = (path) => only.some((t) => claudeSpot(path) === t || claudeSpot(path).startsWith(`${t}/`));
|
|
11078
|
+
return { target, known, remaining, has };
|
|
11079
|
+
}
|
|
11080
|
+
function claudeSpot(path) {
|
|
11081
|
+
return bundlePath(path).replace(/^\.agents\/rules\//, ".claude/rules/");
|
|
11082
|
+
}
|
|
11083
|
+
function trackRemovalNames(removed, target) {
|
|
11084
|
+
const groups = /* @__PURE__ */ new Map();
|
|
11085
|
+
for (const p2 of removed) {
|
|
11086
|
+
const m = /^\.claude\/([^/]+)\/([^/]+?)(?:\.md)?(?:\/|$)/.exec(claudeSpot(p2));
|
|
11087
|
+
const [kind, name] = m ? [m[1] ?? "", m[2] ?? ""] : ["files", p2];
|
|
11088
|
+
groups.set(kind, (groups.get(kind) ?? /* @__PURE__ */ new Set()).add(name));
|
|
11089
|
+
}
|
|
11090
|
+
const names = [...groups].sort(([a], [b2]) => a.localeCompare(b2)).map(([kind, names2]) => `${kind} ${[...names2].sort().join(", ")}`);
|
|
11091
|
+
return `removes: ${names.join(" \xB7 ") || `nothing only ${target} installs`}`;
|
|
11092
|
+
}
|
|
11093
|
+
function trackLeftLines(log, r, harnessRoot) {
|
|
11094
|
+
const recorded2 = new Set(log.assets.map((a) => a.id));
|
|
11095
|
+
const def = (a, tracks) => shouldInstallAsset(a, { tracks, options: DEFAULT_OPTIONS });
|
|
11096
|
+
const mine = (a) => def(a, r.known) && !def(a, r.remaining);
|
|
11097
|
+
const lines2 = EXTERNAL_ASSETS.filter(
|
|
11098
|
+
(a) => a.method.kind !== "internal" && recorded2.has(a.id) && mine(a)
|
|
11099
|
+
).map(
|
|
11100
|
+
(a) => `asset ${a.id} (a default of ${r.target}) \u2014 remove with: agent-harness uninstall --only ${a.id}`
|
|
11101
|
+
);
|
|
11102
|
+
const keys = (log.portions ?? []).filter((p2) => p2.path === ".mcp.json").map((p2) => p2.key);
|
|
11103
|
+
const staysMcp = new Set(trackDefaultMcpAssetIds(harnessRoot, r.remaining));
|
|
11104
|
+
const mcp = trackDefaultMcpAssetIds(harnessRoot, r.known).filter(
|
|
11105
|
+
(id) => !staysMcp.has(id) && keys.includes(`mcpServers.${id}`)
|
|
11106
|
+
);
|
|
11107
|
+
if (mcp.length === 0) return lines2;
|
|
11108
|
+
const flags = [
|
|
11109
|
+
r.remaining.map((t) => `--track ${t}`),
|
|
11110
|
+
installedClis(log).map((c3) => `--cli ${c3}`)
|
|
11111
|
+
];
|
|
11112
|
+
const command = `agent-harness install ${[...flags.flat(), ...mcp.map((id) => `--without ${id}`)].join(" ")}`;
|
|
11113
|
+
return [
|
|
11114
|
+
...lines2,
|
|
11115
|
+
`MCP servers ${mcp.join(" \xB7 ")} (defaults of ${r.target}, kept because your install record has them) \u2014 drop: ${command} (this re-runs the whole install: harness files refreshed, edited ones backed up; --without stays on record, so add --with <id> to get one back later)`
|
|
11116
|
+
];
|
|
11117
|
+
}
|
|
11118
|
+
|
|
11068
11119
|
// src/uninstall-interactive.ts
|
|
11069
11120
|
init_esm_shims();
|
|
11070
11121
|
|
|
@@ -12109,7 +12160,7 @@ function buildRouterChoices(_state, record) {
|
|
|
12109
12160
|
{
|
|
12110
12161
|
value: "uninstall",
|
|
12111
12162
|
label: "Uninstall",
|
|
12112
|
-
hint: "remove everything, one CLI, or single assets",
|
|
12163
|
+
hint: "remove everything, one CLI, one track, or single assets",
|
|
12113
12164
|
enabled: true
|
|
12114
12165
|
},
|
|
12115
12166
|
{
|
|
@@ -12472,7 +12523,7 @@ function hintFor(asset) {
|
|
|
12472
12523
|
return 'no automatic reverse path \u2014 only "Remove everything" clears it';
|
|
12473
12524
|
}
|
|
12474
12525
|
}
|
|
12475
|
-
function buildUninstallModeChoices(clis, assetCount) {
|
|
12526
|
+
function buildUninstallModeChoices(clis, assetCount, tracks) {
|
|
12476
12527
|
return [
|
|
12477
12528
|
{
|
|
12478
12529
|
value: "cli",
|
|
@@ -12480,6 +12531,7 @@ function buildUninstallModeChoices(clis, assetCount) {
|
|
|
12480
12531
|
hint: clis.length > 1 ? "that CLI's own files go; files shared with a remaining CLI stay; your text stays" : 'only one CLI here \u2014 use "Remove everything"',
|
|
12481
12532
|
enabled: clis.length > 1
|
|
12482
12533
|
},
|
|
12534
|
+
...tracks === void 0 ? [] : [trackMode(tracks.length > 1)],
|
|
12483
12535
|
{
|
|
12484
12536
|
value: "selected",
|
|
12485
12537
|
label: "Remove selected assets",
|
|
@@ -12494,6 +12546,12 @@ function buildUninstallModeChoices(clis, assetCount) {
|
|
|
12494
12546
|
}
|
|
12495
12547
|
];
|
|
12496
12548
|
}
|
|
12549
|
+
var trackMode = (many) => ({
|
|
12550
|
+
value: "track",
|
|
12551
|
+
label: "Remove one track",
|
|
12552
|
+
hint: many ? "only that track's own files go" : 'only one track here \u2014 use "Remove everything"',
|
|
12553
|
+
enabled: many
|
|
12554
|
+
});
|
|
12497
12555
|
var CLI_FOLDERS = [".claude/", ".codex/", ".opencode/"];
|
|
12498
12556
|
var FOLDER_CLI = {
|
|
12499
12557
|
".claude/": "claude",
|
|
@@ -12574,7 +12632,7 @@ async function runInteractiveUninstall(projectDir, deps = {}) {
|
|
|
12574
12632
|
const rows2 = buildRemovableRows(log.assets);
|
|
12575
12633
|
const clis = installedClis(log);
|
|
12576
12634
|
const mode = await prompts.selectMode(
|
|
12577
|
-
buildUninstallModeChoices(clis, rows2.length),
|
|
12635
|
+
buildUninstallModeChoices(clis, rows2.length, log.spec.tracks.filter(isTrack)),
|
|
12578
12636
|
`uzys-agent-harness \xB7 uninstall installed: ${clis.join(", ") || "(none)"} \xB7 assets ${rows2.length}`
|
|
12579
12637
|
);
|
|
12580
12638
|
if (mode === null) {
|
|
@@ -12582,6 +12640,7 @@ async function runInteractiveUninstall(projectDir, deps = {}) {
|
|
|
12582
12640
|
return { ok: false, reason: "cancelled" };
|
|
12583
12641
|
}
|
|
12584
12642
|
if (mode === "cli") return pickCli(projectDir, clis, prompts);
|
|
12643
|
+
if (mode === "track") return pickTrack(projectDir, log, prompts);
|
|
12585
12644
|
if (mode === "all") {
|
|
12586
12645
|
const ok2 = await prompts.confirm(
|
|
12587
12646
|
[
|
|
@@ -12644,6 +12703,26 @@ async function pickCli(projectDir, clis, prompts) {
|
|
|
12644
12703
|
}
|
|
12645
12704
|
return { ok: true, options: { projectDir, cli } };
|
|
12646
12705
|
}
|
|
12706
|
+
async function pickTrack(projectDir, log, prompts) {
|
|
12707
|
+
const rows2 = log.spec.tracks.filter(isTrack).map((t) => ({ value: t, label: TRACK_LABELS[t], hint: t }));
|
|
12708
|
+
const track = await prompts.selectTrack(rows2);
|
|
12709
|
+
const left = rows2.map((r) => r.value).filter((t) => t !== track);
|
|
12710
|
+
const ok = track !== null && await prompts.confirm(
|
|
12711
|
+
[
|
|
12712
|
+
`Remove ${track} (remaining: ${left.join(", ")})? Same as: agent-harness uninstall --track ${track}`,
|
|
12713
|
+
...previewTrackRemoval(
|
|
12714
|
+
{ installLog: log, projectDir, harnessRoot: resolveBundleRoot() },
|
|
12715
|
+
track
|
|
12716
|
+
).map((l) => ` \xB7 ${l}`),
|
|
12717
|
+
` \xB7 file counts: agent-harness uninstall --track ${track} --dry-run`
|
|
12718
|
+
].join("\n")
|
|
12719
|
+
);
|
|
12720
|
+
if (!ok) {
|
|
12721
|
+
prompts.cancel("Cancelled.");
|
|
12722
|
+
return { ok: false, reason: "cancelled" };
|
|
12723
|
+
}
|
|
12724
|
+
return { ok: true, options: { projectDir, track } };
|
|
12725
|
+
}
|
|
12647
12726
|
function defaultUninstallPrompts() {
|
|
12648
12727
|
return {
|
|
12649
12728
|
intro: (m) => ge(m),
|
|
@@ -12668,6 +12747,13 @@ function defaultUninstallPrompts() {
|
|
|
12668
12747
|
});
|
|
12669
12748
|
return q(r) ? null : r;
|
|
12670
12749
|
},
|
|
12750
|
+
selectTrack: async (rows2) => {
|
|
12751
|
+
const r = await Ee({
|
|
12752
|
+
message: 'Which track? (the last remaining track cannot be removed this way \u2014 use "Remove everything")',
|
|
12753
|
+
options: rows2.map((x) => ({ value: x.value, label: x.label, hint: x.hint }))
|
|
12754
|
+
});
|
|
12755
|
+
return q(r) ? null : r;
|
|
12756
|
+
},
|
|
12671
12757
|
selectAssets: async (rows2) => {
|
|
12672
12758
|
if (rows2.length === 0) return [];
|
|
12673
12759
|
const r = await ve({
|
|
@@ -12740,6 +12826,13 @@ function uninstallAction(options, deps = {}) {
|
|
|
12740
12826
|
exit(1);
|
|
12741
12827
|
return;
|
|
12742
12828
|
}
|
|
12829
|
+
if (options.track !== void 0) {
|
|
12830
|
+
removeTrackAction(
|
|
12831
|
+
{ options, installLog, projectDir, harnessRoot },
|
|
12832
|
+
{ log, err, exit, rm, writeLog }
|
|
12833
|
+
);
|
|
12834
|
+
return;
|
|
12835
|
+
}
|
|
12743
12836
|
if (options.cli !== void 0) {
|
|
12744
12837
|
removeCliAction(
|
|
12745
12838
|
{ options, installLog, projectDir, harnessRoot },
|
|
@@ -13141,37 +13234,37 @@ var TEMPLATE_DIR_FIELD = {
|
|
|
13141
13234
|
codex: "codexDir",
|
|
13142
13235
|
opencode: "opencodeDir"
|
|
13143
13236
|
};
|
|
13144
|
-
function
|
|
13145
|
-
const
|
|
13146
|
-
|
|
13147
|
-
io.err(
|
|
13148
|
-
io.err(
|
|
13149
|
-
c.dim(
|
|
13150
|
-
" CLI \uD558\uB098 \uC81C\uAC70: uninstall --cli <name> / \uC790\uC0B0 \uD558\uB098 \uC81C\uAC70: uninstall --only <id>"
|
|
13151
|
-
)
|
|
13152
|
-
);
|
|
13237
|
+
function resolveScopeTarget(io, s) {
|
|
13238
|
+
const reject = (msg, hint) => {
|
|
13239
|
+
io.err(status.failure(c.red(`ERROR: ${msg}`)));
|
|
13240
|
+
io.err(c.dim(` ${hint}`));
|
|
13153
13241
|
io.exit(1);
|
|
13154
13242
|
return null;
|
|
13243
|
+
};
|
|
13244
|
+
if (s.conflict) {
|
|
13245
|
+
return reject(
|
|
13246
|
+
`--${s.flag} \uB294 --only \xB7 --cli \xB7 --track \uC911 \uD558\uB098\uB9CC \uC4F8 \uC218 \uC788\uB2E4`,
|
|
13247
|
+
"CLI \uD558\uB098: uninstall --cli <name> / \uD2B8\uB799 \uD558\uB098: uninstall --track <name> / \uC790\uC0B0: uninstall --only <id>"
|
|
13248
|
+
);
|
|
13155
13249
|
}
|
|
13156
|
-
const target = (
|
|
13157
|
-
if (!
|
|
13158
|
-
|
|
13159
|
-
|
|
13160
|
-
|
|
13161
|
-
|
|
13250
|
+
const target = String(s.value ?? "").trim();
|
|
13251
|
+
if (!s.vocab.includes(target)) {
|
|
13252
|
+
return reject(
|
|
13253
|
+
`Invalid --${s.flag} value: ${target || "(empty)"}`,
|
|
13254
|
+
`Must be one of: ${s.vocab.join(" | ")}`
|
|
13255
|
+
);
|
|
13162
13256
|
}
|
|
13163
|
-
|
|
13164
|
-
|
|
13165
|
-
|
|
13166
|
-
|
|
13167
|
-
|
|
13168
|
-
return null;
|
|
13257
|
+
if (!s.installed.includes(target)) {
|
|
13258
|
+
return reject(
|
|
13259
|
+
`${target} is not installed in this project`,
|
|
13260
|
+
`installed: ${s.installed.join(", ") || "(none)"}`
|
|
13261
|
+
);
|
|
13169
13262
|
}
|
|
13170
|
-
if (installed.length <= 1) {
|
|
13171
|
-
|
|
13172
|
-
|
|
13173
|
-
|
|
13174
|
-
|
|
13263
|
+
if (s.installed.length <= 1) {
|
|
13264
|
+
return reject(
|
|
13265
|
+
`${target} \uB294 \uC774 \uD504\uB85C\uC81D\uD2B8\uC758 \uB9C8\uC9C0\uB9C9 ${s.flag === "cli" ? "CLI \uB2E4" : "\uD2B8\uB799\uC774\uB2E4"}`,
|
|
13266
|
+
"\uC804\uB7C9 \uC81C\uAC70\uB294 `agent-harness uninstall` \uC744 \uC4F4\uB2E4 (\uC790\uC0B0\xB7\uC124\uCE58 \uAE30\uB85D\uAE4C\uC9C0 \uD568\uAED8)"
|
|
13267
|
+
);
|
|
13175
13268
|
}
|
|
13176
13269
|
return target;
|
|
13177
13270
|
}
|
|
@@ -13206,41 +13299,11 @@ function keptForRemainingLines(plan, preview) {
|
|
|
13206
13299
|
const what = `${plan.kept.join(" \xB7 ")} \u2014 still used by Claude Code through its .claude/skills/ link(s)`;
|
|
13207
13300
|
return [preview ? ` \u25CB leave ${what}` : ` ${c.yellow("\u2298")} left ${what}`];
|
|
13208
13301
|
}
|
|
13209
|
-
function removeCliHeader(target, installed, remaining) {
|
|
13210
|
-
return [
|
|
13211
|
-
"",
|
|
13212
|
-
c.bold(`uzys-agent-harness \xB7 uninstall --cli ${target}`),
|
|
13213
|
-
"",
|
|
13214
|
-
c.dim(` installed: ${installed.join(", ")}`),
|
|
13215
|
-
c.dim(` removing: ${target}`),
|
|
13216
|
-
c.dim(` remaining: ${remaining.join(", ")}`),
|
|
13217
|
-
""
|
|
13218
|
-
];
|
|
13219
|
-
}
|
|
13220
|
-
function removeCliDryRunLines(plan, ctx, remaining) {
|
|
13221
|
-
const { installLog, projectDir, harnessRoot } = ctx;
|
|
13222
|
-
const lines2 = [c.yellow("[DRY RUN] CLI \uC81C\uAC70 \uBBF8\uB9AC\uBCF4\uAE30 (\uC2E4\uC81C \uBCC0\uACBD \uC5C6\uC74C):"), ""];
|
|
13223
|
-
const preview = removeHarnessFiles(installLog, projectDir, plan.scope, {
|
|
13224
|
-
rm: () => {
|
|
13225
|
-
},
|
|
13226
|
-
write: false,
|
|
13227
|
-
harnessRoot
|
|
13228
|
-
});
|
|
13229
|
-
lines2.push(...harnessRemovalLines(preview, installLog, projectDir, plan.scope, true));
|
|
13230
|
-
lines2.push(...keptForRemainingLines(plan, true));
|
|
13231
|
-
lines2.push(...assetsLeftLines(installLog, projectDir, plan.scope));
|
|
13232
|
-
const rerender = agentsMdRerenderSet(plan.target, remaining, installLog, projectDir);
|
|
13233
|
-
if (rerender !== null) {
|
|
13234
|
-
lines2.push(` \u25CB would re-render AGENTS.md for ${displayNames(rerender)} (your sections kept)`);
|
|
13235
|
-
}
|
|
13236
|
-
lines2.push(` \u25CB install log updated (clis: ${remaining.join(", ")})`, "");
|
|
13237
|
-
return lines2;
|
|
13238
|
-
}
|
|
13239
13302
|
function displayNames(clis) {
|
|
13240
13303
|
return clis.map((c3) => CLI_DISPLAY_NAMES[c3]).join(" \xB7 ");
|
|
13241
13304
|
}
|
|
13242
13305
|
function agentsMdRerenderSet(target, remaining, log, projectDir) {
|
|
13243
|
-
if (!isAgentsMdCli(target)) return null;
|
|
13306
|
+
if (target !== null && !isAgentsMdCli(target)) return null;
|
|
13244
13307
|
const set = agentsMdClis(remaining);
|
|
13245
13308
|
if (set.length === 0) return null;
|
|
13246
13309
|
const recorded2 = (log.externalFiles ?? []).some((f) => f.path === AGENTS_MD2) || (log.portions ?? []).some((p2) => p2.path === AGENTS_MD2);
|
|
@@ -13257,17 +13320,18 @@ function readTextOrEmpty(abs) {
|
|
|
13257
13320
|
return "";
|
|
13258
13321
|
}
|
|
13259
13322
|
}
|
|
13260
|
-
function rerenderAgentsMd(ctx, set) {
|
|
13261
|
-
const {
|
|
13323
|
+
function rerenderAgentsMd(ctx, set, log) {
|
|
13324
|
+
const { projectDir, harnessRoot } = ctx;
|
|
13262
13325
|
return runAgentsMdTransform({
|
|
13263
13326
|
harnessRoot,
|
|
13264
13327
|
projectDir,
|
|
13265
13328
|
clis: agentsMdClis(set),
|
|
13266
|
-
...recordedRenderInputs(projectDir,
|
|
13267
|
-
baseline: new Map((
|
|
13329
|
+
...recordedRenderInputs(projectDir, log, log.spec.tracks.filter(isTrack)),
|
|
13330
|
+
baseline: new Map((log.externalFiles ?? []).map((f) => [f.path, f.sha256])),
|
|
13268
13331
|
refreshOnly: true,
|
|
13269
13332
|
installedByRecord: true,
|
|
13270
|
-
shared: { portions:
|
|
13333
|
+
shared: { portions: log.portions ?? [], excluded: [...excludedIds(log)] },
|
|
13334
|
+
log
|
|
13271
13335
|
});
|
|
13272
13336
|
}
|
|
13273
13337
|
function withRerender(next, r) {
|
|
@@ -13316,66 +13380,197 @@ function settleCliLog(ctx, target, remaining, done, anchorRemoved) {
|
|
|
13316
13380
|
delete next.policyFiles;
|
|
13317
13381
|
delete next.skillFiles;
|
|
13318
13382
|
}
|
|
13319
|
-
|
|
13320
|
-
|
|
13321
|
-
|
|
13322
|
-
|
|
13323
|
-
|
|
13324
|
-
|
|
13383
|
+
return forgetRemoved(next, done);
|
|
13384
|
+
}
|
|
13385
|
+
function forgetRemoved(next, done) {
|
|
13386
|
+
const out = { ...next };
|
|
13387
|
+
const kept = (prefix) => (f) => !done.forgotten.has(`${prefix}${f.path}`);
|
|
13388
|
+
if (out.policyFiles !== void 0) out.policyFiles = out.policyFiles.filter(kept(CLAUDE_DIR3));
|
|
13389
|
+
if (out.skillFiles !== void 0) out.skillFiles = out.skillFiles.filter(kept(SKILLS_DIR3));
|
|
13390
|
+
const survivors = (out.externalFiles ?? []).filter(kept(""));
|
|
13391
|
+
if (survivors.length > 0) out.externalFiles = survivors;
|
|
13392
|
+
else delete out.externalFiles;
|
|
13393
|
+
if (out.rootFiles !== void 0) out.rootFiles = withoutRestored(out.rootFiles, done.displaced);
|
|
13325
13394
|
const touched = new Set(done.shared.map((r) => r.path));
|
|
13326
13395
|
const portions = [
|
|
13327
|
-
...(
|
|
13396
|
+
...(out.portions ?? []).filter((p2) => !touched.has(p2.path)),
|
|
13328
13397
|
...done.shared.flatMap((r) => r.portions)
|
|
13329
13398
|
];
|
|
13330
|
-
if (portions.length > 0)
|
|
13331
|
-
else delete
|
|
13332
|
-
return
|
|
13399
|
+
if (portions.length > 0) out.portions = portions;
|
|
13400
|
+
else delete out.portions;
|
|
13401
|
+
return out;
|
|
13333
13402
|
}
|
|
13334
|
-
function
|
|
13335
|
-
const target = resolveCliTarget(ctx, io);
|
|
13336
|
-
if (target === null) return;
|
|
13403
|
+
function runScopedRemoval(ctx, io, plan) {
|
|
13337
13404
|
const { options, installLog, projectDir, harnessRoot } = ctx;
|
|
13338
|
-
const
|
|
13339
|
-
const remaining =
|
|
13340
|
-
const
|
|
13341
|
-
|
|
13342
|
-
|
|
13343
|
-
|
|
13344
|
-
|
|
13345
|
-
|
|
13346
|
-
}
|
|
13405
|
+
const preview = options.dryRun === true;
|
|
13406
|
+
const remaining = plan.remaining.join(", ");
|
|
13407
|
+
const recordNote = `${plan.flag === "cli" ? "clis" : "tracks"}: ${remaining}`;
|
|
13408
|
+
const header = [
|
|
13409
|
+
"",
|
|
13410
|
+
c.bold(`uzys-agent-harness \xB7 uninstall --${plan.flag} ${plan.target}`),
|
|
13411
|
+
"",
|
|
13412
|
+
c.dim(` installed: ${plan.installed.join(", ")}`),
|
|
13413
|
+
c.dim(` removing: ${plan.target}`),
|
|
13414
|
+
c.dim(` remaining: ${remaining}`),
|
|
13415
|
+
""
|
|
13416
|
+
];
|
|
13417
|
+
for (const line of header) io.log(line);
|
|
13347
13418
|
const done = removeHarnessFiles(installLog, projectDir, plan.scope, {
|
|
13348
|
-
rm:
|
|
13349
|
-
|
|
13419
|
+
rm: preview ? () => {
|
|
13420
|
+
} : io.rm,
|
|
13421
|
+
write: !preview,
|
|
13350
13422
|
harnessRoot
|
|
13351
13423
|
});
|
|
13352
|
-
|
|
13353
|
-
|
|
13354
|
-
|
|
13355
|
-
|
|
13356
|
-
|
|
13357
|
-
|
|
13358
|
-
|
|
13359
|
-
|
|
13360
|
-
|
|
13424
|
+
const what = plan.flag === "cli" ? "CLI" : "\uD2B8\uB799";
|
|
13425
|
+
const lines2 = [
|
|
13426
|
+
...preview ? [c.yellow(`[DRY RUN] ${what} \uC81C\uAC70 \uBBF8\uB9AC\uBCF4\uAE30 (\uC2E4\uC81C \uBCC0\uACBD \uC5C6\uC74C):`), ""] : [],
|
|
13427
|
+
...harnessRemovalLines(done, installLog, projectDir, plan.scope, preview),
|
|
13428
|
+
...plan.leftLines(preview, done)
|
|
13429
|
+
];
|
|
13430
|
+
if (preview) {
|
|
13431
|
+
if (plan.rerenderSet !== null) {
|
|
13432
|
+
lines2.push(
|
|
13433
|
+
` \u25CB would re-render AGENTS.md for ${displayNames(plan.rerenderSet)} (your sections kept)`
|
|
13434
|
+
);
|
|
13435
|
+
}
|
|
13436
|
+
for (const line of [...lines2, ` \u25CB install log updated (${recordNote})`, ""]) io.log(line);
|
|
13437
|
+
io.exit(0);
|
|
13438
|
+
return;
|
|
13439
|
+
}
|
|
13440
|
+
for (const line of lines2) io.log(line);
|
|
13441
|
+
const settled = plan.settle(done);
|
|
13442
|
+
const agentsBefore = readTextOrEmpty(join35(projectDir, AGENTS_MD2));
|
|
13443
|
+
const rerender = plan.rerenderSet === null ? null : rerenderAgentsMd(ctx, plan.rerenderSet, settled);
|
|
13444
|
+
const rewrote = rerender !== null && readTextOrEmpty(join35(projectDir, AGENTS_MD2)) !== agentsBefore;
|
|
13445
|
+
if (rewrote) for (const line of rerenderLines(rerender)) io.log(line);
|
|
13361
13446
|
const next = rerender === null ? settled : withRerender(settled, rerender);
|
|
13362
13447
|
try {
|
|
13363
13448
|
io.writeLog(projectDir, next);
|
|
13364
|
-
io.log(` ${status.success(`install log updated (
|
|
13449
|
+
io.log(` ${status.success(`install log updated (${recordNote})`)}`);
|
|
13365
13450
|
} catch (e2) {
|
|
13366
13451
|
io.err(status.failure(c.red(`ERROR: install log \uAC31\uC2E0 \uC2E4\uD328 \u2014 ${installLogPath(projectDir)}`)));
|
|
13367
13452
|
io.err(c.dim(` ${e2 instanceof Error ? e2.message : String(e2)}`));
|
|
13368
13453
|
io.log("");
|
|
13369
13454
|
io.log(
|
|
13370
|
-
c.yellow(
|
|
13455
|
+
c.yellow(
|
|
13456
|
+
`${plan.target} \uB294 \uC81C\uAC70\uB410\uC73C\uB098 install log \uB97C \uAC31\uC2E0\uD558\uC9C0 \uBABB\uD588\uB2E4 (\uAE30\uB85D\uC774 \uC2E4\uC81C\uC640 \uB2E4\uB974\uB2E4)`
|
|
13457
|
+
)
|
|
13371
13458
|
);
|
|
13372
13459
|
io.exit(1);
|
|
13373
13460
|
return;
|
|
13374
13461
|
}
|
|
13375
13462
|
io.log("");
|
|
13376
|
-
io.log(status.success(c.green(`${target} removed (remaining: ${remaining
|
|
13463
|
+
io.log(status.success(c.green(`${plan.target} removed (remaining: ${remaining})`)));
|
|
13377
13464
|
io.exit(0);
|
|
13378
13465
|
}
|
|
13466
|
+
function removeCliAction(ctx, io) {
|
|
13467
|
+
const { options, installLog, projectDir } = ctx;
|
|
13468
|
+
const installed = installedClis(installLog);
|
|
13469
|
+
const target = resolveScopeTarget(io, {
|
|
13470
|
+
flag: "cli",
|
|
13471
|
+
value: options.cli,
|
|
13472
|
+
conflict: options.only !== void 0,
|
|
13473
|
+
vocab: CLI_BASES,
|
|
13474
|
+
installed
|
|
13475
|
+
});
|
|
13476
|
+
if (target === null) return;
|
|
13477
|
+
const remaining = installed.filter((cli) => cli !== target);
|
|
13478
|
+
const plan = planCliRemoval(target, remaining, installLog, projectDir);
|
|
13479
|
+
runScopedRemoval(ctx, io, {
|
|
13480
|
+
flag: "cli",
|
|
13481
|
+
target,
|
|
13482
|
+
installed,
|
|
13483
|
+
remaining,
|
|
13484
|
+
scope: plan.scope,
|
|
13485
|
+
leftLines: (preview) => [
|
|
13486
|
+
...keptForRemainingLines(plan, preview),
|
|
13487
|
+
...assetsLeftLines(installLog, projectDir, plan.scope)
|
|
13488
|
+
],
|
|
13489
|
+
rerenderSet: agentsMdRerenderSet(target, remaining, installLog, projectDir),
|
|
13490
|
+
settle: (done) => settleCliLog(ctx, target, remaining, done, done.anchor.removed)
|
|
13491
|
+
});
|
|
13492
|
+
}
|
|
13493
|
+
function removeTrackAction(ctx, io) {
|
|
13494
|
+
const { options, installLog, projectDir } = ctx;
|
|
13495
|
+
const target = resolveScopeTarget(io, {
|
|
13496
|
+
flag: "track",
|
|
13497
|
+
value: options.track,
|
|
13498
|
+
conflict: options.only !== void 0 || options.cli !== void 0,
|
|
13499
|
+
vocab: TRACKS,
|
|
13500
|
+
installed: installLog.spec.tracks.filter(isTrack)
|
|
13501
|
+
});
|
|
13502
|
+
if (target === null) return;
|
|
13503
|
+
const removal = trackRemoval(installLog, target);
|
|
13504
|
+
runScopedRemoval(ctx, io, {
|
|
13505
|
+
flag: "track",
|
|
13506
|
+
target,
|
|
13507
|
+
installed: removal.known,
|
|
13508
|
+
remaining: removal.remaining,
|
|
13509
|
+
scope: { ...TRACK_SCOPE, has: removal.has },
|
|
13510
|
+
leftLines: (preview, done) => preview ? trackNotes(ctx, removal, done, false).map((l) => ` \u25CB ${l}`) : trackNotes(ctx, removal, done, true),
|
|
13511
|
+
rerenderSet: agentsMdRerenderSet(null, installedClis(installLog), installLog, projectDir),
|
|
13512
|
+
settle: (done) => settleTrackLog(ctx, target, removal.remaining, done)
|
|
13513
|
+
});
|
|
13514
|
+
}
|
|
13515
|
+
var TRACK_SCOPE = { shared: [], skeleton: [], folders: [], importBlock: false, anchor: false };
|
|
13516
|
+
function trackNotes(ctx, r, d2, write) {
|
|
13517
|
+
const md = join35(ctx.projectDir, "CLAUDE.md");
|
|
13518
|
+
const block = rootBlockNext(ctx, new Set(d2.removed));
|
|
13519
|
+
if (write && block !== null) {
|
|
13520
|
+
backupIfLossyUtf8(md);
|
|
13521
|
+
writeFileSync9(md, block);
|
|
13522
|
+
}
|
|
13523
|
+
const left = trackLeftLines(ctx.installLog, r, ctx.harnessRoot);
|
|
13524
|
+
if (write) {
|
|
13525
|
+
const refreshed = block === null ? [] : ["CLAUDE.md \u2014 harness block refreshed (\uBCF8\uBB38 \uBCF4\uC874)"];
|
|
13526
|
+
return [
|
|
13527
|
+
...refreshed.map((l) => ` ${status.success(l)}`),
|
|
13528
|
+
...left.map((l) => ` \u2298 left ${l}`)
|
|
13529
|
+
];
|
|
13530
|
+
}
|
|
13531
|
+
const would = block === null ? [] : ["would refresh the harness block in CLAUDE.md (\uBCF8\uBB38 \uBCF4\uC874)"];
|
|
13532
|
+
return [trackRemovalNames(d2.removed, r.target), ...would, ...left.map((l) => `leave ${l}`)];
|
|
13533
|
+
}
|
|
13534
|
+
function previewTrackRemoval(ctx, target) {
|
|
13535
|
+
const r = trackRemoval(ctx.installLog, target);
|
|
13536
|
+
const io = { rm: () => {
|
|
13537
|
+
}, write: false, harnessRoot: ctx.harnessRoot };
|
|
13538
|
+
const scope = { ...TRACK_SCOPE, has: r.has };
|
|
13539
|
+
const done = removeHarnessFiles(ctx.installLog, ctx.projectDir, scope, io);
|
|
13540
|
+
const counts = harnessRemovalLines(done, ctx.installLog, ctx.projectDir, scope, true);
|
|
13541
|
+
return [...counts.map((l) => l.replace(/^\s*○ /, "")), ...trackNotes(ctx, r, done, false)];
|
|
13542
|
+
}
|
|
13543
|
+
function rootBlockNext({ installLog, projectDir }, gone) {
|
|
13544
|
+
const anchor = installLog.templates?.rootClaudeMd?.path;
|
|
13545
|
+
const md = join35(projectDir, "CLAUDE.md");
|
|
13546
|
+
const text = safeExists(md) && outsideProjectTarget(projectDir, md) === null ? readTextOrEmpty(md) : "";
|
|
13547
|
+
if (anchor === void 0 || !safeExists(join35(projectDir, anchor))) return null;
|
|
13548
|
+
if (stripHarnessImport(text) === null) return null;
|
|
13549
|
+
const stays = (id) => listFilesRecursive(join35(projectDir, SKILLS_DIR3, id)).some(
|
|
13550
|
+
(f) => !gone.has(`${SKILLS_DIR3}${id}/${f}`)
|
|
13551
|
+
);
|
|
13552
|
+
const continuousSkills = CONTINUOUS_SKILLS.map((s) => s.id).filter(stays);
|
|
13553
|
+
const next = upsertHarnessImport(text, { projectName: "", tracks: [], continuousSkills });
|
|
13554
|
+
return next === text ? null : next;
|
|
13555
|
+
}
|
|
13556
|
+
function settleTrackLog(ctx, target, remaining, done) {
|
|
13557
|
+
const { installLog, projectDir } = ctx;
|
|
13558
|
+
const tracks = installLog.spec.tracks.filter((t) => t !== target);
|
|
13559
|
+
const next = forgetRemoved({ ...installLog, spec: { ...installLog.spec, tracks } }, done);
|
|
13560
|
+
const abs = join35(projectDir, INSTALLED_TRACKS);
|
|
13561
|
+
const key = INSTALLED_TRACKS.slice(CLAUDE_DIR3.length);
|
|
13562
|
+
const prior = installLog.policyFiles?.find((f) => f.path === key)?.sha256;
|
|
13563
|
+
const edited = prior !== void 0 && safeExists(abs) && hashContent(readTextOrEmpty(abs)) !== prior;
|
|
13564
|
+
if (!installedClis(installLog).includes("claude") || edited) return next;
|
|
13565
|
+
if (outsideProjectTarget(projectDir, abs) !== null) return next;
|
|
13566
|
+
const content = installedTracksText(remaining);
|
|
13567
|
+
writeFileSync9(abs, content);
|
|
13568
|
+
if (next.policyFiles !== void 0) {
|
|
13569
|
+
const sha256 = hashContent(content);
|
|
13570
|
+
next.policyFiles = [...next.policyFiles.filter((f) => f.path !== key), { path: key, sha256 }];
|
|
13571
|
+
}
|
|
13572
|
+
return next;
|
|
13573
|
+
}
|
|
13379
13574
|
var UNINSTALL_SHARED = Object.keys(SHARED_FILES).filter(
|
|
13380
13575
|
(p2) => p2 !== "CLAUDE.md"
|
|
13381
13576
|
);
|
|
@@ -14367,6 +14562,9 @@ function registerUninstallCommand(cli) {
|
|
|
14367
14562
|
).option(
|
|
14368
14563
|
"--cli <name>",
|
|
14369
14564
|
`[Scope] Remove one CLI only (${CLI_BASES.join(" | ")}). Shared files stay until the last user leaves`
|
|
14565
|
+
).option(
|
|
14566
|
+
"--track <name>",
|
|
14567
|
+
`[Scope] Remove one track only (${TRACKS.join(" | ")}). Files another recorded track also installs stay`
|
|
14370
14568
|
).option("--yes", "[Mode] Skip the interactive picker and remove everything (non-interactive)").action(async (options) => {
|
|
14371
14569
|
await dispatchUninstall(options);
|
|
14372
14570
|
});
|
|
@@ -14375,7 +14573,7 @@ function shouldRunInteractive(options, isTty) {
|
|
|
14375
14573
|
if (!isTty) return false;
|
|
14376
14574
|
if (options.yes || options.dryRun) return false;
|
|
14377
14575
|
if (options.keepTemplates) return false;
|
|
14378
|
-
return options.only === void 0 && options.cli === void 0;
|
|
14576
|
+
return options.only === void 0 && options.cli === void 0 && options.track === void 0;
|
|
14379
14577
|
}
|
|
14380
14578
|
async function dispatchUninstall(options) {
|
|
14381
14579
|
const projectDir = resolve4(options.projectDir ?? process.cwd());
|
|
@@ -14388,7 +14586,7 @@ async function dispatchUninstall(options) {
|
|
|
14388
14586
|
console.error(
|
|
14389
14587
|
status.failure(
|
|
14390
14588
|
c.red(
|
|
14391
|
-
"ERROR: no terminal and no flag saying what to remove \u2014 nothing was done. Pass --yes (remove everything), --only <ids>, --cli <name>, or --dry-run."
|
|
14589
|
+
"ERROR: no terminal and no flag saying what to remove \u2014 nothing was done. Pass --yes (remove everything), --only <ids>, --cli <name>, --track <name>, or --dry-run."
|
|
14392
14590
|
)
|
|
14393
14591
|
)
|
|
14394
14592
|
);
|
|
@@ -14401,7 +14599,7 @@ async function dispatchUninstall(options) {
|
|
|
14401
14599
|
await runUninstallScreen(projectDir);
|
|
14402
14600
|
}
|
|
14403
14601
|
function lacksRemovalIntent(options) {
|
|
14404
|
-
return options.yes === void 0 && options.dryRun === void 0 && options.only === void 0 && options.cli === void 0;
|
|
14602
|
+
return options.yes === void 0 && options.dryRun === void 0 && options.only === void 0 && options.cli === void 0 && options.track === void 0;
|
|
14405
14603
|
}
|
|
14406
14604
|
async function runUninstallScreen(projectDir, opts = {}) {
|
|
14407
14605
|
const picked = await runInteractiveUninstall(projectDir, opts);
|