@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.
- package/README.ko.md +5 -5
- package/README.md +3 -3
- package/dist/{chunk-OEIGA5IL.js → chunk-B27AJIWG.js} +459 -53
- package/dist/chunk-B27AJIWG.js.map +1 -0
- package/dist/index.js +727 -552
- package/dist/index.js.map +1 -1
- package/dist/trust-tier-drift.js +1 -1
- package/package.json +1 -1
- package/templates/CLAUDE.md +14 -5
- package/templates/agents/implementer.md +1 -1
- package/templates/codex/AGENTS.md.template +11 -0
- package/templates/opencode/AGENTS.md.template +8 -1
- package/templates/opencode/opencode.json.template +0 -1
- package/templates/rules/cli-development.md +2 -2
- package/templates/rules/doc-governance.md +3 -1
- package/templates/rules/test-policy.md +5 -6
- package/templates/scripts/check-absence.sh +205 -0
- package/templates/settings.json +0 -9
- package/templates/skills/gh-issue-workflow/ISSUE.template.md +111 -35
- package/templates/skills/gh-issue-workflow/SKILL.md +268 -138
- package/templates/skills/gh-issue-workflow/references/hierarchy.md +261 -0
- package/templates/skills/gh-issue-workflow/references/operating-model.md +132 -0
- package/templates/skills/gh-issue-workflow/references/readiness.md +121 -0
- package/templates/skills/ui-visual-review/SKILL.md +17 -3
- package/dist/chunk-OEIGA5IL.js.map +0 -1
- package/templates/commands/ecc/checkpoint.md +0 -32
- package/templates/commands/ecc/e2e.md +0 -105
- package/templates/commands/ecc/eval.md +0 -88
- package/templates/commands/ecc/evolve.md +0 -7
- package/templates/commands/ecc/harness-audit.md +0 -73
- package/templates/commands/ecc/instinct-status.md +0 -8
- package/templates/commands/ecc/promote.md +0 -10
- package/templates/commands/ecc/security-scan.md +0 -10
- package/templates/hooks/mcp-pre-exec.sh +0 -122
- package/templates/mcp-allowlist.example +0 -24
- 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
|
|
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/
|
|
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/
|
|
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 = [
|
|
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 =
|
|
1434
|
-
if (!
|
|
1435
|
-
const fm = extractFrontmatter(
|
|
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 =
|
|
1442
|
-
if (!
|
|
1443
|
-
const content =
|
|
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
|
|
1840
|
+
return existsSync3(path2) ? estimateTokens(readFileSync3(path2, "utf8").trim().length) : 0;
|
|
1459
1841
|
}
|
|
1460
1842
|
function descriptorTokens(path2) {
|
|
1461
|
-
if (!
|
|
1462
|
-
const fm = extractFrontmatter(
|
|
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) =>
|
|
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(
|
|
1863
|
+
skillDescriptors += descriptorTokens(join3(tpl(e.source), "SKILL.md"));
|
|
1482
1864
|
skillItems += 1;
|
|
1483
1865
|
}
|
|
1484
1866
|
}
|
|
1485
|
-
const harnessAnchor = fileTokens(
|
|
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-
|
|
2009
|
+
//# sourceMappingURL=chunk-B27AJIWG.js.map
|