@uzysjung/agent-harness 26.151.0 → 26.152.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ko.md +2 -2
- package/README.md +2 -2
- package/dist/{chunk-3QBHZUVB.js → chunk-EQDC2AAU.js} +30 -23
- package/dist/chunk-EQDC2AAU.js.map +1 -0
- package/dist/index.js +133 -263
- package/dist/index.js.map +1 -1
- package/dist/trust-tier-drift.js +1 -1
- package/package.json +1 -1
- package/templates/rules/change-management.md +2 -3
- package/templates/rules/cli-development.md +2 -4
- package/templates/rules/doc-governance.md +1 -1
- package/templates/skills/audit-service-gaps/SKILL.md +0 -2
- package/templates/skills/model-orchestration/SKILL.md +6 -24
- package/templates/track-mcp-map.tsv +1 -1
- package/dist/chunk-3QBHZUVB.js.map +0 -1
- package/templates/agents/build-error-resolver.md +0 -111
- package/templates/agents/plan-checker.md +0 -116
- package/templates/agents/silent-failure-hunter.md +0 -50
- package/templates/skills/agent-introspection-debugging/SKILL.md +0 -153
- package/templates/skills/deep-research/SKILL.md +0 -179
- package/templates/skills/deep-research/agents/openai.yaml +0 -7
- package/templates/skills/eval-harness/SKILL.md +0 -306
- package/templates/skills/eval-harness/agents/openai.yaml +0 -7
- package/templates/skills/verification-loop/SKILL.md +0 -250
- package/templates/skills/verification-loop/agents/openai.yaml +0 -7
- package/templates/skills/verification-loop/references/tracks.md +0 -49
package/dist/index.js
CHANGED
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
INTERNAL_BUNDLED_SKILL_IDS,
|
|
15
15
|
POLICY_DIRS,
|
|
16
16
|
RENAMED_SKILL_IDS,
|
|
17
|
+
RETIRED_AGENTS,
|
|
17
18
|
RETIRED_AGENT_IDS,
|
|
18
19
|
RETIRED_SKILL_IDS,
|
|
19
20
|
TRACKS,
|
|
@@ -62,7 +63,7 @@ import {
|
|
|
62
63
|
upsertHarnessImport,
|
|
63
64
|
withContinuousSkillsNote,
|
|
64
65
|
writeInstallLog
|
|
65
|
-
} from "./chunk-
|
|
66
|
+
} from "./chunk-EQDC2AAU.js";
|
|
66
67
|
|
|
67
68
|
// node_modules/sisteransi/src/index.js
|
|
68
69
|
var require_src = __commonJS({
|
|
@@ -747,7 +748,7 @@ var cac = (name = "") => new CAC(name);
|
|
|
747
748
|
// package.json
|
|
748
749
|
var package_default = {
|
|
749
750
|
name: "@uzysjung/agent-harness",
|
|
750
|
-
version: "26.
|
|
751
|
+
version: "26.152.0",
|
|
751
752
|
description: "Curate vetted AI-coding skills & plugins by your tech stack \u2014 install only what you need, across Claude Code, Codex, OpenCode & Antigravity",
|
|
752
753
|
type: "module",
|
|
753
754
|
publishConfig: {
|
|
@@ -986,14 +987,13 @@ function padDisplay(s, width) {
|
|
|
986
987
|
init_esm_shims();
|
|
987
988
|
import {
|
|
988
989
|
chmodSync as chmodSync3,
|
|
989
|
-
existsSync as
|
|
990
|
+
existsSync as existsSync13,
|
|
990
991
|
mkdirSync as mkdirSync4,
|
|
991
992
|
readdirSync as readdirSync4,
|
|
992
|
-
readFileSync as
|
|
993
|
-
unlinkSync as unlinkSync3,
|
|
993
|
+
readFileSync as readFileSync12,
|
|
994
994
|
writeFileSync as writeFileSync6
|
|
995
995
|
} from "fs";
|
|
996
|
-
import { basename as basename5, dirname as dirname4, join as
|
|
996
|
+
import { basename as basename5, dirname as dirname4, join as join12, resolve } from "path";
|
|
997
997
|
|
|
998
998
|
// src/ci-scaffold.ts
|
|
999
999
|
init_esm_shims();
|
|
@@ -2089,53 +2089,18 @@ function writeMcpJson(path, mcp) {
|
|
|
2089
2089
|
`);
|
|
2090
2090
|
}
|
|
2091
2091
|
|
|
2092
|
-
// src/superseded.ts
|
|
2093
|
-
init_esm_shims();
|
|
2094
|
-
import { existsSync as existsSync12, readFileSync as readFileSync11 } from "fs";
|
|
2095
|
-
import { join as join11 } from "path";
|
|
2096
|
-
function sameTracks(a, b2) {
|
|
2097
|
-
if (a.length !== b2.length) return false;
|
|
2098
|
-
const sa = [...a].sort();
|
|
2099
|
-
const sb = [...b2].sort();
|
|
2100
|
-
return sa.every((t, i) => t === sb[i]);
|
|
2101
|
-
}
|
|
2102
|
-
function policyKey(claudeRelative) {
|
|
2103
|
-
return POLICY_DIRS.some(
|
|
2104
|
-
({ dir, ext }) => claudeRelative.startsWith(`${dir}/`) && claudeRelative.endsWith(ext)
|
|
2105
|
-
) ? claudeRelative : null;
|
|
2106
|
-
}
|
|
2107
|
-
function findSuperseded(projectDir, spec, previous) {
|
|
2108
|
-
if (!previous) return [];
|
|
2109
|
-
if (!sameTracks(previous.spec.tracks, spec.tracks)) return [];
|
|
2110
|
-
const baseline = new Map((previous.policyFiles ?? []).map((f) => [f.path, f.sha256]));
|
|
2111
|
-
if (baseline.size === 0) return [];
|
|
2112
|
-
const out = [];
|
|
2113
|
-
for (const entry of buildManifest(spec)) {
|
|
2114
|
-
if (entry.applies(spec)) continue;
|
|
2115
|
-
const id = classifyBaselineTarget(entry.target)?.id;
|
|
2116
|
-
if (!id) continue;
|
|
2117
|
-
const key = policyKey(entry.target.slice(".claude/".length));
|
|
2118
|
-
if (!key) continue;
|
|
2119
|
-
const abs = join11(projectDir, entry.target);
|
|
2120
|
-
if (!existsSync12(abs)) continue;
|
|
2121
|
-
if (!isHarnessOwned(baseline, key, readFileSync11(abs, "utf-8"))) continue;
|
|
2122
|
-
out.push({ target: entry.target, id, source: entry.source });
|
|
2123
|
-
}
|
|
2124
|
-
return out;
|
|
2125
|
-
}
|
|
2126
|
-
|
|
2127
2092
|
// src/update-mode.ts
|
|
2128
2093
|
init_esm_shims();
|
|
2129
2094
|
import {
|
|
2130
2095
|
copyFileSync,
|
|
2131
|
-
existsSync as
|
|
2096
|
+
existsSync as existsSync12,
|
|
2132
2097
|
mkdirSync as mkdirSync3,
|
|
2133
2098
|
readdirSync as readdirSync3,
|
|
2134
|
-
readFileSync as
|
|
2099
|
+
readFileSync as readFileSync11,
|
|
2135
2100
|
unlinkSync as unlinkSync2,
|
|
2136
2101
|
writeFileSync as writeFileSync5
|
|
2137
2102
|
} from "fs";
|
|
2138
|
-
import { basename as basename4, dirname as dirname3, join as
|
|
2103
|
+
import { basename as basename4, dirname as dirname3, join as join11 } from "path";
|
|
2139
2104
|
var LEGACY_ANCHOR_FILE = ".claude/CLAUDE.md";
|
|
2140
2105
|
function buildUpdateSpec(projectDir, tracks) {
|
|
2141
2106
|
return {
|
|
@@ -2147,7 +2112,7 @@ function buildUpdateSpec(projectDir, tracks) {
|
|
|
2147
2112
|
};
|
|
2148
2113
|
}
|
|
2149
2114
|
function runUpdateMode(projectDir, templatesDir, harnessRoot, deps = {}) {
|
|
2150
|
-
const claudeDir =
|
|
2115
|
+
const claudeDir = join11(projectDir, ".claude");
|
|
2151
2116
|
const report = {
|
|
2152
2117
|
updated: {},
|
|
2153
2118
|
pruned: {},
|
|
@@ -2179,8 +2144,8 @@ function runUpdateMode(projectDir, templatesDir, harnessRoot, deps = {}) {
|
|
|
2179
2144
|
report.needsReinstall = fresh.needsReinstall;
|
|
2180
2145
|
const policyBase = policyBaseline(projectDir);
|
|
2181
2146
|
for (const { dir, ext } of POLICY_DIRS) {
|
|
2182
|
-
const target =
|
|
2183
|
-
const source =
|
|
2147
|
+
const target = join11(claudeDir, dir);
|
|
2148
|
+
const source = join11(templatesDir, dir);
|
|
2184
2149
|
const label = `.claude/${dir}`;
|
|
2185
2150
|
const ctx = { prefix: dir, baseline: policyBase };
|
|
2186
2151
|
const synced = updateDir(target, source, ext, ctx);
|
|
@@ -2190,8 +2155,8 @@ function runUpdateMode(projectDir, templatesDir, harnessRoot, deps = {}) {
|
|
|
2190
2155
|
}
|
|
2191
2156
|
refreshPolicyBaseline(projectDir, templatesDir);
|
|
2192
2157
|
const skillSync = syncSkills(
|
|
2193
|
-
|
|
2194
|
-
|
|
2158
|
+
join11(claudeDir, "skills"),
|
|
2159
|
+
join11(templatesDir, "skills"),
|
|
2195
2160
|
skillBaseline(projectDir),
|
|
2196
2161
|
/* @__PURE__ */ new Date(),
|
|
2197
2162
|
(relInSkills) => foreignOwnedTarget(projectDir, `.claude/skills/${relInSkills}`)
|
|
@@ -2201,11 +2166,11 @@ function runUpdateMode(projectDir, templatesDir, harnessRoot, deps = {}) {
|
|
|
2201
2166
|
report.skillsSkippedLinks = skillSync.skippedLinks;
|
|
2202
2167
|
refreshSkillBaseline(projectDir);
|
|
2203
2168
|
const installLog = readInstallLog(projectDir);
|
|
2204
|
-
if (
|
|
2169
|
+
if (existsSync12(claudeDir) && (installLog === null || installLog.spec.cli.includes("claude"))) {
|
|
2205
2170
|
syncHarnessAnchor(projectDir, templatesDir, report);
|
|
2206
2171
|
}
|
|
2207
|
-
const settingsPath =
|
|
2208
|
-
if (
|
|
2172
|
+
const settingsPath = join11(claudeDir, "settings.json");
|
|
2173
|
+
if (existsSync12(settingsPath)) {
|
|
2209
2174
|
report.staleHookRefs = cleanStaleHookRefs(settingsPath, claudeDir);
|
|
2210
2175
|
}
|
|
2211
2176
|
report.mcpAllowlistRetired = retireMcpAllowlist(projectDir);
|
|
@@ -2234,14 +2199,14 @@ function runUpdateMode(projectDir, templatesDir, harnessRoot, deps = {}) {
|
|
|
2234
2199
|
return report;
|
|
2235
2200
|
}
|
|
2236
2201
|
function staleSkillDirs(claudeDir) {
|
|
2237
|
-
const dir =
|
|
2238
|
-
if (!
|
|
2202
|
+
const dir = join11(claudeDir, "skills");
|
|
2203
|
+
if (!existsSync12(dir)) return [];
|
|
2239
2204
|
const stale = (id) => RENAMED_SKILL_IDS[id] !== void 0 || RETIRED_SKILL_IDS.includes(id);
|
|
2240
2205
|
return readdirSync3(dir, { withFileTypes: true }).filter((e2) => e2.isDirectory() && stale(e2.name)).map((e2) => e2.name);
|
|
2241
2206
|
}
|
|
2242
2207
|
function staleAgentFiles(claudeDir) {
|
|
2243
|
-
const dir =
|
|
2244
|
-
if (!
|
|
2208
|
+
const dir = join11(claudeDir, "agents");
|
|
2209
|
+
if (!existsSync12(dir)) return [];
|
|
2245
2210
|
return readdirSync3(dir, { withFileTypes: true }).filter((e2) => e2.isFile() && e2.name.endsWith(".md")).map((e2) => basename4(e2.name, ".md")).filter((id) => RETIRED_AGENT_IDS.includes(id));
|
|
2246
2211
|
}
|
|
2247
2212
|
function installNewAssets(projectDir, templatesDir) {
|
|
@@ -2256,15 +2221,15 @@ function installNewAssets(projectDir, templatesDir) {
|
|
|
2256
2221
|
for (const entry of trackOnlyFileAssets(installedTracks(projectDir))) {
|
|
2257
2222
|
if (entry.target === HARNESS_ANCHOR_FILE) continue;
|
|
2258
2223
|
if (isBaselineExcluded(entry.target, baselineExcluded)) continue;
|
|
2259
|
-
const target =
|
|
2224
|
+
const target = join11(projectDir, entry.target);
|
|
2260
2225
|
const foreign = foreignOwnedTarget(projectDir, entry.target);
|
|
2261
2226
|
if (foreign !== null) {
|
|
2262
2227
|
if (!foreignOwned.includes(foreign)) foreignOwned.push(foreign);
|
|
2263
2228
|
continue;
|
|
2264
2229
|
}
|
|
2265
|
-
if (
|
|
2266
|
-
const source =
|
|
2267
|
-
if (!
|
|
2230
|
+
if (existsSync12(target)) continue;
|
|
2231
|
+
const source = join11(templatesDir, entry.source);
|
|
2232
|
+
if (!existsSync12(source)) continue;
|
|
2268
2233
|
if (entry.target.startsWith(".claude/")) {
|
|
2269
2234
|
if (!claudeSelected) continue;
|
|
2270
2235
|
if (entry.target.startsWith(".claude/hooks/") || entry.target === ".claude/settings.json") {
|
|
@@ -2289,13 +2254,13 @@ function trackOnlyFileAssets(tracks) {
|
|
|
2289
2254
|
);
|
|
2290
2255
|
}
|
|
2291
2256
|
function syncHarnessAnchor(projectDir, templatesDir, report) {
|
|
2292
|
-
if (
|
|
2257
|
+
if (existsSync12(join11(projectDir, LEGACY_ANCHOR_FILE))) {
|
|
2293
2258
|
report.legacyAnchor = LEGACY_ANCHOR_FILE;
|
|
2294
2259
|
}
|
|
2295
|
-
const templateMd =
|
|
2296
|
-
if (!
|
|
2297
|
-
const anchor =
|
|
2298
|
-
const existed =
|
|
2260
|
+
const templateMd = join11(templatesDir, "CLAUDE.md");
|
|
2261
|
+
if (!existsSync12(templateMd)) return;
|
|
2262
|
+
const anchor = join11(projectDir, HARNESS_ANCHOR_FILE);
|
|
2263
|
+
const existed = existsSync12(anchor);
|
|
2299
2264
|
copyFileSync(templateMd, anchor);
|
|
2300
2265
|
if (existed) {
|
|
2301
2266
|
report.claudeMdUpdated = true;
|
|
@@ -2307,15 +2272,15 @@ function syncHarnessAnchor(projectDir, templatesDir, report) {
|
|
|
2307
2272
|
recordAnchorBaseline(projectDir, anchor);
|
|
2308
2273
|
}
|
|
2309
2274
|
function upsertRootImport(projectDir) {
|
|
2310
|
-
const target =
|
|
2311
|
-
const existing =
|
|
2275
|
+
const target = join11(projectDir, "CLAUDE.md");
|
|
2276
|
+
const existing = existsSync12(target) ? readFileSync11(target, "utf8") : null;
|
|
2312
2277
|
const next = upsertHarnessImport(existing, {
|
|
2313
2278
|
projectName: basename4(projectDir),
|
|
2314
2279
|
tracks: installedTracks(projectDir),
|
|
2315
2280
|
// ADR-085 — update 는 선택 목록을 인자로 받지 않는다. `syncSkills` 와 같은 판정을 쓴다:
|
|
2316
2281
|
// `.claude/skills/<id>` 가 있으면 그 스킬은 이 설치가 고른 것이다.
|
|
2317
2282
|
continuousSkills: CONTINUOUS_SKILLS.map((s) => s.id).filter(
|
|
2318
|
-
(id) =>
|
|
2283
|
+
(id) => existsSync12(join11(projectDir, ".claude/skills", id))
|
|
2319
2284
|
)
|
|
2320
2285
|
});
|
|
2321
2286
|
if (next === existing) return false;
|
|
@@ -2335,7 +2300,7 @@ function recordAnchorBaseline(projectDir, anchor) {
|
|
|
2335
2300
|
...log.templates,
|
|
2336
2301
|
rootClaudeMd: {
|
|
2337
2302
|
path: HARNESS_ANCHOR_FILE,
|
|
2338
|
-
sha256: hashContent(
|
|
2303
|
+
sha256: hashContent(readFileSync11(anchor, "utf8"))
|
|
2339
2304
|
}
|
|
2340
2305
|
}
|
|
2341
2306
|
};
|
|
@@ -2377,16 +2342,16 @@ function isHarnessOwned2(ctx, rel, current) {
|
|
|
2377
2342
|
return isHarnessOwned(ctx.baseline, `${ctx.prefix}/${rel}`, current);
|
|
2378
2343
|
}
|
|
2379
2344
|
function updateDir(target, source, ext, ctx, now = /* @__PURE__ */ new Date()) {
|
|
2380
|
-
if (!
|
|
2345
|
+
if (!existsSync12(target) || !existsSync12(source)) return { updated: 0, backedUp: [] };
|
|
2381
2346
|
let updated = 0;
|
|
2382
2347
|
const backedUp = [];
|
|
2383
2348
|
for (const file of readdirSync3(target)) {
|
|
2384
2349
|
if (!file.endsWith(ext)) continue;
|
|
2385
|
-
const targetFile =
|
|
2386
|
-
const sourceFile =
|
|
2387
|
-
if (!
|
|
2388
|
-
const next =
|
|
2389
|
-
const current =
|
|
2350
|
+
const targetFile = join11(target, file);
|
|
2351
|
+
const sourceFile = join11(source, file);
|
|
2352
|
+
if (!existsSync12(sourceFile)) continue;
|
|
2353
|
+
const next = readFileSync11(sourceFile, "utf8");
|
|
2354
|
+
const current = readFileSync11(targetFile, "utf8");
|
|
2390
2355
|
if (current === next) continue;
|
|
2391
2356
|
if (!isHarnessOwned2(ctx, file, current)) {
|
|
2392
2357
|
backupFile(targetFile, now);
|
|
@@ -2398,7 +2363,7 @@ function updateDir(target, source, ext, ctx, now = /* @__PURE__ */ new Date()) {
|
|
|
2398
2363
|
return { updated, backedUp };
|
|
2399
2364
|
}
|
|
2400
2365
|
function syncSkills(targetDir, sourceDir, baseline, now = /* @__PURE__ */ new Date(), foreignOf) {
|
|
2401
|
-
if (!
|
|
2366
|
+
if (!existsSync12(targetDir) || !existsSync12(sourceDir))
|
|
2402
2367
|
return { updated: 0, backedUp: [], skippedLinks: [], foreignOwned: [] };
|
|
2403
2368
|
let updated = 0;
|
|
2404
2369
|
const backedUp = [];
|
|
@@ -2406,27 +2371,27 @@ function syncSkills(targetDir, sourceDir, baseline, now = /* @__PURE__ */ new Da
|
|
|
2406
2371
|
const foreignOwned = [];
|
|
2407
2372
|
for (const skill of readdirSync3(sourceDir, { withFileTypes: true })) {
|
|
2408
2373
|
if (!skill.isDirectory()) continue;
|
|
2409
|
-
const targetSkill =
|
|
2374
|
+
const targetSkill = join11(targetDir, skill.name);
|
|
2410
2375
|
if (occupiedByNonDirectory(targetSkill)) {
|
|
2411
2376
|
skippedLinks.push(skill.name);
|
|
2412
2377
|
continue;
|
|
2413
2378
|
}
|
|
2414
|
-
if (!
|
|
2415
|
-
for (const rel of listFilesRecursive(
|
|
2416
|
-
const targetFile =
|
|
2379
|
+
if (!existsSync12(targetSkill)) continue;
|
|
2380
|
+
for (const rel of listFilesRecursive(join11(sourceDir, skill.name))) {
|
|
2381
|
+
const targetFile = join11(targetSkill, rel);
|
|
2417
2382
|
const foreign = foreignOf(`${skill.name}/${rel}`);
|
|
2418
2383
|
if (foreign !== null) {
|
|
2419
2384
|
if (!foreignOwned.includes(foreign)) foreignOwned.push(foreign);
|
|
2420
2385
|
continue;
|
|
2421
2386
|
}
|
|
2422
|
-
const next =
|
|
2423
|
-
if (!
|
|
2387
|
+
const next = readFileSync11(join11(sourceDir, skill.name, rel), "utf8");
|
|
2388
|
+
if (!existsSync12(targetFile)) {
|
|
2424
2389
|
mkdirSync3(dirname3(targetFile), { recursive: true });
|
|
2425
2390
|
writeFileSync5(targetFile, next);
|
|
2426
2391
|
updated++;
|
|
2427
2392
|
continue;
|
|
2428
2393
|
}
|
|
2429
|
-
const current =
|
|
2394
|
+
const current = readFileSync11(targetFile, "utf8");
|
|
2430
2395
|
if (current === next) continue;
|
|
2431
2396
|
const recorded = baseline.get(`${skill.name}/${rel}`);
|
|
2432
2397
|
if (recorded === void 0 || hashContent(current) !== recorded) {
|
|
@@ -2472,16 +2437,16 @@ function refreshSkillBaseline(projectDir) {
|
|
|
2472
2437
|
}
|
|
2473
2438
|
}
|
|
2474
2439
|
function pruneOrphans(target, source, ext, ctx, now = /* @__PURE__ */ new Date()) {
|
|
2475
|
-
if (!
|
|
2440
|
+
if (!existsSync12(target) || !existsSync12(source)) return [];
|
|
2476
2441
|
const removed = [];
|
|
2477
2442
|
for (const file of readdirSync3(target)) {
|
|
2478
2443
|
if (!file.endsWith(ext)) continue;
|
|
2479
|
-
const sourceFile =
|
|
2480
|
-
if (!
|
|
2481
|
-
const targetFile =
|
|
2444
|
+
const sourceFile = join11(source, file);
|
|
2445
|
+
if (!existsSync12(sourceFile)) {
|
|
2446
|
+
const targetFile = join11(target, file);
|
|
2482
2447
|
if (!ctx.baseline.has(`${ctx.prefix}/${file}`)) continue;
|
|
2483
2448
|
try {
|
|
2484
|
-
const current =
|
|
2449
|
+
const current = readFileSync11(targetFile, "utf8");
|
|
2485
2450
|
if (!isHarnessOwned2(ctx, file, current)) backupFile(targetFile, now);
|
|
2486
2451
|
unlinkSync2(targetFile);
|
|
2487
2452
|
removed.push(file);
|
|
@@ -2492,8 +2457,8 @@ function pruneOrphans(target, source, ext, ctx, now = /* @__PURE__ */ new Date()
|
|
|
2492
2457
|
return removed;
|
|
2493
2458
|
}
|
|
2494
2459
|
function retireMcpAllowlist(projectDir, now = /* @__PURE__ */ new Date()) {
|
|
2495
|
-
const target =
|
|
2496
|
-
if (!
|
|
2460
|
+
const target = join11(projectDir, ".mcp-allowlist");
|
|
2461
|
+
if (!existsSync12(target)) return null;
|
|
2497
2462
|
try {
|
|
2498
2463
|
const backup = backupFile(target, now);
|
|
2499
2464
|
unlinkSync2(target);
|
|
@@ -2505,7 +2470,7 @@ function retireMcpAllowlist(projectDir, now = /* @__PURE__ */ new Date()) {
|
|
|
2505
2470
|
function cleanStaleHookRefs(settingsPath, claudeDir) {
|
|
2506
2471
|
let settings;
|
|
2507
2472
|
try {
|
|
2508
|
-
settings = JSON.parse(
|
|
2473
|
+
settings = JSON.parse(readFileSync11(settingsPath, "utf8"));
|
|
2509
2474
|
} catch {
|
|
2510
2475
|
return [];
|
|
2511
2476
|
}
|
|
@@ -2532,7 +2497,7 @@ function cleanStaleHookRefs(settingsPath, claudeDir) {
|
|
|
2532
2497
|
function keepHookRef(hook, claudeDir, removed) {
|
|
2533
2498
|
const relPath = projectAnchoredRef(hook?.command ?? "", claudeDir);
|
|
2534
2499
|
if (relPath === void 0) return true;
|
|
2535
|
-
const exists =
|
|
2500
|
+
const exists = existsSync12(join11(claudeDir, relPath));
|
|
2536
2501
|
if (!exists && !removed.includes(relPath)) removed.push(relPath);
|
|
2537
2502
|
return exists;
|
|
2538
2503
|
}
|
|
@@ -2559,14 +2524,14 @@ function projectAnchoredRef(command, claudeDir) {
|
|
|
2559
2524
|
function runInstall(ctx) {
|
|
2560
2525
|
const { harnessRoot, projectDir, spec } = ctx;
|
|
2561
2526
|
const mode = ctx.mode ?? "fresh";
|
|
2562
|
-
const templatesDir =
|
|
2563
|
-
if (!
|
|
2527
|
+
const templatesDir = join12(harnessRoot, "templates");
|
|
2528
|
+
if (!existsSync13(templatesDir)) {
|
|
2564
2529
|
throw new Error(`Templates dir not found: ${templatesDir}`);
|
|
2565
2530
|
}
|
|
2566
|
-
const claudeDir =
|
|
2531
|
+
const claudeDir = join12(projectDir, ".claude");
|
|
2567
2532
|
const previousLog = readInstallLog(projectDir);
|
|
2568
2533
|
const claudeWasSelected = previousLog?.spec.cli.includes("claude") ?? false;
|
|
2569
|
-
if (mode === "update" && !
|
|
2534
|
+
if (mode === "update" && !existsSync13(claudeDir) && (previousLog === null || claudeWasSelected)) {
|
|
2570
2535
|
throw new Error(
|
|
2571
2536
|
claudeWasSelected ? `Update mode found a broken install at ${projectDir} \u2014 this project installed Claude Code assets but \`.claude/\` is gone. Reinstall instead: agent-harness install --track <name>` : `Update mode requires an existing install at ${projectDir}`
|
|
2572
2537
|
);
|
|
@@ -2578,7 +2543,6 @@ function runInstall(ctx) {
|
|
|
2578
2543
|
const manifestSpec = buildManifestSpec(spec);
|
|
2579
2544
|
const policyBase = new Map((previousLog?.policyFiles ?? []).map((f) => [f.path, f.sha256]));
|
|
2580
2545
|
const baselineExcluded = new Set(spec.baselineExclude ?? []);
|
|
2581
|
-
const supersededFound = findSuperseded(projectDir, manifestSpec, previousLog);
|
|
2582
2546
|
const base = spec.cli.includes("claude") ? installClaudeBaseline(manifestSpec, projectDir, templatesDir, policyBase, baselineExcluded) : (
|
|
2583
2547
|
// claude 미선택이어도 CLI 중립 자산은 깔린다. manifest 전체가 `.claude/` baseline 안에서만
|
|
2584
2548
|
// 돌던 탓에 이 자산들이 claude 설치에만 도달했는데, **배포 룰 본문이 이 스크립트들을
|
|
@@ -2634,10 +2598,7 @@ function runInstall(ctx) {
|
|
|
2634
2598
|
baselineForeignOwned: [
|
|
2635
2599
|
...base.foreignOwned,
|
|
2636
2600
|
...externalForeignOwned.filter((f) => !base.foreignOwned.includes(f))
|
|
2637
|
-
]
|
|
2638
|
-
// 밀려난 자산 정리는 **manifest 복사가 끝난 뒤**에 한다. 앞에서 지우면 이번 설치가 다시
|
|
2639
|
-
// 깔 수도 있는 파일을 지웠다 되돌리는 셈이라, 무엇이 최종 상태인지 보고가 흐려진다.
|
|
2640
|
-
superseded: applySupersededCleanup(spec, projectDir, supersededFound)
|
|
2601
|
+
]
|
|
2641
2602
|
};
|
|
2642
2603
|
ctx.onProgress?.({ type: "baseline-complete", baseline });
|
|
2643
2604
|
const external = runExternalPhase(ctx);
|
|
@@ -2653,26 +2614,11 @@ function runInstall(ctx) {
|
|
|
2653
2614
|
);
|
|
2654
2615
|
return { ...baseline, external, staleHookRefs };
|
|
2655
2616
|
}
|
|
2656
|
-
function applySupersededCleanup(spec, projectDir, found) {
|
|
2657
|
-
if (found.length === 0) return { removed: [], kept: [] };
|
|
2658
|
-
if (!spec.cleanSuperseded) return { removed: [], kept: found.map((f) => f.target) };
|
|
2659
|
-
const removed = [];
|
|
2660
|
-
const kept = [];
|
|
2661
|
-
for (const asset of found) {
|
|
2662
|
-
try {
|
|
2663
|
-
unlinkSync3(join13(projectDir, asset.target));
|
|
2664
|
-
removed.push(asset.target);
|
|
2665
|
-
} catch {
|
|
2666
|
-
kept.push(asset.target);
|
|
2667
|
-
}
|
|
2668
|
-
}
|
|
2669
|
-
return { removed, kept };
|
|
2670
|
-
}
|
|
2671
2617
|
function healStaleHookRefs(spec, projectDir) {
|
|
2672
2618
|
if (!spec.cli.includes("claude")) return [];
|
|
2673
|
-
const settingsPath =
|
|
2674
|
-
if (!
|
|
2675
|
-
return cleanStaleHookRefs(settingsPath,
|
|
2619
|
+
const settingsPath = join12(projectDir, ".claude/settings.json");
|
|
2620
|
+
if (!existsSync13(settingsPath)) return [];
|
|
2621
|
+
return cleanStaleHookRefs(settingsPath, join12(projectDir, ".claude"));
|
|
2676
2622
|
}
|
|
2677
2623
|
function resolveBackupPath(ctx, mode, claudeDir) {
|
|
2678
2624
|
const wantBackup = ctx.backup ?? (mode === "update" || mode === "reinstall");
|
|
@@ -2688,9 +2634,6 @@ function runUpdateInstall(ctx, templatesDir, backupPath) {
|
|
|
2688
2634
|
baselineExcluded: [],
|
|
2689
2635
|
baselineExcludedOnDisk: [],
|
|
2690
2636
|
baselineForeignOwned: [],
|
|
2691
|
-
// update 는 밀려난 자산을 다루지 않는다 — 그 판정은 사용자가 옵션을 고르는 설치 경로의
|
|
2692
|
-
// 것이고, update 는 무엇을 골랐는지 다시 묻지 않는다.
|
|
2693
|
-
superseded: { removed: [], kept: [] },
|
|
2694
2637
|
backup: backupPath,
|
|
2695
2638
|
installedTracks: [...ctx.spec.tracks].sort(),
|
|
2696
2639
|
mcpServers: [],
|
|
@@ -2741,12 +2684,12 @@ function installCliNeutralAssets(manifestSpec, projectDir, templatesDir, baselin
|
|
|
2741
2684
|
continue;
|
|
2742
2685
|
}
|
|
2743
2686
|
if (!isCliNeutralTarget(entry.target)) continue;
|
|
2744
|
-
const source =
|
|
2745
|
-
if (!
|
|
2687
|
+
const source = join12(templatesDir, entry.source);
|
|
2688
|
+
if (!existsSync13(source)) {
|
|
2746
2689
|
result.skipped += 1;
|
|
2747
2690
|
continue;
|
|
2748
2691
|
}
|
|
2749
|
-
copyFile(source,
|
|
2692
|
+
copyFile(source, join12(projectDir, entry.target));
|
|
2750
2693
|
result.filesCopied += 1;
|
|
2751
2694
|
}
|
|
2752
2695
|
return result;
|
|
@@ -2758,9 +2701,9 @@ function backupEditedPolicyFile(entryTarget, target, source, baseline) {
|
|
|
2758
2701
|
if (!POLICY_DIRS.some(({ dir, ext }) => rel.startsWith(`${dir}/`) && rel.endsWith(ext))) {
|
|
2759
2702
|
return null;
|
|
2760
2703
|
}
|
|
2761
|
-
if (!
|
|
2762
|
-
const current =
|
|
2763
|
-
if (current ===
|
|
2704
|
+
if (!existsSync13(target)) return null;
|
|
2705
|
+
const current = readFileSync12(target, "utf-8");
|
|
2706
|
+
if (current === readFileSync12(source, "utf-8")) return null;
|
|
2764
2707
|
const recorded = baseline.get(rel);
|
|
2765
2708
|
if (recorded !== void 0 && recorded === hashContent(current)) return null;
|
|
2766
2709
|
return backupFile(target);
|
|
@@ -2773,14 +2716,14 @@ function installClaudeBaseline(manifestSpec, projectDir, templatesDir, policyBas
|
|
|
2773
2716
|
if (!entry.applies(manifestSpec)) {
|
|
2774
2717
|
continue;
|
|
2775
2718
|
}
|
|
2776
|
-
const target =
|
|
2719
|
+
const target = join12(projectDir, entry.target);
|
|
2777
2720
|
if (isBaselineExcluded(entry.target, baselineExcluded)) {
|
|
2778
2721
|
result.excluded.push(entry.target);
|
|
2779
|
-
if (
|
|
2722
|
+
if (existsSync13(target)) result.excludedOnDisk.push(entry.target);
|
|
2780
2723
|
continue;
|
|
2781
2724
|
}
|
|
2782
|
-
const source =
|
|
2783
|
-
if (!
|
|
2725
|
+
const source = join12(templatesDir, entry.source);
|
|
2726
|
+
if (!existsSync13(source)) {
|
|
2784
2727
|
result.skipped += 1;
|
|
2785
2728
|
continue;
|
|
2786
2729
|
}
|
|
@@ -2791,7 +2734,7 @@ function installClaudeBaseline(manifestSpec, projectDir, templatesDir, policyBas
|
|
|
2791
2734
|
}
|
|
2792
2735
|
if (entry.type === "file") {
|
|
2793
2736
|
if (entry.target === ".claude/settings.json") {
|
|
2794
|
-
const backup = backupFileIfChanged(target,
|
|
2737
|
+
const backup = backupFileIfChanged(target, readFileSync12(source, "utf-8"));
|
|
2795
2738
|
if (backup) {
|
|
2796
2739
|
result.backups.push(backup);
|
|
2797
2740
|
}
|
|
@@ -2815,8 +2758,8 @@ function installClaudeBaseline(manifestSpec, projectDir, templatesDir, policyBas
|
|
|
2815
2758
|
}
|
|
2816
2759
|
accumulateCategory(result.categories, entry);
|
|
2817
2760
|
}
|
|
2818
|
-
const hookDir =
|
|
2819
|
-
if (
|
|
2761
|
+
const hookDir = join12(projectDir, ".claude/hooks");
|
|
2762
|
+
if (existsSync13(hookDir)) {
|
|
2820
2763
|
chmodHooksSync(hookDir);
|
|
2821
2764
|
}
|
|
2822
2765
|
writeInstalledTracks(projectDir, manifestSpec.tracks);
|
|
@@ -2830,9 +2773,9 @@ function installClaudeBaseline(manifestSpec, projectDir, templatesDir, policyBas
|
|
|
2830
2773
|
return result;
|
|
2831
2774
|
}
|
|
2832
2775
|
function harnessAnchorLog(projectDir) {
|
|
2833
|
-
const anchor =
|
|
2834
|
-
if (!
|
|
2835
|
-
return { path: HARNESS_ANCHOR_FILE, sha256: hashContent(
|
|
2776
|
+
const anchor = join12(projectDir, HARNESS_ANCHOR_FILE);
|
|
2777
|
+
if (!existsSync13(anchor)) return null;
|
|
2778
|
+
return { path: HARNESS_ANCHOR_FILE, sha256: hashContent(readFileSync12(anchor, "utf-8")) };
|
|
2836
2779
|
}
|
|
2837
2780
|
function writeEnvironmentFiles(projectDir, tracks) {
|
|
2838
2781
|
return {
|
|
@@ -2891,7 +2834,7 @@ function writeInstallLogSafe(ctx, externalFiles, external, rootClaudeMdLog, prev
|
|
|
2891
2834
|
rootFiles
|
|
2892
2835
|
);
|
|
2893
2836
|
const skillFiles = collectSkillHashes(ctx.projectDir);
|
|
2894
|
-
const policyFiles = collectPolicyHashes(ctx.projectDir,
|
|
2837
|
+
const policyFiles = collectPolicyHashes(ctx.projectDir, join12(ctx.harnessRoot, "templates"));
|
|
2895
2838
|
const merged = mergeExternalFiles(ctx.projectDir, previousLog?.externalFiles, externalFiles);
|
|
2896
2839
|
writeInstallLog(ctx.projectDir, {
|
|
2897
2840
|
...log,
|
|
@@ -2907,11 +2850,11 @@ function writeInstallLogSafe(ctx, externalFiles, external, rootClaudeMdLog, prev
|
|
|
2907
2850
|
}
|
|
2908
2851
|
}
|
|
2909
2852
|
function composeAndWriteMcp(harnessRoot, projectDir, spec) {
|
|
2910
|
-
const mcpPath =
|
|
2911
|
-
const created = !
|
|
2853
|
+
const mcpPath = join12(projectDir, ".mcp.json");
|
|
2854
|
+
const created = !existsSync13(mcpPath);
|
|
2912
2855
|
const composed = composeMcpJson({
|
|
2913
|
-
templateMcpPath:
|
|
2914
|
-
trackMapPath:
|
|
2856
|
+
templateMcpPath: join12(harnessRoot, "templates/mcp.json"),
|
|
2857
|
+
trackMapPath: join12(harnessRoot, "templates/track-mcp-map.tsv"),
|
|
2915
2858
|
existingPath: mcpPath,
|
|
2916
2859
|
tracks: spec.tracks
|
|
2917
2860
|
});
|
|
@@ -2964,15 +2907,15 @@ function accumulateCategory(cats, entry) {
|
|
|
2964
2907
|
}
|
|
2965
2908
|
}
|
|
2966
2909
|
function writeInstalledTracks(projectDir, tracks) {
|
|
2967
|
-
const path =
|
|
2910
|
+
const path = join12(projectDir, ".claude/.installed-tracks");
|
|
2968
2911
|
mkdirSync4(dirname4(path), { recursive: true });
|
|
2969
2912
|
const sorted = [...new Set(tracks)].sort().join("\n");
|
|
2970
2913
|
writeFileSync6(path, `${sorted}
|
|
2971
2914
|
`);
|
|
2972
2915
|
}
|
|
2973
2916
|
function writeRootClaudeMd(projectDir, tracks, continuousSkills) {
|
|
2974
|
-
const target =
|
|
2975
|
-
const existing =
|
|
2917
|
+
const target = join12(projectDir, "CLAUDE.md");
|
|
2918
|
+
const existing = existsSync13(target) ? readFileSync12(target, "utf-8") : null;
|
|
2976
2919
|
const content = upsertHarnessImport(existing, {
|
|
2977
2920
|
projectName: basename5(projectDir),
|
|
2978
2921
|
tracks,
|
|
@@ -3401,11 +3344,12 @@ function renderPhase1Rows(log, baseline, verbose = false, withEcc = false, claud
|
|
|
3401
3344
|
);
|
|
3402
3345
|
}
|
|
3403
3346
|
for (const id of baseline.updateMode.retiredAgents) {
|
|
3347
|
+
const instead = RETIRED_AGENTS.find((a) => a.id === id)?.instead;
|
|
3404
3348
|
log(
|
|
3405
3349
|
assetRow(
|
|
3406
3350
|
"skip",
|
|
3407
3351
|
"agents",
|
|
3408
|
-
`${id} \xB7 \uC774 \uB9B4\uB9AC\uC988\uC5D0\uC11C \uC740\uD1F4 \u2014 .claude/agents/${id}.md \uB97C \uC9C0\uC6CC\uB3C4 \uB41C\uB2E4
|
|
3352
|
+
`${id} \xB7 \uC774 \uB9B4\uB9AC\uC988\uC5D0\uC11C \uC740\uD1F4 \u2014 .claude/agents/${id}.md \uB97C \uC9C0\uC6CC\uB3C4 \uB41C\uB2E4` + (instead === void 0 ? "" : ` \xB7 ${instead}`)
|
|
3409
3353
|
)
|
|
3410
3354
|
);
|
|
3411
3355
|
}
|
|
@@ -3532,7 +3476,7 @@ function renderPhase1Rows(log, baseline, verbose = false, withEcc = false, claud
|
|
|
3532
3476
|
phase1Row(
|
|
3533
3477
|
"agents",
|
|
3534
3478
|
cats.agents.length,
|
|
3535
|
-
"
|
|
3479
|
+
"independent verifier \xB7 implementation lane \xB7 domain lanes (track)",
|
|
3536
3480
|
cats.agents
|
|
3537
3481
|
);
|
|
3538
3482
|
}
|
|
@@ -3546,7 +3490,7 @@ function renderPhase1Rows(log, baseline, verbose = false, withEcc = false, claud
|
|
|
3546
3490
|
phase1Row(
|
|
3547
3491
|
"skills",
|
|
3548
3492
|
cats.skills.length,
|
|
3549
|
-
"
|
|
3493
|
+
"bundled methodology skills (track \xB7 opt-in)",
|
|
3550
3494
|
cats.skills
|
|
3551
3495
|
);
|
|
3552
3496
|
}
|
|
@@ -3577,24 +3521,6 @@ function renderPhase1Rows(log, baseline, verbose = false, withEcc = false, claud
|
|
|
3577
3521
|
)
|
|
3578
3522
|
);
|
|
3579
3523
|
}
|
|
3580
|
-
if (baseline.superseded.removed.length > 0) {
|
|
3581
|
-
log(
|
|
3582
|
-
assetRow(
|
|
3583
|
-
"success",
|
|
3584
|
-
"superseded \u2014 cleaned",
|
|
3585
|
-
`${baseline.superseded.removed.length} \u2014 ${baseline.superseded.removed.map((t) => t.replace(/^\.claude\//, "")).join(", ")} \xB7 \uB418\uB3CC\uB9AC\uB824\uBA74 \uADF8 \uC790\uC0B0 \uC5C6\uC774 \uC7AC\uC124\uCE58`
|
|
3586
|
-
)
|
|
3587
|
-
);
|
|
3588
|
-
}
|
|
3589
|
-
if (baseline.superseded.kept.length > 0) {
|
|
3590
|
-
log(
|
|
3591
|
-
assetRow(
|
|
3592
|
-
"skip",
|
|
3593
|
-
"superseded \u2014 kept",
|
|
3594
|
-
`${baseline.superseded.kept.length} \u2014 ${baseline.superseded.kept.map((t) => t.replace(/^\.claude\//, "")).join(", ")} \xB7 \uC774\uBC88 \uC120\uD0DD\uC774 \uB300\uCCB4\uD588\uC9C0\uB9CC \uC9C0\uC6B0\uC9C0 \uC54A\uC558\uB2E4 (\uC81C\uAC70: agent-harness uninstall)`
|
|
3595
|
-
)
|
|
3596
|
-
);
|
|
3597
|
-
}
|
|
3598
3524
|
const TEMPLATES_COL = 28;
|
|
3599
3525
|
if (baseline.rootClaudeMd) {
|
|
3600
3526
|
const n = baseline.rootClaudeMd.tracks.length;
|
|
@@ -3874,8 +3800,8 @@ function registerInstallCommand(cli2) {
|
|
|
3874
3800
|
|
|
3875
3801
|
// src/commands/list.ts
|
|
3876
3802
|
init_esm_shims();
|
|
3877
|
-
import { existsSync as
|
|
3878
|
-
import { join as
|
|
3803
|
+
import { existsSync as existsSync14, readFileSync as readFileSync13 } from "fs";
|
|
3804
|
+
import { join as join13, resolve as resolve3 } from "path";
|
|
3879
3805
|
function listAction(options = {}, deps = {}) {
|
|
3880
3806
|
const log = deps.log ?? console.log;
|
|
3881
3807
|
const err = deps.err ?? console.error;
|
|
@@ -3938,8 +3864,8 @@ function formatTemplateRows(log, projectDir) {
|
|
|
3938
3864
|
const rows = [` ${c.dim(dirs.join(" "))}`];
|
|
3939
3865
|
const rootMd = log.templates.rootClaudeMd;
|
|
3940
3866
|
if (rootMd) {
|
|
3941
|
-
const path =
|
|
3942
|
-
const modified =
|
|
3867
|
+
const path = join13(projectDir, rootMd.path);
|
|
3868
|
+
const modified = existsSync14(path) && hashContent(readFileSync13(path, "utf8")) !== rootMd.sha256;
|
|
3943
3869
|
rows.push(
|
|
3944
3870
|
modified ? ` ${c.dim(rootMd.path)} ${c.yellow("(\uC218\uC815\uB428 \u2014 uninstall \uC2DC \uBCF4\uC874)")}` : ` ${c.dim(rootMd.path)}`
|
|
3945
3871
|
);
|
|
@@ -3947,7 +3873,7 @@ function formatTemplateRows(log, projectDir) {
|
|
|
3947
3873
|
return rows;
|
|
3948
3874
|
}
|
|
3949
3875
|
function formatRootFileRows(rootFiles, projectDir) {
|
|
3950
|
-
const present = rootFiles.filter((f) =>
|
|
3876
|
+
const present = rootFiles.filter((f) => existsSync14(join13(projectDir, f.path)));
|
|
3951
3877
|
const width = Math.max(0, ...present.map((f) => f.path.length));
|
|
3952
3878
|
return present.map(
|
|
3953
3879
|
(f) => ` ${c.dim(padDisplay(f.path, width))} ${c.dim(f.change === "created" ? "\uC0DD\uC131" : "\uBCD1\uD569")} ${c.dim(f.notes.join(" / "))}`
|
|
@@ -3965,15 +3891,15 @@ function registerListCommand(cli2) {
|
|
|
3965
3891
|
init_esm_shims();
|
|
3966
3892
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
3967
3893
|
import {
|
|
3968
|
-
existsSync as
|
|
3894
|
+
existsSync as existsSync15,
|
|
3969
3895
|
lstatSync as lstatSync2,
|
|
3970
3896
|
readdirSync as readdirSync5,
|
|
3971
|
-
readFileSync as
|
|
3897
|
+
readFileSync as readFileSync14,
|
|
3972
3898
|
rmdirSync,
|
|
3973
3899
|
rmSync,
|
|
3974
3900
|
writeFileSync as writeFileSync7
|
|
3975
3901
|
} from "fs";
|
|
3976
|
-
import { dirname as dirname5, join as
|
|
3902
|
+
import { dirname as dirname5, join as join14, relative as relative2, resolve as resolve4, sep as sep2 } from "path";
|
|
3977
3903
|
|
|
3978
3904
|
// src/uninstall-interactive.ts
|
|
3979
3905
|
init_esm_shims();
|
|
@@ -5213,7 +5139,7 @@ function settleLog(ctx, io) {
|
|
|
5213
5139
|
return true;
|
|
5214
5140
|
}
|
|
5215
5141
|
} else {
|
|
5216
|
-
io.rm(
|
|
5142
|
+
io.rm(join14(projectDir, INSTALL_LOG_DIR));
|
|
5217
5143
|
io.rm(legacyInstallLogPath(projectDir));
|
|
5218
5144
|
io.log(` ${status.success("install log removed")}`);
|
|
5219
5145
|
}
|
|
@@ -5263,7 +5189,7 @@ function advisoryLines(plan, projectDir, rootFiles) {
|
|
|
5263
5189
|
return lines;
|
|
5264
5190
|
}
|
|
5265
5191
|
function rootFileAdvisoryLines(rootFiles, projectDir) {
|
|
5266
|
-
const present = rootFiles.filter((f) =>
|
|
5192
|
+
const present = rootFiles.filter((f) => existsSync15(join14(projectDir, f.path)));
|
|
5267
5193
|
if (present.length === 0) return [];
|
|
5268
5194
|
return [
|
|
5269
5195
|
"",
|
|
@@ -5364,16 +5290,16 @@ function buildGlobalAdvisoryCmd(asset) {
|
|
|
5364
5290
|
}
|
|
5365
5291
|
}
|
|
5366
5292
|
function removeTemplates(log, projectDir, rm) {
|
|
5367
|
-
rm(
|
|
5368
|
-
if (log.templates.codexDir) rm(
|
|
5369
|
-
if (log.templates.opencodeDir) rm(
|
|
5293
|
+
rm(join14(projectDir, log.templates.claudeDir));
|
|
5294
|
+
if (log.templates.codexDir) rm(join14(projectDir, log.templates.codexDir));
|
|
5295
|
+
if (log.templates.opencodeDir) rm(join14(projectDir, log.templates.opencodeDir));
|
|
5370
5296
|
const external = removeExternalFiles(log, projectDir, rm);
|
|
5371
5297
|
const importStripped = stripRootImport(projectDir);
|
|
5372
5298
|
const rootMd = log.templates.rootClaudeMd;
|
|
5373
5299
|
if (rootMd) {
|
|
5374
5300
|
if (rootClaudeMdModified(log, projectDir))
|
|
5375
5301
|
return { rootClaudeMdKept: true, importStripped, external };
|
|
5376
|
-
rm(
|
|
5302
|
+
rm(join14(projectDir, rootMd.path));
|
|
5377
5303
|
}
|
|
5378
5304
|
return { rootClaudeMdKept: false, importStripped, external };
|
|
5379
5305
|
}
|
|
@@ -5381,17 +5307,17 @@ function removeExternalFiles(log, projectDir, rm) {
|
|
|
5381
5307
|
const removed = [];
|
|
5382
5308
|
const kept = [];
|
|
5383
5309
|
for (const { path, sha256 } of log.externalFiles ?? []) {
|
|
5384
|
-
const abs =
|
|
5385
|
-
if (!
|
|
5310
|
+
const abs = join14(projectDir, path);
|
|
5311
|
+
if (!existsSync15(abs)) continue;
|
|
5386
5312
|
if (!lstatSync2(abs).isFile()) continue;
|
|
5387
|
-
if (hashContent(
|
|
5313
|
+
if (hashContent(readFileSync14(abs, "utf8")) !== sha256) {
|
|
5388
5314
|
kept.push(path);
|
|
5389
5315
|
continue;
|
|
5390
5316
|
}
|
|
5391
5317
|
rm(abs);
|
|
5392
5318
|
removed.push(path);
|
|
5393
5319
|
}
|
|
5394
|
-
for (const path of removed) pruneEmptyDirsUpward(projectDir, dirname5(
|
|
5320
|
+
for (const path of removed) pruneEmptyDirsUpward(projectDir, dirname5(join14(projectDir, path)));
|
|
5395
5321
|
return { removed, kept };
|
|
5396
5322
|
}
|
|
5397
5323
|
function pruneEmptyDirsUpward(projectDir, startDir) {
|
|
@@ -5413,7 +5339,7 @@ function stripRootImport(projectDir) {
|
|
|
5413
5339
|
const stripped = current === null ? null : stripHarnessImport(current);
|
|
5414
5340
|
if (stripped === null) return false;
|
|
5415
5341
|
try {
|
|
5416
|
-
writeFileSync7(
|
|
5342
|
+
writeFileSync7(join14(projectDir, "CLAUDE.md"), stripped, "utf8");
|
|
5417
5343
|
return true;
|
|
5418
5344
|
} catch {
|
|
5419
5345
|
return false;
|
|
@@ -5423,9 +5349,9 @@ function previewExternalLines(installLog, projectDir) {
|
|
|
5423
5349
|
const lines = [];
|
|
5424
5350
|
let removable = 0;
|
|
5425
5351
|
for (const { path, sha256 } of installLog.externalFiles ?? []) {
|
|
5426
|
-
const abs =
|
|
5427
|
-
if (!
|
|
5428
|
-
if (hashContent(
|
|
5352
|
+
const abs = join14(projectDir, path);
|
|
5353
|
+
if (!existsSync15(abs)) continue;
|
|
5354
|
+
if (hashContent(readFileSync14(abs, "utf8")) !== sha256) {
|
|
5429
5355
|
lines.push(` \u25CB keep ${path} (modified since install \u2014 preserved)`);
|
|
5430
5356
|
continue;
|
|
5431
5357
|
}
|
|
@@ -5451,10 +5377,10 @@ function hasRootImport(projectDir) {
|
|
|
5451
5377
|
return current !== null && stripHarnessImport(current) !== null;
|
|
5452
5378
|
}
|
|
5453
5379
|
function readRootClaudeMd(projectDir) {
|
|
5454
|
-
const path =
|
|
5455
|
-
if (!
|
|
5380
|
+
const path = join14(projectDir, "CLAUDE.md");
|
|
5381
|
+
if (!existsSync15(path)) return null;
|
|
5456
5382
|
try {
|
|
5457
|
-
return
|
|
5383
|
+
return readFileSync14(path, "utf8");
|
|
5458
5384
|
} catch {
|
|
5459
5385
|
return null;
|
|
5460
5386
|
}
|
|
@@ -5462,9 +5388,9 @@ function readRootClaudeMd(projectDir) {
|
|
|
5462
5388
|
function rootClaudeMdModified(log, projectDir) {
|
|
5463
5389
|
const rootMd = log.templates.rootClaudeMd;
|
|
5464
5390
|
if (!rootMd) return false;
|
|
5465
|
-
const path =
|
|
5466
|
-
if (!
|
|
5467
|
-
return hashContent(
|
|
5391
|
+
const path = join14(projectDir, rootMd.path);
|
|
5392
|
+
if (!existsSync15(path)) return false;
|
|
5393
|
+
return hashContent(readFileSync14(path, "utf8")) !== rootMd.sha256;
|
|
5468
5394
|
}
|
|
5469
5395
|
function formatTemplateList(log) {
|
|
5470
5396
|
const items = [log.templates.claudeDir];
|
|
@@ -5476,7 +5402,7 @@ function defaultSpawn2(cmd, args) {
|
|
|
5476
5402
|
return spawnSync2(cmd, [...args], { encoding: "utf8", stdio: "pipe", timeout: 12e4 });
|
|
5477
5403
|
}
|
|
5478
5404
|
function defaultRm(path) {
|
|
5479
|
-
if (
|
|
5405
|
+
if (existsSync15(path)) {
|
|
5480
5406
|
rmSync(path, { recursive: true, force: true });
|
|
5481
5407
|
}
|
|
5482
5408
|
}
|
|
@@ -5524,8 +5450,8 @@ import { resolve as resolve5 } from "path";
|
|
|
5524
5450
|
|
|
5525
5451
|
// src/state.ts
|
|
5526
5452
|
init_esm_shims();
|
|
5527
|
-
import { existsSync as
|
|
5528
|
-
import { join as
|
|
5453
|
+
import { existsSync as existsSync16, readFileSync as readFileSync15 } from "fs";
|
|
5454
|
+
import { join as join15 } from "path";
|
|
5529
5455
|
var META_FILE = ".claude/.installed-tracks";
|
|
5530
5456
|
var LEGACY_SIGNATURES = [
|
|
5531
5457
|
{ rule: "htmx.md", track: "ssr-htmx" },
|
|
@@ -5535,11 +5461,11 @@ var LEGACY_SIGNATURES = [
|
|
|
5535
5461
|
{ rule: "cli-development.md", track: "tooling" }
|
|
5536
5462
|
];
|
|
5537
5463
|
function detectInstallState(projectDir) {
|
|
5538
|
-
const claudeDir =
|
|
5539
|
-
const hasClaudeDir =
|
|
5464
|
+
const claudeDir = join15(projectDir, ".claude");
|
|
5465
|
+
const hasClaudeDir = existsSync16(claudeDir);
|
|
5540
5466
|
if (hasClaudeDir) {
|
|
5541
|
-
const metaPath =
|
|
5542
|
-
if (
|
|
5467
|
+
const metaPath = join15(projectDir, META_FILE);
|
|
5468
|
+
if (existsSync16(metaPath)) {
|
|
5543
5469
|
const tracks2 = readMetafile(metaPath);
|
|
5544
5470
|
return { state: "existing", tracks: tracks2, source: "metafile", hasClaudeDir: true };
|
|
5545
5471
|
}
|
|
@@ -5561,7 +5487,7 @@ function tracksFromLog(tracks) {
|
|
|
5561
5487
|
return [...new Set(tracks.filter(isTrack))].sort();
|
|
5562
5488
|
}
|
|
5563
5489
|
function readMetafile(path) {
|
|
5564
|
-
const raw =
|
|
5490
|
+
const raw = readFileSync15(path, "utf8");
|
|
5565
5491
|
const seen = /* @__PURE__ */ new Set();
|
|
5566
5492
|
for (const line of raw.split(/\s+/)) {
|
|
5567
5493
|
const trimmed = line.trim();
|
|
@@ -5572,13 +5498,13 @@ function readMetafile(path) {
|
|
|
5572
5498
|
return [...seen].sort();
|
|
5573
5499
|
}
|
|
5574
5500
|
function inferFromLegacySignatures(projectDir) {
|
|
5575
|
-
const rulesDir =
|
|
5576
|
-
if (!
|
|
5501
|
+
const rulesDir = join15(projectDir, ".claude/rules");
|
|
5502
|
+
if (!existsSync16(rulesDir)) {
|
|
5577
5503
|
return [];
|
|
5578
5504
|
}
|
|
5579
5505
|
const found = /* @__PURE__ */ new Set();
|
|
5580
5506
|
for (const sig of LEGACY_SIGNATURES) {
|
|
5581
|
-
if (
|
|
5507
|
+
if (existsSync16(join15(rulesDir, sig.rule))) {
|
|
5582
5508
|
found.add(sig.track);
|
|
5583
5509
|
}
|
|
5584
5510
|
}
|
|
@@ -5890,23 +5816,6 @@ Proceed?`,
|
|
|
5890
5816
|
});
|
|
5891
5817
|
return q(result) ? null : result;
|
|
5892
5818
|
},
|
|
5893
|
-
confirmSupersededCleanup: async (items, context) => {
|
|
5894
|
-
const names = items.map((i) => i.target.replace(/^\.claude\//, "")).join("\n ");
|
|
5895
|
-
const result = await ue({
|
|
5896
|
-
message: [
|
|
5897
|
-
`${context.by.join(" \xB7 ")} \uC744(\uB97C) \uACE8\uB77C\uC11C \uC544\uB798 ${items.length}\uAC1C\uAC00 \uC124\uCE58 \uB300\uC0C1\uC5D0\uC11C \uBE60\uC84C\uC2B5\uB2C8\uB2E4.`,
|
|
5898
|
-
"\uADF8\uB7F0\uB370 \uC774\uC804 \uC124\uCE58\uBCF8\uC774 \uB514\uC2A4\uD06C\uC5D0 \uADF8\uB300\uB85C \uB0A8\uC544 \uC788\uC2B5\uB2C8\uB2E4:",
|
|
5899
|
-
` ${names}`,
|
|
5900
|
-
"",
|
|
5901
|
-
`\uADF8\uB300\uB85C \uB450\uBA74 \uAC19\uC740 \uC77C\uC744 \uD558\uB294 \uC5D0\uC774\uC804\uD2B8\uAC00 \uB450 \uBC8C\uC774\uACE0, \uB9E4 \uC138\uC158 ~${context.tokens} tok \uB97C \uACC4\uC18D \uBB45\uB2C8\uB2E4.`,
|
|
5902
|
-
"(\uD558\uB124\uC2A4\uAC00 \uAE54\uC558\uACE0 \uC218\uC815\uB418\uC9C0 \uC54A\uC740 \uD30C\uC77C\uB9CC \uB300\uC0C1\uC785\uB2C8\uB2E4 \u2014 \uC9C1\uC811 \uACE0\uCE5C \uD30C\uC77C\uC740 \uAC74\uB4DC\uB9AC\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4)",
|
|
5903
|
-
"",
|
|
5904
|
-
"\uC815\uB9AC\uD560\uAE4C\uC694?"
|
|
5905
|
-
].join("\n"),
|
|
5906
|
-
initialValue: true
|
|
5907
|
-
});
|
|
5908
|
-
return q(result) ? null : result;
|
|
5909
|
-
},
|
|
5910
5819
|
selectInstallTargets: async (initialChecked, step, recap) => {
|
|
5911
5820
|
const pages = INSTALL_TARGET_PAGES;
|
|
5912
5821
|
const baselineTargets = listBaselineTargets({ tracks: recap?.tracks ?? [] });
|
|
@@ -6172,50 +6081,11 @@ ${summary}`
|
|
|
6172
6081
|
...userOverride ? { userOverride } : {},
|
|
6173
6082
|
...baselineExclude.length > 0 ? { baselineExclude } : {}
|
|
6174
6083
|
};
|
|
6175
|
-
const superseded = findSuperseded(
|
|
6176
|
-
projectDir,
|
|
6177
|
-
buildManifestSpec(spec),
|
|
6178
|
-
readInstallLog(projectDir)
|
|
6179
|
-
);
|
|
6180
|
-
let cleanSuperseded = false;
|
|
6181
|
-
if (superseded.length > 0) {
|
|
6182
|
-
const cause = supersededCause(projectDir, spec, readInstallLog(projectDir), superseded);
|
|
6183
|
-
const answer = await prompts.confirmSupersededCleanup(superseded, {
|
|
6184
|
-
by: cause.length > 0 ? cause : ["\uC774\uBC88 \uC120\uD0DD"],
|
|
6185
|
-
tokens: supersededTokens(superseded)
|
|
6186
|
-
});
|
|
6187
|
-
if (answer === null) {
|
|
6188
|
-
step = "confirm";
|
|
6189
|
-
continue;
|
|
6190
|
-
}
|
|
6191
|
-
cleanSuperseded = answer;
|
|
6192
|
-
}
|
|
6193
6084
|
prompts.outro(stepLabel(WIZARD.INSTALL, "Installing..."));
|
|
6194
|
-
return {
|
|
6195
|
-
ok: true,
|
|
6196
|
-
mode,
|
|
6197
|
-
spec: { ...spec, ...cleanSuperseded ? { cleanSuperseded } : {} }
|
|
6198
|
-
};
|
|
6085
|
+
return { ok: true, mode, spec };
|
|
6199
6086
|
}
|
|
6200
6087
|
}
|
|
6201
6088
|
}
|
|
6202
|
-
function supersededTokens(items) {
|
|
6203
|
-
const r = residentCost(items.map((i) => ({ source: i.source, target: i.target })));
|
|
6204
|
-
return r.rules + r.agentDescriptors + r.skillDescriptors;
|
|
6205
|
-
}
|
|
6206
|
-
function supersededCause(projectDir, spec, log, found) {
|
|
6207
|
-
const selected = spec.userOverride?.forceInclude ?? [];
|
|
6208
|
-
return selected.filter((id) => {
|
|
6209
|
-
const without = {
|
|
6210
|
-
...spec,
|
|
6211
|
-
userOverride: {
|
|
6212
|
-
forceInclude: selected.filter((x) => x !== id),
|
|
6213
|
-
forceExclude: [...spec.userOverride?.forceExclude ?? [], id]
|
|
6214
|
-
}
|
|
6215
|
-
};
|
|
6216
|
-
return findSuperseded(projectDir, buildManifestSpec(without), log).length < found.length;
|
|
6217
|
-
});
|
|
6218
|
-
}
|
|
6219
6089
|
function tracksEqual(a, b2) {
|
|
6220
6090
|
if (a.length !== b2.length) return false;
|
|
6221
6091
|
const sortedA = [...a].sort();
|