@uzysjung/agent-harness 26.151.0 → 26.153.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-OYNSK6QF.js} +49 -33
- package/dist/chunk-OYNSK6QF.js.map +1 -0
- package/dist/index.js +157 -243
- package/dist/index.js.map +1 -1
- package/dist/trust-tier-drift.js +1 -1
- package/package.json +1 -1
- package/templates/CLAUDE.md +93 -145
- package/templates/codex/README.md +6 -7
- package/templates/codex/config.toml.template +1 -9
- 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-harness-fit/README.md +76 -43
- package/templates/skills/audit-harness-fit/SKILL.md +62 -47
- package/templates/skills/audit-harness-fit/evals/scenarios.yaml +179 -22
- package/templates/skills/audit-harness-fit/references/apply.md +74 -45
- package/templates/skills/audit-harness-fit/references/audit.md +142 -118
- package/templates/skills/audit-harness-fit/references/populate.md +53 -49
- package/templates/skills/audit-harness-fit/references/verification.md +131 -100
- package/templates/skills/audit-service-gaps/SKILL.md +0 -2
- package/templates/skills/clear-korean-communication/SKILL.md +76 -209
- package/templates/skills/external-model-consult/scripts/codex-ask.sh +9 -4
- package/templates/skills/external-model-consult/scripts/gemini-ask.sh +9 -4
- package/templates/skills/model-orchestration/SKILL.md +123 -285
- package/templates/skills/objective-brief/SKILL.md +3 -3
- 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/codex/hooks/README.md +0 -37
- package/templates/codex/hooks/session-start.sh +0 -7
- package/templates/codex/hooks/uncommitted-check.sh +0 -7
- package/templates/skills/agent-introspection-debugging/SKILL.md +0 -153
- package/templates/skills/clear-korean-communication/references/pre-send-checklist.md +0 -37
- package/templates/skills/clear-korean-communication/references/why-it-works.md +0 -24
- package/templates/skills/clear-korean-communication/references/worked-examples.md +0 -89
- 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,9 +14,11 @@ 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,
|
|
21
|
+
TRACK_AGENTS,
|
|
20
22
|
__commonJS,
|
|
21
23
|
__toESM,
|
|
22
24
|
anyTrack,
|
|
@@ -62,7 +64,7 @@ import {
|
|
|
62
64
|
upsertHarnessImport,
|
|
63
65
|
withContinuousSkillsNote,
|
|
64
66
|
writeInstallLog
|
|
65
|
-
} from "./chunk-
|
|
67
|
+
} from "./chunk-OYNSK6QF.js";
|
|
66
68
|
|
|
67
69
|
// node_modules/sisteransi/src/index.js
|
|
68
70
|
var require_src = __commonJS({
|
|
@@ -747,7 +749,7 @@ var cac = (name = "") => new CAC(name);
|
|
|
747
749
|
// package.json
|
|
748
750
|
var package_default = {
|
|
749
751
|
name: "@uzysjung/agent-harness",
|
|
750
|
-
version: "26.
|
|
752
|
+
version: "26.153.0",
|
|
751
753
|
description: "Curate vetted AI-coding skills & plugins by your tech stack \u2014 install only what you need, across Claude Code, Codex, OpenCode & Antigravity",
|
|
752
754
|
type: "module",
|
|
753
755
|
publishConfig: {
|
|
@@ -986,14 +988,13 @@ function padDisplay(s, width) {
|
|
|
986
988
|
init_esm_shims();
|
|
987
989
|
import {
|
|
988
990
|
chmodSync as chmodSync3,
|
|
989
|
-
existsSync as
|
|
991
|
+
existsSync as existsSync13,
|
|
990
992
|
mkdirSync as mkdirSync4,
|
|
991
993
|
readdirSync as readdirSync4,
|
|
992
|
-
readFileSync as
|
|
993
|
-
unlinkSync as unlinkSync3,
|
|
994
|
+
readFileSync as readFileSync12,
|
|
994
995
|
writeFileSync as writeFileSync6
|
|
995
996
|
} from "fs";
|
|
996
|
-
import { basename as basename5, dirname as dirname4, join as
|
|
997
|
+
import { basename as basename5, dirname as dirname4, join as join12, resolve } from "path";
|
|
997
998
|
|
|
998
999
|
// src/ci-scaffold.ts
|
|
999
1000
|
init_esm_shims();
|
|
@@ -2089,53 +2090,18 @@ function writeMcpJson(path, mcp) {
|
|
|
2089
2090
|
`);
|
|
2090
2091
|
}
|
|
2091
2092
|
|
|
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
2093
|
// src/update-mode.ts
|
|
2128
2094
|
init_esm_shims();
|
|
2129
2095
|
import {
|
|
2130
2096
|
copyFileSync,
|
|
2131
|
-
existsSync as
|
|
2097
|
+
existsSync as existsSync12,
|
|
2132
2098
|
mkdirSync as mkdirSync3,
|
|
2133
2099
|
readdirSync as readdirSync3,
|
|
2134
|
-
readFileSync as
|
|
2100
|
+
readFileSync as readFileSync11,
|
|
2135
2101
|
unlinkSync as unlinkSync2,
|
|
2136
2102
|
writeFileSync as writeFileSync5
|
|
2137
2103
|
} from "fs";
|
|
2138
|
-
import { basename as basename4, dirname as dirname3, join as
|
|
2104
|
+
import { basename as basename4, dirname as dirname3, join as join11 } from "path";
|
|
2139
2105
|
var LEGACY_ANCHOR_FILE = ".claude/CLAUDE.md";
|
|
2140
2106
|
function buildUpdateSpec(projectDir, tracks) {
|
|
2141
2107
|
return {
|
|
@@ -2147,7 +2113,7 @@ function buildUpdateSpec(projectDir, tracks) {
|
|
|
2147
2113
|
};
|
|
2148
2114
|
}
|
|
2149
2115
|
function runUpdateMode(projectDir, templatesDir, harnessRoot, deps = {}) {
|
|
2150
|
-
const claudeDir =
|
|
2116
|
+
const claudeDir = join11(projectDir, ".claude");
|
|
2151
2117
|
const report = {
|
|
2152
2118
|
updated: {},
|
|
2153
2119
|
pruned: {},
|
|
@@ -2171,6 +2137,7 @@ function runUpdateMode(projectDir, templatesDir, harnessRoot, deps = {}) {
|
|
|
2171
2137
|
restored: [],
|
|
2172
2138
|
needsReinstall: [],
|
|
2173
2139
|
retiredAgents: [],
|
|
2140
|
+
demotedAgents: [],
|
|
2174
2141
|
mcpAllowlistRetired: null
|
|
2175
2142
|
};
|
|
2176
2143
|
const fresh = installNewAssets(projectDir, templatesDir);
|
|
@@ -2179,8 +2146,8 @@ function runUpdateMode(projectDir, templatesDir, harnessRoot, deps = {}) {
|
|
|
2179
2146
|
report.needsReinstall = fresh.needsReinstall;
|
|
2180
2147
|
const policyBase = policyBaseline(projectDir);
|
|
2181
2148
|
for (const { dir, ext } of POLICY_DIRS) {
|
|
2182
|
-
const target =
|
|
2183
|
-
const source =
|
|
2149
|
+
const target = join11(claudeDir, dir);
|
|
2150
|
+
const source = join11(templatesDir, dir);
|
|
2184
2151
|
const label = `.claude/${dir}`;
|
|
2185
2152
|
const ctx = { prefix: dir, baseline: policyBase };
|
|
2186
2153
|
const synced = updateDir(target, source, ext, ctx);
|
|
@@ -2190,8 +2157,8 @@ function runUpdateMode(projectDir, templatesDir, harnessRoot, deps = {}) {
|
|
|
2190
2157
|
}
|
|
2191
2158
|
refreshPolicyBaseline(projectDir, templatesDir);
|
|
2192
2159
|
const skillSync = syncSkills(
|
|
2193
|
-
|
|
2194
|
-
|
|
2160
|
+
join11(claudeDir, "skills"),
|
|
2161
|
+
join11(templatesDir, "skills"),
|
|
2195
2162
|
skillBaseline(projectDir),
|
|
2196
2163
|
/* @__PURE__ */ new Date(),
|
|
2197
2164
|
(relInSkills) => foreignOwnedTarget(projectDir, `.claude/skills/${relInSkills}`)
|
|
@@ -2201,15 +2168,16 @@ function runUpdateMode(projectDir, templatesDir, harnessRoot, deps = {}) {
|
|
|
2201
2168
|
report.skillsSkippedLinks = skillSync.skippedLinks;
|
|
2202
2169
|
refreshSkillBaseline(projectDir);
|
|
2203
2170
|
const installLog = readInstallLog(projectDir);
|
|
2204
|
-
if (
|
|
2171
|
+
if (existsSync12(claudeDir) && (installLog === null || installLog.spec.cli.includes("claude"))) {
|
|
2205
2172
|
syncHarnessAnchor(projectDir, templatesDir, report);
|
|
2206
2173
|
}
|
|
2207
|
-
const settingsPath =
|
|
2208
|
-
if (
|
|
2174
|
+
const settingsPath = join11(claudeDir, "settings.json");
|
|
2175
|
+
if (existsSync12(settingsPath)) {
|
|
2209
2176
|
report.staleHookRefs = cleanStaleHookRefs(settingsPath, claudeDir);
|
|
2210
2177
|
}
|
|
2211
2178
|
report.mcpAllowlistRetired = retireMcpAllowlist(projectDir);
|
|
2212
2179
|
report.retiredAgents = staleAgentFiles(claudeDir);
|
|
2180
|
+
report.demotedAgents = demotedAgentFiles(claudeDir, installedTracks(projectDir));
|
|
2213
2181
|
const external = refreshExternalCli(projectDir, harnessRoot);
|
|
2214
2182
|
report.externalUpdated = external.externalUpdated;
|
|
2215
2183
|
report.externalBackedUp = external.externalBackedUp;
|
|
@@ -2234,16 +2202,25 @@ function runUpdateMode(projectDir, templatesDir, harnessRoot, deps = {}) {
|
|
|
2234
2202
|
return report;
|
|
2235
2203
|
}
|
|
2236
2204
|
function staleSkillDirs(claudeDir) {
|
|
2237
|
-
const dir =
|
|
2238
|
-
if (!
|
|
2205
|
+
const dir = join11(claudeDir, "skills");
|
|
2206
|
+
if (!existsSync12(dir)) return [];
|
|
2239
2207
|
const stale = (id) => RENAMED_SKILL_IDS[id] !== void 0 || RETIRED_SKILL_IDS.includes(id);
|
|
2240
2208
|
return readdirSync3(dir, { withFileTypes: true }).filter((e2) => e2.isDirectory() && stale(e2.name)).map((e2) => e2.name);
|
|
2241
2209
|
}
|
|
2242
2210
|
function staleAgentFiles(claudeDir) {
|
|
2243
|
-
const dir =
|
|
2244
|
-
if (!
|
|
2211
|
+
const dir = join11(claudeDir, "agents");
|
|
2212
|
+
if (!existsSync12(dir)) return [];
|
|
2245
2213
|
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
2214
|
}
|
|
2215
|
+
function demotedAgentFiles(claudeDir, tracks) {
|
|
2216
|
+
if (tracks.length === 0) return [];
|
|
2217
|
+
const dir = join11(claudeDir, "agents");
|
|
2218
|
+
if (!existsSync12(dir)) return [];
|
|
2219
|
+
const demoted = TRACK_AGENTS.filter(([, pattern]) => !anyTrack(tracks, pattern)).map(
|
|
2220
|
+
([id]) => id
|
|
2221
|
+
);
|
|
2222
|
+
return readdirSync3(dir, { withFileTypes: true }).filter((e2) => e2.isFile() && e2.name.endsWith(".md")).map((e2) => basename4(e2.name, ".md")).filter((id) => demoted.includes(id));
|
|
2223
|
+
}
|
|
2247
2224
|
function installNewAssets(projectDir, templatesDir) {
|
|
2248
2225
|
const installed = [];
|
|
2249
2226
|
const restored = [];
|
|
@@ -2256,15 +2233,15 @@ function installNewAssets(projectDir, templatesDir) {
|
|
|
2256
2233
|
for (const entry of trackOnlyFileAssets(installedTracks(projectDir))) {
|
|
2257
2234
|
if (entry.target === HARNESS_ANCHOR_FILE) continue;
|
|
2258
2235
|
if (isBaselineExcluded(entry.target, baselineExcluded)) continue;
|
|
2259
|
-
const target =
|
|
2236
|
+
const target = join11(projectDir, entry.target);
|
|
2260
2237
|
const foreign = foreignOwnedTarget(projectDir, entry.target);
|
|
2261
2238
|
if (foreign !== null) {
|
|
2262
2239
|
if (!foreignOwned.includes(foreign)) foreignOwned.push(foreign);
|
|
2263
2240
|
continue;
|
|
2264
2241
|
}
|
|
2265
|
-
if (
|
|
2266
|
-
const source =
|
|
2267
|
-
if (!
|
|
2242
|
+
if (existsSync12(target)) continue;
|
|
2243
|
+
const source = join11(templatesDir, entry.source);
|
|
2244
|
+
if (!existsSync12(source)) continue;
|
|
2268
2245
|
if (entry.target.startsWith(".claude/")) {
|
|
2269
2246
|
if (!claudeSelected) continue;
|
|
2270
2247
|
if (entry.target.startsWith(".claude/hooks/") || entry.target === ".claude/settings.json") {
|
|
@@ -2289,13 +2266,13 @@ function trackOnlyFileAssets(tracks) {
|
|
|
2289
2266
|
);
|
|
2290
2267
|
}
|
|
2291
2268
|
function syncHarnessAnchor(projectDir, templatesDir, report) {
|
|
2292
|
-
if (
|
|
2269
|
+
if (existsSync12(join11(projectDir, LEGACY_ANCHOR_FILE))) {
|
|
2293
2270
|
report.legacyAnchor = LEGACY_ANCHOR_FILE;
|
|
2294
2271
|
}
|
|
2295
|
-
const templateMd =
|
|
2296
|
-
if (!
|
|
2297
|
-
const anchor =
|
|
2298
|
-
const existed =
|
|
2272
|
+
const templateMd = join11(templatesDir, "CLAUDE.md");
|
|
2273
|
+
if (!existsSync12(templateMd)) return;
|
|
2274
|
+
const anchor = join11(projectDir, HARNESS_ANCHOR_FILE);
|
|
2275
|
+
const existed = existsSync12(anchor);
|
|
2299
2276
|
copyFileSync(templateMd, anchor);
|
|
2300
2277
|
if (existed) {
|
|
2301
2278
|
report.claudeMdUpdated = true;
|
|
@@ -2307,15 +2284,15 @@ function syncHarnessAnchor(projectDir, templatesDir, report) {
|
|
|
2307
2284
|
recordAnchorBaseline(projectDir, anchor);
|
|
2308
2285
|
}
|
|
2309
2286
|
function upsertRootImport(projectDir) {
|
|
2310
|
-
const target =
|
|
2311
|
-
const existing =
|
|
2287
|
+
const target = join11(projectDir, "CLAUDE.md");
|
|
2288
|
+
const existing = existsSync12(target) ? readFileSync11(target, "utf8") : null;
|
|
2312
2289
|
const next = upsertHarnessImport(existing, {
|
|
2313
2290
|
projectName: basename4(projectDir),
|
|
2314
2291
|
tracks: installedTracks(projectDir),
|
|
2315
2292
|
// ADR-085 — update 는 선택 목록을 인자로 받지 않는다. `syncSkills` 와 같은 판정을 쓴다:
|
|
2316
2293
|
// `.claude/skills/<id>` 가 있으면 그 스킬은 이 설치가 고른 것이다.
|
|
2317
2294
|
continuousSkills: CONTINUOUS_SKILLS.map((s) => s.id).filter(
|
|
2318
|
-
(id) =>
|
|
2295
|
+
(id) => existsSync12(join11(projectDir, ".claude/skills", id))
|
|
2319
2296
|
)
|
|
2320
2297
|
});
|
|
2321
2298
|
if (next === existing) return false;
|
|
@@ -2335,7 +2312,7 @@ function recordAnchorBaseline(projectDir, anchor) {
|
|
|
2335
2312
|
...log.templates,
|
|
2336
2313
|
rootClaudeMd: {
|
|
2337
2314
|
path: HARNESS_ANCHOR_FILE,
|
|
2338
|
-
sha256: hashContent(
|
|
2315
|
+
sha256: hashContent(readFileSync11(anchor, "utf8"))
|
|
2339
2316
|
}
|
|
2340
2317
|
}
|
|
2341
2318
|
};
|
|
@@ -2377,16 +2354,16 @@ function isHarnessOwned2(ctx, rel, current) {
|
|
|
2377
2354
|
return isHarnessOwned(ctx.baseline, `${ctx.prefix}/${rel}`, current);
|
|
2378
2355
|
}
|
|
2379
2356
|
function updateDir(target, source, ext, ctx, now = /* @__PURE__ */ new Date()) {
|
|
2380
|
-
if (!
|
|
2357
|
+
if (!existsSync12(target) || !existsSync12(source)) return { updated: 0, backedUp: [] };
|
|
2381
2358
|
let updated = 0;
|
|
2382
2359
|
const backedUp = [];
|
|
2383
2360
|
for (const file of readdirSync3(target)) {
|
|
2384
2361
|
if (!file.endsWith(ext)) continue;
|
|
2385
|
-
const targetFile =
|
|
2386
|
-
const sourceFile =
|
|
2387
|
-
if (!
|
|
2388
|
-
const next =
|
|
2389
|
-
const current =
|
|
2362
|
+
const targetFile = join11(target, file);
|
|
2363
|
+
const sourceFile = join11(source, file);
|
|
2364
|
+
if (!existsSync12(sourceFile)) continue;
|
|
2365
|
+
const next = readFileSync11(sourceFile, "utf8");
|
|
2366
|
+
const current = readFileSync11(targetFile, "utf8");
|
|
2390
2367
|
if (current === next) continue;
|
|
2391
2368
|
if (!isHarnessOwned2(ctx, file, current)) {
|
|
2392
2369
|
backupFile(targetFile, now);
|
|
@@ -2398,7 +2375,7 @@ function updateDir(target, source, ext, ctx, now = /* @__PURE__ */ new Date()) {
|
|
|
2398
2375
|
return { updated, backedUp };
|
|
2399
2376
|
}
|
|
2400
2377
|
function syncSkills(targetDir, sourceDir, baseline, now = /* @__PURE__ */ new Date(), foreignOf) {
|
|
2401
|
-
if (!
|
|
2378
|
+
if (!existsSync12(targetDir) || !existsSync12(sourceDir))
|
|
2402
2379
|
return { updated: 0, backedUp: [], skippedLinks: [], foreignOwned: [] };
|
|
2403
2380
|
let updated = 0;
|
|
2404
2381
|
const backedUp = [];
|
|
@@ -2406,27 +2383,27 @@ function syncSkills(targetDir, sourceDir, baseline, now = /* @__PURE__ */ new Da
|
|
|
2406
2383
|
const foreignOwned = [];
|
|
2407
2384
|
for (const skill of readdirSync3(sourceDir, { withFileTypes: true })) {
|
|
2408
2385
|
if (!skill.isDirectory()) continue;
|
|
2409
|
-
const targetSkill =
|
|
2386
|
+
const targetSkill = join11(targetDir, skill.name);
|
|
2410
2387
|
if (occupiedByNonDirectory(targetSkill)) {
|
|
2411
2388
|
skippedLinks.push(skill.name);
|
|
2412
2389
|
continue;
|
|
2413
2390
|
}
|
|
2414
|
-
if (!
|
|
2415
|
-
for (const rel of listFilesRecursive(
|
|
2416
|
-
const targetFile =
|
|
2391
|
+
if (!existsSync12(targetSkill)) continue;
|
|
2392
|
+
for (const rel of listFilesRecursive(join11(sourceDir, skill.name))) {
|
|
2393
|
+
const targetFile = join11(targetSkill, rel);
|
|
2417
2394
|
const foreign = foreignOf(`${skill.name}/${rel}`);
|
|
2418
2395
|
if (foreign !== null) {
|
|
2419
2396
|
if (!foreignOwned.includes(foreign)) foreignOwned.push(foreign);
|
|
2420
2397
|
continue;
|
|
2421
2398
|
}
|
|
2422
|
-
const next =
|
|
2423
|
-
if (!
|
|
2399
|
+
const next = readFileSync11(join11(sourceDir, skill.name, rel), "utf8");
|
|
2400
|
+
if (!existsSync12(targetFile)) {
|
|
2424
2401
|
mkdirSync3(dirname3(targetFile), { recursive: true });
|
|
2425
2402
|
writeFileSync5(targetFile, next);
|
|
2426
2403
|
updated++;
|
|
2427
2404
|
continue;
|
|
2428
2405
|
}
|
|
2429
|
-
const current =
|
|
2406
|
+
const current = readFileSync11(targetFile, "utf8");
|
|
2430
2407
|
if (current === next) continue;
|
|
2431
2408
|
const recorded = baseline.get(`${skill.name}/${rel}`);
|
|
2432
2409
|
if (recorded === void 0 || hashContent(current) !== recorded) {
|
|
@@ -2472,16 +2449,16 @@ function refreshSkillBaseline(projectDir) {
|
|
|
2472
2449
|
}
|
|
2473
2450
|
}
|
|
2474
2451
|
function pruneOrphans(target, source, ext, ctx, now = /* @__PURE__ */ new Date()) {
|
|
2475
|
-
if (!
|
|
2452
|
+
if (!existsSync12(target) || !existsSync12(source)) return [];
|
|
2476
2453
|
const removed = [];
|
|
2477
2454
|
for (const file of readdirSync3(target)) {
|
|
2478
2455
|
if (!file.endsWith(ext)) continue;
|
|
2479
|
-
const sourceFile =
|
|
2480
|
-
if (!
|
|
2481
|
-
const targetFile =
|
|
2456
|
+
const sourceFile = join11(source, file);
|
|
2457
|
+
if (!existsSync12(sourceFile)) {
|
|
2458
|
+
const targetFile = join11(target, file);
|
|
2482
2459
|
if (!ctx.baseline.has(`${ctx.prefix}/${file}`)) continue;
|
|
2483
2460
|
try {
|
|
2484
|
-
const current =
|
|
2461
|
+
const current = readFileSync11(targetFile, "utf8");
|
|
2485
2462
|
if (!isHarnessOwned2(ctx, file, current)) backupFile(targetFile, now);
|
|
2486
2463
|
unlinkSync2(targetFile);
|
|
2487
2464
|
removed.push(file);
|
|
@@ -2492,8 +2469,8 @@ function pruneOrphans(target, source, ext, ctx, now = /* @__PURE__ */ new Date()
|
|
|
2492
2469
|
return removed;
|
|
2493
2470
|
}
|
|
2494
2471
|
function retireMcpAllowlist(projectDir, now = /* @__PURE__ */ new Date()) {
|
|
2495
|
-
const target =
|
|
2496
|
-
if (!
|
|
2472
|
+
const target = join11(projectDir, ".mcp-allowlist");
|
|
2473
|
+
if (!existsSync12(target)) return null;
|
|
2497
2474
|
try {
|
|
2498
2475
|
const backup = backupFile(target, now);
|
|
2499
2476
|
unlinkSync2(target);
|
|
@@ -2505,7 +2482,7 @@ function retireMcpAllowlist(projectDir, now = /* @__PURE__ */ new Date()) {
|
|
|
2505
2482
|
function cleanStaleHookRefs(settingsPath, claudeDir) {
|
|
2506
2483
|
let settings;
|
|
2507
2484
|
try {
|
|
2508
|
-
settings = JSON.parse(
|
|
2485
|
+
settings = JSON.parse(readFileSync11(settingsPath, "utf8"));
|
|
2509
2486
|
} catch {
|
|
2510
2487
|
return [];
|
|
2511
2488
|
}
|
|
@@ -2532,7 +2509,7 @@ function cleanStaleHookRefs(settingsPath, claudeDir) {
|
|
|
2532
2509
|
function keepHookRef(hook, claudeDir, removed) {
|
|
2533
2510
|
const relPath = projectAnchoredRef(hook?.command ?? "", claudeDir);
|
|
2534
2511
|
if (relPath === void 0) return true;
|
|
2535
|
-
const exists =
|
|
2512
|
+
const exists = existsSync12(join11(claudeDir, relPath));
|
|
2536
2513
|
if (!exists && !removed.includes(relPath)) removed.push(relPath);
|
|
2537
2514
|
return exists;
|
|
2538
2515
|
}
|
|
@@ -2559,14 +2536,14 @@ function projectAnchoredRef(command, claudeDir) {
|
|
|
2559
2536
|
function runInstall(ctx) {
|
|
2560
2537
|
const { harnessRoot, projectDir, spec } = ctx;
|
|
2561
2538
|
const mode = ctx.mode ?? "fresh";
|
|
2562
|
-
const templatesDir =
|
|
2563
|
-
if (!
|
|
2539
|
+
const templatesDir = join12(harnessRoot, "templates");
|
|
2540
|
+
if (!existsSync13(templatesDir)) {
|
|
2564
2541
|
throw new Error(`Templates dir not found: ${templatesDir}`);
|
|
2565
2542
|
}
|
|
2566
|
-
const claudeDir =
|
|
2543
|
+
const claudeDir = join12(projectDir, ".claude");
|
|
2567
2544
|
const previousLog = readInstallLog(projectDir);
|
|
2568
2545
|
const claudeWasSelected = previousLog?.spec.cli.includes("claude") ?? false;
|
|
2569
|
-
if (mode === "update" && !
|
|
2546
|
+
if (mode === "update" && !existsSync13(claudeDir) && (previousLog === null || claudeWasSelected)) {
|
|
2570
2547
|
throw new Error(
|
|
2571
2548
|
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
2549
|
);
|
|
@@ -2578,7 +2555,6 @@ function runInstall(ctx) {
|
|
|
2578
2555
|
const manifestSpec = buildManifestSpec(spec);
|
|
2579
2556
|
const policyBase = new Map((previousLog?.policyFiles ?? []).map((f) => [f.path, f.sha256]));
|
|
2580
2557
|
const baselineExcluded = new Set(spec.baselineExclude ?? []);
|
|
2581
|
-
const supersededFound = findSuperseded(projectDir, manifestSpec, previousLog);
|
|
2582
2558
|
const base = spec.cli.includes("claude") ? installClaudeBaseline(manifestSpec, projectDir, templatesDir, policyBase, baselineExcluded) : (
|
|
2583
2559
|
// claude 미선택이어도 CLI 중립 자산은 깔린다. manifest 전체가 `.claude/` baseline 안에서만
|
|
2584
2560
|
// 돌던 탓에 이 자산들이 claude 설치에만 도달했는데, **배포 룰 본문이 이 스크립트들을
|
|
@@ -2634,10 +2610,7 @@ function runInstall(ctx) {
|
|
|
2634
2610
|
baselineForeignOwned: [
|
|
2635
2611
|
...base.foreignOwned,
|
|
2636
2612
|
...externalForeignOwned.filter((f) => !base.foreignOwned.includes(f))
|
|
2637
|
-
]
|
|
2638
|
-
// 밀려난 자산 정리는 **manifest 복사가 끝난 뒤**에 한다. 앞에서 지우면 이번 설치가 다시
|
|
2639
|
-
// 깔 수도 있는 파일을 지웠다 되돌리는 셈이라, 무엇이 최종 상태인지 보고가 흐려진다.
|
|
2640
|
-
superseded: applySupersededCleanup(spec, projectDir, supersededFound)
|
|
2613
|
+
]
|
|
2641
2614
|
};
|
|
2642
2615
|
ctx.onProgress?.({ type: "baseline-complete", baseline });
|
|
2643
2616
|
const external = runExternalPhase(ctx);
|
|
@@ -2653,26 +2626,11 @@ function runInstall(ctx) {
|
|
|
2653
2626
|
);
|
|
2654
2627
|
return { ...baseline, external, staleHookRefs };
|
|
2655
2628
|
}
|
|
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
2629
|
function healStaleHookRefs(spec, projectDir) {
|
|
2672
2630
|
if (!spec.cli.includes("claude")) return [];
|
|
2673
|
-
const settingsPath =
|
|
2674
|
-
if (!
|
|
2675
|
-
return cleanStaleHookRefs(settingsPath,
|
|
2631
|
+
const settingsPath = join12(projectDir, ".claude/settings.json");
|
|
2632
|
+
if (!existsSync13(settingsPath)) return [];
|
|
2633
|
+
return cleanStaleHookRefs(settingsPath, join12(projectDir, ".claude"));
|
|
2676
2634
|
}
|
|
2677
2635
|
function resolveBackupPath(ctx, mode, claudeDir) {
|
|
2678
2636
|
const wantBackup = ctx.backup ?? (mode === "update" || mode === "reinstall");
|
|
@@ -2688,9 +2646,6 @@ function runUpdateInstall(ctx, templatesDir, backupPath) {
|
|
|
2688
2646
|
baselineExcluded: [],
|
|
2689
2647
|
baselineExcludedOnDisk: [],
|
|
2690
2648
|
baselineForeignOwned: [],
|
|
2691
|
-
// update 는 밀려난 자산을 다루지 않는다 — 그 판정은 사용자가 옵션을 고르는 설치 경로의
|
|
2692
|
-
// 것이고, update 는 무엇을 골랐는지 다시 묻지 않는다.
|
|
2693
|
-
superseded: { removed: [], kept: [] },
|
|
2694
2649
|
backup: backupPath,
|
|
2695
2650
|
installedTracks: [...ctx.spec.tracks].sort(),
|
|
2696
2651
|
mcpServers: [],
|
|
@@ -2741,12 +2696,12 @@ function installCliNeutralAssets(manifestSpec, projectDir, templatesDir, baselin
|
|
|
2741
2696
|
continue;
|
|
2742
2697
|
}
|
|
2743
2698
|
if (!isCliNeutralTarget(entry.target)) continue;
|
|
2744
|
-
const source =
|
|
2745
|
-
if (!
|
|
2699
|
+
const source = join12(templatesDir, entry.source);
|
|
2700
|
+
if (!existsSync13(source)) {
|
|
2746
2701
|
result.skipped += 1;
|
|
2747
2702
|
continue;
|
|
2748
2703
|
}
|
|
2749
|
-
copyFile(source,
|
|
2704
|
+
copyFile(source, join12(projectDir, entry.target));
|
|
2750
2705
|
result.filesCopied += 1;
|
|
2751
2706
|
}
|
|
2752
2707
|
return result;
|
|
@@ -2758,9 +2713,9 @@ function backupEditedPolicyFile(entryTarget, target, source, baseline) {
|
|
|
2758
2713
|
if (!POLICY_DIRS.some(({ dir, ext }) => rel.startsWith(`${dir}/`) && rel.endsWith(ext))) {
|
|
2759
2714
|
return null;
|
|
2760
2715
|
}
|
|
2761
|
-
if (!
|
|
2762
|
-
const current =
|
|
2763
|
-
if (current ===
|
|
2716
|
+
if (!existsSync13(target)) return null;
|
|
2717
|
+
const current = readFileSync12(target, "utf-8");
|
|
2718
|
+
if (current === readFileSync12(source, "utf-8")) return null;
|
|
2764
2719
|
const recorded = baseline.get(rel);
|
|
2765
2720
|
if (recorded !== void 0 && recorded === hashContent(current)) return null;
|
|
2766
2721
|
return backupFile(target);
|
|
@@ -2773,14 +2728,14 @@ function installClaudeBaseline(manifestSpec, projectDir, templatesDir, policyBas
|
|
|
2773
2728
|
if (!entry.applies(manifestSpec)) {
|
|
2774
2729
|
continue;
|
|
2775
2730
|
}
|
|
2776
|
-
const target =
|
|
2731
|
+
const target = join12(projectDir, entry.target);
|
|
2777
2732
|
if (isBaselineExcluded(entry.target, baselineExcluded)) {
|
|
2778
2733
|
result.excluded.push(entry.target);
|
|
2779
|
-
if (
|
|
2734
|
+
if (existsSync13(target)) result.excludedOnDisk.push(entry.target);
|
|
2780
2735
|
continue;
|
|
2781
2736
|
}
|
|
2782
|
-
const source =
|
|
2783
|
-
if (!
|
|
2737
|
+
const source = join12(templatesDir, entry.source);
|
|
2738
|
+
if (!existsSync13(source)) {
|
|
2784
2739
|
result.skipped += 1;
|
|
2785
2740
|
continue;
|
|
2786
2741
|
}
|
|
@@ -2791,7 +2746,7 @@ function installClaudeBaseline(manifestSpec, projectDir, templatesDir, policyBas
|
|
|
2791
2746
|
}
|
|
2792
2747
|
if (entry.type === "file") {
|
|
2793
2748
|
if (entry.target === ".claude/settings.json") {
|
|
2794
|
-
const backup = backupFileIfChanged(target,
|
|
2749
|
+
const backup = backupFileIfChanged(target, readFileSync12(source, "utf-8"));
|
|
2795
2750
|
if (backup) {
|
|
2796
2751
|
result.backups.push(backup);
|
|
2797
2752
|
}
|
|
@@ -2815,8 +2770,8 @@ function installClaudeBaseline(manifestSpec, projectDir, templatesDir, policyBas
|
|
|
2815
2770
|
}
|
|
2816
2771
|
accumulateCategory(result.categories, entry);
|
|
2817
2772
|
}
|
|
2818
|
-
const hookDir =
|
|
2819
|
-
if (
|
|
2773
|
+
const hookDir = join12(projectDir, ".claude/hooks");
|
|
2774
|
+
if (existsSync13(hookDir)) {
|
|
2820
2775
|
chmodHooksSync(hookDir);
|
|
2821
2776
|
}
|
|
2822
2777
|
writeInstalledTracks(projectDir, manifestSpec.tracks);
|
|
@@ -2830,9 +2785,9 @@ function installClaudeBaseline(manifestSpec, projectDir, templatesDir, policyBas
|
|
|
2830
2785
|
return result;
|
|
2831
2786
|
}
|
|
2832
2787
|
function harnessAnchorLog(projectDir) {
|
|
2833
|
-
const anchor =
|
|
2834
|
-
if (!
|
|
2835
|
-
return { path: HARNESS_ANCHOR_FILE, sha256: hashContent(
|
|
2788
|
+
const anchor = join12(projectDir, HARNESS_ANCHOR_FILE);
|
|
2789
|
+
if (!existsSync13(anchor)) return null;
|
|
2790
|
+
return { path: HARNESS_ANCHOR_FILE, sha256: hashContent(readFileSync12(anchor, "utf-8")) };
|
|
2836
2791
|
}
|
|
2837
2792
|
function writeEnvironmentFiles(projectDir, tracks) {
|
|
2838
2793
|
return {
|
|
@@ -2891,7 +2846,7 @@ function writeInstallLogSafe(ctx, externalFiles, external, rootClaudeMdLog, prev
|
|
|
2891
2846
|
rootFiles
|
|
2892
2847
|
);
|
|
2893
2848
|
const skillFiles = collectSkillHashes(ctx.projectDir);
|
|
2894
|
-
const policyFiles = collectPolicyHashes(ctx.projectDir,
|
|
2849
|
+
const policyFiles = collectPolicyHashes(ctx.projectDir, join12(ctx.harnessRoot, "templates"));
|
|
2895
2850
|
const merged = mergeExternalFiles(ctx.projectDir, previousLog?.externalFiles, externalFiles);
|
|
2896
2851
|
writeInstallLog(ctx.projectDir, {
|
|
2897
2852
|
...log,
|
|
@@ -2907,11 +2862,11 @@ function writeInstallLogSafe(ctx, externalFiles, external, rootClaudeMdLog, prev
|
|
|
2907
2862
|
}
|
|
2908
2863
|
}
|
|
2909
2864
|
function composeAndWriteMcp(harnessRoot, projectDir, spec) {
|
|
2910
|
-
const mcpPath =
|
|
2911
|
-
const created = !
|
|
2865
|
+
const mcpPath = join12(projectDir, ".mcp.json");
|
|
2866
|
+
const created = !existsSync13(mcpPath);
|
|
2912
2867
|
const composed = composeMcpJson({
|
|
2913
|
-
templateMcpPath:
|
|
2914
|
-
trackMapPath:
|
|
2868
|
+
templateMcpPath: join12(harnessRoot, "templates/mcp.json"),
|
|
2869
|
+
trackMapPath: join12(harnessRoot, "templates/track-mcp-map.tsv"),
|
|
2915
2870
|
existingPath: mcpPath,
|
|
2916
2871
|
tracks: spec.tracks
|
|
2917
2872
|
});
|
|
@@ -2964,15 +2919,15 @@ function accumulateCategory(cats, entry) {
|
|
|
2964
2919
|
}
|
|
2965
2920
|
}
|
|
2966
2921
|
function writeInstalledTracks(projectDir, tracks) {
|
|
2967
|
-
const path =
|
|
2922
|
+
const path = join12(projectDir, ".claude/.installed-tracks");
|
|
2968
2923
|
mkdirSync4(dirname4(path), { recursive: true });
|
|
2969
2924
|
const sorted = [...new Set(tracks)].sort().join("\n");
|
|
2970
2925
|
writeFileSync6(path, `${sorted}
|
|
2971
2926
|
`);
|
|
2972
2927
|
}
|
|
2973
2928
|
function writeRootClaudeMd(projectDir, tracks, continuousSkills) {
|
|
2974
|
-
const target =
|
|
2975
|
-
const existing =
|
|
2929
|
+
const target = join12(projectDir, "CLAUDE.md");
|
|
2930
|
+
const existing = existsSync13(target) ? readFileSync12(target, "utf-8") : null;
|
|
2976
2931
|
const content = upsertHarnessImport(existing, {
|
|
2977
2932
|
projectName: basename5(projectDir),
|
|
2978
2933
|
tracks,
|
|
@@ -2997,6 +2952,8 @@ function listHookFiles(hookDir) {
|
|
|
2997
2952
|
|
|
2998
2953
|
// src/commands/install-render.ts
|
|
2999
2954
|
init_esm_shims();
|
|
2955
|
+
import { existsSync as existsSync14, readdirSync as readdirSync5 } from "fs";
|
|
2956
|
+
import { join as join13 } from "path";
|
|
3000
2957
|
|
|
3001
2958
|
// src/preset-recommend.ts
|
|
3002
2959
|
init_esm_shims();
|
|
@@ -3062,12 +3019,14 @@ function renderInstallHeader(log, spec, mode) {
|
|
|
3062
3019
|
for (const [cat, ids] of groupAssetsByCategory(finalAssets)) {
|
|
3063
3020
|
log(` ${c.dim(`\xB7 ${cat}:`)} ${ids.join(", ")}`);
|
|
3064
3021
|
}
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
|
|
3070
|
-
|
|
3022
|
+
if (mode !== "update") {
|
|
3023
|
+
const assetSpec = buildManifestSpec(spec);
|
|
3024
|
+
const cost = formatResidentCostLine(
|
|
3025
|
+
residentCost(buildManifest(assetSpec).filter((e2) => e2.applies(assetSpec))),
|
|
3026
|
+
summarizeContextCost(finalAssets).unmeasuredCount
|
|
3027
|
+
);
|
|
3028
|
+
if (cost) log(` ${c.dim(`\xB7 ${cost}`)}`);
|
|
3029
|
+
}
|
|
3071
3030
|
}
|
|
3072
3031
|
log("");
|
|
3073
3032
|
}
|
|
@@ -3121,7 +3080,7 @@ function createInstallRenderer(log, spec, verbose) {
|
|
|
3121
3080
|
};
|
|
3122
3081
|
return { callbacks, phase2HeaderPrinted: () => phase2HeaderPrinted };
|
|
3123
3082
|
}
|
|
3124
|
-
function renderUpdateSummary(log, report) {
|
|
3083
|
+
function renderUpdateSummary(log, spec, report) {
|
|
3125
3084
|
log("");
|
|
3126
3085
|
log(unifiedSection("Summary"));
|
|
3127
3086
|
log("");
|
|
@@ -3131,8 +3090,26 @@ function renderUpdateSummary(log, report) {
|
|
|
3131
3090
|
log(infoRow("BACKUP", shortenPath(report.backup)));
|
|
3132
3091
|
log(infoRow("ROLLBACK", `rm -rf .claude && mv ${shortenPath(report.backup)} .claude`));
|
|
3133
3092
|
}
|
|
3093
|
+
const cost = formatResidentCostLine(
|
|
3094
|
+
residentCost(residentEntriesOnDisk(spec)),
|
|
3095
|
+
summarizeContextCost(finalSelectedAssets(spec.tracks, spec.userOverride)).unmeasuredCount
|
|
3096
|
+
);
|
|
3097
|
+
if (cost) log(infoRow("CONTEXT", cost));
|
|
3134
3098
|
log("");
|
|
3135
3099
|
}
|
|
3100
|
+
function residentEntriesOnDisk(spec) {
|
|
3101
|
+
const assetSpec = buildManifestSpec(spec);
|
|
3102
|
+
const planned = buildManifest(assetSpec).filter((e2) => e2.applies(assetSpec));
|
|
3103
|
+
const agentsDir = join13(spec.projectDir, ".claude", "agents");
|
|
3104
|
+
const onDisk = existsSync14(agentsDir) ? readdirSync5(agentsDir, { withFileTypes: true }).filter((e2) => e2.isFile() && e2.name.endsWith(".md")).map((e2) => ({
|
|
3105
|
+
source: `agents/${e2.name}`,
|
|
3106
|
+
target: `.claude/agents/${e2.name}`,
|
|
3107
|
+
// 배포판이 아니라 **이 프로젝트의 파일**을 잰다 — 사용자가 고친 descriptor 도,
|
|
3108
|
+
// 배포판에 더는 없는 에이전트도 실제로 상주하는 것은 이쪽이다.
|
|
3109
|
+
file: join13(agentsDir, e2.name)
|
|
3110
|
+
})) : [];
|
|
3111
|
+
return [...planned.filter((e2) => !e2.target.startsWith(".claude/agents/")), ...onDisk];
|
|
3112
|
+
}
|
|
3136
3113
|
function renderCliArtifacts(log, spec, report) {
|
|
3137
3114
|
const hasArtifacts = Boolean(report.codex || report.opencode || report.antigravity);
|
|
3138
3115
|
const cliSelected = targetsInclude(spec.cli, "codex") || targetsInclude(spec.cli, "opencode") || targetsInclude(spec.cli, "antigravity");
|
|
@@ -3401,11 +3378,22 @@ function renderPhase1Rows(log, baseline, verbose = false, withEcc = false, claud
|
|
|
3401
3378
|
);
|
|
3402
3379
|
}
|
|
3403
3380
|
for (const id of baseline.updateMode.retiredAgents) {
|
|
3381
|
+
const instead = RETIRED_AGENTS.find((a) => a.id === id)?.instead;
|
|
3382
|
+
log(
|
|
3383
|
+
assetRow(
|
|
3384
|
+
"skip",
|
|
3385
|
+
"agents",
|
|
3386
|
+
`${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}`)
|
|
3387
|
+
)
|
|
3388
|
+
);
|
|
3389
|
+
}
|
|
3390
|
+
for (const [id, pattern] of TRACK_AGENTS) {
|
|
3391
|
+
if (!baseline.updateMode.demotedAgents.includes(id)) continue;
|
|
3404
3392
|
log(
|
|
3405
3393
|
assetRow(
|
|
3406
3394
|
"skip",
|
|
3407
3395
|
"agents",
|
|
3408
|
-
`${id} \xB7 \uC774 \
|
|
3396
|
+
`${id} \xB7 \uC774 \uD2B8\uB799\uC5D0\uC11C\uB294 \uB354 \uC774\uC0C1 \uC124\uCE58\uD558\uC9C0 \uC54A\uB294\uB2E4 \u2014 ${pattern.split("|").join(" \xB7 ")} \uD2B8\uB799 \uC804\uC6A9 \xB7 .claude/agents/${id}.md \uB97C \uC9C0\uC6CC\uB3C4 \uB41C\uB2E4`
|
|
3409
3397
|
)
|
|
3410
3398
|
);
|
|
3411
3399
|
}
|
|
@@ -3532,7 +3520,7 @@ function renderPhase1Rows(log, baseline, verbose = false, withEcc = false, claud
|
|
|
3532
3520
|
phase1Row(
|
|
3533
3521
|
"agents",
|
|
3534
3522
|
cats.agents.length,
|
|
3535
|
-
"
|
|
3523
|
+
"independent verifier \xB7 implementation lane \xB7 domain lanes (track)",
|
|
3536
3524
|
cats.agents
|
|
3537
3525
|
);
|
|
3538
3526
|
}
|
|
@@ -3546,7 +3534,7 @@ function renderPhase1Rows(log, baseline, verbose = false, withEcc = false, claud
|
|
|
3546
3534
|
phase1Row(
|
|
3547
3535
|
"skills",
|
|
3548
3536
|
cats.skills.length,
|
|
3549
|
-
"
|
|
3537
|
+
"bundled methodology skills (track \xB7 opt-in)",
|
|
3550
3538
|
cats.skills
|
|
3551
3539
|
);
|
|
3552
3540
|
}
|
|
@@ -3577,24 +3565,6 @@ function renderPhase1Rows(log, baseline, verbose = false, withEcc = false, claud
|
|
|
3577
3565
|
)
|
|
3578
3566
|
);
|
|
3579
3567
|
}
|
|
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
3568
|
const TEMPLATES_COL = 28;
|
|
3599
3569
|
if (baseline.rootClaudeMd) {
|
|
3600
3570
|
const n = baseline.rootClaudeMd.tracks.length;
|
|
@@ -3809,7 +3779,7 @@ function executeSpec(spec, deps = {}) {
|
|
|
3809
3779
|
return;
|
|
3810
3780
|
}
|
|
3811
3781
|
if (report.updateMode) {
|
|
3812
|
-
renderUpdateSummary(log, report);
|
|
3782
|
+
renderUpdateSummary(log, spec, report);
|
|
3813
3783
|
return;
|
|
3814
3784
|
}
|
|
3815
3785
|
if (renderer.phase2HeaderPrinted()) {
|
|
@@ -3874,7 +3844,7 @@ function registerInstallCommand(cli2) {
|
|
|
3874
3844
|
|
|
3875
3845
|
// src/commands/list.ts
|
|
3876
3846
|
init_esm_shims();
|
|
3877
|
-
import { existsSync as existsSync15, readFileSync as
|
|
3847
|
+
import { existsSync as existsSync15, readFileSync as readFileSync13 } from "fs";
|
|
3878
3848
|
import { join as join14, resolve as resolve3 } from "path";
|
|
3879
3849
|
function listAction(options = {}, deps = {}) {
|
|
3880
3850
|
const log = deps.log ?? console.log;
|
|
@@ -3939,7 +3909,7 @@ function formatTemplateRows(log, projectDir) {
|
|
|
3939
3909
|
const rootMd = log.templates.rootClaudeMd;
|
|
3940
3910
|
if (rootMd) {
|
|
3941
3911
|
const path = join14(projectDir, rootMd.path);
|
|
3942
|
-
const modified = existsSync15(path) && hashContent(
|
|
3912
|
+
const modified = existsSync15(path) && hashContent(readFileSync13(path, "utf8")) !== rootMd.sha256;
|
|
3943
3913
|
rows.push(
|
|
3944
3914
|
modified ? ` ${c.dim(rootMd.path)} ${c.yellow("(\uC218\uC815\uB428 \u2014 uninstall \uC2DC \uBCF4\uC874)")}` : ` ${c.dim(rootMd.path)}`
|
|
3945
3915
|
);
|
|
@@ -3967,8 +3937,8 @@ import { spawnSync as spawnSync2 } from "child_process";
|
|
|
3967
3937
|
import {
|
|
3968
3938
|
existsSync as existsSync16,
|
|
3969
3939
|
lstatSync as lstatSync2,
|
|
3970
|
-
readdirSync as
|
|
3971
|
-
readFileSync as
|
|
3940
|
+
readdirSync as readdirSync6,
|
|
3941
|
+
readFileSync as readFileSync14,
|
|
3972
3942
|
rmdirSync,
|
|
3973
3943
|
rmSync,
|
|
3974
3944
|
writeFileSync as writeFileSync7
|
|
@@ -5384,7 +5354,7 @@ function removeExternalFiles(log, projectDir, rm) {
|
|
|
5384
5354
|
const abs = join15(projectDir, path);
|
|
5385
5355
|
if (!existsSync16(abs)) continue;
|
|
5386
5356
|
if (!lstatSync2(abs).isFile()) continue;
|
|
5387
|
-
if (hashContent(
|
|
5357
|
+
if (hashContent(readFileSync14(abs, "utf8")) !== sha256) {
|
|
5388
5358
|
kept.push(path);
|
|
5389
5359
|
continue;
|
|
5390
5360
|
}
|
|
@@ -5400,7 +5370,7 @@ function pruneEmptyDirsUpward(projectDir, startDir) {
|
|
|
5400
5370
|
const rel = relative2(projectDir, cursor);
|
|
5401
5371
|
if (rel === "" || rel.startsWith(`..${sep2}`) || rel === "..") return;
|
|
5402
5372
|
try {
|
|
5403
|
-
if (
|
|
5373
|
+
if (readdirSync6(cursor).length > 0) return;
|
|
5404
5374
|
rmdirSync(cursor);
|
|
5405
5375
|
} catch {
|
|
5406
5376
|
return;
|
|
@@ -5425,7 +5395,7 @@ function previewExternalLines(installLog, projectDir) {
|
|
|
5425
5395
|
for (const { path, sha256 } of installLog.externalFiles ?? []) {
|
|
5426
5396
|
const abs = join15(projectDir, path);
|
|
5427
5397
|
if (!existsSync16(abs)) continue;
|
|
5428
|
-
if (hashContent(
|
|
5398
|
+
if (hashContent(readFileSync14(abs, "utf8")) !== sha256) {
|
|
5429
5399
|
lines.push(` \u25CB keep ${path} (modified since install \u2014 preserved)`);
|
|
5430
5400
|
continue;
|
|
5431
5401
|
}
|
|
@@ -5454,7 +5424,7 @@ function readRootClaudeMd(projectDir) {
|
|
|
5454
5424
|
const path = join15(projectDir, "CLAUDE.md");
|
|
5455
5425
|
if (!existsSync16(path)) return null;
|
|
5456
5426
|
try {
|
|
5457
|
-
return
|
|
5427
|
+
return readFileSync14(path, "utf8");
|
|
5458
5428
|
} catch {
|
|
5459
5429
|
return null;
|
|
5460
5430
|
}
|
|
@@ -5464,7 +5434,7 @@ function rootClaudeMdModified(log, projectDir) {
|
|
|
5464
5434
|
if (!rootMd) return false;
|
|
5465
5435
|
const path = join15(projectDir, rootMd.path);
|
|
5466
5436
|
if (!existsSync16(path)) return false;
|
|
5467
|
-
return hashContent(
|
|
5437
|
+
return hashContent(readFileSync14(path, "utf8")) !== rootMd.sha256;
|
|
5468
5438
|
}
|
|
5469
5439
|
function formatTemplateList(log) {
|
|
5470
5440
|
const items = [log.templates.claudeDir];
|
|
@@ -5524,7 +5494,7 @@ import { resolve as resolve5 } from "path";
|
|
|
5524
5494
|
|
|
5525
5495
|
// src/state.ts
|
|
5526
5496
|
init_esm_shims();
|
|
5527
|
-
import { existsSync as existsSync17, readFileSync as
|
|
5497
|
+
import { existsSync as existsSync17, readFileSync as readFileSync15 } from "fs";
|
|
5528
5498
|
import { join as join16 } from "path";
|
|
5529
5499
|
var META_FILE = ".claude/.installed-tracks";
|
|
5530
5500
|
var LEGACY_SIGNATURES = [
|
|
@@ -5561,7 +5531,7 @@ function tracksFromLog(tracks) {
|
|
|
5561
5531
|
return [...new Set(tracks.filter(isTrack))].sort();
|
|
5562
5532
|
}
|
|
5563
5533
|
function readMetafile(path) {
|
|
5564
|
-
const raw =
|
|
5534
|
+
const raw = readFileSync15(path, "utf8");
|
|
5565
5535
|
const seen = /* @__PURE__ */ new Set();
|
|
5566
5536
|
for (const line of raw.split(/\s+/)) {
|
|
5567
5537
|
const trimmed = line.trim();
|
|
@@ -5890,23 +5860,6 @@ Proceed?`,
|
|
|
5890
5860
|
});
|
|
5891
5861
|
return q(result) ? null : result;
|
|
5892
5862
|
},
|
|
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
5863
|
selectInstallTargets: async (initialChecked, step, recap) => {
|
|
5911
5864
|
const pages = INSTALL_TARGET_PAGES;
|
|
5912
5865
|
const baselineTargets = listBaselineTargets({ tracks: recap?.tracks ?? [] });
|
|
@@ -6172,50 +6125,11 @@ ${summary}`
|
|
|
6172
6125
|
...userOverride ? { userOverride } : {},
|
|
6173
6126
|
...baselineExclude.length > 0 ? { baselineExclude } : {}
|
|
6174
6127
|
};
|
|
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
6128
|
prompts.outro(stepLabel(WIZARD.INSTALL, "Installing..."));
|
|
6194
|
-
return {
|
|
6195
|
-
ok: true,
|
|
6196
|
-
mode,
|
|
6197
|
-
spec: { ...spec, ...cleanSuperseded ? { cleanSuperseded } : {} }
|
|
6198
|
-
};
|
|
6129
|
+
return { ok: true, mode, spec };
|
|
6199
6130
|
}
|
|
6200
6131
|
}
|
|
6201
6132
|
}
|
|
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
6133
|
function tracksEqual(a, b2) {
|
|
6220
6134
|
if (a.length !== b2.length) return false;
|
|
6221
6135
|
const sortedA = [...a].sort();
|