@uzysjung/agent-harness 26.146.1 → 26.147.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-OEIGA5IL.js → chunk-GPH5AWLX.js} +450 -53
- package/dist/chunk-GPH5AWLX.js.map +1 -0
- package/dist/index.js +604 -529
- 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,312 @@ 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
|
|
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
|
|
1024
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) {
|
|
1097
|
+
if (!existsSync(source)) {
|
|
1098
|
+
throw new Error(`Source dir not found: ${source}`);
|
|
1099
|
+
}
|
|
1100
|
+
mkdirSync(target, { recursive: true });
|
|
1101
|
+
cpSync(source, target, { recursive: true, force: true });
|
|
1102
|
+
}
|
|
1103
|
+
var MAX_BACKUP_CLAIMS = 100;
|
|
1104
|
+
function claimBackupPath(base, claim) {
|
|
1105
|
+
for (let n = 1; n <= MAX_BACKUP_CLAIMS; n++) {
|
|
1106
|
+
const candidate = n === 1 ? base : `${base}-${n}`;
|
|
1107
|
+
try {
|
|
1108
|
+
claim(candidate);
|
|
1109
|
+
return candidate;
|
|
1110
|
+
} catch (err) {
|
|
1111
|
+
if (err.code !== "EEXIST") throw err;
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
throw new Error(`Cannot allocate a backup path for ${base} (${MAX_BACKUP_CLAIMS} taken)`);
|
|
1115
|
+
}
|
|
1116
|
+
function backupDir(target, now = /* @__PURE__ */ new Date()) {
|
|
1117
|
+
if (!existsSync(target)) {
|
|
1118
|
+
return null;
|
|
1119
|
+
}
|
|
1120
|
+
const backup = claimBackupPath(`${target}.backup-${formatStamp(now)}`, (c) => mkdirSync(c));
|
|
1121
|
+
rmdirSync(backup);
|
|
1122
|
+
renameSync(target, backup);
|
|
1123
|
+
return backup;
|
|
1124
|
+
}
|
|
1125
|
+
function copyBackupDir(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
|
+
cpSync(realpathSync(target), backup, { recursive: true });
|
|
1131
|
+
return backup;
|
|
1132
|
+
}
|
|
1133
|
+
function backupFileIfChanged(target, newContent, now = /* @__PURE__ */ new Date()) {
|
|
1134
|
+
if (!existsSync(target)) {
|
|
1135
|
+
return null;
|
|
1136
|
+
}
|
|
1137
|
+
if (readFileSync(target, "utf-8") === newContent) {
|
|
1138
|
+
return null;
|
|
1139
|
+
}
|
|
1140
|
+
return backupFile(target, now);
|
|
1141
|
+
}
|
|
1142
|
+
function backupFile(target, now = /* @__PURE__ */ new Date()) {
|
|
1143
|
+
return claimBackupPath(
|
|
1144
|
+
`${target}.backup-${formatStamp(now)}`,
|
|
1145
|
+
(c) => copyFileSync(target, c, fsConstants.COPYFILE_EXCL)
|
|
1146
|
+
);
|
|
1147
|
+
}
|
|
1148
|
+
function listFilesRecursive(dir, prefix = "") {
|
|
1149
|
+
if (!existsSync(dir)) return [];
|
|
1150
|
+
const out = [];
|
|
1151
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
1152
|
+
const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
1153
|
+
if (entry.isDirectory()) {
|
|
1154
|
+
out.push(...listFilesRecursive(join(dir, entry.name), rel));
|
|
1155
|
+
} else if (entry.isFile()) {
|
|
1156
|
+
out.push(rel);
|
|
1157
|
+
}
|
|
1158
|
+
}
|
|
1159
|
+
return out;
|
|
1160
|
+
}
|
|
1161
|
+
function formatStamp(now) {
|
|
1162
|
+
return now.toISOString().replace(/[-:]/g, "").replace(/\.\d+Z$/, "Z").slice(0, 15);
|
|
1163
|
+
}
|
|
1164
|
+
function ensureProjectSkeleton(projectDir) {
|
|
1165
|
+
const dirs = [
|
|
1166
|
+
".claude/commands/uzys",
|
|
1167
|
+
".claude/rules",
|
|
1168
|
+
".claude/skills",
|
|
1169
|
+
".claude/agents",
|
|
1170
|
+
".claude/hooks",
|
|
1171
|
+
"docs/decisions"
|
|
1172
|
+
];
|
|
1173
|
+
for (const d of dirs) {
|
|
1174
|
+
mkdirSync(join(projectDir, d), { recursive: true });
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
// src/install-log.ts
|
|
1179
|
+
var INSTALL_LOG_FILENAME = ".harness-install.json";
|
|
1180
|
+
var INSTALL_LOG_DIR = ".uzys-agent-harness";
|
|
1181
|
+
var LEGACY_INSTALL_LOG_DIR = ".claude";
|
|
1182
|
+
var INSTALL_LOG_VERSION = 1;
|
|
1183
|
+
var POLICY_DIRS = [
|
|
1184
|
+
{ dir: "rules", ext: ".md" },
|
|
1185
|
+
{ dir: "agents", ext: ".md" },
|
|
1186
|
+
{ dir: "commands/uzys", ext: ".md" },
|
|
1187
|
+
{ dir: "hooks", ext: ".sh" }
|
|
1188
|
+
];
|
|
1189
|
+
function buildAssetEntries(report, scope) {
|
|
1190
|
+
if (!report) return [];
|
|
1191
|
+
return report.attempted.filter((r) => r.ok).map((r) => assetToLogEntry(r.asset, scope, r.version));
|
|
1192
|
+
}
|
|
1193
|
+
function assetToLogEntry(asset, scope, version) {
|
|
1194
|
+
const detail = methodDetail(asset.method);
|
|
1195
|
+
const entry = {
|
|
1196
|
+
id: asset.id,
|
|
1197
|
+
category: asset.category,
|
|
1198
|
+
method: asset.method.kind,
|
|
1199
|
+
scope,
|
|
1200
|
+
detail
|
|
1201
|
+
};
|
|
1202
|
+
if (version) entry.version = version;
|
|
1203
|
+
return entry;
|
|
1204
|
+
}
|
|
1205
|
+
function methodDetail(method) {
|
|
1206
|
+
switch (method.kind) {
|
|
1207
|
+
case "plugin":
|
|
1208
|
+
return { marketplace: method.marketplace, pluginId: method.pluginId };
|
|
1209
|
+
case "skill":
|
|
1210
|
+
return { source: method.source, ...method.skill ? { skill: method.skill } : {} };
|
|
1211
|
+
case "npm":
|
|
1212
|
+
return { pkg: method.pkg };
|
|
1213
|
+
case "npx-run":
|
|
1214
|
+
return { cmd: method.cmd, args: (method.args ?? []).join(" ") };
|
|
1215
|
+
case "shell-script":
|
|
1216
|
+
return { script: method.script, args: method.args.join(" ") };
|
|
1217
|
+
case "internal":
|
|
1218
|
+
return { key: method.key };
|
|
1219
|
+
}
|
|
1220
|
+
}
|
|
1221
|
+
function buildInstallLog(spec, external, scope, rootClaudeMd, previous, claudeDirMovedAside = false, rootFiles = []) {
|
|
1222
|
+
const templates = {
|
|
1223
|
+
claudeDir: ".claude/",
|
|
1224
|
+
...spec.cli.includes("codex") ? { codexDir: ".codex/" } : {},
|
|
1225
|
+
...spec.cli.includes("opencode") ? { opencodeDir: ".opencode/" } : {},
|
|
1226
|
+
...rootClaudeMd ? { rootClaudeMd } : {}
|
|
1227
|
+
};
|
|
1228
|
+
const log = {
|
|
1229
|
+
schemaVersion: INSTALL_LOG_VERSION,
|
|
1230
|
+
installedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1231
|
+
scope,
|
|
1232
|
+
spec: {
|
|
1233
|
+
tracks: spec.tracks,
|
|
1234
|
+
cli: spec.cli,
|
|
1235
|
+
...spec.baselineExclude && spec.baselineExclude.length > 0 ? { baselineExclude: spec.baselineExclude } : {}
|
|
1236
|
+
},
|
|
1237
|
+
// 이번 설치가 만든 항목이 이기고, 이번에 안 만든 항목은 이전 값을 그대로 둔다.
|
|
1238
|
+
// (예: claude 로 깔고 나중에 codex 만 추가 설치해도 root CLAUDE.md 기록이 살아남는다)
|
|
1239
|
+
templates: { ...previous?.templates, ...templates },
|
|
1240
|
+
assets: mergeAssets(
|
|
1241
|
+
claudeDirMovedAside ? previous?.assets?.filter(survivesClaudeDirRename) : previous?.assets,
|
|
1242
|
+
buildAssetEntries(external, scope)
|
|
1243
|
+
)
|
|
1244
|
+
};
|
|
1245
|
+
const mergedRootFiles = mergeRootFiles(previous?.rootFiles, rootFiles);
|
|
1246
|
+
if (mergedRootFiles.length > 0) log.rootFiles = mergedRootFiles;
|
|
1247
|
+
return log;
|
|
1248
|
+
}
|
|
1249
|
+
function mergeRootFiles(previous, current) {
|
|
1250
|
+
const byPath = /* @__PURE__ */ new Map();
|
|
1251
|
+
for (const file of [...previous ?? [], ...current]) {
|
|
1252
|
+
const prior = byPath.get(file.path);
|
|
1253
|
+
byPath.set(
|
|
1254
|
+
file.path,
|
|
1255
|
+
prior ? {
|
|
1256
|
+
path: file.path,
|
|
1257
|
+
change: prior.change === "created" ? "created" : file.change,
|
|
1258
|
+
notes: [.../* @__PURE__ */ new Set([...prior.notes, ...file.notes])]
|
|
1259
|
+
} : file
|
|
1260
|
+
);
|
|
1261
|
+
}
|
|
1262
|
+
return [...byPath.values()];
|
|
1263
|
+
}
|
|
1264
|
+
function survivesClaudeDirRename(asset) {
|
|
1265
|
+
if (asset.scope === "global") return true;
|
|
1266
|
+
switch (asset.method) {
|
|
1267
|
+
case "plugin":
|
|
1268
|
+
return true;
|
|
1269
|
+
// `~/.claude/plugins/cache` — 프로젝트 밖
|
|
1270
|
+
case "npm":
|
|
1271
|
+
return true;
|
|
1272
|
+
// `node_modules/`
|
|
1273
|
+
case "skill":
|
|
1274
|
+
return false;
|
|
1275
|
+
// `npx skills add` project scope → `.claude/skills/`
|
|
1276
|
+
case "shell-script":
|
|
1277
|
+
return false;
|
|
1278
|
+
// ecc-prune → `.claude/local-plugins/`
|
|
1279
|
+
case "npx-run":
|
|
1280
|
+
return false;
|
|
1281
|
+
case "internal":
|
|
1282
|
+
return false;
|
|
1283
|
+
}
|
|
1284
|
+
}
|
|
1285
|
+
function mergeAssets(previous, current) {
|
|
1286
|
+
if (!previous || previous.length === 0) return [...current];
|
|
1287
|
+
const currentById = new Map(current.map((a) => [a.id, a]));
|
|
1288
|
+
const previousIds = new Set(previous.map((a) => a.id));
|
|
1289
|
+
return [
|
|
1290
|
+
...previous.map((a) => currentById.get(a.id) ?? a),
|
|
1291
|
+
...current.filter((a) => !previousIds.has(a.id))
|
|
1292
|
+
];
|
|
1293
|
+
}
|
|
1294
|
+
function hashContent(content) {
|
|
1295
|
+
return createHash("sha256").update(content, "utf8").digest("hex");
|
|
1296
|
+
}
|
|
1297
|
+
function isHarnessOwned(baseline, key, current) {
|
|
1298
|
+
const recorded = baseline.get(key);
|
|
1299
|
+
return recorded !== void 0 && recorded === hashContent(current);
|
|
1300
|
+
}
|
|
1301
|
+
function mergeExternalFiles(projectDir, previous, current) {
|
|
1302
|
+
const byPath = /* @__PURE__ */ new Map();
|
|
1303
|
+
for (const file of [...previous ?? [], ...current]) byPath.set(file.path, file);
|
|
1304
|
+
return [...byPath.values()].filter((f) => existsSync2(join2(projectDir, f.path)));
|
|
1305
|
+
}
|
|
1306
|
+
function collectSkillHashes(projectDir) {
|
|
1307
|
+
const skillsDir = join2(projectDir, ".claude/skills");
|
|
1308
|
+
return listFilesRecursive(skillsDir).map((rel) => ({
|
|
1309
|
+
path: rel,
|
|
1310
|
+
sha256: hashContent(readFileSync2(join2(skillsDir, rel), "utf8"))
|
|
1311
|
+
}));
|
|
1312
|
+
}
|
|
1313
|
+
function collectPolicyHashes(projectDir, templatesDir) {
|
|
1314
|
+
const out = [];
|
|
1315
|
+
for (const { dir, ext } of POLICY_DIRS) {
|
|
1316
|
+
const targetDir = join2(projectDir, ".claude", dir);
|
|
1317
|
+
const sourceDir = join2(templatesDir, dir);
|
|
1318
|
+
for (const rel of listFilesRecursive(targetDir)) {
|
|
1319
|
+
if (!rel.endsWith(ext)) continue;
|
|
1320
|
+
if (!existsSync2(join2(sourceDir, rel))) continue;
|
|
1321
|
+
out.push({
|
|
1322
|
+
path: `${dir}/${rel}`,
|
|
1323
|
+
sha256: hashContent(readFileSync2(join2(targetDir, rel), "utf8"))
|
|
1324
|
+
});
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
1327
|
+
return out;
|
|
1328
|
+
}
|
|
1329
|
+
function writeInstallLog(projectDir, log) {
|
|
1330
|
+
const path2 = installLogPath(projectDir);
|
|
1331
|
+
mkdirSync2(dirname2(path2), { recursive: true });
|
|
1332
|
+
writeFileSync(path2, `${JSON.stringify(log, null, 2)}
|
|
1333
|
+
`, "utf8");
|
|
1334
|
+
migrateAwayLegacyLog(projectDir);
|
|
1335
|
+
return path2;
|
|
1336
|
+
}
|
|
1337
|
+
function migrateAwayLegacyLog(projectDir) {
|
|
1338
|
+
const legacy = legacyInstallLogPath(projectDir);
|
|
1339
|
+
if (!existsSync2(legacy)) return;
|
|
1340
|
+
try {
|
|
1341
|
+
rmSync(legacy);
|
|
1342
|
+
} catch {
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
function readInstallLog(projectDir) {
|
|
1346
|
+
const path2 = [installLogPath(projectDir), legacyInstallLogPath(projectDir)].find(
|
|
1347
|
+
(p) => existsSync2(p)
|
|
1348
|
+
);
|
|
1349
|
+
if (!path2) return null;
|
|
1350
|
+
try {
|
|
1351
|
+
const parsed = JSON.parse(readFileSync2(path2, "utf8"));
|
|
1352
|
+
if (Array.isArray(parsed.assets)) {
|
|
1353
|
+
parsed.assets = parsed.assets.map(
|
|
1354
|
+
(a) => a.method === "npm-global" ? { ...a, method: "npm" } : a
|
|
1355
|
+
);
|
|
1356
|
+
}
|
|
1357
|
+
return parsed;
|
|
1358
|
+
} catch {
|
|
1359
|
+
return null;
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
function installLogPath(projectDir) {
|
|
1363
|
+
return join2(projectDir, INSTALL_LOG_DIR, INSTALL_LOG_FILENAME);
|
|
1364
|
+
}
|
|
1365
|
+
function legacyInstallLogPath(projectDir) {
|
|
1366
|
+
return join2(projectDir, LEGACY_INSTALL_LOG_DIR, INSTALL_LOG_FILENAME);
|
|
1367
|
+
}
|
|
1025
1368
|
|
|
1026
1369
|
// src/project-claude-merge.ts
|
|
1027
1370
|
init_esm_shims();
|
|
@@ -1165,7 +1508,7 @@ var ui = (s) => hasUiTrack(s.tracks);
|
|
|
1165
1508
|
var onTracks = (pattern) => (s) => anyTrack(s.tracks, pattern);
|
|
1166
1509
|
var COMMON_RULES = ["git-policy", "change-management", "doc-governance"];
|
|
1167
1510
|
var DEV_RULES = ["test-policy", "ship-checklist"];
|
|
1168
|
-
var UI_RULES = [
|
|
1511
|
+
var UI_RULES = [];
|
|
1169
1512
|
var TRACK_RULES = {
|
|
1170
1513
|
"csr-supabase": [],
|
|
1171
1514
|
"csr-fastify": [],
|
|
@@ -1199,6 +1542,7 @@ function resolveRules(spec) {
|
|
|
1199
1542
|
}
|
|
1200
1543
|
return [...set].sort();
|
|
1201
1544
|
}
|
|
1545
|
+
var ALL_RULES = resolveRules({ tracks: [...TRACKS] });
|
|
1202
1546
|
var CORE_AGENTS = ["reviewer", "data-analyst", "strategist"];
|
|
1203
1547
|
var CORE_AGENTS_ECC = ["code-reviewer", "security-reviewer"];
|
|
1204
1548
|
var DEV_AGENTS = ["plan-checker", "implementer"];
|
|
@@ -1206,7 +1550,6 @@ var DEV_AGENTS_ECC = ["silent-failure-hunter", "build-error-resolver"];
|
|
|
1206
1550
|
var ALWAYS_HOOKS = [
|
|
1207
1551
|
"session-start.sh",
|
|
1208
1552
|
"protect-files.sh",
|
|
1209
|
-
"mcp-pre-exec.sh",
|
|
1210
1553
|
// UserPromptSubmit 넛지. 차단하지 않는 유일한 훅 — 판정은 결정적 두 조건뿐이고(길이 ·
|
|
1211
1554
|
// `<objective>` 표식 부재) 변환 자체는 `task-brief` 스킬 몫이다. `templates/settings.json`
|
|
1212
1555
|
// 배선과 한 벌 (hook-wiring-parity 가 한쪽만 있는 상태를 문다).
|
|
@@ -1222,6 +1565,9 @@ var MODIFIED_ECC_SKILL_DIRS = [...MODIFIED_COMMON_SKILL_DIRS, ...MODIFIED_DEV_SK
|
|
|
1222
1565
|
var UI_SKILL_DIRS = ["ui-visual-review"];
|
|
1223
1566
|
var UI_SKILL_DIRS_ECC = ["e2e-testing"];
|
|
1224
1567
|
var PYTHON_SKILL_DIRS_ECC = ["python-patterns", "python-testing"];
|
|
1568
|
+
function isCliNeutralTarget(target) {
|
|
1569
|
+
return target.startsWith(`${INSTALL_LOG_DIR}/`);
|
|
1570
|
+
}
|
|
1225
1571
|
function buildManifest(spec) {
|
|
1226
1572
|
const m = [];
|
|
1227
1573
|
for (const r of resolveRules(spec)) {
|
|
@@ -1232,12 +1578,6 @@ function buildManifest(spec) {
|
|
|
1232
1578
|
applies: all
|
|
1233
1579
|
});
|
|
1234
1580
|
}
|
|
1235
|
-
m.push({
|
|
1236
|
-
source: "commands/ecc",
|
|
1237
|
-
target: ".claude/commands/ecc",
|
|
1238
|
-
type: "dir",
|
|
1239
|
-
applies: (s) => !s.withEcc
|
|
1240
|
-
});
|
|
1241
1581
|
m.push({
|
|
1242
1582
|
source: "CLAUDE.md",
|
|
1243
1583
|
target: HARNESS_ANCHOR_FILE,
|
|
@@ -1256,6 +1596,12 @@ function buildManifest(spec) {
|
|
|
1256
1596
|
type: "file",
|
|
1257
1597
|
applies: all
|
|
1258
1598
|
});
|
|
1599
|
+
m.push({
|
|
1600
|
+
source: "scripts/check-absence.sh",
|
|
1601
|
+
target: ".uzys-agent-harness/check-absence.sh",
|
|
1602
|
+
type: "file",
|
|
1603
|
+
applies: all
|
|
1604
|
+
});
|
|
1259
1605
|
for (const a of CORE_AGENTS) {
|
|
1260
1606
|
m.push({
|
|
1261
1607
|
source: `agents/${a}.md`,
|
|
@@ -1411,10 +1757,37 @@ function buildManifest(spec) {
|
|
|
1411
1757
|
return m;
|
|
1412
1758
|
}
|
|
1413
1759
|
|
|
1760
|
+
// src/categories.ts
|
|
1761
|
+
init_esm_shims();
|
|
1762
|
+
var CATEGORIES = [
|
|
1763
|
+
"frontend",
|
|
1764
|
+
"backend",
|
|
1765
|
+
"data",
|
|
1766
|
+
"business",
|
|
1767
|
+
"dev-tools",
|
|
1768
|
+
"understanding",
|
|
1769
|
+
"visual-media",
|
|
1770
|
+
"workflow",
|
|
1771
|
+
"ecc-suite"
|
|
1772
|
+
];
|
|
1773
|
+
var CATEGORY_TITLES = {
|
|
1774
|
+
frontend: "\u{1F3A8} Frontend (UI \xB7 Design)",
|
|
1775
|
+
backend: "\u{1F5C4}\uFE0F Backend (API \xB7 DB \xB7 Deploy)",
|
|
1776
|
+
data: "\u{1F4CA} Data",
|
|
1777
|
+
business: "\u{1F4BC} Business (Documents)",
|
|
1778
|
+
"dev-tools": "\u{1F6E1}\uFE0F Dev Tools (Security \xB7 Quality)",
|
|
1779
|
+
// v26.78.0 — 에이전트 인지 증강: 환경에 대한 지각(영상·웹·코드) + 기억(memory).
|
|
1780
|
+
understanding: "\u{1F9E0} Understanding (Perception \xB7 Memory)",
|
|
1781
|
+
// v26.85.0 — 코드-퍼스트 비주얼/미디어 제작: 슬라이드·다이어그램·모션·동영상.
|
|
1782
|
+
"visual-media": "\u{1F3AC} Visual & Media (Slides \xB7 Diagrams \xB7 Motion \xB7 Video)",
|
|
1783
|
+
workflow: "\u{1F504} Workflow (Development Cycle)",
|
|
1784
|
+
"ecc-suite": "\u{1F4E6} ECC Suite"
|
|
1785
|
+
};
|
|
1786
|
+
|
|
1414
1787
|
// src/context-cost.ts
|
|
1415
1788
|
init_esm_shims();
|
|
1416
|
-
import { existsSync, readFileSync } from "fs";
|
|
1417
|
-
import { join, resolve } from "path";
|
|
1789
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
|
|
1790
|
+
import { join as join3, resolve } from "path";
|
|
1418
1791
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
1419
1792
|
var CHARS_PER_TOKEN = 4;
|
|
1420
1793
|
function estimateTokens(chars) {
|
|
@@ -1430,17 +1803,17 @@ function extractFrontmatter(content) {
|
|
|
1430
1803
|
function assetDescriptorTokens(assetId, root = resolveBundleRoot()) {
|
|
1431
1804
|
const asset = EXTERNAL_ASSETS.find((a) => a.id === assetId);
|
|
1432
1805
|
if (!asset || asset.method.kind !== "internal") return null;
|
|
1433
|
-
const skillMd =
|
|
1434
|
-
if (!
|
|
1435
|
-
const fm = extractFrontmatter(
|
|
1806
|
+
const skillMd = join3(root, "templates", "skills", asset.method.key, "SKILL.md");
|
|
1807
|
+
if (!existsSync3(skillMd)) return null;
|
|
1808
|
+
const fm = extractFrontmatter(readFileSync3(skillMd, "utf8"));
|
|
1436
1809
|
return fm === null ? null : estimateTokens(fm.length);
|
|
1437
1810
|
}
|
|
1438
1811
|
function assetBodyTokens(assetId, root = resolveBundleRoot()) {
|
|
1439
1812
|
const asset = EXTERNAL_ASSETS.find((a) => a.id === assetId);
|
|
1440
1813
|
if (!asset || asset.method.kind !== "internal") return null;
|
|
1441
|
-
const skillMd =
|
|
1442
|
-
if (!
|
|
1443
|
-
const content =
|
|
1814
|
+
const skillMd = join3(root, "templates", "skills", asset.method.key, "SKILL.md");
|
|
1815
|
+
if (!existsSync3(skillMd)) return null;
|
|
1816
|
+
const content = readFileSync3(skillMd, "utf8");
|
|
1444
1817
|
const fm = extractFrontmatter(content);
|
|
1445
1818
|
if (fm === null) return estimateTokens(content.length);
|
|
1446
1819
|
const close = content.indexOf("\n---", content.indexOf(fm) + fm.length);
|
|
@@ -1455,15 +1828,15 @@ function assetCostRows(assetIds, root = resolveBundleRoot()) {
|
|
|
1455
1828
|
})).sort((a, b) => (b.bodyTokens ?? -1) - (a.bodyTokens ?? -1));
|
|
1456
1829
|
}
|
|
1457
1830
|
function fileTokens(path2) {
|
|
1458
|
-
return
|
|
1831
|
+
return existsSync3(path2) ? estimateTokens(readFileSync3(path2, "utf8").trim().length) : 0;
|
|
1459
1832
|
}
|
|
1460
1833
|
function descriptorTokens(path2) {
|
|
1461
|
-
if (!
|
|
1462
|
-
const fm = extractFrontmatter(
|
|
1834
|
+
if (!existsSync3(path2)) return 0;
|
|
1835
|
+
const fm = extractFrontmatter(readFileSync3(path2, "utf8"));
|
|
1463
1836
|
return fm === null ? 0 : estimateTokens(fm.length);
|
|
1464
1837
|
}
|
|
1465
1838
|
function residentCost(entries, root = resolveBundleRoot()) {
|
|
1466
|
-
const tpl = (source) =>
|
|
1839
|
+
const tpl = (source) => join3(root, "templates", source);
|
|
1467
1840
|
let rules = 0;
|
|
1468
1841
|
let skillDescriptors = 0;
|
|
1469
1842
|
let agentDescriptors = 0;
|
|
@@ -1478,11 +1851,11 @@ function residentCost(entries, root = resolveBundleRoot()) {
|
|
|
1478
1851
|
agentDescriptors += descriptorTokens(tpl(e.source));
|
|
1479
1852
|
agentItems += 1;
|
|
1480
1853
|
} else if (e.target.startsWith(".claude/skills/")) {
|
|
1481
|
-
skillDescriptors += descriptorTokens(
|
|
1854
|
+
skillDescriptors += descriptorTokens(join3(tpl(e.source), "SKILL.md"));
|
|
1482
1855
|
skillItems += 1;
|
|
1483
1856
|
}
|
|
1484
1857
|
}
|
|
1485
|
-
const harnessAnchor = fileTokens(
|
|
1858
|
+
const harnessAnchor = fileTokens(join3(root, "templates", "CLAUDE.md"));
|
|
1486
1859
|
const projectScaffold = estimateTokens(renderFillScaffold().trim().length);
|
|
1487
1860
|
const projectClaudeMd = harnessAnchor + projectScaffold;
|
|
1488
1861
|
const claudeMdItems = (harnessAnchor > 0 ? 1 : 0) + (projectScaffold > 0 ? 1 : 0);
|
|
@@ -1565,6 +1938,8 @@ export {
|
|
|
1565
1938
|
__commonJS,
|
|
1566
1939
|
__toESM,
|
|
1567
1940
|
init_esm_shims,
|
|
1941
|
+
anyTrack,
|
|
1942
|
+
hasUiTrack,
|
|
1568
1943
|
TRACKS,
|
|
1569
1944
|
isTrack,
|
|
1570
1945
|
CLI_BASES,
|
|
@@ -1572,8 +1947,6 @@ export {
|
|
|
1572
1947
|
isInstallScope,
|
|
1573
1948
|
resolveScope,
|
|
1574
1949
|
DEFAULT_OPTIONS,
|
|
1575
|
-
anyTrack,
|
|
1576
|
-
hasUiTrack,
|
|
1577
1950
|
EXTERNAL_ASSETS,
|
|
1578
1951
|
DEV_METHOD_SKILL_IDS,
|
|
1579
1952
|
INTERNAL_BUNDLED_SKILL_IDS,
|
|
@@ -1584,14 +1957,38 @@ export {
|
|
|
1584
1957
|
assetCliSupport,
|
|
1585
1958
|
assetReachesCli,
|
|
1586
1959
|
filterApplicableAssets,
|
|
1960
|
+
ensureDir,
|
|
1961
|
+
copyFile,
|
|
1962
|
+
copyDir,
|
|
1963
|
+
backupDir,
|
|
1964
|
+
copyBackupDir,
|
|
1965
|
+
backupFileIfChanged,
|
|
1966
|
+
backupFile,
|
|
1967
|
+
listFilesRecursive,
|
|
1968
|
+
ensureProjectSkeleton,
|
|
1969
|
+
INSTALL_LOG_DIR,
|
|
1970
|
+
POLICY_DIRS,
|
|
1971
|
+
buildInstallLog,
|
|
1972
|
+
hashContent,
|
|
1973
|
+
isHarnessOwned,
|
|
1974
|
+
mergeExternalFiles,
|
|
1975
|
+
collectSkillHashes,
|
|
1976
|
+
collectPolicyHashes,
|
|
1977
|
+
writeInstallLog,
|
|
1978
|
+
readInstallLog,
|
|
1979
|
+
installLogPath,
|
|
1980
|
+
legacyInstallLogPath,
|
|
1587
1981
|
HARNESS_ANCHOR_FILE,
|
|
1588
1982
|
HARNESS_IMPORT_LINE,
|
|
1589
1983
|
renderFillScaffold,
|
|
1590
1984
|
upsertHarnessImport,
|
|
1591
1985
|
stripHarnessImport,
|
|
1986
|
+
resolveRules,
|
|
1987
|
+
ALL_RULES,
|
|
1988
|
+
isCliNeutralTarget,
|
|
1989
|
+
buildManifest,
|
|
1592
1990
|
CATEGORIES,
|
|
1593
1991
|
CATEGORY_TITLES,
|
|
1594
|
-
buildManifest,
|
|
1595
1992
|
estimateTokens,
|
|
1596
1993
|
resolveBundleRoot,
|
|
1597
1994
|
assetCostRows,
|
|
@@ -1600,4 +1997,4 @@ export {
|
|
|
1600
1997
|
formatResidentCostLine,
|
|
1601
1998
|
formatResidentCostBlock
|
|
1602
1999
|
};
|
|
1603
|
-
//# sourceMappingURL=chunk-
|
|
2000
|
+
//# sourceMappingURL=chunk-GPH5AWLX.js.map
|