@uzysjung/agent-harness 26.148.1 → 26.149.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -56,7 +56,7 @@ import {
56
56
  summarizeContextCost,
57
57
  upsertHarnessImport,
58
58
  writeInstallLog
59
- } from "./chunk-PTUM2NI3.js";
59
+ } from "./chunk-YSW3OLH4.js";
60
60
 
61
61
  // node_modules/sisteransi/src/index.js
62
62
  var require_src = __commonJS({
@@ -741,7 +741,7 @@ var cac = (name = "") => new CAC(name);
741
741
  // package.json
742
742
  var package_default = {
743
743
  name: "@uzysjung/agent-harness",
744
- version: "26.148.1",
744
+ version: "26.149.0",
745
745
  description: "Curate vetted AI-coding skills & plugins by your tech stack \u2014 install only what you need, across Claude Code, Codex, OpenCode & Antigravity",
746
746
  type: "module",
747
747
  publishConfig: {
@@ -982,9 +982,9 @@ import {
982
982
  chmodSync as chmodSync2,
983
983
  existsSync as existsSync13,
984
984
  mkdirSync as mkdirSync4,
985
- readdirSync as readdirSync3,
985
+ readdirSync as readdirSync4,
986
986
  readFileSync as readFileSync12,
987
- unlinkSync as unlinkSync2,
987
+ unlinkSync as unlinkSync3,
988
988
  writeFileSync as writeFileSync6
989
989
  } from "fs";
990
990
  import { basename as basename5, dirname as dirname4, join as join12, resolve } from "path";
@@ -1231,7 +1231,7 @@ function writeRules(harnessRoot, projectDir, writer) {
1231
1231
  template,
1232
1232
  claudeMd,
1233
1233
  projectName: basename(projectDir),
1234
- projectContext: renderFillScaffold()
1234
+ projectContext: renderFillScaffold("antigravity-rule")
1235
1235
  });
1236
1236
  return writer.write(target, rulesOut) ? target : null;
1237
1237
  }
@@ -1377,7 +1377,7 @@ function runCodexTransform(params) {
1377
1377
  template: agentsTemplate,
1378
1378
  claudeMd,
1379
1379
  projectName,
1380
- projectContext: renderFillScaffold(),
1380
+ projectContext: renderFillScaffold("agents-md"),
1381
1381
  // Codex 는 룰 디렉터리가 없다 — 룰이 AGENTS.md 본문에 들어가야 도달한다(§Harness Rules).
1382
1382
  harnessRules: renderRulesBlock(portRules(harnessRoot, rules))
1383
1383
  });
@@ -1442,7 +1442,7 @@ function readOptionalJson(path) {
1442
1442
 
1443
1443
  // src/opencode/transform.ts
1444
1444
  init_esm_shims();
1445
- import { existsSync as existsSync7, readFileSync as readFileSync6 } from "fs";
1445
+ import { existsSync as existsSync7, readdirSync, readFileSync as readFileSync6, unlinkSync } from "fs";
1446
1446
  import { basename as basename3, join as join7 } from "path";
1447
1447
 
1448
1448
  // src/opencode/agents-md.ts
@@ -1456,72 +1456,6 @@ function renderAgentsMd2(params) {
1456
1456
  return renameSlashes2(replaced);
1457
1457
  }
1458
1458
 
1459
- // src/opencode/commands.ts
1460
- init_esm_shims();
1461
- function renderCommandFromSkill(source, id, opts) {
1462
- const { description, body } = parseSkillFrontmatter(source);
1463
- const finalDescription = description || `${id} (dev-method skill, OpenCode command fallback)`;
1464
- const escapedDesc = finalDescription.replace(/"/g, '\\"');
1465
- const renamedBody = renameSlashes2(body).trimEnd();
1466
- const agent = opts?.shellDependent ? "build" : "plan";
1467
- return [
1468
- "---",
1469
- `description: "${escapedDesc}"`,
1470
- `agent: ${agent}`,
1471
- "---",
1472
- "",
1473
- renamedBody,
1474
- ""
1475
- ].join("\n");
1476
- }
1477
- function parseSkillFrontmatter(source) {
1478
- const lines = source.split(/\r?\n/);
1479
- if (lines[0] !== "---") {
1480
- const firstLine = lines[0] ?? "";
1481
- return { description: firstLine.trim(), body: lines.slice(1).join("\n") };
1482
- }
1483
- let secondDelimAt = -1;
1484
- let description = "";
1485
- for (let i = 1; i < lines.length; i++) {
1486
- const line = lines[i] ?? "";
1487
- if (line === "---") {
1488
- secondDelimAt = i;
1489
- break;
1490
- }
1491
- const inline = line.match(/^description:\s*(.+)$/);
1492
- if (!inline) {
1493
- continue;
1494
- }
1495
- const raw = (inline[1] ?? "").trim();
1496
- if (raw === ">-" || raw === ">" || raw === "|" || raw === "|-") {
1497
- const collected = [];
1498
- for (let j3 = i + 1; j3 < lines.length; j3++) {
1499
- const next = lines[j3] ?? "";
1500
- if (next === "---") {
1501
- break;
1502
- }
1503
- if (next.trim() === "" || /^\s/.test(next)) {
1504
- collected.push(next.trim());
1505
- } else {
1506
- break;
1507
- }
1508
- }
1509
- description = collected.join(" ").replace(/\s+/g, " ").trim();
1510
- } else {
1511
- description = stripQuotes(raw);
1512
- }
1513
- }
1514
- const body = secondDelimAt >= 0 ? lines.slice(secondDelimAt + 1).join("\n").replace(/^\n+/, "") : source;
1515
- return { description, body };
1516
- }
1517
- function stripQuotes(raw) {
1518
- const trimmed = raw.trim();
1519
- if (trimmed.startsWith('"') && trimmed.endsWith('"')) {
1520
- return trimmed.slice(1, -1);
1521
- }
1522
- return trimmed;
1523
- }
1524
-
1525
1459
  // src/opencode/opencode-json.ts
1526
1460
  init_esm_shims();
1527
1461
  function renderOpencodeJson(params) {
@@ -1565,7 +1499,7 @@ function runOpencodeTransform(params) {
1565
1499
  template: agentsTemplate,
1566
1500
  claudeMd,
1567
1501
  projectName,
1568
- projectContext: renderFillScaffold(),
1502
+ projectContext: renderFillScaffold("agents-md"),
1569
1503
  // codex 와 **같은 파일**(프로젝트 루트 `AGENTS.md`)이다. 두 transform 이 서로 다른 본문을
1570
1504
  // 쓰면 나중에 도는 쪽이 앞선 쪽을 덮어써, codex+opencode 조합에서 룰이 통째로 사라진다
1571
1505
  // (독립 검증 C-1 실측). 같은 내용을 쓰면 순서가 결과를 바꾸지 않는다.
@@ -1574,27 +1508,37 @@ function runOpencodeTransform(params) {
1574
1508
  writer.write(agentsMdPath, agentsMdOut);
1575
1509
  const opencodeJsonPath = join7(projectDir, "opencode.json");
1576
1510
  writer.write(opencodeJsonPath, renderOpencodeJson({ template: opencodeTemplate, mcp }));
1577
- const cmdDir = join7(projectDir, ".opencode/commands");
1578
- if (!refreshOnly) ensureDir(cmdDir);
1579
- const commandFiles = [];
1511
+ const skillFiles = [];
1580
1512
  for (const id of selectedInternalSkills) {
1581
1513
  const src = join7(harnessRoot, "templates/skills", id, "SKILL.md");
1582
1514
  if (!existsSync7(src)) {
1583
1515
  continue;
1584
1516
  }
1585
- const target = join7(cmdDir, `${id}.md`);
1586
- const shellDependent = existsSync7(join7(harnessRoot, "templates/skills", id, "scripts"));
1587
- const wrote = writer.write(
1588
- target,
1589
- renderCommandFromSkill(readFileSync6(src, "utf8"), id, { shellDependent })
1590
- );
1591
- if (!wrote) continue;
1592
- commandFiles.push(target);
1517
+ const target = join7(projectDir, ".agents", "skills", id, "SKILL.md");
1518
+ if (!writer.write(target, renderBundledSkill(readFileSync6(src, "utf8")))) continue;
1519
+ skillFiles.push(target);
1520
+ }
1521
+ const retiredCommands = [];
1522
+ const cmdDir = join7(projectDir, ".opencode/commands");
1523
+ if (existsSync7(cmdDir)) {
1524
+ for (const entry of readdirSync(cmdDir)) {
1525
+ if (!entry.endsWith(".md")) continue;
1526
+ const id = entry.slice(0, -3);
1527
+ if (!existsSync7(join7(harnessRoot, "templates/skills", id, "SKILL.md"))) continue;
1528
+ const victim = join7(cmdDir, entry);
1529
+ try {
1530
+ backupFile(victim);
1531
+ unlinkSync(victim);
1532
+ retiredCommands.push(victim);
1533
+ } catch {
1534
+ }
1535
+ }
1593
1536
  }
1594
1537
  return {
1595
1538
  agentsMdPath,
1596
1539
  opencodeJsonPath,
1597
- commandFiles,
1540
+ skillFiles,
1541
+ retiredCommands,
1598
1542
  ownership: writer.result()
1599
1543
  };
1600
1544
  }
@@ -1755,8 +1699,8 @@ function addGitignoreEnv(projectDir) {
1755
1699
  if (GITIGNORE_ENV_PATTERN.test(content)) {
1756
1700
  return false;
1757
1701
  }
1758
- const sep2 = content.endsWith("\n") ? "" : "\n";
1759
- appendFileSync(path, `${sep2}
1702
+ const sep3 = content.endsWith("\n") ? "" : "\n";
1703
+ appendFileSync(path, `${sep3}
1760
1704
  # Secret env (auto-added by agent-harness install)
1761
1705
  .env
1762
1706
  `);
@@ -1777,9 +1721,9 @@ function addGitignoreAgentArtifacts(projectDir) {
1777
1721
  if (missing.length === 0) {
1778
1722
  return [];
1779
1723
  }
1780
- const sep2 = content.endsWith("\n") ? "" : "\n";
1724
+ const sep3 = content.endsWith("\n") ? "" : "\n";
1781
1725
  const block = [GITIGNORE_AGENT_ARTIFACT_HEADER, ...missing].join("\n");
1782
- appendFileSync(path, `${sep2}
1726
+ appendFileSync(path, `${sep3}
1783
1727
  ${block}
1784
1728
  `);
1785
1729
  return [...missing];
@@ -1788,7 +1732,7 @@ ${block}
1788
1732
  // src/external-installer.ts
1789
1733
  init_esm_shims();
1790
1734
  import { spawnSync } from "child_process";
1791
- import { existsSync as existsSync9, readdirSync, readFileSync as readFileSync8 } from "fs";
1735
+ import { existsSync as existsSync9, readdirSync as readdirSync2, readFileSync as readFileSync8 } from "fs";
1792
1736
  import { homedir as homedir2 } from "os";
1793
1737
  import { join as join9 } from "path";
1794
1738
  var DEFAULT_SPAWN_TIMEOUT_MS = 12e4;
@@ -1999,7 +1943,7 @@ function detectVersion(method, spawn) {
1999
1943
  const marketplaceShort = method.pluginId.slice(at2 + 1);
2000
1944
  const cacheBase = join9(homedir2(), ".claude/plugins/cache", marketplaceShort, plugin);
2001
1945
  if (!existsSync9(cacheBase)) return void 0;
2002
- const versions = readdirSync(cacheBase).filter((v2) => /^\d/.test(v2)).sort();
1946
+ const versions = readdirSync2(cacheBase).filter((v2) => /^\d/.test(v2)).sort();
2003
1947
  return versions.at(-1);
2004
1948
  }
2005
1949
  case "npm": {
@@ -2148,9 +2092,9 @@ import {
2148
2092
  copyFileSync,
2149
2093
  existsSync as existsSync12,
2150
2094
  mkdirSync as mkdirSync3,
2151
- readdirSync as readdirSync2,
2095
+ readdirSync as readdirSync3,
2152
2096
  readFileSync as readFileSync11,
2153
- unlinkSync,
2097
+ unlinkSync as unlinkSync2,
2154
2098
  writeFileSync as writeFileSync5
2155
2099
  } from "fs";
2156
2100
  import { basename as basename4, dirname as dirname3, join as join11 } from "path";
@@ -2375,7 +2319,7 @@ function updateDir(target, source, ext, ctx, now = /* @__PURE__ */ new Date()) {
2375
2319
  if (!existsSync12(target) || !existsSync12(source)) return { updated: 0, backedUp: [] };
2376
2320
  let updated = 0;
2377
2321
  const backedUp = [];
2378
- for (const file of readdirSync2(target)) {
2322
+ for (const file of readdirSync3(target)) {
2379
2323
  if (!file.endsWith(ext)) continue;
2380
2324
  const targetFile = join11(target, file);
2381
2325
  const sourceFile = join11(source, file);
@@ -2399,7 +2343,7 @@ function syncSkills(targetDir, sourceDir, baseline, now = /* @__PURE__ */ new Da
2399
2343
  const backedUp = [];
2400
2344
  const skippedLinks = [];
2401
2345
  const foreignOwned = [];
2402
- for (const skill of readdirSync2(sourceDir, { withFileTypes: true })) {
2346
+ for (const skill of readdirSync3(sourceDir, { withFileTypes: true })) {
2403
2347
  if (!skill.isDirectory()) continue;
2404
2348
  const targetSkill = join11(targetDir, skill.name);
2405
2349
  if (occupiedByNonDirectory(targetSkill)) {
@@ -2469,7 +2413,7 @@ function refreshSkillBaseline(projectDir) {
2469
2413
  function pruneOrphans(target, source, ext, ctx, now = /* @__PURE__ */ new Date()) {
2470
2414
  if (!existsSync12(target) || !existsSync12(source)) return [];
2471
2415
  const removed = [];
2472
- for (const file of readdirSync2(target)) {
2416
+ for (const file of readdirSync3(target)) {
2473
2417
  if (!file.endsWith(ext)) continue;
2474
2418
  const sourceFile = join11(source, file);
2475
2419
  if (!existsSync12(sourceFile)) {
@@ -2478,7 +2422,7 @@ function pruneOrphans(target, source, ext, ctx, now = /* @__PURE__ */ new Date()
2478
2422
  try {
2479
2423
  const current = readFileSync11(targetFile, "utf8");
2480
2424
  if (!isHarnessOwned2(ctx, file, current)) backupFile(targetFile, now);
2481
- unlinkSync(targetFile);
2425
+ unlinkSync2(targetFile);
2482
2426
  removed.push(file);
2483
2427
  } catch {
2484
2428
  }
@@ -2491,7 +2435,7 @@ function retireMcpAllowlist(projectDir, now = /* @__PURE__ */ new Date()) {
2491
2435
  if (!existsSync12(target)) return null;
2492
2436
  try {
2493
2437
  const backup = backupFile(target, now);
2494
- unlinkSync(target);
2438
+ unlinkSync2(target);
2495
2439
  return backup;
2496
2440
  } catch {
2497
2441
  return null;
@@ -2655,7 +2599,7 @@ function applySupersededCleanup(spec, projectDir, found) {
2655
2599
  const kept = [];
2656
2600
  for (const asset of found) {
2657
2601
  try {
2658
- unlinkSync2(join12(projectDir, asset.target));
2602
+ unlinkSync3(join12(projectDir, asset.target));
2659
2603
  removed.push(asset.target);
2660
2604
  } catch {
2661
2605
  kept.push(asset.target);
@@ -2993,7 +2937,7 @@ function chmodHooksSync(hookDir) {
2993
2937
  }
2994
2938
  }
2995
2939
  function listHookFiles(hookDir) {
2996
- return readdirSync3(hookDir, { withFileTypes: true }).filter((e2) => e2.isFile() && e2.name.endsWith(".sh")).map((e2) => resolve(hookDir, e2.name));
2940
+ return readdirSync4(hookDir, { withFileTypes: true }).filter((e2) => e2.isFile() && e2.name.endsWith(".sh")).map((e2) => resolve(hookDir, e2.name));
2997
2941
  }
2998
2942
 
2999
2943
  // src/commands/install-render.ts
@@ -3167,7 +3111,24 @@ function renderCliArtifacts(log, spec, report) {
3167
3111
  }
3168
3112
  if (report.opencode) {
3169
3113
  log(assetRow("success", "opencode.json", "$schema + 5 keys"));
3170
- log(assetRow("success", ".opencode/commands/", `${report.opencode.commandFiles.length} files`));
3114
+ if (report.opencode.skillFiles.length > 0) {
3115
+ log(
3116
+ assetRow(
3117
+ "success",
3118
+ ".agents/skills/",
3119
+ `${report.opencode.skillFiles.length} dev-method skills (codex\xB7antigravity \uC640 \uAC19\uC740 \uC790\uB9AC)`
3120
+ )
3121
+ );
3122
+ }
3123
+ if (report.opencode.retiredCommands.length > 0) {
3124
+ log(
3125
+ assetRow(
3126
+ "skip",
3127
+ ".opencode/commands/",
3128
+ `${report.opencode.retiredCommands.length} retired \xB7 \uAC19\uC740 \uC2A4\uD0AC\uC774 .agents/skills/ \uB85C \uC62E\uACA8\uC84C\uB2E4 (\uBC31\uC5C5 \uB0A8\uAE40)`
3129
+ )
3130
+ );
3131
+ }
3171
3132
  }
3172
3133
  if (report.antigravity) {
3173
3134
  if (report.antigravity.rulesFile) {
@@ -3900,8 +3861,16 @@ function registerListCommand(cli2) {
3900
3861
  // src/commands/uninstall.ts
3901
3862
  init_esm_shims();
3902
3863
  import { spawnSync as spawnSync2 } from "child_process";
3903
- import { existsSync as existsSync15, readFileSync as readFileSync14, rmSync, writeFileSync as writeFileSync7 } from "fs";
3904
- import { join as join14, resolve as resolve4 } from "path";
3864
+ import {
3865
+ existsSync as existsSync15,
3866
+ lstatSync as lstatSync2,
3867
+ readdirSync as readdirSync5,
3868
+ readFileSync as readFileSync14,
3869
+ rmdirSync,
3870
+ rmSync,
3871
+ writeFileSync as writeFileSync7
3872
+ } from "fs";
3873
+ import { dirname as dirname5, join as join14, relative as relative2, resolve as resolve4, sep as sep2 } from "path";
3905
3874
 
3906
3875
  // src/uninstall-interactive.ts
3907
3876
  init_esm_shims();
@@ -5037,8 +5006,13 @@ function uninstallAction(options, deps = {}) {
5037
5006
  const logSurvives = selectedIds !== null;
5038
5007
  const { succeeded, failed, removedIds } = executeReverse(plan, log, logSurvives);
5039
5008
  if (!keepTemplates) {
5040
- const { rootClaudeMdKept, importStripped } = removeTemplates(installLog, projectDir, rm);
5009
+ const { rootClaudeMdKept, importStripped, external } = removeTemplates(
5010
+ installLog,
5011
+ projectDir,
5012
+ rm
5013
+ );
5041
5014
  log(` ${status.success(`templates removed: ${formatTemplateList(installLog)}`)}`);
5015
+ for (const line of externalRemovalLines(external)) log(line);
5042
5016
  if (importStripped) {
5043
5017
  log(` ${status.success("CLAUDE.md \u2014 harness @import removed (\uBCF8\uBB38 \uBCF4\uC874)")}`);
5044
5018
  }
@@ -5164,6 +5138,7 @@ function dryRunLines(plan, installLog, projectDir, keepTemplates, rootFiles) {
5164
5138
  if (hasRootImport(projectDir)) {
5165
5139
  lines.push(" \u25CB strip harness @import from CLAUDE.md (\uBCF8\uBB38 \uBCF4\uC874)");
5166
5140
  }
5141
+ lines.push(...previewExternalLines(installLog, projectDir));
5167
5142
  }
5168
5143
  lines.push(...advisoryLines(plan, projectDir, rootFiles), "");
5169
5144
  return lines;
@@ -5289,13 +5264,46 @@ function removeTemplates(log, projectDir, rm) {
5289
5264
  rm(join14(projectDir, log.templates.claudeDir));
5290
5265
  if (log.templates.codexDir) rm(join14(projectDir, log.templates.codexDir));
5291
5266
  if (log.templates.opencodeDir) rm(join14(projectDir, log.templates.opencodeDir));
5267
+ const external = removeExternalFiles(log, projectDir, rm);
5292
5268
  const importStripped = stripRootImport(projectDir);
5293
5269
  const rootMd = log.templates.rootClaudeMd;
5294
5270
  if (rootMd) {
5295
- if (rootClaudeMdModified(log, projectDir)) return { rootClaudeMdKept: true, importStripped };
5271
+ if (rootClaudeMdModified(log, projectDir))
5272
+ return { rootClaudeMdKept: true, importStripped, external };
5296
5273
  rm(join14(projectDir, rootMd.path));
5297
5274
  }
5298
- return { rootClaudeMdKept: false, importStripped };
5275
+ return { rootClaudeMdKept: false, importStripped, external };
5276
+ }
5277
+ function removeExternalFiles(log, projectDir, rm) {
5278
+ const removed = [];
5279
+ const kept = [];
5280
+ for (const { path, sha256 } of log.externalFiles ?? []) {
5281
+ const abs = join14(projectDir, path);
5282
+ if (!existsSync15(abs)) continue;
5283
+ if (!lstatSync2(abs).isFile()) continue;
5284
+ if (hashContent(readFileSync14(abs, "utf8")) !== sha256) {
5285
+ kept.push(path);
5286
+ continue;
5287
+ }
5288
+ rm(abs);
5289
+ removed.push(path);
5290
+ }
5291
+ for (const path of removed) pruneEmptyDirsUpward(projectDir, dirname5(join14(projectDir, path)));
5292
+ return { removed, kept };
5293
+ }
5294
+ function pruneEmptyDirsUpward(projectDir, startDir) {
5295
+ let cursor = startDir;
5296
+ while (true) {
5297
+ const rel = relative2(projectDir, cursor);
5298
+ if (rel === "" || rel.startsWith(`..${sep2}`) || rel === "..") return;
5299
+ try {
5300
+ if (readdirSync5(cursor).length > 0) return;
5301
+ rmdirSync(cursor);
5302
+ } catch {
5303
+ return;
5304
+ }
5305
+ cursor = dirname5(cursor);
5306
+ }
5299
5307
  }
5300
5308
  function stripRootImport(projectDir) {
5301
5309
  const current = readRootClaudeMd(projectDir);
@@ -5308,6 +5316,33 @@ function stripRootImport(projectDir) {
5308
5316
  return false;
5309
5317
  }
5310
5318
  }
5319
+ function previewExternalLines(installLog, projectDir) {
5320
+ const lines = [];
5321
+ let removable = 0;
5322
+ for (const { path, sha256 } of installLog.externalFiles ?? []) {
5323
+ const abs = join14(projectDir, path);
5324
+ if (!existsSync15(abs)) continue;
5325
+ if (hashContent(readFileSync14(abs, "utf8")) !== sha256) {
5326
+ lines.push(` \u25CB keep ${path} (modified since install \u2014 preserved)`);
5327
+ continue;
5328
+ }
5329
+ removable += 1;
5330
+ }
5331
+ if (removable > 0) lines.unshift(` \u25CB remove ${removable} CLI output file(s)`);
5332
+ return lines;
5333
+ }
5334
+ function externalRemovalLines(external) {
5335
+ const lines = [];
5336
+ if (external.removed.length > 0) {
5337
+ lines.push(` ${status.success(`CLI outputs removed: ${external.removed.length} file(s)`)}`);
5338
+ }
5339
+ for (const path of external.kept) {
5340
+ lines.push(
5341
+ ` ${c.yellow("\u2298")} ${path} kept \u2014 modified since install. Remove manually if intended.`
5342
+ );
5343
+ }
5344
+ return lines;
5345
+ }
5311
5346
  function hasRootImport(projectDir) {
5312
5347
  const current = readRootClaudeMd(projectDir);
5313
5348
  return current !== null && stripHarnessImport(current) !== null;