@uzysjung/agent-harness 26.168.0 → 26.170.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 CHANGED
@@ -173,7 +173,7 @@ your-project/
173
173
  | `project-management` | PM 업무 | 없음 |
174
174
  | `growth-marketing` | 그로스·콘텐츠 마케팅 | 없음 (`marketingskills` 선택 가능) |
175
175
 
176
- MCP 서버도 함께 들어간다. 모든 트랙에 `context7`(최신 라이브러리 문서)가 들어가고, 웹 트랙(`csr-*`, `ssr-*`, `full`)에는 Google 사용 통계를 끈 `chrome-devtools`가 더해진다. `csr-*`와 `ssr-htmx` 트랙에는 `railway-mcp-server`(Railway 원격 서버 — 처음 쓸 때 로그인한다)가, `csr-supabase`에는 `supabase`가 더해진다. `full`은 둘 다 받는다. `ssr-nextjs`에서는 Railway MCP가 기본이 아니고, 직접 고르면 들어간다. Vercel·GitHub 공식 원격 서버(`vercel`, `github-mcp`)는 미리 체크되지 않는다 — `--with`로 더한다.
176
+ MCP 서버도 함께 들어간다. 모든 트랙에 `context7`(최신 라이브러리 문서)가 들어가고, 웹 트랙(`csr-*`, `ssr-*`, `full`)에는 Google 사용 통계를 끈 `chrome-devtools`가 더해진다. `csr-*`와 `ssr-htmx` 트랙에는 `railway-mcp-server`(Railway 원격 서버 — 처음 쓸 때 로그인한다)가, `csr-supabase`에는 `supabase`(Supabase 원격 서버 — 처음 쓸 때 로그인한다)가 더해진다. `full`은 둘 다 받는다. `ssr-nextjs`에서는 Railway MCP가 기본이 아니고, 직접 고르면 들어간다. Vercel·GitHub 공식 원격 서버(`vercel`, `github-mcp`)는 미리 체크되지 않는다 — `--with`로 더한다. 서버마다 위저드 3단계에 한 줄씩 보이고, 체크를 풀거나 `--without <이름>`(예: `--without context7`)을 주면 `.mcp.json`·Codex·OpenCode 에서 빠진다.
177
177
 
178
178
  미리 체크되지는 않지만 3단계나 `--with <id>`로 더할 수 있는 것도 있다.
179
179
 
@@ -198,7 +198,7 @@ MCP 서버도 함께 들어간다. 모든 트랙에 `context7`(최신 라이브
198
198
 
199
199
  `update`는 하네스가 설치한 것을 새 버전으로 바꾸고, 새 버전에 추가된 것을 넣고, 없어진 하네스 파일(훅 스크립트, `.mcp.json`의 서버, `AGENTS.md`의 하네스 절 등)을 되살린다. `--without`으로 일부러 뺀 항목은 계속 빠져 있고, 고르지 않은 도구는 설치하지 않는다. `update --only skills`처럼 한 묶음만 업데이트할 수도 있다.
200
200
 
201
- 터미널에서 `uninstall`을 실행하면 무엇을 지울지(도구 하나, 고른 항목, 전부) 고를 수 있고, `--dry-run`을 붙이면 지우기 전에 계획을 먼저 보여 준다. 전부 지울 때 `.claude/`, `.codex/`, `.opencode/` 폴더는 삭제하지 않고 `.claude.backup-<시각>`처럼 이름을 바꿔 옆에 남긴다. 그 안에 내가 넣어 둔 파일도 함께 옮겨지니, 필요한 것은 백업 폴더에서 다시 꺼내 쓰면 된다.
201
+ 터미널에서 `uninstall`을 실행하면 무엇을 지울지(도구 하나, 고른 항목, 전부) 고를 수 있고, `--dry-run`을 붙이면 지우기 전에 계획을 먼저 보여 준다. 전부 지울 때는 하네스가 쓴 파일만 하나씩 지운다. `.claude/`, `.codex/`, `.opencode/` 안에 내가 넣어 둔 파일은 제자리에 그대로 남고, 하네스가 대신 자리를 차지했던 내 파일은 원래 자리로 되돌려 놓는다.
202
202
 
203
203
  **이미 진행 중인 프로젝트에 설치해도 된다.** 내가 고친 파일을 바꿔야 할 때는 같은 폴더에 시간이 적힌 백업을 먼저 만들고 그 경로를 알려 준다. 내가 쓰거나 고친 파일은 백업 없이 지우지 않고, 기존 `.mcp.json`의 서버도 덮어쓰지 않고 합친다([기존 프로젝트에 설치하기](docs/USAGE.md#installing-into-an-existing-project)).
204
204
 
package/README.md CHANGED
@@ -166,7 +166,7 @@ A track is a starting setup for a dev stack or a kind of work: the top nine rows
166
166
  | `project-management` | PM work | None |
167
167
  | `growth-marketing` | Growth and content marketing | None (`marketingskills` available) |
168
168
 
169
- MCP servers come too. Every track gets `context7` (current library docs). Web tracks (`csr-*`, `ssr-*`, `full`) add `chrome-devtools`, with Google's usage statistics turned off. The `csr-*` and `ssr-htmx` tracks add `railway-mcp-server` (Railway's remote server — you sign in on first use), and `csr-supabase` adds `supabase`. `full` gets both. On `ssr-nextjs`, Railway MCP is not a default; pick it if you want it. Vercel's and GitHub's official remote servers (`vercel`, `github-mcp`) are never pre-checked — add them with `--with`.
169
+ MCP servers come too. Every track gets `context7` (current library docs). Web tracks (`csr-*`, `ssr-*`, `full`) add `chrome-devtools`, with Google's usage statistics turned off. The `csr-*` and `ssr-htmx` tracks add `railway-mcp-server` (Railway's remote server — you sign in on first use), and `csr-supabase` adds `supabase` (Supabase's remote server — you sign in on first use). `full` gets both. On `ssr-nextjs`, Railway MCP is not a default; pick it if you want it. Vercel's and GitHub's official remote servers (`vercel`, `github-mcp`) are never pre-checked — add them with `--with`. Each server is a row in step 3 of the wizard: uncheck it or pass `--without <name>` (e.g. `--without context7`) to leave it out of `.mcp.json`, Codex and OpenCode.
170
170
 
171
171
  More external assets are never pre-checked but can be added at step 3 or with `--with <id>`:
172
172
 
@@ -191,7 +191,7 @@ Each track's full set is in [docs/TRACKS.md](docs/TRACKS.md). Every asset's sour
191
191
 
192
192
  `update` refreshes what the harness installed, adds what a newer release introduced, and puts back harness files that went missing — a hook script, a server in `.mcp.json`, a section of `AGENTS.md`. What you dropped on purpose with `--without` stays out. It never installs a tool you did not choose. `update --only skills` limits it to one group.
193
193
 
194
- In a terminal, `uninstall` offers three choices (one tool, selected assets, or everything), and `--dry-run` shows the plan first. When you remove everything, `.claude/`, `.codex/`, and `.opencode/` are not deleted but renamed to something like `.claude.backup-<time>`. Files you put in them move along, so take back what you need from the backup.
194
+ In a terminal, `uninstall` offers three choices (one tool, selected assets, or everything), and `--dry-run` shows the plan first. When you remove everything, only the files the harness wrote are removed, one by one; your own files in `.claude/`, `.codex/` and `.opencode/` stay where they are, and a file of yours the harness had replaced is put back.
195
195
 
196
196
  **Safe on an existing project.** Before replacing a file you edited, the harness writes a timestamped backup next to it and prints the path. Nothing you wrote or edited is deleted without a backup beside it, and your existing `.mcp.json` servers are merged, not replaced ([installing into an existing project](docs/USAGE.md#installing-into-an-existing-project)).
197
197
 
@@ -505,6 +505,32 @@ var EXTERNAL_ASSETS = [
505
505
  cliSupportOverride: ["claude", "codex", "opencode"],
506
506
  method: { kind: "internal", key: "chrome-devtools" }
507
507
  },
508
+ // #751 — Context7 MCP. 트랙 표 행의 패턴 `*` 가 모든 트랙의 기본이라 위저드 3단계에 늘 체크된 채 보이고, 풀거나
509
+ // `--without context7` 이면 `.mcp.json` · Codex · OpenCode 세 자리에서 빠진다(#709 · #722 와 같은 경로). 기본값은 그대로다.
510
+ {
511
+ id: "context7",
512
+ tier: "vetted",
513
+ // upstash/context7 62.9k (REPO_OVERRIDE)
514
+ description: "Context7 MCP \u2014 current, version-specific library docs for the agent, in .mcp.json, Codex and OpenCode. Default on every track; uncheck it to leave it out",
515
+ category: "understanding",
516
+ source: "upstash",
517
+ condition: { kind: "opt-in" },
518
+ cliSupportOverride: ["claude", "codex", "opencode"],
519
+ method: { kind: "internal", key: "context7" }
520
+ },
521
+ // #751 — Supabase MCP. 트랙 표 행이 csr-supabase · full 의 기본이고, 이 항목은 그 밖의 트랙에서 고르게 · 기본 트랙에서
522
+ // 빼게 한다. 서버 정의(명령 · 인자)는 트랙 표 한 곳이다.
523
+ {
524
+ id: "supabase",
525
+ tier: "vetted",
526
+ // supabase/mcp 2.9k (REPO_OVERRIDE)
527
+ description: "Supabase's official remote MCP (https://mcp.supabase.com/mcp, signs in with OAuth on first use) in .mcp.json, Codex and OpenCode. Default on csr-supabase \xB7 full; check it to add it on any other track. Only if this project uses Supabase",
528
+ category: "backend",
529
+ source: "supabase",
530
+ condition: { kind: "opt-in" },
531
+ cliSupportOverride: ["claude", "codex", "opencode"],
532
+ method: { kind: "internal", key: "supabase" }
533
+ },
508
534
  // === csr-supabase|full CLI ===
509
535
  {
510
536
  id: "vercel-cli",
@@ -650,7 +676,7 @@ var EXTERNAL_ASSETS = [
650
676
  pluginId: "security-guidance@claude-plugins-official"
651
677
  }
652
678
  },
653
- // v26.110.0 (ADR-039) — context7 플러그인은 **미등록** (검토 후 철회): templates/mcp.json ·
679
+ // v26.110.0 (ADR-039) — context7 플러그인은 **미등록** (검토 후 철회): 트랙 표(#751 전 templates/mcp.json) ·
654
680
  // codex config.toml.template · opencode 설정이 이미 @upstash/context7-mcp 를 기본 wiring —
655
681
  // 플러그인 추가 = 동일 서버 중복 등록이고 도달 범위도 더 좁다(plugin=claude-only vs
656
682
  // 템플릿=claude+codex+opencode). "문서 조회 기본 제공" 요구는 기충족.
@@ -1090,14 +1116,14 @@ init_esm_shims();
1090
1116
 
1091
1117
  // src/install-log.ts
1092
1118
  init_esm_shims();
1093
- import { createHash as createHash2 } from "crypto";
1094
- import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync3, rmSync as rmSync2, statSync, writeFileSync } from "fs";
1119
+ import { createHash } from "crypto";
1120
+ import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync3, rmSync, statSync, writeFileSync } from "fs";
1095
1121
  import { dirname as dirname3, join as join4 } from "path";
1096
1122
 
1097
1123
  // package.json
1098
1124
  var package_default = {
1099
1125
  name: "@uzysjung/agent-harness",
1100
- version: "26.168.0",
1126
+ version: "26.170.0",
1101
1127
  description: "Curate vetted AI-coding skills & plugins by your tech stack \u2014 install only what you need, across Claude Code, Codex, OpenCode & Antigravity",
1102
1128
  type: "module",
1103
1129
  publishConfig: {
@@ -1119,6 +1145,7 @@ var package_default = {
1119
1145
  build: "tsup",
1120
1146
  dev: "tsup --watch",
1121
1147
  "gen:compat": "npm run build && node scripts/gen-compatibility.mjs",
1148
+ "gen:legacy-fingerprints": "node scripts/gen-legacy-fingerprints.mjs",
1122
1149
  test: "vitest run",
1123
1150
  "test:watch": "vitest",
1124
1151
  "test:coverage": "vitest run --coverage",
@@ -1235,20 +1262,13 @@ function targetsInclude(targets, base) {
1235
1262
 
1236
1263
  // src/fs-ops.ts
1237
1264
  init_esm_shims();
1238
- import { createHash } from "crypto";
1239
1265
  import {
1240
1266
  copyFileSync,
1241
1267
  existsSync,
1242
1268
  constants as fsConstants,
1243
1269
  mkdirSync,
1244
1270
  readdirSync,
1245
- readFileSync,
1246
- readlinkSync as readlinkSync2,
1247
- realpathSync as realpathSync2,
1248
- renameSync,
1249
- rmdirSync,
1250
- rmSync,
1251
- symlinkSync
1271
+ readFileSync
1252
1272
  } from "fs";
1253
1273
  import { dirname as dirname2, join as join2 } from "path";
1254
1274
 
@@ -1377,48 +1397,22 @@ function claimBackupPath(base, claim) {
1377
1397
  }
1378
1398
  throw new Error(`Cannot allocate a backup path for ${base} (${MAX_BACKUP_CLAIMS} taken)`);
1379
1399
  }
1380
- function backupDir(target, now = /* @__PURE__ */ new Date()) {
1381
- if (!existsSync(target)) {
1382
- return null;
1383
- }
1384
- const backup = claimBackupPath(`${target}.backup-${formatStamp(now)}`, (c) => mkdirSync(c));
1385
- rmdirSync(backup);
1386
- renameSync(target, backup);
1387
- return backup;
1388
- }
1389
- function copyBackupDir(target, now = /* @__PURE__ */ new Date()) {
1390
- if (!existsSync(target)) {
1391
- return null;
1392
- }
1393
- const backup = claimBackupPath(`${target}.backup-${formatStamp(now)}`, (c) => mkdirSync(c));
1394
- try {
1395
- copyTreePreservingLinks(realpathSync2(target), backup);
1396
- } catch (e) {
1397
- rmSync(backup, { recursive: true, force: true });
1398
- const detail = e instanceof Error ? e.message : String(e);
1399
- throw new Error(`backup of ${target} failed, partial copy removed \u2014 ${detail}`);
1400
- }
1401
- return backup;
1402
- }
1403
- function copyTreePreservingLinks(source, target) {
1404
- mkdirSync(target, { recursive: true });
1405
- for (const entry of readdirSync(source, { withFileTypes: true })) {
1406
- const src = join2(source, entry.name);
1407
- const dst = join2(target, entry.name);
1408
- if (entry.isSymbolicLink()) {
1409
- symlinkSync(readlinkSync2(src), dst);
1410
- continue;
1411
- }
1412
- if (entry.isDirectory()) copyTreePreservingLinks(src, dst);
1413
- else if (entry.isFile()) copyFile(src, dst);
1414
- }
1415
- }
1416
1400
  function backupFile(target, now = /* @__PURE__ */ new Date()) {
1417
1401
  return claimBackupPath(
1418
1402
  `${target}.backup-${formatStamp(now)}`,
1419
1403
  (c) => copyFileSync(target, c, fsConstants.COPYFILE_EXCL)
1420
1404
  );
1421
1405
  }
1406
+ function backupFromCopy(target, source, now = /* @__PURE__ */ new Date()) {
1407
+ mkdirSync(dirname2(target), { recursive: true });
1408
+ return claimBackupPath(
1409
+ `${target}.backup-${formatStamp(now)}`,
1410
+ (c) => copyFileSync(source, c, fsConstants.COPYFILE_EXCL)
1411
+ );
1412
+ }
1413
+ function claimBackupDir(target, now = /* @__PURE__ */ new Date()) {
1414
+ return claimBackupPath(`${target}.backup-${formatStamp(now)}`, (c) => mkdirSync(c));
1415
+ }
1422
1416
  function backupOriginal(backupPath) {
1423
1417
  const m = /^(.+)\.backup-\d{8}T\d{6}(?:-\d+)?$/.exec(backupPath);
1424
1418
  return m?.[1] ?? null;
@@ -1439,44 +1433,20 @@ function listFilesRecursive(dir, prefix = "") {
1439
1433
  function formatStamp(now) {
1440
1434
  return now.toISOString().replace(/[-:]/g, "").replace(/\.\d+Z$/, "Z").slice(0, 15);
1441
1435
  }
1436
+ var PROJECT_SKELETON_DIRS = [
1437
+ ".claude/commands/uzys",
1438
+ ".claude/rules",
1439
+ ".claude/skills",
1440
+ ".claude/agents",
1441
+ ".claude/hooks",
1442
+ "docs/decisions"
1443
+ ];
1442
1444
  function ensureProjectSkeleton(projectDir) {
1443
- const dirs = [
1444
- ".claude/commands/uzys",
1445
- ".claude/rules",
1446
- ".claude/skills",
1447
- ".claude/agents",
1448
- ".claude/hooks",
1449
- "docs/decisions"
1450
- ];
1451
- for (const d of dirs) {
1445
+ for (const d of PROJECT_SKELETON_DIRS) {
1452
1446
  if (outsideProjectTarget(projectDir, join2(projectDir, d)) !== null) continue;
1453
1447
  mkdirSync(join2(projectDir, d), { recursive: true });
1454
1448
  }
1455
1449
  }
1456
- function dirTreesIdentical(a, b) {
1457
- if (!existsSync(a) || !existsSync(b)) return false;
1458
- const signature = (root, rel = "", out = /* @__PURE__ */ new Map()) => {
1459
- for (const e of readdirSync(join2(root, rel), { withFileTypes: true })) {
1460
- const r = rel === "" ? e.name : `${rel}/${e.name}`;
1461
- if (e.isSymbolicLink()) out.set(r, `link:${readlinkSync2(join2(root, r))}`);
1462
- else if (e.isDirectory()) signature(root, r, out);
1463
- else if (e.isFile()) {
1464
- out.set(
1465
- r,
1466
- createHash("sha256").update(readFileSync(join2(root, r))).digest("hex")
1467
- );
1468
- }
1469
- }
1470
- return out;
1471
- };
1472
- const sa = signature(a);
1473
- const sb = signature(b);
1474
- if (sa.size !== sb.size) return false;
1475
- for (const [rel, sha] of sa) {
1476
- if (sb.get(rel) !== sha) return false;
1477
- }
1478
- return true;
1479
- }
1480
1450
  function backupIfLossyUtf8(target, now = /* @__PURE__ */ new Date()) {
1481
1451
  if (!existsSync(target)) return null;
1482
1452
  let raw;
@@ -1693,7 +1663,7 @@ function mergeAssets(previous, current) {
1693
1663
  ];
1694
1664
  }
1695
1665
  function hashContent(content) {
1696
- return createHash2("sha256").update(content, "utf8").digest("hex");
1666
+ return createHash("sha256").update(content, "utf8").digest("hex");
1697
1667
  }
1698
1668
  function isHarnessOwned(baseline, key, current) {
1699
1669
  const recorded = baseline.get(key);
@@ -1753,7 +1723,7 @@ function migrateAwayLegacyLog(projectDir) {
1753
1723
  const legacy = legacyInstallLogPath(projectDir);
1754
1724
  if (!existsSync2(legacy)) return;
1755
1725
  try {
1756
- rmSync2(legacy);
1726
+ rmSync(legacy);
1757
1727
  } catch {
1758
1728
  }
1759
1729
  }
@@ -2268,6 +2238,14 @@ var RETIRED_PATHS = [
2268
2238
  "hooks/task-brief-nudge.sh",
2269
2239
  "hooks/uncommitted-check.sh"
2270
2240
  ];
2241
+ var RETIRED_HOOK_WIRING_REFS = [
2242
+ // 6617766 (2026-04-14, phase4b) 배선 → c8fdef1 (2026-05-03, CL-v2 observer 중복 fire 제거) 제거.
2243
+ // PreToolUse · PostToolUse 각각 matcher "*" 묶음 하나 — `observe.sh pre|post`, async.
2244
+ "skills/continuous-learning-v2/hooks/observe.sh",
2245
+ // 5defc58 (2026-04-14, wire strategic-compact) 배선 → a125453 (2026-09-13, #444 스킬 3종 은퇴) 제거.
2246
+ // PreToolUse 의 protect-files 묶음 안 두 번째 핸들러, async.
2247
+ "skills/strategic-compact/suggest-compact.sh"
2248
+ ];
2271
2249
  var RETIRED_AGENT_IDS = RETIRED_PATHS.filter(
2272
2250
  (p) => p.startsWith("agents/")
2273
2251
  ).map((p) => p.slice("agents/".length).replace(/\.md$/, ""));
@@ -2657,13 +2635,13 @@ export {
2657
2635
  ensureDir,
2658
2636
  copyFile,
2659
2637
  copyDir,
2660
- backupDir,
2661
- copyBackupDir,
2662
2638
  backupFile,
2639
+ backupFromCopy,
2640
+ claimBackupDir,
2663
2641
  backupOriginal,
2664
2642
  listFilesRecursive,
2643
+ PROJECT_SKELETON_DIRS,
2665
2644
  ensureProjectSkeleton,
2666
- dirTreesIdentical,
2667
2645
  backupIfLossyUtf8,
2668
2646
  HARNESS_ANCHOR_FILE,
2669
2647
  HARNESS_IMPORT_LINE,
@@ -2682,6 +2660,7 @@ export {
2682
2660
  TRACK_AGENTS,
2683
2661
  RETIRED_AGENTS,
2684
2662
  RETIRED_PATHS,
2663
+ RETIRED_HOOK_WIRING_REFS,
2685
2664
  RETIRED_AGENT_IDS,
2686
2665
  isCliNeutralTarget,
2687
2666
  buildManifest,
@@ -2693,6 +2672,7 @@ export {
2693
2672
  LEGACY_INSTALL_LOG_DIR,
2694
2673
  POLICY_DIRS,
2695
2674
  installedClis,
2675
+ buildAssetEntries,
2696
2676
  buildInstallLog,
2697
2677
  mergeRootFiles,
2698
2678
  hashContent,
@@ -2723,4 +2703,4 @@ export {
2723
2703
  CATEGORIES,
2724
2704
  CATEGORY_TITLES
2725
2705
  };
2726
- //# sourceMappingURL=chunk-V4UY222W.js.map
2706
+ //# sourceMappingURL=chunk-MGFWOWF4.js.map