@uzysjung/agent-harness 26.146.1 → 26.147.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.
Files changed (36) hide show
  1. package/README.ko.md +5 -5
  2. package/README.md +3 -3
  3. package/dist/{chunk-OEIGA5IL.js → chunk-B27AJIWG.js} +459 -53
  4. package/dist/chunk-B27AJIWG.js.map +1 -0
  5. package/dist/index.js +727 -552
  6. package/dist/index.js.map +1 -1
  7. package/dist/trust-tier-drift.js +1 -1
  8. package/package.json +1 -1
  9. package/templates/CLAUDE.md +14 -5
  10. package/templates/agents/implementer.md +1 -1
  11. package/templates/codex/AGENTS.md.template +11 -0
  12. package/templates/opencode/AGENTS.md.template +8 -1
  13. package/templates/opencode/opencode.json.template +0 -1
  14. package/templates/rules/cli-development.md +2 -2
  15. package/templates/rules/doc-governance.md +3 -1
  16. package/templates/rules/test-policy.md +5 -6
  17. package/templates/scripts/check-absence.sh +205 -0
  18. package/templates/settings.json +0 -9
  19. package/templates/skills/gh-issue-workflow/ISSUE.template.md +111 -35
  20. package/templates/skills/gh-issue-workflow/SKILL.md +268 -138
  21. package/templates/skills/gh-issue-workflow/references/hierarchy.md +261 -0
  22. package/templates/skills/gh-issue-workflow/references/operating-model.md +132 -0
  23. package/templates/skills/gh-issue-workflow/references/readiness.md +121 -0
  24. package/templates/skills/ui-visual-review/SKILL.md +17 -3
  25. package/dist/chunk-OEIGA5IL.js.map +0 -1
  26. package/templates/commands/ecc/checkpoint.md +0 -32
  27. package/templates/commands/ecc/e2e.md +0 -105
  28. package/templates/commands/ecc/eval.md +0 -88
  29. package/templates/commands/ecc/evolve.md +0 -7
  30. package/templates/commands/ecc/harness-audit.md +0 -73
  31. package/templates/commands/ecc/instinct-status.md +0 -8
  32. package/templates/commands/ecc/promote.md +0 -10
  33. package/templates/commands/ecc/security-scan.md +0 -10
  34. package/templates/hooks/mcp-pre-exec.sh +0 -122
  35. package/templates/mcp-allowlist.example +0 -24
  36. package/templates/rules/playwright-launch.md +0 -16
@@ -229,7 +229,7 @@ var EXTERNAL_ASSETS = [
229
229
  id: "gh-issue-workflow",
230
230
  tier: "official",
231
231
  // uzys 자사 스킬 리포
232
- description: "GitHub issue workflow \u2014 investigate, draft, create, implement, verify, and close issue-backed work while keeping read-only, draft, external-write, and status stages distinct",
232
+ description: "GitHub issue workflow \u2014 run work through issues so user and agent share one backlog: group tasks under a parent (epic) issue, order them with milestones and labels, read comments before starting, promote decisions into the body, and keep read-only, draft, external-write, and status stages distinct",
233
233
  category: "workflow",
234
234
  source: "uzys",
235
235
  // 전 트랙 상주 보존 — north-star 와 같은 이유 (이관 전 COMMON_SKILL_DIRS).
@@ -499,6 +499,27 @@ var EXTERNAL_ASSETS = [
499
499
  condition: { kind: "opt-in" },
500
500
  method: { kind: "skill", source: "oso95/scroll-world", skill: "scroll-world" }
501
501
  },
502
+ // 2026-08-16 — Preline 은 Tailwind 컴포넌트 킷이지만 **프레임워크에 매이지 않는다**: htmx·
503
+ // vanilla 에도 붙는다(사용자 확인). 그래서 `frontend` 이면서 React/Vue 트랙 전용이 아니다.
504
+ //
505
+ // 리포 안에 스킬은 `skills/theme-generator/` **하나**다 — 공식 문서는 이것을
506
+ // "preline-theme-generator" 라 부르지만 실제 디렉터리명은 `theme-generator` 이고,
507
+ // `npx skills add` 가 보는 것은 디렉터리명이다(실측 2026-08-16). 문서 표기를 그대로 옮겼다면
508
+ // 설치가 조용히 빗나갔다.
509
+ //
510
+ // 같은 날 조사한 flowbite 는 **넣지 않는다**: 에이전트용 제공물이 MCP 서버뿐인데, 이 저장소의
511
+ // `.mcp.json` 조립은 트랙 조건만 읽어 opt-in 경로가 없다 — 넣으면 해당 트랙 전원에게 항상
512
+ // 켜진다. 안 쓰는 사람에게 MCP 툴 스키마만큼의 상주 비용을 물리는 형태라 기각(사용자 확정).
513
+ {
514
+ id: "preline",
515
+ tier: "vetted",
516
+ // htmlstreamofficial/preline 6,386 (2026-08-16)
517
+ description: "Preline theme generator \u2014 turns a brand description into a Preline UI Tailwind theme (tokens, dark mode, component overrides); framework-agnostic, works with plain HTML and htmx",
518
+ category: "frontend",
519
+ source: "htmlstreamofficial",
520
+ condition: { kind: "opt-in" },
521
+ method: { kind: "skill", source: "htmlstreamofficial/preline", skill: "theme-generator" }
522
+ },
502
523
  // === dev tools (has_dev_track) ===
503
524
  // v26.110.0 (ADR-039) — code-review: /code-review 커맨드 1개 (다중 에이전트 confidence
504
525
  // 스코어링 PR 리뷰). 기본 리뷰 스택(reviewer·code-reviewer·security-reviewer)과 표면 중복
@@ -783,6 +804,51 @@ var EXTERNAL_ASSETS = [
783
804
  skill: "web-design-guidelines"
784
805
  }
785
806
  },
807
+ {
808
+ // 2026-08-17 (사용자 지정) — 웹 게임 개발. **웹 전용이다**: 스킬 본문 frontmatter 가
809
+ // "HTML5, Canvas, WebGL, and JavaScript" 로 스스로 범위를 적고 있고 프레임워크도
810
+ // Phaser · Three.js · Babylon.js · A-Frame 이다. Unity/Unreal/Godot 은 안 덮으므로
811
+ // description 에 "web" 을 남긴다 — 엔진 이름을 지우면 네이티브 엔진까지 되는 것처럼 읽힌다.
812
+ // opt-in 인 이유: frontend 트랙 사용자 대부분은 게임을 만들지 않는다. 게임 트랙은 아직 없다.
813
+ id: "game-engine",
814
+ tier: "vetted",
815
+ // github/awesome-copilot 37,909★ (MIT, 2026-08-17 `gh api` 실측 · GitHub 공식 조직)
816
+ description: "Game engine \u2014 web game development with HTML5 Canvas \xB7 WebGL (Phaser \xB7 Three.js \xB7 Babylon.js): game loop, physics, collision, sprites, tilemaps, gamepad input, WebRTC multiplayer",
817
+ category: "frontend",
818
+ source: "github",
819
+ condition: { kind: "opt-in" },
820
+ method: {
821
+ kind: "skill",
822
+ source: "https://github.com/github/awesome-copilot",
823
+ skill: "game-engine"
824
+ }
825
+ },
826
+ {
827
+ // 2026-08-17 (사용자 지정) — 게임 스튜디오 **프로세스** 스킬 73종.
828
+ //
829
+ // description 이 두 가지를 반드시 말해야 하는 이유 (둘 다 Docker 실측):
830
+ // ① **에이전트는 안 깔린다.** 리포의 엔진 전문성(`godot-specialist`·`unity-specialist`·
831
+ // `unreal-specialist`)은 49개 *에이전트*에 있는데 `npx skills add` 는 `.claude/skills/`
832
+ // 만 가져온다(실측: 73 SKILL.md, settings.json·rules·hooks 미설치). "Unity 되나?" 로
833
+ // 고른 사용자가 스프린트 계획 스킬을 받게 되므로 안 적으면 그게 거짓 광고다.
834
+ // ② **상주 ~4,440 tok/세션.** 73 descriptor 실측(17,760자/4). tooling 트랙 baseline 전체가
835
+ // ~5,037 tok 이라 이 자산 하나가 상주를 거의 두 배로 만든다 — 이 저장소의 1차 지표를
836
+ // 가장 크게 움직이는 자산이라 선택 시점에 숫자가 보여야 한다.
837
+ //
838
+ // 저자의 공식 설치법은 `git clone` (프로젝트 템플릿)이다. 그 경로는 `.claude/settings.json`·
839
+ // rules·hooks 를 함께 들여와 우리 하네스와 같은 자리를 다투므로 카탈로그에 넣지 않는다.
840
+ id: "game-studios",
841
+ tier: "vetted",
842
+ // Donchitos/Claude-Code-Game-Studios 23,924★ (MIT · 최종 push 2026-05-21, 2026-08-17 `gh api` 실측)
843
+ description: "Game studio workflow \u2014 73 process skills (sprint-plan \xB7 qa-plan \xB7 playtest-report \xB7 release-checklist \xB7 art-bible \xB7 balance-check). Skills only: the repo's 49 engine agents (godot/unity/unreal-specialist) are NOT installed. ~4,440 tok/session resident",
844
+ category: "workflow",
845
+ source: "Donchitos",
846
+ condition: { kind: "opt-in" },
847
+ method: {
848
+ kind: "skill",
849
+ source: "https://github.com/Donchitos/Claude-Code-Game-Studios"
850
+ }
851
+ },
786
852
  // === Executive tracks ===
787
853
  {
788
854
  id: "anthropic-document-skills",
@@ -993,35 +1059,321 @@ function filterApplicableAssets(assets, ctx) {
993
1059
  return assets.filter((a) => shouldInstallAsset(a, ctx));
994
1060
  }
995
1061
 
996
- // src/categories.ts
1062
+ // src/manifest.ts
997
1063
  init_esm_shims();
998
- var CATEGORIES = [
999
- "frontend",
1000
- "backend",
1001
- "data",
1002
- "business",
1003
- "dev-tools",
1004
- "understanding",
1005
- "visual-media",
1006
- "workflow",
1007
- "ecc-suite"
1008
- ];
1009
- var CATEGORY_TITLES = {
1010
- frontend: "\u{1F3A8} Frontend (UI \xB7 Design)",
1011
- backend: "\u{1F5C4}\uFE0F Backend (API \xB7 DB \xB7 Deploy)",
1012
- data: "\u{1F4CA} Data",
1013
- business: "\u{1F4BC} Business (Documents)",
1014
- "dev-tools": "\u{1F6E1}\uFE0F Dev Tools (Security \xB7 Quality)",
1015
- // v26.78.0 — 에이전트 인지 증강: 환경에 대한 지각(영상·웹·코드) + 기억(memory).
1016
- understanding: "\u{1F9E0} Understanding (Perception \xB7 Memory)",
1017
- // v26.85.0 — 코드-퍼스트 비주얼/미디어 제작: 슬라이드·다이어그램·모션·동영상.
1018
- "visual-media": "\u{1F3AC} Visual & Media (Slides \xB7 Diagrams \xB7 Motion \xB7 Video)",
1019
- workflow: "\u{1F504} Workflow (Development Cycle)",
1020
- "ecc-suite": "\u{1F4E6} ECC Suite"
1021
- };
1022
1064
 
1023
- // src/manifest.ts
1065
+ // src/install-log.ts
1024
1066
  init_esm_shims();
1067
+ import { createHash } from "crypto";
1068
+ import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, rmSync, writeFileSync } from "fs";
1069
+ import { dirname as dirname2, join as join2 } from "path";
1070
+
1071
+ // src/fs-ops.ts
1072
+ init_esm_shims();
1073
+ import {
1074
+ copyFileSync,
1075
+ cpSync,
1076
+ existsSync,
1077
+ constants as fsConstants,
1078
+ mkdirSync,
1079
+ readdirSync,
1080
+ readFileSync,
1081
+ realpathSync,
1082
+ renameSync,
1083
+ rmdirSync
1084
+ } from "fs";
1085
+ import { dirname, join } from "path";
1086
+ function ensureDir(path2) {
1087
+ mkdirSync(path2, { recursive: true });
1088
+ }
1089
+ function copyFile(source, target) {
1090
+ if (!existsSync(source)) {
1091
+ throw new Error(`Source not found: ${source}`);
1092
+ }
1093
+ mkdirSync(dirname(target), { recursive: true });
1094
+ copyFileSync(source, target);
1095
+ }
1096
+ function copyDir(source, target, foreignOf) {
1097
+ if (!existsSync(source)) {
1098
+ throw new Error(`Source dir not found: ${source}`);
1099
+ }
1100
+ mkdirSync(target, { recursive: true });
1101
+ const skipped = [];
1102
+ for (const rel of listFilesRecursive(source)) {
1103
+ const foreign = foreignOf(rel);
1104
+ if (foreign !== null) {
1105
+ skipped.push(foreign);
1106
+ continue;
1107
+ }
1108
+ copyFile(join(source, rel), join(target, rel));
1109
+ }
1110
+ return skipped;
1111
+ }
1112
+ var MAX_BACKUP_CLAIMS = 100;
1113
+ function claimBackupPath(base, claim) {
1114
+ for (let n = 1; n <= MAX_BACKUP_CLAIMS; n++) {
1115
+ const candidate = n === 1 ? base : `${base}-${n}`;
1116
+ try {
1117
+ claim(candidate);
1118
+ return candidate;
1119
+ } catch (err) {
1120
+ if (err.code !== "EEXIST") throw err;
1121
+ }
1122
+ }
1123
+ throw new Error(`Cannot allocate a backup path for ${base} (${MAX_BACKUP_CLAIMS} taken)`);
1124
+ }
1125
+ function backupDir(target, now = /* @__PURE__ */ new Date()) {
1126
+ if (!existsSync(target)) {
1127
+ return null;
1128
+ }
1129
+ const backup = claimBackupPath(`${target}.backup-${formatStamp(now)}`, (c) => mkdirSync(c));
1130
+ rmdirSync(backup);
1131
+ renameSync(target, backup);
1132
+ return backup;
1133
+ }
1134
+ function copyBackupDir(target, now = /* @__PURE__ */ new Date()) {
1135
+ if (!existsSync(target)) {
1136
+ return null;
1137
+ }
1138
+ const backup = claimBackupPath(`${target}.backup-${formatStamp(now)}`, (c) => mkdirSync(c));
1139
+ cpSync(realpathSync(target), backup, { recursive: true });
1140
+ return backup;
1141
+ }
1142
+ function backupFileIfChanged(target, newContent, now = /* @__PURE__ */ new Date()) {
1143
+ if (!existsSync(target)) {
1144
+ return null;
1145
+ }
1146
+ if (readFileSync(target, "utf-8") === newContent) {
1147
+ return null;
1148
+ }
1149
+ return backupFile(target, now);
1150
+ }
1151
+ function backupFile(target, now = /* @__PURE__ */ new Date()) {
1152
+ return claimBackupPath(
1153
+ `${target}.backup-${formatStamp(now)}`,
1154
+ (c) => copyFileSync(target, c, fsConstants.COPYFILE_EXCL)
1155
+ );
1156
+ }
1157
+ function listFilesRecursive(dir, prefix = "") {
1158
+ if (!existsSync(dir)) return [];
1159
+ const out = [];
1160
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
1161
+ const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
1162
+ if (entry.isDirectory()) {
1163
+ out.push(...listFilesRecursive(join(dir, entry.name), rel));
1164
+ } else if (entry.isFile()) {
1165
+ out.push(rel);
1166
+ }
1167
+ }
1168
+ return out;
1169
+ }
1170
+ function formatStamp(now) {
1171
+ return now.toISOString().replace(/[-:]/g, "").replace(/\.\d+Z$/, "Z").slice(0, 15);
1172
+ }
1173
+ function ensureProjectSkeleton(projectDir) {
1174
+ const dirs = [
1175
+ ".claude/commands/uzys",
1176
+ ".claude/rules",
1177
+ ".claude/skills",
1178
+ ".claude/agents",
1179
+ ".claude/hooks",
1180
+ "docs/decisions"
1181
+ ];
1182
+ for (const d of dirs) {
1183
+ mkdirSync(join(projectDir, d), { recursive: true });
1184
+ }
1185
+ }
1186
+
1187
+ // src/install-log.ts
1188
+ var INSTALL_LOG_FILENAME = ".harness-install.json";
1189
+ var INSTALL_LOG_DIR = ".uzys-agent-harness";
1190
+ var LEGACY_INSTALL_LOG_DIR = ".claude";
1191
+ var INSTALL_LOG_VERSION = 1;
1192
+ var POLICY_DIRS = [
1193
+ { dir: "rules", ext: ".md" },
1194
+ { dir: "agents", ext: ".md" },
1195
+ { dir: "commands/uzys", ext: ".md" },
1196
+ { dir: "hooks", ext: ".sh" }
1197
+ ];
1198
+ function buildAssetEntries(report, scope) {
1199
+ if (!report) return [];
1200
+ return report.attempted.filter((r) => r.ok).map((r) => assetToLogEntry(r.asset, scope, r.version));
1201
+ }
1202
+ function assetToLogEntry(asset, scope, version) {
1203
+ const detail = methodDetail(asset.method);
1204
+ const entry = {
1205
+ id: asset.id,
1206
+ category: asset.category,
1207
+ method: asset.method.kind,
1208
+ scope,
1209
+ detail
1210
+ };
1211
+ if (version) entry.version = version;
1212
+ return entry;
1213
+ }
1214
+ function methodDetail(method) {
1215
+ switch (method.kind) {
1216
+ case "plugin":
1217
+ return { marketplace: method.marketplace, pluginId: method.pluginId };
1218
+ case "skill":
1219
+ return { source: method.source, ...method.skill ? { skill: method.skill } : {} };
1220
+ case "npm":
1221
+ return { pkg: method.pkg };
1222
+ case "npx-run":
1223
+ return { cmd: method.cmd, args: (method.args ?? []).join(" ") };
1224
+ case "shell-script":
1225
+ return { script: method.script, args: method.args.join(" ") };
1226
+ case "internal":
1227
+ return { key: method.key };
1228
+ }
1229
+ }
1230
+ function buildInstallLog(spec, external, scope, rootClaudeMd, previous, claudeDirMovedAside = false, rootFiles = []) {
1231
+ const templates = {
1232
+ claudeDir: ".claude/",
1233
+ ...spec.cli.includes("codex") ? { codexDir: ".codex/" } : {},
1234
+ ...spec.cli.includes("opencode") ? { opencodeDir: ".opencode/" } : {},
1235
+ ...rootClaudeMd ? { rootClaudeMd } : {}
1236
+ };
1237
+ const log = {
1238
+ schemaVersion: INSTALL_LOG_VERSION,
1239
+ installedAt: (/* @__PURE__ */ new Date()).toISOString(),
1240
+ scope,
1241
+ spec: {
1242
+ tracks: spec.tracks,
1243
+ cli: spec.cli,
1244
+ ...spec.baselineExclude && spec.baselineExclude.length > 0 ? { baselineExclude: spec.baselineExclude } : {}
1245
+ },
1246
+ // 이번 설치가 만든 항목이 이기고, 이번에 안 만든 항목은 이전 값을 그대로 둔다.
1247
+ // (예: claude 로 깔고 나중에 codex 만 추가 설치해도 root CLAUDE.md 기록이 살아남는다)
1248
+ templates: { ...previous?.templates, ...templates },
1249
+ assets: mergeAssets(
1250
+ claudeDirMovedAside ? previous?.assets?.filter(survivesClaudeDirRename) : previous?.assets,
1251
+ buildAssetEntries(external, scope)
1252
+ )
1253
+ };
1254
+ const mergedRootFiles = mergeRootFiles(previous?.rootFiles, rootFiles);
1255
+ if (mergedRootFiles.length > 0) log.rootFiles = mergedRootFiles;
1256
+ return log;
1257
+ }
1258
+ function mergeRootFiles(previous, current) {
1259
+ const byPath = /* @__PURE__ */ new Map();
1260
+ for (const file of [...previous ?? [], ...current]) {
1261
+ const prior = byPath.get(file.path);
1262
+ byPath.set(
1263
+ file.path,
1264
+ prior ? {
1265
+ path: file.path,
1266
+ change: prior.change === "created" ? "created" : file.change,
1267
+ notes: [.../* @__PURE__ */ new Set([...prior.notes, ...file.notes])]
1268
+ } : file
1269
+ );
1270
+ }
1271
+ return [...byPath.values()];
1272
+ }
1273
+ function survivesClaudeDirRename(asset) {
1274
+ if (asset.scope === "global") return true;
1275
+ switch (asset.method) {
1276
+ case "plugin":
1277
+ return true;
1278
+ // `~/.claude/plugins/cache` — 프로젝트 밖
1279
+ case "npm":
1280
+ return true;
1281
+ // `node_modules/`
1282
+ case "skill":
1283
+ return false;
1284
+ // `npx skills add` project scope → `.claude/skills/`
1285
+ case "shell-script":
1286
+ return false;
1287
+ // ecc-prune → `.claude/local-plugins/`
1288
+ case "npx-run":
1289
+ return false;
1290
+ case "internal":
1291
+ return false;
1292
+ }
1293
+ }
1294
+ function mergeAssets(previous, current) {
1295
+ if (!previous || previous.length === 0) return [...current];
1296
+ const currentById = new Map(current.map((a) => [a.id, a]));
1297
+ const previousIds = new Set(previous.map((a) => a.id));
1298
+ return [
1299
+ ...previous.map((a) => currentById.get(a.id) ?? a),
1300
+ ...current.filter((a) => !previousIds.has(a.id))
1301
+ ];
1302
+ }
1303
+ function hashContent(content) {
1304
+ return createHash("sha256").update(content, "utf8").digest("hex");
1305
+ }
1306
+ function isHarnessOwned(baseline, key, current) {
1307
+ const recorded = baseline.get(key);
1308
+ return recorded !== void 0 && recorded === hashContent(current);
1309
+ }
1310
+ function mergeExternalFiles(projectDir, previous, current) {
1311
+ const byPath = /* @__PURE__ */ new Map();
1312
+ for (const file of [...previous ?? [], ...current]) byPath.set(file.path, file);
1313
+ return [...byPath.values()].filter((f) => existsSync2(join2(projectDir, f.path)));
1314
+ }
1315
+ function collectSkillHashes(projectDir) {
1316
+ const skillsDir = join2(projectDir, ".claude/skills");
1317
+ return listFilesRecursive(skillsDir).map((rel) => ({
1318
+ path: rel,
1319
+ sha256: hashContent(readFileSync2(join2(skillsDir, rel), "utf8"))
1320
+ }));
1321
+ }
1322
+ function collectPolicyHashes(projectDir, templatesDir) {
1323
+ const out = [];
1324
+ for (const { dir, ext } of POLICY_DIRS) {
1325
+ const targetDir = join2(projectDir, ".claude", dir);
1326
+ const sourceDir = join2(templatesDir, dir);
1327
+ for (const rel of listFilesRecursive(targetDir)) {
1328
+ if (!rel.endsWith(ext)) continue;
1329
+ if (!existsSync2(join2(sourceDir, rel))) continue;
1330
+ out.push({
1331
+ path: `${dir}/${rel}`,
1332
+ sha256: hashContent(readFileSync2(join2(targetDir, rel), "utf8"))
1333
+ });
1334
+ }
1335
+ }
1336
+ return out;
1337
+ }
1338
+ function writeInstallLog(projectDir, log) {
1339
+ const path2 = installLogPath(projectDir);
1340
+ mkdirSync2(dirname2(path2), { recursive: true });
1341
+ writeFileSync(path2, `${JSON.stringify(log, null, 2)}
1342
+ `, "utf8");
1343
+ migrateAwayLegacyLog(projectDir);
1344
+ return path2;
1345
+ }
1346
+ function migrateAwayLegacyLog(projectDir) {
1347
+ const legacy = legacyInstallLogPath(projectDir);
1348
+ if (!existsSync2(legacy)) return;
1349
+ try {
1350
+ rmSync(legacy);
1351
+ } catch {
1352
+ }
1353
+ }
1354
+ function readInstallLog(projectDir) {
1355
+ const path2 = [installLogPath(projectDir), legacyInstallLogPath(projectDir)].find(
1356
+ (p) => existsSync2(p)
1357
+ );
1358
+ if (!path2) return null;
1359
+ try {
1360
+ const parsed = JSON.parse(readFileSync2(path2, "utf8"));
1361
+ if (Array.isArray(parsed.assets)) {
1362
+ parsed.assets = parsed.assets.map(
1363
+ (a) => a.method === "npm-global" ? { ...a, method: "npm" } : a
1364
+ );
1365
+ }
1366
+ return parsed;
1367
+ } catch {
1368
+ return null;
1369
+ }
1370
+ }
1371
+ function installLogPath(projectDir) {
1372
+ return join2(projectDir, INSTALL_LOG_DIR, INSTALL_LOG_FILENAME);
1373
+ }
1374
+ function legacyInstallLogPath(projectDir) {
1375
+ return join2(projectDir, LEGACY_INSTALL_LOG_DIR, INSTALL_LOG_FILENAME);
1376
+ }
1025
1377
 
1026
1378
  // src/project-claude-merge.ts
1027
1379
  init_esm_shims();
@@ -1165,7 +1517,7 @@ var ui = (s) => hasUiTrack(s.tracks);
1165
1517
  var onTracks = (pattern) => (s) => anyTrack(s.tracks, pattern);
1166
1518
  var COMMON_RULES = ["git-policy", "change-management", "doc-governance"];
1167
1519
  var DEV_RULES = ["test-policy", "ship-checklist"];
1168
- var UI_RULES = ["playwright-launch"];
1520
+ var UI_RULES = [];
1169
1521
  var TRACK_RULES = {
1170
1522
  "csr-supabase": [],
1171
1523
  "csr-fastify": [],
@@ -1199,6 +1551,7 @@ function resolveRules(spec) {
1199
1551
  }
1200
1552
  return [...set].sort();
1201
1553
  }
1554
+ var ALL_RULES = resolveRules({ tracks: [...TRACKS] });
1202
1555
  var CORE_AGENTS = ["reviewer", "data-analyst", "strategist"];
1203
1556
  var CORE_AGENTS_ECC = ["code-reviewer", "security-reviewer"];
1204
1557
  var DEV_AGENTS = ["plan-checker", "implementer"];
@@ -1206,7 +1559,6 @@ var DEV_AGENTS_ECC = ["silent-failure-hunter", "build-error-resolver"];
1206
1559
  var ALWAYS_HOOKS = [
1207
1560
  "session-start.sh",
1208
1561
  "protect-files.sh",
1209
- "mcp-pre-exec.sh",
1210
1562
  // UserPromptSubmit 넛지. 차단하지 않는 유일한 훅 — 판정은 결정적 두 조건뿐이고(길이 ·
1211
1563
  // `<objective>` 표식 부재) 변환 자체는 `task-brief` 스킬 몫이다. `templates/settings.json`
1212
1564
  // 배선과 한 벌 (hook-wiring-parity 가 한쪽만 있는 상태를 문다).
@@ -1222,6 +1574,9 @@ var MODIFIED_ECC_SKILL_DIRS = [...MODIFIED_COMMON_SKILL_DIRS, ...MODIFIED_DEV_SK
1222
1574
  var UI_SKILL_DIRS = ["ui-visual-review"];
1223
1575
  var UI_SKILL_DIRS_ECC = ["e2e-testing"];
1224
1576
  var PYTHON_SKILL_DIRS_ECC = ["python-patterns", "python-testing"];
1577
+ function isCliNeutralTarget(target) {
1578
+ return target.startsWith(`${INSTALL_LOG_DIR}/`);
1579
+ }
1225
1580
  function buildManifest(spec) {
1226
1581
  const m = [];
1227
1582
  for (const r of resolveRules(spec)) {
@@ -1232,12 +1587,6 @@ function buildManifest(spec) {
1232
1587
  applies: all
1233
1588
  });
1234
1589
  }
1235
- m.push({
1236
- source: "commands/ecc",
1237
- target: ".claude/commands/ecc",
1238
- type: "dir",
1239
- applies: (s) => !s.withEcc
1240
- });
1241
1590
  m.push({
1242
1591
  source: "CLAUDE.md",
1243
1592
  target: HARNESS_ANCHOR_FILE,
@@ -1256,6 +1605,12 @@ function buildManifest(spec) {
1256
1605
  type: "file",
1257
1606
  applies: all
1258
1607
  });
1608
+ m.push({
1609
+ source: "scripts/check-absence.sh",
1610
+ target: ".uzys-agent-harness/check-absence.sh",
1611
+ type: "file",
1612
+ applies: all
1613
+ });
1259
1614
  for (const a of CORE_AGENTS) {
1260
1615
  m.push({
1261
1616
  source: `agents/${a}.md`,
@@ -1411,10 +1766,37 @@ function buildManifest(spec) {
1411
1766
  return m;
1412
1767
  }
1413
1768
 
1769
+ // src/categories.ts
1770
+ init_esm_shims();
1771
+ var CATEGORIES = [
1772
+ "frontend",
1773
+ "backend",
1774
+ "data",
1775
+ "business",
1776
+ "dev-tools",
1777
+ "understanding",
1778
+ "visual-media",
1779
+ "workflow",
1780
+ "ecc-suite"
1781
+ ];
1782
+ var CATEGORY_TITLES = {
1783
+ frontend: "\u{1F3A8} Frontend (UI \xB7 Design)",
1784
+ backend: "\u{1F5C4}\uFE0F Backend (API \xB7 DB \xB7 Deploy)",
1785
+ data: "\u{1F4CA} Data",
1786
+ business: "\u{1F4BC} Business (Documents)",
1787
+ "dev-tools": "\u{1F6E1}\uFE0F Dev Tools (Security \xB7 Quality)",
1788
+ // v26.78.0 — 에이전트 인지 증강: 환경에 대한 지각(영상·웹·코드) + 기억(memory).
1789
+ understanding: "\u{1F9E0} Understanding (Perception \xB7 Memory)",
1790
+ // v26.85.0 — 코드-퍼스트 비주얼/미디어 제작: 슬라이드·다이어그램·모션·동영상.
1791
+ "visual-media": "\u{1F3AC} Visual & Media (Slides \xB7 Diagrams \xB7 Motion \xB7 Video)",
1792
+ workflow: "\u{1F504} Workflow (Development Cycle)",
1793
+ "ecc-suite": "\u{1F4E6} ECC Suite"
1794
+ };
1795
+
1414
1796
  // src/context-cost.ts
1415
1797
  init_esm_shims();
1416
- import { existsSync, readFileSync } from "fs";
1417
- import { join, resolve } from "path";
1798
+ import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
1799
+ import { join as join3, resolve } from "path";
1418
1800
  import { fileURLToPath as fileURLToPath2 } from "url";
1419
1801
  var CHARS_PER_TOKEN = 4;
1420
1802
  function estimateTokens(chars) {
@@ -1430,17 +1812,17 @@ function extractFrontmatter(content) {
1430
1812
  function assetDescriptorTokens(assetId, root = resolveBundleRoot()) {
1431
1813
  const asset = EXTERNAL_ASSETS.find((a) => a.id === assetId);
1432
1814
  if (!asset || asset.method.kind !== "internal") return null;
1433
- const skillMd = join(root, "templates", "skills", asset.method.key, "SKILL.md");
1434
- if (!existsSync(skillMd)) return null;
1435
- const fm = extractFrontmatter(readFileSync(skillMd, "utf8"));
1815
+ const skillMd = join3(root, "templates", "skills", asset.method.key, "SKILL.md");
1816
+ if (!existsSync3(skillMd)) return null;
1817
+ const fm = extractFrontmatter(readFileSync3(skillMd, "utf8"));
1436
1818
  return fm === null ? null : estimateTokens(fm.length);
1437
1819
  }
1438
1820
  function assetBodyTokens(assetId, root = resolveBundleRoot()) {
1439
1821
  const asset = EXTERNAL_ASSETS.find((a) => a.id === assetId);
1440
1822
  if (!asset || asset.method.kind !== "internal") return null;
1441
- const skillMd = join(root, "templates", "skills", asset.method.key, "SKILL.md");
1442
- if (!existsSync(skillMd)) return null;
1443
- const content = readFileSync(skillMd, "utf8");
1823
+ const skillMd = join3(root, "templates", "skills", asset.method.key, "SKILL.md");
1824
+ if (!existsSync3(skillMd)) return null;
1825
+ const content = readFileSync3(skillMd, "utf8");
1444
1826
  const fm = extractFrontmatter(content);
1445
1827
  if (fm === null) return estimateTokens(content.length);
1446
1828
  const close = content.indexOf("\n---", content.indexOf(fm) + fm.length);
@@ -1455,15 +1837,15 @@ function assetCostRows(assetIds, root = resolveBundleRoot()) {
1455
1837
  })).sort((a, b) => (b.bodyTokens ?? -1) - (a.bodyTokens ?? -1));
1456
1838
  }
1457
1839
  function fileTokens(path2) {
1458
- return existsSync(path2) ? estimateTokens(readFileSync(path2, "utf8").trim().length) : 0;
1840
+ return existsSync3(path2) ? estimateTokens(readFileSync3(path2, "utf8").trim().length) : 0;
1459
1841
  }
1460
1842
  function descriptorTokens(path2) {
1461
- if (!existsSync(path2)) return 0;
1462
- const fm = extractFrontmatter(readFileSync(path2, "utf8"));
1843
+ if (!existsSync3(path2)) return 0;
1844
+ const fm = extractFrontmatter(readFileSync3(path2, "utf8"));
1463
1845
  return fm === null ? 0 : estimateTokens(fm.length);
1464
1846
  }
1465
1847
  function residentCost(entries, root = resolveBundleRoot()) {
1466
- const tpl = (source) => join(root, "templates", source);
1848
+ const tpl = (source) => join3(root, "templates", source);
1467
1849
  let rules = 0;
1468
1850
  let skillDescriptors = 0;
1469
1851
  let agentDescriptors = 0;
@@ -1478,11 +1860,11 @@ function residentCost(entries, root = resolveBundleRoot()) {
1478
1860
  agentDescriptors += descriptorTokens(tpl(e.source));
1479
1861
  agentItems += 1;
1480
1862
  } else if (e.target.startsWith(".claude/skills/")) {
1481
- skillDescriptors += descriptorTokens(join(tpl(e.source), "SKILL.md"));
1863
+ skillDescriptors += descriptorTokens(join3(tpl(e.source), "SKILL.md"));
1482
1864
  skillItems += 1;
1483
1865
  }
1484
1866
  }
1485
- const harnessAnchor = fileTokens(join(root, "templates", "CLAUDE.md"));
1867
+ const harnessAnchor = fileTokens(join3(root, "templates", "CLAUDE.md"));
1486
1868
  const projectScaffold = estimateTokens(renderFillScaffold().trim().length);
1487
1869
  const projectClaudeMd = harnessAnchor + projectScaffold;
1488
1870
  const claudeMdItems = (harnessAnchor > 0 ? 1 : 0) + (projectScaffold > 0 ? 1 : 0);
@@ -1565,6 +1947,8 @@ export {
1565
1947
  __commonJS,
1566
1948
  __toESM,
1567
1949
  init_esm_shims,
1950
+ anyTrack,
1951
+ hasUiTrack,
1568
1952
  TRACKS,
1569
1953
  isTrack,
1570
1954
  CLI_BASES,
@@ -1572,8 +1956,6 @@ export {
1572
1956
  isInstallScope,
1573
1957
  resolveScope,
1574
1958
  DEFAULT_OPTIONS,
1575
- anyTrack,
1576
- hasUiTrack,
1577
1959
  EXTERNAL_ASSETS,
1578
1960
  DEV_METHOD_SKILL_IDS,
1579
1961
  INTERNAL_BUNDLED_SKILL_IDS,
@@ -1584,14 +1966,38 @@ export {
1584
1966
  assetCliSupport,
1585
1967
  assetReachesCli,
1586
1968
  filterApplicableAssets,
1969
+ ensureDir,
1970
+ copyFile,
1971
+ copyDir,
1972
+ backupDir,
1973
+ copyBackupDir,
1974
+ backupFileIfChanged,
1975
+ backupFile,
1976
+ listFilesRecursive,
1977
+ ensureProjectSkeleton,
1978
+ INSTALL_LOG_DIR,
1979
+ POLICY_DIRS,
1980
+ buildInstallLog,
1981
+ hashContent,
1982
+ isHarnessOwned,
1983
+ mergeExternalFiles,
1984
+ collectSkillHashes,
1985
+ collectPolicyHashes,
1986
+ writeInstallLog,
1987
+ readInstallLog,
1988
+ installLogPath,
1989
+ legacyInstallLogPath,
1587
1990
  HARNESS_ANCHOR_FILE,
1588
1991
  HARNESS_IMPORT_LINE,
1589
1992
  renderFillScaffold,
1590
1993
  upsertHarnessImport,
1591
1994
  stripHarnessImport,
1995
+ resolveRules,
1996
+ ALL_RULES,
1997
+ isCliNeutralTarget,
1998
+ buildManifest,
1592
1999
  CATEGORIES,
1593
2000
  CATEGORY_TITLES,
1594
- buildManifest,
1595
2001
  estimateTokens,
1596
2002
  resolveBundleRoot,
1597
2003
  assetCostRows,
@@ -1600,4 +2006,4 @@ export {
1600
2006
  formatResidentCostLine,
1601
2007
  formatResidentCostBlock
1602
2008
  };
1603
- //# sourceMappingURL=chunk-OEIGA5IL.js.map
2009
+ //# sourceMappingURL=chunk-B27AJIWG.js.map