@uzysjung/agent-harness 26.125.0 → 26.126.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -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.125.0",
715
+ version: "26.126.1",
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: {
@@ -907,12 +907,12 @@ init_esm_shims();
907
907
  import {
908
908
  chmodSync as chmodSync2,
909
909
  existsSync as existsSync12,
910
- mkdirSync as mkdirSync4,
911
- readdirSync as readdirSync3,
910
+ mkdirSync as mkdirSync5,
911
+ readdirSync as readdirSync4,
912
912
  readFileSync as readFileSync11,
913
913
  writeFileSync as writeFileSync9
914
914
  } from "fs";
915
- import { basename as basename4, dirname as dirname4, join as join11, resolve } from "path";
915
+ import { basename as basename4, dirname as dirname5, join as join11, resolve } from "path";
916
916
 
917
917
  // src/antigravity/transform.ts
918
918
  init_esm_shims();
@@ -962,7 +962,15 @@ function portBody(body) {
962
962
 
963
963
  // src/fs-ops.ts
964
964
  init_esm_shims();
965
- import { copyFileSync, cpSync, existsSync, mkdirSync, readFileSync, renameSync } from "fs";
965
+ import {
966
+ copyFileSync,
967
+ cpSync,
968
+ existsSync,
969
+ mkdirSync,
970
+ readdirSync,
971
+ readFileSync,
972
+ renameSync
973
+ } from "fs";
966
974
  import { dirname, join } from "path";
967
975
  function ensureDir(path) {
968
976
  mkdirSync(path, { recursive: true });
@@ -1004,10 +1012,26 @@ function backupFileIfChanged(target, newContent, now = /* @__PURE__ */ new Date(
1004
1012
  if (readFileSync(target, "utf-8") === newContent) {
1005
1013
  return null;
1006
1014
  }
1015
+ return backupFile(target, now);
1016
+ }
1017
+ function backupFile(target, now = /* @__PURE__ */ new Date()) {
1007
1018
  const backup = `${target}.backup-${formatStamp(now)}`;
1008
1019
  copyFileSync(target, backup);
1009
1020
  return backup;
1010
1021
  }
1022
+ function listFilesRecursive(dir, prefix = "") {
1023
+ if (!existsSync(dir)) return [];
1024
+ const out = [];
1025
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
1026
+ const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
1027
+ if (entry.isDirectory()) {
1028
+ out.push(...listFilesRecursive(join(dir, entry.name), rel));
1029
+ } else if (entry.isFile()) {
1030
+ out.push(rel);
1031
+ }
1032
+ }
1033
+ return out;
1034
+ }
1011
1035
  function formatStamp(now) {
1012
1036
  return now.toISOString().replace(/[-:]/g, "").replace(/\.\d+Z$/, "Z").slice(0, 15);
1013
1037
  }
@@ -1508,7 +1532,7 @@ function writeMcpAllowlist(projectDir) {
1508
1532
  // src/external-installer.ts
1509
1533
  init_esm_shims();
1510
1534
  import { spawnSync } from "child_process";
1511
- import { existsSync as existsSync7, readdirSync, readFileSync as readFileSync6 } from "fs";
1535
+ import { existsSync as existsSync7, readdirSync as readdirSync2, readFileSync as readFileSync6 } from "fs";
1512
1536
  import { homedir as homedir2 } from "os";
1513
1537
  import { join as join7 } from "path";
1514
1538
  var DEFAULT_SPAWN_TIMEOUT_MS = 12e4;
@@ -1679,7 +1703,7 @@ function detectVersion(method, spawn) {
1679
1703
  const marketplaceShort = method.pluginId.slice(at2 + 1);
1680
1704
  const cacheBase = join7(homedir2(), ".claude/plugins/cache", marketplaceShort, plugin);
1681
1705
  if (!existsSync7(cacheBase)) return void 0;
1682
- const versions = readdirSync(cacheBase).filter((v2) => /^\d/.test(v2)).sort();
1706
+ const versions = readdirSync2(cacheBase).filter((v2) => /^\d/.test(v2)).sort();
1683
1707
  return versions.at(-1);
1684
1708
  }
1685
1709
  case "npm": {
@@ -1826,6 +1850,13 @@ function mergeAssets(previous, current) {
1826
1850
  function hashContent(content) {
1827
1851
  return createHash("sha256").update(content, "utf8").digest("hex");
1828
1852
  }
1853
+ function collectSkillHashes(projectDir) {
1854
+ const skillsDir = join8(projectDir, ".claude/skills");
1855
+ return listFilesRecursive(skillsDir).map((rel) => ({
1856
+ path: rel,
1857
+ sha256: hashContent(readFileSync7(join8(skillsDir, rel), "utf8"))
1858
+ }));
1859
+ }
1829
1860
  function writeInstallLog(projectDir, log) {
1830
1861
  const path = join8(projectDir, ".claude", INSTALL_LOG_FILENAME);
1831
1862
  mkdirSync3(dirname3(path), { recursive: true });
@@ -2114,19 +2145,21 @@ init_esm_shims();
2114
2145
  import {
2115
2146
  copyFileSync as copyFileSync2,
2116
2147
  existsSync as existsSync11,
2117
- readdirSync as readdirSync2,
2148
+ mkdirSync as mkdirSync4,
2149
+ readdirSync as readdirSync3,
2118
2150
  readFileSync as readFileSync10,
2119
2151
  unlinkSync,
2120
2152
  writeFileSync as writeFileSync8
2121
2153
  } from "fs";
2122
- import { join as join10 } from "path";
2154
+ import { dirname as dirname4, join as join10 } from "path";
2123
2155
  function runUpdateMode(projectDir, templatesDir) {
2124
2156
  const claudeDir = join10(projectDir, ".claude");
2125
2157
  const report = {
2126
2158
  updated: {},
2127
2159
  pruned: {},
2128
2160
  staleHookRefs: [],
2129
- claudeMdUpdated: false
2161
+ claudeMdUpdated: false,
2162
+ skillsBackedUp: []
2130
2163
  };
2131
2164
  const targets = [
2132
2165
  {
@@ -2158,6 +2191,14 @@ function runUpdateMode(projectDir, templatesDir) {
2158
2191
  report.updated[t.label] = updateDir(t.target, t.source, t.pattern);
2159
2192
  report.pruned[t.label] = pruneOrphans(t.target, t.source, t.pattern);
2160
2193
  }
2194
+ const skillSync = syncSkills(
2195
+ join10(claudeDir, "skills"),
2196
+ join10(templatesDir, "skills"),
2197
+ skillBaseline(projectDir)
2198
+ );
2199
+ report.updated[".claude/skills"] = skillSync.updated;
2200
+ report.skillsBackedUp = skillSync.backedUp;
2201
+ refreshSkillBaseline(projectDir);
2161
2202
  const claudeMd = join10(claudeDir, "CLAUDE.md");
2162
2203
  const templateMd = join10(templatesDir, "CLAUDE.md");
2163
2204
  if (existsSync11(claudeMd) && existsSync11(templateMd)) {
@@ -2173,7 +2214,7 @@ function runUpdateMode(projectDir, templatesDir) {
2173
2214
  function updateDir(target, source, ext) {
2174
2215
  if (!existsSync11(target) || !existsSync11(source)) return 0;
2175
2216
  let count = 0;
2176
- for (const file of readdirSync2(target)) {
2217
+ for (const file of readdirSync3(target)) {
2177
2218
  if (!file.endsWith(ext)) continue;
2178
2219
  const targetFile = join10(target, file);
2179
2220
  const sourceFile = join10(source, file);
@@ -2184,10 +2225,56 @@ function updateDir(target, source, ext) {
2184
2225
  }
2185
2226
  return count;
2186
2227
  }
2228
+ function syncSkills(targetDir, sourceDir, baseline, now = /* @__PURE__ */ new Date()) {
2229
+ if (!existsSync11(targetDir) || !existsSync11(sourceDir)) return { updated: 0, backedUp: [] };
2230
+ let updated = 0;
2231
+ const backedUp = [];
2232
+ for (const skill of readdirSync3(sourceDir, { withFileTypes: true })) {
2233
+ if (!skill.isDirectory()) continue;
2234
+ const targetSkill = join10(targetDir, skill.name);
2235
+ if (!existsSync11(targetSkill)) continue;
2236
+ for (const rel of listFilesRecursive(join10(sourceDir, skill.name))) {
2237
+ const targetFile = join10(targetSkill, rel);
2238
+ const next = readFileSync10(join10(sourceDir, skill.name, rel), "utf8");
2239
+ if (!existsSync11(targetFile)) {
2240
+ mkdirSync4(dirname4(targetFile), { recursive: true });
2241
+ writeFileSync8(targetFile, next);
2242
+ updated++;
2243
+ continue;
2244
+ }
2245
+ const current = readFileSync10(targetFile, "utf8");
2246
+ if (current === next) continue;
2247
+ const recorded = baseline.get(`${skill.name}/${rel}`);
2248
+ if (recorded === void 0 || hashContent(current) !== recorded) {
2249
+ backupFile(targetFile, now);
2250
+ backedUp.push(`${skill.name}/${rel}`);
2251
+ }
2252
+ writeFileSync8(targetFile, next);
2253
+ updated++;
2254
+ }
2255
+ }
2256
+ return { updated, backedUp };
2257
+ }
2258
+ function skillBaseline(projectDir) {
2259
+ const log = readInstallLog(projectDir);
2260
+ return new Map((log?.skillFiles ?? []).map((f) => [f.path, f.sha256]));
2261
+ }
2262
+ function refreshSkillBaseline(projectDir) {
2263
+ const log = readInstallLog(projectDir);
2264
+ if (!log) return;
2265
+ const skillFiles = collectSkillHashes(projectDir);
2266
+ const next = { ...log };
2267
+ if (skillFiles.length > 0) next.skillFiles = skillFiles;
2268
+ else delete next.skillFiles;
2269
+ try {
2270
+ writeInstallLog(projectDir, next);
2271
+ } catch {
2272
+ }
2273
+ }
2187
2274
  function pruneOrphans(target, source, ext) {
2188
2275
  if (!existsSync11(target) || !existsSync11(source)) return [];
2189
2276
  const removed = [];
2190
- for (const file of readdirSync2(target)) {
2277
+ for (const file of readdirSync3(target)) {
2191
2278
  if (!file.endsWith(ext)) continue;
2192
2279
  const sourceFile = join10(source, file);
2193
2280
  if (!existsSync11(sourceFile)) {
@@ -2484,7 +2571,8 @@ function writeInstallLogSafe(ctx, external, rootClaudeMdLog, previousLog, claude
2484
2571
  claudeDirMovedAside,
2485
2572
  rootFiles
2486
2573
  );
2487
- writeInstallLog(ctx.projectDir, log);
2574
+ const skillFiles = collectSkillHashes(ctx.projectDir);
2575
+ writeInstallLog(ctx.projectDir, skillFiles.length > 0 ? { ...log, skillFiles } : log);
2488
2576
  } catch (e2) {
2489
2577
  ctx.onProgress?.({
2490
2578
  type: "install-log-error",
@@ -2604,7 +2692,7 @@ function accumulateCategory(cats, entry) {
2604
2692
  }
2605
2693
  function writeInstalledTracks(projectDir, tracks) {
2606
2694
  const path = join11(projectDir, ".claude/.installed-tracks");
2607
- mkdirSync4(dirname4(path), { recursive: true });
2695
+ mkdirSync5(dirname5(path), { recursive: true });
2608
2696
  const sorted = [...new Set(tracks)].sort().join("\n");
2609
2697
  writeFileSync9(path, `${sorted}
2610
2698
  `);
@@ -2625,7 +2713,7 @@ function chmodHooksSync(hookDir) {
2625
2713
  }
2626
2714
  }
2627
2715
  function listHookFiles(hookDir) {
2628
- return readdirSync3(hookDir, { withFileTypes: true }).filter((e2) => e2.isFile() && e2.name.endsWith(".sh")).map((e2) => resolve(hookDir, e2.name));
2716
+ return readdirSync4(hookDir, { withFileTypes: true }).filter((e2) => e2.isFile() && e2.name.endsWith(".sh")).map((e2) => resolve(hookDir, e2.name));
2629
2717
  }
2630
2718
 
2631
2719
  // src/commands/install-render.ts
@@ -2928,6 +3016,15 @@ function renderPhase1Rows(log, baseline, verbose = false, withEcc = false, claud
2928
3016
  if (baseline.updateMode.claudeMdUpdated) {
2929
3017
  log(assetRow("success", ".claude/CLAUDE.md", "refreshed from template"));
2930
3018
  }
3019
+ if (baseline.updateMode.skillsBackedUp.length > 0) {
3020
+ log(
3021
+ assetRow(
3022
+ "skip",
3023
+ ".claude/skills edited files",
3024
+ `${baseline.updateMode.skillsBackedUp.length} backed up as *.backup-<time>`
3025
+ )
3026
+ );
3027
+ }
2931
3028
  if (baseline.updateMode.staleHookRefs.length > 0) {
2932
3029
  log(
2933
3030
  assetRow(
@@ -4871,7 +4968,9 @@ function buildRouterChoices(state) {
4871
4968
  {
4872
4969
  value: "update",
4873
4970
  label: "Update policy files (auto-backup)",
4874
- hint: "Refresh rules / agents / commands / hooks from latest templates",
4971
+ // v26.126.0 (R-3a) skills 목록에 들어왔다. 문구가 update 의 광고이고
4972
+ // (update 는 위저드로만 도달한다) 실동작과 어긋나면 그 자체로 거짓출하다.
4973
+ hint: "Refresh rules / agents / commands / hooks / skills \u2014 your edits are backed up",
4875
4974
  enabled: true
4876
4975
  },
4877
4976
  {