@uzysjung/agent-harness 26.167.0 → 26.169.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.
Files changed (33) hide show
  1. package/README.ko.md +2 -2
  2. package/README.md +2 -2
  3. package/dist/{chunk-KVXCTS6D.js → chunk-SU2RUN2W.js} +116 -88
  4. package/dist/chunk-SU2RUN2W.js.map +1 -0
  5. package/dist/index.js +1643 -966
  6. package/dist/index.js.map +1 -1
  7. package/dist/trust-tier-drift.js +5 -2
  8. package/dist/trust-tier-drift.js.map +1 -1
  9. package/package.json +1 -1
  10. package/templates/CLAUDE.md +7 -0
  11. package/templates/mcp.json +2 -8
  12. package/templates/rules/ship-checklist.md +2 -2
  13. package/templates/skills/audit-harness-fit/SKILL.md +15 -4
  14. package/templates/skills/audit-harness-fit/references/apply.md +18 -0
  15. package/templates/skills/audit-harness-fit/references/audit.md +16 -3
  16. package/templates/skills/audit-harness-fit/references/verification.md +7 -0
  17. package/templates/skills/chatgpt-delegate/SKILL.md +221 -0
  18. package/templates/skills/external-model-consult/SKILL.md +82 -40
  19. package/templates/skills/external-model-consult/references/direct-calls.md +23 -14
  20. package/templates/skills/external-model-consult/references/model-evidence.md +156 -0
  21. package/templates/skills/external-model-consult/scripts/codex-ask.sh +4 -3
  22. package/templates/skills/external-model-consult/scripts/gemini-ask.sh +3 -3
  23. package/templates/skills/gemini-delegate/SKILL.md +223 -0
  24. package/templates/skills/genspark-delegate/SKILL.md +283 -0
  25. package/templates/skills/model-orchestration/SKILL.md +5 -0
  26. package/templates/skills/multi-model-debate/SKILL.md +187 -0
  27. package/templates/skills/multi-model-debate/agents/openai.yaml +4 -0
  28. package/templates/skills/multi-model-debate/references/adapters.md +143 -0
  29. package/templates/skills/multi-model-debate/references/evaluation.md +80 -0
  30. package/templates/skills/multi-model-debate/references/prompts.md +87 -0
  31. package/templates/skills/multi-model-debate/references/research.md +171 -0
  32. package/templates/track-mcp-map.tsv +3 -1
  33. package/dist/chunk-KVXCTS6D.js.map +0 -1
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
 
@@ -280,6 +280,53 @@ var EXTERNAL_ASSETS = [
280
280
  condition: { kind: "has-dev-track" },
281
281
  method: { kind: "internal", key: "external-model-consult" }
282
282
  },
283
+ {
284
+ // #729 (ADR-109) — 토론 정책(참여자 · 독립성 · 라운드 · 예산 · 재시도 · 대체 · 복귀 · 통합)은 이 스킬만
285
+ // 소유한다. 하위 경로(external-model-consult · 브라우저 위임 3종)는 실행 매개변수와 결과·상태만 맡는다.
286
+ // has-dev-track 인 이유: 브라우저 전제가 없다 — external-model-consult 의 CLI 둘만으로 "서로 다른 실제 모델 2개"가
287
+ // 성립하고, 없는 경로는 실행 시점에 unavailable 로 기록한다. DEV_METHOD 에 넣지 않는다(수단 — ADR-105 D2).
288
+ id: "multi-model-debate",
289
+ tier: "official",
290
+ // uzys 자사 스킬
291
+ description: "Multi-model debate \u2014 two or three different real models answer independently, then only decision-relevant disagreements are cross-examined and checked; uses whichever installed routes are available (external-model-consult CLIs or the browser delegates)",
292
+ category: "dev-tools",
293
+ source: "uzys",
294
+ condition: { kind: "has-dev-track" },
295
+ method: { kind: "internal", key: "multi-model-debate" }
296
+ },
297
+ // #729 (ADR-109, 사용자 결정 2026-10-12) — 브라우저 위임 3종은 opt-in. 실행 전제(브라우저 도구 · 로그인된 브라우저 ·
298
+ // 그 서비스 계정)가 하네스 밖에 있고 Codex CLI 에는 브라우저가 없다(공식 문서). #489 가 agent-browser 를 opt-in 으로
299
+ // 내린 축과 같다. 설치는 디렉터리 복사뿐이다 — `--with <id>` · 위저드 체크로 켠다.
300
+ {
301
+ id: "gemini-delegate",
302
+ tier: "official",
303
+ // uzys 자사 스킬
304
+ description: "Gemini delegate \u2014 hand design, review, or Korean polishing to Gemini on the web and collect the answer (needs a browser tool + your logged-in browser)",
305
+ category: "dev-tools",
306
+ source: "uzys",
307
+ condition: { kind: "opt-in" },
308
+ method: { kind: "internal", key: "gemini-delegate" }
309
+ },
310
+ {
311
+ id: "chatgpt-delegate",
312
+ tier: "official",
313
+ // uzys 자사 스킬
314
+ description: "ChatGPT delegate \u2014 hand design, planning, or review to ChatGPT on the web and collect the answer (needs a browser tool + your logged-in browser)",
315
+ category: "dev-tools",
316
+ source: "uzys",
317
+ condition: { kind: "opt-in" },
318
+ method: { kind: "internal", key: "chatgpt-delegate" }
319
+ },
320
+ {
321
+ id: "genspark-delegate",
322
+ tier: "official",
323
+ // uzys 자사 스킬
324
+ description: "Genspark delegate \u2014 hand design or planning to Genspark AI Chat, or generate and download images with AI Image (needs a browser tool + your logged-in browser)",
325
+ category: "dev-tools",
326
+ source: "uzys",
327
+ condition: { kind: "opt-in" },
328
+ method: { kind: "internal", key: "genspark-delegate" }
329
+ },
283
330
  {
284
331
  // 복원 9종이 아니라 신설이다 — 이관 이력이 없다.
285
332
  // 전 트랙인 이유: 위임과 요청 정규화는 개발 트랙의 행위가 아니라 **전 트랙의 행위**다.
@@ -458,6 +505,32 @@ var EXTERNAL_ASSETS = [
458
505
  cliSupportOverride: ["claude", "codex", "opencode"],
459
506
  method: { kind: "internal", key: "chrome-devtools" }
460
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
+ },
461
534
  // === csr-supabase|full CLI ===
462
535
  {
463
536
  id: "vercel-cli",
@@ -603,7 +676,7 @@ var EXTERNAL_ASSETS = [
603
676
  pluginId: "security-guidance@claude-plugins-official"
604
677
  }
605
678
  },
606
- // v26.110.0 (ADR-039) — context7 플러그인은 **미등록** (검토 후 철회): templates/mcp.json ·
679
+ // v26.110.0 (ADR-039) — context7 플러그인은 **미등록** (검토 후 철회): 트랙 표(#751 전 templates/mcp.json) ·
607
680
  // codex config.toml.template · opencode 설정이 이미 @upstash/context7-mcp 를 기본 wiring —
608
681
  // 플러그인 추가 = 동일 서버 중복 등록이고 도달 범위도 더 좁다(plugin=claude-only vs
609
682
  // 템플릿=claude+codex+opencode). "문서 조회 기본 제공" 요구는 기충족.
@@ -935,7 +1008,12 @@ var INTERNAL_BUNDLED_SKILL_IDS = [
935
1008
  "model-orchestration",
936
1009
  "external-model-consult",
937
1010
  "self-hosted-github-runner",
938
- "natural-korean"
1011
+ "natural-korean",
1012
+ // #729 (ADR-109) — 토론(개발 트랙 기본) + 브라우저 위임 3종(opt-in). condition 이 설치를 가른다.
1013
+ "multi-model-debate",
1014
+ "gemini-delegate",
1015
+ "chatgpt-delegate",
1016
+ "genspark-delegate"
939
1017
  ];
940
1018
  var CONTINUOUS_SKILLS = [
941
1019
  {
@@ -1038,14 +1116,14 @@ init_esm_shims();
1038
1116
 
1039
1117
  // src/install-log.ts
1040
1118
  init_esm_shims();
1041
- import { createHash as createHash2 } from "crypto";
1042
- 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 as writeFileSync2 } from "fs";
1043
1121
  import { dirname as dirname3, join as join4 } from "path";
1044
1122
 
1045
1123
  // package.json
1046
1124
  var package_default = {
1047
1125
  name: "@uzysjung/agent-harness",
1048
- version: "26.167.0",
1126
+ version: "26.169.0",
1049
1127
  description: "Curate vetted AI-coding skills & plugins by your tech stack \u2014 install only what you need, across Claude Code, Codex, OpenCode & Antigravity",
1050
1128
  type: "module",
1051
1129
  publishConfig: {
@@ -1183,7 +1261,6 @@ function targetsInclude(targets, base) {
1183
1261
 
1184
1262
  // src/fs-ops.ts
1185
1263
  init_esm_shims();
1186
- import { createHash } from "crypto";
1187
1264
  import {
1188
1265
  copyFileSync,
1189
1266
  existsSync,
@@ -1191,12 +1268,7 @@ import {
1191
1268
  mkdirSync,
1192
1269
  readdirSync,
1193
1270
  readFileSync,
1194
- readlinkSync as readlinkSync2,
1195
- realpathSync as realpathSync2,
1196
- renameSync,
1197
- rmdirSync,
1198
- rmSync,
1199
- symlinkSync
1271
+ writeFileSync
1200
1272
  } from "fs";
1201
1273
  import { dirname as dirname2, join as join2 } from "path";
1202
1274
 
@@ -1325,48 +1397,19 @@ function claimBackupPath(base, claim) {
1325
1397
  }
1326
1398
  throw new Error(`Cannot allocate a backup path for ${base} (${MAX_BACKUP_CLAIMS} taken)`);
1327
1399
  }
1328
- function backupDir(target, now = /* @__PURE__ */ new Date()) {
1329
- if (!existsSync(target)) {
1330
- return null;
1331
- }
1332
- const backup = claimBackupPath(`${target}.backup-${formatStamp(now)}`, (c) => mkdirSync(c));
1333
- rmdirSync(backup);
1334
- renameSync(target, backup);
1335
- return backup;
1336
- }
1337
- function copyBackupDir(target, now = /* @__PURE__ */ new Date()) {
1338
- if (!existsSync(target)) {
1339
- return null;
1340
- }
1341
- const backup = claimBackupPath(`${target}.backup-${formatStamp(now)}`, (c) => mkdirSync(c));
1342
- try {
1343
- copyTreePreservingLinks(realpathSync2(target), backup);
1344
- } catch (e) {
1345
- rmSync(backup, { recursive: true, force: true });
1346
- const detail = e instanceof Error ? e.message : String(e);
1347
- throw new Error(`backup of ${target} failed, partial copy removed \u2014 ${detail}`);
1348
- }
1349
- return backup;
1350
- }
1351
- function copyTreePreservingLinks(source, target) {
1352
- mkdirSync(target, { recursive: true });
1353
- for (const entry of readdirSync(source, { withFileTypes: true })) {
1354
- const src = join2(source, entry.name);
1355
- const dst = join2(target, entry.name);
1356
- if (entry.isSymbolicLink()) {
1357
- symlinkSync(readlinkSync2(src), dst);
1358
- continue;
1359
- }
1360
- if (entry.isDirectory()) copyTreePreservingLinks(src, dst);
1361
- else if (entry.isFile()) copyFile(src, dst);
1362
- }
1363
- }
1364
1400
  function backupFile(target, now = /* @__PURE__ */ new Date()) {
1365
1401
  return claimBackupPath(
1366
1402
  `${target}.backup-${formatStamp(now)}`,
1367
1403
  (c) => copyFileSync(target, c, fsConstants.COPYFILE_EXCL)
1368
1404
  );
1369
1405
  }
1406
+ function backupContent(target, content, now = /* @__PURE__ */ new Date()) {
1407
+ mkdirSync(dirname2(target), { recursive: true });
1408
+ return claimBackupPath(
1409
+ `${target}.backup-${formatStamp(now)}`,
1410
+ (c) => writeFileSync(c, content, { flag: "wx" })
1411
+ );
1412
+ }
1370
1413
  function backupOriginal(backupPath) {
1371
1414
  const m = /^(.+)\.backup-\d{8}T\d{6}(?:-\d+)?$/.exec(backupPath);
1372
1415
  return m?.[1] ?? null;
@@ -1387,44 +1430,20 @@ function listFilesRecursive(dir, prefix = "") {
1387
1430
  function formatStamp(now) {
1388
1431
  return now.toISOString().replace(/[-:]/g, "").replace(/\.\d+Z$/, "Z").slice(0, 15);
1389
1432
  }
1433
+ var PROJECT_SKELETON_DIRS = [
1434
+ ".claude/commands/uzys",
1435
+ ".claude/rules",
1436
+ ".claude/skills",
1437
+ ".claude/agents",
1438
+ ".claude/hooks",
1439
+ "docs/decisions"
1440
+ ];
1390
1441
  function ensureProjectSkeleton(projectDir) {
1391
- const dirs = [
1392
- ".claude/commands/uzys",
1393
- ".claude/rules",
1394
- ".claude/skills",
1395
- ".claude/agents",
1396
- ".claude/hooks",
1397
- "docs/decisions"
1398
- ];
1399
- for (const d of dirs) {
1442
+ for (const d of PROJECT_SKELETON_DIRS) {
1400
1443
  if (outsideProjectTarget(projectDir, join2(projectDir, d)) !== null) continue;
1401
1444
  mkdirSync(join2(projectDir, d), { recursive: true });
1402
1445
  }
1403
1446
  }
1404
- function dirTreesIdentical(a, b) {
1405
- if (!existsSync(a) || !existsSync(b)) return false;
1406
- const signature = (root, rel = "", out = /* @__PURE__ */ new Map()) => {
1407
- for (const e of readdirSync(join2(root, rel), { withFileTypes: true })) {
1408
- const r = rel === "" ? e.name : `${rel}/${e.name}`;
1409
- if (e.isSymbolicLink()) out.set(r, `link:${readlinkSync2(join2(root, r))}`);
1410
- else if (e.isDirectory()) signature(root, r, out);
1411
- else if (e.isFile()) {
1412
- out.set(
1413
- r,
1414
- createHash("sha256").update(readFileSync(join2(root, r))).digest("hex")
1415
- );
1416
- }
1417
- }
1418
- return out;
1419
- };
1420
- const sa = signature(a);
1421
- const sb = signature(b);
1422
- if (sa.size !== sb.size) return false;
1423
- for (const [rel, sha] of sa) {
1424
- if (sb.get(rel) !== sha) return false;
1425
- }
1426
- return true;
1427
- }
1428
1447
  function backupIfLossyUtf8(target, now = /* @__PURE__ */ new Date()) {
1429
1448
  if (!existsSync(target)) return null;
1430
1449
  let raw;
@@ -1641,7 +1660,7 @@ function mergeAssets(previous, current) {
1641
1660
  ];
1642
1661
  }
1643
1662
  function hashContent(content) {
1644
- return createHash2("sha256").update(content, "utf8").digest("hex");
1663
+ return createHash("sha256").update(content, "utf8").digest("hex");
1645
1664
  }
1646
1665
  function isHarnessOwned(baseline, key, current) {
1647
1666
  const recorded = baseline.get(key);
@@ -1679,7 +1698,7 @@ function writeInstallLog(projectDir, log) {
1679
1698
  const path2 = installLogPath(projectDir);
1680
1699
  mkdirSync2(dirname3(path2), { recursive: true });
1681
1700
  const marked = { ...normalizeLogOrder(log), excludedKeysMigrated: true };
1682
- writeFileSync(path2, `${JSON.stringify(marked, null, 2)}
1701
+ writeFileSync2(path2, `${JSON.stringify(marked, null, 2)}
1683
1702
  `, "utf8");
1684
1703
  migrateAwayLegacyLog(projectDir);
1685
1704
  return path2;
@@ -1701,7 +1720,7 @@ function migrateAwayLegacyLog(projectDir) {
1701
1720
  const legacy = legacyInstallLogPath(projectDir);
1702
1721
  if (!existsSync2(legacy)) return;
1703
1722
  try {
1704
- rmSync2(legacy);
1723
+ rmSync(legacy);
1705
1724
  } catch {
1706
1725
  }
1707
1726
  }
@@ -2216,6 +2235,14 @@ var RETIRED_PATHS = [
2216
2235
  "hooks/task-brief-nudge.sh",
2217
2236
  "hooks/uncommitted-check.sh"
2218
2237
  ];
2238
+ var RETIRED_HOOK_WIRING_REFS = [
2239
+ // 6617766 (2026-04-14, phase4b) 배선 → c8fdef1 (2026-05-03, CL-v2 observer 중복 fire 제거) 제거.
2240
+ // PreToolUse · PostToolUse 각각 matcher "*" 묶음 하나 — `observe.sh pre|post`, async.
2241
+ "skills/continuous-learning-v2/hooks/observe.sh",
2242
+ // 5defc58 (2026-04-14, wire strategic-compact) 배선 → a125453 (2026-09-13, #444 스킬 3종 은퇴) 제거.
2243
+ // PreToolUse 의 protect-files 묶음 안 두 번째 핸들러, async.
2244
+ "skills/strategic-compact/suggest-compact.sh"
2245
+ ];
2219
2246
  var RETIRED_AGENT_IDS = RETIRED_PATHS.filter(
2220
2247
  (p) => p.startsWith("agents/")
2221
2248
  ).map((p) => p.slice("agents/".length).replace(/\.md$/, ""));
@@ -2605,13 +2632,12 @@ export {
2605
2632
  ensureDir,
2606
2633
  copyFile,
2607
2634
  copyDir,
2608
- backupDir,
2609
- copyBackupDir,
2610
2635
  backupFile,
2636
+ backupContent,
2611
2637
  backupOriginal,
2612
2638
  listFilesRecursive,
2639
+ PROJECT_SKELETON_DIRS,
2613
2640
  ensureProjectSkeleton,
2614
- dirTreesIdentical,
2615
2641
  backupIfLossyUtf8,
2616
2642
  HARNESS_ANCHOR_FILE,
2617
2643
  HARNESS_IMPORT_LINE,
@@ -2630,6 +2656,7 @@ export {
2630
2656
  TRACK_AGENTS,
2631
2657
  RETIRED_AGENTS,
2632
2658
  RETIRED_PATHS,
2659
+ RETIRED_HOOK_WIRING_REFS,
2633
2660
  RETIRED_AGENT_IDS,
2634
2661
  isCliNeutralTarget,
2635
2662
  buildManifest,
@@ -2641,6 +2668,7 @@ export {
2641
2668
  LEGACY_INSTALL_LOG_DIR,
2642
2669
  POLICY_DIRS,
2643
2670
  installedClis,
2671
+ buildAssetEntries,
2644
2672
  buildInstallLog,
2645
2673
  mergeRootFiles,
2646
2674
  hashContent,
@@ -2671,4 +2699,4 @@ export {
2671
2699
  CATEGORIES,
2672
2700
  CATEGORY_TITLES
2673
2701
  };
2674
- //# sourceMappingURL=chunk-KVXCTS6D.js.map
2702
+ //# sourceMappingURL=chunk-SU2RUN2W.js.map