@uzysjung/agent-harness 26.161.0 → 26.163.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ko.md +9 -10
- package/README.md +9 -10
- package/dist/{chunk-MBRLCU5O.js → chunk-5T6MDT67.js} +208 -46
- package/dist/chunk-5T6MDT67.js.map +1 -0
- package/dist/index.js +4315 -1133
- package/dist/index.js.map +1 -1
- package/dist/trust-tier-drift.js +1 -1
- package/package.json +4 -3
- package/templates/codex/README.md +2 -2
- package/templates/codex/config.toml.template +15 -23
- package/templates/hooks/protect-files.sh +8 -3
- package/templates/hooks/session-start.sh +27 -6
- package/templates/scripts/check-absence.sh +5 -4
- package/templates/scripts/protect-branch.sh +2 -1
- package/templates/scripts/spec-drift-check.sh +19 -2
- package/dist/chunk-MBRLCU5O.js.map +0 -1
package/README.ko.md
CHANGED
|
@@ -16,21 +16,20 @@ AI 코딩 도구에 꼭 필요한 룰·훅·스킬만 남기고 나머지는 모
|
|
|
16
16
|
|
|
17
17
|
## 빠른 시작
|
|
18
18
|
|
|
19
|
-
Node 20 이상이 필요하다. 프로젝트 폴더에서 다음 명령을 실행한다:
|
|
19
|
+
Node 20.12 이상이 필요하다. 프로젝트 폴더에서 다음 명령을 실행한다:
|
|
20
20
|
|
|
21
21
|
```bash
|
|
22
22
|
npx -y @uzysjung/agent-harness
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
-
위저드가 다음
|
|
25
|
+
위저드가 다음 다섯 단계를 안내한다:
|
|
26
26
|
|
|
27
27
|
```
|
|
28
|
-
1/
|
|
29
|
-
2/
|
|
30
|
-
3/
|
|
31
|
-
4/
|
|
32
|
-
5/
|
|
33
|
-
6/6 Installing
|
|
28
|
+
1/5 Tracks 무엇을 만드는지(스택) — 항목을 미리 체크해 줄 뿐이다
|
|
29
|
+
2/5 CLI claude / codex / opencode / antigravity — 여러 개 가능
|
|
30
|
+
3/5 Install items 트랙에 맞는 항목이 미리 체크되어 있다. 원치 않는 것은 해제
|
|
31
|
+
4/5 Confirm 요약과 함께, 이 선택이 매 세션에 얹는 컨텍스트 크기를 보여 준다
|
|
32
|
+
5/5 Installing
|
|
34
33
|
```
|
|
35
34
|
|
|
36
35
|
그다음 같은 폴더에서 AI 코딩 도구를 실행하면, 첫 세션부터 룰과 스킬이 바로 적용된다:
|
|
@@ -51,7 +50,7 @@ claude # 또는 codex / opencode / agy
|
|
|
51
50
|
| **훅** | CLI 가 스스로 실행하는 스크립트. Claude Code 에는 2개가 들어간다: 하나는 세션을 시작할 때 스펙과 변경 기록을 불러오고, 다른 하나는 `.env` · lock 파일 · 인증서 편집을 막는다 — 하네스에서 "안 된다"고 제한하는 유일한 장치이며, 편집을 막을 때마다 로그를 한 줄 남긴다 | 자동으로 — 세션을 시작할 때, 편집하기 직전에 |
|
|
52
51
|
| **스킬** | 작업에 필요할 때 에이전트가 열어보는 단계별 절차서. 이 저장소에서 관리하는 방법론 스킬과 트랙에 필요한 기술 스택 스킬이 들어 있다(예: `csr-supabase` 의 React · shadcn · Supabase · Postgres) | 필요할 때만 — 한 줄짜리 설명만 항상 띄워 두고, 본문은 사용할 때만 읽는다 |
|
|
53
52
|
| **에이전트** | 메인 에이전트가 작업을 맡기는 조수. 모든 트랙에 독립 검증자 `reviewer` 가 있고, 개발 트랙에는 `implementer` 가 있으며, 그것을 쓰는 트랙에만 `data-analyst` · `strategist` 가 들어간다 | 메인 에이전트가 작업을 위임할 때 |
|
|
54
|
-
| **앵커** | CLI 가 매 세션마다 읽는 작업 원칙 파일. 원래 있던 내 `CLAUDE.md` 파일은 그대로 유지된다 — 하네스는 import
|
|
53
|
+
| **앵커** | CLI 가 매 세션마다 읽는 작업 원칙 파일. 원래 있던 내 `CLAUDE.md` 파일은 그대로 유지된다 — 하네스는 표식으로 감싼 import 블록(`@CLAUDE-uzys-harness.md` 참조와 그 아래 스킬 안내)만 추가할 뿐 나머지는 건드리지 않는다([어느 파일이 누구 것인가](docs/CONTEXT-FILES.md)) | 매 세션 |
|
|
55
54
|
|
|
56
55
|
모든 트랙에 공통으로 들어가는 방법론 스킬은 4가지다: `north-star` · `objective-brief` · `gh-issue-workflow` · `audit-harness-fit`. 번들 스킬은 `--with` 나 `--without` 뒤에 이름을 적어 추가하거나 뺄 수 있다.
|
|
57
56
|
|
|
@@ -94,7 +93,7 @@ claude # 또는 codex / opencode / agy
|
|
|
94
93
|
|
|
95
94
|
**기존 프로젝트에 적용해도 안전하다.** 내가 직접 수정한 파일을 교체해야 할 때는 타임스탬프를 붙여 백업본을 남기고 그 경로를 알려 준다. 내가 작성하거나 수정한 파일은 백업 없이 지우지 않으며, 기존에 있던 `.mcp.json` 서버 설정은 덮어쓰지 않고 내용을 병합한다([기존 프로젝트에 설치하기](docs/USAGE.md#installing-into-an-existing-project)).
|
|
96
95
|
|
|
97
|
-
|
|
96
|
+
**현재 프로젝트에만 설치된다.** `~/.opencode/` · `~/.gemini/` 디렉터리나 전역 npm 환경에는 아무것도 설치하지 않는다. 프로젝트 밖에 쓰는 옵트인 예외는 둘뿐이다 — Claude Code 플러그인(`claude` CLI 가 플러그인 캐시를 `~/.claude/plugins/` 에 저장하며 프로젝트는 메타데이터로 구분), 그리고 `--with-codex-trust`(Codex 가 프로젝트 설정을 읽도록 `~/.codex/config.toml` 에 `[projects]` trust 항목 하나 추가 — [자세히](docs/USAGE.md#scope)). `.claude/` 디렉터리 밖에는 `CLAUDE.md`/`AGENTS.md` 스캐폴드와 하네스 앵커, `.mcp.json`, `.gitignore` 파일에 추가하는 몇 줄(파일이 이미 있을 때), `csr-supabase`(및 `full`) 트랙의 `.env.example`, `--with ci-scaffold` 를 줬을 때만 `.github/workflows/`, 그리고 설치 기록을 남기는 `.uzys-agent-harness/` 디렉터리를 생성한다 — [전체 설치 목록 보기](docs/USAGE.md#what-the-harness-writes).
|
|
98
97
|
|
|
99
98
|
## 다른 도구를 이미 쓰고 있다면
|
|
100
99
|
|
package/README.md
CHANGED
|
@@ -16,21 +16,20 @@ Works with **Claude Code** · **Codex** · **OpenCode** · **Antigravity**.
|
|
|
16
16
|
|
|
17
17
|
## Quick start
|
|
18
18
|
|
|
19
|
-
You need Node 20 or newer. Run this in your project folder:
|
|
19
|
+
You need Node 20.12 or newer. Run this in your project folder:
|
|
20
20
|
|
|
21
21
|
```bash
|
|
22
22
|
npx -y @uzysjung/agent-harness
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
-
The wizard asks
|
|
25
|
+
The wizard asks five things:
|
|
26
26
|
|
|
27
27
|
```
|
|
28
|
-
1/
|
|
29
|
-
2/
|
|
30
|
-
3/
|
|
31
|
-
4/
|
|
32
|
-
5/
|
|
33
|
-
6/6 Installing
|
|
28
|
+
1/5 Tracks what you are building (a stack) — this only pre-checks items
|
|
29
|
+
2/5 CLI claude / codex / opencode / antigravity — one or more
|
|
30
|
+
3/5 Install items everything is pre-checked for your track; uncheck what you don't want
|
|
31
|
+
4/5 Confirm summary, plus how much context your selection adds to each session
|
|
32
|
+
5/5 Installing
|
|
34
33
|
```
|
|
35
34
|
|
|
36
35
|
Then open your AI coding tool in the same folder. The rules and skills are live from the first session:
|
|
@@ -51,7 +50,7 @@ No terminal for the wizard (CI, containers, scripts)? Use flags: `install --trac
|
|
|
51
50
|
| **Hooks** | Scripts your CLI runs on its own. Two, on Claude Code: one loads your spec and change log at session start; one blocks edits to `.env`, lock files, and certificates — the only thing in the harness that says "no", and it logs one line each time | Automatically, at session start or before an edit |
|
|
52
51
|
| **Skills** | Step-by-step playbooks the agent opens when a task calls for them — the method skills written in this repo, plus the stack skills your track needs (for example React, shadcn, Supabase, Postgres on `csr-supabase`) | Only when relevant — a one-line description stays loaded, the body loads on use |
|
|
53
52
|
| **Agents** | Helpers the main agent can hand work to: an independent `reviewer` on every track, `implementer` on dev tracks, `data-analyst` and `strategist` on the tracks that use them | When the main agent delegates |
|
|
54
|
-
| **Anchor** | One working-principles file your CLI reads every session. Your own `CLAUDE.md` stays yours — the harness adds one import
|
|
53
|
+
| **Anchor** | One working-principles file your CLI reads every session. Your own `CLAUDE.md` stays yours — the harness adds one marked import block (the `@CLAUDE-uzys-harness.md` reference and the skills guidance that follows it) and never touches the rest ([which file is whose](docs/CONTEXT-FILES.md)) | Every session |
|
|
55
54
|
|
|
56
55
|
Four method skills go to every track: `north-star`, `objective-brief`, `gh-issue-workflow`, `audit-harness-fit`. Bundled skills can be added or dropped by name with `--with` / `--without`.
|
|
57
56
|
|
|
@@ -94,7 +93,7 @@ In a terminal, `uninstall` offers three choices — one CLI, selected assets, or
|
|
|
94
93
|
|
|
95
94
|
**Safe on an existing project.** Before replacing a file you edited, the harness writes a timestamped backup next to it and prints the path. Nothing you wrote or edited is deleted without a backup beside it, and your existing `.mcp.json` servers are merged, not replaced ([installing into an existing project](docs/USAGE.md#installing-into-an-existing-project)).
|
|
96
95
|
|
|
97
|
-
**Your project only
|
|
96
|
+
**Your project only.** Nothing goes to `~/.opencode/`, `~/.gemini/`, or global npm. Two opt-in exceptions write outside the project: Claude Code plugins (the `claude` CLI keeps its plugin cache under `~/.claude/plugins/` and isolates projects by metadata), and `--with-codex-trust`, which adds one `[projects]` trust entry to `~/.codex/config.toml` so Codex reads its project config ([details](docs/USAGE.md#scope)). Besides `.claude/`, install writes `CLAUDE.md`/`AGENTS.md` scaffolds and their harness anchor, `.mcp.json`, a few `.gitignore` lines (when that file exists), an `.env.example` on `csr-supabase` (and `full`), `.github/workflows/` only with `--with ci-scaffold`, and its own record at `.uzys-agent-harness/` — [the full list](docs/USAGE.md#what-the-harness-writes).
|
|
98
97
|
|
|
99
98
|
## Already using another tool?
|
|
100
99
|
|
|
@@ -156,6 +156,22 @@ var EXTERNAL_ASSETS = [
|
|
|
156
156
|
// === Repo-bundled internal skills (uzys 1st-party, v26.87.0) ===
|
|
157
157
|
// 2026-08-02 복원 (ADR-062) — 이관(ADR-060) 후 감사에서 본문 열화 104건(dropped 76 ·
|
|
158
158
|
// damaged 28)이 확인돼 9종을 이 리포 번들로 되돌렸다. 아래 10종 전부 `kind: "internal"`.
|
|
159
|
+
// #602 — ui-visual-review: TRACKS.md 가 말하는 트랙 추종 번들 스킬("follows the track, not
|
|
160
|
+
// the catalog"). internal 카탈로그 자산으로 등록해 selectedInternalSkills 흐름에 태우면
|
|
161
|
+
// .agents/skills(비-Claude 자리)에도 깔린다 — 종전의 .claude/ 전용 배선과 어긋나던 것.
|
|
162
|
+
{
|
|
163
|
+
id: "ui-visual-review",
|
|
164
|
+
tier: "official",
|
|
165
|
+
// uzys 자사 스킬
|
|
166
|
+
description: "UI visual review \u2014 screenshot capture, baseline diff, and a review gate for UI changes (ships with UI tracks; ssr-htmx excluded)",
|
|
167
|
+
category: "frontend",
|
|
168
|
+
source: "uzys",
|
|
169
|
+
condition: {
|
|
170
|
+
kind: "any-track",
|
|
171
|
+
tracks: ["csr-supabase", "csr-fastify", "csr-fastapi", "ssr-nextjs", "full"]
|
|
172
|
+
},
|
|
173
|
+
method: { kind: "internal", key: "ui-visual-review" }
|
|
174
|
+
},
|
|
159
175
|
{
|
|
160
176
|
id: "compaction-handoff",
|
|
161
177
|
tier: "official",
|
|
@@ -870,6 +886,10 @@ var DEV_METHOD_SKILL_IDS = [
|
|
|
870
886
|
];
|
|
871
887
|
var INTERNAL_BUNDLED_SKILL_IDS = [
|
|
872
888
|
...DEV_METHOD_SKILL_IDS,
|
|
889
|
+
// #602 — 트랙을 따라가는 번들 스킬(csr-*·ssr-*·full). selectedInternalSkills 로 흘러야
|
|
890
|
+
// .agents/skills(비-Claude 자리)에도 깔린다 — manifest 의 UI_SKILL_DIRS 배선은 .claude/ 만
|
|
891
|
+
// 알아 TRACKS.md 와 어긋났다. 조건은 buildAssetSpec 이 UI 트랙으로 건다.
|
|
892
|
+
"ui-visual-review",
|
|
873
893
|
"north-star",
|
|
874
894
|
"gh-issue-workflow",
|
|
875
895
|
"objective-brief",
|
|
@@ -1134,13 +1154,31 @@ ${block}
|
|
|
1134
1154
|
`;
|
|
1135
1155
|
}
|
|
1136
1156
|
function stripHarnessImport(text) {
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
const
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1157
|
+
if (!text.includes(IMPORT_MARKER_START) && !text.includes(IMPORT_MARKER_END)) return null;
|
|
1158
|
+
const endsWithNewline = text.endsWith("\n");
|
|
1159
|
+
const out = [];
|
|
1160
|
+
const lines = text.split("\n");
|
|
1161
|
+
let i = 0;
|
|
1162
|
+
while (i < lines.length) {
|
|
1163
|
+
const line = lines[i] ?? "";
|
|
1164
|
+
if (line.includes(IMPORT_MARKER_START)) {
|
|
1165
|
+
let j = i;
|
|
1166
|
+
while (j < lines.length && !(lines[j] ?? "").includes(IMPORT_MARKER_END)) j++;
|
|
1167
|
+
i = j < lines.length ? j + 1 : i + 1;
|
|
1168
|
+
if (out.length > 0 && out[out.length - 1] === "") out.pop();
|
|
1169
|
+
continue;
|
|
1170
|
+
}
|
|
1171
|
+
if (line.includes(IMPORT_MARKER_END) || line.trim() === HARNESS_IMPORT_LINE) {
|
|
1172
|
+
i++;
|
|
1173
|
+
continue;
|
|
1174
|
+
}
|
|
1175
|
+
out.push(line);
|
|
1176
|
+
i++;
|
|
1177
|
+
}
|
|
1178
|
+
let result = out.join("\n");
|
|
1179
|
+
if (endsWithNewline && result !== "" && !result.endsWith("\n")) result += "\n";
|
|
1180
|
+
if (!endsWithNewline && result.endsWith("\n")) result = result.slice(0, -1);
|
|
1181
|
+
return result === text ? null : result;
|
|
1144
1182
|
}
|
|
1145
1183
|
function hasHarnessImport(text) {
|
|
1146
1184
|
if (text.includes(IMPORT_MARKER_START)) return true;
|
|
@@ -1359,8 +1397,8 @@ init_esm_shims();
|
|
|
1359
1397
|
|
|
1360
1398
|
// src/install-log.ts
|
|
1361
1399
|
init_esm_shims();
|
|
1362
|
-
import { createHash } from "crypto";
|
|
1363
|
-
import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync3, rmSync, writeFileSync } from "fs";
|
|
1400
|
+
import { createHash as createHash2 } from "crypto";
|
|
1401
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync3, rmSync as rmSync2, writeFileSync } from "fs";
|
|
1364
1402
|
import { dirname as dirname2, join as join3 } from "path";
|
|
1365
1403
|
|
|
1366
1404
|
// src/cli-targets.ts
|
|
@@ -1414,17 +1452,20 @@ function targetsInclude(targets, base) {
|
|
|
1414
1452
|
|
|
1415
1453
|
// src/fs-ops.ts
|
|
1416
1454
|
init_esm_shims();
|
|
1455
|
+
import { createHash } from "crypto";
|
|
1417
1456
|
import {
|
|
1418
1457
|
copyFileSync,
|
|
1419
|
-
cpSync,
|
|
1420
1458
|
existsSync as existsSync2,
|
|
1421
1459
|
constants as fsConstants,
|
|
1422
1460
|
mkdirSync,
|
|
1423
1461
|
readdirSync,
|
|
1424
1462
|
readFileSync as readFileSync2,
|
|
1463
|
+
readlinkSync,
|
|
1425
1464
|
realpathSync,
|
|
1426
1465
|
renameSync,
|
|
1427
|
-
rmdirSync
|
|
1466
|
+
rmdirSync,
|
|
1467
|
+
rmSync,
|
|
1468
|
+
symlinkSync
|
|
1428
1469
|
} from "fs";
|
|
1429
1470
|
import { dirname, join as join2 } from "path";
|
|
1430
1471
|
function ensureDir(path2) {
|
|
@@ -1480,17 +1521,27 @@ function copyBackupDir(target, now = /* @__PURE__ */ new Date()) {
|
|
|
1480
1521
|
return null;
|
|
1481
1522
|
}
|
|
1482
1523
|
const backup = claimBackupPath(`${target}.backup-${formatStamp(now)}`, (c) => mkdirSync(c));
|
|
1483
|
-
|
|
1524
|
+
try {
|
|
1525
|
+
copyTreePreservingLinks(realpathSync(target), backup);
|
|
1526
|
+
} catch (e) {
|
|
1527
|
+
rmSync(backup, { recursive: true, force: true });
|
|
1528
|
+
const detail = e instanceof Error ? e.message : String(e);
|
|
1529
|
+
throw new Error(`backup of ${target} failed, partial copy removed \u2014 ${detail}`);
|
|
1530
|
+
}
|
|
1484
1531
|
return backup;
|
|
1485
1532
|
}
|
|
1486
|
-
function
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1533
|
+
function copyTreePreservingLinks(source, target) {
|
|
1534
|
+
mkdirSync(target, { recursive: true });
|
|
1535
|
+
for (const entry of readdirSync(source, { withFileTypes: true })) {
|
|
1536
|
+
const src = join2(source, entry.name);
|
|
1537
|
+
const dst = join2(target, entry.name);
|
|
1538
|
+
if (entry.isSymbolicLink()) {
|
|
1539
|
+
symlinkSync(readlinkSync(src), dst);
|
|
1540
|
+
continue;
|
|
1541
|
+
}
|
|
1542
|
+
if (entry.isDirectory()) copyTreePreservingLinks(src, dst);
|
|
1543
|
+
else if (entry.isFile()) copyFile(src, dst);
|
|
1492
1544
|
}
|
|
1493
|
-
return backupFile(target, now);
|
|
1494
1545
|
}
|
|
1495
1546
|
function backupFile(target, now = /* @__PURE__ */ new Date()) {
|
|
1496
1547
|
return claimBackupPath(
|
|
@@ -1527,6 +1578,45 @@ function ensureProjectSkeleton(projectDir) {
|
|
|
1527
1578
|
mkdirSync(join2(projectDir, d), { recursive: true });
|
|
1528
1579
|
}
|
|
1529
1580
|
}
|
|
1581
|
+
function dirTreesIdentical(a, b) {
|
|
1582
|
+
if (!existsSync2(a) || !existsSync2(b)) return false;
|
|
1583
|
+
const signature = (root, rel = "", out = /* @__PURE__ */ new Map()) => {
|
|
1584
|
+
for (const e of readdirSync(join2(root, rel), { withFileTypes: true })) {
|
|
1585
|
+
const r = rel === "" ? e.name : `${rel}/${e.name}`;
|
|
1586
|
+
if (e.isSymbolicLink()) out.set(r, `link:${readlinkSync(join2(root, r))}`);
|
|
1587
|
+
else if (e.isDirectory()) signature(root, r, out);
|
|
1588
|
+
else if (e.isFile()) {
|
|
1589
|
+
out.set(
|
|
1590
|
+
r,
|
|
1591
|
+
createHash("sha256").update(readFileSync2(join2(root, r))).digest("hex")
|
|
1592
|
+
);
|
|
1593
|
+
}
|
|
1594
|
+
}
|
|
1595
|
+
return out;
|
|
1596
|
+
};
|
|
1597
|
+
const sa = signature(a);
|
|
1598
|
+
const sb = signature(b);
|
|
1599
|
+
if (sa.size !== sb.size) return false;
|
|
1600
|
+
for (const [rel, sha] of sa) {
|
|
1601
|
+
if (sb.get(rel) !== sha) return false;
|
|
1602
|
+
}
|
|
1603
|
+
return true;
|
|
1604
|
+
}
|
|
1605
|
+
function backupIfLossyUtf8(target, now = /* @__PURE__ */ new Date()) {
|
|
1606
|
+
if (!existsSync2(target)) return null;
|
|
1607
|
+
let raw;
|
|
1608
|
+
try {
|
|
1609
|
+
raw = readFileSync2(target);
|
|
1610
|
+
} catch {
|
|
1611
|
+
return null;
|
|
1612
|
+
}
|
|
1613
|
+
if (Buffer.from(raw.toString("utf8")).equals(raw)) return null;
|
|
1614
|
+
const backup = backupFile(target, now);
|
|
1615
|
+
console.error(
|
|
1616
|
+
`\u26A0 ${target} contains bytes that are not valid UTF-8 \u2014 rewriting would alter them permanently. Original bytes saved as ${backup}`
|
|
1617
|
+
);
|
|
1618
|
+
return backup;
|
|
1619
|
+
}
|
|
1530
1620
|
|
|
1531
1621
|
// src/install-log.ts
|
|
1532
1622
|
var INSTALL_LOG_FILENAME = ".harness-install.json";
|
|
@@ -1626,7 +1716,7 @@ function mergeRootFiles(previous, current) {
|
|
|
1626
1716
|
const prior = byPath.get(file.path);
|
|
1627
1717
|
byPath.set(
|
|
1628
1718
|
file.path,
|
|
1629
|
-
prior ? {
|
|
1719
|
+
prior && file.change !== "displaced" ? {
|
|
1630
1720
|
path: file.path,
|
|
1631
1721
|
change: prior.change === "created" ? "created" : file.change,
|
|
1632
1722
|
notes: [.../* @__PURE__ */ new Set([...prior.notes, ...file.notes])]
|
|
@@ -1666,7 +1756,7 @@ function mergeAssets(previous, current) {
|
|
|
1666
1756
|
];
|
|
1667
1757
|
}
|
|
1668
1758
|
function hashContent(content) {
|
|
1669
|
-
return
|
|
1759
|
+
return createHash2("sha256").update(content, "utf8").digest("hex");
|
|
1670
1760
|
}
|
|
1671
1761
|
function isHarnessOwned(baseline, key, current) {
|
|
1672
1762
|
const recorded = baseline.get(key);
|
|
@@ -1712,26 +1802,45 @@ function migrateAwayLegacyLog(projectDir) {
|
|
|
1712
1802
|
const legacy = legacyInstallLogPath(projectDir);
|
|
1713
1803
|
if (!existsSync3(legacy)) return;
|
|
1714
1804
|
try {
|
|
1715
|
-
|
|
1805
|
+
rmSync2(legacy);
|
|
1716
1806
|
} catch {
|
|
1717
1807
|
}
|
|
1718
1808
|
}
|
|
1719
1809
|
function readInstallLog(projectDir) {
|
|
1810
|
+
return readInstallLogStatus(projectDir).log;
|
|
1811
|
+
}
|
|
1812
|
+
function readInstallLogStatus(projectDir) {
|
|
1720
1813
|
const path2 = [installLogPath(projectDir), legacyInstallLogPath(projectDir)].find(
|
|
1721
1814
|
(p) => existsSync3(p)
|
|
1722
1815
|
);
|
|
1723
|
-
if (!path2) return null;
|
|
1816
|
+
if (!path2) return { status: "missing", log: null };
|
|
1817
|
+
let parsed;
|
|
1724
1818
|
try {
|
|
1725
|
-
|
|
1726
|
-
if (Array.isArray(parsed.assets)) {
|
|
1727
|
-
parsed.assets = parsed.assets.map(
|
|
1728
|
-
(a) => a.method === "npm-global" ? { ...a, method: "npm" } : a
|
|
1729
|
-
);
|
|
1730
|
-
}
|
|
1731
|
-
return parsed;
|
|
1819
|
+
parsed = JSON.parse(readFileSync3(path2, "utf8"));
|
|
1732
1820
|
} catch {
|
|
1733
|
-
return null;
|
|
1821
|
+
return { status: "corrupted", log: null };
|
|
1822
|
+
}
|
|
1823
|
+
if (!isValidInstallLogShape(parsed)) return { status: "corrupted", log: null };
|
|
1824
|
+
const log = parsed;
|
|
1825
|
+
if (Array.isArray(log.assets)) {
|
|
1826
|
+
log.assets = log.assets.map(
|
|
1827
|
+
(a) => a.method === "npm-global" ? { ...a, method: "npm" } : a
|
|
1828
|
+
);
|
|
1734
1829
|
}
|
|
1830
|
+
return { status: "ok", log };
|
|
1831
|
+
}
|
|
1832
|
+
function isValidInstallLogShape(value) {
|
|
1833
|
+
if (typeof value !== "object" || value === null) return false;
|
|
1834
|
+
const v = value;
|
|
1835
|
+
const spec = v.spec;
|
|
1836
|
+
if (typeof spec !== "object" || spec === null) return false;
|
|
1837
|
+
if (!Array.isArray(spec.tracks)) return false;
|
|
1838
|
+
if (!Array.isArray(v.assets)) return false;
|
|
1839
|
+
if (typeof v.templates !== "object" || v.templates === null) return false;
|
|
1840
|
+
return true;
|
|
1841
|
+
}
|
|
1842
|
+
function corruptedInstallLogMessage(projectDir) {
|
|
1843
|
+
return `install log is corrupted at ${installLogPath(projectDir)} \u2014 run install --reinstall to rebuild it`;
|
|
1735
1844
|
}
|
|
1736
1845
|
function installLogPath(projectDir) {
|
|
1737
1846
|
return join3(projectDir, INSTALL_LOG_DIR, INSTALL_LOG_FILENAME);
|
|
@@ -1752,7 +1861,6 @@ function buildAssetSpec(ctx) {
|
|
|
1752
1861
|
}
|
|
1753
1862
|
var all = () => true;
|
|
1754
1863
|
var dev = (s) => hasDevTrack(s.tracks);
|
|
1755
|
-
var ui = (s) => hasUiTrack(s.tracks);
|
|
1756
1864
|
var onTracks = (pattern) => (s) => anyTrack(s.tracks, pattern);
|
|
1757
1865
|
var COMMON_RULES = ["git-policy", "change-management", "doc-governance"];
|
|
1758
1866
|
var DEV_RULES = ["test-policy", "ship-checklist"];
|
|
@@ -1808,11 +1916,63 @@ var RETIRED_AGENTS = [
|
|
|
1808
1916
|
{ id: "silent-failure-hunter", instead: "Claude Code \uC758 `/code-review` \uAC00 \uAC19\uC740 \uC77C\uC744 \uD55C\uB2E4" },
|
|
1809
1917
|
{ id: "build-error-resolver", instead: "\uBAA8\uB378\uC774 \uAE30\uBCF8\uC73C\uB85C \uD558\uB294 \uC77C\uC774\uB77C \uB300\uCCB4 \uC790\uC0B0\uC774 \uC5C6\uB2E4" }
|
|
1810
1918
|
];
|
|
1811
|
-
var
|
|
1919
|
+
var RETIRED_PATHS = [
|
|
1920
|
+
// rules (23)
|
|
1921
|
+
"rules/api-contract.md",
|
|
1922
|
+
"rules/benchmark-parity.md",
|
|
1923
|
+
"rules/code-style.md",
|
|
1924
|
+
"rules/commit-policy.md",
|
|
1925
|
+
"rules/data-analysis.md",
|
|
1926
|
+
"rules/database.md",
|
|
1927
|
+
"rules/design-workflow.md",
|
|
1928
|
+
"rules/ecc-git-workflow.md",
|
|
1929
|
+
"rules/ecc-performance-common.md",
|
|
1930
|
+
"rules/ecc-security-common.md",
|
|
1931
|
+
"rules/ecc-testing.md",
|
|
1932
|
+
"rules/error-handling.md",
|
|
1933
|
+
"rules/gates-taxonomy.md",
|
|
1934
|
+
"rules/gsd-planner-antipatterns.md",
|
|
1935
|
+
"rules/htmx.md",
|
|
1936
|
+
"rules/model-routing.md",
|
|
1937
|
+
"rules/nextjs.md",
|
|
1938
|
+
"rules/no-false-ship.md",
|
|
1939
|
+
"rules/playwright-launch.md",
|
|
1940
|
+
"rules/pyside6.md",
|
|
1941
|
+
"rules/seo.md",
|
|
1942
|
+
"rules/shadcn.md",
|
|
1943
|
+
"rules/tauri.md",
|
|
1944
|
+
// agents (5)
|
|
1945
|
+
"agents/build-error-resolver.md",
|
|
1946
|
+
"agents/code-reviewer.md",
|
|
1947
|
+
"agents/plan-checker.md",
|
|
1948
|
+
"agents/security-reviewer.md",
|
|
1949
|
+
"agents/silent-failure-hunter.md",
|
|
1950
|
+
// commands/uzys (7)
|
|
1951
|
+
"commands/uzys/auto.md",
|
|
1952
|
+
"commands/uzys/build.md",
|
|
1953
|
+
"commands/uzys/plan.md",
|
|
1954
|
+
"commands/uzys/review.md",
|
|
1955
|
+
"commands/uzys/ship.md",
|
|
1956
|
+
"commands/uzys/spec.md",
|
|
1957
|
+
"commands/uzys/test.md",
|
|
1958
|
+
// hooks (10)
|
|
1959
|
+
"hooks/agentshield-gate.sh",
|
|
1960
|
+
"hooks/checkpoint-snapshot.sh",
|
|
1961
|
+
"hooks/codebase-map.sh",
|
|
1962
|
+
"hooks/gate-check.sh",
|
|
1963
|
+
"hooks/hito-counter.sh",
|
|
1964
|
+
"hooks/karpathy-gate.sh",
|
|
1965
|
+
"hooks/mcp-pre-exec.sh",
|
|
1966
|
+
"hooks/spec-drift-check.sh",
|
|
1967
|
+
"hooks/task-brief-nudge.sh",
|
|
1968
|
+
"hooks/uncommitted-check.sh"
|
|
1969
|
+
];
|
|
1970
|
+
var RETIRED_AGENT_IDS = RETIRED_PATHS.filter(
|
|
1971
|
+
(p) => p.startsWith("agents/")
|
|
1972
|
+
).map((p) => p.slice("agents/".length).replace(/\.md$/, ""));
|
|
1812
1973
|
var ALWAYS_HOOKS = ["session-start.sh", "protect-files.sh"];
|
|
1813
1974
|
var COMMON_SKILL_DIRS = [];
|
|
1814
1975
|
var DEV_SKILL_DIRS = [];
|
|
1815
|
-
var UI_SKILL_DIRS = ["ui-visual-review"];
|
|
1816
1976
|
function isCliNeutralTarget(target) {
|
|
1817
1977
|
return target.startsWith(`${INSTALL_LOG_DIR}/`);
|
|
1818
1978
|
}
|
|
@@ -1887,7 +2047,14 @@ function buildManifest(spec) {
|
|
|
1887
2047
|
source: `skills/${sd}`,
|
|
1888
2048
|
target: `.claude/skills/${sd}`,
|
|
1889
2049
|
type: "dir",
|
|
1890
|
-
|
|
2050
|
+
// #602 — selectedInternalSkills 가 SSOT(위저드 해제·--without 반영). `tracks` 만 담긴
|
|
2051
|
+
// 스펙(listBaselineTargets·위저드 1단계 등)에서는 카탈로그 조건으로 판정한다 —
|
|
2052
|
+
// buildAssetSpec 와 같은 isAssetSelected 라 두 번 적는 사본이 아니라 같은 원천이다.
|
|
2053
|
+
applies: (s) => s.selectedInternalSkills !== void 0 ? s.selectedInternalSkills.includes(sd) : (
|
|
2054
|
+
// #602 폴백은 트랙 추종 번들 스킬 한정 — 카탈로그 옵트인(compaction-handoff 등)은
|
|
2055
|
+
// 선택 목록 없이는 깔리지 않는다(위저드 해제·--without 의 SSOT 를 우회하는 금이 된다).
|
|
2056
|
+
sd === "ui-visual-review" && isAssetSelected(sd, { tracks: s.tracks, options: { withCodexTrust: false } })
|
|
2057
|
+
)
|
|
1891
2058
|
});
|
|
1892
2059
|
}
|
|
1893
2060
|
for (const sd of DEV_SKILL_DIRS) {
|
|
@@ -1898,14 +2065,6 @@ function buildManifest(spec) {
|
|
|
1898
2065
|
applies: dev
|
|
1899
2066
|
});
|
|
1900
2067
|
}
|
|
1901
|
-
for (const sd of UI_SKILL_DIRS) {
|
|
1902
|
-
m.push({
|
|
1903
|
-
source: `skills/${sd}`,
|
|
1904
|
-
target: `.claude/skills/${sd}`,
|
|
1905
|
-
type: "dir",
|
|
1906
|
-
applies: ui
|
|
1907
|
-
});
|
|
1908
|
-
}
|
|
1909
2068
|
for (const h of ALWAYS_HOOKS) {
|
|
1910
2069
|
m.push({
|
|
1911
2070
|
source: `hooks/${h}`,
|
|
@@ -2001,10 +2160,11 @@ export {
|
|
|
2001
2160
|
copyDir,
|
|
2002
2161
|
backupDir,
|
|
2003
2162
|
copyBackupDir,
|
|
2004
|
-
backupFileIfChanged,
|
|
2005
2163
|
backupFile,
|
|
2006
2164
|
listFilesRecursive,
|
|
2007
2165
|
ensureProjectSkeleton,
|
|
2166
|
+
dirTreesIdentical,
|
|
2167
|
+
backupIfLossyUtf8,
|
|
2008
2168
|
INSTALL_LOG_FILENAME,
|
|
2009
2169
|
INSTALL_LOG_DIR,
|
|
2010
2170
|
POLICY_DIRS,
|
|
@@ -2017,17 +2177,19 @@ export {
|
|
|
2017
2177
|
collectPolicyHashes,
|
|
2018
2178
|
writeInstallLog,
|
|
2019
2179
|
readInstallLog,
|
|
2180
|
+
readInstallLogStatus,
|
|
2181
|
+
corruptedInstallLogMessage,
|
|
2020
2182
|
installLogPath,
|
|
2021
2183
|
legacyInstallLogPath,
|
|
2022
2184
|
buildAssetSpec,
|
|
2023
2185
|
resolveRules,
|
|
2024
|
-
ALL_RULES,
|
|
2025
2186
|
TRACK_AGENTS,
|
|
2026
2187
|
RETIRED_AGENTS,
|
|
2188
|
+
RETIRED_PATHS,
|
|
2027
2189
|
RETIRED_AGENT_IDS,
|
|
2028
2190
|
isCliNeutralTarget,
|
|
2029
2191
|
buildManifest,
|
|
2030
2192
|
CATEGORIES,
|
|
2031
2193
|
CATEGORY_TITLES
|
|
2032
2194
|
};
|
|
2033
|
-
//# sourceMappingURL=chunk-
|
|
2195
|
+
//# sourceMappingURL=chunk-5T6MDT67.js.map
|