@uzysjung/agent-harness 26.131.1 → 26.132.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +112 -43
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -712,7 +712,7 @@ var cac = (name = "") => new CAC(name);
|
|
|
712
712
|
// package.json
|
|
713
713
|
var package_default = {
|
|
714
714
|
name: "@uzysjung/agent-harness",
|
|
715
|
-
version: "26.
|
|
715
|
+
version: "26.132.0",
|
|
716
716
|
description: "Curate vetted AI-coding skills & plugins by your tech stack \u2014 install only what you need, across Claude Code, Codex, OpenCode & Antigravity",
|
|
717
717
|
type: "module",
|
|
718
718
|
publishConfig: {
|
|
@@ -1743,6 +1743,12 @@ import { existsSync as existsSync8, mkdirSync as mkdirSync3, readFileSync as rea
|
|
|
1743
1743
|
import { dirname as dirname3, join as join8 } from "path";
|
|
1744
1744
|
var INSTALL_LOG_FILENAME = ".harness-install.json";
|
|
1745
1745
|
var INSTALL_LOG_VERSION = 1;
|
|
1746
|
+
var POLICY_DIRS = [
|
|
1747
|
+
{ dir: "rules", ext: ".md" },
|
|
1748
|
+
{ dir: "agents", ext: ".md" },
|
|
1749
|
+
{ dir: "commands/uzys", ext: ".md" },
|
|
1750
|
+
{ dir: "hooks", ext: ".sh" }
|
|
1751
|
+
];
|
|
1746
1752
|
function buildAssetEntries(report, scope) {
|
|
1747
1753
|
if (!report) return [];
|
|
1748
1754
|
return report.attempted.filter((r) => r.ok).map((r) => assetToLogEntry(r.asset, scope, r.version));
|
|
@@ -1857,6 +1863,22 @@ function collectSkillHashes(projectDir) {
|
|
|
1857
1863
|
sha256: hashContent(readFileSync7(join8(skillsDir, rel), "utf8"))
|
|
1858
1864
|
}));
|
|
1859
1865
|
}
|
|
1866
|
+
function collectPolicyHashes(projectDir, templatesDir) {
|
|
1867
|
+
const out = [];
|
|
1868
|
+
for (const { dir, ext } of POLICY_DIRS) {
|
|
1869
|
+
const targetDir = join8(projectDir, ".claude", dir);
|
|
1870
|
+
const sourceDir = join8(templatesDir, dir);
|
|
1871
|
+
for (const rel of listFilesRecursive(targetDir)) {
|
|
1872
|
+
if (!rel.endsWith(ext)) continue;
|
|
1873
|
+
if (!existsSync8(join8(sourceDir, rel))) continue;
|
|
1874
|
+
out.push({
|
|
1875
|
+
path: `${dir}/${rel}`,
|
|
1876
|
+
sha256: hashContent(readFileSync7(join8(targetDir, rel), "utf8"))
|
|
1877
|
+
});
|
|
1878
|
+
}
|
|
1879
|
+
}
|
|
1880
|
+
return out;
|
|
1881
|
+
}
|
|
1860
1882
|
function writeInstallLog(projectDir, log) {
|
|
1861
1883
|
const path = join8(projectDir, ".claude", INSTALL_LOG_FILENAME);
|
|
1862
1884
|
mkdirSync3(dirname3(path), { recursive: true });
|
|
@@ -2167,38 +2189,21 @@ function runUpdateMode(projectDir, templatesDir) {
|
|
|
2167
2189
|
pruned: {},
|
|
2168
2190
|
staleHookRefs: [],
|
|
2169
2191
|
claudeMdUpdated: false,
|
|
2170
|
-
skillsBackedUp: []
|
|
2192
|
+
skillsBackedUp: [],
|
|
2193
|
+
policyBackedUp: []
|
|
2171
2194
|
};
|
|
2172
|
-
const
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
},
|
|
2185
|
-
{
|
|
2186
|
-
target: join10(claudeDir, "commands/uzys"),
|
|
2187
|
-
source: join10(templatesDir, "commands/uzys"),
|
|
2188
|
-
pattern: ".md",
|
|
2189
|
-
label: ".claude/commands/uzys"
|
|
2190
|
-
},
|
|
2191
|
-
{
|
|
2192
|
-
target: join10(claudeDir, "hooks"),
|
|
2193
|
-
source: join10(templatesDir, "hooks"),
|
|
2194
|
-
pattern: ".sh",
|
|
2195
|
-
label: ".claude/hooks"
|
|
2196
|
-
}
|
|
2197
|
-
];
|
|
2198
|
-
for (const t of targets) {
|
|
2199
|
-
report.updated[t.label] = updateDir(t.target, t.source, t.pattern);
|
|
2200
|
-
report.pruned[t.label] = pruneOrphans(t.target, t.source, t.pattern);
|
|
2201
|
-
}
|
|
2195
|
+
const policyBase = policyBaseline(projectDir);
|
|
2196
|
+
for (const { dir, ext } of POLICY_DIRS) {
|
|
2197
|
+
const target = join10(claudeDir, dir);
|
|
2198
|
+
const source = join10(templatesDir, dir);
|
|
2199
|
+
const label = `.claude/${dir}`;
|
|
2200
|
+
const ctx = { prefix: dir, baseline: policyBase };
|
|
2201
|
+
const synced = updateDir(target, source, ext, ctx);
|
|
2202
|
+
report.updated[label] = synced.updated;
|
|
2203
|
+
report.policyBackedUp.push(...synced.backedUp);
|
|
2204
|
+
report.pruned[label] = pruneOrphans(target, source, ext, ctx);
|
|
2205
|
+
}
|
|
2206
|
+
refreshPolicyBaseline(projectDir, templatesDir);
|
|
2202
2207
|
const skillSync = syncSkills(
|
|
2203
2208
|
join10(claudeDir, "skills"),
|
|
2204
2209
|
join10(templatesDir, "skills"),
|
|
@@ -2219,19 +2224,30 @@ function runUpdateMode(projectDir, templatesDir) {
|
|
|
2219
2224
|
}
|
|
2220
2225
|
return report;
|
|
2221
2226
|
}
|
|
2222
|
-
function
|
|
2223
|
-
|
|
2224
|
-
|
|
2227
|
+
function isHarnessOwned(ctx, rel, current) {
|
|
2228
|
+
const recorded = ctx.baseline.get(`${ctx.prefix}/${rel}`);
|
|
2229
|
+
return recorded !== void 0 && recorded === hashContent(current);
|
|
2230
|
+
}
|
|
2231
|
+
function updateDir(target, source, ext, ctx, now = /* @__PURE__ */ new Date()) {
|
|
2232
|
+
if (!existsSync11(target) || !existsSync11(source)) return { updated: 0, backedUp: [] };
|
|
2233
|
+
let updated = 0;
|
|
2234
|
+
const backedUp = [];
|
|
2225
2235
|
for (const file of readdirSync3(target)) {
|
|
2226
2236
|
if (!file.endsWith(ext)) continue;
|
|
2227
2237
|
const targetFile = join10(target, file);
|
|
2228
2238
|
const sourceFile = join10(source, file);
|
|
2229
|
-
if (existsSync11(sourceFile))
|
|
2230
|
-
|
|
2231
|
-
|
|
2239
|
+
if (!existsSync11(sourceFile)) continue;
|
|
2240
|
+
const next = readFileSync10(sourceFile, "utf8");
|
|
2241
|
+
const current = readFileSync10(targetFile, "utf8");
|
|
2242
|
+
if (current === next) continue;
|
|
2243
|
+
if (!isHarnessOwned(ctx, file, current)) {
|
|
2244
|
+
backupFile(targetFile, now);
|
|
2245
|
+
backedUp.push(`${ctx.prefix}/${file}`);
|
|
2232
2246
|
}
|
|
2247
|
+
writeFileSync8(targetFile, next);
|
|
2248
|
+
updated++;
|
|
2233
2249
|
}
|
|
2234
|
-
return
|
|
2250
|
+
return { updated, backedUp };
|
|
2235
2251
|
}
|
|
2236
2252
|
function syncSkills(targetDir, sourceDir, baseline, now = /* @__PURE__ */ new Date()) {
|
|
2237
2253
|
if (!existsSync11(targetDir) || !existsSync11(sourceDir)) return { updated: 0, backedUp: [] };
|
|
@@ -2267,6 +2283,22 @@ function skillBaseline(projectDir) {
|
|
|
2267
2283
|
const log = readInstallLog(projectDir);
|
|
2268
2284
|
return new Map((log?.skillFiles ?? []).map((f) => [f.path, f.sha256]));
|
|
2269
2285
|
}
|
|
2286
|
+
function policyBaseline(projectDir) {
|
|
2287
|
+
const log = readInstallLog(projectDir);
|
|
2288
|
+
return new Map((log?.policyFiles ?? []).map((f) => [f.path, f.sha256]));
|
|
2289
|
+
}
|
|
2290
|
+
function refreshPolicyBaseline(projectDir, templatesDir) {
|
|
2291
|
+
const log = readInstallLog(projectDir);
|
|
2292
|
+
if (!log) return;
|
|
2293
|
+
const policyFiles = collectPolicyHashes(projectDir, templatesDir);
|
|
2294
|
+
const next = { ...log };
|
|
2295
|
+
if (policyFiles.length > 0) next.policyFiles = policyFiles;
|
|
2296
|
+
else delete next.policyFiles;
|
|
2297
|
+
try {
|
|
2298
|
+
writeInstallLog(projectDir, next);
|
|
2299
|
+
} catch {
|
|
2300
|
+
}
|
|
2301
|
+
}
|
|
2270
2302
|
function refreshSkillBaseline(projectDir) {
|
|
2271
2303
|
const log = readInstallLog(projectDir);
|
|
2272
2304
|
if (!log) return;
|
|
@@ -2279,7 +2311,7 @@ function refreshSkillBaseline(projectDir) {
|
|
|
2279
2311
|
} catch {
|
|
2280
2312
|
}
|
|
2281
2313
|
}
|
|
2282
|
-
function pruneOrphans(target, source, ext) {
|
|
2314
|
+
function pruneOrphans(target, source, ext, ctx, now = /* @__PURE__ */ new Date()) {
|
|
2283
2315
|
if (!existsSync11(target) || !existsSync11(source)) return [];
|
|
2284
2316
|
const removed = [];
|
|
2285
2317
|
for (const file of readdirSync3(target)) {
|
|
@@ -2287,7 +2319,10 @@ function pruneOrphans(target, source, ext) {
|
|
|
2287
2319
|
const sourceFile = join10(source, file);
|
|
2288
2320
|
if (!existsSync11(sourceFile)) {
|
|
2289
2321
|
const targetFile = join10(target, file);
|
|
2322
|
+
if (!ctx.baseline.has(`${ctx.prefix}/${file}`)) continue;
|
|
2290
2323
|
try {
|
|
2324
|
+
const current = readFileSync10(targetFile, "utf8");
|
|
2325
|
+
if (!isHarnessOwned(ctx, file, current)) backupFile(targetFile, now);
|
|
2291
2326
|
unlinkSync(targetFile);
|
|
2292
2327
|
removed.push(file);
|
|
2293
2328
|
} catch {
|
|
@@ -2353,7 +2388,8 @@ function runInstall(ctx) {
|
|
|
2353
2388
|
return runUpdateInstall(ctx, templatesDir, backupPath);
|
|
2354
2389
|
}
|
|
2355
2390
|
const manifestSpec = buildManifestSpec(spec);
|
|
2356
|
-
const
|
|
2391
|
+
const policyBase = new Map((previousLog?.policyFiles ?? []).map((f) => [f.path, f.sha256]));
|
|
2392
|
+
const base = spec.cli.includes("claude") ? installClaudeBaseline(manifestSpec, projectDir, templatesDir, policyBase) : emptyClaudeBaseline();
|
|
2357
2393
|
const mcpResult = composeAndWriteMcp(harnessRoot, projectDir, spec);
|
|
2358
2394
|
const ciScaffold = isAssetSelected("ci-scaffold", {
|
|
2359
2395
|
tracks: spec.tracks,
|
|
@@ -2453,7 +2489,21 @@ function emptyClaudeBaseline() {
|
|
|
2453
2489
|
backups: []
|
|
2454
2490
|
};
|
|
2455
2491
|
}
|
|
2456
|
-
function
|
|
2492
|
+
function backupEditedPolicyFile(entryTarget, target, source, baseline) {
|
|
2493
|
+
const prefix = ".claude/";
|
|
2494
|
+
if (!entryTarget.startsWith(prefix)) return null;
|
|
2495
|
+
const rel = entryTarget.slice(prefix.length);
|
|
2496
|
+
if (!POLICY_DIRS.some(({ dir, ext }) => rel.startsWith(`${dir}/`) && rel.endsWith(ext))) {
|
|
2497
|
+
return null;
|
|
2498
|
+
}
|
|
2499
|
+
if (!existsSync12(target)) return null;
|
|
2500
|
+
const current = readFileSync11(target, "utf-8");
|
|
2501
|
+
if (current === readFileSync11(source, "utf-8")) return null;
|
|
2502
|
+
const recorded = baseline.get(rel);
|
|
2503
|
+
if (recorded !== void 0 && recorded === hashContent(current)) return null;
|
|
2504
|
+
return backupFile(target);
|
|
2505
|
+
}
|
|
2506
|
+
function installClaudeBaseline(manifestSpec, projectDir, templatesDir, policyBase) {
|
|
2457
2507
|
ensureProjectSkeleton(projectDir);
|
|
2458
2508
|
const result = emptyClaudeBaseline();
|
|
2459
2509
|
const manifest = buildManifest(manifestSpec);
|
|
@@ -2473,6 +2523,11 @@ function installClaudeBaseline(manifestSpec, projectDir, templatesDir) {
|
|
|
2473
2523
|
if (backup) {
|
|
2474
2524
|
result.backups.push(backup);
|
|
2475
2525
|
}
|
|
2526
|
+
} else {
|
|
2527
|
+
const backup = backupEditedPolicyFile(entry.target, target, source, policyBase);
|
|
2528
|
+
if (backup) {
|
|
2529
|
+
result.backups.push(backup);
|
|
2530
|
+
}
|
|
2476
2531
|
}
|
|
2477
2532
|
copyFile(source, target);
|
|
2478
2533
|
result.filesCopied += 1;
|
|
@@ -2580,7 +2635,12 @@ function writeInstallLogSafe(ctx, external, rootClaudeMdLog, previousLog, claude
|
|
|
2580
2635
|
rootFiles
|
|
2581
2636
|
);
|
|
2582
2637
|
const skillFiles = collectSkillHashes(ctx.projectDir);
|
|
2583
|
-
|
|
2638
|
+
const policyFiles = collectPolicyHashes(ctx.projectDir, join11(ctx.harnessRoot, "templates"));
|
|
2639
|
+
writeInstallLog(ctx.projectDir, {
|
|
2640
|
+
...log,
|
|
2641
|
+
...skillFiles.length > 0 ? { skillFiles } : {},
|
|
2642
|
+
...policyFiles.length > 0 ? { policyFiles } : {}
|
|
2643
|
+
});
|
|
2584
2644
|
} catch (e2) {
|
|
2585
2645
|
ctx.onProgress?.({
|
|
2586
2646
|
type: "install-log-error",
|
|
@@ -3033,6 +3093,15 @@ function renderPhase1Rows(log, baseline, verbose = false, withEcc = false, claud
|
|
|
3033
3093
|
)
|
|
3034
3094
|
);
|
|
3035
3095
|
}
|
|
3096
|
+
if (baseline.updateMode.policyBackedUp.length > 0) {
|
|
3097
|
+
log(
|
|
3098
|
+
assetRow(
|
|
3099
|
+
"skip",
|
|
3100
|
+
"edited policy files",
|
|
3101
|
+
`${baseline.updateMode.policyBackedUp.length} backed up as *.backup-<time>`
|
|
3102
|
+
)
|
|
3103
|
+
);
|
|
3104
|
+
}
|
|
3036
3105
|
if (baseline.updateMode.staleHookRefs.length > 0) {
|
|
3037
3106
|
log(
|
|
3038
3107
|
assetRow(
|