@uzysjung/agent-harness 26.174.0 → 26.176.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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../node_modules/tsup/assets/esm_shims.js","../src/types.ts","../src/external-assets.ts","../src/track-match.ts","../src/manifest.ts","../src/install-log.ts","../package.json","../src/adapters/shared-files.ts","../src/cli-targets.ts","../src/fs-ops.ts","../src/outside-project.ts","../src/track-heal.ts","../src/project-claude-merge.ts","../src/context-cost.ts","../src/mcp-merge.ts","../src/adapters/index.ts","../src/adapters/json-keys.ts","../src/hook-ref.ts","../src/legacy-fingerprints.ts","../src/adapters/contract.ts","../src/adapters/lines.ts","../src/adapters/text-runs.ts","../src/adapters/marker-md.ts","../src/agents-md-merge.ts","../src/adapters/toml-region.ts","../node_modules/smol-toml/dist/index.js","../node_modules/smol-toml/dist/parse.js","../node_modules/smol-toml/dist/struct.js","../node_modules/smol-toml/dist/primitive.js","../node_modules/smol-toml/dist/error.js","../node_modules/smol-toml/dist/extract.js","../node_modules/smol-toml/dist/date.js","../node_modules/smol-toml/dist/util.js","../node_modules/smol-toml/dist/stringify.js","../src/mcp-prior.ts","../src/categories.ts"],"sourcesContent":["// Shim globals in esm bundle\nimport path from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\nconst getFilename = () => fileURLToPath(import.meta.url)\nconst getDirname = () => path.dirname(getFilename())\n\nexport const __dirname = /* @__PURE__ */ getDirname()\nexport const __filename = /* @__PURE__ */ getFilename()\n","/** Available installation tracks. v0.5.0 — 11 Track (PM/Growth Marketing 추가). */\nexport const TRACKS = [\n // #456 (2026-09-20) — 스택 무관: 원칙·방법론·테스트 스킬만. 스택을 아직 안 정한 프로젝트용.\n \"base\",\n \"tooling\",\n \"csr-supabase\",\n \"csr-fastify\",\n \"csr-fastapi\",\n \"ssr-htmx\",\n // #820 — htmx 의 Node 백엔드. `ssr-htmx`(FastAPI) 의 id 는 설치 기록이 갖고 있어 그대로 두고 옆에 더했다.\n \"ssr-htmx-fastify\",\n \"ssr-nextjs\",\n \"data\",\n \"executive\",\n \"full\",\n \"project-management\",\n \"growth-marketing\",\n] as const;\nexport type Track = (typeof TRACKS)[number];\n\nexport function isTrack(value: unknown): value is Track {\n return typeof value === \"string\" && (TRACKS as readonly string[]).includes(value);\n}\n\n/**\n * CLI target — multi-select base × combination (v0.8.0 — alias 제거).\n *\n * Base CLI: claude / codex / opencode / antigravity (4 base). 15 combinations possible (2^4 - 1, empty 제외).\n * Legacy `both` / `all` alias는 v0.7.0 deprecation 거쳐 v0.8.0에서 invalid input.\n * Migration: `--cli claude --cli codex` (repeatable) 또는 multiselect 인터랙티브.\n */\nexport const CLI_BASES = [\"claude\", \"codex\", \"opencode\", \"antigravity\"] as const;\nexport type CliBase = (typeof CLI_BASES)[number];\n\nexport function isCliBase(value: unknown): value is CliBase {\n return typeof value === \"string\" && (CLI_BASES as readonly string[]).includes(value);\n}\n\n/** Sorted readonly array of CliBase. install pipeline의 분기 input. */\nexport type CliTargets = ReadonlyArray<CliBase>;\n\n/**\n * v26.64.0 (ADR-020) — Installation scope.\n *\n * - \"project\" (default): claude plugin `--scope project`, npx skills (default), npm `--save-dev`,\n * codex/opencode → 프로젝트 dir (`.codex/`, `.opencode/`). 사용자 명시 동의 없이는 글로벌 미수정.\n * - \"global\" (opt-in): claude plugin `--scope user`, npx skills `-g`, npm `-g`,\n * codex/opencode → `~/.codex/`, `~/.opencode/`. 사용자가 interactive 또는 `--scope global` 로 명시.\n *\n * NORTH_STAR.md D16 본질 — 사용자 무인지 글로벌 write 금지.\n */\nexport const INSTALL_SCOPES = [\"project\", \"global\"] as const;\nexport type InstallScope = (typeof INSTALL_SCOPES)[number];\n\nexport function isInstallScope(value: unknown): value is InstallScope {\n return typeof value === \"string\" && (INSTALL_SCOPES as readonly string[]).includes(value);\n}\n\n/**\n * v26.64.0 (ADR-020) — `InstallSpec.scope` 가 optional 이므로 사용 시 default \"project\" 로 normalize.\n * 모든 분기 코드 (external-installer, codex/*, opencode/*) 는 이 함수로 scope 결정.\n */\nexport function resolveScope(scope: InstallScope | undefined): InstallScope {\n return scope ?? \"project\";\n}\n\n/**\n * Optional opt-in feature flags collected interactively.\n *\n * v26.81.0 (ADR-022, BREAKING) — 자산 1:1 boolean 13종(withGsd/withEcc/withTob/\n * withSuperpowers/withAddyAgentSkills/withWshobsonAgents/withOpenspec/withBmad/\n * withClaudeVideo/withUnderstandAnything/withAgentmemory + withTauri/withUzysHarness)\n * 완전 삭제. 자산 선택은 `userOverride.forceInclude`(wizard 체크 / `--with <id>`)로\n * 일원화 — 자산 추가 시 플래그 코드 0곳. 잔존 = 자산이 아닌 **설치 동작 옵션**만.\n *\n * #492 — `withPrune`(ECC prune 실행)도 빠졌다. 그 옵션이 켜던 자산(`ecc-prune`)과 전제 자산\n * (`ecc-plugin`)이 카탈로그에서 은퇴해 남길 동작이 없다.\n */\nexport interface OptionFlags {\n /** Codex global opt-in: ~/.codex/config.toml [projects.\"...\"] trust entry. D16 — 사용자 명시 동의 필수. */\n withCodexTrust: boolean;\n}\n\nexport const DEFAULT_OPTIONS: OptionFlags = {\n withCodexTrust: false,\n};\n\n/**\n * #480 — `update` 가 갱신하는 묶음. 설치자가 고를 수 있다(`update --only <group>` · 위저드 체크박스).\n * 안 고르면 전부. 룰·앵커를 고친 설치자가 스킬만 받고 싶을 때 쓴다.\n */\nexport const UPDATE_GROUPS = [\n \"skills\",\n \"new-skills\",\n \"rules\",\n \"anchor\",\n \"hooks\",\n \"external\",\n] as const;\nexport type UpdateGroup = (typeof UPDATE_GROUPS)[number];\n\n/** Aggregate result of interactive flow — the spec the install pipeline consumes. */\nexport interface InstallSpec {\n tracks: Track[];\n options: OptionFlags;\n /** v0.7.0 — sorted readonly array of CliBase (이전: single CliMode). */\n cli: CliTargets;\n projectDir: string;\n /**\n * v26.64.0 (ADR-020) — Installation scope. Default \"project\" — 사용자 무인지 글로벌 write 방지.\n * Interactive prompt 또는 `--scope global` 명시 시에만 \"global\".\n * Optional + default \"project\" — 명시 안 한 사용처 (기존 tests / wizard non-scope-aware) 안전한 fallback.\n * 사용처는 `resolveScope(spec.scope)` 또는 `scope ?? \"project\"` 로 normalize.\n */\n scope?: InstallScope;\n /**\n * #480 — update 모드에서 갱신할 묶음. undefined = 전부. `install` 은 읽지 않는다.\n */\n updateOnly?: ReadonlyArray<UpdateGroup>;\n /**\n * v26.47.0 — User-level override of preset/option condition (Phase C full).\n * `forceInclude`: condition 무관 강제 포함 / `forceExclude`: condition 무관 강제 제외.\n * 우선순위: forceExclude > forceInclude > condition.\n * Optional — 미제공 시 기존 condition 만 평가 (backward compat).\n */\n userOverride?: {\n forceInclude: ReadonlyArray<string>;\n forceExclude: ReadonlyArray<string>;\n };\n /**\n * 2026-08-16 — 사용자가 **해제한** baseline 자산 id (`baseline:<kind>/<name>`).\n *\n * `userOverride.forceExclude` 와 나눈 이유는 대상이 다른 목록이기 때문이다: 저쪽은\n * `EXTERNAL_ASSETS` 의 id 를 보고, 이쪽은 `manifest.ts` 가 트랙에서 유도하는 설치 대상을 본다.\n * 한 필드에 섞으면 오타 하나가 어느 목록의 미스인지 판별할 수 없고, 조용히 아무것도 안 하는\n * 제외 지시가 생긴다.\n *\n * 비어 있거나 없으면 트랙이 고른 것을 전부 깐다 — 즉 **기본 동작은 그대로**다.\n */\n baselineExclude?: ReadonlyArray<string>;\n /**\n * 설계 selection-record §1 — 이번 선택이 어디서 왔나(설치 기록 이력 `selections[].via`). 없으면 플래그.\n */\n selectionVia?: \"flag\" | \"wizard\";\n /**\n * ADR-099 R4 — `--without` 으로 받은 함께 쓰는 파일의 키 id(`mcp:github` · `settings:statusLine`). install 이\n * `excluded` 에 더하고, 그 키는 기록 sha 와 같을 때만 파일에서 걷는다.\n */\n keyExclude?: ReadonlyArray<string>;\n}\n","/**\n * External asset matrix — bash setup-harness.sh L791~1067 등가 데이터.\n *\n * SPEC: docs/specs/cli-rewrite-completeness.md F3\n * Source: setup-harness.sh@911c246~1 (v27.18 직전, bash cutover 전)\n *\n * Track 또는 옵션 조건이 충족되면 install pipeline에서 method를 호출.\n * 실패는 \"warn-skip\" — 종료 시 누락 자산 보고 (OQ1 결정).\n *\n * code-style 800줄 cap 예외: 본 파일은 ~85% 가 큐레이션 자산 카탈로그(43 entry × ~13줄)\n * = 데이터. 로직(interface·condition 평가·shouldInstallAsset)은 소량. 데이터/로직 분리는\n * 별도 사이클(Phase R) 후보 — 현재는 카탈로그 단일 SSOT 가독성 우선. (v26.79.0 기준 802줄)\n */\n\nimport type { Category, Source } from \"./categories.js\";\nimport { hasDevTrack, hasUiTrack } from \"./track-match.js\";\nimport { CLI_BASES, type CliTargets, type OptionFlags, TRACKS, type Track } from \"./types.js\";\n\nexport type ExternalAssetMethod =\n /** `npx skills add <source>[ --skill <name>] --yes` */\n | { kind: \"skill\"; source: string; skill?: string }\n /** `claude plugin marketplace add <marketplace>` + `claude plugin install <pluginId>` */\n | { kind: \"plugin\"; marketplace: string; pluginId: string }\n /**\n * `npm install --save-dev <pkg>@<version>` (global scope 시 `-g`).\n * v26.80.0 — `version` 필수 (pinning). vetting 은 시점 검증인데 unpinned 는 미래 코드\n * 실행 = hijacked vetted repo 직행 구멍 (ADR-021 \"지속 검증\" 주장과 모순). pkg 는 bare\n * 이름 유지 (detectVersion 이 `<npm root>/<pkg>/package.json` 경로로 사용). bump 정책:\n * A2 자산 audit 주기에 Docker 검증 후 갱신 (docs/COMPATIBILITY.md §pinning).\n */\n | { kind: \"npm\"; pkg: string; version: string }\n /** `npx <cmd>@<version> [args...]` — fire-and-forget 실행. v26.80.0 — version 필수 (위와 동일 근거). */\n | { kind: \"npx-run\"; cmd: string; version: string; args?: string[] }\n /**\n * v26.81.0 (ADR-022) — 내부 템플릿 자산. external-installer 가 spawn 하지 않음 —\n * installer Phase 1 의 manifest/transform 게이팅이 `isAssetSelected(key)` 로 읽는다.\n * (이전 OptionFlags.withTauri/withUzysHarness 자리. wizard/CLI 표면은 일반 자산과 동일)\n */\n | {\n kind: \"internal\";\n key:\n | \"tauri-desktop\"\n // v26.87.0 — dev-method skills (uzys 1st-party, repo-bundled templates).\n // 2026-08-02 복원 (ADR-062) — 이관(ADR-060)이 본문을 열화시켜 이 리포 번들로 되돌렸다.\n // 9종 전부 `templates/skills/<id>/` 로 다시 번들된다.\n | \"compaction-handoff\"\n | \"user-centered-explanation\"\n | \"north-star\"\n | \"audit-service-gaps\"\n | \"multi-persona-review\"\n | \"recurrence-prevention\"\n | \"gh-issue-workflow\"\n | \"model-orchestration\"\n | \"external-model-consult\"\n // #729 (ADR-109) — 여러 실제 모델의 토론(개발 트랙 기본)과 브라우저 위임 3종(opt-in).\n | \"multi-model-debate\"\n | \"gemini-delegate\"\n | \"chatgpt-delegate\"\n | \"genspark-delegate\"\n // 위임·설계·다단계 작업의 요청을 canonical 브리프 형태로 정규화.\n | \"objective-brief\"\n // 설치된 상주 조종층(앵커·룰·훅·permissions·descriptor)의 밥값 감사 — 자기유지 루프.\n | \"audit-harness-fit\"\n // CI 가 멈췄을 때 워크플로를 복제하지 않고 self-hosted runner 로 돌린다.\n | \"self-hosted-github-runner\"\n // 한국어 답변·글쓰기·번역·퇴고 — 뜻과 말투를 살리며 번역투·상투구·기계적 반복을 줄인다.\n | \"natural-korean\"\n // v26.108.0 — CI 스캐폴드 (.github/workflows fill-in 템플릿). ADR-037.\n | \"ci-scaffold\"\n // #602 — 트랙 추종 UI 검수 스킬. .claude/ 전용 배선이 비-Claude 자리에 못 가던 것을\n // internal 카탈로그로 편입해 해소.\n | \"ui-visual-review\"\n // #709 (ADR-101) — 트랙 표(`templates/track-mcp-map.tsv`)의 같은 이름 행을 어느 트랙에서든 고르게 한다.\n // 번들 스킬이 아니다 — `.mcp.json` 렌더(`selectedMcpServers`)가 읽는다.\n | \"railway-mcp-server\"\n // #722 · #711 (ADR-106) — 같은 경로의 MCP 서버 행: 웹 트랙 기본 chrome-devtools · 기본 없는 원격 서버 둘.\n | \"chrome-devtools\"\n | \"github-mcp\"\n | \"vercel\"\n // #751 — 남은 기본 서버 둘도 같은 경로: 모든 트랙 기본 context7(패턴 `*`) · csr-supabase · full 기본 supabase.\n | \"context7\"\n | \"supabase\";\n };\n\nexport type ExternalAssetCondition =\n /** Track 중 1개 이상이 set와 일치 */\n | { kind: \"any-track\"; tracks: Track[] }\n /** dev track (executive 외 모두) */\n | { kind: \"has-dev-track\" }\n /** OptionFlags 의 특정 플래그 true (잔존 동작 옵션용 — ADR-022 후 자산 토글엔 사용 금지) */\n | { kind: \"option\"; flag: keyof OptionFlags }\n /**\n * v26.81.0 (ADR-022) — 순수 opt-in: condition 매치 항상 false. wizard 체크 또는\n * `--with <id>` 의 forceInclude 로만 설치. 이전의 자산 1:1 OptionFlags(`withBmad` 등\n * 13종)를 대체 — 자산 추가 시 플래그 코드 0곳.\n */\n | { kind: \"opt-in\" };\n\nexport interface ExternalAsset {\n /** 안정 식별자 — 로깅 + 누락 보고 + 테스트에서 사용 */\n id: string;\n /** 사람이 읽는 라벨 (한 줄) */\n description: string;\n /** v26.43.0 — Category-based UI 그룹화. SPEC §3.1. */\n category: Category;\n /** v26.43.0 — 정확한 출처 (GitHub org/user). Step 2 라벨. SPEC §3.5 R6. */\n source: Source;\n condition: ExternalAssetCondition;\n method: ExternalAssetMethod;\n /**\n * v26.79.0 — 검증 Trust Tier. 자산 entry 의 **필수 필드** (SSOT). 이전엔 별도 `TRUST_TIER`\n * Record 였으나 누락(컴파일러 미검출) + stale(좀비 키, v26.76.0 content-creator 제거 전례)\n * drift 가 가능했음 → entry 에 통합: **누락은 컴파일 에러, stale 은 구조적 불가능**.\n * `TRUST_TIER` / `assetTrustTier` 는 이 필드에서 derive (EXTERNAL_ASSETS 정의 뒤).\n * star snapshot(2026-05~06)은 각 entry tier 라인 주석. 실 drift 판정은 trust-tier-drift 가 live fetch.\n */\n tier: TrustTier;\n /**\n * v26.102.0 (ADR-031) — kind 기본 도달 범위의 **개별 자산 예외**. 설정 시 assetCliSupport 가\n * kind derive 대신 이 값을 쓴다. 사용 조건: method 인자/스크립트가 특정 CLI 전용 산출물을\n * 만드는 경우만 (예: bmad `--tools claude-code`). 근거 주석 필수 — SOD 리뷰가 \"예외 0\" 가정을\n * bmad 로 반증(Critical-1)해 도입.\n */\n cliSupportOverride?: CliTargets;\n}\n\n/**\n * v26.71.0 (PRD v26-71) — 검증 Trust Tier. North Star 세 기둥 ②.\n * - official: Anthropic 공식 marketplace + 본 하네스 자체.\n * - vetted: star ≥ 1000 + 활성 유지보수 (D2; 라이선스 미표기/비-OSI 는 출처 신뢰).\n * - experimental: star < 1000 — opt-in + 경고 (pre-check 제외).\n */\nexport type TrustTier = \"official\" | \"vetted\" | \"experimental\";\n\n/** csr-*|ssr-nextjs|full per bash setup-harness.sh L1041 (ssr-htmx 제외 — htmx는 React 미사용). */\n/**\n * #709 (ADR-101) — 스택에 **UI 가 있는** 트랙(csr-* · ssr-* · full). frontend-design 처럼 UI 를 만드는 도구는\n * 이 트랙에서만 미리 체크한다 — `base` 에 이어(#456 결정 B) UI 가 없는 `data` · `tooling` 도 뺀다. 열거하지 않고\n * `hasUiTrack` 에서 유도한다 — 트랙이 늘어도 여기가 뒤처지지 않는다.\n */\nconst UI_TRACKS: Track[] = TRACKS.filter((t) => hasUiTrack([t]));\n\nconst CSR_SSR_NEXTJS_FULL: Track[] = [\n \"csr-supabase\",\n \"csr-fastify\",\n \"csr-fastapi\",\n \"ssr-nextjs\",\n \"full\",\n];\n\n/**\n * v0.8.1 — executive-style Track SSOT (reviewer MEDIUM-3 fix).\n *\n * 3 Track 모두 dev/UI baseline 미적용 — `.claude/agents/strategist` + project-claude/<track>.md만.\n * `track-match.ts:hasDevTrack()` 의 negation domain. 사용처:\n * - `shouldInstallAsset` `has-dev-track` 분기 코멘트 (L458)\n * - `tests/external-assets.test.ts` invariant\n *\n * 신규 executive-style Track 추가 시 이 상수만 수정 → 모든 사용처 자동 반영.\n */\nexport const EXECUTIVE_STYLE_TRACKS: ReadonlyArray<Track> = [\n \"executive\",\n \"project-management\",\n \"growth-marketing\",\n];\n\n/**\n * v0.8.1 — `hasDevTrack` SSOT 의 array 표현 (reviewer MEDIUM-3 fix).\n *\n * `track-match.ts:hasDevTrack()` 와 동등 (TRACKS \\ EXECUTIVE_STYLE_TRACKS = 8 Track).\n * `any-track` condition 에 dev set 전체를 인라인하지 않도록 사용.\n */\nexport const DEV_TRACKS: ReadonlyArray<Track> = [\n \"base\", // #456 — 스택 없는 dev 트랙\n \"csr-supabase\",\n \"csr-fastify\",\n \"csr-fastapi\",\n \"ssr-htmx\",\n \"ssr-htmx-fastify\",\n \"ssr-nextjs\",\n \"data\",\n \"tooling\",\n \"full\",\n];\n\n/**\n * 외부 자산 매트릭스. bash setup-harness.sh@911c246~1 L791~1067 + 1320~1370 동등.\n *\n * 자산 수는 여기 적지 않는다 — SSOT 는 `EXTERNAL_ASSETS.length` 이고 사람이 읽는 수는\n * `docs/COMPATIBILITY.md` 서문이다(`tests/docs-supply-chain.test.ts` 가 대조). 여기 세어 적은\n * 수는 두 번째 사본이라 썩는다(#726 때 \"50\" 이 실제 52 였다). **들어오고 나간 이력은 여기\n * 쌓지 않는다** — 그건 ADR·CHANGELOG 것이고, 여기 쌓이면 다음 사람이 카탈로그 대신 이력을\n * 읽는다(#492 직전까지 11줄이 그랬다). 빠진 자산을 기존 설치본에 말하는 표는 `REMOVED_CATALOG_ASSETS`.\n *\n * 호출 순서: data → dev-baseline → railway → supabase-cli → dev-tools →\n * supabase-skills → react/ui → next → executive → GSD → security.\n */\nexport const EXTERNAL_ASSETS: ReadonlyArray<ExternalAsset> = [\n // === data Track ===\n {\n id: \"anthropic-data-plugin\",\n tier: \"official\", // anthropics/knowledge-work-plugins 18k\n description:\n \"Anthropic data plugin — SQL exploration, analysis, and visualization workflows. Installing it does not connect your warehouse; that is a separate MCP setup\",\n category: \"data\",\n source: \"anthropics\",\n condition: { kind: \"any-track\", tracks: [\"data\", \"full\"] },\n method: {\n kind: \"plugin\",\n marketplace: \"anthropics/knowledge-work-plugins\",\n pluginId: \"data@knowledge-work-plugins\",\n },\n },\n\n // === Internal template assets (v26.81.0, ADR-022 — 이전 OptionFlags.withTauri/withUzysHarness) ===\n {\n id: \"tauri-desktop\",\n tier: \"official\", // uzys 본 하네스 자체 템플릿\n description: \"Tauri desktop rule template (CSR/full tracks — manifest rule mapping)\",\n category: \"frontend\",\n source: \"uzys\",\n condition: { kind: \"opt-in\" },\n method: { kind: \"internal\", key: \"tauri-desktop\" },\n },\n // v26.108.0 (ADR-037, 라이프사이클 자산화 ②) — CI 스캐폴드: `.github/workflows/` fill-in\n // 워크플로(tag-트리거 CI + 실DB 서비스 컨테이너 + coverage 게이트 + Playwright E2E).\n // 실무 CI 패턴의 도메인 중립 일반화 (CI 부재 프로젝트의 갭이 계기). `.claude/` 밖에\n // 쓰는 첫 자산 — 기존 워크플로 파일은 절대 덮어쓰지 않는다 (src/ci-scaffold.ts,\n // manifest 경유 아님 — CLI 선택과 무관하게 설치되는 CLI-agnostic 산출물).\n // ADR-105 (#726, 사용자 결정 2026-10-11) — opt-in 전용 → 개발 트랙 기본. 없을 때만 쓰는 계약은 그대로.\n {\n id: \"ci-scaffold\",\n tier: \"official\", // uzys 본 하네스 자체 템플릿\n description:\n \"CI scaffold — .github/workflows fill-in templates (tag-triggered CI + real-DB service container + coverage gate + Playwright E2E); writes only when the file is absent, never overwrites existing workflow files\",\n category: \"workflow\",\n source: \"uzys\",\n condition: { kind: \"has-dev-track\" },\n method: { kind: \"internal\", key: \"ci-scaffold\" },\n },\n\n // === Repo-bundled internal skills (uzys 1st-party, v26.87.0) ===\n // 2026-08-02 복원 (ADR-062) — 이관(ADR-060) 후 감사에서 본문 열화 104건(dropped 76 ·\n // damaged 28)이 확인돼 9종을 이 리포 번들로 되돌렸다. 아래 10종 전부 `kind: \"internal\"`.\n // #602 — ui-visual-review: TRACKS.md 가 말하는 트랙 추종 번들 스킬(\"follows the track, not\n // the catalog\"). internal 카탈로그 자산으로 등록해 selectedInternalSkills 흐름에 태우면\n // .agents/skills(비-Claude 자리)에도 깔린다 — 종전의 .claude/ 전용 배선과 어긋나던 것.\n {\n id: \"ui-visual-review\",\n tier: \"official\", // uzys 자사 스킬\n description:\n \"UI visual review — screenshot capture, baseline diff, and a review gate for UI changes (ships with UI tracks; ssr-htmx excluded)\",\n category: \"frontend\",\n source: \"uzys\",\n condition: {\n kind: \"any-track\",\n tracks: [\"csr-supabase\", \"csr-fastify\", \"csr-fastapi\", \"ssr-nextjs\", \"full\"],\n },\n method: { kind: \"internal\", key: \"ui-visual-review\" },\n },\n {\n id: \"compaction-handoff\",\n tier: \"official\", // uzys 본 하네스 자체 템플릿\n description:\n \"Compaction handoff — persist durable state + git snapshot + resume anchor before a context /compact so nothing is lost\",\n category: \"workflow\",\n source: \"uzys\",\n condition: { kind: \"has-dev-track\" },\n method: { kind: \"internal\", key: \"compaction-handoff\" },\n },\n\n // === uzys 방법론 스킬 — 이 리포 번들 (2026-08-02 복원, ADR-062) ===\n // ASIS(ADR-060): `kind: \"skill\"` — uzysjung/uzys-agent-skills 에서 `npx skills add`.\n // TOBE: templates/skills/<id>/ 번들 → 설치 시 dir copy (`kind: \"internal\"`).\n // 이관본이 판정 기준·수치·워크드 예시를 잃어(감사 실측 104건) 스킬이 무엇을 하라는지가\n // 남고 무엇으로 판정하는지가 사라졌다. 배포 경로를 하나로 모으는 이득보다 본문 보존이\n // 앞선다 — 본문 게이트(테스트)가 이 리포에만 있기 때문이다. tier official = 자사\n // (star 무관 — trust-tier-drift 가 official 을 건너뛴다). condition 은 이관 전 도달 범위 유지.\n {\n // #485 — 전신 `clear-korean-communication` 개명. 이름이 \"한국어 전용\"으로 읽혀 영어 사용자에게\n // 닿지 않았다(사용자 결정 2026-09-20). 본문은 사용자가 검토한 개정안, 답변 언어는 사용자의 언어.\n id: \"user-centered-explanation\",\n tier: \"official\", // uzys 자사 스킬 리포\n description:\n \"User-centered explanation — explain problems, changes, and choices as what the user does and sees (impact and cause first, then evidence), in the user's language; covers decisions, approval requests, and AS-IS/TO-BE contrasts of screens and flows\",\n category: \"workflow\",\n source: \"uzys\",\n condition: { kind: \"has-dev-track\" },\n method: { kind: \"internal\", key: \"user-centered-explanation\" },\n },\n {\n id: \"north-star\",\n tier: \"official\", // uzys 자사 스킬 리포\n description:\n \"North Star — create or safely update the project direction baseline and its revision-linked roadmap, then analyze the impact on audits, verification criteria, issues, and work in progress\",\n category: \"workflow\",\n source: \"uzys\",\n // 전 트랙 상주 보존 — 이관 전에는 manifest COMMON_SKILL_DIRS(조건 없는 전 트랙 설치)였다.\n // 이 카탈로그에 \"always\" kind 가 없어 전 트랙 나열로 같은 도달 범위를 표현한다.\n condition: { kind: \"any-track\", tracks: [...TRACKS] },\n method: { kind: \"internal\", key: \"north-star\" },\n },\n {\n id: \"audit-service-gaps\",\n tier: \"official\", // uzys 자사 스킬 리포\n description:\n \"Audit service gaps — audit observable gaps between the service's current state and an explicit North Star baseline (reverse · defect · experience · benchmark · verify · change-impact · drift modes)\",\n category: \"dev-tools\",\n source: \"uzys\",\n condition: { kind: \"has-dev-track\" },\n method: { kind: \"internal\", key: \"audit-service-gaps\" },\n },\n {\n id: \"multi-persona-review\",\n tier: \"official\", // uzys 자사 스킬 리포\n description:\n \"Multi-persona review — review one artifact through independent stakeholder and failure lenses, then synthesize evidence-backed, severity-ranked findings without discarding minority views\",\n category: \"dev-tools\",\n source: \"uzys\",\n condition: { kind: \"has-dev-track\" },\n method: { kind: \"internal\", key: \"multi-persona-review\" },\n },\n {\n id: \"recurrence-prevention\",\n tier: \"official\", // uzys 자사 스킬 리포\n description:\n \"Recurrence prevention — reconstruct a repeated defect's failure signature and timeline, then pick the least costly countermeasure (code · regression test · rule · hook · derivation · gate)\",\n category: \"workflow\",\n source: \"uzys\",\n condition: { kind: \"has-dev-track\" },\n method: { kind: \"internal\", key: \"recurrence-prevention\" },\n },\n {\n id: \"gh-issue-workflow\",\n tier: \"official\", // uzys 자사 스킬 리포\n description:\n \"GitHub issue workflow — 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\",\n category: \"workflow\",\n source: \"uzys\",\n // 전 트랙 상주 보존 — north-star 와 같은 이유 (이관 전 COMMON_SKILL_DIRS).\n condition: { kind: \"any-track\", tracks: [...TRACKS] },\n method: { kind: \"internal\", key: \"gh-issue-workflow\" },\n },\n {\n id: \"model-orchestration\",\n tier: \"official\", // uzys 자사 스킬 리포\n description:\n \"Model orchestration — decompose work by capability and route independent, bounded tasks to available agents while preserving ownership, permissions, and verification. Model names are Claude Code vocabulary; on other CLIs the same split maps onto that vendor's tiers\",\n category: \"workflow\",\n source: \"uzys\",\n // ADR-105 (#726) — opt-in → 개발 트랙 기본.\n condition: { kind: \"has-dev-track\" },\n method: { kind: \"internal\", key: \"model-orchestration\" },\n },\n {\n // 전신 = gemini-consult + codex-consult. provider 중립 한 스킬로 통합됐다(통합은 유지 —\n // 되돌린 것은 배포 경로이지 통합이 아니다).\n id: \"external-model-consult\",\n tier: \"official\", // uzys 자사 스킬 리포\n description:\n \"External model consult — ask an available external model/provider for an independent draft, critique, or provider-only output such as image generation (requires that provider's CLI)\",\n category: \"dev-tools\",\n source: \"uzys\",\n // ADR-105 (#726) — opt-in → 개발 트랙 기본. 설치는 디렉터리 복사뿐이라 provider CLI 가 없어도\n // 설치는 성공한다 — 없으면 실행 시점에 래퍼가 멈추고 스킬이 \"설치·로그인은 사용자 몫\"을 알린다.\n condition: { kind: \"has-dev-track\" },\n method: { kind: \"internal\", key: \"external-model-consult\" },\n },\n {\n // #729 (ADR-109) — 토론 정책(참여자 · 독립성 · 라운드 · 예산 · 재시도 · 대체 · 복귀 · 통합)은 이 스킬만\n // 소유한다. 하위 경로(external-model-consult · 브라우저 위임 3종)는 실행 매개변수와 결과·상태만 맡는다.\n // has-dev-track 인 이유: 브라우저 전제가 없다 — external-model-consult 의 CLI 둘만으로 \"서로 다른 실제 모델 2개\"가\n // 성립하고, 없는 경로는 실행 시점에 unavailable 로 기록한다. DEV_METHOD 에 넣지 않는다(수단 — ADR-105 D2).\n id: \"multi-model-debate\",\n tier: \"official\", // uzys 자사 스킬\n description:\n \"Multi-model debate — two or three different real models answer independently, then only decision-relevant disagreements are cross-examined and checked; uses whichever installed routes are available (external-model-consult CLIs or the browser delegates)\",\n category: \"dev-tools\",\n source: \"uzys\",\n condition: { kind: \"has-dev-track\" },\n method: { kind: \"internal\", key: \"multi-model-debate\" },\n },\n // #729 (ADR-109, 사용자 결정 2026-10-12) — 브라우저 위임 3종은 opt-in. 실행 전제(브라우저 도구 · 로그인된 브라우저 ·\n // 그 서비스 계정)가 하네스 밖에 있고 Codex CLI 에는 브라우저가 없다(공식 문서). #489 가 agent-browser 를 opt-in 으로\n // 내린 축과 같다. 설치는 디렉터리 복사뿐이다 — `--with <id>` · 위저드 체크로 켠다.\n {\n id: \"gemini-delegate\",\n tier: \"official\", // uzys 자사 스킬\n description:\n \"Gemini delegate — hand design, review, or Korean polishing to Gemini on the web and collect the answer (needs a browser tool + your logged-in browser)\",\n category: \"dev-tools\",\n source: \"uzys\",\n condition: { kind: \"opt-in\" },\n method: { kind: \"internal\", key: \"gemini-delegate\" },\n },\n {\n id: \"chatgpt-delegate\",\n tier: \"official\", // uzys 자사 스킬\n description:\n \"ChatGPT delegate — hand design, planning, or review to ChatGPT on the web and collect the answer (needs a browser tool + your logged-in browser)\",\n category: \"dev-tools\",\n source: \"uzys\",\n condition: { kind: \"opt-in\" },\n method: { kind: \"internal\", key: \"chatgpt-delegate\" },\n },\n {\n id: \"genspark-delegate\",\n tier: \"official\", // uzys 자사 스킬\n description:\n \"Genspark delegate — hand design or planning to Genspark AI Chat, or generate and download images with AI Image (needs a browser tool + your logged-in browser)\",\n category: \"dev-tools\",\n source: \"uzys\",\n condition: { kind: \"opt-in\" },\n method: { kind: \"internal\", key: \"genspark-delegate\" },\n },\n {\n // 복원 9종이 아니라 신설이다 — 이관 이력이 없다.\n // 전 트랙인 이유: 위임과 요청 정규화는 개발 트랙의 행위가 아니라 **전 트랙의 행위**다.\n // executive 트랙도 서브에이전트에 일을 넘기고, 그때 브리프가 없으면 완료 판정 기준 없이 넘긴다.\n // ADR-088 (#426 F-04) — 전신 `task-brief` 개명. 문턱이 **위임 · 설계 · 다단계(피처/프로젝트\n // 규모 이상)** 로 좁혀졌고, 한 줄 요청마다 브리프를 권하던 매 프롬프트 넛지 훅과 상시 스킬\n // 안내(`CONTINUOUS_SKILLS`)가 함께 빠졌다. 이름이 그 문턱을 들고 있다.\n id: \"objective-brief\",\n tier: \"official\", // uzys 자사 스킬\n description:\n \"Objective brief — normalize a delegation, a design task, or multi-step work at feature scale into the canonical brief (objective · inputs · invariants · success criteria · boundaries · autonomy · verification) and write every delegation prompt in that same shape\",\n category: \"workflow\",\n source: \"uzys\",\n condition: { kind: \"any-track\", tracks: [...TRACKS] },\n method: { kind: \"internal\", key: \"objective-brief\" },\n },\n {\n // objective-brief 와 같은 신설이다 — 이관 이력이 없다.\n // 전 트랙인 이유: 이 하네스는 **모든 트랙에** 앵커·룰·훅을 깐다. 그 상주층이 밥값을 하는지\n // 되묻는 루프만 개발 트랙에 두면, 상주 비용은 전원이 무는데 감사는 일부만 갖는 비대칭이\n // 된다. 감사 대상이 개발 산출물이 아니라 **설치본 자신**이라 트랙 술어와 무관하다.\n // ADR-084 (#425) — 2판. 판정 근거가 공식 체크리스트 인용에서 **확정된 의도 + 리포 실증**으로\n // 바뀌었고, 모드 4개(audit · verification · apply · populate)를 SKILL.md 가 references 로\n // 라우팅한다. 비-Claude CLI 도 디렉터리째 받는 것이 전제다(#431).\n id: \"audit-harness-fit\",\n tier: \"official\", // uzys 자사 스킬\n description:\n \"Audit harness fit — improve agent instructions and skills for autonomous, productive delivery with verifiable quality: resolve needless questions and repeated checks, conflicting decisions, missing actionable context, and low-value procedures; adapt guidance to demonstrated model and tool capabilities; also fill or refresh AGENTS.md / CLAUDE.md project context from repository evidence\",\n category: \"workflow\",\n source: \"uzys\",\n condition: { kind: \"any-track\", tracks: [...TRACKS] },\n method: { kind: \"internal\", key: \"audit-harness-fit\" },\n },\n\n {\n // CI 가 멈췄을 때(결제 실패·쿼터 소진·Actions 장애) 검증을 포기하지도, 위조하지도 않는 절차.\n // has-dev-track 인 이유: 이건 **누가 요청해서 켜는 것이 아니라 사고가 났을 때 켜지는** 것이다.\n // CI 가 죽고 나서야 필요해지는 자산을 opt-in 으로 두면, 정작 그 순간에 없다. 대신 executive\n // 트랙에는 깔지 않는다 — 거기엔 워크플로가 없다.\n // DEV_METHOD_SKILL_IDS 에 넣지 않는 이유: 저건 wizard 의 \"하네스 방법론 N종\" 묶음이고,\n // 이건 방법론이 아니라 사고 대응 런북이다. 묶음에 끼면 묶음의 뜻이 흐려진다.\n id: \"self-hosted-github-runner\",\n tier: \"official\", // uzys 자사 스킬\n description:\n \"Self-hosted GitHub runner — when hosted runners stop (billing failure, spending limit, minutes exhausted, org quota, Actions outage), run the repo's existing workflow files unchanged on a Docker self-hosted runner instead of copying CI steps into a local script; covers runner-label switching, architecture and cache-key traps, container isolation, the risk that pull_request jobs then execute on your own machine, and the rollback path\",\n category: \"dev-tools\",\n source: \"uzys\",\n condition: { kind: \"has-dev-track\" },\n method: { kind: \"internal\", key: \"self-hosted-github-runner\" },\n },\n\n {\n // 트랙과 무관한 글쓰기 규율이다. ADR-105 (#726, 사용자 결정 2026-10-11) — opt-in → 개발 트랙\n // 기본. 한국어로 쓰지 않는 설치자에게는 descriptor 상주가 낭비라는 대가를 받아들였다\n // (빼려면 `--without natural-korean`).\n // user-centered-explanation 과의 경계: 저쪽은 **무엇을 어떤 형식으로 말할지**(설명·승인\n // 요청의 구조)이고, 이쪽은 **한국어로 쓰는 행위 자체**(답변·글쓰기·번역·퇴고)의 표현이다.\n // #428 — 전신 `humanize-korean`(#355, 진단-우선 퇴고)을 개명·교체했다. 범위가 퇴고에서\n // 쓰기·번역까지로 넓어져 이름이 뜻과 어긋났다. 옛 id 로 깐 설치본은 `update` 가 손대지\n // 않고(설치된 디렉터리만 갱신) 그대로 남는다 — 새 판은 `--with natural-korean` 으로 받는다.\n id: \"natural-korean\",\n tier: \"official\", // uzys 자사 스킬\n description:\n \"Natural Korean — write, answer, translate, and revise in Korean that reads as Korean: keep meaning and register, cut translationese, needless English, stock phrases, and mechanical repetition; fix only what is actually awkward\",\n category: \"workflow\",\n source: \"uzys\",\n condition: { kind: \"has-dev-track\" },\n method: { kind: \"internal\", key: \"natural-korean\" },\n },\n\n // === Option-gated (v26.42.0 — opt-in, BREAKING vs prior has-dev-track auto-install) ===\n {\n // v26.75.0 (ADR-021) — `npm i --save-dev @fission-ai/openspec` 후 `openspec init` 로 슬래시 주입.\n id: \"openspec\",\n tier: \"vetted\", // Fission-AI/OpenSpec 53k\n description:\n \"OpenSpec — spec-driven change workflow for an existing codebase (propose → apply → archive). Use when your team has adopted a shared spec-and-delta process; it is a collaboration contract, not a coding aid\",\n category: \"workflow\",\n source: \"fission-ai\",\n condition: { kind: \"opt-in\" },\n method: { kind: \"npm\", pkg: \"@fission-ai/openspec\", version: \"1.4.1\" },\n },\n {\n // v26.75.0 (ADR-021) — 비대화형 install. v26.75.1: `--directory .` 누락 시 \"Installation\n // directory\" 프롬프트에서 hang (Docker realcli 검출). cwd(=project) 기준 `.` 지정으로 봉합.\n id: \"bmad-method\",\n tier: \"vetted\", // bmad-code-org/BMAD-METHOD 48k\n description:\n \"BMAD-METHOD — multi-agent agile workflow (PM / Architect / Dev roles). Use for team-scale delivery with fixed roles; the pinned 6.9.0 still runs the full ceremony, so skip it for solo or small changes\",\n category: \"workflow\",\n source: \"bmad-code-org\",\n condition: { kind: \"opt-in\" },\n // v26.102.0 (ADR-031) — args 의 `--tools claude-code` 하드코딩이 claude 전용 산출물\n // (.claude/ agent commands)을 생성 (Docker 실검증 realcli-workflows-2026-06-06).\n // kind 기본값(npx-run=전 CLI)이 이 자산에선 거짓 → override. 대칭 실현(--tools derive)은 M4+.\n cliSupportOverride: [\"claude\"],\n method: {\n kind: \"npx-run\",\n cmd: \"bmad-method\",\n version: \"6.9.0\",\n args: [\"install\", \"--directory\", \".\", \"--tools\", \"claude-code\", \"--yes\"],\n },\n },\n\n // === Railway (csr-fastify|csr-fastapi|ssr-*|full) ===\n // v0.6.3 — railway-plugin entry 제거. railwayapp/railway-plugin repo 자체 존재 안 함\n // (404 Not Found). 공식 docs (https://docs.railway.com/ai/claude-code-plugin) 형식은\n // marketplace add `railwayapp/railway-skills` + plugin install `railway@railway-skills`만.\n // → 아래 railway-skills entry로 단일화.\n {\n id: \"railway-skills\",\n tier: \"experimental\", // railwayapp/railway-skills 268\n description:\n \"Railway skills — deploy and manage projects, services, and environments on Railway. Only for projects hosted on Railway; it drives a real account\",\n category: \"backend\",\n source: \"railwayapp\",\n // 2026-08-02 사용자 결정: 트랙 기본 → opt-in (ADR-063)\n condition: { kind: \"opt-in\" },\n method: {\n kind: \"plugin\",\n marketplace: \"railwayapp/railway-skills\",\n pluginId: \"railway@railway-skills\",\n },\n },\n // #709 (ADR-101) — Railway MCP 서버의 **선택** 경로. 서버 정의는 트랙 표(`templates/track-mcp-map.tsv`)의 같은 이름\n // 행 하나다(key === 행 이름). 그 행의 패턴이 기본 트랙(csr-* · ssr-htmx · ssr-htmx-fastify · full)을 정하고, 이 항목은 그 밖의 트랙\n // (ssr-nextjs 등)에서 위저드 체크 · `--with` 로 고르게 한다. experimental 이라 조건으로는 미리 체크되지 않는다.\n // #712 (ADR-106) — 행이 Railway 공식 원격 서버(`https://mcp.railway.com`, OAuth)로 바뀌었다. 이름 · 키 id 는 그대로라 기록이\n // 이어져 update 가 같은 키를 갈아 끼운다.\n {\n id: \"railway-mcp-server\",\n tier: \"experimental\", // railwayapp/cli 621★ (원격 서버에 공개 저장소가 따로 없다 — 같은 벤더의 CLI 를 감시, REPO_OVERRIDE)\n description:\n \"Railway's official remote MCP (https://mcp.railway.com, signs in with OAuth on first use) in .mcp.json, Codex and OpenCode — default on csr-* · ssr-htmx · ssr-htmx-fastify · full; check it to add it on any other track\",\n category: \"backend\",\n source: \"railwayapp\",\n condition: { kind: \"opt-in\" },\n // #709 리뷰 NOTE 3 — `.mcp.json` · Codex `[mcp_servers]` · `opencode.json` `mcp` 만 받는다. Antigravity 변환에는 MCP 가 없다\n // (internal 기본값 = 4 CLI 는 과대 표기).\n cliSupportOverride: [\"claude\", \"codex\", \"opencode\"],\n method: { kind: \"internal\", key: \"railway-mcp-server\" },\n },\n // #711 (ADR-106) — Vercel 공식 원격 MCP. 트랙 표 행의 패턴은 `-`(기본 트랙 없음) — 어느 트랙에서도 미리 체크하지 않는다\n // (사용자 확정 2026-10-11 \"opt-in\", ssr-nextjs 포함). Antigravity 는 MCP 를 받지 않는다(Vercel 승인 클라이언트에도 없다).\n {\n id: \"vercel\",\n tier: \"vetted\", // Vercel MCP 에 공개 저장소가 따로 없다 — 같은 벤더의 vercel/vercel 16k 를 감시(REPO_OVERRIDE)\n description:\n \"Vercel's official remote MCP (https://mcp.vercel.com, signs in with OAuth on first use) in .mcp.json, Codex and OpenCode. Only if this project deploys to Vercel\",\n category: \"backend\",\n source: \"vercel\",\n condition: { kind: \"opt-in\" },\n cliSupportOverride: [\"claude\", \"codex\", \"opencode\"],\n method: { kind: \"internal\", key: \"vercel\" },\n },\n // #722 (ADR-106) — GitHub 공식 원격 MCP 의 opt-in 경로. 기본에서 뺀 옛 `github`(@modelcontextprotocol/server-github, 상류\n // deprecated)와 **다른 이름**이다 — 같은 이름이면 옛 설치본의 기록된 몫이 선택으로 읽혀 PAT 없는 설치자에게 매 세션 실패하는\n // 서버가 조용히 깔린다. 인증 = 환경변수 GITHUB_PAT(파일에는 이름만).\n {\n id: \"github-mcp\",\n tier: \"vetted\", // github/github-mcp-server 33.5k (REPO_OVERRIDE)\n description:\n \"GitHub's official remote MCP server (api.githubcopilot.com) in .mcp.json, Codex and OpenCode — needs a GitHub personal access token in GITHUB_PAT in the shell you start the CLI from. Not needed when the agent uses the gh CLI\",\n category: \"dev-tools\",\n source: \"github\",\n condition: { kind: \"opt-in\" },\n cliSupportOverride: [\"claude\", \"codex\", \"opencode\"],\n method: { kind: \"internal\", key: \"github-mcp\" },\n },\n // #722 (ADR-106) — Chrome DevTools MCP. 트랙 표 행이 웹 트랙(csr-* · ssr-* · full)의 기본이고, 이 항목은 그 밖의 트랙에서\n // 고르게 한다. Google 사용 통계는 행의 `--no-usage-statistics` 로 끈다.\n {\n id: \"chrome-devtools\",\n tier: \"vetted\", // ChromeDevTools/chrome-devtools-mcp 53k (REPO_OVERRIDE)\n description:\n \"Chrome DevTools MCP — browser automation, console, network and performance tools for the agent. Default on csr-* · ssr-* · full; check it to add it on any other track. Google's usage statistics are turned off (--no-usage-statistics)\",\n category: \"understanding\",\n source: \"ChromeDevTools\",\n condition: { kind: \"opt-in\" },\n cliSupportOverride: [\"claude\", \"codex\", \"opencode\"],\n method: { kind: \"internal\", key: \"chrome-devtools\" },\n },\n // #751 — Context7 MCP. 트랙 표 행의 패턴 `*` 가 모든 트랙의 기본이라 위저드 3단계에 늘 체크된 채 보이고, 풀거나\n // `--without context7` 이면 `.mcp.json` · Codex · OpenCode 세 자리에서 빠진다(#709 · #722 와 같은 경로). 기본값은 그대로다.\n {\n id: \"context7\",\n tier: \"vetted\", // upstash/context7 62.9k (REPO_OVERRIDE)\n description:\n \"Context7 MCP — current, version-specific library docs for the agent, in .mcp.json, Codex and OpenCode. Default on every track; uncheck it to leave it out\",\n category: \"understanding\",\n source: \"upstash\",\n condition: { kind: \"opt-in\" },\n cliSupportOverride: [\"claude\", \"codex\", \"opencode\"],\n method: { kind: \"internal\", key: \"context7\" },\n },\n // #751 — Supabase MCP. 트랙 표 행이 csr-supabase · full 의 기본이고, 이 항목은 그 밖의 트랙에서 고르게 · 기본 트랙에서\n // 빼게 한다. 서버 정의(명령 · 인자)는 트랙 표 한 곳이다.\n {\n id: \"supabase\",\n tier: \"vetted\", // supabase/mcp 2.9k (REPO_OVERRIDE)\n description:\n \"Supabase's official remote MCP (https://mcp.supabase.com/mcp, signs in with OAuth on first use) in .mcp.json, Codex and OpenCode. Default on csr-supabase · full; check it to add it on any other track. Only if this project uses Supabase\",\n category: \"backend\",\n source: \"supabase\",\n condition: { kind: \"opt-in\" },\n cliSupportOverride: [\"claude\", \"codex\", \"opencode\"],\n method: { kind: \"internal\", key: \"supabase\" },\n },\n\n // === csr-supabase|full CLI ===\n {\n id: \"vercel-cli\",\n tier: \"vetted\", // vercel/vercel 16k\n description:\n \"Vercel CLI — the real deploy tool as a devDependency (global under --scope global). Only if this project deploys to Vercel. Non-interactive `vercel link`/deploy needs `--scope` when your account has several teams (55.0.0+)\",\n category: \"backend\",\n source: \"vercel\",\n // 2026-08-02 사용자 결정: 트랙 기본 → opt-in (ADR-063). #709 가 ssr-nextjs 기본으로 바꿨다가 #715 에서 되돌렸다 —\n // 설치자 package.json 에 의존성을 더하고(에이전트는 npx 로 쓸 수 있다) login 없이는 쓸 것이 없으며 핀이 낡는다.\n // Vercel 공식 MCP 도 opt-in 이다(`vercel` 항목, #711 · ADR-106 — 사용자 확정 2026-10-11).\n condition: { kind: \"opt-in\" },\n method: { kind: \"npm\", pkg: \"vercel\", version: \"63.1.2\" },\n },\n {\n // v26.106.0 (ADR-035, 사용자 승인 B): 배포 CLI 2종 동시 기본의 중복 해소 — npm dl 실측 10.11:1\n // (vercel 2.79M vs netlify 276k /주, 2026-07-18) → vercel 만 기본, netlify 는 opt-in.\n id: \"netlify-cli\",\n tier: \"vetted\", // netlify/cli 1.9k\n description:\n \"Netlify CLI — the real deploy tool as a devDependency (global under --scope global). Only if this project deploys to Netlify\",\n category: \"backend\",\n source: \"netlify\",\n condition: { kind: \"opt-in\" },\n method: { kind: \"npm\", pkg: \"netlify-cli\", version: \"26.1.0\" },\n },\n {\n id: \"supabase-cli\",\n tier: \"vetted\", // supabase 103k\n description:\n \"Supabase CLI — the real Supabase tool as a devDependency (global under --scope global); first `supabase login` opens an OAuth browser flow. Only if this project uses Supabase\",\n category: \"backend\",\n source: \"supabase\",\n // 2026-08-02 사용자 결정: 트랙 기본 → opt-in (ADR-063)\n condition: { kind: \"opt-in\" },\n method: { kind: \"npm\", pkg: \"supabase\", version: \"2.108.0\" },\n },\n\n // === UI tracks (csr-*|ssr-*|full) ===\n // v26.92.0 — frontend-design (Anthropic official).\n // 사용자 결정: has-dev-track 기본추천 (모든 개발 트랙, executive 제외).\n // #709 (ADR-101) — UI 가 있는 트랙만(`UI_TRACKS`). data · tooling 은 미리 체크하지 않는다.\n // category=frontend (UI 자산, wizard 그룹).\n //\n // 2026-08-26 (#344) — **plugin → skill.** 배달 방식이 도달 CLI 를 정한다(assetCliSupport):\n // `plugin` 은 `claude plugin install` 을 spawn 하므로 **구조적으로 Claude Code 전용**이라,\n // Codex·OpenCode·Antigravity 를 고른 사용자는 기본 추천 자산인데도 한 번도 받지 못했다\n // (사용자 적발). 같은 스킬이 `anthropics/skills` 에 원본으로 있고 `npx skills add` 는\n // `--agent` 로 네 CLI 전부에 깐다 — 실재 확인 2026-08-26: `skills/frontend-design/SKILL.md`.\n // tier 는 official 그대로(저자 동일). marketplace 경유가 아니게 되어 upstream 판본을 직접 쓴다.\n {\n id: \"frontend-design\",\n tier: \"official\", // anthropics/skills (Anthropic 저자, ★171.5K — 2026-08-26 gh api 실측)\n description:\n \"frontend-design — visual direction, typography, and anti-generic UI guidance (Anthropic official). For new UI or an explicit redesign; do not let it override an existing design system\",\n category: \"frontend\",\n source: \"anthropics\",\n condition: { kind: \"any-track\", tracks: UI_TRACKS },\n method: { kind: \"skill\", source: \"anthropics/skills\", skill: \"frontend-design\" },\n },\n // 2026-08-02 정비 — 프론트엔드 품질 3종 (사용자 지시). 전부 opt-in: frontend-design 이 기본\n // 생성기이고 이 셋은 그 위의 취향·디테일 층이라 상시 비용을 물릴 근거가 없다 (ADR-032/035\n // 와 같은 축). star 는 각 tier 라인 주석 = 2026-08-02 `gh api` 실측.\n {\n id: \"jakubkrehel-skills\",\n tier: \"vetted\", // jakubkrehel/skills 2,602 (2026-08-02)\n description:\n \"Better-* interface suite — one skill per concern: typography, OKLCH color, accessibility, layout, UX writing, component states. Add when you need a specific UI review, not as a default\",\n category: \"frontend\",\n source: \"jakubkrehel\",\n condition: { kind: \"opt-in\" },\n // `--skill` 미지정 = 리포의 7 스킬 전부 설치. 다른 multi-skill 출처(K-Dense·softaworks)는\n // 한 자산 = 한 스킬이라 `--skill` 을 썼지만, 여기 광고 문구가 \"7 skills\" 세트다 —\n // 한 개만 깔면 description 이 곧 거짓이 된다. 실호출 형태는 Docker 실설치(AC8)로 확인한다.\n method: { kind: \"skill\", source: \"jakubkrehel/skills\" },\n },\n {\n id: \"taste-skill\",\n tier: \"vetted\", // Leonxlnx/taste-skill 70,078 (2026-08-02)\n description:\n \"taste-skill — tunes the look of landing pages, portfolios, and redesigns (variance · motion · density). Not for dashboards or multi-step product UI\",\n category: \"frontend\",\n source: \"Leonxlnx\",\n condition: { kind: \"opt-in\" },\n // 리포에 스킬 14종(brandkit·brutalist·minimalist 등)이 있고 본체가 `taste-skill` —\n // 스타일 변형은 그 안에서 고른다. 다른 단일 대표 자산(remotion)과 같은 형태.\n // 2026-09-20 (#420) — 업스트림이 SKILL.md 의 `name:` 을 `design-taste-frontend` 로 바꿨다. `npx skills\n // add --skill` 은 디렉터리명이 아니라 **frontmatter name** 으로 맞춘다(실측: 옛 이름은 \"No matching\n // skills found\", 새 이름은 설치 완료). 디렉터리는 여전히 `skills/taste-skill/` 이다.\n method: { kind: \"skill\", source: \"Leonxlnx/taste-skill\", skill: \"design-taste-frontend\" },\n },\n {\n id: \"scroll-world\",\n tier: \"vetted\", // oso95/scroll-world 6,863 (2026-08-02)\n description:\n \"scroll-world — scroll-driven 3D landing pages with generated media assets. A special production pipeline that needs external image/video services; only when that effect is requested\",\n category: \"frontend\",\n source: \"oso95\",\n condition: { kind: \"opt-in\" },\n method: { kind: \"skill\", source: \"oso95/scroll-world\", skill: \"scroll-world\" },\n },\n // 2026-08-16 — Preline 은 Tailwind 컴포넌트 킷이지만 **프레임워크에 매이지 않는다**: htmx·\n // vanilla 에도 붙는다(사용자 확인). 그래서 `frontend` 이면서 React/Vue 트랙 전용이 아니다.\n //\n // 리포 안에 테마 스킬은 `skills/theme-generator/` 하나다. 2026-08-16 실측에서는 `npx skills add`\n // 가 디렉터리명(`theme-generator`)으로 맞췄는데, 2026-09-01 부터 그 이름이 \"No matching skills\n // found\" 가 됐다(#420) — 지금은 SKILL.md 의 `name:`(`preline-theme-generator`)으로 맞춘다(실측\n // 2026-09-20: 옛 이름 exit 1 · 새 이름 설치 완료). 이름이 바뀌면 월 1회 catalog-verify 가 잡는다.\n //\n // 같은 날 조사한 flowbite 는 **넣지 않는다**: 에이전트용 제공물이 MCP 서버뿐인데, 그때 이 저장소의\n // `.mcp.json` 조립은 트랙 조건만 읽어 opt-in 경로가 없었다 — 넣으면 해당 트랙 전원에게 항상\n // 켜진다. 안 쓰는 사람에게 MCP 툴 스키마만큼의 상주 비용을 물리는 형태라 기각(사용자 확정).\n // #709 (ADR-101) 부터는 카탈로그 `internal` 항목으로 opt-in 경로가 있다(`railway-mcp-server`). flowbite 는\n // 그 경로로 다시 심사할 수 있다 — 이 변경은 넣지 않는다.\n {\n id: \"preline\",\n tier: \"vetted\", // htmlstreamofficial/preline 6,386 (2026-08-16)\n description:\n \"Preline theme generator — turns a brand description into a Preline UI Tailwind theme (tokens, dark mode, overrides) with a local generator script. Only for projects that use Preline\",\n category: \"frontend\",\n source: \"htmlstreamofficial\",\n condition: { kind: \"opt-in\" },\n method: {\n kind: \"skill\",\n source: \"htmlstreamofficial/preline\",\n skill: \"preline-theme-generator\",\n },\n },\n\n // #822 (사용자 결정 2026-10-14) — ast-grep 공식 스킬. 언어에 매이지 않아 트랙에 묶지 않고 opt-in.\n // 벤더 README 가 \"설치하면 발견될 뿐 선호되지 않는다\"고 적고, 기본으로 쓰게 하려면 상주 지시를 권한다 — 관측 없는\n // 상주는 넣지 않으므로 원하는 사람이 고르는 자리에만 둔다. 스킬 파일에 CLI 설치 안내가 없어 description 에 적는다.\n // 리포에 스킬이 2종(ast-grep · ast-grep-outline)이라 하나만 받는다. #820 의 htmx-fastapi-ui 룰은 이 스킬 없이 동작한다.\n {\n id: \"ast-grep\",\n tier: \"experimental\", // ast-grep/agent-skill 893★ (license none — README \"follows ast-grep's MIT\"; 본체 ast-grep/ast-grep 16k MIT, 2026-10-14)\n description:\n \"ast-grep — write and test structural (AST) search and lint rules across languages (ast-grep official). Needs the ast-grep CLI installed separately; fires reliably only when you ask for ast-grep\",\n category: \"dev-tools\",\n source: \"ast-grep\",\n condition: { kind: \"opt-in\" },\n method: { kind: \"skill\", source: \"ast-grep/agent-skill\", skill: \"ast-grep\" },\n },\n\n // === dev tools (has_dev_track) ===\n // v26.110.0 (ADR-039) — security-guidance: 매 편집 패턴 경고 + LLM diff 리뷰 (훅 12파일,\n // Python + Agent SDK 의존). 상시 훅 = 매 편집 비용 + 폭발 반경 — 실측 전 기본설치 금지\n // (Context Cost NSM) → opt-in. Claude Code `/security-review`·agentshield ship 게이트와 보완.\n {\n id: \"security-guidance\",\n tier: \"official\", // anthropics/claude-plugins-official (220.8K installs, 사용자 관측 2026-07-18)\n description:\n \"security-guidance — pattern warnings on every edit plus an LLM review of diffs and commits (Anthropic official). Weigh it against security tools you already run: overlap, false positives, and cost per edit\",\n category: \"dev-tools\",\n source: \"anthropics\",\n condition: { kind: \"opt-in\" },\n method: {\n kind: \"plugin\",\n marketplace: \"anthropics/claude-plugins-official\",\n pluginId: \"security-guidance@claude-plugins-official\",\n },\n },\n // v26.110.0 (ADR-039) — context7 플러그인은 **미등록** (검토 후 철회): 트랙 표(#751 전 templates/mcp.json) ·\n // codex config.toml.template · opencode 설정이 이미 @upstash/context7-mcp 를 기본 wiring —\n // 플러그인 추가 = 동일 서버 중복 등록이고 도달 범위도 더 좁다(plugin=claude-only vs\n // 템플릿=claude+codex+opencode). \"문서 조회 기본 제공\" 요구는 기충족.\n {\n id: \"agent-browser\",\n tier: \"vetted\", // vercel-labs/agent-browser 34k\n description:\n \"agent-browser — drives a real browser from the terminal (Playwright wrapper: screenshots, DOM search). Only if your CLI has no equivalent browser tool\",\n // v26.78.0 — Understanding 으로 재분류: 웹 지각(screenshot·DOM). 영상/코드 지각과 같은 축.\n category: \"understanding\",\n source: \"vercel-labs\",\n // #489 (사용자 결정 2026-09-20 a) — 기본 체크에서 opt-in 으로. computer-use 가 같은 일을 하고,\n // 셋 중 설치 비용이 가장 크다(npm devDep → 안 쓰는 프로젝트에 node_modules 만 남는다).\n // 3단계에는 그대로 뜨되 체크 해제, `--with agent-browser` 로 켠다. 기존 설치본은 영향 없음.\n condition: { kind: \"opt-in\" },\n method: { kind: \"npm\", pkg: \"agent-browser\", version: \"0.31.0\" },\n },\n // v26.78.0 — Understanding 신규 3종 (plugin, opt-in). 에이전트 인지 증강: 영상·코드 지각 + 메모리.\n {\n id: \"claude-video\",\n tier: \"vetted\", // bradautomates/claude-video 1.8k\n description:\n \"Claude Video — fetches a video from a URL, extracts frames and a transcript so the agent can see and hear it. For video evidence, not general image understanding\",\n category: \"understanding\",\n source: \"bradautomates\",\n condition: { kind: \"opt-in\" },\n method: {\n kind: \"plugin\",\n marketplace: \"bradautomates/claude-video\",\n pluginId: \"watch@claude-video\",\n },\n },\n {\n id: \"understand-anything\",\n tier: \"vetted\", // Lum1104/Understand-Anything 53k\n description:\n \"Understand Anything — builds an interactive knowledge graph and dashboard of your codebase. For visual exploration of a large codebase; the graph costs time to build and keep current\",\n category: \"understanding\",\n source: \"Lum1104\",\n condition: { kind: \"opt-in\" },\n method: {\n kind: \"plugin\",\n marketplace: \"Lum1104/Understand-Anything\",\n pluginId: \"understand-anything@understand-anything\",\n },\n },\n\n // === Visual & Media (v26.85.0) — 코드-퍼스트 제작 자산. 전부 opt-in. ===\n // Docker 실설치 검증 PASS (실 claude 2.1.177): plugin install / npx skills add resolve 확인.\n {\n id: \"frontend-slides\",\n tier: \"vetted\", // zarazhangrui/frontend-slides 21k\n description:\n \"frontend-slides — dependency-free HTML slide decks with PPTX-to-HTML and PDF export scripts. When an HTML deck is the actual deliverable\",\n category: \"visual-media\",\n source: \"zarazhangrui\",\n condition: { kind: \"opt-in\" },\n method: {\n kind: \"plugin\",\n marketplace: \"zarazhangrui/frontend-slides\",\n pluginId: \"frontend-slides@frontend-slides\",\n },\n },\n {\n id: \"marp-slide\",\n tier: \"vetted\", // softaworks/agent-toolkit 2k\n description:\n \"marp-slide — Marp Markdown slides with 7 themes and PPTX/PDF export. Only for a Marp-based workflow\",\n category: \"visual-media\",\n source: \"softaworks\",\n // softaworks plugin dir 는 plugin.json 부재 → skill 경로가 안전 (Docker 검증).\n condition: { kind: \"opt-in\" },\n method: { kind: \"skill\", source: \"softaworks/agent-toolkit\", skill: \"marp-slide\" },\n },\n {\n id: \"gsap-skills\",\n tier: \"vetted\", // greensock/gsap-skills 9k (GSAP 본가 공식)\n description:\n \"GSAP skills — GreenSock's own guide to timelines, ScrollTrigger, and framework lifecycle cleanup. Only for projects that already use GSAP; not a reason to pick it\",\n category: \"visual-media\",\n source: \"greensock\",\n condition: { kind: \"opt-in\" },\n method: {\n kind: \"plugin\",\n marketplace: \"greensock/gsap-skills\",\n pluginId: \"gsap-skills@gsap-skills\",\n },\n },\n {\n // remotion: star 3.6k → vetted (star 기반 tier SSOT + trust-tier-drift CI 정합, 사용자 결정 2026-06-13).\n // 코어 BUSL(Business Source License) — opt-in + description 고지로 신중 취급 (경고 배지 대신).\n // --skill 값 = remotion-best-practices (dir `remotion` ≠ frontmatter name, Docker 실측 확정).\n id: \"remotion\",\n tier: \"vetted\", // remotion-dev/skills 3.6k (license none — 출처 신뢰; 코어 BUSL 고지)\n description:\n \"Remotion — programmatic MP4 video from React components (core license BUSL). The skill is a reference; the renderer and its environment are a separate install\",\n category: \"visual-media\",\n source: \"remotion-dev\",\n condition: { kind: \"opt-in\" },\n method: { kind: \"skill\", source: \"remotion-dev/skills\", skill: \"remotion-best-practices\" },\n },\n // Issue #176 — 프레젠테이션 생성 스택 4종 (Docker 실설치 4/4 PASS: skills@1.5.11 add <src> --agent claude-code --skill, 2026-06-20).\n {\n id: \"ppt-master\",\n tier: \"vetted\", // hugohe3/ppt-master 29k\n description:\n \"ppt-master — editable PowerPoint (.pptx) from a document, with native shapes, speaker notes, and your own template. When the deliverable must stay editable in PowerPoint\",\n category: \"visual-media\",\n source: \"hugohe3\",\n condition: { kind: \"opt-in\" },\n method: { kind: \"skill\", source: \"hugohe3/ppt-master\", skill: \"ppt-master\" },\n },\n {\n // ADR-104 (#726) — web-video-presentation 의 교체. plugin 인 이유: skill 방식이면 상류 CLI 가\n // 작업 중 `npx hyperframes skills update`/`init` 으로 같은 스킬 디렉터리를 다시 쓰고 prune 해\n // 설치 기록(#573 sha)과 어긋난다. plugin 모드는 그 런타임 설치를 상류가 스스로 금한다.\n // Anthropic 공식 마켓플레이스 항목(sha 고정 URL 소스, 내용 저자 HeyGen) — 그래서 tier official.\n id: \"hyperframes\",\n tier: \"official\", // anthropics/claude-plugins-official 등재 (heygen-com/hyperframes)\n description:\n \"HyperFrames — write video as HTML and render it to MP4 (headless Chrome + FFmpeg): narrated explainers, slideshow-style decks, captions and voice-over. Rendering needs Node 22+ and FFmpeg\",\n category: \"visual-media\",\n source: \"heygen-com\",\n condition: { kind: \"opt-in\" },\n method: {\n kind: \"plugin\",\n marketplace: \"anthropics/claude-plugins-official\",\n pluginId: \"hyperframes@claude-plugins-official\",\n },\n },\n\n // === Supabase agent-skills (csr-supabase|full) ===\n {\n id: \"supabase-agent-skills\",\n tier: \"vetted\", // supabase/agent-skills 2.2k\n description:\n \"Supabase agent skills — auth, RLS, edge functions, realtime, and current docs (Supabase official). Only for projects on Supabase; pre-checked on csr-supabase and full\",\n category: \"backend\",\n source: \"supabase\",\n condition: { kind: \"any-track\", tracks: [\"csr-supabase\", \"full\"] },\n method: {\n kind: \"plugin\",\n marketplace: \"supabase/agent-skills\",\n pluginId: \"supabase@supabase-agent-skills\",\n },\n },\n {\n id: \"postgres-best-practices\",\n tier: \"vetted\", // supabase/agent-skills 2.2k\n description:\n \"Postgres best practices — schema, index, and query optimization reference (Supabase official). Reach for it when designing tables or fixing slow queries, not on every edit\",\n category: \"backend\",\n source: \"supabase\",\n condition: { kind: \"any-track\", tracks: [\"csr-supabase\", \"full\"] },\n method: {\n kind: \"plugin\",\n marketplace: \"supabase/agent-skills\",\n pluginId: \"postgres-best-practices@supabase-agent-skills\",\n },\n },\n\n // === React + Next UI tracks ===\n // v0.6.3 — vercel-labs/agent-skills source는 short form 안 됨. full HTTPS URL 필요.\n // 사용자 확인 형식: `npx skills add https://github.com/vercel-labs/agent-skills --skill <name>`.\n {\n id: \"react-best-practices\",\n tier: \"vetted\", // vercel-labs/agent-skills 27k (license none — 출처 신뢰)\n description:\n \"React best practices — Vercel's data-fetching order, server rendering, serialization, and cache guidance. For performance work in React, not every component edit\",\n category: \"frontend\",\n source: \"vercel-labs\",\n condition: { kind: \"any-track\", tracks: CSR_SSR_NEXTJS_FULL },\n method: {\n kind: \"skill\",\n source: \"https://github.com/vercel-labs/agent-skills\",\n // v0.6.5 — skills.sh registry name. GitHub dir 이름(react-best-practices)과 다름.\n // skills.sh: 대부분 vercel- prefix (web-design-guidelines, deploy-to-vercel만 예외).\n skill: \"vercel-react-best-practices\",\n },\n },\n {\n id: \"shadcn-ui\",\n tier: \"vetted\", // shadcn-ui/ui 115k\n description:\n \"shadcn/ui — reads this project's component config and registry so the agent adds components the way your setup expects (shadcn official). Only for projects that use shadcn\",\n category: \"frontend\",\n source: \"shadcn-ui\",\n condition: { kind: \"any-track\", tracks: CSR_SSR_NEXTJS_FULL },\n // v26.54.1 — shadcn/ui repo 의 실제 skill 이름은 `shadcn` (자산 id 와 다름).\n method: { kind: \"skill\", source: \"shadcn/ui\", skill: \"shadcn\" },\n },\n {\n // v26.106.0 (ADR-035, 사용자 승인 D): 순수 pattern-guide(일반 디자인 가이드라인) → opt-in 강등\n // (T2 가설 전제 — taste 3종 중복 주장은 검증자 정정으로 기각, 근거는 P축 단독).\n id: \"web-design-guidelines\",\n tier: \"vetted\", // vercel-labs/agent-skills 27k (license none — 출처 신뢰)\n description:\n \"Web design guidelines — Vercel's review checklist for hierarchy, color, spacing, and accessibility. A review aid, not a generator; frontend-design covers the default\",\n category: \"frontend\",\n source: \"vercel-labs\",\n condition: { kind: \"opt-in\" },\n method: {\n kind: \"skill\",\n source: \"https://github.com/vercel-labs/agent-skills\",\n skill: \"web-design-guidelines\",\n },\n },\n {\n // 2026-08-17 (사용자 지정) — 웹 게임 개발. **웹 전용이다**: 스킬 본문 frontmatter 가\n // \"HTML5, Canvas, WebGL, and JavaScript\" 로 스스로 범위를 적고 있고 프레임워크도\n // Phaser · Three.js · Babylon.js · A-Frame 이다. Unity/Unreal/Godot 은 안 덮으므로\n // description 에 \"web\" 을 남긴다 — 엔진 이름을 지우면 네이티브 엔진까지 되는 것처럼 읽힌다.\n // opt-in 인 이유: frontend 트랙 사용자 대부분은 게임을 만들지 않는다. 게임 트랙은 아직 없다.\n id: \"game-engine\",\n tier: \"vetted\", // github/awesome-copilot 37,909★ (MIT, 2026-08-17 `gh api` 실측 · GitHub 공식 조직)\n description:\n \"Game engine — web game development with HTML5 Canvas / WebGL (Phaser, Three.js, Babylon.js): game loop, physics, sprites, tilemaps, input. Web games only, not Unity or Unreal\",\n category: \"frontend\",\n source: \"github\",\n condition: { kind: \"opt-in\" },\n method: {\n kind: \"skill\",\n source: \"https://github.com/github/awesome-copilot\",\n skill: \"game-engine\",\n },\n },\n // === Executive tracks ===\n {\n id: \"anthropic-document-skills\",\n tier: \"official\", // anthropics/skills 144k\n description:\n \"Anthropic document skills — pptx / docx / xlsx / pdf authoring with real scripts (slide duplication, OOXML validation, thumbnails). For document deliverables\",\n category: \"business\",\n source: \"anthropics\",\n condition: { kind: \"any-track\", tracks: [\"executive\", \"full\"] },\n method: {\n kind: \"plugin\",\n marketplace: \"anthropics/skills\",\n pluginId: \"document-skills@anthropic-agent-skills\",\n },\n },\n // alirezarezvani/claude-skills marketplace (v2.3.0) — 2026-04-25 통합 갱신.\n // 기존 alirezarezvani/c-level-skills + alirezarezvani/finance-skills 별도 marketplace\n // → 통합된 alirezarezvani/claude-skills marketplace (claude-code-skills 이름)로 이동.\n {\n id: \"finance-skills\",\n tier: \"vetted\", // alirezarezvani 16k\n description:\n \"finance-skills — financial analyst, SaaS metrics, and investment advisor skills with calculation scripts. For financial analysis work; the tools matter more than the role names\",\n category: \"business\",\n source: \"alirezarezvani\",\n // 2026-08-02 사용자 결정: 트랙 기본 → opt-in (ADR-063)\n condition: { kind: \"opt-in\" },\n method: {\n kind: \"plugin\",\n marketplace: \"alirezarezvani/claude-skills\",\n pluginId: \"finance-skills@claude-code-skills\",\n },\n },\n\n // === Growth Marketing Track (v0.5.0) ===\n // 2026-08-02 정비 — alirezarezvani 번들 4종(c-level·business-growth·pm·marketing) +\n // research-summarizer 제거. 남는 마케팅 번들은 아래 coreyhaines31/marketingskills 하나로,\n // 동명이물(marketing-skills ≠ marketingskills) 병존도 이로써 해소된다.\n // v26.91.0 — coreyhaines31/marketingskills (35k★, MIT, plugin v2.5.1). opt-in =\n // growth-marketing 외 전 트랙에서도 wizard 토글 + `--with marketingskills` 로 설치 가능\n // (\"SEO 는 일반 개발에도 활용\" 충족: dev 트랙에서 본 번들 토글). 45 스킬 중 SEO 7종\n // (seo-audit/schema/ai-seo/site-architecture/programmatic-seo/content/aso)은 상호참조\n // (product-marketing 우선 + seo-audit↔schema↔ai-seo)라 부분 추출 시 포인터 깨짐 → 번들\n // 통째 설치만 정합. repoForAsset = marketplace(coreyhaines31/marketingskills) → drift 라이브.\n {\n id: \"marketingskills\",\n tier: \"vetted\", // coreyhaines31 35k\n description:\n \"marketingskills — SEO, conversion, ads, content, and measurement (45 skills, cross-referenced). For marketing work; too broad as a default on a development track\",\n category: \"business\",\n source: \"coreyhaines31\",\n condition: { kind: \"opt-in\" },\n method: {\n kind: \"plugin\",\n marketplace: \"coreyhaines31/marketingskills\",\n pluginId: \"marketing-skills@marketingskills\",\n },\n },\n // v26.76.0 — content-creator / demand-gen 제거: alirezarezvani/claude-skills marketplace.json 에\n // 해당 plugin 부재(Docker 실설치 검출, exit 1). 거짓 광고 0건 원칙(Promise=Implementation).\n\n // 2026-08-02 정비 — karpathy-coder 제거. Write|Edit 마다 Python complexity 검사를 돌리는\n // 상시 훅이었고, 그 검사는 현행 모델이 이미 하는 일이다 (Opus 5 가이드의 \"legacy harness\n // scaffolding\"에 정확히 해당). 딸린 배선(`--with-karpathy-hook` 플래그 · templates/hooks/\n // karpathy-gate.sh · settings.json PreToolUse auto-wire)도 함께 삭제 — BREAKING, ADR-060.\n\n // === Option-gated ===\n {\n // ADR-104 (#726) — trailofbits-skills(differential-review)의 교체. Anthropic 공식 플러그인.\n // `git push` 에 훅을 걸고 스캔이 시간·토큰을 쓰므로 opt-in 이다.\n id: \"claude-security\",\n tier: \"official\", // anthropics/claude-plugins-official 등재 (./plugins/claude-security)\n description:\n \"Claude Security — on-request vulnerability scan of a branch, PR, or commit diff; a verifier agent challenges each finding and results are tallied in code; asks you to confirm the cost before it starts. For a light diff check, the built-in /security-review is enough\",\n category: \"dev-tools\",\n source: \"anthropics\",\n condition: { kind: \"opt-in\" },\n method: {\n kind: \"plugin\",\n marketplace: \"anthropics/claude-plugins-official\",\n pluginId: \"claude-security@claude-plugins-official\",\n },\n },\n];\n\n/**\n * ADR-104 (#726) — 카탈로그에서 빠진 외부 자산. **설치 기록에** 이 id 가 남은 설치본에게 update 화면이\n * 할 말을 정한다(자산마다 한 줄 — 빠졌다/바뀌었다 · 깐 것은 남는다 · 새 것 받기 · 옛 것 치우기).\n *\n * 기록에서만 고른다 — 디스크 존재는 소유의 증거가 아니다(ADR-096). 같은 이름을 설치자가 하네스 밖에서\n * 직접 깔았을 수 있어서 `RETIRED_SKILL_IDS`(디스크로 찾는다, 번들 스킬 전용)와 가른다. 지우지 않는다\n * (ADR-093). `replacedBy` 는 지금 카탈로그에 실재해야 한다(🧪 `tests/external-assets.test.ts`).\n */\nexport const REMOVED_CATALOG_ASSETS: Readonly<Record<string, { replacedBy?: string }>> = {\n \"product-skills\": {},\n agentmemory: {},\n revealjs: {},\n \"trailofbits-skills\": { replacedBy: \"claude-security\" },\n \"web-video-presentation\": { replacedBy: \"hyperframes\" },\n};\n\n/**\n * v26.87.0 — dev-method skill ids (uzys 1st-party, internal templates). installer 의\n * `selectedInternalSkills` 계산 + manifest copy 게이팅 + 테스트가 공유하는 SSOT.\n * 각 id 는 method.kind===\"internal\" 이며 `templates/skills/<id>/SKILL.md` 로 번들된다.\n *\n * 2026-08-02 복원 (ADR-062) — 이관(ADR-060)으로 1종까지 줄었던 목록이 6종으로 돌아왔다.\n * 멤버십 기준은 **`condition.kind === \"has-dev-track\"` 인 번들 방법론 스킬**이다 (그 불변식을\n * `tests/external-assets.test.ts` 가 단언한다). 역은 성립하지 않는다 — has-dev-track 번들 스킬이라도\n * 방법론이 아니면(self-hosted-github-runner 런북 · model-orchestration · external-model-consult ·\n * natural-korean 수단, ADR-105) 여기 넣지 않는다. 이 목록은 설치 여부를 정하지 않는다(설치 선택은\n * `INTERNAL_BUNDLED_SKILL_IDS` + 각 condition) — 문서 수치·테스트가 쓰는 분류다.\n */\nexport const DEV_METHOD_SKILL_IDS: ReadonlyArray<string> = [\n \"compaction-handoff\",\n \"user-centered-explanation\",\n \"audit-service-gaps\",\n \"multi-persona-review\",\n \"recurrence-prevention\",\n];\n\n/**\n * v26.95.0 — ALL repo-bundled internal skill ids (dev-method + other bundled skills). Bundling is\n * condition-agnostic: manifest Claude dir-copy, the 3 non-Claude CLI transforms, and\n * gen-compatibility iterate THIS superset so every bundled skill renders across CLIs; each entry's\n * `condition` (has-dev-track vs opt-in) still gates whether it actually installs. Kept separate\n * from `DEV_METHOD_SKILL_IDS` so \"dev-method\" keeps meaning the has-dev-track methodology skills.\n *\n * 2026-08-02 복원 (ADR-062) — superset 이 다시 진부분집합이 됐다. dev-method 에 더해 전 트랙\n * 번들(north-star·gh-issue-workflow 등)과 방법론이 아닌 has-dev-track 번들(model-orchestration·\n * external-model-consult·natural-korean·self-hosted-github-runner, ADR-105)이 여기에만 있다.\n */\nexport const INTERNAL_BUNDLED_SKILL_IDS: ReadonlyArray<string> = [\n ...DEV_METHOD_SKILL_IDS,\n // #602 — 트랙을 따라가는 번들 스킬(csr-*·ssr-*·full). selectedInternalSkills 로 흘러야\n // .agents/skills(비-Claude 자리)에도 깔린다 — manifest 의 UI_SKILL_DIRS 배선은 .claude/ 만\n // 알아 TRACKS.md 와 어긋났다. 조건은 buildAssetSpec 이 UI 트랙으로 건다.\n \"ui-visual-review\",\n \"north-star\",\n \"gh-issue-workflow\",\n \"objective-brief\",\n \"audit-harness-fit\",\n \"model-orchestration\",\n \"external-model-consult\",\n \"self-hosted-github-runner\",\n \"natural-korean\",\n // #729 (ADR-109) — 토론(개발 트랙 기본) + 브라우저 위임 3종(opt-in). condition 이 설치를 가른다.\n \"multi-model-debate\",\n \"gemini-delegate\",\n \"chatgpt-delegate\",\n \"genspark-delegate\",\n];\n\n/**\n * ADR-085 (#427) — **매 응답·매 위임에 적용되는** 번들 스킬.\n *\n * 스킬 본문은 프롬프트가 그 스킬의 일처럼 보일 때 열린다. 그건 작업형 스킬에는 충분하고 여기\n * 적힌 것들에는 부족하다 — \"모든 답변에\" · \"모든 위임에\" 는 어떤 프롬프트도 닮지 않아서, 어딘가에\n * 한 줄이 없으면 영영 안 열린다(ADR-068 실측). 그 한 줄은 v26.150.0 까지 배포 앵커 꼬리절이\n * 들고 있었는데, 앵커가 전역 6원칙과 **바이트 동일**해지면서(ADR-085) 자리를 옮겼다:\n * 설치기가 **실제로 깐 스킬만** 골라 프로젝트 맥락 블록에 적는다(`renderContinuousSkillsNote`).\n * 앵커의 \"where installed\" 조건문보다 정확하다 — 안 깐 스킬 이름이 상주하지 않는다.\n *\n * 문장은 설치자의 에이전트가 읽는 지시문이다. 한 줄 = 언제 적용되는가 뿐, 설명·이력 없음.\n *\n * ADR-088 (#426 F-04) — `objective-brief`(구 `task-brief`)가 여기서 빠졌다. 그 스킬은 위임·설계·\n * 다단계 작업에만 쓰므로 \"매 응답·매 위임\" 축이 아니고, 상시 안내로 두면 한 줄 요청에도 브리프가\n * 앞섰다. 발화는 스킬 descriptor 가 맡는다.\n */\nexport const CONTINUOUS_SKILLS: ReadonlyArray<{ id: string; whenToApply: string }> = [\n {\n // #493 (#485 분리, 사용자 결정 2026-09-20) — 상주 한 줄의 문안은 사용자가 이슈에 적은 원칙이다:\n // 평소 설명부터 사용자 행동·결과를 먼저, 재설명·UX 전후·중요한 선택 정리는 스킬로.\n id: \"user-centered-explanation\",\n whenToApply:\n \"when explaining a development problem, change, or choice to the user, explain what the user does and sees before internal implementation — in every answer and report, not only when approval is asked for; for a detailed re-explanation, a UX before/after comparison, or laying out an important decision, apply this skill\",\n },\n {\n id: \"model-orchestration\",\n whenToApply:\n \"when work is delegated, it decides which lane takes the work and how that lane is run\",\n },\n];\n\n/**\n * 개명된 스킬 id (old → new). ADR-088 (#426 F-04) · #428(humanize-korean → natural-korean).\n *\n * `update` 는 **설치된 디렉터리만** 갱신하므로 옛 id 로 깐 설치본은 그대로 남는다 — 지우지 않는\n * 것이 현행 규율이다(남의 프로젝트 파일을 우리가 삭제하지 않는다). 그래서 화면이 대신 말한다:\n * 무엇이 무엇으로 바뀌었고 새 판을 어떻게 받는지. 렌더는 이 맵만 읽는다(`install-render.ts`).\n */\nexport const RENAMED_SKILL_IDS: Readonly<Record<string, string>> = {\n \"task-brief\": \"objective-brief\",\n \"humanize-korean\": \"natural-korean\",\n // #485 (2026-09-20) — 새 이름은 `new-skills` 묶음(#480 ①)이 같은 update 에서 깐다.\n \"clear-korean-communication\": \"user-centered-explanation\",\n};\n\n/**\n * 은퇴한 스킬 id — 카탈로그에도 번들에도 없고 대체 id 도 없다. ADR-088 (#426 F-09 · F-10 · F-11).\n *\n * `RENAMED_SKILL_IDS` 와 가르는 이유: \"새 이름으로 받아라\"와 \"이제 안 쓴다\"는 사용자가 할 일이\n * 다르다. 한 문구로 뭉치면 은퇴한 스킬의 새 판을 찾아 헤매게 된다.\n */\nexport const RETIRED_SKILL_IDS: ReadonlyArray<string> = [\n \"strategic-compact\",\n \"continuous-learning-v2\",\n \"spec-scaling\",\n // ADR-090 (#452) — 관측 없는 자산 은퇴. 앞 세 종과 같은 경로로 안내한다.\n \"verification-loop\",\n \"deep-research\",\n \"eval-harness\",\n \"agent-introspection-debugging\",\n // #492 — ECC cherry-pick 스킬 7종 은퇴. 번들에서 빠졌으니 설치본에 남은 디렉터리는 죽은\n // 사본이다 — `update` 화면이 \"지워도 된다\"를 말하고 삭제는 사용자가 한다(위와 같은 규율).\n \"python-patterns\",\n \"python-testing\",\n \"e2e-testing\",\n \"nextjs-turbopack\",\n \"market-research\",\n \"investor-materials\",\n \"investor-outreach\",\n];\n\n/**\n * v26.79.0 — `TRUST_TIER` 는 EXTERNAL_ASSETS.tier 에서 derive (단일 출처). 별도 Record 유지 시\n * 누락/stale drift 가능 → 제거. 기존 소비자(prompts.ts·gen-compatibility·trust-tier-drift)는\n * 이 derived map 을 그대로 import. id 키는 각 자산 id 와 1:1 (자산 추가 시 자동 반영).\n */\nexport const TRUST_TIER: Record<string, TrustTier> = Object.fromEntries(\n EXTERNAL_ASSETS.map((a) => [a.id, a.tier]),\n);\n\n/** 자산의 검증 tier. 미분류(catalog 외 id)는 보수적으로 experimental. */\nexport function assetTrustTier(assetId: string): TrustTier {\n return TRUST_TIER[assetId] ?? \"experimental\";\n}\n\n/**\n * v26.47.0 — User override of preset/option condition (Phase C full, SPEC §3.1).\n * - `forceInclude`: condition 무관 강제 포함 (사용자가 명시 추가)\n * - `forceExclude`: condition 무관 강제 제외 (사용자가 추천 ✓ 풀음)\n *\n * 우선순위: `forceExclude` > `forceInclude` > `condition`.\n */\nexport interface UserOverride {\n forceInclude: ReadonlyArray<string>;\n forceExclude: ReadonlyArray<string>;\n}\n\n/**\n * 조건 평가 — 주어진 spec(tracks + options + userOverride)에서 자산이 설치 대상인지 판정.\n */\nexport function shouldInstallAsset(\n asset: ExternalAsset,\n ctx: {\n tracks: ReadonlyArray<Track>;\n options: OptionFlags;\n userOverride?: UserOverride;\n },\n): boolean {\n // v26.47.0 — userOverride 우선순위: forceExclude > forceInclude > condition.\n if (ctx.userOverride?.forceExclude.includes(asset.id)) return false;\n if (ctx.userOverride?.forceInclude.includes(asset.id)) return true;\n // v26.71.1 — experimental(T3) opt-in only (PRD v26-71 R6/AC4). condition 매치만으론 미설치.\n // --with <id> (forceInclude) 또는 interactive 체크(→forceInclude) 시에만 설치 — 선택권 유지(강제 차단 아님).\n // v26.71.0 은 recommendedExternalAssets(pre-check)에만 제외 적용 → 비대화형/미체크 install 경로 누락 버그 fix.\n // 게이트는 명시 분류(TRUST_TIER[id])만 본다 — assetTrustTier()의 unknown→experimental default 는\n // DISPLAY(경고 배지)용이며 설치 게이트엔 미적용. 실 자산 전부 매핑은 \"no-missing\" 테스트가 강제(AC1).\n if (TRUST_TIER[asset.id] === \"experimental\") return false;\n return matchesCondition(asset, ctx);\n}\n\n/**\n * Track/option condition 매치만 평가 (tier·override 무관). shouldInstallAsset 의 조건절 +\n * experimentalOptInCandidates 의 \"조건은 맞지만 T3\" 판정에서 공유 (SSOT).\n */\nfunction matchesCondition(\n asset: ExternalAsset,\n ctx: { tracks: ReadonlyArray<Track>; options: OptionFlags },\n): boolean {\n const cond = asset.condition;\n switch (cond.kind) {\n case \"any-track\":\n return ctx.tracks.some((t) => cond.tracks.includes(t));\n case \"has-dev-track\":\n // SSOT — track-match.ts hasDevTrack(): csr-*|ssr-*|data|full|tooling (= DEV_TRACKS).\n // EXECUTIVE_STYLE_TRACKS (executive + project-management + growth-marketing) 는 제외.\n return hasDevTrack(ctx.tracks);\n case \"option\":\n return ctx.options[cond.flag] === true;\n case \"opt-in\":\n // v26.81.0 (ADR-022) — 순수 opt-in: condition 으론 절대 설치 안 됨.\n // forceInclude(wizard 체크 / --with <id>)가 shouldInstallAsset 상위에서 처리.\n return false;\n }\n}\n\n/**\n * v26.81.0 (ADR-022) — spec 에서 특정 자산의 선택 여부 판정.\n * 내부 자산(tauri-desktop)의 manifest/transform 게이팅이 이전\n * `spec.options.withTauri` 등 boolean 자리를 대체해 호출. (wizard 체크/--with 는\n * forceInclude 로 들어오므로 shouldInstallAsset 가 그대로 판정)\n */\nexport function isAssetSelected(\n assetId: string,\n ctx: {\n tracks: ReadonlyArray<Track>;\n options: OptionFlags;\n userOverride?: UserOverride;\n },\n): boolean {\n const asset = EXTERNAL_ASSETS.find((a) => a.id === assetId);\n return asset ? shouldInstallAsset(asset, ctx) : false;\n}\n\n/**\n * v26.71.1 — track/option condition 은 매치하지만 T3(experimental)라서 default 설치에서 제외된 자산.\n * forceInclude(--with / interactive 체크)된 것은 이미 설치되므로 제외. 비대화형 install 의\n * discoverability 힌트용 (Transparent Defaults — 숨김 0건. --with 로 opt-in 가능함을 사용자에게 알림).\n */\nexport function experimentalOptInCandidates(ctx: {\n tracks: ReadonlyArray<Track>;\n options: OptionFlags;\n userOverride?: UserOverride;\n}): ReadonlyArray<ExternalAsset> {\n return EXTERNAL_ASSETS.filter(\n (a) =>\n TRUST_TIER[a.id] === \"experimental\" &&\n !ctx.userOverride?.forceInclude.includes(a.id) &&\n matchesCondition(a, ctx),\n );\n}\n\n/**\n * spec에 적용 가능한 자산 필터.\n */\n/**\n * v26.102.0 (ADR-031, Batch3) — 자산의 CLI 도달 범위. 별도 필드가 아니라 method.kind 에서\n * **derive** 한다: 도달 범위의 SSOT 는 installOne 의 실동작이며, 같은 사실을 entry 필드로\n * 중복 기입하면 kind 와 필드가 어긋나는 drift 가 가능해진다 (no-false-ship \"동일 목록 2곳\n * 하드코딩 금지\").\n * - plugin: installPlugin 이 `claude plugin marketplace/install` 을 spawn — 구조적 claude 전용\n * - skill: skills CLI 가 `--agent` 매핑(SKILLS_CLI_AGENT_MAP)으로 선택 CLI 전부에 설치\n * - npm/npx-run: 프로젝트 레벨 (CLI 무관)\n * - internal: Phase 1 manifest/transform 이 CLI 별 렌더 (external spawn 단계 미도달)\n */\nexport function assetCliSupport(asset: ExternalAsset): CliTargets {\n // v26.102.0 SOD 리뷰 Critical-1 — kind 기본값이 거짓인 자산(bmad `--tools claude-code`)은\n // entry 의 cliSupportOverride 가 우선한다.\n if (asset.cliSupportOverride) {\n return [...asset.cliSupportOverride];\n }\n switch (asset.method.kind) {\n case \"plugin\":\n return [\"claude\"];\n case \"skill\":\n case \"npm\":\n case \"npx-run\":\n case \"internal\":\n // 공유 배열 원본 대신 사본 반환 — 타입 없는 소비자(gen-compatibility.mjs)의\n // in-place sort 가 CLI_BASES 를 전역 변형하는 사고 차단 (불변성 rule).\n return [...CLI_BASES];\n }\n}\n\n/**\n * 선택 CLI 와 자산 도달 범위의 교집합 여부. `cli` 가 빈 배열이면 레거시 관례\n * (buildSkillArgs 등 \"미지정 = 전체\")를 따라 필터하지 않는다.\n */\nexport function assetReachesCli(asset: ExternalAsset, cli: CliTargets): boolean {\n if (cli.length === 0) {\n return true;\n }\n const support = assetCliSupport(asset);\n return cli.some((c) => support.includes(c));\n}\n\nexport function filterApplicableAssets(\n assets: ReadonlyArray<ExternalAsset>,\n ctx: {\n tracks: ReadonlyArray<Track>;\n options: OptionFlags;\n userOverride?: UserOverride;\n },\n): ReadonlyArray<ExternalAsset> {\n return assets.filter((a) => shouldInstallAsset(a, ctx));\n}\n","import type { Track } from \"./types.js\";\n\n/** Match a track against a glob-style pattern (bash-compatible: `csr-*`, `csr-*|full`). */\nexport function matchTrack(track: Track, pattern: string): boolean {\n return pattern.split(\"|\").some((p) => globToRegex(p.trim()).test(track));\n}\n\nexport function anyTrack(tracks: ReadonlyArray<Track>, pattern: string): boolean {\n return tracks.some((t) => matchTrack(t, pattern));\n}\n\nexport function hasDevTrack(tracks: ReadonlyArray<Track>): boolean {\n // Dev tracks = anything that is NOT executive AND NOT tooling-only-meta.\n // Mirrors setup-harness.sh has_dev_track: csr-*, ssr-*, data, full, tooling.\n // #456 — base 는 dev 트랙이다(방법론 스킬·implementer 를 받는다). 스택 전용 자산은 각자의\n // 트랙 조건이 거른다.\n return anyTrack(tracks, \"csr-*|ssr-*|data|full|tooling|base\");\n}\n\nexport function hasUiTrack(tracks: ReadonlyArray<Track>): boolean {\n return anyTrack(tracks, \"csr-*|ssr-*|full\");\n}\n\n/** Convert a bash glob (only `*` is supported) to a strict-anchored RegExp. */\nfunction globToRegex(pattern: string): RegExp {\n const escaped = pattern.replace(/[.+?^${}()|[\\]\\\\]/g, \"\\\\$&\").replace(/\\*/g, \".*\");\n return new RegExp(`^${escaped}$`);\n}\n","import {\n INTERNAL_BUNDLED_SKILL_IDS,\n isAssetSelected,\n type UserOverride,\n} from \"./external-assets.js\";\nimport { INSTALL_LOG_DIR } from \"./install-log.js\";\nimport { HARNESS_ANCHOR_FILE } from \"./project-claude-merge.js\";\nimport { anyTrack, hasDevTrack } from \"./track-match.js\";\nimport { type OptionFlags, TRACKS, type Track } from \"./types.js\";\n\n/**\n * Source-relative paths under `templates/` map to project-relative targets under\n * the project root (typically `.claude/...` or `CLAUDE.md`).\n *\n * Each entry declares an `applies(spec)` predicate based on the selected tracks.\n * Phase C scope: rules + commands + agents + base skills + hooks + project CLAUDE.md.\n * Phase E will extend with the remaining track-specific skills.\n */\n\nexport interface AssetSpec {\n /** Selected tracks (union). */\n tracks: ReadonlyArray<Track>;\n /**\n * Optional opt-in: --with-tauri.\n * Note: copied from `OptionFlags.withTauri` by installer; keep both fields in sync\n * when adding new opt-in flags that affect manifest gating.\n *\n * 2026-08-02 정비 — `tauri` **룰**이 배포에서 빠져 이 필드를 읽는 게이팅은 현재 없다.\n * 필드를 남긴 이유는 `tauri-desktop` 자산 선택 자체는 살아 있고(installer 가 여전히 채운다)\n * 트랙×opt-in 곱을 도는 게이트들이 이 축을 그대로 쓰기 때문이다.\n */\n withTauri?: boolean;\n /**\n * v26.87.0 — 선택된 내부 dev-method skill id 집합 (uzys 1st-party, repo-bundled).\n * installer 가 `DEV_METHOD_SKILL_IDS` 를 `isAssetSelected` 로 필터해 채운다 — 즉\n * track(has-dev-track) 기본 + wizard uncheck / `--without <id>` (forceExclude) 반영.\n * buildManifest 의 skill-dir copy 가 이 목록에 포함된 id 만 게이팅한다.\n */\n selectedInternalSkills?: ReadonlyArray<string>;\n}\n\n/**\n * `AssetSpec` 의 derive. installer 의 `buildManifestSpec`, 설치 화면 두 표면, 계측 경로가\n * **모두 이것을 부른다** — 그 목록이 여기서 끝이라는 뜻은 아니다(아래 \"남은 자리\" 참조).\n *\n * WHY (#320): 상주 비용을 세는 자리들이 이 spec 을 **손으로 조립**하고 있었고, 전부\n * `selectedInternalSkills` 를 안 채웠다. 그 필드가 비면 번들 스킬의 `applies` 가\n * `(undefined ?? []).includes(...)` 로 전부 false 가 되어 **계측에서 통째로 빠진다**.\n * 실측: track=tooling 에서 상주가 23개/~5,331 로 보고됐지만 실제는 34개/~7,781 —\n * 1차 NSM 이 실제의 약 68%만 세고 있었다.\n *\n * 원인은 필드 하나가 아니라 **조립을 각자 하고 있었다는 것**이다. 그래서 고친 방향은 각자\n * 채우기가 아니라 이 함수로 모으는 것이다. 개수를 여기 적지 않는다 — 세어 적은 숫자는 두 번째\n * 사본이라 썩는다(실제로 커밋 본문의 \"다섯\"과 이 주석의 \"넷\"이 갈렸고 독립 리뷰가 잡았다).\n *\n * **남은 자리(정직하게)**: `protect-branch-surface` · `spec-drift-script-surface` ·\n * `resident-reach-4cli` 세 테스트는 여전히 좁은 spec 을 손으로 만든다. 셋 다 스킬 게이팅과\n * 무관한 단일 `.uzys-agent-harness/*.sh` 엔트리만 보므로 현재 무해하지만, \"모든 자리가 여기를\n * 부른다\"고 읽으면 틀린다.\n *\n * `cli` 는 받지 않는다 — `buildManifest` 도 `applies` 도 그 필드를 읽지 않는다(계측 spec 들이\n * `cli: [\"claude\"]` 를 넣고 있었지만 아무 효과가 없었다).\n */\nexport function buildAssetSpec(ctx: {\n tracks: ReadonlyArray<Track>;\n options: OptionFlags;\n userOverride?: UserOverride;\n}): Required<AssetSpec> {\n return {\n tracks: ctx.tracks,\n withTauri: isAssetSelected(\"tauri-desktop\", ctx),\n // v26.87.0 — internal bundled skills (dev-method + opt-in advisors, v26.95.0). id 별 condition\n // (has-dev-track vs opt-in)은 isAssetSelected 가 적용한다.\n selectedInternalSkills: INTERNAL_BUNDLED_SKILL_IDS.filter((id) => isAssetSelected(id, ctx)),\n };\n}\n\nexport interface AssetEntry {\n source: string; // relative to repo `templates/`\n target: string; // relative to project root\n type: \"file\" | \"dir\";\n applies: (spec: AssetSpec) => boolean;\n}\n\nconst all = (): boolean => true;\nconst dev = (s: AssetSpec): boolean => hasDevTrack(s.tracks);\nconst onTracks =\n (pattern: string) =>\n (s: AssetSpec): boolean =>\n anyTrack(s.tracks, pattern);\n\n// v26.107.0 (ADR-036, 라이프사이클 자산화 ①) — doc-governance: SSOT 위계 + \"merge = 코드 +\n// 추적 동기화\" 의무. 실무 관행의 일반화. 문서 규약은 전 트랙 공통.\n// 2026-08-02 정비 ② — `gates-taxonomy` 제거. 게이트 4유형(Pre-flight/Revision/Escalation/Abort)은\n// 모델이 이미 아는 분류였고, 설치자가 받아 가는 것은 판정이 아니라 어휘표였다.\nconst COMMON_RULES = [\"git-policy\", \"change-management\", \"doc-governance\"];\nconst DEV_RULES = [\"test-policy\", \"ship-checklist\"];\n// 2026-08-04 (#284) — `benchmark-parity` 제거. 그 룰이 담고 있던 것은 gap.md 표 스키마 · PR\n// 의무 필드 · dogfood walkthrough 절차였다. 전부 **그 작업을 할 때만** 필요한 절차인데 매\n// 세션 상주했다.\n// 2026-08-12 — `playwright-launch` 제거(사용자 결정). 남은 것은 브라우저 금지문이었는데,\n// 그 룰 본문이 스스로 \"절차는 `ui-visual-review` 스킬이 SSOT\" 라고 적고 있었다. 금지와 절차가\n// 한 자리에 있는 편이 낫다고 판단해 스킬로 합쳤고, 발화가 늦는 위험은 스킬 description 이\n// \"브라우저를 여는 어떤 방법이든 그 전에 읽어라\"로 흡수한다.\n// 2026-10-11 (#725 · #717) — `ui-components`: React·Next.js UI 는 shadcn/ui 로 만들고\n// `@shadcn/lint` 로 검사한다. 아래 2026-08-02 에 뺀 shadcn 룰과 축이 다르다 — 그건 모델이 이미\n// 아는 사용법이었고, 이건 **모델이 모르는 새 도구**(2026-09 출시)와 **사용자가 고른 스택**이다.\n// `paths:` 로 `.tsx`/`.jsx` 를 만질 때만 싣는다(Claude Code · Antigravity).\n// #820 — React 를 쓰는 트랙에만 간다. htmx 트랙은 화면이 서버 템플릿 + daisyUI 라 이 룰의 \"shadcn/ui 로\"가\n// 틀린 안내가 되고, Fastify 서버 JSX(`.tsx`)에서는 같은 경로로 로드돼 htmx 룰과 부딪친다.\nconst UI_RULES: string[] = [\"ui-components\"];\nconst REACT_UI_TRACKS = \"csr-*|ssr-nextjs|full\";\n\n// 2026-08-02 정비 — 기술스택 상세 룰(shadcn·nextjs·htmx·pyside6·database·api-contract·\n// data-analysis·tauri)을 배포에서 뺐다. 모델이 이미 아는 것을 상주로 부담시키던 자리다.\n// 남은 트랙 매핑은 `cli-development` 하나뿐 — 그래도 표를 유지하는 이유는 트랙별 룰이라는\n// 축 자체는 살아 있고, 배열을 없애면 다음에 트랙 룰을 넣을 자리가 사라지기 때문이다.\nconst TRACK_RULES: Record<Track, string[]> = {\n base: [], // #456 — 공통 룰만. cli-development 는 tooling·full 전용\n \"csr-supabase\": [],\n \"csr-fastify\": [],\n \"csr-fastapi\": [],\n // #820 — htmx 화면 기술(Tailwind v4 + daisyUI)과 그 스택에서 실제로 도는 린트. 백엔드마다 템플릿 형식이\n // 달라 린트가 갈린다(Jinja = ast-grep · 서버 JSX = @shadcn/lint) — 실측 = `docs/plans/820-spike/`. `full` 은 아래.\n \"ssr-htmx\": [\"htmx-fastapi-ui\"],\n \"ssr-htmx-fastify\": [\"htmx-fastify-ui\"],\n \"ssr-nextjs\": [],\n data: [],\n executive: [],\n tooling: [\"cli-development\"],\n // #820 — htmx 룰 둘은 `full` 에 넣지 않는다. OpenCode 가 섞인 설치는 룰을 AGENTS.md 본문에 인라인하는데, full 의 렌더가\n // Antigravity 상한(24,000 B)을 넘는다(22,322 → 24,455 B, `agents-md-size-budget`). htmx 를 쓰면 그 트랙을 함께 고른다.\n full: [\"cli-development\"],\n // v0.5.0 — executive-style baselines (no dev rules; common rules only).\n \"project-management\": [],\n \"growth-marketing\": [],\n};\n\n/** Resolve the unique set of rule names to install for the given spec. */\nexport function resolveRules(spec: AssetSpec): string[] {\n const set = new Set<string>(COMMON_RULES);\n if (hasDevTrack(spec.tracks)) {\n for (const r of DEV_RULES) {\n set.add(r);\n }\n }\n if (anyTrack(spec.tracks, REACT_UI_TRACKS)) {\n for (const r of UI_RULES) {\n set.add(r);\n }\n }\n for (const t of spec.tracks) {\n for (const r of TRACK_RULES[t]) {\n set.add(r);\n }\n }\n return [...set].sort();\n}\n\n/**\n * 모든 트랙의 룰 union — `update` 가 쓴다. 전부 넘기고 \"이 프로젝트에 뭐가 깔렸나\"는\n * `refreshOnly` 가 디스크로 대신 판정한다 (`ALL_CLI_TARGETS` · `INTERNAL_BUNDLED_SKILL_IDS` 와\n * 같은 형태). `resolveRules` 에서 파생하므로 룰이 늘거나 줄어도 여기를 고칠 필요가 없다.\n */\nexport const ALL_RULES: ReadonlyArray<string> = resolveRules({ tracks: [...TRACKS] });\n\n// 전 트랙 에이전트. ADR-090 (#452) — `data-analyst`·`strategist` 가 여기서 빠져 트랙 조건부로\n// 내려갔다(`TRACK_AGENTS`): 도메인 관례를 싣는 descriptor 인데 tooling 설치자에게는 열릴 일이\n// 없어 상주만 했다. ADR-089 (#445) 이후 ECC 폴백 에이전트 축은 비어 있고, ADR-090 에서\n// dev 축 폴백 두 종(`silent-failure-hunter`·`build-error-resolver`)도 은퇴해 그 축 자체가\n// 없어졌다 — #492 에서 ECC 축(과 그 게이트)이 통째로 빠지면서 `superseded` 판정도 함께\n// 사라졌다. 같은 형태가 다시 생기면 그 판정부터 되살린다.\nconst CORE_AGENTS = [\"reviewer\"];\n\n// v26.138.0 — implementer: 구현 레인. 기존 에이전트 8종이 전부 검토·검증·도메인 특화라\n// 설치자는 \"코드를 볼 사람\"만 받고 \"쓸 사람\"은 못 받았다. 근거 = 두 코퍼스 실측 대조에서\n// 서브에이전트 코드 Edit 433 vs 3 — 규율 차이가 아니라 **레인 부재**였다.\n// ADR-090 (#452) — `plan-checker` 은퇴(spawn 0 · 없는 절·없는 파일을 지목). 계획 문서 검토는\n// `reviewer` 가 같은 계약으로 한다.\nconst DEV_AGENTS = [\"implementer\"];\n\n/**\n * 트랙 조건부 에이전트 — 그 도메인을 고른 설치자에게만 간다. ADR-090 (#452).\n *\n * 전 트랙이던 두 종을 여기로 내린 이유: descriptor 는 트랙과 무관하게 매 세션 상주하는데,\n * tooling·ssr-nextjs 만 고른 설치자에게 \"데이터 분석 관례\"·\"사업 전략\" 레인은 열릴 일이 없다.\n * 트랙 패턴은 각 에이전트의 도메인과 1:1 이다.\n *\n * **export 인 이유** (#458): 강등 **전에** 깔린 설치본에는 이 파일들이 디스크에 남는다\n * (`pruneOrphans` 는 템플릿에 원본이 있는 한 못 지운다 — 은퇴가 아니라 트랙 조건화라 원본은\n * 그대로다). `update` 화면이 \"어느 트랙 전용인지 · 지워도 된다\"를 말하려면 **id 와 패턴을 같이**\n * 읽어야 하고, 그 근거는 배선 SSOT 인 이 표 하나다 — update 쪽에 id 를 다시 적으면 다음 강등에서\n * 한쪽이 조용히 뒤처진다(`RETIRED_AGENT_IDS` 와 같은 규율).\n */\nexport const TRACK_AGENTS: ReadonlyArray<readonly [string, string]> = [\n [\"data-analyst\", \"data|full\"],\n [\"strategist\", \"executive|full\"],\n];\n\n/**\n * 은퇴한 에이전트 id — 번들에도 manifest 에도 없고 대체 파일도 없다. ADR-089 (#445).\n *\n * 이미 깔린 파일의 운명은 **설치 기록(ADR-047)이 그 파일을 하네스 것이라 말하느냐**(`recorded()`)로 갈린다. 말하면\n * `pruneOrphans` 가 `update` 때 회수한다(편집분 · 체크섬 없는 옛 판은 백업 — 파일 필드가 없던 옛 로그의 은퇴 자리도\n * `RETIRED_PATHS` 로 여기다, #796 §1.2 ③). 말하지 못하면 파일이 남고, 침묵하면 사용자는 그게 죽은 사본인 줄\n * 모른다 — 그때만 화면이 대신 말하고 손은 사용자가. 스킬 쪽\n * `RETIRED_SKILL_IDS`(`external-assets.ts`)와 같은 형태이고, 렌더는 이 목록만 읽는다\n * (`commands/install-render.ts`). 안내 스캔은 prune 뒤에 돈다 — 이미 지운 파일을 \"지워도 된다\"고\n * 말하지 않기 위해서다.\n *\n * 여기에 이름을 두는 이유: 에이전트를 무엇을 어디에 깔지 정하는 **배선 SSOT 가 이 파일**이라\n * 은퇴 목록이 갈라지면 다음 은퇴에서 한쪽이 조용히 뒤처진다.\n */\nexport const RETIRED_AGENTS: ReadonlyArray<{ id: string; instead: string }> = [\n { id: \"code-reviewer\", instead: \"Claude Code 의 `/code-review` 가 같은 일을 한다\" },\n { id: \"security-reviewer\", instead: \"Claude Code 의 `/security-review` 가 같은 일을 한다\" },\n // ADR-090 (#452) — spawn 0 · 결함만 관측된 세 종. **대안을 종마다 적는다**: 한 문구로 뭉치면\n // 자기 에이전트와 상관없는 대안을 읽게 되고, 그건 \"대신 쓸 것을 말한다\"의 뜻을 잃는다.\n { id: \"plan-checker\", instead: \"`reviewer` 가 계획 문서도 같은 계약으로 검토한다\" },\n { id: \"silent-failure-hunter\", instead: \"Claude Code 의 `/code-review` 가 같은 일을 한다\" },\n { id: \"build-error-resolver\", instead: \"모델이 기본으로 하는 일이라 대체 자산이 없다\" },\n];\n\n/**\n * 하네스가 `.claude/` 에 배포했다가 은퇴시킨 정책 파일 — **옛 로그 키 형식**(`policyFiles` 의 `.claude/` 상대 경로).\n *\n * 왜 있나 (#551 리뷰 B3): 옛 판(v26.161.0 까지)의 `policyFiles` 는 설치 뒤 디스크를 훑어 적은 값이라 설치자 파일이\n * 섞여 있고, 새 판은 거기에 소유 필터를 건다(`recorded.ts`). 필터가 \"지금 배포하는 것\" 만 소유로 인정하면 과거에\n * 배포했다가 은퇴시킨 파일이 설치자 파일로 떨어져 update 도 uninstall 도 영영 치우지 않는다 — 오늘 `pruneOrphans` 가\n * 하던 회수가 사라진다. 이 목록의 경로는 \"기록 있음 · sha 없음\" 으로 읽혀 `backup+remove` 로 치워진다(옛 스캔 sha 는\n * 하네스 내용의 증거가 아니므로 설치자가 같은 이름으로 쓴 파일이어도 바이트는 백업에 남는다).\n *\n * **닫힌 목록이다.** 옛 판(디스크 스캔 로그) 전용이다. 새 판이 로그를 쓰기 시작한 뒤(`records: \"writer\"`)의 은퇴는\n * writer 가 쓴 기록이 치우므로 여기에 더하지 않는다 — 앞으로 늘지 않는다.\n *\n * 범위 = `pruneOrphans` 가 회수하던 `.claude/{rules,agents,commands/uzys,hooks}`(확장자는 `POLICY_DIRS` 그대로).\n * 출처 = `git log --diff-filter=DR --name-status -- templates/rules templates/agents templates/hooks templates/commands`\n * 중 그 범위 · 확장자이고 지금 템플릿에 없는 것 45개(이름 변경 0, 2026-09-27). 스킬은 범위 밖이다 — 스킬 은퇴는 안내만\n * 한다(`RETIRED_SKILL_IDS`). 다른 CLI 경로는 옛 스캔 필드에 없다(그쪽은 쓰는 순간 적는 `externalFiles` 뿐이다).\n */\nexport const RETIRED_PATHS: ReadonlyArray<string> = [\n // rules (23)\n \"rules/api-contract.md\",\n \"rules/benchmark-parity.md\",\n \"rules/code-style.md\",\n \"rules/commit-policy.md\",\n \"rules/data-analysis.md\",\n \"rules/database.md\",\n \"rules/design-workflow.md\",\n \"rules/ecc-git-workflow.md\",\n \"rules/ecc-performance-common.md\",\n \"rules/ecc-security-common.md\",\n \"rules/ecc-testing.md\",\n \"rules/error-handling.md\",\n \"rules/gates-taxonomy.md\",\n \"rules/gsd-planner-antipatterns.md\",\n \"rules/htmx.md\",\n \"rules/model-routing.md\",\n \"rules/nextjs.md\",\n \"rules/no-false-ship.md\",\n \"rules/playwright-launch.md\",\n \"rules/pyside6.md\",\n \"rules/seo.md\",\n \"rules/shadcn.md\",\n \"rules/tauri.md\",\n // agents (5)\n \"agents/build-error-resolver.md\",\n \"agents/code-reviewer.md\",\n \"agents/plan-checker.md\",\n \"agents/security-reviewer.md\",\n \"agents/silent-failure-hunter.md\",\n // commands/uzys (7)\n \"commands/uzys/auto.md\",\n \"commands/uzys/build.md\",\n \"commands/uzys/plan.md\",\n \"commands/uzys/review.md\",\n \"commands/uzys/ship.md\",\n \"commands/uzys/spec.md\",\n \"commands/uzys/test.md\",\n // hooks (10)\n \"hooks/agentshield-gate.sh\",\n \"hooks/checkpoint-snapshot.sh\",\n \"hooks/codebase-map.sh\",\n \"hooks/gate-check.sh\",\n \"hooks/hito-counter.sh\",\n \"hooks/karpathy-gate.sh\",\n \"hooks/mcp-pre-exec.sh\",\n \"hooks/spec-drift-check.sh\",\n \"hooks/task-brief-nudge.sh\",\n \"hooks/uncommitted-check.sh\",\n];\n\n/**\n * 옛 템플릿(`templates/settings.json`)이 `.claude/hooks/` **밖**의 스크립트로 배선했다가 은퇴시킨 하네스 훅 참조 —\n * `.claude/` 상대경로(`projectAnchoredRef` 의 반환 형태).\n *\n * 왜 있나 (#746 재리뷰 B-1): 그 배선은 하네스가 깔았는데 옛 로그 몫 찾기(`legacySettingsSeed`)는 `hooks/` 아래만\n * 알아보므로 기록 밖에 남는다. update 3단계는 기록 밖 죽은 참조를 설치자 몫으로 보고 알리기만 하니(#746 리뷰\n * BLOCKER-1), 이 목록이 없으면 은퇴한 하네스 배선이 영구히 남고 화면이 그것을 \"yours\" 라고 부른다. update 는 이 목록의\n * 참조 중 **스크립트가 없는 것**만 걷는다(`removeRetiredHookRefs`) — 스크립트가 있으면 설치자가 스킬을 남겨 쓰는 중이다.\n *\n * **닫힌 목록이다.** 출처 = `git log -p -- templates/settings.json` 의 `$CLAUDE_PROJECT_DIR/.claude/` 앵커 command 중\n * `hooks/` 밖인 것 전부(2026-10-12 확인, 2개). 현행 템플릿 렌더는 `hooks/` 밖 핸들러를 거부하므로(`install-writes`\n * 의 settings 렌더가 throw) 앞으로 늘지 않는다. 목록 밖의 기록 밖 참조는 계속 알리기만 한다.\n */\nexport const RETIRED_HOOK_WIRING_REFS: ReadonlyArray<string> = [\n // 6617766 (2026-04-14, phase4b) 배선 → c8fdef1 (2026-05-03, CL-v2 observer 중복 fire 제거) 제거.\n // PreToolUse · PostToolUse 각각 matcher \"*\" 묶음 하나 — `observe.sh pre|post`, async.\n \"skills/continuous-learning-v2/hooks/observe.sh\",\n // 5defc58 (2026-04-14, wire strategic-compact) 배선 → a125453 (2026-09-13, #444 스킬 3종 은퇴) 제거.\n // PreToolUse 의 protect-files 묶음 안 두 번째 핸들러, async.\n \"skills/strategic-compact/suggest-compact.sh\",\n];\n\n/**\n * 은퇴 판정용 에이전트 id — `RETIRED_PATHS` 에서 derive 한다(은퇴 목록이 둘로 갈라지지 않게). 대안 문구는\n * `RETIRED_AGENTS` 가 갖고, 두 목록의 id 가 같은지는 `tests/recorded.test.ts` 가 문다.\n */\nexport const RETIRED_AGENT_IDS: ReadonlyArray<string> = RETIRED_PATHS.filter((p) =>\n p.startsWith(\"agents/\"),\n).map((p) => p.slice(\"agents/\".length).replace(/\\.md$/, \"\"));\n\n/**\n * Hooks installed for every project (parity with setup-harness.sh L815-826).\n *\n * 2026-08-02 정비 — `spec-drift-check.sh` 제거. 산문(doc-governance·ship-checklist)은 이것을\n * 차단 게이트로 적었지만 실제로는 어디에도 배선돼 있지 않았고(`templates/settings.json` 미참조),\n * 미완 체크박스가 쌓인 상태에서도 `ship` 모드가 exit 0 이었다 — 즉 설치자가 매 프로젝트에서\n * 받아 가는 것은 게이트가 아니라 게이트라는 **이름**이었다.\n *\n * 2026-08-02 정비 ② — `checkpoint-snapshot.sh` 제거. 같은 형태의 다른 결말이다: 배선이 없어\n * (`\"PostToolUse\": []`) 설치는 되고 **실행은 0** 이었다. 안 도는 훅은 실패 증상이 없어 프로즈로는\n * 못 잡는다 — 재발은 `tests/hook-wiring-parity.test.ts` 가 양방향으로 문다.\n *\n * 2026-08-16 정비 ③ — `mcp-pre-exec.sh` 제거(ADR-072). 앞의 둘과 달리 이건 **돌고 있었고 잘\n * 돌았다** — 지운 이유가 배선이 아니라 목적이다. 이 하네스가 파는 것은 \"사용자가 AI 코딩 도구로\n * 개발을 잘하게 만드는 것\"인데, MCP 서버를 새로 붙이는 순간 차단하는 훅은 그 반대로 작동한다\n * (판정 기준 = 루트 `CLAUDE.md` §판정은 목적에서 시작한다). 결정론적으로 구현됐다는 사실은\n * 목적 적합성의 증거가 아니다.\n */\n// ADR-088 (#426 F-04) — UserPromptSubmit 넛지 훅이 빠졌다. 긴 프롬프트마다 \"브리프로 정리하라\"를\n// 한 줄 붙였는데, 브리프는 위임·설계·다단계 작업에만 쓰는 것으로 문턱이 올라갔다(`objective-brief`).\n// 매 프롬프트 훅은 그 문턱을 표현할 수 없다 — 길이는 규모의 증거가 아니다.\nexport const ALWAYS_HOOKS = [\"session-start.sh\", \"protect-files.sh\"];\n\n// 2026-08-02 정비 (ADR-060) — north-star · gh-issue-workflow 는 uzysjung/uzys-agent-skills 로\n// 이관돼 카탈로그 엔트리(`kind: \"skill\"`)가 됐다. 전 트랙 도달 범위는 그 엔트리의\n// `any-track: 전 트랙` condition 이 이어받는다 (강등 아님).\n// #492 — ECC 축(cherry-pick × plugin gating, ADR-019)이 통째로 없어졌다. 폴백 사본 7종은\n// 은퇴(`RETIRED_SKILL_IDS`)했고 `ecc-plugin`·`ecc-prune` 은 카탈로그에서 빠졌다. 남은 두 상수는\n// 우리가 직접 쓰는 스킬의 자리다 — 빈 목록이면 아래 루프가 0회 돌아 엔트리를 안 만든다.\nconst COMMON_SKILL_DIRS: string[] = [];\nconst DEV_SKILL_DIRS: string[] = [];\n\n// #602 — ui-visual-review 는 INTERNAL_BUNDLED_SKILL_IDS 로 이동(4CLI 배선). 스펙의\n// selectedInternalSkills 가 UI 트랙 조건을 건다 — 여기 별도 루프는 .claude/ 만 깔던 누수의 원천이었다.\n\n/**\n * CLI 중립 자산인가 — `.uzys-agent-harness/` 아래는 4개 CLI 와 사람이 함께 쓰는 슬롯이다\n * (install log 와 같은 디렉터리, ADR-050). claude 를 고르지 않은 설치에도 이 자산들은 깔린다.\n *\n * 접두사에서 derive 하는 이유: 대상 목록을 두 번 적으면 그 사본이 다음 drift 서식지가 된다.\n * 아래 두 entry 의 주석이 이미 \"CLI 중립 슬롯\"이라 적고 있었는데 배선만 그러지 못했다.\n */\nexport function isCliNeutralTarget(target: string): boolean {\n return target.startsWith(`${INSTALL_LOG_DIR}/`);\n}\n\n/** Build the full asset manifest for the given spec. */\nexport function buildManifest(spec: AssetSpec): AssetEntry[] {\n const m: AssetEntry[] = [];\n\n // Rules\n for (const r of resolveRules(spec)) {\n m.push({\n source: `rules/${r}.md`,\n target: `.claude/rules/${r}.md`,\n type: \"file\",\n applies: all,\n });\n }\n\n // 2026-08-16 — `commands/ecc` 8종 삭제 (ADR-073). ADR-019 는 이것을 \"ECC 플러그인을 안 고른\n // 사람도 같은 명령을 쓰게\" 하는 opt-out 폴백으로 넣었는데, 8개 중 **2개**(`e2e`·`eval`)가\n // 플러그인 에이전트(`everything-claude-code:*`)를 직접 불러 폴백 상황에서 아예 못 돌았다.\n // 나머지 6개는 돌았지만 플러그인이 여섯을 모두 제공하므로 폴백을 기본값에 둘 이유가 없다.\n // 2026-08-18 정정(#338) — 이 주석과 ADR-073 Context 는 \"5개가 안 고른 자산을 가리킨다\"로\n // 적고 있었다. 그중 셋이 부르던 학습 스킬은 같은 `!s.withEcc` 조건으로 **함께 깔리는**\n // C2 항목이었다 — \"별도 opt-in 스킬이라 대개 없다\"가 틀렸다. 결정은 유지, 숫자만 정정.\n // (그 스킬은 ADR-088 에서 은퇴했다 — 명령 템플릿도 이미 없다.)\n // #492 — `ecc-plugin`·`ecc-prune` 자산도 카탈로그에서 빠져 ECC 축 자체가 없다.\n\n // 하네스 앵커 — **프로젝트 루트**에 하네스 소유 파일로 나간다 (P5 · ADR-060).\n // 루트 `CLAUDE.md` 는 사용자 것이고, 거기엔 이 파일을 끌어오는 `@import` 한 줄만 들어간다\n // (`project-claude-merge.ts` upsertHarnessImport). 파일명은 그 모듈이 SSOT.\n m.push({\n source: \"CLAUDE.md\",\n target: HARNESS_ANCHOR_FILE,\n type: \"file\",\n applies: all,\n });\n\n // Server-side git guards. Target is CLI-neutral on purpose — a repository rule protects the\n // default branch for every CLI and every human at once, so it does not belong under `.claude/`.\n // Same directory as the install log (ADR-050), which is already the CLI-agnostic slot.\n m.push({\n source: \"scripts/protect-branch.sh\",\n target: \".uzys-agent-harness/protect-branch.sh\",\n type: \"file\",\n applies: all,\n });\n\n // SPEC/TODO drift 탐지기. **훅이 아니라 명시 호출 스크립트다** — 자동 발화 경로가 없는 것이\n // 정직한 상태이고(옛 훅 판본이 죽은 이유가 배선 없는 훅 라벨이었다), 호출 지점은 배포판 룰\n // 둘이 적는다: ship-checklist 의 SPEC/PRD 정합성 항목(exit 2 = 차단) · doc-governance 검증\n // 게이트 절. 타깃은 protect-branch 와 같은 CLI 중립 슬롯 — 문서 drift 는 4개 CLI 와 사람이\n // 함께 보는 관심사라 `.claude/` 아래가 아니다.\n m.push({\n source: \"scripts/spec-drift-check.sh\",\n target: \".uzys-agent-harness/spec-drift-check.sh\",\n type: \"file\",\n applies: all,\n });\n\n // 부정 결론(\"없다\"·\"안 된다\")의 대조군 강제기. 룰 `cli-development` 이 세 규약을 프로즈로 적고\n // 있었는데 같은 실수가 계속 났다 — 프로즈는 사람이 매번 기억해야 하고, 기억해야 하는 규약은\n // 규약이 아니다. 타깃이 CLI 중립 슬롯인 이유는 protect-branch·spec-drift-check 과 같다:\n // 검증 규율은 4개 CLI 와 사람이 함께 쓰는 관심사라 `.claude/` 아래가 아니다.\n m.push({\n source: \"scripts/check-absence.sh\",\n target: \".uzys-agent-harness/check-absence.sh\",\n type: \"file\",\n applies: all,\n });\n\n // Agents (본 프로젝트)\n for (const a of CORE_AGENTS) {\n m.push({\n source: `agents/${a}.md`,\n target: `.claude/agents/${a}.md`,\n type: \"file\",\n applies: all,\n });\n }\n for (const a of DEV_AGENTS) {\n m.push({\n source: `agents/${a}.md`,\n target: `.claude/agents/${a}.md`,\n type: \"file\",\n applies: dev,\n });\n }\n // ADR-090 (#452) — 도메인 트랙을 고른 설치에만.\n for (const [a, pattern] of TRACK_AGENTS) {\n m.push({\n source: `agents/${a}.md`,\n target: `.claude/agents/${a}.md`,\n type: \"file\",\n applies: onTracks(pattern),\n });\n }\n\n // Common skill directories\n // #409 — 스킬은 **전부 디렉터리 단위**로 등록한다. 파일 단위 예외가 하나 있었고 사고를 냈다:\n // 스킬 경로를 다루는 코드가 \"항상 디렉터리\"를 가정해 뒤에 `/SKILL.md` 를 붙여 없는 경로를 만들고,\n // 상주 계측이 **조용히 0으로 셌다**(ADR-083). 파일 단위는 `references/` 를 나중에 붙여도 배포에서\n // 조용히 빠진다. 게이트가 이 통일을 강제한다: tests/skill-registration-uniform.test.ts\n for (const sd of COMMON_SKILL_DIRS) {\n m.push({\n source: `skills/${sd}`,\n target: `.claude/skills/${sd}`,\n type: \"dir\",\n applies: all,\n });\n }\n // v26.87.0 — internal bundled skills (uzys 1st-party templates: dev-method + opt-in advisors,\n // v26.95.0). Whole-dir copy so sidecar files ship too (e.g. gemini-consult/scripts/gemini-ask.sh).\n // Gated on `selectedInternalSkills` (installer computes it via isAssetSelected) — NOT track alone —\n // so a wizard uncheck / `--without <id>` drops the copy, and opt-in ones copy only when selected.\n for (const sd of INTERNAL_BUNDLED_SKILL_IDS) {\n m.push({\n source: `skills/${sd}`,\n target: `.claude/skills/${sd}`,\n type: \"dir\",\n // #602 — selectedInternalSkills 가 SSOT(위저드 해제·--without 반영). `tracks` 만 담긴\n // 스펙(listBaselineTargets·위저드 1단계 등)에서는 카탈로그 조건으로 판정한다 —\n // buildAssetSpec 와 같은 isAssetSelected 라 두 번 적는 사본이 아니라 같은 원천이다.\n applies: (s) =>\n s.selectedInternalSkills !== undefined\n ? s.selectedInternalSkills.includes(sd)\n : // #602 폴백은 트랙 추종 번들 스킬 한정 — 카탈로그 옵트인(compaction-handoff 등)은\n // 선택 목록 없이는 깔리지 않는다(위저드 해제·--without 의 SSOT 를 우회하는 금이 된다).\n sd === \"ui-visual-review\" &&\n isAssetSelected(sd, { tracks: s.tracks, options: { withCodexTrust: false } }),\n });\n }\n for (const sd of DEV_SKILL_DIRS) {\n m.push({\n source: `skills/${sd}`,\n target: `.claude/skills/${sd}`,\n type: \"dir\",\n applies: dev,\n });\n }\n\n // Hooks\n for (const h of ALWAYS_HOOKS) {\n m.push({\n source: `hooks/${h}`,\n target: `.claude/hooks/${h}`,\n type: \"file\",\n applies: all,\n });\n }\n\n // settings.json\n m.push({\n source: \"settings.json\",\n target: \".claude/settings.json\",\n type: \"file\",\n applies: all,\n });\n\n // Project root CLAUDE.md — handled outside manifest by `mergeProjectClaude`\n // (single/multi/full tracks all merged from fragments in installer.ts).\n\n return m;\n}\n","/**\n * Install log — `.uzys-agent-harness/.harness-install.json`.\n *\n * v26.64.0 (ADR-020) — install 종료 시 자산 list + scope + timestamp 기록.\n * uninstall command 가 본 log 를 읽어 정확한 reverse 수행.\n *\n * 글로벌 자산 (scope=global 또는 codexOptIn) 은 log 에 안내용으로만 기록 — uninstall 시 자동 삭제 X (D16).\n */\n\nimport { createHash } from \"node:crypto\";\nimport { existsSync, mkdirSync, readFileSync, rmSync, statSync, writeFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport packageJson from \"../package.json\";\nimport { isKeyId } from \"./adapters/shared-files.js\";\nimport { CLI_BASE_SORT_ORDER } from \"./cli-targets.js\";\nimport {\n type ExternalAsset,\n type ExternalAssetMethod,\n INTERNAL_BUNDLED_SKILL_IDS,\n} from \"./external-assets.js\";\nimport type { ExternalInstallReport } from \"./external-installer.js\";\nimport { listFilesRecursive } from \"./fs-ops.js\";\nimport { healRecordedTracks } from \"./track-heal.js\";\nimport { type CliBase, type InstallScope, type InstallSpec, isCliBase, isTrack } from \"./types.js\";\n\nexport const INSTALL_LOG_FILENAME = \".harness-install.json\";\n/**\n * v26.135.0 (#253 · ADR-050) — 설치 로그 디렉터리. **CLI 중립이라 CLI 디렉터리 밖에 둔다.**\n *\n * v26.134.1 까지는 `.claude/` 에 뒀는데, opencode/codex 단독 설치에서도 로그를 쓰려고\n * `.claude/` 를 만들어 버렸다 — 고르지 않은 CLI 의 디렉터리가 생기고, 그 안엔 로그 하나뿐이다.\n */\nexport const INSTALL_LOG_DIR = \".uzys-agent-harness\";\n/** v26.64.0 ~ v26.134.1 의 위치. 읽기 폴백 + 다음 write 때 1회 이관 대상. */\nexport const LEGACY_INSTALL_LOG_DIR = \".claude\";\nexport const INSTALL_LOG_VERSION = 1;\n\n/**\n * 로그에 실릴 수 있는 method 종류. **카탈로그의 현재 method 보다 넓다** — #492 에서 은퇴한\n * `shell-script`(ecc-prune)를 깐 설치본의 로그가 디스크에 남아 있고, uninstall/list 는 그\n * 로그를 그대로 읽는다. 여기서 빼면 옛 로그가 아래 exhaustive switch 어느 가지에도 안 걸려\n * 안내 문구가 `undefined` 가 된다.\n */\nexport type InstallLogMethod = ExternalAssetMethod[\"kind\"] | \"shell-script\";\n\nexport interface InstallLogAsset {\n id: string;\n category: string;\n /** External asset method.kind 그대로 (+ 은퇴한 legacy kind). uninstall reverse 시 분기 기준. */\n method: InstallLogMethod;\n /** scope=global 자산은 uninstall 시 안내만 (D16 — 글로벌 자동 삭제 금지). */\n scope: InstallScope;\n /** method 별 추가 정보. plugin: marketplace + pluginId. skill: source. npm: pkg. */\n detail: Record<string, string>;\n /** installed 시점 version (detectVersion 결과, 없으면 undefined). */\n version?: string;\n /**\n * #573 — 이 자산의 도구가 **이 프로젝트에 놓은 파일**(project 상대 경로 + 놓은 순간의 sha). 지금은 project scope\n * `skill` 만 적는다. uninstall 은 이 경로만 지운다(설치자가 고친 파일은 그 파일 하나를 백업한 뒤) — 외부 도구의\n * 제거 명령에 맡기면 그 도구가 고른 범위(다른 에이전트 자리의 설치자 사본까지)를 지웠다.\n * **부재 = 이 필드 이전의 기록** — 무엇을 놓았는지 모르므로 uninstall 은 지우지 않고 남는 것을 알린다.\n * 빈 배열 = 놓은 것이 없다고 기록됐다. `INSTALL_LOG_VERSION` 은 올리지 않는다(부재를 정상으로 읽는 관행).\n */\n files?: ReadonlyArray<InstallLogSkillFile>;\n /**\n * #851 (ADR-112) — 소스 전체를 까는 global `skill` 자산(`detail.skill` 없음)이 홈 스킬 자리에 깐 스킬 폴더 이름. install · update 가\n * 도구 호출 전후로 잰다(`installOne` — 바뀐 폴더 ∩ 도구 잠금 파일이 이 소스로 적은 이름). 회수(`reclaimGlobalSkillCopies`)가 이 이름으로 프로젝트 사본 자리를 찾는다. 다시 깔면\n * 합집합이다 — 상류가 이름을 바꿔도 옛 이름은 이 자산이 깐 자리다. **부재 = 이름을 모른다**(이 필드 이전의 기록 — 회수하지 않고,\n * 다음 update 가 잰다). `INSTALL_LOG_VERSION` 은 올리지 않는다(`files` 와 같은 관행).\n */\n skills?: ReadonlyArray<string>;\n}\n\n/**\n * v26.124.0 (F-1f) — install 이 `.claude/` **밖**에 만들거나 고친 프로젝트 루트 파일.\n *\n * uninstall 은 이 목록을 **안내만 하고 지우지 않는다**. `.mcp.json`/`.gitignore` 에는 사용자\n * 내용이 섞이고, `.github/workflows/` 는 설치 후 사용자 소유물이기 때문 (ci-scaffold.ts 안전\n * 계약 2 · F-1d 와 같은 방침). 기록이 없으면 안내도 없다 — 그래서 install 이 적어 둔다.\n */\nexport interface InstallLogRootFile {\n /** project-relative 경로 (예: `.mcp.json`, `.github/workflows/ci.yml`) */\n path: string;\n /**\n * created = 하네스가 없던 파일을 만들었다 (내용 전부 하네스 것 → 손 안 댔으면 지워도 안전).\n * modified = 이미 있던 사용자 파일에 병합/추가했다 (직접 확인이 필요하다).\n *\n * #551 (ADR-097) 이 두 값을 더한다 — 아직 아무 경로도 쓰지 않는다(배선은 설계 §9 PR-3 이후):\n * advisory = 넘겨준 파일(스캐폴드 · 도구 산출물). 갱신도 삭제도 하지 않고 알리기만 한다.\n * displaced = 하네스 자리에 있던 설치자 파일을 비켜 뒀다. `notes[0]` = 백업 경로(없으면 백업 없이\n * 그대로 둔 것) — 하네스가 그 자리를 떠날 때 제자리로 되돌리는 근거다(`judgeDisplaced`).\n */\n change: \"created\" | \"modified\" | \"advisory\" | \"displaced\";\n /** 무엇을 했는지 — uninstall 안내에 그대로 나온다. 재설치 시 합집합으로 누적된다. */\n notes: string[];\n}\n\n/**\n * v26.126.0 (R-3a · ADR-046) — `.claude/skills/` 안 파일 하나의 **설치 시점 기준선**.\n *\n * update 는 이 해시로 \"사용자가 고쳤는가\"를 판정한다. 기록이 없으면 판정이 불가능하고,\n * 그때는 내용 비교로 폴백해 보수적으로 백업한다 (ADR-046 파생규칙 3).\n */\nexport interface InstallLogSkillFile {\n /** `.claude/skills/` 기준 상대 경로 (예: `multi-persona-review/SKILL.md`) */\n path: string;\n /** 하네스가 그 자리에 놓아둔 내용의 sha256. 지금 디스크가 이것과 다르면 = 사용자가 고쳤다. */\n sha256: string;\n}\n\n/**\n * v26.132.0 (ADR-047) — update 가 동기화하는 정책 디렉터리. **이 목록이 SSOT 다.**\n *\n * update 의 은퇴 회수(`pruneOrphans`)와 기록 접근자(`recorded()` 의 디렉터리 단위)가 같은 목록에서\n * 나와야 한다. 두 곳에 따로 적으면 한쪽에만 디렉터리가 추가됐을 때 조용히 갈리고, 그러면\n * **기준선 없는 디렉터리가 생겨 그 안의 파일이 전부 \"판정 불가\"로 떨어진다** — 이 repo 가\n * 반복해서 당한 \"열거 사본\" 실패 모드다 (`no-false-ship.md` \"게이트는 열거하지 말고 훑어라\").\n */\nexport const POLICY_DIRS = [\n { dir: \"rules\", ext: \".md\" },\n { dir: \"agents\", ext: \".md\" },\n { dir: \"commands/uzys\", ext: \".md\" },\n { dir: \"hooks\", ext: \".sh\" },\n] as const;\n\n/**\n * #551 (ADR-097) — 함께 쓰는 파일의 하네스 몫을 읽고 쓰는 방식 넷. 어느 경로가 어느 어댑터인지는\n * `src/adapters/index.ts` 의 `SHARED_FILES` 가 SSOT 다.\n */\nexport type Adapter = \"marker-md\" | \"json-keys\" | \"toml-region\" | \"lines\";\n\n/**\n * #551 (ADR-097) — 함께 쓰는 파일 안의 하네스 몫 **키 하나**. `key` 의 뜻은 어댑터가 정한다\n * (`mcpServers.github` · `hooks.SessionStart#session-start.sh` · 마커 블록 이름 · `.gitignore` 줄 …).\n * `sha256` = 하네스가 그 키에 써 둔 값 — 지금 파일의 값과 다르면 설치자가 고친 것이다.\n */\nexport interface InstallLogPortion {\n /** project-relative 경로 (`.mcp.json` · `.claude/settings.json` …) */\n path: string;\n adapter: Adapter;\n key: string;\n sha256: string;\n}\n\nexport interface InstallLog {\n /** schema version — backward compat 검출용 */\n schemaVersion: number;\n /** harness 가 install 한 시점 ISO timestamp */\n installedAt: string;\n /** 전체 install scope. 자산 per-asset scope 와 동일 (현재는 single global scope) */\n scope: InstallScope;\n /** install 시 spec 요약 (tracks/cli — uninstall reasoning 용) */\n spec: {\n /**\n * #585 — **이 프로젝트에 깔린 트랙 집합**(누적, 정렬). `clis` 와 같은 규칙이다 — install 은 더하기만 하고, 트랙을\n * 빼는 명령은 없다(전량 uninstall 이 기록째 지운다). 마지막 설치분으로 덮던 탓에 claude 없이 다른 트랙으로 CLI 를\n * 더 깔면 claude 로 깐 트랙이 기록에서 사라져, 아무것도 안 바꾼 Update 가 추가 설치로 돌았다.\n */\n tracks: ReadonlyArray<string>;\n /**\n * **마지막 설치가 고른 CLI**. 누적하지 않는다 — 설치 화면·`list` 의 표시용이다.\n * \"지금 이 프로젝트에 무엇이 깔려 있나\"는 아래 `clis` 가 답한다(#528).\n */\n cli: ReadonlyArray<string>;\n /**\n * #528 (Epic #527 정의 1) — **지금 이 프로젝트에 깔려 있는 CLI 집합**(누적).\n * install 은 더하기만 하고, 빼는 것은 `uninstall --cli <name>` 뿐이다.\n *\n * `cli` 와 나눈 이유: 그 필드는 **마지막 설치분**이라 \"claude 로 깔고 나중에 opencode 를\n * 더했다\"를 표현할 수 없다. 그래서 위저드에서 claude 를 풀고 opencode 를 더하면 로그가\n * `[\"opencode\"]` 로 덮이고, 다음 update 가 새 릴리즈의 Claude 자산을 \"안 골랐다\"고 보고\n * 깔지 않았다(실측 2026-09-21). 파일은 그대로 남아 있는데도.\n *\n * **필드가 없는 옛 로그는 정상이다** — `installedClis` 가 그때 유도한다(`INSTALL_LOG_VERSION`\n * 을 올리지 않는 이유: 부재를 정상으로 읽는 이 파일의 기존 관행 그대로다).\n */\n clis?: ReadonlyArray<CliBase>;\n /**\n * 2026-08-16 (ADR-074) — 사용자가 위저드/`--without` 로 **해제한** 트랙 baseline 자산 id\n * (`baseline:<kind>/<name>`). 아무것도 안 뺐으면 필드 자체가 없다(기존 로그도 이 상태).\n *\n * **`update` 가 이걸 읽어야 해제가 유지된다.** 안 남기면 update 는 트랙에서 manifest 를\n * 다시 유도할 뿐이라 사용자가 뺀 룰·에이전트를 되살리고, 화면엔 *\"added by this release\"*\n * 라고 적는다 — 같은 릴리즈에서 방금 설치한 파일인데도. 독립 리뷰가 실측으로 잡았다.\n *\n * `cli` 와 같이 **누적하지 않는다**: install 은 매번 이 목록대로 다시 거르므로\n * 로그는 마지막 설치가 실제로 한 일이어야 한다. 제외 없이 다시 깔면 파일이 돌아오고,\n * 그때 기록이 남아 있으면 로그가 디스크와 다른 말을 한다.\n */\n baselineExclude?: ReadonlyArray<string>;\n /**\n * #505 — 사용자가 위저드 체크 해제 / `--without <id>` 로 뺀 **번들 스킬** id\n * (`INTERNAL_BUNDLED_SKILL_IDS` 의 id 그대로 — `baseline:` 접두 없음). 아무것도 안 뺐으면\n * 필드 자체가 없다.\n *\n * `baselineExclude` 와 **보는 목록이 다르다**: 번들 스킬은 자산 페이지에서 개별 선택되므로\n * baseline 후보가 아니고(`baseline-targets.ts` `listBaselineTargets` 주석), 해제는\n * `userOverride.forceExclude` 에만 남았다. install 은 그걸 보고 안 깔지만 `update` 는 spec 을\n * 트랙에서 다시 유도하므로 **뺀 스킬을 되돌려 깔았다**(#505) — 사용자는 update 때마다 같은\n * 디렉터리를 다시 지워야 했다.\n *\n * `baselineExclude` 와 같이 **누적하지 않는다**: 로그는 마지막 설치가 실제로 한 일이다.\n */\n skillExclude?: ReadonlyArray<string>;\n };\n /** templates 출처 — uninstall 시 templates 제거 위치 */\n templates: {\n /**\n * .claude/ project local — **claude 를 고른 설치에만** 있다 (#528).\n *\n * v26.160.1 까지는 고르지 않아도 `\".claude/\"` 가 적혔다. 디스크에 없는 디렉터리를 기록이\n * 있다고 말하는 셈이라, 옛 로그에서 CLI 집합을 유도할 때 **이 필드는 단서가 아니다**\n * (claude 의 단서는 `rootClaudeMd` 하나 — `installedClis`. `policyFiles`·`skillFiles` 는 옛 판이\n * claude 선택과 무관하게 훑어 적어 단서가 못 된다, BLOCKER-5).\n */\n claudeDir?: string;\n /** .codex/ project local (cli=codex 시) */\n codexDir?: string;\n /** .opencode/ project local (cli=opencode 시) */\n opencodeDir?: string;\n /**\n * project root CLAUDE.md (cli=claude 시 생성).\n * uninstall 시 sha256 이 install 시점과 동일할 때만 삭제 — 사용자가 수정했으면 보존.\n */\n rootClaudeMd?: { path: string; sha256: string };\n };\n /** external-installer 가 install 한 자산 (ok=true 만) */\n assets: ReadonlyArray<InstallLogAsset>;\n /**\n * v26.124.0 (F-1f) — `.claude/` 밖 루트 파일. 건드린 게 없으면 필드 자체가 없다\n * (v26.123.0 이하 로그도 이 상태 — 읽는 쪽은 부재를 정상으로 다뤄야 한다).\n */\n rootFiles?: ReadonlyArray<InstallLogRootFile>;\n /**\n * #644 — `--with-codex-trust` 가 **하네스가 실제로 더한** 전역 trust 항목(`registered` 일 때만 적는다 —\n * 이미 있던 항목은 설치자 것이라 적지 않는다). uninstall 은 이 기록으로 \"손수 지울 것\" 에 올릴 뿐 전역 파일은\n * 건드리지 않는다(D16). 부재 = 정상(옛 로그 · 플래그 없이 깐 설치).\n */\n codexTrust?: { configPath: string; projectDir: string };\n /**\n * v26.126.0 (R-3a · ADR-046) — 스킬 파일 기준선 해시. 스킬을 안 깔았으면 필드 자체가 없다\n * (v26.125.0 이하 로그도 이 상태 — 읽는 쪽은 부재를 정상으로 다뤄야 한다).\n */\n skillFiles?: ReadonlyArray<InstallLogSkillFile>;\n /**\n * v26.132.0 (ADR-047) — 정책 파일(rules/agents/commands/hooks) 기준선 해시.\n * 경로는 `.claude/` 상대 (예: `rules/git-policy.md`).\n *\n * 두 가지를 판정한다: ⓐ 덮어쓰기 전 \"사용자가 고쳤는가\" ⓑ prune 시 \"하네스가 깔았던 것인가\".\n * ⓑ 때문에 **부재를 \"전부 하네스 것\"으로 읽으면 안 된다** — 기록이 없으면 소유를 주장할 수\n * 없으므로 아무것도 지우지 않는다 (v26.131.x 이하 로그가 이 상태).\n */\n policyFiles?: ReadonlyArray<InstallLogSkillFile>;\n /**\n * v26.133.0 (ADR-048) — 외부 CLI 산출물 기준선. 경로는 **projectDir 상대**\n * (`AGENTS.md` · `.codex/hooks/session-start.sh` · `.agents/skills/<id>/SKILL.md` ·\n * `opencode.json` · `.opencode/commands/<id>.md`).\n *\n * `policyFiles` 와 달리 prune 판정에는 쓰지 않는다 — 외부 CLI 쪽은 삭제 경로 자체가 없다.\n * 덮어쓰기 전 \"사용자가 고쳤는가\" 하나만 판정한다. codex/opencode 를 안 깔았으면 필드가\n * 없다 (v26.132.x 이하 로그도 이 상태 — 부재는 정상이고, 그때는 보수적 백업으로 떨어진다).\n */\n externalFiles?: ReadonlyArray<InstallLogSkillFile>;\n /**\n * #551 (ADR-097) — 함께 쓰는 파일의 하네스 몫, **키 단위**. 하네스가 실제로 더한 키만 담는다 —\n * 설치 전에 이미 있던 키는 값이 하네스 판과 같아도 적지 않는다(설치자 것). strip 은 여기 적힌\n * 키만 뺀다. 부재 = 정상(옛 로그 · 아직 아무도 쓰지 않는다 — 배선은 설계 §9 PR-3 이후).\n * `INSTALL_LOG_VERSION` 은 올리지 않는다(ADR-096 D3 관행).\n */\n portions?: ReadonlyArray<InstallLogPortion>;\n /**\n * #551 (ADR-097) — 설치자가 뺀 것 **한 목록**. baseline id(`baseline:rules/x`) · 번들 스킬 id ·\n * 외부 자산 id · 함께 쓰는 파일의 키 id(`mcp:github` · `settings:statusLine` ·\n * `settings:hooks.<Event>#<script>` · `opencode:mcp.<name>` · `gitignore:<line>`)를 가리지 않는다.\n * **누적한다** — install 의 `--without` 은 더하고 `--with` 만 뺀다. 어느 실행도 새로 계산해 덮지\n * 않는다. 옛 `spec.baselineExclude` · `spec.skillExclude` 는 읽기 폴백으로 합쳐 읽는다\n * (`excludedIds`, `src/recorded.ts`).\n */\n excluded?: ReadonlyArray<string>;\n /**\n * ADR-099 R5 — 옛 판(v26.162.0–26.163.0)이 `excluded` 에 자동으로 적은 것을 한 번 정리했다는 표시. 이 판은 기록을\n * 읽는 순간 정리하고(`migrateExcluded`) 쓸 때 이 표시를 남긴다 — 표시가 있는 기록은 다시 정리하지 않는다(그 뒤\n * `--without <키 id>` 로 명시한 빼기를 지우지 않기 위해서다). `log.version` 은 update 가 갱신하지 않아 쓸 수 없다.\n */\n excludedKeysMigrated?: true;\n /**\n * ADR-099 보강(설계 `docs/plans/selection-record-design-2026-10-04.md` §1) — 넣기·빼기 선택의 **이력**. `excluded` 가 최신\n * 선택(SSOT)이고 이것은 그 선택이 언제 어떤 실행으로 바뀌었는지다. 한 항목 = `excluded` 가 실제로 바뀐 실행(효과분만) ·\n * 최근 `SELECTIONS_MAX` 개. 부재 = 정상(옛 기록 · 바뀐 적 없음). `INSTALL_LOG_VERSION` 은 올리지 않는다.\n */\n selections?: ReadonlyArray<SelectionEvent>;\n /**\n * #551 (ADR-097 Q1) — 새 판(쓰는 순간 기록하는 writer)이 이 로그를 처음 쓸 때 적는 표시. 있으면\n * 옛 판이 디스크를 훑어 적은 `policyFiles`·`skillFiles` 에 거는 소유 필터를 더는 적용하지 않는다.\n */\n records?: \"writer\";\n}\n\n/**\n * antigravity 의 전용 표지 — 옛 로그 유도의 단서 (#528). 이 CLI 는 `templates` 항목이 없어\n * 그 절만 봐서는 존재를 알 수 없고, `externalFiles` 에 남는 이 경로가 유일한 기록이다.\n */\nconst ANTIGRAVITY_RULE_FILE = \".agents/rules/uzys-harness.md\";\n\n/**\n * **지금 이 프로젝트에 깔려 있는 CLI 집합** (#528 · Epic #527 정의 1).\n *\n * `spec.clis` 가 있으면 그것이 답이다. 없으면(v26.160.1 이하로 깐 로그) **기록만으로 1회\n * 유도**한다:\n *\n * claude ⇐ `spec.cli` ∋ claude ∨ `templates.rootClaudeMd`\n * codex ⇐ `spec.cli` ∋ codex ∨ `templates.codexDir`\n * opencode ⇐ `spec.cli` ∋ opencode ∨ `templates.opencodeDir`\n * antigravity ⇐ `spec.cli` ∋ antigravity ∨ `externalFiles` ∋ `.agents/rules/uzys-harness.md`\n *\n * **디스크 존재는 신호가 아니다**(ADR-096 Decision 6). 이 함수가 `projectDir` 를 받지 않는\n * 이유가 그것이다 — 한 번은 `.claude/` 의 실존을 claude 의 단서로 썼고, 그러면 하네스가 깐\n * 적 없는 **설치자 소유 `.claude/`**(Claude Code 가 권한 승인 때 만드는 `settings.local.json`\n * 하나면 생긴다)가 \"깔린 CLI\" 로 읽혀 `uninstall --cli claude` 가 그 트리를 통째로 지운다\n * (독립 리뷰 BLOCKER-1, 2026-09-22). 소유는 기록에서만 나온다.\n *\n * 그 자리를 대신하는 claude 의 기록은 **앵커 sha(`templates.rootClaudeMd`) 하나**다 — claude 를\n * 고른 설치에만 적히고 이후 설치에도 누적된다(`buildInstallLog` 의 templates 병합). 쓸 수 없는\n * 기록 둘: `templates.claudeDir` 는 v26.160.1 까지 고르지 않아도 적혔고, `policyFiles`·`skillFiles`\n * 는 v26.160.1 까지 claude 선택과 무관하게 **디스크의 `.claude/` 를 훑어** 적혔다 — 설치자 파일이\n * 템플릿과 같은 상대 경로(`rules/git-policy.md`)면 codex 단독 로그에도 들어 있다(독립 리뷰\n * BLOCKER-5, 컨테이너 실측). 이 판부터 두 기준선은 claude 가 깔렸을 때만 찍지만, 유도가 존재하는\n * 이유인 옛 로그에서는 그 보장이 없으므로 단서로 쓰지 않는다. 놓치는 창 = 앵커 기록이 없는\n * claude 설치본(v26.70.0 이하로 깔고 그 뒤 claude 재설치가 없는 경우) — 틀리는 방향이 안전\n * (`.claude/` 를 건드리지 않는다)이고 재설치가 `clis` 를 굳힌다.\n *\n * **읽기만으로 로그를 고치지 않는다.** 유도 결과는 다음에 로그를 다시 쓸 때(`buildInstallLog`)\n * 기록된다 — `list` 나 `update --dry-run` 같은 읽기 경로가 디스크 기록을 바꾸면, 사용자가\n * 아무것도 안 했는데 기록이 달라진다.\n *\n * @returns `CLI_BASE_SORT_ORDER` 정렬. **로그가 없으면 빈 배열** — \"아무 CLI 도 없다\"가 아니라\n * \"기록이 없어 말할 수 없다\"이고, 그 구분은 호출부가 한다 (update 는 전부로, install 은\n * 이번 선택만으로 다룬다).\n */\nexport function installedClis(log: InstallLog | null): ReadonlyArray<CliBase> {\n if (log === null) return [];\n if (log.spec.clis) return sortClis(log.spec.clis);\n const found = new Set<CliBase>(log.spec.cli.filter(isCliBase));\n if (log.templates.rootClaudeMd !== undefined) found.add(\"claude\");\n if (log.templates.codexDir !== undefined) found.add(\"codex\");\n if (log.templates.opencodeDir !== undefined) found.add(\"opencode\");\n if ((log.externalFiles ?? []).some((f) => f.path === ANTIGRAVITY_RULE_FILE)) {\n found.add(\"antigravity\");\n }\n return sortClis([...found]);\n}\n\n/** 정렬은 `CLI_BASE_SORT_ORDER` 하나만 쓴다 — 로그 diff 가 순서 때문에 흔들리지 않게. */\nfunction sortClis(clis: ReadonlyArray<CliBase>): CliBase[] {\n return [...new Set(clis)]\n .filter(isCliBase)\n .sort((a, b) => CLI_BASE_SORT_ORDER[a] - CLI_BASE_SORT_ORDER[b]);\n}\n\n/**\n * external-installer 의 result 를 InstallLogAsset 으로 변환.\n * ok=false 자산은 제외 (실제 install 안 됨 → uninstall 대상 아님).\n */\nexport function buildAssetEntries(\n report: ExternalInstallReport | null,\n scope: InstallScope,\n previous?: ReadonlyArray<InstallLogAsset>,\n): InstallLogAsset[] {\n if (!report) return [];\n return report.attempted\n .filter((r) => r.ok)\n .map((r) => {\n const recordedBefore = previous?.find((a) => a.id === r.asset.id);\n const entry = {\n ...assetToLogEntry(r.asset, scope, r.version),\n ...skillNames(recordedBefore, scope, r.skills),\n };\n if (r.files === undefined) return entry;\n // 옛 판이 깐 자산(기록은 있는데 파일 목록이 없다)은 다시 깔아도 \"모름\" 그대로 둔다 — 옛 판이 놓은 파일은\n // 호출 전부터 있어 이번 비교에 안 잡히므로, 여기서 목록을 만들면 \"0개\" 가 되어 uninstall 이 남는 파일을\n // 말하지 않고 기록만 지운다(PR #686 리뷰 B1). 모름이면 uninstall 이 지우지 않고 남는 폴더를 알린다.\n if (recordedBefore !== undefined && recordedBefore.files === undefined) return entry;\n return { ...entry, files: mergeToolFiles(recordedBefore?.files, r.files) };\n });\n}\n\n/**\n * #851 (ADR-112) — global 자산의 스킬 이름 기록. 이번에 잰 이름(`run`)을 앞선 global 기록의 이름에 더한다 — 재지 않았으면 앞선\n * 이름을 잇는다. project 로 깔면 이름을 두지 않는다(그 자리는 `files` 가 말한다). 남길 이름이 없으면 필드를 두지 않는다.\n */\nexport function skillNames(\n prior: InstallLogAsset | undefined,\n scope: InstallScope,\n run: ReadonlyArray<string> | undefined,\n): { skills?: string[] } {\n if (scope !== \"global\") return {};\n const kept = prior?.scope === \"global\" && Array.isArray(prior.skills) ? prior.skills : [];\n const names = [...new Set([...kept, ...(run ?? [])])].sort();\n return names.length > 0 ? { skills: names } : {};\n}\n\n/**\n * #573 — 같은 자산을 다시 깔 때의 파일 기록. 이번에 새로 놓인 파일(`created`)을 더하고, 이미 이 자산 것으로 적힌\n * 파일은 도구가 다시 쓴 값(`changed`)으로 sha 를 잇는다. 기록에 없던 파일은 이번에 바뀌었어도 소유하지 않는다 —\n * 호출 전부터 있던 파일은 설치자 것일 수 있다(디스크 존재 ≠ 소유, ADR-096).\n */\nfunction mergeToolFiles(\n prior: ReadonlyArray<InstallLogSkillFile> | undefined,\n run: { created: ReadonlyArray<InstallLogSkillFile>; changed: ReadonlyArray<InstallLogSkillFile> },\n): InstallLogSkillFile[] {\n const changed = new Map(run.changed.map((f) => [f.path, f.sha256]));\n const byPath = new Map<string, string>();\n for (const f of prior ?? []) byPath.set(f.path, changed.get(f.path) ?? f.sha256);\n for (const f of run.created) byPath.set(f.path, f.sha256);\n return [...byPath].map(([path, sha256]) => ({ path, sha256 }));\n}\n\nfunction assetToLogEntry(\n asset: ExternalAsset,\n scope: InstallScope,\n version: string | undefined,\n): InstallLogAsset {\n const detail = methodDetail(asset.method);\n const entry: InstallLogAsset = {\n id: asset.id,\n category: asset.category,\n method: asset.method.kind,\n scope,\n detail,\n };\n if (version) entry.version = version;\n return entry;\n}\n\nfunction methodDetail(method: ExternalAssetMethod): Record<string, string> {\n switch (method.kind) {\n case \"plugin\":\n return { marketplace: method.marketplace, pluginId: method.pluginId };\n case \"skill\":\n return { source: method.source, ...(method.skill ? { skill: method.skill } : {}) };\n case \"npm\":\n return { pkg: method.pkg };\n case \"npx-run\":\n return { cmd: method.cmd, args: (method.args ?? []).join(\" \") };\n case \"internal\":\n // v26.81.0 (ADR-022) — Phase 1 manifest 가 설치 주체. external 단계에선 미기록이 정상.\n return { key: method.key };\n }\n}\n\n/** #505 — 로그에 남길 해제 대상 판별용. 외부 자산 제외는 update 가 재설치하지 않아 제외한다. */\nconst BUNDLED_SKILL_IDS: ReadonlySet<string> = new Set(INTERNAL_BUNDLED_SKILL_IDS);\n\n/**\n * install log 생성. `previous` 가 있으면 **누적**한다 (v26.123.0 — F-1a).\n *\n * install 은 이전에 설치한 것을 지우지 않는다. 그런데 로그는 매번 새로 만들어 덮어썼으므로,\n * 나중에 `install --with <id>` 를 한 번만 해도 1회차 자산이 기록에서 사라지고 **uninstall 이\n * 그걸 못 찾아 남긴다**. 디스크에는 남아 있는데 기록에는 없는 = 로그가 거짓이 되는 상태.\n *\n * `spec.cli` 는 누적하지 않는다(마지막 설치분 — 표시용). `spec.tracks` · `spec.clis` 는 누적한다(#528 · #585) — install\n * 은 이전 트랙 파일을 지우지 않고 `--reinstall` 도 `.claude/` 를 옮기지 않으므로(#551 PR-3) 합집합이 디스크와 같다.\n *\n * `claudeDirMovedAside` = 이번 설치가 `.claude/` 를 backup 으로 rename 했는가. 그 경우\n * **`.claude/` 안에 살던 이전 자산은 실제로 사라졌으므로 누적에서 뺀다** — 안 빼면 F-1a 를\n * 반대 방향으로 재현한다(있지도 않은 걸 있다고 기록). 해당: project scope 의 `skill`\n * (`npx skills add` 가 `.claude/skills/` 에 설치) 와 legacy `shell-script`(ecc-prune →\n * `.claude/local-plugins/`). plugin/npm 은 프로젝트 밖에 살아 남으므로 유지한다.\n */\nexport function buildInstallLog(\n spec: InstallSpec,\n external: ExternalInstallReport | null,\n scope: InstallScope,\n rootClaudeMd?: { path: string; sha256: string } | null,\n previous?: InstallLog | null,\n claudeDirMovedAside = false,\n rootFiles: ReadonlyArray<InstallLogRootFile> = [],\n codexTrust?: InstallLog[\"codexTrust\"],\n): InstallLog {\n // #505 — 번들 스킬 해제는 **자산 id** 로 들어온다(`userOverride.forceExclude`). 위저드 체크\n // 해제와 `--without <id>` 가 같은 자리로 모이므로 두 진입점이 한 줄로 덮인다.\n const skillExclude = (spec.userOverride?.forceExclude ?? []).filter((id) =>\n BUNDLED_SKILL_IDS.has(id),\n );\n // #528 — `.claude/` 는 claude 를 골랐을 때만 적는다. 고르지 않아도 적던 탓에 기록이\n // 디스크에 없는 디렉터리를 있다고 말했고, uninstall 은 그 경로를 rm 하려 들었다(무해했지만\n // \"templates removed: .claude/\" 라고 화면에 찍혔다 — 없는 것을 지웠다는 보고다).\n const templates: InstallLog[\"templates\"] = {\n ...(spec.cli.includes(\"claude\") ? { claudeDir: \".claude/\" } : {}),\n ...(spec.cli.includes(\"codex\") ? { codexDir: \".codex/\" } : {}),\n ...(spec.cli.includes(\"opencode\") ? { opencodeDir: \".opencode/\" } : {}),\n ...(rootClaudeMd ? { rootClaudeMd } : {}),\n };\n // #528 — CLI 집합은 **더해지기만 한다**. reinstall(`.claude/` backup rename) 에서도 누적인\n // 이유: 밀려나는 것은 `.claude/` 뿐이고 다른 CLI 의 산출물(`AGENTS.md`·`.opencode/`)은\n // 그대로 디스크에 남기 때문이다. 빼는 경로는 `uninstall --cli <name>` 하나다.\n const clis = sortClis([...installedClis(previous ?? null), ...spec.cli.filter(isCliBase)]);\n // #585 — 트랙도 CLI 와 같이 더해지기만 한다. 이전 기록 중 지금 어휘에 없는 트랙(폐기)은 이어받지 않는다 — 읽는 쪽이\n // 어차피 거르고, 남기면 영영 기록에 붙어 다닌다.\n const tracks = [\n ...new Set([...(previous?.spec.tracks ?? []).filter(isTrack), ...spec.tracks]),\n ].sort();\n const log: InstallLog = {\n schemaVersion: INSTALL_LOG_VERSION,\n installedAt: new Date().toISOString(),\n scope,\n spec: {\n tracks,\n cli: spec.cli,\n clis,\n ...(spec.baselineExclude && spec.baselineExclude.length > 0\n ? { baselineExclude: spec.baselineExclude }\n : {}),\n ...(skillExclude.length > 0 ? { skillExclude } : {}),\n },\n // 이번 설치가 만든 항목이 이기고, 이번에 안 만든 항목은 이전 값을 그대로 둔다.\n // (예: claude 로 깔고 나중에 codex 만 추가 설치해도 root CLAUDE.md 기록이 살아남는다)\n templates: { ...previous?.templates, ...templates },\n assets: mergeAssets(\n claudeDirMovedAside ? previous?.assets?.filter(survivesClaudeDirRename) : previous?.assets,\n buildAssetEntries(external, scope, previous?.assets),\n ),\n };\n // 루트 파일은 `.claude/` 밖이라 backup rename 과 무관하게 살아남는다 → 무조건 누적.\n const mergedRootFiles = mergeRootFiles(previous?.rootFiles, rootFiles);\n if (mergedRootFiles.length > 0) log.rootFiles = mergedRootFiles;\n // #644 — 전역 파일의 항목은 설치 폴더가 아니라 홈에 있어 backup rename 과 무관하다 → 이전 기록을 이어받는다.\n const trust = codexTrust ?? previous?.codexTrust;\n if (trust) log.codexTrust = trust;\n return log;\n}\n\n/**\n * 경로 기준 합집합. 자산과 달리 **이번 설치분이 이전 것을 덮지 않고 합친다** — `.gitignore` 에\n * 1회차는 `.env`, 2회차는 `.factory/` 를 추가하면 둘 다 디스크에 남아 있으므로 둘 다 알려야 한다.\n * `change` 는 한 번이라도 created 면 created — 하네스가 만든 파일에 나중에 병합한 것뿐이고,\n * 사용자에게는 \"전부 하네스 것\"이 여전히 참이다 (modified 로 낮추면 지워도 될 것을 못 지운다).\n *\n * #551 — `displaced` 는 합치지 않고 **마지막 하나**만 둔다. `notes[0]` 이 되돌릴 백업 경로라, 합집합이면\n * 옛 백업을 가리키게 된다(설계 §1.2 \"displaced 되돌리기 세부\").\n */\nexport function mergeRootFiles(\n previous: ReadonlyArray<InstallLogRootFile> | undefined,\n current: ReadonlyArray<InstallLogRootFile>,\n): InstallLogRootFile[] {\n const byPath = new Map<string, InstallLogRootFile>();\n for (const file of [...(previous ?? []), ...current]) {\n const prior = byPath.get(file.path);\n byPath.set(\n file.path,\n prior && file.change !== \"displaced\"\n ? {\n path: file.path,\n change: prior.change === \"created\" ? \"created\" : file.change,\n notes: [...new Set([...prior.notes, ...file.notes])],\n }\n : file,\n );\n }\n return [...byPath.values()];\n}\n\n/**\n * `.claude/` 가 backup 으로 밀려도 살아남는 자산인가 — 산출물이 프로젝트 `.claude/` 밖인가.\n *\n * **exhaustive switch 로 쓴다(default 없음).** method 종류가 늘면 빌드가 깨져서 이 판단을\n * 강제로 하게 만든다 — `!==` 목록이면 새 method 가 조용히 \"살아남음\"으로 분류되고, 그건\n * 곧 없는 걸 있다고 기록하는 것이다 (`no-false-ship` §Drift 구조 차단: 하드코딩 목록에는\n * exhaustiveness 가드 없이 머지 금지, 기본값은 면제가 아니라 검사).\n */\nfunction survivesClaudeDirRename(asset: InstallLogAsset): boolean {\n if (asset.scope === \"global\") return true; // 글로벌 영역은 install 이 건드리지 않는다\n switch (asset.method) {\n case \"plugin\":\n return true; // `~/.claude/plugins/cache` — 프로젝트 밖\n case \"npm\":\n return true; // `node_modules/`\n case \"skill\":\n return false; // `npx skills add` project scope → `.claude/skills/`\n case \"shell-script\":\n return false; // legacy(#492 은퇴): ecc-prune → `.claude/local-plugins/`\n case \"npx-run\":\n // bmad-method 는 `--tools claude-code` 로 `.claude/` 안에 agent command 를 만든다\n // (external-assets.ts 의 cliSupportOverride 주석 + Docker 실증 realcli-workflows-2026-06-06).\n // `_bmad/` 는 루트에 남지만, `.claude/` 산출물이 사라진 이상 \"그대로 설치됨\"이 아니다.\n return false;\n case \"internal\":\n // 실제로는 로그에 실리지 않는다(external-installer 가 사전 제외). 그래도 기본값은 검사.\n return false;\n }\n}\n\n/** id 기준 합집합 — 같은 id 는 이번 설치분이 이긴다 (version/scope 가 최신). 순서는 안정적. */\nfunction mergeAssets(\n previous: ReadonlyArray<InstallLogAsset> | undefined,\n current: ReadonlyArray<InstallLogAsset>,\n): InstallLogAsset[] {\n if (!previous || previous.length === 0) return [...current];\n const currentById = new Map(current.map((a) => [a.id, a]));\n const previousIds = new Set(previous.map((a) => a.id));\n return [\n ...previous.map((a) => currentById.get(a.id) ?? a),\n ...current.filter((a) => !previousIds.has(a.id)),\n ];\n}\n\n/** install log + root CLAUDE.md 등 자산 무결성 비교용 sha256 (hex). */\nexport function hashContent(content: string): string {\n return createHash(\"sha256\").update(content, \"utf8\").digest(\"hex\");\n}\n\n/**\n * 외부 CLI 기준선 누적 (v26.133.0 · ADR-048).\n *\n * 이번 실행이 안 건드린 산출물(예: 지난번엔 opencode 도 깔았는데 이번엔 codex 만)은 디스크에\n * 그대로 있으므로 **이전 기록을 유지한다** — 지우면 다음 실행이 판정 불가로 떨어져 멀쩡한\n * 파일을 백업한다.\n *\n * **디스크에서 사라진 항목도 빼지 않는다**(ADR-099 R2) — 기록이 되살림의 근거다. 손으로 지운 하네스 파일은 빼기가 아니라\n * 상태이고, 그 기록을 지우면 update 가 그 파일을 영영 되돌리지 못한다(#598 — AGENTS.md 가 기록에서도 사라졌다). 기록을 걷는\n * 길은 따로 있다: 은퇴는 `harnessRemove` 의 `forget`, `uninstall --cli` 는 그 CLI 의 기록을 직접 걷는다.\n */\nexport function mergeExternalFiles(\n previous: ReadonlyArray<InstallLogSkillFile> | undefined,\n current: ReadonlyArray<InstallLogSkillFile>,\n): InstallLogSkillFile[] {\n const byPath = new Map<string, InstallLogSkillFile>();\n for (const file of [...(previous ?? []), ...current]) byPath.set(file.path, file);\n return [...byPath.values()];\n}\n\n/**\n * install log write. 위치: `<projectDir>/.uzys-agent-harness/.harness-install.json`.\n *\n * 디렉터리는 write 직전에 보장한다 — 하네스 전용이라 다른 어떤 phase 도 만들지 않는다.\n *\n * v26.135.0 (#253) — 구 위치(`.claude/`)에 파일이 있으면 **여기서 이관이 끝난다**: 새 위치에\n * 쓴 뒤 구 파일을 지운다. 이관을 별도 명령이나 install 경로에만 두면, 로그를 쓰는 다른 경로\n * (`update` 의 refreshExternalCli, `uninstall --only` 의 로그 갱신)가 구 파일을 남긴 채\n * 새 파일을 만들어 **같은 프로젝트에 로그가 2벌** 남는다.\n */\nexport function writeInstallLog(projectDir: string, log: InstallLog): string {\n const path = installLogPath(projectDir);\n mkdirSync(dirname(path), { recursive: true });\n // ADR-099 R5 — 이 판이 쓰는 기록은 모두 읽을 때 정리된 것(`migrateExcluded`)이거나 새 기록이다 — 표시를 남긴다\n const marked: InstallLog = { ...normalizeLogOrder(log), excludedKeysMigrated: true };\n writeFileSync(path, `${JSON.stringify(marked, null, 2)}\\n`, \"utf8\");\n migrateAwayLegacyLog(projectDir);\n return path;\n}\n\n/**\n * ADR-100 — 기록은 저장소에 커밋된다. 경로 배열을 path(+key) 로 정렬해 같은 내용이면 같은 바이트가 되게 한다 — install 은\n * 쓴 순서로, update 는 디렉터리를 읽은 순서로 적어, 같은 판 update 가 순서만 바꾼 diff 를 팀원마다 커밋 후보로 남겼다.\n * 정렬은 안정이라 같은 path(+key) 항목끼리의 순서(뒤 것이 이기는 읽기)는 그대로다. `sortClis` 와 같은 이유다.\n */\nfunction normalizeLogOrder(log: InstallLog): InstallLog {\n const cmp = (a: string, b: string): number => (a < b ? -1 : a > b ? 1 : 0);\n const byPath = <T extends { path: string }>(xs: ReadonlyArray<T>): T[] =>\n [...xs].sort((a, b) => cmp(a.path, b.path));\n const out: InstallLog = { ...log };\n if (log.policyFiles !== undefined) out.policyFiles = byPath(log.policyFiles);\n if (log.skillFiles !== undefined) out.skillFiles = byPath(log.skillFiles);\n if (log.externalFiles !== undefined) out.externalFiles = byPath(log.externalFiles);\n if (log.rootFiles !== undefined) out.rootFiles = byPath(log.rootFiles);\n if (log.portions !== undefined) {\n out.portions = [...log.portions].sort((a, b) => cmp(a.path, b.path) || cmp(a.key, b.key));\n }\n return out;\n}\n\n/**\n * 구 위치 파일 제거. 실패해도 **install 을 죽이지 않는다** — 읽기는 새 위치가 우선이라\n * 남은 구 파일은 더 이상 아무것도 결정하지 않는다(무해한 잔재). 반대로 여기서 throw 하면\n * 정상 완료된 설치가 \"실패\"로 보고된다. 조용한 성공이 아니라 **의도된 비치명 처리**다.\n */\nfunction migrateAwayLegacyLog(projectDir: string): void {\n const legacy = legacyInstallLogPath(projectDir);\n if (!existsSync(legacy)) return;\n try {\n rmSync(legacy);\n } catch {\n /* 위 주석 참조 — 새 위치가 이미 SSOT */\n }\n}\n\n/**\n * 새 위치 → 구 위치 순으로 찾는다. 구 위치 폴백이 없으면 v26.134.1 이하로 설치한 사용자의\n * `list` / `uninstall` / `update` 가 전부 \"install log not found\" 로 죽는다.\n *\n * 상태 검출(state.ts)·렌더링 등 null 을 \"설치 없음\"으로 접는 호출자를 위해 종전 시그니처는\n * 그대로 둔다(#640). 사용자 면 명령(list·uninstall)은 `readInstallLogStatus` 로 \"없음\"과\n * \"깨짐\"을 구분해 말한다 — 파일이 있는데 \"not found\" 라고 진단하면 사용자는 원인(병합 충돌·\n * 부분 기록·수동 편집)을 영원히 못 찾는다.\n */\nexport function readInstallLog(projectDir: string): InstallLog | null {\n return readInstallLogStatus(projectDir).log;\n}\n\nexport type InstallLogStatus =\n | { status: \"missing\"; log: null }\n | { status: \"corrupted\"; log: null }\n | { status: \"ok\"; log: InstallLog };\n\n/** #640 — 기록을 \"없음 / 깨짐 / 정상\"으로 구분해 낸다. */\nexport function readInstallLogStatus(projectDir: string): InstallLogStatus {\n const path = [installLogPath(projectDir), legacyInstallLogPath(projectDir)].find((p) =>\n existsSync(p),\n );\n if (!path) return { status: \"missing\", log: null };\n let parsed: unknown;\n try {\n parsed = JSON.parse(readFileSync(path, \"utf8\"));\n } catch {\n return { status: \"corrupted\", log: null };\n }\n if (!isValidInstallLogShape(parsed)) return { status: \"corrupted\", log: null };\n const log = parsed as InstallLog;\n // v26.68.0 — backward compat: method.kind \"npm-global\" → \"npm\" rename.\n // v26.64.0 ~ v26.67.0 시점 install log 가 새 uninstall 에서 작동하도록 normalize.\n if (Array.isArray(log.assets)) {\n log.assets = log.assets.map((a) =>\n (a.method as string) === \"npm-global\" ? { ...a, method: \"npm\" } : a,\n );\n }\n const migrated = migrateExcluded(log, projectDir);\n // #585 후속(리뷰 B1·B2) — 옛 판이 덮어쓴 트랙을 sha 로 보증된 메타파일로 되살린다(읽는 쪽 전부가 같은 트랙을 본다)\n const healed = healRecordedTracks(migrated.log, projectDir, installedClis(migrated.log));\n // #853 — 호출자가 받는 객체에 건다. heal 은 새 객체를 낼 수 있다\n if (migrated.droppedKeys.length > 0) {\n LEGACY_RELEASED.set(healed, {\n released: migrated.droppedKeys,\n catalog: migrated.releasedCatalog,\n });\n }\n return { status: \"ok\", log: healed };\n}\n\n/** 읽은 기록 → 그 기록을 읽을 때 옛 판 정리(R5 · 설계 §2.2)가 푼 것. 기록 파일에는 남지 않는다(화면용 — 이력은 `selections`). */\nconst LEGACY_RELEASED = new WeakMap<InstallLog, { released: string[]; catalog: string[] }>();\n\n/**\n * ADR-099 R5 — 이 기록을 읽을 때 옛 판의 자동 추론분 · 마지막 설치가 다시 깐 것이라 `excluded` 에서 푼 id 전부. 이번 실행이\n * 그것을 실제로 되살렸는지 화면이 가르는 데만 쓴다(`↺ restored N harness part(s) an earlier version had marked as removed`).\n */\nexport function legacyDroppedKeys(log: InstallLog | null): ReadonlyArray<string> {\n return log === null ? [] : (LEGACY_RELEASED.get(log)?.released ?? []);\n}\n\n/** 설계 §2.2 규칙 2 로 푼 카탈로그 id — 화면 `↺ <id> — released: the last install re-added it (26.162–26.163 record)`. */\nexport function legacyReleasedCatalog(log: InstallLog | null): ReadonlyArray<string> {\n return log === null ? [] : (LEGACY_RELEASED.get(log)?.catalog ?? []);\n}\n\n/** 넣기·빼기 선택 이력 한 항목(설계 §1). `with` = 이번에 `excluded` 에서 빠진 id · `without` = 새로 들어온 id. */\nexport interface SelectionEvent {\n at: string;\n /** 이 항목을 쓴 하네스 판. */\n harness: string;\n by: \"install\" | \"migration\";\n via?: \"flag\" | \"wizard\";\n with?: ReadonlyArray<string>;\n without?: ReadonlyArray<string>;\n /** migration — 근거가 있어 푼 것. */\n released?: ReadonlyArray<string>;\n /** migration — 근거가 없어 뺀 채 둔 카탈로그 id. */\n kept?: ReadonlyArray<string>;\n /** #600 — 도중에 멈춘 install 의 기록. */\n interrupted?: true;\n}\n\n/** 이력은 최근 이만큼만 둔다(≤ 15 KB) — 오래된 것부터 떨어진다. */\nexport const SELECTIONS_MAX = 100;\n\n/** 지금 이 하네스 판 — 이력 항목의 `harness`. */\nexport const HARNESS_VERSION: string = packageJson.version;\n\n/** 이력에 한 항목을 더한다 — 효과분(with · without · released · kept)이 모두 비면 기록을 그대로 둔다. */\nexport function appendSelection(log: InstallLog, ev: SelectionEvent): InstallLog {\n const empty = (a: ReadonlyArray<string> | undefined): boolean =>\n a === undefined || a.length === 0;\n if (empty(ev.with) && empty(ev.without) && empty(ev.released) && empty(ev.kept)) return log;\n const clean: SelectionEvent = {\n at: ev.at,\n harness: ev.harness,\n by: ev.by,\n ...(ev.via !== undefined ? { via: ev.via } : {}),\n ...(empty(ev.with) ? {} : { with: [...(ev.with ?? [])] }),\n ...(empty(ev.without) ? {} : { without: [...(ev.without ?? [])] }),\n ...(empty(ev.released) ? {} : { released: [...(ev.released ?? [])] }),\n ...(empty(ev.kept) ? {} : { kept: [...(ev.kept ?? [])] }),\n ...(ev.interrupted ? { interrupted: true as const } : {}),\n };\n return { ...log, selections: [...(log.selections ?? []), clean].slice(-SELECTIONS_MAX) };\n}\n\n/**\n * 설계 §2.2 창 W — 옛 기록에서 \"마지막 install 이 이 카탈로그 스킬을 썼다\" 로 읽는 폭. X 를 쓴 뒤 `installedAt` 까지는 남은\n * 외부 자산(스킬 1개 ≈5 s × 선택분 · npm 자산 상한 600 s)과 CLI 변환(초 단위)이다 — 10 분은 npm 상한 하나를 품는 크기다.\n * 키우면 `install` → `install --without X` 를 연달아 한 설치자를 \"다시 깔았다\" 로 읽는 폭이 넓어지고(update 가 X 를 다시\n * 갱신 · `--without X` 한 번으로 복귀), 줄이면 느린 네트워크의 \"다시 깐\" 설치자가 `⊘` 줄을 보고 install 을 한 번 더 한다.\n */\nexport const LEGACY_REINSTALL_WINDOW_MS = 600_000;\n\n/** 기록된 프로젝트 스킬 자산이 놓은 파일들(도구의 파일 기록 #573, 없으면 스킬 자리 두 곳의 파일 전부) — 없으면 빈 배열. */\nfunction assetFiles(projectDir: string, asset: InstallLogAsset): string[] {\n if (asset.files !== undefined) {\n return asset.files.map((f) => join(projectDir, f.path)).filter((p) => existsSync(p));\n }\n const dir = asset.detail.skill ?? asset.id;\n return [\".claude/skills\", \".agents/skills\"].flatMap((d) =>\n listFilesRecursive(join(projectDir, d, dir)).map((rel) => join(projectDir, d, dir, rel)),\n );\n}\n\n/**\n * 설계 §2.2 — 옛 기록의 카탈로그 X(`assets ∩ excluded`)는 \"깔았다가 마지막 install 에서 뺐다\"(A2)와 \"뺐다가 플래그 없이 다시\n * 깔았다\"(B)가 기록으로 같다. 묻지 않고 디스크 근거로 가른다: 폴더 없음 → 뺀 채(B 는 폴더가 있다) · 폴더 안 파일의 최대 mtime 이\n * `installedAt` 직전 창 안 → 마지막 install 이 X 를 썼다 = 그 실행에 `--without X` 가 없었다 → 푼다 · 창보다 이르다 → 마지막\n * install 은 X 를 안 썼다 → 뺀 채 · `installedAt` 뒤 → 그 뒤 다시 써졌다(163 의 update 등) → 근거 없음 → 뺀 채(화면이 할 일을 말한다).\n */\nfunction legacyCatalogReleased(\n projectDir: string,\n log: InstallLog,\n asset: InstallLogAsset,\n): boolean {\n if (asset.method !== \"skill\" || asset.scope === \"global\") return false; // 디스크로 알 수 없다 — 근거 없음\n const files = assetFiles(projectDir, asset);\n if (files.length === 0) return false;\n const t = Date.parse(log.installedAt);\n if (Number.isNaN(t)) return false;\n const m = Math.max(...files.map((f) => statSync(f).mtimeMs));\n return m <= t && m >= t - LEGACY_REINSTALL_WINDOW_MS;\n}\n\n/**\n * ADR-099 R5 · 설계 §2.2 — 옛 판(v26.162.0–26.163.0)이 굳힌 `excluded` 를 한 번 푼다. 표시(`excludedKeysMigrated`)가 있으면 그대로.\n *\n * - **키 id**(`SHARED_FILES` 접두 전부)는 푼다 — `--without` 이 키 id 를 받은 판은 없으므로(설계 G1) 표시 없는 기록의\n * 키 id 는 전부 \"기록에 있는데 파일에 없다\" 의 자동 추론이다.\n * - **baseline id** 는 `spec.baselineExclude`(마지막 설치의 플래그)에 있는 것만, **번들 스킬 id** 는 `spec.skillExclude` 에\n * 있는 것만 남긴다 — 마지막 install 의 입력이 선택이다(설계 §3).\n * - **카탈로그 id** 가 `log.assets` 에도 있으면 `legacyCatalogReleased`(폴더 · mtime 창)로 가른다. `projectDir` 이 없으면\n * 디스크를 볼 수 없어 뺀 채 둔다.\n * 판정은 이력에 `{by:\"migration\", released, kept}` 한 항목으로 남는다(기록을 다음에 쓸 때).\n */\nexport function migrateExcluded(\n log: InstallLog,\n projectDir?: string,\n): { log: InstallLog; droppedKeys: string[]; releasedCatalog: string[] } {\n if (log.excludedKeysMigrated === true) return { log, droppedKeys: [], releasedCatalog: [] };\n const baseline = new Set(log.spec.baselineExclude ?? []);\n const skills = new Set(log.spec.skillExclude ?? []);\n const released: string[] = [];\n const releasedCatalog: string[] = [];\n const keptCatalog: string[] = [];\n const kept: string[] = [];\n for (const id of log.excluded ?? []) {\n if (isKeyId(id)) {\n released.push(id);\n continue;\n }\n if (id.startsWith(\"baseline:\") && !baseline.has(id)) {\n released.push(id);\n continue;\n }\n if (BUNDLED_SKILL_IDS.has(id) && !skills.has(id)) {\n released.push(id);\n continue;\n }\n const asset = log.assets.find((a) => a.id === id);\n if (asset !== undefined && !BUNDLED_SKILL_IDS.has(id) && !id.startsWith(\"baseline:\")) {\n if (projectDir !== undefined && legacyCatalogReleased(projectDir, log, asset)) {\n released.push(id);\n releasedCatalog.push(id);\n continue;\n }\n keptCatalog.push(id);\n }\n kept.push(id);\n }\n let next: InstallLog = { ...log, excludedKeysMigrated: true };\n if (kept.length > 0) next.excluded = kept;\n else delete next.excluded;\n next = appendSelection(next, {\n at: new Date().toISOString(),\n harness: HARNESS_VERSION,\n by: \"migration\",\n released,\n kept: keptCatalog,\n });\n return { log: next, droppedKeys: released, releasedCatalog };\n}\n\n/**\n * #640 — 파싱은 되지만 필수 필드가 빠진 기록(shared 충돌·부분 기록·수동 편집)을 가려낸다.\n * 아래 세 축은 list·uninstall·removeTemplates 가 무방비로 접근하던 곳 = 실제 크래시 지점.\n */\nfunction isValidInstallLogShape(value: unknown): value is InstallLog {\n if (typeof value !== \"object\" || value === null) return false;\n const v = value as Record<string, unknown>;\n const spec = v.spec as Record<string, unknown> | undefined;\n if (typeof spec !== \"object\" || spec === null) return false;\n if (!Array.isArray(spec.tracks)) return false;\n // #852 — 구기록은 cli · 현기록은 clis. 둘 다 없으면 installedClis 가 읽을 곳이 없다\n if (!Array.isArray(spec.cli) && !Array.isArray(spec.clis)) return false;\n if (!Array.isArray(v.assets)) return false;\n if (typeof v.templates !== \"object\" || v.templates === null) return false;\n return true;\n}\n\n/**\n * #640 — \"깨짐\" 상태에서 사용자에게 보여줄 한 줄. 되살리는 길까지 말한다. ADR-100 — 기록이 커밋되면 깨짐의 흔한 원인은 병합\n * 충돌이고, 그때는 한쪽을 통째로 고르는 것이 기록을 잃지 않는 길이다. `--reinstall` 은 기록 없이 다시 만들어 빼기 · 외부\n * 자산을 잊으므로 그 대가를 같은 줄에서 말한다.\n */\nexport function corruptedInstallLogMessage(projectDir: string): string {\n return `install log is corrupted at ${installLogPath(projectDir)} — if it holds git conflict markers, take one side whole; otherwise run install --reinstall (it forgets recorded exclusions and external assets)`;\n}\n\nexport function installLogPath(projectDir: string): string {\n return join(projectDir, INSTALL_LOG_DIR, INSTALL_LOG_FILENAME);\n}\n\n/** v26.134.1 이하가 쓰던 위치. 읽기 폴백과 이관에만 쓴다 — 새로 쓰는 곳은 없다. */\nexport function legacyInstallLogPath(projectDir: string): string {\n return join(projectDir, LEGACY_INSTALL_LOG_DIR, INSTALL_LOG_FILENAME);\n}\n","{\n \"name\": \"@uzysjung/agent-harness\",\n \"version\": \"26.176.0\",\n \"description\": \"Curate vetted AI-coding skills & plugins by your tech stack — install only what you need, across Claude Code, Codex, OpenCode & Antigravity\",\n \"type\": \"module\",\n \"publishConfig\": {\n \"access\": \"public\"\n },\n \"engines\": {\n \"node\": \">=20.12.0\"\n },\n \"bin\": {\n \"agent-harness\": \"./dist/index.js\"\n },\n \"files\": [\n \"dist\",\n \"templates\",\n \"README.md\",\n \"LICENSE\"\n ],\n \"scripts\": {\n \"build\": \"tsup\",\n \"dev\": \"tsup --watch\",\n \"gen:compat\": \"npm run build && node scripts/gen-compatibility.mjs\",\n \"gen:legacy-fingerprints\": \"node scripts/gen-legacy-fingerprints.mjs\",\n \"test\": \"vitest run\",\n \"test:watch\": \"vitest\",\n \"test:coverage\": \"vitest run --coverage\",\n \"typecheck\": \"tsc --noEmit\",\n \"lint\": \"biome check src tests\",\n \"lint:fix\": \"biome check --write src tests\",\n \"format\": \"biome format --write src tests\",\n \"ci\": \"npm run typecheck && npm run lint && npm run test:coverage && npm run build\",\n \"prepare\": \"[ -d dist ] || npm run build\",\n \"cost:report\": \"npm run build && node scripts/context-cost-report.mjs\",\n \"cost:baseline\": \"npm run build && node scripts/context-cost-baseline.mjs\",\n \"assets:history\": \"bash scripts/asset-history.sh\",\n \"release:audit\": \"node scripts/release-audit.mjs\",\n \"security\": \"npm audit --omit=dev --audit-level=high && npx --yes ecc-agentshield@1.4.0 scan --baseline security-baseline.json --gate\",\n \"security:baseline\": \"npx --yes ecc-agentshield@1.4.0 scan --save-baseline security-baseline.json\"\n },\n \"dependencies\": {\n \"@clack/prompts\": \"^1.3.0\",\n \"cac\": \"^7.0.0\",\n \"smol-toml\": \"^1.9.0\"\n },\n \"devDependencies\": {\n \"@biomejs/biome\": \"^2.4.13\",\n \"@types/node\": \"^25.6.0\",\n \"@vitest/coverage-v8\": \"^4.1.11\",\n \"tsup\": \"^9.0.0\",\n \"typescript\": \"^5.6.0\",\n \"vitest\": \"^4.1.11\"\n },\n \"license\": \"MIT\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/uzysjung/uzys-agent-harness.git\"\n }\n}\n","/**\n * 함께 쓰는 파일 표 (#551 · ADR-097 §6.2) — `index.ts` 가 다시 내보낸다. 어댑터 구현을 끌어오지 않는 따로 된 모듈인\n * 이유: 설치 기록(`install-log.ts`)이 키 id 접두를 이 표에서 유도해야 하는데(ADR-099 R5), 어댑터들은 기록 모듈의\n * `hashContent` 를 모듈 평가 때 부른다 — 표만 여기 두면 순환 import 가 생기지 않는다.\n */\n\nimport type { Adapter } from \"../install-log.js\";\n\nexport interface SharedFile {\n adapter: Adapter;\n /** 키 id 접두(`mcp:`) */\n prefix: string;\n /** 어댑터 키 안에서 id 로 옮길 때 떼는 머리(`.mcp.json` 의 `mcpServers.`) */\n strip?: string;\n}\n\nexport const SHARED_FILES: Readonly<Record<string, SharedFile>> = {\n \"CLAUDE.md\": { adapter: \"marker-md\", prefix: \"claude-md:\" },\n \"AGENTS.md\": { adapter: \"marker-md\", prefix: \"agents-md:\" },\n \".claude/settings.json\": { adapter: \"json-keys\", prefix: \"settings:\" },\n \".mcp.json\": { adapter: \"json-keys\", prefix: \"mcp:\", strip: \"mcpServers.\" },\n \"opencode.json\": { adapter: \"json-keys\", prefix: \"opencode:\" },\n \".codex/config.toml\": { adapter: \"toml-region\", prefix: \"codex:\" },\n \".gitignore\": { adapter: \"lines\", prefix: \"gitignore:\" },\n};\n\n/** 키 id 의 접두 전부(`mcp:` · `settings:` …) — 키 id 를 알아보는 곳은 이것으로만 판정한다(ADR-099 키 id 집합). */\nexport const KEY_ID_PREFIXES: ReadonlyArray<string> = Object.values(SHARED_FILES).map(\n (f) => f.prefix,\n);\n\n/** 함께 쓰는 파일의 키 id 인가(`mcp:github` · `settings:hooks.SessionStart#x.sh`). */\nexport function isKeyId(id: string): boolean {\n return KEY_ID_PREFIXES.some((p) => id.startsWith(p));\n}\n\n/** 키 id 가 가리키는 함께 쓰는 파일(project 상대) — 키 id 가 아니면 null. */\nexport function sharedPathOfKeyId(id: string): string | null {\n for (const [path, file] of Object.entries(SHARED_FILES))\n if (id.startsWith(file.prefix)) return path;\n return null;\n}\n","/**\n * CLI targets parser — v0.7.0 multi-select.\n *\n * SPEC: docs/specs/cli-multi-select.md F2 (parseCliTargets).\n *\n * Input shapes:\n * - undefined / null / \"\" / [] → [\"claude\"] (default)\n * - \"claude\" / \"codex\" / \"opencode\" / \"antigravity\" → single-element array\n * - [\"claude\", \"codex\"] (cac repeatable) → sorted array\n * - \"both\" / \"all\" (v0.8.0 제거된 legacy alias) → ok=false + 마이그레이션 안내 (throw 아님)\n * - \"invalid\" → ok=false + error (throw 아님)\n *\n * Output:\n * - ok: 유효하면 true, reject 시 false (+ error 메시지, targets=[\"claude\"] default)\n * - targets: sorted ReadonlyArray<CliBase> (claude → codex → opencode → antigravity 순)\n * - warnings: 메시지 배열 (현재 reject-only 정책이라 비어 있음)\n */\n\nimport { CLI_BASES, type CliBase, type CliTargets, isCliBase } from \"./types.js\";\n\n/** SSOT — claude → codex → opencode → antigravity 정렬 순서. prompts.ts에서 import. */\nexport const CLI_BASE_SORT_ORDER: Record<CliBase, number> = {\n claude: 0,\n codex: 1,\n opencode: 2,\n antigravity: 3,\n};\n\n/**\n * CLI 표시명 SSOT — 설치 화면 요약(`CLI` 행)과 `AGENTS.md` 제목(`{CLI_NAMES}`)이 같은 이름을 쓴다.\n * 사본을 두면 한쪽만 바뀐다.\n */\nexport const CLI_DISPLAY_NAMES: Record<CliBase, string> = {\n claude: \"Claude\",\n codex: \"Codex\",\n opencode: \"OpenCode\",\n antigravity: \"Antigravity\",\n};\n\nexport interface ParseCliTargetsResult {\n ok: boolean;\n targets: CliTargets;\n warnings: ReadonlyArray<string>;\n /** ok=false 시 reject 사유. */\n error?: string;\n}\n\n/**\n * `--cli` 입력을 sorted CliTargets로 정규화.\n *\n * Default `[\"claude\"]` (비어있거나 undefined일 때).\n * Invalid 모드는 reject (ok=false).\n *\n * v0.8.0 — `both`/`all` legacy alias 제거 (v0.7.0에서 1 release deprecation 거침).\n * `both`/`all` 입력 시 invalid reject + 마이그레이션 안내.\n */\nexport function parseCliTargets(input: string | string[] | undefined): ParseCliTargetsResult {\n const items = normalizeInput(input);\n if (items.length === 0) {\n return { ok: true, targets: [\"claude\"], warnings: [] };\n }\n\n const collected = new Set<CliBase>();\n const warnings: string[] = [];\n\n for (const item of items) {\n if (!isCliBase(item)) {\n // v0.8.0 — alias 제거 마이그레이션 힌트\n let hint = \"\";\n if (item === \"both\") {\n hint = \"\\n v0.8.0 removed 'both' alias. Use --cli claude --cli codex.\";\n } else if (item === \"all\") {\n hint =\n \"\\n v0.8.0 removed 'all' alias. Use --cli claude --cli codex --cli opencode.\";\n } else if (item.includes(\",\")) {\n // v0.7.1 — comma-separated input hint\n hint = \"\\n Tip: comma-separated not supported. Use --cli A --cli B for multiple.\";\n }\n return {\n ok: false,\n targets: [\"claude\"],\n warnings,\n error: `Invalid --cli value: ${item}. Must be one of: ${CLI_BASES.join(\" | \")}${hint}`,\n };\n }\n collected.add(item);\n }\n\n const targets = [...collected].sort((a, b) => CLI_BASE_SORT_ORDER[a] - CLI_BASE_SORT_ORDER[b]);\n return { ok: true, targets, warnings };\n}\n\nfunction normalizeInput(input: string | string[] | undefined): string[] {\n if (input === undefined || input === null) return [];\n if (typeof input === \"string\") {\n const trimmed = input.trim();\n return trimmed === \"\" ? [] : [trimmed];\n }\n return input.filter((s) => typeof s === \"string\" && s.trim() !== \"\").map((s) => s.trim());\n}\n\n/** Targets에 특정 base 포함 여부. has() 패턴. */\nexport function targetsInclude(targets: CliTargets, base: CliBase): boolean {\n return targets.includes(base);\n}\n","import {\n copyFileSync,\n existsSync,\n constants as fsConstants,\n lstatSync,\n mkdirSync,\n readdirSync,\n readFileSync,\n readlinkSync,\n symlinkSync,\n} from \"node:fs\";\nimport { basename, dirname, join } from \"node:path\";\nimport { outsideProjectTarget } from \"./outside-project.js\";\n\n/** Ensure a directory exists, creating parents as needed. Idempotent. */\nexport function ensureDir(path: string): void {\n mkdirSync(path, { recursive: true });\n}\n\nexport interface CopyResult {\n copied: number;\n skipped: number;\n}\n\n/** Copy a single file, creating parent dirs as needed. Idempotent. */\nexport function copyFile(source: string, target: string): void {\n if (!existsSync(source)) {\n throw new Error(`Source not found: ${source}`);\n }\n mkdirSync(dirname(target), { recursive: true });\n copyFileSync(source, target);\n}\n\n/**\n * Copy a directory's **files** recursively. Creates target if missing.\n *\n * 계약이 통짜 `cpSync` 보다 좁다 — source 쪽 **심볼릭 링크와 빈 디렉터리는 재현되지 않는다**.\n * 지금 `templates/` 에는 둘 다 0개이고(git 은 빈 디렉터리를 담지 못한다) 실행 비트·내용·\n * 사용자 추가 파일 보존은 종전과 같다. 스킬에 심링크를 넣게 되면 이 줄부터 다시 볼 것.\n *\n * **파일 단위로 돈다** (`cpSync` 통짜 복사가 아니다, #343). 통짜 복사는 대상 트리 안에 링크가\n * 섞여 있어도 그대로 따라가 남의 파일을 덮는데, 그것을 걸러낼 자리가 호출자에게 없었다.\n * 무엇을 건너뛸지 **판정은 호출자 몫**이다 — fs 층에 소유권 정책을 넣으면 그 정책이 두 번째\n * 사본으로 자라난다(`foreign-slot.ts` 가 그 SSOT).\n *\n * @param foreignOf **필수**다. 옵셔널로 두면 다음 호출자가 조용히 빠뜨리고, 그 형태가\n * 이 사이클에 세 번 났다(쓰는 주체가 판정을 안 부름). 가드가 필요 없는 호출은 `() => null` 로\n * **명시**한다 — 사람이 매번 기억해야 하는 규약은 규약이 아니다.\n * source 기준 상대경로를 받아, 그 파일을 쓰면 남의 것을 건드리는 경우\n * **그 자리의 경로**를 돌려준다(아니면 null). 파일 경로가 아니라 자리를 받는 이유는\n * 사용자가 옮겨야 할 대상이 파일이 아니라 그 자리(예: 중간 디렉터리 링크)이기 때문이다.\n * @returns 건너뛴 자리들 (**중복 포함** — 거르는 것은 호출자 몫). 화면에 낸다 — 침묵 금지\n */\nexport function copyDir(\n source: string,\n target: string,\n foreignOf: (relFile: string) => string | null,\n): string[] {\n if (!existsSync(source)) {\n throw new Error(`Source dir not found: ${source}`);\n }\n mkdirSync(target, { recursive: true });\n const skipped: string[] = [];\n for (const rel of listFilesRecursive(source)) {\n const foreign = foreignOf(rel);\n if (foreign !== null) {\n // 한 자리가 여러 파일을 가릴 수 있다(중간 디렉터리 링크) — 같은 자리가 여러 번 담긴다.\n // 중복 제거는 **호출자가** 한다: 엔트리를 넘나드는 중복은 여기서 볼 수 없어 어차피\n // 그쪽이 한 번 더 걸러야 하고, 같은 규칙을 두 곳에 두면 한쪽이 죽은 줄이 된다.\n skipped.push(foreign);\n continue;\n }\n copyFile(join(source, rel), join(target, rel));\n }\n return skipped;\n}\n\n/** 같은 초 안에서 허용하는 백업 개수 상한. 넘으면 조용히 덮는 대신 크게 실패한다. */\nconst MAX_BACKUP_CLAIMS = 100;\n\n/**\n * 한 이름을 **원자적으로 선점**해서 돌려준다 — 같은 stamp 로 두 번 백업해도 앞선 백업을 덮지 않게.\n *\n * `formatStamp` 은 초 해상도라 사용자가 `update`/재설치를 연달아 돌리면 같은 이름이 나온다.\n * 그때 세 백업 함수가 서로 다르게 망가졌다 (R-3m):\n * - `cpSync`/`copyFileSync` 는 조용히 **앞선 백업을 덮는다** — 지키려던 사용자 편집분이 사라진다.\n * - `renameSync` 는 ENOTEMPTY 로 죽는다 — 재설치가 크래시한다.\n *\n * `existsSync` 로 \"비었나\"를 **판정하지 않는다**: 확인과 생성 사이가 곧 경합 창이다 (ADR-049\n * \"판정하지 말고 디스크가 답하게 하라\"). 생성 자체를 원자적으로 시도하고 EEXIST 를 디스크의\n * 대답으로 받는다 — `mkdirSync`(비재귀)와 `copyFileSync(..., COPYFILE_EXCL)` 둘 다 그 성질을 준다.\n */\nfunction claimBackupPath(base: string, claim: (candidate: string) => void): string {\n for (let n = 1; n <= MAX_BACKUP_CLAIMS; n++) {\n const candidate = n === 1 ? base : `${base}-${n}`;\n try {\n claim(candidate);\n return candidate;\n } catch (err) {\n // EEXIST = \"그 이름은 이미 누가 썼다\". 그 외(ENOENT·EACCES…)는 재시도로 풀릴 문제가 아니다.\n if ((err as NodeJS.ErrnoException).code !== \"EEXIST\") throw err;\n }\n }\n throw new Error(`Cannot allocate a backup path for ${base} (${MAX_BACKUP_CLAIMS} taken)`);\n}\n\n/**\n * 기존 파일을 timestamp 백업본으로 복사하고 그 경로를 반환한다. **판정은 호출자 몫.**\n *\n * 판정을 여기 두지 않는 이유는 술어가 \"새 내용과 다른가\"가 아니기 때문이다 — update 의 스킬\n * 갱신(ADR-046)은 \"**설치 시점**과 다른가\"로 판정한다. 하네스가 개선해서\n * 달라진 파일은 사용자가 안 건드렸으므로 백업 없이 덮어써야 하고, 내용 비교만으로는 그\n * 둘을 구분할 수 없다.\n *\n * 심볼릭 링크는 **실체 내용으로** 백업된다 (`copyFileSync` 가 링크를 따라간다). 호출자는 백업 직후 같은 경로를 덮어쓰는데, 그 쓰기도 링크를 따라가\n * 링크 대상을 바꾼다. 링크 사본을 남기면 백업이 가리키는 내용도 같이 바뀌어 아무것도 못 지킨다.\n */\nexport function backupFile(target: string, now: Date = new Date()): string {\n return claimBackupPath(`${target}.backup-${formatStamp(now)}`, (c) =>\n copyFileSync(target, c, fsConstants.COPYFILE_EXCL),\n );\n}\n\n/**\n * #780 — `backupFile` 과 같은 이름 규칙으로 **다른 자리에 옮겨 둔 파일**(`source`)을 `target` 의 백업으로 쓴다. 외부 스킬\n * 도구는 스킬 폴더를 새로 만들어 옆에 둔 백업까지 지우므로, 호출 전에 자리 밖 백업 폴더(`claimBackupDir`)로 옮겨 두었다가\n * 호출 뒤에 쓴다.\n */\nexport function backupFromCopy(target: string, source: string, now: Date = new Date()): string {\n mkdirSync(dirname(target), { recursive: true });\n return claimBackupPath(`${target}.backup-${formatStamp(now)}`, (c) =>\n copyFileSync(source, c, fsConstants.COPYFILE_EXCL),\n );\n}\n\n/** #839 — 링크를 링크 그대로 `<target>.backup-<시각>` 으로 남긴다(가리키는 글자 그대로 — 대상 본체는 건드리지 않는다). */\nexport function backupLink(target: string, source: string, now: Date = new Date()): string {\n mkdirSync(dirname(target), { recursive: true });\n return claimBackupPath(`${target}.backup-${formatStamp(now)}`, (c) =>\n symlinkSync(readlinkSync(source), c),\n );\n}\n\n/** #780 — `<dir>.backup-<시각>[-n]` 폴더를 새로 만든다(`backupFile` 과 같은 이름 규칙 — 무시 목록 · 백업 정리가 같이 본다). */\nexport function claimBackupDir(target: string, now: Date = new Date()): string {\n return claimBackupPath(`${target}.backup-${formatStamp(now)}`, (c) => mkdirSync(c));\n}\n\n/**\n * #600 — `backupFile` 이 만든 이름에서 원본 경로를 되찾는다(`<file>.backup-<stamp>[-n]`). 이름 규칙을 아는 곳이\n * 이 파일 하나라 여기 둔다. 그 모양이 아니면 `null`.\n */\nexport function backupOriginal(backupPath: string): string | null {\n const m = /^(.+)\\.backup-\\d{8}T\\d{6}(?:-\\d+)?$/.exec(backupPath);\n return m?.[1] ?? null;\n}\n\n/**\n * #890 · #897 — AppleDouble 짝인가: 이름이 `._<name>` 이고 같은 폴더에 `<name>`(파일 · 폴더 · 링크)이 있다. macOS 는 확장 속성을 담지\n * 못하는 볼륨(exFAT · FAT · SMB 등)에 그것을 이 이름의 파일로 두고, `<name>` 을 옮기거나 지우면 짝도 함께 옮기거나 지운다(실측\n * 2026-10-25, macOS exFAT 디스크 이미지 — 새 파일 · 폴더마다 짝이 생긴다). 짝은 독립 파일이 아니라 `<name>` 에 딸린 메타데이터라\n * 기록 · 소유 판정 · 정리 · 백업 · 되돌리기 어디에도 따로 들지 않는다 — 디렉터리 목록이 그 판정들로 흘러가는 곳은 모두 이것으로 거른다.\n * `<name>` 이 없는 `._x` 는 평범한 파일이다(일반 볼륨의 설치자 파일 — 지금처럼 지킨다).\n */\nexport function isAppleDoubleOf(name: string, siblings: ReadonlySet<string>): boolean {\n return name.length > 2 && name.startsWith(\"._\") && siblings.has(name.slice(2));\n}\n\n/**\n * 목록을 거치지 않은 경로(설치 기록의 파일 등)가 AppleDouble 짝인가 — 같은 판정(`isAppleDoubleOf`)을 디스크의 이름 짝으로 본다. 옛 판이\n * 짝을 자산 파일로 적어 둔 기록이 있다(#890) — 기록은 고치지 않고 읽는 쪽이 이것으로 거른다. 못 보면 false(평범한 파일).\n */\nexport function isAppleDoublePath(abs: string): boolean {\n const name = basename(abs);\n try {\n return (\n isAppleDoubleOf(name, new Set([name.slice(2)])) &&\n lstatSync(join(dirname(abs), name.slice(2)), { throwIfNoEntry: false }) !== undefined\n );\n } catch {\n return false;\n }\n}\n\n/** `readdirSync` 결과에서 AppleDouble 짝(`isAppleDoubleOf`)을 뺀다 — 이름 배열 · `Dirent` 배열 둘 다. */\nexport function withoutAppleDouble<T extends string | { name: string }>(\n entries: ReadonlyArray<T>,\n): T[] {\n const nameOf = (e: T): string => (typeof e === \"string\" ? e : e.name);\n const names = new Set(entries.map(nameOf));\n return entries.filter((e) => !isAppleDoubleOf(nameOf(e), names));\n}\n\n/**\n * `dir` 아래 모든 파일의 상대 경로 (디렉터리 자체는 제외). 순서는 readdir 순. AppleDouble 짝은 들지 않는다(`isAppleDoubleOf`).\n *\n * `readdirSync(dir, { recursive: true })` 를 안 쓰는 이유: 그 옵션은 Node 20.1.0 에 들어왔는데\n * `engines` 는 `>=20.0.0` 이다. 20.0.x 에서 조용히 `undefined` 를 흘리는 대신 직접 순회한다.\n */\nexport function listFilesRecursive(dir: string, prefix = \"\"): string[] {\n if (!existsSync(dir)) return [];\n const out: string[] = [];\n for (const entry of withoutAppleDouble(readdirSync(dir, { withFileTypes: true }))) {\n const rel = prefix ? `${prefix}/${entry.name}` : entry.name;\n if (entry.isDirectory()) {\n out.push(...listFilesRecursive(join(dir, entry.name), rel));\n } else if (entry.isFile()) {\n out.push(rel);\n }\n }\n return out;\n}\n\n/**\n * #839 — `listFilesRecursive` 와 같되 안의 링크(파일 · 폴더)는 따라가지 않고 그 자체를 낸다(`link: true`). `dir` 자체가 링크면\n * 따라 들어간다. 외부 스킬 도구 앞의 hold 전용 — 기록(`snapshotSkillRoots`)은 링크를 건너뛰는 `listFilesRecursive` 를 쓴다.\n * AppleDouble 짝은 들지 않는다 — 짝을 두는 볼륨은 `<name>` 을 옮길 때 짝을 함께 옮긴다(따로 옮기면 되돌리기가 ENOENT 로 죽는다, #890).\n */\nexport function listEntriesRecursive(\n dir: string,\n prefix = \"\",\n): Array<{ rel: string; link: boolean }> {\n if (!existsSync(dir)) return [];\n const out: Array<{ rel: string; link: boolean }> = [];\n for (const entry of withoutAppleDouble(readdirSync(dir, { withFileTypes: true }))) {\n const rel = prefix ? `${prefix}/${entry.name}` : entry.name;\n if (entry.isDirectory()) {\n out.push(...listEntriesRecursive(join(dir, entry.name), rel));\n } else if (entry.isFile() || entry.isSymbolicLink()) {\n out.push({ rel, link: entry.isSymbolicLink() });\n }\n }\n return out;\n}\n\nfunction formatStamp(now: Date): string {\n return now\n .toISOString()\n .replace(/[-:]/g, \"\")\n .replace(/\\.\\d+Z$/, \"Z\")\n .slice(0, 15);\n}\n\n/** Create a project skeleton: <project>/.claude/{commands/uzys,rules,skills,agents,hooks}. */\n/**\n * install 이 기록 없이 만드는 빈 뼈대 — uninstall 은 같은 목록을 빈 디렉터리 걷기의 시작점으로 쓴다(#746 · 설계 §2.3 #8 ⓑ).\n * 비어 있을 때만 걷힌다 — 설치자가 무엇이든 넣었으면 남는다.\n */\nexport const PROJECT_SKELETON_DIRS: ReadonlyArray<string> = [\n \".claude/commands/uzys\",\n \".claude/rules\",\n \".claude/skills\",\n \".claude/agents\",\n \".claude/hooks\",\n \"docs/decisions\",\n];\n\nexport function ensureProjectSkeleton(projectDir: string): void {\n for (const d of PROJECT_SKELETON_DIRS) {\n // ADR-098 — 실체가 프로젝트 밖(예: `.claude` → dotfiles)이면 빈 폴더도 만들지 않는다.\n if (outsideProjectTarget(projectDir, join(projectDir, d)) !== null) continue;\n mkdirSync(join(projectDir, d), { recursive: true });\n }\n}\n\n/**\n * #653 — 유효하지 않은 UTF-8 바이트가 섞인 파일을 문자열 왕복(read-utf8 → write-utf8)으로\n * 다시 쓰면 해당 바이트가 영구 변형되고, uninstall 로는 원본으로 돌아오지 않는다.\n * 루트 `CLAUDE.md` 의 마커 삽입/제거 경로가 이 왕복을 쓰므로, **변형이 일어나기 전에** 원시\n * 바이트를 옆에 백업해 둔다. 무경고 변형이 아니라 \"백업했음 + 원인\"을 사용자에게 말한다.\n *\n * 문자열로 다시 조립한 뒤에도 바이트가 동일한 파일(정상 UTF-8)은 아무것도 하지 않는다.\n */\nexport function backupIfLossyUtf8(target: string, now: Date = new Date()): string | null {\n if (!existsSync(target)) return null;\n let raw: Buffer;\n try {\n raw = readFileSync(target);\n } catch {\n return null;\n }\n if (Buffer.from(raw.toString(\"utf8\")).equals(raw)) return null;\n const backup = backupFile(target, now);\n console.error(\n `⚠ ${target} contains bytes that are not valid UTF-8 — rewriting would alter them permanently. Original bytes saved as ${backup}`,\n );\n return backup;\n}\n","/**\n * 링크를 따라간 실체가 **프로젝트 밖**인가 — install · update · uninstall 이 같은 판정을 쓴다 (#678 · #668).\n *\n * 설치자가 하네스 폴더나 그 안 파일을 프로젝트 밖(dotfiles 등)으로 링크해 두면, 링크를 따라 쓰는 순간\n * 하네스가 **이 프로젝트 기록에 없는 파일**을 바꾼다 — 두 프로젝트가 그 폴더를 같이 쓰면 한쪽의 실행이 다른\n * 쪽을 바꾼다. 세 동작 모두 그 실체는 건드리지 않고 \"남김 + 경로\"를 말한다(루트 CLAUDE.md §설치자 디스크는\n * 한 원칙으로). 프로젝트 **안**을 가리키는 링크는 따라가 쓴다(같은 프로젝트의 공유 자리).\n */\n\nimport { lstatSync, readlinkSync, realpathSync } from \"node:fs\";\nimport { basename, dirname, isAbsolute, join, relative, resolve, sep } from \"node:path\";\n\n/** 프로젝트 밖이라 건드리지 않은 자리 하나. */\nexport interface OutsideLink {\n /** project-relative(슬래시) — 쓰려던 경로. */\n path: string;\n /** 그 경로가 실제로 가리키는 절대경로. */\n target: string;\n /** project-relative — 프로젝트 밖으로 나가는 링크가 걸린 자리(파일 링크면 `path` 자신, 폴더 링크면 그 폴더). */\n link: string;\n /** `link` 가 가리키는 절대경로. */\n linkTarget: string;\n}\n\n/** 링크를 따라간 실체가 프로젝트 루트 밖인가 — realpath 기준(프로젝트 경로 자체가 링크여도 같은 잣대). */\nexport function isOutsideProject(projectDir: string, target: string): boolean {\n // 루트도 대상과 같은 방식으로 푼다 — 아직 없는 프로젝트 경로(`/tmp/x` → `/private/tmp/x`)에서 대상만 풀리면 전부 \"밖\" 으로 읽힌다.\n // 못 풀면 원 경로로 비교한다.\n const root = resolveWriteTarget(projectDir) ?? projectDir;\n const rel = relative(root, target);\n return rel === \"..\" || rel.startsWith(`..${sep}`) || isAbsolute(rel);\n}\n\n/**\n * 이 경로에 쓰면 실제로 어디가 바뀌는가 — 아직 없는 파일이면 가장 가까운 있는 조상을 풀고 나머지를 붙인다\n * (밖 폴더 링크 안에 **새로** 만드는 것도 밖에 쓰는 것이다). 깨진 링크는 링크 문자열을 따라간다 — 쓰기는\n * 깨진 링크의 대상을 만든다. 풀 수 없으면(권한 등) null = 모른다.\n */\nexport function resolveWriteTarget(abs: string): string | null {\n const tail: string[] = [];\n const hops = new Set<string>();\n let path = abs;\n for (;;) {\n try {\n return join(realpathSync(path), ...tail);\n } catch {\n /* 없거나 깨진 링크 — 아래에서 가른다 */\n }\n let isLink = false;\n try {\n const st = lstatSync(path, { throwIfNoEntry: false });\n if (st !== undefined && !st.isSymbolicLink()) return null; // 있는데 못 푼다 — 모른다\n isLink = st !== undefined;\n } catch {\n return null;\n }\n if (isLink) {\n if (hops.has(path)) return null; // 링크 고리\n hops.add(path);\n path = resolve(dirname(path), readlinkSync(path));\n continue;\n }\n const parent = dirname(path);\n if (parent === path) return null;\n tail.unshift(basename(path));\n path = parent;\n }\n}\n\n/**\n * `abs` 에 쓰면 프로젝트 밖이 바뀌는가 — 바뀌면 그 자리(`OutsideLink`), 아니면 null.\n * 판정할 수 없으면 null 이다(지금까지의 동작 그대로 — 이 판정은 밖을 알아볼 때만 멈춘다).\n */\nexport function outsideProjectTarget(projectDir: string, abs: string): OutsideLink | null {\n const target = resolveWriteTarget(abs);\n if (target === null || !isOutsideProject(projectDir, target)) return null;\n const rel = relative(projectDir, abs);\n const parts = rel.split(sep);\n // 밖으로 나가는 링크가 걸린 가장 얕은 자리 — 화면이 폴더 링크를 파일마다가 아니라 한 줄로 말한다.\n let link = rel;\n let linkTarget = target;\n for (let i = 1; i <= parts.length; i++) {\n const prefix = join(projectDir, ...parts.slice(0, i));\n const st = lstatSync(prefix, { throwIfNoEntry: false });\n if (st?.isSymbolicLink() !== true) continue;\n const resolved = resolveWriteTarget(prefix);\n if (resolved === null || !isOutsideProject(projectDir, resolved)) continue;\n link = parts.slice(0, i).join(sep);\n linkTarget = resolved;\n break;\n }\n const slash = (p: string): string => p.split(sep).join(\"/\");\n return { path: slash(rel), target, link: slash(link), linkTarget };\n}\n\n/**\n * `rel` 자리 **자체**가 프로젝트 밖으로 나가는 링크인가 — 폴더째 옮기려는 자리(uninstall 의 `.codex` 등)용.\n * 그 링크를 옮기면 설치자가 걸어 둔 링크가 프로젝트에서 사라진다(#692). 맞으면 그 자리, 아니면 null.\n */\nexport function outsideLinkAt(projectDir: string, rel: string): OutsideLink | null {\n const abs = join(projectDir, rel);\n try {\n if (!lstatSync(abs, { throwIfNoEntry: false })?.isSymbolicLink()) return null;\n } catch {\n return null; // 판정할 수 없다 — 지금까지의 동작 그대로\n }\n return outsideProjectTarget(projectDir, abs);\n}\n\n/** 한 실행이 밖이라 건너뛴 자리를 모은다 — 쓰기 직전에 `skip(abs)` 를 부르고, true 면 쓰지 않는다. */\nexport interface OutsideGuard {\n skip(abs: string): boolean;\n list(): OutsideLink[];\n}\n\nexport function createOutsideGuard(projectDir: string): OutsideGuard {\n const seen = new Map<string, OutsideLink>();\n return {\n skip(abs) {\n const hit = outsideProjectTarget(projectDir, abs);\n if (hit === null) return false;\n if (!seen.has(hit.path)) seen.set(hit.path, hit);\n return true;\n },\n list: () => [...seen.values()],\n };\n}\n\n/** 여러 출처의 목록을 경로당 하나로 합친다. */\nexport function mergeOutside(...lists: ReadonlyArray<ReadonlyArray<OutsideLink>>): OutsideLink[] {\n const out = new Map<string, OutsideLink>();\n for (const list of lists) for (const o of list) if (!out.has(o.path)) out.set(o.path, o);\n return [...out.values()];\n}\n","/**\n * #585 후속(리뷰 B1 · B2) — 옛 판(v26.163.0 이하)이 마지막 install 의 트랙으로 **덮어쓴** 기록의 트랙을 되살린다.\n *\n * 옛 판은 `spec.tracks` 를 누적하지 않았다. 그래서 `install --track tooling --cli claude --cli antigravity` 뒤\n * `install --track data --cli codex` 를 하면 기록은 `[data]` 인데 디스크에는 tooling 의 몫이 하네스 것으로 깔려 있다.\n *\n * 되살린 트랙은 **더 까는 결과**를 낳는다(update 가 그 트랙의 몫을 모든 CLI 에 깔고 `.mcp.json` 에 서버를 더하며, 위저드는 깔린\n * 트랙을 뺄 수 없다). 그래서 근거는 **트랙 이름을 그대로 적은 정확한 근거 하나**만 쓴다: `.claude/.installed-tracks` — claude 를\n * 깐 실행이 쓰는 하네스 파일이고, **기록에 그 sha 가 있고 디스크 내용이 같을 때만** 읽는다(sha 가 없으면 기록이 그 내용을\n * 보증하지 않는다 — #699 \"메타파일이 달라도 트랙은 기록에서\").\n *\n * 파일 근거(claude 자리에 기록된 트랙 파일)로는 트랙을 더하지 않는다 — claude 자리 파일이 같은 형제 트랙(csr-* · ssr-nextjs,\n * base · ssr-htmx)을 가를 수 없어 고르지 않은 트랙과 그 MCP 서버를 더했다(리뷰 B2). 그 근거는 **남기기만 하는** 쪽, 트랙 밖\n * 회수의 보호(`out-of-track.ts`)에서만 쓴다.\n *\n * 1회 표시는 두지 않는다 — 이 판은 메타파일을 기록과 같은 누적 트랙으로 쓰므로(`installer.ts`) 다시 읽어도 더할 트랙이 없다.\n * 되살린 트랙은 읽는 쪽 전부가 같이 보고, 기록에는 다음 쓰기 때 남는다.\n */\n\nimport { lstatSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { hashContent, type InstallLog } from \"./install-log.js\";\nimport { buildAssetSpec, buildManifest } from \"./manifest.js\";\nimport { DEFAULT_OPTIONS, isTrack, type Track } from \"./types.js\";\n\nconst META_FILE = \".claude/.installed-tracks\";\n/** `policyFiles` 의 키(`.claude/` 상대). */\nconst META_KEY = \".installed-tracks\";\n\nexport function memoTargets(\n render: (tracks: ReadonlyArray<Track>) => Iterable<string>,\n): (tracks: ReadonlyArray<Track>) => ReadonlySet<string> {\n const cache = new Map<string, ReadonlySet<string>>();\n return (tracks) => {\n const key = [...tracks].sort().join(\",\");\n const hit = cache.get(key) ?? new Set(render(tracks));\n cache.set(key, hit);\n return hit;\n };\n}\n\n/** claude 자리(파일 자산 · 번들 스킬 디렉터리)에 트랙이 가져오는 대상 — manifest 렌더(프로젝트 상대). */\nexport const claudeTargets = memoTargets((tracks) => {\n const spec = buildAssetSpec({ tracks, options: DEFAULT_OPTIONS });\n return buildManifest(spec)\n .filter((e) => e.applies(spec) && (e.type === \"file\" || e.target.startsWith(\".claude/skills/\")))\n .map((e) => e.target);\n});\n\n/** 기록된 claude 자리 경로(프로젝트 상대) — 파일과 스킬 디렉터리. */\nexport function claudeRecorded(log: InstallLog): Set<string> {\n return new Set([\n ...(log.policyFiles ?? []).map((f) => `.claude/${f.path}`),\n ...(log.skillFiles ?? []).map((f) => `.claude/skills/${f.path.split(\"/\")[0] ?? \"\"}`),\n ]);\n}\n\n/** 잘린 트랙을 되살린 기록. claude 가 깔린 기록이 아니면 그대로다(근거가 claude 자리의 메타파일이다). */\nexport function healRecordedTracks(\n log: InstallLog,\n projectDir: string,\n clis: ReadonlyArray<string>,\n): InstallLog {\n if (!clis.includes(\"claude\")) return log;\n const recorded = log.spec.tracks.filter(isTrack);\n const added = metafileTracks(log, projectDir).filter((t) => !recorded.includes(t));\n if (added.length === 0) return log;\n return {\n ...log,\n spec: { ...log.spec, tracks: [...new Set([...log.spec.tracks, ...added])].sort() },\n };\n}\n\nfunction metafileTracks(log: InstallLog, projectDir: string): Track[] {\n const abs = join(projectDir, META_FILE);\n if (lstatSync(abs, { throwIfNoEntry: false })?.isFile() !== true) return [];\n const content = readFileSync(abs, \"utf8\");\n const sha = log.policyFiles?.find((f) => f.path === META_KEY)?.sha256;\n if (sha === undefined || hashContent(content) !== sha) return []; // 기록이 보증하지 않는 내용은 근거가 아니다\n return content\n .split(\"\\n\")\n .map((l) => l.trim())\n .filter(isTrack);\n}\n","import { CONTINUOUS_SKILLS } from \"./external-assets.js\";\n\n/**\n * Project-context scaffold for the delivered CLAUDE.md / AGENTS.md.\n *\n * The installer is a pure Node CLI and CANNOT run an LLM, so it cannot fill a\n * project-specific context file at install time. Shipping generic per-track prose\n * instead produced \"meaningless\" files (a literal `# [Project Name]` title, a Bash\n * stack advertised to every project, phantom rule names). This module ships an\n * honest fill-in SCAFFOLD instead: each section carries an embedded `<!-- FILL: -->`\n * instruction the user runs in their own coding agent post-install (or by hand).\n * The same scaffold body feeds BOTH the Claude Code root CLAUDE.md and the\n * `{PROJECT_CONTEXT}` block of every AGENTS.md — one source, byte-identical across\n * all 4 CLIs, with no disk read-back coupling.\n */\n\n/**\n * 하네스 앵커의 **설치 파일명** (프로젝트 루트). manifest target · 루트 CLAUDE.md 의 `@import`\n * 줄 · update 갱신 · uninstall 회수가 전부 이 상수를 쓴다.\n *\n * 이름이 여러 곳에 리터럴로 살면 한 곳만 바뀌었을 때 조용히 갈리고, 그때 생기는 것은 아무도\n * 갱신·회수하지 않는 고아 파일이다 (`no-false-ship.md` §Drift 구조 차단).\n */\nexport const HARNESS_ANCHOR_FILE = \"CLAUDE-uzys-harness.md\";\n\n/** 루트 CLAUDE.md 가 앵커를 끌어오는 Claude Code memory import 줄. */\nexport const HARNESS_IMPORT_LINE = `@${HARNESS_ANCHOR_FILE}`;\n\n/**\n * 관리 마커 — **하네스가 넣은 줄과 사용자가 쓴 줄을 가르는 유일한 표식**이다.\n * 재실행 시 중복 추가를 막고(idempotent), uninstall 이 사용자 본문을 건드리지 않고\n * 이 블록만 도려낼 수 있게 한다.\n */\nconst IMPORT_MARKER_START = \"<!-- uzys-harness:import:start -->\";\nconst IMPORT_MARKER_END = \"<!-- uzys-harness:import:end -->\";\n\n/**\n * 안내 절의 헤딩. **마커 없는 옛 `AGENTS.md`** 에서 이 조각을 찾아내는 단서라\n * (`agents-md-merge.ts`) 리터럴을 양쪽에 두지 않는다 — 갈리면 조각이 두 벌 쌓인다.\n */\nexport const CONTINUOUS_SKILLS_HEADING = \"## Skills that apply continuously\";\n\n/**\n * ADR-085 — 상시 적용 스킬 안내. **실제로 깔린 것만** 적는다.\n *\n * 배포 앵커가 전역 6원칙과 바이트 동일해지면서 앵커 꼬리절(`Skills that apply continuously`)이\n * 사라졌다. 그 절이 하던 일 — 프롬프트 모양으로는 절대 발화하지 않는 스킬을 매 응답에 열게\n * 하는 것 — 을 설치기가 프로젝트 맥락 블록에서 한다. 어느 스킬이 그런지는 카탈로그\n * (`CONTINUOUS_SKILLS`)가 SSOT 이고, 여기는 선택된 것만 골라 줄을 만든다.\n *\n * @returns 안내 블록. 해당 스킬이 하나도 선택되지 않았으면 빈 문자열 — 한 줄도 상주시키지 않는다.\n */\nexport function renderContinuousSkillsNote(selectedInternalSkills: ReadonlyArray<string>): string {\n const lines = CONTINUOUS_SKILLS.filter((s) => selectedInternalSkills.includes(s.id)).map(\n (s) => `- \\`${s.id}\\` — ${s.whenToApply}.`,\n );\n if (lines.length === 0) return \"\";\n return [\n CONTINUOUS_SKILLS_HEADING,\n \"\",\n \"These installed skills apply to every response or delegation; no prompt ever looks like their job, so open them from this list:\",\n \"\",\n ...lines,\n ].join(\"\\n\");\n}\n\n/** 스캐폴드 뒤에 상시 스킬 안내를 붙인다 — 안내가 비면 스캐폴드를 그대로 돌려준다. */\nexport function withContinuousSkillsNote(\n scaffold: string,\n selectedInternalSkills: ReadonlyArray<string>,\n): string {\n const note = renderContinuousSkillsNote(selectedInternalSkills);\n return note ? `${scaffold}\\n\\n${note}` : scaffold;\n}\n\n/** 관리 블록 본문 — import 한 줄 + (있으면) 상시 스킬 안내. 블록 안은 하네스 소유다. */\nfunction importBlock(continuousSkills: ReadonlyArray<string>): string {\n const note = renderContinuousSkillsNote(continuousSkills);\n const body = note ? `${HARNESS_IMPORT_LINE}\\n\\n${note}` : HARNESS_IMPORT_LINE;\n return `${IMPORT_MARKER_START}\\n${body}\\n${IMPORT_MARKER_END}`;\n}\n\n/**\n * The MUST-HAVE project-context sections, from a harness perspective: the context\n * an agent needs to work on ANY project with few round-trips. Order is intentional\n * (identity → stack → architecture → assets → boundaries → verify).\n */\nexport const FILL_SECTIONS = [\n \"identity\",\n \"stack\",\n \"architecture\",\n \"installed-assets\",\n \"boundaries\",\n \"verify\",\n] as const;\nexport type FillSection = (typeof FILL_SECTIONS)[number];\n\ninterface FillSpec {\n /** Rendered `## <title>` header. */\n title: string;\n /** Body of the `<!-- FILL:<id> — … -->` comment: what to inspect and write. */\n prompt: string;\n /** Honest `_(not filled yet — …)_` line so an unfilled section never states a false fact. */\n placeholder: string;\n}\n\nconst FILL_SPECS: Record<FillSection, FillSpec> = {\n identity: {\n title: \"Identity & Purpose\",\n prompt:\n 'Replace the H1 title above with this project\\'s real name, then state in 1-2 plain sentences what it does, who uses it, and why it exists. Sources: README.md, the package.json / pyproject.toml \"description\", docs/. Do NOT describe the harness itself. Delete this comment when done.',\n placeholder: \"what this project is, who it is for, and why it exists\",\n },\n stack: {\n title: \"Stack & Commands\",\n prompt:\n \"Replace the list below with this project's REAL stack. Inspect package.json / pyproject.toml / go.mod / Cargo.toml / Gemfile + lockfiles and any Makefile/justfile/package scripts. List the language(s)+versions, framework(s), package manager, and the exact install/build/test/run commands. Verify each command exists before writing it — never guess. Delete lines that do not apply, then delete this comment.\",\n placeholder: \"languages, runtimes, package manager, and the install/build/test/run commands\",\n },\n architecture: {\n title: \"Architecture & Layout\",\n prompt:\n \"Map this repository. List each top-level source directory and what it holds, the entry point(s), the 3-5 files a newcomer reads first, and how data/requests flow between the main layers. View the real tree (e.g. `git ls-files | sed 's#/.*##' | sort -u`) — do not assume a framework's conventional layout. Flag any generated/vendored directories that must never be hand-edited. If this is a single small module, say so in one line. Delete this comment when done.\",\n placeholder: \"where things live, the entry points, and how data flows between layers\",\n },\n \"installed-assets\": {\n title: \"Installed Harness Assets\",\n prompt:\n \"List the harness assets installed in this project (rules / skills / agents / commands) and add one line each on when to reach for it here. Verify against the harness folders this CLI reads (.claude/rules/ · .claude/skills/ · .claude/agents/ for Claude Code; AGENTS.md § Harness Rules · .agents/skills/ for Codex and OpenCode; .agents/rules/ · .agents/skills/ for Antigravity) and list ONLY assets whose file exists on disk — do not invent any. Do NOT restate the harness's working principles (they live in this project's harness anchor and rules layer) — cross-reference them instead. Delete this comment when done.\",\n placeholder: \"the installed rules/skills/agents/commands and when to use each\",\n },\n boundaries: {\n title: \"Boundaries — Always / Ask First / Never\",\n prompt:\n 'Fill the Always / Ask First / Never lists with this repo\\'s real red lines. Read the CI config, CODEOWNERS, .gitignore, and release/deploy scripts. Never must include secrets, generated files, force-push, and direct commits to the default branch, plus any repo-specific \"do not touch\" directories. Every entry must be specific and enforceable here. Delete this comment when done.',\n placeholder: \"what to always do, ask before doing, and never do in this repo\",\n },\n verify: {\n title: \"Verification Gate\",\n prompt:\n 'State the single command (or short sequence) that PROVES a change is safe here — the test/lint/typecheck/build gate you run before committing — plus the coverage/CI threshold and what \"done\" means. Sources: package scripts, CI workflow files, CONTRIBUTING. An agent must be able to self-verify without guessing. Delete this comment when done.',\n placeholder: \"the single command that proves a change is safe, and the done bar\",\n },\n};\n\n/**\n * Which file the scaffold is being rendered INTO. It decides one line of the banner: whether the\n * reader is holding the harness anchor or a file that points at one.\n *\n * WHY this is a parameter (#305): the banner used to be one constant shipped byte-identically into\n * all four surfaces, and its third line was written for `CLAUDE.md`. Rendered into `AGENTS.md` it\n * asserted two things that are false there — \"imported from this file\" (measured: 0 import lines)\n * and \"this file is project-specific context only\" (measured: 7 principle sections). A reader of\n * AGENTS.md was told the principles live somewhere else, so they went looking for a file that a\n * Codex-only install does not even have.\n */\nexport type ScaffoldSurface = \"claude\" | \"agents-md\";\n\n/**\n * The anchor sentence, true for the file it is rendered into.\n *\n * There is no import to point at outside Claude Code: `AGENTS.md` has no include directive —\n * agentsmd/agents.md#11 and openai/codex#6038 · #17401 are all still open feature requests\n * (measured 2026-08-29). So every non-Claude surface carries the principles inline and IS the\n * anchor; saying otherwise sends the reader after a file that may not exist.\n */\nfunction anchorLine(surface: ScaffoldSurface): string {\n if (surface === \"claude\") {\n return `> The working principles live in this project's harness anchor \\`${HARNESS_ANCHOR_FILE}\\`, imported at the bottom of this file. Everything above that import is **project-specific context only**.`;\n }\n return `> **This file (\\`AGENTS.md\\`) is the harness anchor** — it carries the working principles in full, further down. Only the \\`## …\\` sections immediately below are project-specific context for you to fill in.`;\n}\n\n/** Visible (rendered-markdown) banner — HTML FILL comments are invisible in a preview, so this\n * blockquote is what stops the scaffold from reading as verified project fact for a human. */\nexport function scaffoldBanner(surface: ScaffoldSurface): string {\n return [\n \"> ⚙️ **SCAFFOLD — not filled in yet.** The sections below are a fill-in template for THIS project, not verified facts.\",\n \"> To fill: open this file and paste each `<!-- FILL: … -->` comment's instruction into your coding agent (e.g. Claude Code) — it will inspect the real repo and write the section. You can also fill them by hand; the comments are the instructions.\",\n anchorLine(surface),\n ].join(\"\\n\");\n}\n\n/**\n * 하네스가 루트 `CLAUDE.md` · `AGENTS.md` 에 쓴 배너 줄 — 현행 판 + 옛 판 문구(태그 v26.96.0~v26.174.0 의\n * `project-claude-merge.ts` 실측, 2026-10-19). 옛 판 문구는 그 판이 쓴 바이트 그대로 고정한다(상수가 바뀌어도 옛 디스크는 그대로다).\n *\n * #837 · #862 — 배너는 **이 줄과 똑같은 줄만** 하네스 것이다. 인용 블록째 하네스 것으로 보면 배너 바로 아래 설치자가 이어 적은\n * 인용 줄까지 하네스 몫이 된다 — uninstall 의 배너 걷기(#837)도 \"채웠는가\" 판정(#862)도 이 집합 하나로 가른다.\n */\nexport const SCAFFOLD_BANNER_LINES: ReadonlySet<string> = new Set([\n ...scaffoldBanner(\"claude\").split(\"\\n\"),\n ...scaffoldBanner(\"agents-md\").split(\"\\n\"),\n // v26.96.0~v26.139.0 — 네 자리에 같은 배너\n \"> The universal harness rules (Rule 1–12) live in the separate rules layer. This file is **project-specific context only**.\",\n // v26.140.0\n \"> The working principles live in this project's harness anchor — `.claude/CLAUDE.md` (Claude Code), `AGENTS.md` (Codex/OpenCode), or `.agents/rules/uzys-harness.md` (Antigravity). This file is **project-specific context only**.\",\n // v26.141.0~v26.148.1\n \"> The working principles live in this project's harness anchor — `CLAUDE-uzys-harness.md` (Claude Code, imported from this file), `AGENTS.md` (Codex/OpenCode), or `.agents/rules/uzys-harness.md` (Antigravity). This file is **project-specific context only**.\",\n]);\n\n/**\n * Shared project-context scaffold body: the visible banner followed by the six MUST-HAVE\n * sections, each a `## Title` + a self-contained `<!-- FILL:id — … -->` comment + an honest\n * `_(not filled yet — …)_` placeholder. Pure and track-agnostic, so the Claude Code CLAUDE.md\n * and every AGENTS.md `{PROJECT_CONTEXT}` block get byte-identical prompts from one source.\n */\nexport function renderFillScaffold(surface: ScaffoldSurface = \"claude\"): string {\n const blocks = FILL_SECTIONS.map((id) => {\n const spec = FILL_SPECS[id];\n return `## ${spec.title}\\n\\n<!-- FILL:${id} — ${spec.prompt} -->\\n\\n_(not filled yet — ${spec.placeholder})_`;\n });\n return `${scaffoldBanner(surface)}\\n\\n${blocks.join(\"\\n\\n\")}`;\n}\n\n/** 스캐폴드 배너를 알아보는 표식. 배너 문구가 바뀌어도 이 문장은 배너의 정체다. */\nexport const SCAFFOLD_MARK = \"SCAFFOLD — not filled in yet\";\n/** 루트 `CLAUDE.md` 머리의 설치 메타 한 줄 (`mergeProjectClaude`). */\nconst TRACK_NOTE_MARK = \"Active track(s):\";\n\n/**\n * **하네스가 쓴 인용문 블록만** 뗀다 — 스캐폴드 배너와 트랙 안내.\n *\n * 인용문 run 을 통째로 보고 그 안에 하네스 표식이 있을 때만 뗀다. 설치자가 적어 둔 인용문\n * (프로젝트 한 줄 소개 같은 것)은 표식이 없으니 그대로 남는다. 앞머리만 보지 않고 전체를 훑는다 —\n * 설치자가 H1 바로 뒤에 자기 인용문을 끼우면 하네스 앞머리가 그 아래로 밀린다(#305).\n */\nexport function dropHarnessQuoteBlocks(lines: ReadonlyArray<string>): string[] {\n const out: string[] = [];\n let i = 0;\n while (i < lines.length) {\n if (!(lines[i] ?? \"\").trim().startsWith(\">\")) {\n out.push(lines[i] ?? \"\");\n i += 1;\n continue;\n }\n let end = i;\n while (end < lines.length && (lines[end] ?? \"\").trim().startsWith(\">\")) end++;\n const run = lines.slice(i, end);\n const text = run.join(\"\\n\");\n if (!text.includes(SCAFFOLD_MARK) && !text.includes(TRACK_NOTE_MARK)) out.push(...run);\n i = end;\n }\n return out;\n}\n\n/**\n * 상시 스킬 안내 조각(ADR-085)이 끝나는 줄 — 헤딩 다음부터 빈 줄 · 소개 문장 · `- \\`id\\` — …` 항목만 조각으로\n * 친다. 그 밖의 첫 줄이 설치자 텍스트의 시작이다(#503 독립 리뷰 B1 — 안내 *뒤*에 적은 설치자 문단을 조각으로\n * 삼키지 않는다). 마커 없는 옛 판에서 조각을 찾는 단서도 이것 하나다.\n */\nexport function continuousNoteEnd(lines: ReadonlyArray<string>, headingAt: number): number {\n let i = headingAt + 1;\n while (i < lines.length) {\n const l = (lines[i] ?? \"\").replace(/\\r$/, \"\").trim();\n const isNote = l === \"\" || l.startsWith(\"- `\") || l.startsWith(\"These installed skills apply\");\n if (!isNote) break;\n i++;\n }\n return i;\n}\n\n/** 마커 유무와 무관하게 상시 스킬 안내 조각을 뗀다 — 옛 Antigravity 앵커는 마커를 걷어 썼다. */\nexport function dropContinuousSkillsNote(lines: ReadonlyArray<string>): string[] {\n const at = lines.findIndex((l) => l.replace(/\\r$/, \"\").trim() === CONTINUOUS_SKILLS_HEADING);\n if (at === -1) return [...lines];\n return [...lines.slice(0, at), ...lines.slice(continuousNoteEnd(lines, at))];\n}\n\n/** `<!-- FILL:… -->` 주석 줄(여러 줄이면 `-->` 까지)을 뗀다. */\nfunction dropFillComments(lines: ReadonlyArray<string>): string[] {\n const out: string[] = [];\n let inFill = false;\n for (const line of lines) {\n if (!inFill && line.trim().startsWith(\"<!-- FILL:\")) inFill = true;\n if (inFill) {\n if (line.includes(\"-->\")) inFill = false;\n continue;\n }\n out.push(line);\n }\n return out;\n}\n\n/**\n * #862 — 하네스가 쓴 인용 **줄**만 뗀다: 배너 줄(`SCAFFOLD_BANNER_LINES` 와 똑같은 줄) · 트랙 안내 줄(v26.140.0 이하 루트 `CLAUDE.md`\n * 머리 — 트랙 이름이 들어 있어 머리말로 알아본다). 인용 블록째 떼면 배너 바로 아래 이어 적은 설치자 인용 줄이 \"안 채움\" 으로 읽혀\n * uninstall 이 그 줄이 든 `AGENTS.md` 를 파일째 지웠다.\n */\nfunction dropHarnessBannerLines(lines: ReadonlyArray<string>): string[] {\n return lines.filter((line) => {\n const bare = line.replace(/\\r$/, \"\");\n return !SCAFFOLD_BANNER_LINES.has(bare) && !bare.startsWith(`> ${TRACK_NOTE_MARK}`);\n });\n}\n\n/** 비교용 — 하네스가 쓴 줄(배너 · 트랙 안내 · FILL 주석 · 상시 스킬 안내)과 빈 줄 · 줄 끝 공백을 뺀 나머지. */\nfunction scaffoldComparable(lines: ReadonlyArray<string>): string[] {\n return dropFillComments(dropContinuousSkillsNote(dropHarnessBannerLines(lines)))\n .map((l) => l.replace(/\\r$/, \"\").trimEnd())\n .filter((l) => l.trim() !== \"\");\n}\n\n/**\n * 프로젝트 맥락 본문이 **손대지 않은 스캐폴드**인가 — 설치자가 채운 것이 없는가 (v26.165.0 독립 검증 BLOCKER-2).\n *\n * 하네스가 쓴 줄을 **양쪽(디스크 본문 · 기준 스캐폴드)에서 뗀 뒤** 비교한다. 바이트 비교로 두면 FILL 프롬프트\n * 문구를 고친 릴리즈가 옛 판으로 깐 모든 안 채운 스캐폴드를 \"채웠다\" 로 읽고, 옛 Antigravity 앵커의 마커 없는\n * 상시 스킬 안내가 설치자 글로 읽힌다. 남는 비교 대상은 절 제목과 `_(not filled yet — …)_` 자리표시자다 —\n * 설치자가 \"Delete this comment when done\" 대로 주석만 지운 상태도 같은 뜻이라 \"안 채움\" 이다.\n *\n * 앵커 씨 뿌리기(`anchor-seed.ts`) · uninstall 의 `AGENTS.md` 걷기 · 옛 Antigravity 앵커 은퇴가 이 함수 하나를\n * 쓴다 — 판정이 갈리면 \"옮겼다/채웠다\" 가 서로 다른 답을 낸다.\n */\nexport function isUnfilledScaffold(body: ReadonlyArray<string>): boolean {\n const pristine = scaffoldComparable(renderFillScaffold(\"agents-md\").split(\"\\n\"));\n return scaffoldComparable(body).join(\"\\n\") === pristine.join(\"\\n\");\n}\n\nexport interface MergeOptions {\n /** Project directory basename → the H1 title (fixes the shipped `# [Project Name]` literal). */\n projectName: string;\n /**\n * ADR-085 — 이 설치가 깐 번들 스킬 id. 관리 블록 안의 상시 스킬 안내는 여기서 고른다.\n * 생략 = 안내 없음(레거시 호출부·테스트). update 는 `.claude/skills/<id>` 존재로 채운다.\n */\n continuousSkills?: ReadonlyArray<string>;\n /**\n * #528 — 파일을 **새로 만들 때** 프로젝트 맥락 자리에 넣을 본문. 생략 = 빈 스캐폴드.\n *\n * CLI 를 더해 앵커가 새로 태어날 때 다른 앵커의 설치자 절을 옮겨 심는 자리다(`anchor-seed.ts`).\n * 이미 있는 파일에는 쓰이지 않는다 — 그쪽은 본문이 설치자 소유고 우리는 마커 블록만 책임진다.\n */\n projectContext?: string;\n}\n\n/**\n * Build the project-root CLAUDE.md: a real H1 project name + the shared fill scaffold. The\n * scaffold is track-agnostic — the installed-assets section is filled from real files at fill time.\n * 트랙 안내 줄(`> Active track(s):`)은 쓰지 않는다(#833) — 이 파일은 설치자 것이라 트랙을 더하거나\n * 빼도 갱신할 수 없고, 낡은 안내를 매 세션 읽히게 된다. 트랙의 SSOT 는 설치 기록이다(`list`).\n */\nexport function mergeProjectClaude(opts: MergeOptions): string {\n return `# ${opts.projectName}\\n\\n${opts.projectContext ?? renderFillScaffold()}\\n`;\n}\n\n/**\n * 루트 `CLAUDE.md` 에 하네스 앵커 import 를 **비파괴로** 얹는다 (P5 · ADR-060).\n *\n * v26.140.0 까지 설치는 이 파일을 `mergeProjectClaude()` 결과로 **통째 덮어썼다** — 백업은\n * 남겼지만, 사용자가 채워 넣은 프로젝트 맥락이 매 재설치마다 스캐폴드로 되돌아갔다. 하네스\n * 내용은 `HARNESS_ANCHOR_FILE` 로 따로 나가므로, 루트 CLAUDE.md 에는 그것을 끌어오는 한 줄만\n * 있으면 된다. 그래서 소유가 갈린다: 앵커 파일 = 하네스 소유(갱신·회수 가능), 루트 CLAUDE.md\n * = 사용자 소유(우리는 마커 블록만 책임진다).\n *\n * @param existing 디스크의 현재 내용. 파일이 없으면 `null`.\n * @returns 기록할 내용. 이미 import 가 있으면 **입력과 바이트 동일**(= 쓰지 않아도 된다).\n */\nexport function upsertHarnessImport(existing: string | null, opts: MergeOptions): string {\n const block = importBlock(opts.continuousSkills ?? []);\n if (existing === null) {\n return `${mergeProjectClaude(opts)}\\n${block}\\n`;\n }\n // ADR-085 — 마커 블록이 있으면 **그 안만** 현행으로 바꾼다. 블록 안은 하네스 소유라 사용자\n // 편집을 덮는 것이 아니고, 스킬을 더 깔거나 뺐을 때 안내가 따라와야 한다. 내용이 같으면\n // 입력을 그대로 돌려준다(= 파일을 만지지 않는다).\n const start = existing.indexOf(IMPORT_MARKER_START);\n if (start !== -1) {\n const endAt = existing.indexOf(IMPORT_MARKER_END, start);\n if (endAt === -1) return existing; // 깨진 블록 — 우리가 판정할 수 없다. 손대지 않는다.\n const current = existing.slice(start, endAt + IMPORT_MARKER_END.length);\n // 문자열 replace 는 치환문의 `$` 를 패턴으로 읽는다(리뷰 N-2) — slice 결합으로 그대로 넣는다.\n if (current === block) return existing;\n return `${existing.slice(0, start)}${block}${existing.slice(endAt + IMPORT_MARKER_END.length)}`;\n }\n if (hasHarnessImport(existing)) {\n // 사용자가 손으로 적은 import 줄 — 파일 전체가 사용자 소유라 블록을 얹지 않는다. 그때는\n // 상시 스킬 안내도 못 넣는다(관리 자리가 없다).\n return existing;\n }\n const body = existing.endsWith(\"\\n\") ? existing : `${existing}\\n`;\n return `${body}\\n${block}\\n`;\n}\n\n/**\n * 마커 블록만 도려낸다 — uninstall 이 사용자 본문을 지우지 않고 하네스 몫만 회수하는 경로.\n *\n * 블록 앞뒤로 우리가 넣었던 빈 줄까지 되돌리므로, 설치 전 내용이 그대로였다면 결과는\n * **설치 전과 바이트 동일**해진다.\n *\n * @returns 회수 후 내용. 마커가 없으면 `null` (= 우리가 건드린 적 없다 → 파일을 만지지 않는다).\n */\nexport function stripHarnessImport(text: string): string | null {\n // #628 — 마커가 한쪽이라도 있으면 우리가 넣은 블록이 손상된 것이다 — 정리 대상.\n // 양쪽 다 없는 파일만 \"우리가 건드린 적 없다\"(기존 계약: 건드리지 않는다).\n if (!text.includes(IMPORT_MARKER_START) && !text.includes(IMPORT_MARKER_END)) return null;\n const endsWithNewline = text.endsWith(\"\\n\");\n const out: string[] = [];\n const lines = text.split(\"\\n\");\n let i = 0;\n while (i < lines.length) {\n const line = lines[i] ?? \"\";\n if (line.includes(IMPORT_MARKER_START)) {\n // 블록 끝까지 통째로 — 끝 마커가 없으면(끊어진 블록) 시작 줄만 걷고 아래 스캔이\n // 남은 import 줄·고아 end 마커를 정리한다(#628: 앵커는 지워지므로 참조가 남으면 안 된다).\n let j = i;\n while (j < lines.length && !(lines[j] ?? \"\").includes(IMPORT_MARKER_END)) j++;\n i = j < lines.length ? j + 1 : i + 1;\n // #620 — 설치가 블록 **앞에** 넣은 빈 줄 하나만 되돌린다. 사용자의 원래 빈 줄을\n // 지우지 않는다(이전 구현은 \\n+ 를 통째로 접어 인접 빈 줄을 흡수했다).\n if (out.length > 0 && out[out.length - 1] === \"\") out.pop();\n continue;\n }\n if (line.includes(IMPORT_MARKER_END) || line.trim() === HARNESS_IMPORT_LINE) {\n // 고아 end 마커 · 마커 밖의 중복 import 줄(#628 복제 블록의 잔여)\n i++;\n continue;\n }\n out.push(line);\n i++;\n }\n // #620 — 파일 끝 개행 상태를 보존한다(강제로 붙이거나 떼지 않는다).\n let result = out.join(\"\\n\");\n if (endsWithNewline && result !== \"\" && !result.endsWith(\"\\n\")) result += \"\\n\";\n if (!endsWithNewline && result.endsWith(\"\\n\")) result = result.slice(0, -1);\n return result === text ? null : result;\n}\n\n/**\n * import 가 이미 사는가 — 마커 블록이거나, 사용자가 손으로 적은 실제 import 줄이거나.\n *\n * **코드펜스 안은 세지 않는다.** Claude Code 는 펜스 안의 `@path` 를 import 로 읽지 않으므로,\n * 문서에서 이 파일명을 예시로 인용한 것을 import 로 오인하면 진짜 import 를 영영 안 넣는다\n * (= 앵커가 조용히 안 실린다).\n */\nfunction hasHarnessImport(text: string): boolean {\n if (text.includes(IMPORT_MARKER_START)) return true;\n let inFence = false;\n for (const line of text.split(\"\\n\")) {\n if (/^\\s*(?:```|~~~)/.test(line)) {\n inFence = !inFence;\n continue;\n }\n if (!inFence && line.trim() === HARNESS_IMPORT_LINE) return true;\n }\n return false;\n}\n","import { existsSync, readFileSync } from \"node:fs\";\nimport { join, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { CONTINUOUS_SKILLS, EXTERNAL_ASSETS } from \"./external-assets.js\";\nimport { parseTrackMcpMap, selectableMcpServers } from \"./mcp-merge.js\";\nimport { renderFillScaffold, withContinuousSkillsNote } from \"./project-claude-merge.js\";\nimport type { CliTargets } from \"./types.js\";\n\n/**\n * v26.103.0 (ADR-032) — Session-Start Context Cost.\n *\n * 설치 자산이 세션 시작 시 상주시키는 descriptor(SKILL.md frontmatter: name + description)의\n * 토큰 추정치. NORTH_STAR §2 NSM \"Session-Start Context Cost\" 의 계측 구현.\n *\n * 측정 범위 (no-false-ship):\n * - repo-bundled internal 스킬만 실측 — frontmatter 문자수 → chars/4 근사 (표기는 항상 \"~\").\n * - 외부 자산(plugin/skill/npm/npx-run/shell-script)은 설치 시점에 frontmatter 를 알 수 없어\n * **unmeasured** 로 표기한다. 추정치를 실측처럼 표기하지 않는다.\n */\nexport const CHARS_PER_TOKEN = 4;\n\nexport function estimateTokens(chars: number): number {\n return Math.ceil(chars / CHARS_PER_TOKEN);\n}\n\n/**\n * 패키지 루트(= `templates/` 가 있는 곳) 해석. 이 파일은 src/ 최상위(dev)와 dist/ 번들 출력\n * 양쪽에서 한 단계 위가 패키지 루트라 동일 식이 성립한다 — `defaultHarnessRoot` 와 식은 같지만\n * 그쪽은 src/commands/ 아래라 dev 에선 틀려 테스트 주입이 필수인 것과 달리, 여기는 양쪽 유효.\n * `fileURLToPath` 필수 — `.pathname` 은 공백/비ASCII 경로를 percent-encoded 로 남겨\n * existsSync 가 전부 실패한다 (SOD 리뷰 F1).\n */\nexport function resolveBundleRoot(): string {\n return resolve(fileURLToPath(new URL(\".\", import.meta.url)), \"..\");\n}\n\n/** SKILL.md 선두의 frontmatter 블록(`---` ... `---`) 내용. 없으면 null. */\nexport function extractFrontmatter(content: string): string | null {\n // BOM/CRLF 허용 — Windows 클론(core.autocrlf)에서 전량 unmeasured 강등 방지 (SOD 리뷰 F7).\n const m = content.match(/^?---\\r?\\n([\\s\\S]*?)\\r?\\n---/);\n return m?.[1] ?? null;\n}\n\n/**\n * 자산 1개의 descriptor 토큰 추정. internal(repo-bundled) + 템플릿 실재 + frontmatter 존재일\n * 때만 숫자, 그 외 null(unmeasured). 표시용 계측이므로 파일 이상 시 throw 하지 않는다 —\n * 없는 값을 만들어내는 대신 unmeasured 로 강등된다.\n */\nexport function assetDescriptorTokens(\n assetId: string,\n root: string = resolveBundleRoot(),\n): number | null {\n const asset = EXTERNAL_ASSETS.find((a) => a.id === assetId);\n if (!asset || asset.method.kind !== \"internal\") return null;\n const skillMd = join(root, \"templates\", \"skills\", asset.method.key, \"SKILL.md\");\n if (!existsSync(skillMd)) return null;\n const fm = extractFrontmatter(readFileSync(skillMd, \"utf8\"));\n return fm === null ? null : estimateTokens(fm.length);\n}\n\n/**\n * v26.116.0 (ADR-043) — 발화(fired) 비용. 스킬이 실제로 트리거되면 body 전체가 컨텍스트에 들어온다.\n * descriptor 는 전원이 매 세션 무는 상주 비용이고 body 는 트리거될 때만 무는 비용이라 **단위가\n * 다르다** — 두 값을 더한 \"총합\"은 만들지 않는다 (근거 없는 가중 합산 금지).\n */\nexport function assetBodyTokens(\n assetId: string,\n root: string = resolveBundleRoot(),\n): number | null {\n const asset = EXTERNAL_ASSETS.find((a) => a.id === assetId);\n if (!asset || asset.method.kind !== \"internal\") return null;\n const skillMd = join(root, \"templates\", \"skills\", asset.method.key, \"SKILL.md\");\n if (!existsSync(skillMd)) return null;\n const content = readFileSync(skillMd, \"utf8\");\n const fm = extractFrontmatter(content);\n // frontmatter 가 없으면 파일 전체가 body. 있으면 닫는 `---` 이후만.\n if (fm === null) return estimateTokens(content.length);\n const close = content.indexOf(\"\\n---\", content.indexOf(fm) + fm.length);\n const body = close === -1 ? \"\" : content.slice(close + \"\\n---\".length);\n return estimateTokens(body.trim().length);\n}\n\nexport interface AssetCostRow {\n id: string;\n /** 상주 — 설치한 전원이 매 세션 무는 비용. */\n descriptorTokens: number | null;\n /** 발화 — 스킬이 트리거될 때만 무는 비용. */\n bodyTokens: number | null;\n}\n\n/**\n * 자산별 비용 행. **발화 비용 내림차순** 정렬 — \"무엇부터 검토할 것인가\"의 순서다 (ADR-043\n * 1단계: 값싼 전수 계측으로 순위를 세우고, 비싼 eval 은 상위에만). unmeasured(외부 자산)는 뒤로.\n */\nexport function assetCostRows(\n assetIds: ReadonlyArray<string>,\n root: string = resolveBundleRoot(),\n): AssetCostRow[] {\n return assetIds\n .map((id) => ({\n id,\n descriptorTokens: assetDescriptorTokens(id, root),\n bodyTokens: assetBodyTokens(id, root),\n }))\n .sort((a, b) => (b.bodyTokens ?? -1) - (a.bodyTokens ?? -1));\n}\n\n/**\n * v26.117.0 (ADR-044) — 상주 비용의 **표면 전체**.\n *\n * v26.116.0 까지는 스킬 descriptor 만 셌다. 그 정의에는 굿하트 구멍이 있다: SKILL.md 산문을\n * 룰 파일로 옮기면 **발화 시에만 내던 비용이 매 세션 상주로 바뀌어 실제로는 악화**되는데 지표는\n * 개선으로 표시된다. 사용자를 나쁘게 만드는 리팩터링을 보상하는 지표는 없느니만 못하다.\n *\n * 판정 기준은 **표면 열거가 아니라 \"상주인가 발화인가\"** — 새 표면이 생겨도 기준이 그대로다.\n * - 상주: rules(전문) · CLAUDE.md(전문) · skills/agents 의 descriptor\n * - 발화: skills/agents 의 body — 트리거될 때만\n * - 비대상: hooks — **단, 근거는 \"실행될 뿐\"이 아니다** (v26.137.0 정정). 훅 stdout 은 CLI 의\n * `content` 필드로만 컨텍스트에 진입하고, 그 필드는 훅이\n * `hookSpecificOutput.{additionalContext|initialUserMessage}` 를 쓰거나 stdout 이 **비-JSON 일 때만**\n * 채워진다. 우리 SessionStart 훅은 전자를 쓰므로 진입하되 양이 ~19 tok/세션(실측)이라 계측\n * 대상에서 뺀다 — **조건부 제외이지 구조적 제외가 아니다.** 훅이 늘거나 출력이 길어지면\n * 상주에 얹힌다. 외부 플러그인 훅은 여기 안 잡힌다: 올바른 스키마를 쓰는 플러그인 훅 하나가\n * 실측상 +2,222 tok/세션(+37.8%)을 아무 고지 없이 얹을 수 있었다.\n */\n/**\n * **상주 비용은 한 숫자가 아니다 (ADR-083, 사용자 확정 2026-08-30).**\n *\n * 하나로 합치면 정반대 행동을 요구하는 둘이 섞인다:\n * - **지시문**(룰 + CLAUDE.md) — 우리가 \"항상 읽혀라\"고 정해서 넣은 것. 중복이거나 모델이\n * 이미 아는 것이면 **빼는 것이 순수 이득**이다.\n * - **발화 표면**(스킬·에이전트 descriptor) — 자산이 존재하면 따라온다. 줄이려면 자산을\n * 빼거나 설명을 깎아야 하는데 **깎으면 안 불린다**. #333 에서 상한을 맞추다 트리거 문구\n * 6개가 소실됐고 #399 에서 되살렸다 — descriptor 는 비용이 아니라 **발화 조건**이다.\n *\n * 합쳐 두면 **가치 있는 스킬을 추가할 때 지표가 나빠진다**. 좋은 일을 하면 숫자가 나빠지는\n * 지표는 판단을 도와주지 않는다. 그래서 축을 가르고, 판정도 따로 한다 —\n * 지시문은 줄이는 방향으로 ratchet, 발화 표면은 **기존 자산의 조용한 증가만** 막는다\n * (새 자산 추가는 선택이지 악화가 아니다).\n *\n * **관리 대상은 우리가 만든 것뿐이다.** 외부 자산은 설치 시점에 내용을 알 수 없어 여기 안\n * 들어오고, 리포트가 \"미계측 N종\"으로 **알려만 준다**(no-false-ship).\n */\nexport interface ResidentCost {\n rules: number;\n projectClaudeMd: number;\n /** `projectClaudeMd`/`items.claudeMd` 가 가리키는 **실제 파일 이름** — CLI 따라 `CLAUDE.md` · `AGENTS.md` (#615). */\n contextFile: string;\n /**\n * #615 남은 틈 ① — 룰이 `contextFile` 안에 인라인으로 들어 있어 **그 파일 토큰에 이미 포함**됐다\n * (`rules` 토큰은 0). `\"planned\"` = 이번 실행이 그 파일을 다시 쓰지 않아 룰 수가 계획 값이다.\n */\n rulesInline?: \"rendered\" | \"planned\";\n /** #615 남은 틈 — `contextFile` 숫자가 무엇을 쟀는지(또는 못 쟀는지) 화면에 붙이는 한 구절. */\n contextNote?: string;\n skillDescriptors: number;\n agentDescriptors: number;\n total: number;\n /** 같은 표면의 **개수**. 토큰과 단위가 다르므로 절대 같은 필드에 섞지 않는다 (아래 주석). */\n items: ResidentItemCount;\n /**\n * 우리가 \"항상 읽혀라\"고 쓴 지시문 — 룰 + CLAUDE.md.\n * **작을수록 좋은 축이 아니다**(사용자 확정 2026-08-30). 판단은 크기가 아니라\n * *불필요한 지시문이 있나*로 한다. ratchet 이 지키는 것은 **조용한 증가 차단**이다.\n */\n directive: ResidentAxis;\n /** 우리 스킬·에이전트가 발화하려고 상주시키는 descriptor. **정확도와 함께 보는 축**. */\n firing: ResidentAxis;\n /** 스킬 id → descriptor 토큰. 기존 스킬의 조용한 증가를 잡는 ratchet 의 입력. */\n perSkillDescriptor: Readonly<Record<string, number>>;\n}\n\n/**\n * 상주 비용의 **한 축**. 개수와 토큰을 같이 들고 다닌다 — 둘을 따로 두면 한쪽만 갱신되는\n * drift 가 난다(v26.140.0 에 실제로 났다).\n */\nexport interface ResidentAxis {\n items: number;\n tokens: number;\n}\n\n/**\n * 네 범주 값에서 **축을 derive** 한다. 축을 손으로 적는 자리를 만들지 않는다 — #320 이 정확히\n * 그렇게 났다(같은 조립을 여러 곳이 각자 하다 한 곳이 틀렸다). 테스트 픽스처도 이걸 쓴다.\n */\nexport function makeResidentCost(base: {\n rules: number;\n projectClaudeMd: number;\n skillDescriptors: number;\n agentDescriptors: number;\n items: ResidentItemCount;\n perSkillDescriptor?: Readonly<Record<string, number>>;\n contextFile?: string;\n rulesInline?: \"rendered\" | \"planned\";\n contextNote?: string;\n}): ResidentCost {\n return {\n ...base,\n contextFile: base.contextFile ?? \"CLAUDE.md\",\n perSkillDescriptor: base.perSkillDescriptor ?? {},\n total: base.rules + base.projectClaudeMd + base.skillDescriptors + base.agentDescriptors,\n directive: {\n items: base.items.rules + base.items.claudeMd,\n tokens: base.rules + base.projectClaudeMd,\n },\n firing: {\n items: base.items.skills + base.items.agents,\n tokens: base.skillDescriptors + base.agentDescriptors,\n },\n };\n}\n\n/**\n * v26.140.0 — 상주 비용의 **양(quantity) 축**.\n *\n * 토큰은 버리지 않되(부수 표시값 + ratchet 유지) 1차 축은 **개수**다. 근거: ADR-051 실측에서\n * 토큰의 금전 비용은 무의미했고($2.94/1k요청 · 컨텍스트 0.59%), 실제로 아픈 비용 — 교차참조,\n * 서로 모순되는 지시, 문서 drift, 유지보수 — 는 **항목 수**에 비례한다. 5,000 토큰이 룰 1개일\n * 때와 룰 20개일 때는 같은 비용이 아니다.\n *\n * 세는 대상은 토큰을 재는 대상과 **정확히 같다** (한쪽만 늘어나는 drift 방지):\n * rules 1파일 = 1 · skills 1디렉터리 = 1 · agents 1파일 = 1 · CLAUDE.md **2**(앵커 + 스캐폴드).\n * hooks 는 여기서도 비대상 — 상주 표면이 아니다.\n */\nexport interface ResidentItemCount {\n rules: number;\n skills: number;\n agents: number;\n claudeMd: number;\n total: number;\n}\n\n/** 파일 1개의 토큰. 없으면 0 (합계를 오염시키지 않되 throw 하지 않는다). */\nfunction fileTokens(path: string): number {\n return existsSync(path) ? estimateTokens(readFileSync(path, \"utf8\").trim().length) : 0;\n}\n\n/** descriptor(frontmatter)만. frontmatter 가 없으면 0 — 상주 비용이 아니다. */\nfunction descriptorTokens(path: string): number {\n if (!existsSync(path)) return 0;\n const fm = extractFrontmatter(readFileSync(path, \"utf8\"));\n return fm === null ? 0 : estimateTokens(fm.length);\n}\n\n/**\n * 앵커(원칙 본문 + 프로젝트 맥락 스캐폴드)가 **어느 파일에 어떤 모양으로** 깔리는가 (#615).\n * 정의의 출처는 `docs/CONTEXT-FILES.md` §3 — 여기서 새로 정하지 않는다.\n */\nexport function anchorSurface(cli: CliTargets): {\n label: string;\n files: 1 | 2;\n scaffold: \"claude\" | \"agents-md\";\n} {\n if (cli.includes(\"claude\") || cli.length === 0) {\n return { label: \"CLAUDE.md\", files: 2, scaffold: \"claude\" };\n }\n // 비-Claude CLI 는 같은 `AGENTS.md` 를 읽는다 — 섞여도 한 파일이다(ADR-103: Antigravity 의 옛 앵커 파일은 은퇴).\n return { label: \"AGENTS.md\", files: 1, scaffold: \"agents-md\" };\n}\n\n/**\n * 설치 계획(manifest 엔트리)에서 상주 비용을 실측. `applies` 로 이미 걸러진 엔트리를 받으므로\n * **트랙별 실제 설치분**이 반영된다 (templates/ 전체 합계 같은 부풀린 수치가 아니다).\n *\n * `file` 은 **잴 대상이 배포판이 아닌 경우**의 탈출구다 (#458). update 는 갱신 **후 디스크**를\n * 재야 한다: 트랙에서 강등·은퇴한 에이전트는 manifest 에 없는데 파일은 남아 매 세션 상주하므로,\n * 계획으로 재면 화면 숫자가 그 설치본의 실제보다 작다. 파일 자산은 파일 경로, `skills` 는\n * 디렉터리 경로를 준다(엔트리 `source` 와 같은 단위). 안 주면 지금처럼 `templates/<source>` 다.\n *\n * `contextFiles` 는 같은 탈출구의 CLAUDE.md 행 판이다 (#615 사례 4). update 가 끝난 뒤에는 앵커와\n * 루트 `CLAUDE.md` 가 디스크에 있고, 설치자가 키운 본문까지 매 세션 읽힌다 — 템플릿 상수로 재면\n * 그 본문이 빠진다. 주면 그 파일들(있는 것만)을 재고, 안 주면 지금처럼 템플릿 + 스캐폴드다.\n * 아직 디스크에 없는 내용(설치 전 헤더가 예측한 루트 `CLAUDE.md`, #615 남은 틈 ②)은 `{ text }` 로 준다.\n */\nexport function residentCost(\n entries: ReadonlyArray<{ source: string; target: string; file?: string }>,\n root: string = resolveBundleRoot(),\n cli: CliTargets = [\"claude\"],\n contextFiles?: ReadonlyArray<string | { text: string }>,\n): ResidentCost {\n const tpl = (source: string): string => join(root, \"templates\", source);\n const measured = (e: { source: string; file?: string }): string => e.file ?? tpl(e.source);\n const perSkill: Record<string, number> = {};\n let rules = 0;\n let skillDescriptors = 0;\n let agentDescriptors = 0;\n let ruleItems = 0;\n let skillItems = 0;\n let agentItems = 0;\n for (const e of entries) {\n // 토큰과 개수를 **같은 분기에서** 올린다 — 분기를 나누면 둘이 다른 대상을 세게 된다.\n if (e.target.startsWith(\".claude/rules/\")) {\n rules += fileTokens(measured(e));\n ruleItems += 1;\n } else if (e.target.startsWith(\".claude/agents/\")) {\n agentDescriptors += descriptorTokens(measured(e));\n agentItems += 1;\n } else if (e.target.startsWith(\".claude/skills/\")) {\n // skills 엔트리는 **항상 디렉터리**다 — `tests/skill-registration-uniform.test.ts` 가\n // 그것을 강제한다(#409). 파일 단위 등록을 받아 주던 분기는 통일과 함께 걷었다:\n // 일어날 수 없는 상태에 방어를 두면 다음 사람이 그 형태가 지원된다고 읽는다.\n const t = descriptorTokens(join(measured(e), \"SKILL.md\"));\n skillDescriptors += t;\n skillItems += 1;\n // 기존 스킬의 **조용한 증가**를 잡으려면 id 별 값이 필요하다 — 총합만으로는 새 스킬\n // 추가와 기존 스킬 팽창을 구분할 수 없고, 그 둘은 판정이 정반대다.\n const id = /\\.claude\\/skills\\/([^/]+)/.exec(e.target)?.[1];\n if (id !== undefined) perSkill[id] = (perSkill[id] ?? 0) + t;\n }\n }\n // 설치가 상주시키는 CLAUDE.md 는 **둘**이다. v26.140.0 까지는 앵커만 재면서 라벨은\n // \"스캐폴드\"였다 — 이름과 실측 대상이 다르면 그 지표로는 before/after 를 판정할 수 없다.\n // ① 하네스 앵커 `CLAUDE-uzys-harness.md` (루트) — manifest `applies: all` 이라 항상 깔린다.\n // v26.140.0 까지는 `.claude/CLAUDE.md` 였다 (P5 · ADR-060 에서 루트로 이동). 어느 쪽이든\n // **원본은 `templates/CLAUDE.md` 하나**라 여기서 재는 값은 이동과 무관하다.\n // ② 프로젝트 스캐폴드 루트 `CLAUDE.md` — manifest 밖에서 `writeRootClaudeMd()` 가 쓴다\n // (없으면 스캐폴드 생성, 있으면 앵커 import 한 줄 추가). **파일이 아니라 생성물**이라\n // `fileTokens` 로는 영원히 0 이었다.\n // ②는 `renderFillScaffold()` 만 잰다 — `mergeProjectClaude()` 의 머리 1줄(프로젝트명)은\n // 설치처마다 길이가 달라 ratchet 축으로 못 쓴다. 그만큼 이 값은 **하한**이다.\n const harnessAnchor = fileTokens(join(root, \"templates\", \"CLAUDE.md\"));\n // #615 — **그 CLI 가 실제로 읽는 앵커 파일**을 잰다. claude 를 안 고른 설치는 루트 `CLAUDE.md` 도\n // `CLAUDE-uzys-harness.md` 도 만들지 않는다(`docs/CONTEXT-FILES.md` §3): Codex · OpenCode · Antigravity 는\n // `AGENTS.md` 한 파일에 앵커 본문 + 스캐폴드를 담는다(ADR-103). 그래서 그 경우는 **파일 1개**이고 이름이 CLI 따라 바뀐다 — 상수\n // `CLAUDE.md 2` 를 그대로 찍던 것이 사례 1 이다. claude 가 섞이면 claude 몫이 기준이다(다른 CLI 의\n // 앵커를 더하지 않는다 — 비용은 세션 하나가 무는 값이다).\n const surface = anchorSurface(cli);\n // ②는 claude 에서는 루트 `CLAUDE.md`, 나머지에서는 앵커 파일 안의 `{PROJECT_CONTEXT}` 다.\n // ADR-085 — 스캐폴드 뒤에 붙는 상시 스킬 안내까지 잰다. 전 스킬 선택 기준(상한) — 안내는\n // 깔린 스킬만 적으므로 실제 설치본은 이보다 작거나 같다.\n const projectScaffold = estimateTokens(\n withContinuousSkillsNote(\n renderFillScaffold(surface.scaffold),\n CONTINUOUS_SKILLS.map((s) => s.id),\n ).trim().length,\n );\n // 토큰이 0 인 쪽은 항목도 0 (한쪽만 세면 그게 곧 drift). 앵커는 부재할 수 있고,\n // 스캐폴드는 코드 생성물이라 부재할 수 없다.\n // claude 는 두 파일(앵커 + 루트 CLAUDE.md), 나머지는 한 파일이다.\n const onDisk = contextFiles\n ?.map((f) => (typeof f === \"string\" ? fileTokens(f) : estimateTokens(f.text.trim().length)))\n .filter((t) => t > 0);\n const projectClaudeMd =\n onDisk === undefined ? harnessAnchor + projectScaffold : onDisk.reduce((a, b) => a + b, 0);\n const claudeMdItems =\n onDisk !== undefined\n ? onDisk.length\n : surface.files === 1\n ? 1\n : (harnessAnchor > 0 ? 1 : 0) + (projectScaffold > 0 ? 1 : 0);\n // 축은 `makeResidentCost` 하나가 derive 한다 — 여기서 다시 조립하면 사본이 둘이 된다.\n return makeResidentCost({\n rules,\n projectClaudeMd,\n skillDescriptors,\n agentDescriptors,\n perSkillDescriptor: perSkill,\n contextFile: surface.label,\n items: {\n rules: ruleItems,\n skills: skillItems,\n agents: agentItems,\n claudeMd: claudeMdItems,\n total: ruleItems + skillItems + agentItems + claudeMdItems,\n },\n });\n}\n\nexport interface ContextCostSummary {\n /** 실측된 스킬들의 토큰 합 (추정치). */\n measuredTokens: number;\n measuredCount: number;\n /** 외부 자산(ADR-032) — 설치 시점에 콘텐츠를 모른다. */\n unmeasuredCount: number;\n /** #856 — MCP 서버 행. 세션마다 서버가 주는 tool schema 가 상주하지만 설치 시점엔 잴 수 없다(ADR-044 §4). */\n mcpUnmeasuredCount: number;\n}\n\n/** #856 — 트랙 표(`templates/track-mcp-map.tsv`)의 고를 수 있는 MCP 서버 행인 자산 id. */\nfunction mcpServerAssetIds(root: string): Set<string> {\n const path = join(root, \"templates\", \"track-mcp-map.tsv\");\n if (!existsSync(path)) return new Set();\n return new Set(selectableMcpServers(parseTrackMcpMap(readFileSync(path, \"utf8\"))).values());\n}\n\nexport function summarizeContextCost(\n assetIds: ReadonlyArray<string>,\n root: string = resolveBundleRoot(),\n): ContextCostSummary {\n let measuredTokens = 0;\n let measuredCount = 0;\n let unmeasuredCount = 0;\n let mcpUnmeasuredCount = 0;\n const mcp = mcpServerAssetIds(root);\n for (const id of assetIds) {\n const tokens = assetDescriptorTokens(id, root);\n if (tokens === null) {\n // #856 — 미계측은 둘로 가른다(ADR-044 §4). MCP 서버 행은 tool schema 가 상주하므로 따로 센다 — \"external\" 이라\n // 부르지 않는다(ADR-032 의 external = 외부 자산). 그 밖의 internal(ci-scaffold 류)은 세션에 상주시키는 것이 없어 세지 않는다.\n if (mcp.has(id)) mcpUnmeasuredCount += 1;\n else if (EXTERNAL_ASSETS.find((a) => a.id === id)?.method.kind !== \"internal\")\n unmeasuredCount += 1;\n } else {\n measuredTokens += tokens;\n measuredCount += 1;\n }\n }\n return { measuredTokens, measuredCount, unmeasuredCount, mcpUnmeasuredCount };\n}\n\n/**\n * 표시 라인. wizard confirm 과 비대화형 header 가 동일 문구를 쓴다 (표면별 상이 문구 금지 —\n * v26.88.0 이중 고지 사고의 교훈). 자산 0개면 null.\n */\nexport function formatContextCostLine(s: ContextCostSummary): string | null {\n if (s.measuredCount === 0 && s.unmeasuredCount === 0) return null;\n if (s.measuredCount === 0) {\n return `session-start context cost: unmeasured (${s.unmeasuredCount} external asset${s.unmeasuredCount === 1 ? \"\" : \"s\"})`;\n }\n const skills = `${s.measuredCount} bundled skill${s.measuredCount === 1 ? \"\" : \"s\"} measured`;\n const external = s.unmeasuredCount > 0 ? ` · ${s.unmeasuredCount} external unmeasured` : \"\";\n return `session-start context cost: ~${s.measuredTokens} tokens (${skills}${external})`;\n}\n\n/**\n * v26.117.0 (ADR-044) — 설치 요약에 표시하는 상주 비용 라인.\n *\n * 이전 라인(`formatContextCostLine`)은 **스킬 descriptor 만** 세면서 \"session-start context\n * cost\" 라고 표기해, 실측상 상주 비용의 ~10% 를 전부인 양 보여주고 있었다 (tooling 트랙 실측:\n * 스킬 descriptor ~547 vs 상주 합계 ~5,194). 표시 숫자가 실제의 9분의 1이면 그것은 계측이\n * 아니라 오보다 (no-false-ship). 내역을 함께 보여 어디에 비용이 있는지 드러낸다.\n *\n * v26.140.0 — **개수가 먼저, 토큰이 뒤.** 1차 축이 항목 수로 바뀌었으므로 읽는 순서도 그대로\n * 따라간다 (표면마다 순서가 다르면 그 자체가 혼선이다 — cost:report 도 같은 순서).\n *\n * 이 함수가 상주 비용 **표시의 단일 출처**다. 표면(설치 헤더 / wizard confirm / cost:report)이\n * 각자 문자열을 조립하면 한 표면만 갱신되는 drift 가 다시 생긴다 — 지난번에 개수가 리포트에만\n * 붙고 나머지 둘에 없던 것이 정확히 그 형태였다. 계약은\n * `tests/context-cost-display-parity.test.ts` 가 글롭으로 강제한다.\n */\nexport function formatResidentCostLine(\n r: ResidentCost,\n unmeasured: Pick<ContextCostSummary, \"unmeasuredCount\" | \"mcpUnmeasuredCount\">,\n): string | null {\n const { unmeasuredCount, mcpUnmeasuredCount } = unmeasured;\n if (r.total === 0) return null;\n // ADR-083 — 내역을 **두 축으로 묶어** 보여준다. 총합은 그대로 낸다(설치자는 전부를 문다).\n // 달라지는 것은 \"그중 무엇을 줄일 수 있는가\"가 한눈에 갈린다는 것이다:\n // directives 는 우리가 쓴 상시 지시문, triggers 는 스킬·에이전트가 발화하려고 상주시키는\n // descriptor 다. **둘 다 작을수록 좋은 값이 아니다** — 앞은 \"불필요한 것이 있나\", 뒤는\n // \"깎으면 안 불린다\"로 판단이 다르다. 갈라 보여 주는 이유가 그것이다.\n // #615 남은 틈 — 룰이 앵커 파일 안에 인라인이면 토큰은 그 파일 숫자에 들어 있다(두 번 세지 않는다).\n const rules = r.rulesInline\n ? `rules ${r.items.rules} inside ${r.contextFile}${r.rulesInline === \"planned\" ? \" (planned count)\" : \"\"}`\n : `rules ${r.items.rules} ~${r.rules}`;\n const note = r.contextNote ? ` ${r.contextNote}` : \"\";\n const parts = [\n `directives ${r.directive.items} ~${r.directive.tokens}`,\n `[${rules} · ${r.contextFile} ${r.items.claudeMd} ~${r.projectClaudeMd}${note}]`,\n `triggers ${r.firing.items} ~${r.firing.tokens}`,\n `[skills ${r.items.skills} ~${r.skillDescriptors} · agents ${r.items.agents} ~${r.agentDescriptors}]`,\n ].join(\" · \");\n const external =\n unmeasuredCount > 0\n ? ` · ${unmeasuredCount} external asset${unmeasuredCount === 1 ? \"\" : \"s\"} unmeasured`\n : \"\";\n // #856 (ADR-044 §4) — MCP 서버가 세션마다 주는 tool schema 는 상주하지만 설치 시점엔 잴 수 없다 — 숫자에 없다는 것을 말한다\n const mcp =\n mcpUnmeasuredCount > 0\n ? ` · ${mcpUnmeasuredCount} MCP server tool schema${mcpUnmeasuredCount === 1 ? \"\" : \"s\"} unmeasured`\n : \"\";\n return `session-start context cost: ${r.items.total} items resident · ~${r.total} tokens/session (${parts}${external}${mcp})`;\n}\n\n/**\n * cost:report 상주 표의 행 정의. `gap` = 라벨 뒤 공백 수 — 한글 라벨이 2배폭이라 `padEnd` 로는\n * 정렬이 안 맞아 행마다 명시한다.\n */\nconst RESIDENT_ROWS: ReadonlyArray<{\n label: string;\n gap: number;\n count: (r: ResidentCost) => number;\n tokens: (r: ResidentCost) => number;\n}> = [\n {\n // ADR-083 — 축 소계를 **행으로** 낸다. 소계가 없으면 읽는 사람이 네 줄을 머리로 더해야\n // 하고, 그러면 \"무엇을 줄여야 하나\"가 표에서 안 보인다.\n label: \"지시문\",\n gap: 13,\n count: (r) => r.directive.items,\n tokens: (r) => r.directive.tokens,\n },\n { label: \" rules\", gap: 12, count: (r) => r.items.rules, tokens: (r) => r.rules },\n {\n // 한 행에 두 파일(앵커 + 스캐폴드). 개수 열이 2 를 뱉어 그 사실을 드러낸다 — 라벨로\n // 하나만 이름 붙이면 v26.140.0 이전과 같은 거짓 라벨이 된다.\n label: \" CLAUDE.md\",\n gap: 8,\n count: (r) => r.items.claudeMd,\n tokens: (r) => r.projectClaudeMd,\n },\n {\n label: \"발화 표면\",\n gap: 10,\n count: (r) => r.firing.items,\n tokens: (r) => r.firing.tokens,\n },\n {\n label: \" skill descriptors\",\n gap: 0,\n count: (r) => r.items.skills,\n tokens: (r) => r.skillDescriptors,\n },\n {\n label: \" agent descriptors\",\n gap: 0,\n count: (r) => r.items.agents,\n tokens: (r) => r.agentDescriptors,\n },\n];\n\n/**\n * v26.140.0 — `npm run cost:report` 의 상주 표. 한 줄 라인과 **같은 함수 가족**에 둔다.\n *\n * 스크립트가 자기 표를 직접 조립하던 동안 개수 열은 리포트에만 있고 설치 표면 둘에는 없었다.\n * 숫자 렌더를 여기로 모아 두면 표면이 늘어도 개수/토큰이 함께 따라간다.\n */\nexport function formatResidentCostBlock(r: ResidentCost): string[] {\n const n = (v: number): string => String(v).padStart(3);\n return [\n \" 개수 토큰\",\n ...RESIDENT_ROWS.map(\n (row) => ` ${row.label}${\" \".repeat(row.gap)}${n(row.count(r))}개 ~${row.tokens(r)}`,\n ),\n \" ─────────────────────────────────\",\n ` 상주 합계 ${n(r.items.total)}개 상주 · ~${r.total} tokens/세션`,\n \"\",\n \" 지시문 = 우리가 항상 읽히게 넣은 것. **작을수록 좋은 값이 아니다** — 판단은\",\n \" '불필요한 지시문이 있나'로 한다. ratchet 은 조용한 증가만 막는다.\",\n \" 발화 표면 = 스킬·에이전트가 불리려고 상주시키는 descriptor. 깎으면 안 불린다.\",\n ];\n}\n","import { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { keyId } from \"./adapters/index.js\";\nimport { jsonSha } from \"./adapters/json-keys.js\";\nimport { EXTERNAL_ASSETS, isAssetSelected } from \"./external-assets.js\";\nimport type { InstallLog } from \"./install-log.js\";\nimport { priorMcpValues } from \"./mcp-prior.js\";\nimport { anyTrack } from \"./track-match.js\";\nimport type { Track } from \"./types.js\";\n\n/**\n * 하네스 MCP 서버 하나 — 내부 모델(#711 · ADR-106). 세 렌더러(`.mcp.json` · Codex · OpenCode)가 각자 형식으로 낸다.\n * - stdio: 지금까지의 모양(Claude `.mcp.json` 형식과 같다)\n * - http: 원격(Streamable HTTP) 서버. `auth.bearerEnv` = `Authorization: Bearer` 를 **환경변수 이름**으로만 둔다 — 값은 어느\n * 파일에도 쓰지 않는다(Claude `${VAR}` · Codex `bearer_token_env_var` · OpenCode `{env:VAR}` 가 실행 때 읽는다).\n */\nexport type McpServerConfig =\n | { type?: \"stdio\"; command: string; args: string[]; env?: Record<string, string> }\n | { type: \"http\"; url: string; auth?: { bearerEnv: string } };\n\nexport interface McpJson {\n mcpServers: Record<string, McpServerConfig>;\n _comment?: string;\n}\n\nexport interface TrackMcpRow {\n name: string;\n /** 기본 트랙 패턴. `-` 는 어떤 트랙과도 맞지 않는다 — 같은 이름의 카탈로그 `internal` 자산으로만 고른다. */\n pattern: string;\n server: McpServerConfig;\n}\n\n/** 환경변수 이름 — 파일에 쓰는 것은 이 이름뿐이라 셸이 그대로 읽는 모양만 받는다. */\nconst ENV_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;\n\n/** 원격 행의 4열(`{\"url\":\"https://…\",\"bearerEnv\"?:\"NAME\"}`) — 아니면 null(행을 버린다). */\nfunction parseRemote(raw: string): McpServerConfig | null {\n let v: unknown;\n try {\n v = JSON.parse(raw);\n } catch {\n return null;\n }\n if (v === null || typeof v !== \"object\" || Array.isArray(v)) return null;\n const { url, bearerEnv } = v as { url?: unknown; bearerEnv?: unknown };\n if (typeof url !== \"string\" || !url.startsWith(\"https://\")) return null;\n if (bearerEnv === undefined) return { type: \"http\", url };\n if (typeof bearerEnv !== \"string\" || !ENV_NAME.test(bearerEnv)) return null;\n return { type: \"http\", url, auth: { bearerEnv } };\n}\n\n/**\n * Parse `templates/track-mcp-map.tsv` (tab-separated, comment-aware). 3열이 리터럴 `url` 이면 원격 행이다(4열 = JSON 객체).\n * 깨진 행은 버린다.\n */\nexport function parseTrackMcpMap(raw: string): TrackMcpRow[] {\n const rows: TrackMcpRow[] = [];\n for (const line of raw.split(/\\r?\\n/)) {\n const trimmed = line.trim();\n if (!trimmed || trimmed.startsWith(\"#\")) {\n continue;\n }\n const parts = line.split(\"\\t\");\n if (parts.length < 4) {\n continue;\n }\n const [name, pattern, command, argsJson] = parts;\n if (!name || !pattern || !command) {\n continue;\n }\n if (command === \"url\") {\n const server = parseRemote(argsJson ?? \"\");\n if (server !== null) rows.push({ name, pattern, server });\n continue;\n }\n let args: unknown;\n try {\n args = JSON.parse(argsJson ?? \"[]\");\n } catch {\n continue;\n }\n if (!Array.isArray(args) || !args.every((a) => typeof a === \"string\")) {\n continue;\n }\n rows.push({ name, pattern, server: { type: \"stdio\", command, args: args as string[] } });\n }\n return rows;\n}\n\n/**\n * 내부 모델 → Claude Code `.mcp.json` 값. `.mcp.json` 에 쓰는 자리 · 그 값과 대조하는 자리(옛 로그 식별 · uninstall 잔존\n * 안내)만 이것을 거친다. 원격은 `type: \"http\"` 가 필수다(공식: `url` 만 있고 `type` 이 없으면 설정 오류).\n */\nexport function toClaudeServer(cfg: McpServerConfig): Record<string, unknown> {\n if (cfg.type === \"http\") {\n return cfg.auth === undefined\n ? { type: \"http\", url: cfg.url }\n : {\n type: \"http\",\n url: cfg.url,\n headers: { Authorization: `Bearer \\${${cfg.auth.bearerEnv}}` },\n };\n }\n const out: Record<string, unknown> = {\n type: cfg.type ?? \"stdio\",\n command: cfg.command,\n args: cfg.args,\n };\n if (cfg.env && Object.keys(cfg.env).length > 0) out.env = { ...cfg.env };\n return out;\n}\n\n/**\n * Apply track-aware MCP rows to a base `.mcp.json` object.\n * Existing entries (including user customizations) are preserved.\n */\nexport function mergeMcpServers(\n base: McpJson,\n rows: ReadonlyArray<TrackMcpRow>,\n tracks: ReadonlyArray<Track>,\n /** #709 — 트랙 패턴 밖이어도 넣을 행 이름(`selectedMcpServers`). 기본 행과 겹치면 한 항목이다. */\n chosen: ReadonlyArray<string> = [],\n /** #709 리뷰 NOTE 1 — 설치자가 명시적으로 뺀 행 이름(`droppedMcpServers`). 기본 행이어도 넣지 않는다. */\n dropped: ReadonlyArray<string> = [],\n): McpJson {\n const out: McpJson = {\n ...base,\n mcpServers: { ...base.mcpServers },\n };\n for (const row of rows) {\n if (dropped.includes(row.name)) {\n continue;\n }\n if (!anyTrack(tracks, row.pattern) && !chosen.includes(row.name)) {\n continue;\n }\n if (out.mcpServers[row.name]) {\n // Preserve existing — do not overwrite user customizations.\n continue;\n }\n out.mcpServers[row.name] = row.server;\n }\n // Strip _comment marker (parity with the bash `jq 'del(._comment)'`).\n delete out._comment;\n return out;\n}\n\n/**\n * 하네스가 이 트랙에 까는 `.mcp.json` 서버 — 템플릿 + 트랙 표. **설치자 파일과 합치지 않는다**:\n * 설치자 파일에 하네스 몫을 더하는 일은 `json-keys` 어댑터가 한다(#551 PR-3). 여기서 합치던 판은\n * 설치자 파일을 못 읽으면 템플릿으로 덮었다(#574) — 합치는 자리를 하나로 줄여 그 경로가 없다.\n */\nexport function composeMcpJson(opts: {\n templateMcpPath: string;\n trackMapPath: string;\n tracks: ReadonlyArray<Track>;\n /** #709 — 트랙 패턴 밖에서 넣을 행 이름을 표에서 고른다(`selectedMcpServers` · uninstall 은 고를 수 있는 행 전부). */\n chosen: (rows: ReadonlyArray<TrackMcpRow>) => ReadonlyArray<string>;\n /** #709 리뷰 NOTE 1 — 기본 행이어도 뺄 행 이름. 없으면 아무것도 안 뺀다. */\n dropped?: (rows: ReadonlyArray<TrackMcpRow>) => ReadonlyArray<string>;\n}): McpJson {\n const base = JSON.parse(readFileSync(opts.templateMcpPath, \"utf8\")) as McpJson;\n const mapRaw = existsSync(opts.trackMapPath) ? readFileSync(opts.trackMapPath, \"utf8\") : \"\";\n const rows = parseTrackMcpMap(mapRaw);\n return mergeMcpServers(base, rows, opts.tracks, opts.chosen(rows), opts.dropped?.(rows) ?? []);\n}\n\n/**\n * #709 (ADR-101) — 어느 트랙에서든 **고를 수 있는** 트랙 표 행(서버 이름 → 자산 id): 카탈로그 `internal` 자산의 `key` 가\n * 행 이름인 것. 대응은 이 한 규칙뿐이다 — 따로 표를 두지 않는다. 행의 패턴은 기본 트랙을 정하고, 이 자산은 그 밖의\n * 트랙에서 위저드 체크 · `--with <id>` 로 고르게 한다. 서버 정의(명령 · 인자)는 여전히 트랙 표 한 곳에 있다.\n */\nexport function selectableMcpServers(rows: ReadonlyArray<TrackMcpRow>): Map<string, string> {\n const names = new Set(rows.map((r) => r.name));\n const out = new Map<string, string>();\n for (const a of EXTERNAL_ASSETS) {\n if (a.method.kind === \"internal\" && names.has(a.method.key)) out.set(a.method.key, a.id);\n }\n return out;\n}\n\n/** 이 실행에서 무엇을 골랐나 — `selectedMcpServers` 의 입력. */\nexport interface McpChoice {\n /** 이번 선택 — `isAssetSelected` 와 같은 입력(트랙 · `--with`/위저드 체크). */\n spec: Parameters<typeof isAssetSelected>[1];\n /** 이 실행 뒤의 누적 빼기(install = `thisRunExclusions`, update = 기록의 `excludedIds`). */\n excluded: ReadonlySet<string>;\n previousLog: InstallLog | null;\n /**\n * #722 (ADR-106, 설계 검증 B1 ⓐ) — 옛 로그(몫 기록 전)의 `.mcp.json` 을 읽을 프로젝트. 주면 하네스가 **만든** 그 파일에 하네스가\n * 쓴 값 그대로 남은 고를 수 있는 서버를 기록된 몫처럼 선택으로 본다(`legacyMcpServers`). 생략 = 그 근거를 쓰지 않는다.\n */\n projectDir?: string;\n}\n\n/**\n * #709 — 트랙 패턴 밖에서 이번 렌더에 넣을 선택 행 이름. 둘 다 만족해야 한다:\n *\n * ⓐ 설치자가 빼지 않았다 — 자산 id(`railway-mcp-server`)로도 키 id(`mcp:railway-mcp-server`, ADR-099 R4)로도(`droppedMcpServers`).\n * ⓑ 이번 실행이 골랐거나(위저드 체크 · `--with`), **기록**이 그 서버를 하네스 몫으로 적었다(`.mcp.json` 몫). 디스크\n * 존재는 근거가 아니다(ADR-096) — 기록 조항이 없으면 렌더에 없는 기록 키를 `planUpsert` 가 지운다(조용한 삭제).\n */\nexport function selectedMcpServers(rows: ReadonlyArray<TrackMcpRow>, choice: McpChoice): string[] {\n const recorded = new Set(\n (choice.previousLog?.portions ?? []).filter((p) => p.path === \".mcp.json\").map((p) => p.key),\n );\n const legacy = new Set(\n choice.projectDir === undefined\n ? []\n : legacyMcpServers(choice.projectDir, choice.previousLog, rows),\n );\n const dropped = new Set(droppedMcpServers(rows, choice.excluded));\n const out: string[] = [];\n for (const [name, assetId] of selectableMcpServers(rows)) {\n if (dropped.has(name)) continue;\n const key = `mcpServers.${name}`;\n if (isAssetSelected(assetId, choice.spec) || recorded.has(key) || legacy.has(name)) {\n out.push(name);\n }\n }\n return out;\n}\n\n/**\n * #722 (ADR-106, 설계 검증 B1 ⓐ) — 옛 로그(몫 기록 전 · v26.161 이하)에서 **값으로** 알아본 하네스 서버 중 고를 수 있는 행의\n * 이름. 근거는 `legacyMcpSeed` 와 같다: 기록이 하네스가 그 파일을 **만들었다**고 말하고(`rootFiles.change === \"created\"`), 값이\n * 하네스가 쓴 것(지금 행 · `PRIOR_MCP_SERVERS`)과 같다. 디스크 존재만으로는 고르지 않는다(ADR-096).\n *\n * 이것을 선택으로 넘기는 이유: 기록 있는 설치본은 기록된 몫(ⓑ)으로 같은 서버를 남기고 갈아 끼운다. 옛 로그만 이 근거가 없어\n * 걷거나 관리 밖에 두면 같은 디스크가 이전 판에 따라 다른 결과를 받는다(루트 CLAUDE.md §설치자 디스크 한 원칙).\n */\nexport function legacyMcpServers(\n projectDir: string,\n previousLog: InstallLog | null,\n rows: ReadonlyArray<TrackMcpRow>,\n): string[] {\n if (previousLog === null || previousLog.records === \"writer\") return [];\n if ((previousLog.portions ?? []).some((p) => p.path === \".mcp.json\")) return [];\n const created = (previousLog.rootFiles ?? []).some(\n (f) => f.path === \".mcp.json\" && f.change === \"created\",\n );\n if (!created) return [];\n const servers = readMcpServers(join(projectDir, \".mcp.json\"));\n if (servers === null) return [];\n const out: string[] = [];\n for (const name of selectableMcpServers(rows).keys()) {\n if (!Object.hasOwn(servers, name)) continue;\n const disk = jsonSha(servers[name]);\n const row = rows.find((r) => r.name === name);\n const known = [...(row ? [row.server] : []), ...priorMcpValues(name)];\n if (known.some((v) => jsonSha(toClaudeServer(v)) === disk)) out.push(name);\n }\n return out;\n}\n\n/** `.mcp.json` 의 `mcpServers` — 없거나 못 읽으면 null(아무것도 알아보지 않는다). */\nfunction readMcpServers(path: string): Record<string, unknown> | null {\n if (!existsSync(path)) return null;\n try {\n const parsed = JSON.parse(readFileSync(path, \"utf8\")) as unknown;\n if (parsed === null || typeof parsed !== \"object\" || Array.isArray(parsed)) return null;\n const servers = (parsed as { mcpServers?: unknown }).mcpServers;\n if (servers === null || typeof servers !== \"object\" || Array.isArray(servers)) return null;\n return servers as Record<string, unknown>;\n } catch {\n return null;\n }\n}\n\n/**\n * #709 리뷰 NOTE 1 — 설치자가 명시적으로 뺀 고를 수 있는 행: 자산 id(`railway-mcp-server`) 또는 키 id(`mcp:railway-mcp-server`).\n * **트랙 기본 행이어도** 렌더에서 뺀다 — 렌더는 `.mcp.json` · Codex · OpenCode 가 같이 받으므로 명시한 빼기가 트랙과 무관하게 세\n * 자리 모두에 닿는다. 키 id 도 같은 규칙이다: 키 id 를 `.mcp.json` 에만 걸면 기본 행이 아닌 트랙에서는 다음 실행에 몫 기록이 사라져\n * 결국 세 자리 모두에서 빠지므로(선택 조항 ⓑ 가 근거를 잃는다), 트랙에 따라 결과가 갈린다.\n */\nexport function droppedMcpServers(\n rows: ReadonlyArray<TrackMcpRow>,\n excluded: ReadonlySet<string>,\n): string[] {\n return [...selectableMcpServers(rows)]\n .filter(([name, assetId]) => {\n const id = keyId(\".mcp.json\", `mcpServers.${name}`);\n return excluded.has(assetId) || (id !== null && excluded.has(id));\n })\n .map(([name]) => name);\n}\n\n/**\n * #709 — 기록이 하네스 몫으로 적은 `.mcp.json` 서버 중 카탈로그 `internal` 자산이 가리키는 것의 자산 id. 위저드 update\n * 흐름이 이 서버를 **체크된 채** 보이고(체크 = 확인 뒤 디스크에 있다), 해제하면 `--without <id>` 로 낸다. 기록된 몫은\n * 렌더한 키뿐이라 같은 이름의 `internal` 자산이면 곧 트랙 표의 고를 수 있는 행이다 — 트랙 표를 다시 읽지 않는다.\n */\nexport function recordedMcpAssetIds(log: InstallLog | null): string[] {\n const recorded = new Set(\n (log?.portions ?? []).filter((p) => p.path === \".mcp.json\").map((p) => p.key),\n );\n return EXTERNAL_ASSETS.filter(\n (a) => a.method.kind === \"internal\" && recorded.has(`mcpServers.${a.method.key}`),\n ).map((a) => a.id);\n}\n","/**\n * 함께 쓰는 파일 → 어댑터 표 (#551 · ADR-097 §6.2). **이 표에 있는 경로가 `shared` 다** — 파일 종류는\n * 저장하지 않고 여기서 유도한다(설계 §1.2). 새 함께 쓰는 파일은 여기 한 줄, 새 형식이면 어댑터 하나.\n *\n * 키 id(`excluded` 에 적히는 이름)도 여기서 나온다(표 자체는 `shared-files.ts`). 설치자가 `--with`/`--without` 으로\n * 주는 키 id 는 이번 렌더가 내는 것과 대조한다(ADR-099 R4 — `key-ids.ts`).\n */\n\nimport type { Adapter } from \"../install-log.js\";\nimport type { PortionAdapter } from \"./contract.js\";\nimport { isContainerKey, jsonKeys } from \"./json-keys.js\";\nimport { lines } from \"./lines.js\";\nimport { markerMd } from \"./marker-md.js\";\nimport { SHARED_FILES } from \"./shared-files.js\";\nimport { tomlRegion } from \"./toml-region.js\";\n\nexport type { PortionAdapter, StripResult, UpsertResult } from \"./contract.js\";\nexport { isKeyId, KEY_ID_PREFIXES, SHARED_FILES, sharedPathOfKeyId } from \"./shared-files.js\";\n\nexport const ADAPTERS: {\n \"marker-md\": PortionAdapter<string>;\n \"json-keys\": PortionAdapter<unknown>;\n \"toml-region\": PortionAdapter<string>;\n lines: PortionAdapter<string>;\n} = {\n \"marker-md\": markerMd,\n \"json-keys\": jsonKeys,\n \"toml-region\": tomlRegion,\n lines,\n};\n\n/** 이 경로가 함께 쓰는 파일이면 그 어댑터, 아니면 null. */\nexport function adapterFor(path: string): Adapter | null {\n return SHARED_FILES[path]?.adapter ?? null;\n}\n\n/**\n * 어댑터 키 → `excluded` 에 적는 키 id. 하네스가 만든 빈 컨테이너 키는 id 가 아니다(null).\n * 예: (`.mcp.json`, `mcpServers.github`) → `mcp:github` · (`.claude/settings.json`, `statusLine`) → `settings:statusLine`.\n */\nexport function keyId(path: string, key: string): string | null {\n const file = SHARED_FILES[path];\n if (file === undefined || isContainerKey(key)) return null;\n const body =\n file.strip !== undefined && key.startsWith(file.strip) ? key.slice(file.strip.length) : key;\n return `${file.prefix}${body}`;\n}\n\n/** `excluded` 목록에서 이 파일의 것만 어댑터 키로 — upsert/strip 의 `excluded` 입력. */\nexport function excludedKeys(path: string, excluded: Iterable<string>): Set<string> {\n const file = SHARED_FILES[path];\n const out = new Set<string>();\n if (file === undefined) return out;\n for (const id of excluded) {\n if (!id.startsWith(file.prefix)) continue;\n out.add(`${file.strip ?? \"\"}${id.slice(file.prefix.length)}`);\n }\n return out;\n}\n","/**\n * `json-keys` — `.mcp.json` · `opencode.json` · `.claude/settings.json` (#551 · ADR-097 §6.2).\n *\n * 키 문법(설계 표 그대로):\n * `statusLine` 최상위 속성\n * `mcpServers.<name>` · `mcp.<name>` 객체 안 속성 — 첫 `.` 에서만 자른다(서버 이름에 `.` 이 있어도 된다)\n * `hooks.<Event>#<script>` **핸들러 하나** — `hooks.<Event>` 의 matcher 묶음 안 `hooks[]` 중\n * `command` 가 **이 프로젝트의** `.claude/hooks/<script>` 를 부르는 것\n * (앵커 판정 = `hook-ref.ts` `projectAnchoredRef`, 치유기와 같은 함수 —\n * 홈 `~/.claude/hooks/` 의 같은 이름은 설치자 것). 같은 묶음의 다른\n * 핸들러는 설치자 몫이다(#551 리뷰 B1). 렌더 값은 **더할 때 붙일 묶음**\n * `{ matcher?, hooks: [<핸들러>] }` 이고, 몫(sha)은 그 핸들러뿐이다\n * `hooks.<Event>#<script>{}` 그 핸들러를 담은 묶음을 하네스가 **만들었다** — 핸들러를 빼서 묶음의\n * `hooks` 가 비면 묶음째 걷는다. 설치자 묶음은 비어도 남긴다\n * `<path>{}` · `<path>[]` 하네스가 **만든** 빈 컨테이너 — strip 이 비었으면 걷는다\n *\n * 컨테이너 키가 따로 있는 이유: strip 은 \"빈 컨테이너 정리\"를 해야 하는데(설정 파일에 `hooks` 가 없던\n * 설치자에게 빈 `\"hooks\": {}` 를 남기면 원본이 아니다), 설치자가 **원래 갖고 있던** 빈 컨테이너\n * (`\"mcpServers\": {}`)까지 걷으면 역시 원본이 아니다. 누가 만들었는지는 기록만 안다.\n *\n * 값의 sha 는 키 순서를 정렬한 JSON 으로 잰다 — 설치자가 들여쓰기·키 순서만 바꾼 것은 편집이 아니다.\n * 쓰기는 `JSON.stringify(…, null, 2)` 이고, 바뀐 것이 없으면 입력 바이트를 그대로 돌려준다(N-a).\n * 기존 `mcp-merge.ts` 의 `mergeUserBase` 는 파싱 실패 시 템플릿으로 덮는다(#574) — 그 호출부는 PR-3 이\n * 이 어댑터로 옮기며 고친다. 여기서는 파싱 실패 = `{ ok: false }` 다.\n */\n\nimport { join } from \"node:path\";\nimport { projectAnchoredRef } from \"../hook-ref.js\";\nimport { hashContent } from \"../install-log.js\";\nimport {\n type PortionAdapter,\n type PortionShas,\n planStrip,\n planUpsert,\n type StripResult,\n type UpsertResult,\n} from \"./contract.js\";\n\ntype JsonObject = Record<string, unknown>;\n\nconst UNREADABLE = \"invalid JSON\";\n\nfunction isObject(v: unknown): v is JsonObject {\n return typeof v === \"object\" && v !== null && !Array.isArray(v);\n}\n\nfunction parseRoot(text: string): JsonObject | null {\n try {\n const v: unknown = JSON.parse(text);\n return isObject(v) ? v : null;\n } catch {\n return null;\n }\n}\n\n/** 키 순서를 정렬한 직렬화 — sha 가 들여쓰기·순서에 흔들리지 않게. */\nfunction stable(v: unknown): string {\n if (Array.isArray(v)) return `[${v.map(stable).join(\",\")}]`;\n if (isObject(v)) {\n const keys = Object.keys(v).sort();\n return `{${keys.map((k) => `${JSON.stringify(k)}:${stable(v[k])}`).join(\",\")}}`;\n }\n return JSON.stringify(v) ?? \"null\";\n}\n\nexport function jsonSha(v: unknown): string {\n return hashContent(stable(v));\n}\n\n/** 컨테이너 키의 sha 는 뜻이 없다 — 고정값으로 둬 기록이 흔들리지 않게 한다. */\nconst CONTAINER_SHA = hashContent(\"uzys-harness:container\");\n\ntype Parsed =\n | { kind: \"container\"; path: string[]; shape: \"object\" | \"array\" }\n | { kind: \"group\"; path: string[]; script: string }\n | { kind: \"item\"; path: string[]; script: string }\n | { kind: \"prop\"; path: string[]; name: string };\n\n/** `a` → [] + a · `a.b` → [a] + b (첫 `.` 에서만). */\nfunction splitHead(s: string): string[] {\n const dot = s.indexOf(\".\");\n return dot === -1 ? [s] : [s.slice(0, dot), s.slice(dot + 1)];\n}\n\nfunction parseKey(key: string): Parsed {\n const container = /^(.+?)(\\{\\}|\\[\\])$/.exec(key);\n if (container) {\n const inner = parseKey(container[1] ?? \"\");\n if (inner.kind === \"item\") return { kind: \"group\", path: inner.path, script: inner.script };\n return {\n kind: \"container\",\n path: splitHead(container[1] ?? \"\"),\n shape: container[2] === \"{}\" ? \"object\" : \"array\",\n };\n }\n const hash = key.indexOf(\"#\");\n if (hash !== -1)\n return { kind: \"item\", path: splitHead(key.slice(0, hash)), script: key.slice(hash + 1) };\n const path = splitHead(key);\n return { kind: \"prop\", path: path.slice(0, -1), name: path.at(-1) ?? key };\n}\n\n/** 값이 아니라 \"하네스가 만들었다\" 는 표시인 키(빈 컨테이너 · 묶음) — 키 id 가 없고 sha 도 뜻이 없다. */\nexport function isContainerKey(key: string): boolean {\n const kind = parseKey(key).kind;\n return kind === \"container\" || kind === \"group\";\n}\n\nfunction isGroupKey(key: string): boolean {\n return parseKey(key).kind === \"group\";\n}\n\n/** settings.json 훅 핸들러는 기록된 키면 sha 와 무관하게 뺀다(N-f · N13 — 스크립트가 함께 사라진다). */\nfunction alwaysStrip(key: string): boolean {\n return parseKey(key).kind === \"item\";\n}\n\n/** 이 핸들러가 이 프로젝트의 `.claude/hooks/<script>` 를 부르는가 — 판정은 치유기와 같은 함수. */\nfunction callsScript(handler: unknown, script: string, claudeDir: string | undefined): boolean {\n if (!isObject(handler) || typeof handler.command !== \"string\") return false;\n return projectAnchoredRef(handler.command, claudeDir) === `hooks/${script}`;\n}\n\ninterface HandlerAt {\n groups: unknown[];\n gi: number;\n hooks: unknown[];\n hi: number;\n}\n\n/** 이벤트 배열에서 그 스크립트를 부르는 핸들러의 자리 **전부**(배열 순서) — 설치자가 같은 스크립트를 또 등록했을 수 있다(#840). */\nfunction findHandlers(\n root: JsonObject,\n path: ReadonlyArray<string>,\n script: string,\n claudeDir: string | undefined,\n): HandlerAt[] {\n const groups = walk(root, path);\n if (!Array.isArray(groups)) return [];\n const out: HandlerAt[] = [];\n for (const [gi, group] of groups.entries()) {\n if (!isObject(group) || !Array.isArray(group.hooks)) continue;\n for (const [hi, h] of group.hooks.entries()) {\n if (callsScript(h, script, claudeDir)) out.push({ groups, gi, hooks: group.hooks, hi });\n }\n }\n return out;\n}\n\n/**\n * 그중 하네스 몫인 핸들러 — 값이 기록 sha 와 같은 것, 없으면(설치자가 고쳤다) 첫 것. 없으면 undefined.\n * 같은 스크립트를 부르는 설치자 핸들러가 앞에 와도 그것을 하네스 몫으로 읽지 않는다(#840).\n */\nfunction findHandler(\n root: JsonObject,\n path: ReadonlyArray<string>,\n script: string,\n claudeDir: string | undefined,\n sha?: string,\n): HandlerAt | undefined {\n const all = findHandlers(root, path, script, claudeDir);\n return all.find((at) => jsonSha(at.hooks[at.hi]) === sha) ?? all[0];\n}\n\n/** 렌더가 준 묶음에서 그 스크립트를 부르는 핸들러 — 없으면 렌더가 잘못됐다. */\nfunction renderedHandler(key: string, group: unknown, claudeDir: string | undefined): unknown {\n const p = parseKey(key);\n const hooks = isObject(group) && Array.isArray(group.hooks) ? group.hooks : [];\n const handler =\n p.kind === \"item\" ? hooks.find((h) => callsScript(h, p.script, claudeDir)) : undefined;\n if (handler === undefined) {\n throw new Error(`json-keys: rendered group for ${key} has no handler calling that hook script`);\n }\n return handler;\n}\n\n/** 경로의 컨테이너. 없으면 undefined, 있는데 모양이 다르면 null(설치자 값 — 건드리지 않는다). */\nfunction walk(root: JsonObject, path: ReadonlyArray<string>): unknown {\n let cur: unknown = root;\n for (const seg of path) {\n if (!isObject(cur)) return null;\n cur = cur[seg];\n if (cur === undefined) return undefined;\n }\n return cur;\n}\n\nfunction readKey(\n root: JsonObject,\n key: string,\n claudeDir: string | undefined,\n sha?: string,\n): unknown {\n const p = parseKey(key);\n if (p.kind === \"item\" || p.kind === \"group\") {\n const at = findHandler(root, p.path, p.script, claudeDir, sha);\n if (at === undefined) return undefined;\n return p.kind === \"item\" ? at.hooks[at.hi] : at.groups[at.gi];\n }\n const holder = walk(root, p.path);\n if (p.kind === \"container\") return holder ?? undefined;\n return isObject(holder) ? holder[p.name] : undefined;\n}\n\n/**\n * 값을 쓴다 — 없는 컨테이너는 만들고 그 컨테이너 키를 `created` 에 적는다.\n * @returns 컨테이너 자리에 모양이 다른 설치자 값이 있으면 false(쓰지 않았다)\n */\nfunction writeKey(\n root: JsonObject,\n key: string,\n value: unknown,\n created: string[],\n claudeDir: string | undefined,\n sha?: string,\n): boolean {\n const p = parseKey(key);\n if (p.kind === \"container\" || p.kind === \"group\") return false;\n // 컨테이너 사슬: 마지막 칸은 item 이면 배열, prop 이면 객체\n let cur: JsonObject = root;\n const chain = p.path;\n for (const [i, seg] of chain.entries()) {\n const last = i === chain.length - 1;\n const wantArray = last && p.kind === \"item\";\n const existing = cur[seg];\n if (existing === undefined) {\n const fresh = wantArray ? [] : {};\n cur[seg] = fresh;\n created.push(`${chain.slice(0, i + 1).join(\".\")}${wantArray ? \"[]\" : \"{}\"}`);\n } else if (wantArray ? !Array.isArray(existing) : !isObject(existing)) {\n return false;\n }\n if (!wantArray) cur = cur[seg] as JsonObject;\n }\n if (p.kind === \"item\") {\n // 있으면 핸들러만 갈아 끼운다(묶음 · 형제 핸들러는 그대로). 없으면 렌더의 묶음을 새로 붙이고 그 묶음을\n // 하네스가 만들었다고 적는다 — 설치자 묶음 안에 끼워 넣지 않는다(뺄 때 그 묶음을 걷을 근거가 없다).\n const at = findHandler(root, chain, p.script, claudeDir, sha);\n if (at !== undefined) {\n at.hooks[at.hi] = structuredClone(renderedHandler(key, value, claudeDir));\n } else {\n (walk(root, chain) as unknown[]).push(structuredClone(value));\n created.push(`${key}{}`);\n }\n return true;\n }\n cur[p.name] = value;\n return true;\n}\n\n/**\n * 핸들러 키는 그 스크립트를 부르는 핸들러를 **전부** 뺀다 — 스크립트가 함께 사라지므로 하나라도 남기면 죽은 참조다(N-f · #840).\n * @param ownsGroup 핸들러 키일 때 — 하네스 몫 핸들러(`sha` 로 고른 것)의 묶음을 하네스가 만들었나. 그렇고 비면 그 묶음째\n * 걷는다. 설치자 묶음은 비어도 남는다.\n * @returns 하네스 몫이 아닌 핸들러(설치자가 같은 스크립트로 또 등록한 줄)까지 걷었나 — 호출부가 `removedEdited` 로 보내\n * 걷기 전 백업(#800)을 받게 한다.\n */\nfunction deleteKey(\n root: JsonObject,\n key: string,\n claudeDir: string | undefined,\n ownsGroup = false,\n sha?: string,\n): boolean {\n const p = parseKey(key);\n if (p.kind === \"group\") return false;\n if (p.kind === \"item\") {\n const ours = findHandler(root, p.path, p.script, claudeDir, sha);\n const all = findHandlers(root, p.path, p.script, claudeDir);\n for (const at of [...all].reverse()) at.hooks.splice(at.hi, 1);\n if (ours !== undefined && ownsGroup && ours.hooks.length === 0) {\n ours.groups.splice(ours.gi, 1);\n }\n return all.length > 1;\n }\n const holderPath = p.kind === \"container\" ? p.path.slice(0, -1) : p.path;\n const name = p.kind === \"container\" ? (p.path.at(-1) ?? \"\") : p.name;\n const holder = holderPath.length === 0 ? root : walk(root, holderPath);\n if (isObject(holder)) delete holder[name];\n return false;\n}\n\n/** `plan.remove` 를 걷고 `removedEdited` 를 돌려준다 — 계획의 것 + 설치자 줄까지 걷은 키(#840). */\nfunction deleteRemoved(\n root: JsonObject,\n plan: { remove: string[]; removedEdited: string[] },\n recorded: PortionShas,\n claudeDir: string | undefined,\n): string[] {\n const owned = ownedGroups(recorded.keys());\n const edited = [...plan.removedEdited];\n for (const k of plan.remove) {\n const extra = deleteKey(root, k, claudeDir, owned.has(k), recorded.get(k));\n if (extra && !edited.includes(k)) edited.push(k);\n }\n return edited;\n}\n\nfunction isEmptyContainer(v: unknown): boolean {\n return (Array.isArray(v) && v.length === 0) || (isObject(v) && Object.keys(v).length === 0);\n}\n\n/**\n * 하네스가 만든 컨테이너 중 비었으면 걷는다 — 깊은 것부터. 남은 것만 기록으로 돌려준다.\n * 묶음 키는 여기서 다루지 않는다 — 핸들러를 뺄 때 함께 판정한다(`deleteKey` 의 `ownsGroup`).\n */\nfunction pruneContainers(root: JsonObject, containers: ReadonlyArray<string>): string[] {\n const depth = (k: string) => parseKey(k).path.length;\n const kept: string[] = [];\n const plain = containers.filter((k) => !isGroupKey(k));\n for (const key of [...plain].sort((a, b) => depth(b) - depth(a))) {\n const v = readKey(root, key, undefined);\n if (v === undefined || v === null) continue;\n if (isEmptyContainer(v)) deleteKey(root, key, undefined);\n else kept.push(key);\n }\n return kept;\n}\n\nfunction present(\n root: JsonObject,\n keys: Iterable<string>,\n claudeDir: string | undefined,\n recorded: PortionShas = new Map(),\n): Map<string, string> {\n const out = new Map<string, string>();\n for (const k of keys) {\n if (isContainerKey(k)) continue;\n const v = readKey(root, k, claudeDir, recorded.get(k));\n if (v !== undefined) out.set(k, jsonSha(v));\n }\n return out;\n}\n\nfunction claudeDirOf(projectDir: string | undefined): string | undefined {\n return projectDir === undefined ? undefined : join(projectDir, \".claude\");\n}\n\n/** 몫으로 남은 핸들러 중 묶음을 하네스가 만든 것 — 묶음 키를 이어 적는다. */\nfunction carryGroups(\n root: JsonObject,\n portions: Map<string, string>,\n owned: ReadonlySet<string>,\n claudeDir: string | undefined,\n): void {\n for (const key of [...portions.keys()]) {\n if (parseKey(key).kind !== \"item\" || !owned.has(key)) continue;\n if (readKey(root, key, claudeDir) !== undefined) portions.set(`${key}{}`, CONTAINER_SHA);\n }\n}\n\nfunction ownedGroups(keys: Iterable<string>): Set<string> {\n return new Set([...keys].filter(isGroupKey).map((k) => k.slice(0, -2)));\n}\n\nfunction serialize(root: JsonObject): string {\n return `${JSON.stringify(root, null, 2)}\\n`;\n}\n\nfunction valueKeys(m: ReadonlyMap<string, unknown>): string[] {\n return [...m.keys()].filter((k) => !isContainerKey(k));\n}\n\nexport const jsonKeys: PortionAdapter<unknown> = {\n unreadable: UNREADABLE,\n\n read(text, keys, projectDir) {\n const root = parseRoot(text);\n if (root === null) return null;\n return present(root, keys ?? Object.keys(root), claudeDirOf(projectDir));\n },\n\n upsert(existing, input): UpsertResult {\n const fresh = existing === null;\n const root = fresh ? {} : parseRoot(existing);\n if (root === null) return { ok: false, reason: UNREADABLE };\n const claudeDir = claudeDirOf(input.projectDir);\n const recorded = fresh ? new Map<string, string>() : input.recorded;\n const recordedValues = new Map([...recorded].filter(([k]) => !isContainerKey(k)));\n const render = new Map([...input.render].filter(([k]) => !isContainerKey(k)));\n const renderSha = ([k, v]: [string, unknown]): [string, string] => [\n k,\n jsonSha(parseKey(k).kind === \"item\" ? renderedHandler(k, v, claudeDir) : v),\n ];\n const plan = planUpsert({\n render: new Map([...render].map(renderSha)),\n recorded: recordedValues,\n present: present(\n root,\n [...recordedValues.keys(), ...render.keys()],\n claudeDir,\n recordedValues,\n ),\n excluded: input.excluded,\n alwaysStrip,\n });\n const before = stable(root);\n const created: string[] = [];\n const owned = ownedGroups(recorded.keys());\n const removedEdited = deleteRemoved(root, plan, recorded, claudeDir);\n for (const k of plan.replace) {\n writeKey(root, k, render.get(k), created, claudeDir, recorded.get(k));\n }\n const kept = [...plan.kept];\n const restored = [...plan.restored];\n const missing = [...plan.missing];\n for (const k of plan.add) {\n if (!writeKey(root, k, render.get(k), created, claudeDir)) {\n // 컨테이너 모양이 달라 못 썼다\n const prior = recordedValues.get(k);\n if (prior === undefined) {\n // 처음 더하려던 키 — 그 자리를 설치 전부터 있던 설치자 값이 차지했다(Q3)\n plan.portions.delete(k);\n kept.push(k);\n } else {\n // #881 — 기록된 몫이 사라졌는데 그 자리 모양이 달라 되돌리지 못했다. 이긴 설치자 값이 아니라 \"kept yours\" 가 아니다 —\n // 기록 sha 를 잇는(되돌릴 근거, ADR-099 R1) `missing` 이고, 렌더에 있는 키라 호출부가 \"되돌리지 못했다\" 로 말한다\n plan.portions.set(k, prior);\n missing.push(k);\n }\n const r = restored.indexOf(k);\n if (r >= 0) restored.splice(r, 1);\n }\n }\n const containers = pruneContainers(root, [\n ...[...recorded.keys()].filter(isContainerKey),\n ...created,\n ]);\n for (const c of containers) plan.portions.set(c, CONTAINER_SHA);\n carryGroups(root, plan.portions, new Set([...owned, ...ownedGroups(created)]), claudeDir);\n // 실제로 바뀐 것만 — 컨테이너 모양이 달라 못 쓴 키는 바꾼 것이 아니다(입력 바이트를 그대로 둔다)\n const changed = fresh || stable(root) !== before;\n return {\n ok: true,\n text: changed ? serialize(root) : (existing ?? \"\"),\n changed,\n portions: plan.portions,\n restored,\n missing,\n removed: plan.remove,\n removedEdited,\n replaced: plan.replace,\n kept,\n };\n },\n\n strip(existing, input): StripResult {\n const root = parseRoot(existing);\n if (root === null) return { ok: false, reason: UNREADABLE };\n const claudeDir = claudeDirOf(input.projectDir);\n const plan = planStrip({\n recorded: new Map([...input.recorded].filter(([k]) => !isContainerKey(k))),\n present: present(root, valueKeys(input.recorded), claudeDir, input.recorded),\n excluded: input.excluded,\n alwaysStrip,\n });\n const before = stable(root);\n const owned = ownedGroups(input.recorded.keys());\n const removedEdited = deleteRemoved(root, plan, input.recorded, claudeDir);\n const containers = pruneContainers(root, [...input.recorded.keys()].filter(isContainerKey));\n for (const c of containers) plan.portions.set(c, CONTAINER_SHA);\n carryGroups(root, plan.portions, owned, claudeDir);\n const changed = stable(root) !== before;\n return {\n ok: true,\n text: changed ? serialize(root) : existing,\n changed,\n removed: plan.remove,\n removedEdited,\n kept: plan.kept,\n portions: plan.portions,\n empty: Object.keys(root).length === 0,\n };\n },\n};\n","import { existsSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { backupIfLossyUtf8 } from \"./fs-ops.js\";\nimport { LEGACY_HOOK_COMMANDS } from \"./legacy-fingerprints.js\";\n\n/**\n * 훅 command 가 **이 프로젝트의** `.claude/` 아래 스크립트를 부르는가 — 판정 한 곳 (#551).\n *\n * 죽은 참조 탐지기(`keepHookRef` · `findStaleHookRefs`)와 함께 쓰는 파일 어댑터(`adapters/json-keys.ts` 의\n * settings.json 훅 핸들러 식별)가 같은 규칙을 써야 한다 — 둘이 갈라지면 한쪽이 홈 `~/.claude/` 의 설치자\n * 훅을 하네스 것으로 읽는다(H-2 · #551 리뷰 B1). 그래서 치유기 안에 살던 함수를 그대로 옮겨 왔다.\n */\n\n/**\n * `.claude/` 라는 이름이 가리키는 대상은 **둘**이다 — 이 프로젝트의 `<projectDir>/.claude/` 와\n * 사용자 홈의 `~/.claude/`. 치유기가 소유를 주장할 수 있는 것은 앞의 것뿐이고, 뒤의 것은\n * 사용자 전역 설정(플러그인 훅이 실제로 사는 `~/.claude/plugins/**` 포함)이다. 지우면 치유가\n * 아니라 파손이다 (ADR-057 Decision 2).\n *\n * 그래서 **앵커 화이트리스트**로 짠다: 참조가 아래 접두사 중 하나로 시작할 때만 판정 대상이고,\n * 나머지는 전부 기본값 = 보존으로 떨어진다. 반대 방향(`$HOME` 을 빼고, `~` 를 빼고,\n * `${CLAUDE_CONFIG_DIR}` 를 빼고 …)으로 짜면 그 예외 목록이 두 번째 하드코딩 사본이 되어\n * **다음에 나올 표기 하나가 곧 다음 서식지**가 된다 (`no-false-ship` §게이트는 열거하지 말고\n * 훑어라 — 5회 재발한 실패 모드).\n *\n * @param claudeDir 이 프로젝트의 `.claude/` 절대경로. 문자열 비교이지 경로 정규화가 아니다 —\n * 심볼릭 링크·마운트로 표기가 갈리면 **어느 방향이든 앵커가 성립하지 않아 보존**된다:\n * 기준보다 짧게 쓰인 방향(`claudeDir` = `/private/var/…`, command = `/var/…`)은 앵커 문자열\n * 자체가 없고, 앞에 뭔가 더 붙은 방향(`/private/var/…` · 바인드마운트 `/mnt/host/var/…`)은\n * 문자열은 품고 있지만 **토큰 시작이 아니라서** 앵커가 아니다(H-4). 후자를 앵커로 세면\n * 실경로가 다른, 실존하는 남의 훅을 지운다. 정규화를 붙이려면 양쪽을 같이 정규화해야 한다.\n * **생략하면** 절대경로 앵커 없이 `$CLAUDE_PROJECT_DIR` 두 표기만 본다 — 프로젝트 경로를 모르는 호출부\n * (함께 쓰는 파일의 어댑터가 경로 없이 불릴 때)가 빈 문자열을 넘겨 앵커가 `/` 로 넓어지는 것을 막는다.\n * @returns `.claude/` 기준 상대경로. 앵커가 없거나 `.sh` 참조가 아니면 undefined.\n */\nexport function projectAnchoredRef(command: string, claudeDir?: string): string | undefined {\n const anchors = [\n \"$CLAUDE_PROJECT_DIR/.claude/\",\n // biome-ignore lint/suspicious/noTemplateCurlyInString: 훅 command 원문의 셸 변수 표기 (JS 템플릿 아님)\n \"${CLAUDE_PROJECT_DIR}/.claude/\",\n // 레거시 설치는 `$CLAUDE_PROJECT_DIR` 이전에 절대경로를 그대로 박아 넣었다 — 가리키는 곳이\n // 같은 프로젝트이므로 표기가 다르다고 면제할 이유가 없다.\n ...(claudeDir === undefined ? [] : [`${claudeDir}/`]),\n ];\n for (const anchor of anchors) {\n // 앵커는 command **어딘가에 박혀 있는 문자열**이 아니라 \"참조가 여기서 시작한다\"는 주장이다.\n // 그래서 출현마다 시작 경계를 보고, 토큰 중간 출현은 건너뛴 뒤 **다음 출현**을 계속 본다 —\n // 첫 출현만 보고 포기하면 (`… /mnt/host<claudeDir>/x.sh … <claudeDir>/y.sh` 처럼) 뒤에 있는\n // 진짜 앵커를 놓친다.\n for (let at = command.indexOf(anchor); at >= 0; at = command.indexOf(anchor, at + 1)) {\n // 시작 고정은 끝과 **같은 개념**이다 — 앞 문자가 토큰 문자가 아니면(= 없거나 따옴표·공백)\n // 토큰 시작이다. 구분자를 열거하면 그 목록이 토큰 정의의 두 번째 사본이 되어 빠진 형태\n // 하나가 다음 서식지가 된다. 안 하면 실경로가 다른 참조(`/private…` · 바인드마운트 ·\n // 백업 트리)를 이 프로젝트 것으로 오인해 **실존하는 남의 훅을 지운다** (ADR-057).\n // command 맨 앞(`at === 0`)은 `charAt(-1)` 이 빈 문자열이라 토큰 시작으로 떨어진다.\n if (/[^\"\\s]/.test(command.charAt(at - 1))) continue;\n // 끝 고정도 **토큰 경계**로 짠다 — 같은 클래스의 부정 룩어헤드가 \"토큰 문자가 더 없다\"\n // 하나로 따옴표·공백·문자열 끝을 전부 덮는다. 안 하면 `.sh` 가 접두사인 경로\n // (`run.shell` · `x.sh.bak`)에서 앞부분만 캡처해 실존하는 훅을 stale 로 오판한다.\n const rel = command.slice(at + anchor.length).match(/^[^\"\\s]+\\.sh(?![^\"\\s])/)?.[0];\n if (rel) return rel;\n }\n }\n return undefined;\n}\n\n/** 옛 템플릿 훅 줄(`LEGACY_HOOK_COMMANDS`)이 부르는 참조(`.claude/` 상대) — 같은 생성물에서 유도한다. */\nexport const LEGACY_HOOK_REFS: ReadonlyArray<string> = [...LEGACY_HOOK_COMMANDS].flatMap(\n (c) => projectAnchoredRef(c) ?? [],\n);\n\n/**\n * #781 — 이 훅 줄을 옛 하네스 줄로 볼 수 있는가. 옛 바이트를 아는 참조(태그 114판의 8가지)는 **그 바이트 그대로일 때만** — 설치자가\n * 고치거나 자기 명령을 이어 붙인 줄은 참조가 같아도 설치자 줄이다. 생성 범위 이전 배선(`observe.sh` · `checkpoint-snapshot.sh` 등,\n * npm 게시 전)은 바이트를 몰라 참조로 본다. update 의 옛 몫 찾기(`legacySettingsSeed`)와 걷기(`removeRetiredHookRefs`)가 이 판정 하나를 쓴다.\n */\nexport function isLegacyHookLine(command: unknown, claudeDir?: string): boolean {\n if (typeof command !== \"string\") return false;\n const ref = projectAnchoredRef(command, claudeDir);\n if (ref === undefined) return false;\n return !LEGACY_HOOK_REFS.includes(ref) || LEGACY_HOOK_COMMANDS.has(command);\n}\n\n/* ────────────────────────────────────────────────────────────────────────────\n * settings.json 죽은 훅 참조 탐지기 — install(#603) · update(#746 리뷰 BLOCKER-1) 모두 **알리기만** 한다.\n * 예외 하나: 옛 템플릿이 깐 은퇴 배선의 닫힌 목록은 update · uninstall 이 하네스 몫으로 걷는다(`removeRetiredHookRefs`, #746 재리뷰 B-1).\n * update-mode.ts 에서 옮겼다: installer 가 import 하려는데 installer←update-mode 의존이\n * 이미 있어 반대 방향 import 는 순환이 된다. 판정(SSOT)도 여기(projectAnchoredRef)다.\n * ──────────────────────────────────────────────────────────────────────────── */\n\nexport interface HookCommand {\n type?: string;\n command?: string;\n}\n\nexport interface HookEntry {\n matcher?: string;\n hooks: HookCommand[];\n}\n\ninterface SettingsJson {\n hooks?: Record<string, HookEntry[]>;\n [key: string]: unknown;\n}\n\n/** `hook.command` 가 이 프로젝트 `.claude/` 의 스크립트를 부르는가 — 안 부르면 보존. */\nexport function keepHookRef(hook: HookCommand, claudeDir: string, removed: string[]): boolean {\n const relPath = projectAnchoredRef(hook?.command ?? \"\", claudeDir);\n if (relPath === undefined) return true; // 이 프로젝트에 앵커된 hook script 참조 아님 — 보존\n const exists = existsSync(join(claudeDir, relPath));\n // 중복 제거는 **경로 기준**. 파일명 기준이면 같은 이름이 두 디렉터리에 있을 때\n // 살아 있는 쪽 때문에 죽은 쪽이 보고에서 사라진다.\n if (!exists && !removed.includes(relPath)) removed.push(relPath);\n return exists;\n}\n\n/**\n * 읽기 전용 — 실존 파일 없는 hook script 참조를 **찾기만** 한다(`.claude/` 상대경로). 기록에 없는 참조는 설치자 몫이다\n * (팀이 커밋한 생성 스크립트 훅 — 스크립트는 빌드 산출물이라 새 클론엔 아직 없다). 지우면 원본이 어디에도 안 남으므로\n * install(#603 · B-666-1)도 update(#746 — 폴더 사본이 없어져 되살릴 길이 없다)도 지우지 않고 이것으로 알리기만 한다.\n * 하네스가 기록한 훅의 배선은 함께 쓰는 파일 writer 가 소유한다(ADR-099 R2).\n *\n * @param spare `.claude/` 상대경로를 받아 true 면 그 참조는 판정하지 않는다 — update 는 **기록된 하네스 훅 스크립트**를\n * 넘긴다(0단계가 되살리고 배선은 writer 가 소유한다). 생략하면 전부 판정한다.\n */\nexport function findStaleHookRefs(\n settingsPath: string,\n claudeDir: string,\n spare?: (relPath: string) => boolean,\n): string[] {\n let settings: SettingsJson;\n try {\n settings = JSON.parse(readFileSync(settingsPath, \"utf8\")) as SettingsJson;\n } catch {\n return [];\n }\n const stale: string[] = [];\n for (const eventEntries of Object.values(settings.hooks ?? {})) {\n if (!Array.isArray(eventEntries)) continue; // non-array event — 판정 대상 아님\n for (const entry of eventEntries) {\n if (!Array.isArray(entry?.hooks)) continue;\n for (const hook of entry.hooks) {\n if (spare?.(projectAnchoredRef(hook?.command ?? \"\", claudeDir) ?? \"\") === true) continue;\n keepHookRef(hook, claudeDir, stale);\n }\n }\n }\n return stale;\n}\n\n/**\n * `refs`(하네스가 깔았다가 은퇴시킨 배선의 닫힌 목록, `RETIRED_HOOK_WIRING_REFS`)를 부르는 핸들러를 걷는다 — update 는\n * **스크립트가 없는 것**만(#746 재리뷰 B-1 — 스크립트가 있으면 설치자가 스킬을 남겨 쓰는 중이다), uninstall 은 스크립트와\n * 무관하게 전부(`evenIfScriptExists` — 하네스를 걷는 동작이라 하네스가 깐 배선도 걷는다. 남기면 은퇴 스킬 폴더에 남은 스크립트를\n * 매 도구 호출마다 계속 부른다, PR-D 재리뷰 NOTE-B). 하네스 몫만 빼는 것이다(ADR-097 결정 4) — 같은 묶음의 다른 핸들러\n * (하네스 protect-files · 설치자 핸들러)는 남기고, 이 제거로 빈 묶음만 치운다. 목록 밖 참조는 판정하지 않는다\n * (`findStaleHookRefs` 가 알리기만 한다). 걷은 것이 없거나 `write: false`(미리보기)면 파일을 쓰지 않는다.\n *\n * @returns 걷은(미리보기면 걷을) 참조의 `.claude/` 상대경로(중복 없이).\n */\nexport function removeRetiredHookRefs(\n settingsPath: string,\n claudeDir: string,\n refs: ReadonlyArray<string>,\n opts: { evenIfScriptExists?: boolean; write?: boolean } = {},\n): string[] {\n let settings: SettingsJson;\n try {\n settings = JSON.parse(readFileSync(settingsPath, \"utf8\")) as SettingsJson;\n } catch {\n return [];\n }\n const removed: string[] = [];\n const isRetiredDead = (hook: HookCommand): boolean => {\n const rel = projectAnchoredRef(hook?.command ?? \"\", claudeDir);\n if (rel === undefined || !refs.includes(rel)) return false;\n if (!isLegacyHookLine(hook?.command, claudeDir)) return false; // #781 — 고친 줄은 설치자 줄이다\n if (opts.evenIfScriptExists !== true && existsSync(join(claudeDir, rel))) return false;\n if (!removed.includes(rel)) removed.push(rel);\n return true;\n };\n const hooks: Record<string, HookEntry[]> = {};\n for (const [event, entries] of Object.entries(settings.hooks ?? {})) {\n if (!Array.isArray(entries)) {\n hooks[event] = entries; // non-array event — 그대로\n continue;\n }\n hooks[event] = entries.flatMap((entry) => {\n if (!Array.isArray(entry?.hooks)) return [entry];\n const kept = entry.hooks.filter((hook) => !isRetiredDead(hook));\n if (kept.length === entry.hooks.length) return [entry];\n return kept.length > 0 ? [{ ...entry, hooks: kept }] : []; // 이 제거로 빈 묶음만 치운다\n });\n }\n if (removed.length > 0 && opts.write !== false) {\n backupIfLossyUtf8(settingsPath); // #653 · #847 — 재직렬화가 비UTF-8 바이트를 영구 변형하기 전에 원시 바이트를 보존한다\n writeFileSync(settingsPath, `${JSON.stringify({ ...settings, hooks }, null, 2)}\\n`);\n }\n return removed;\n}\n","// 생성물 — scripts/gen-legacy-fingerprints.mjs 가 쓴다. 손으로 고치지 않는다(ADR-111).\n// 입력: git 태그 v26.70.0~v26.169.0 양끝 고정(실제 26.70.0~26.169.0, 114판). 앵커 절 · AGENTS.md 절 · 머리 지문은 ~v26.159.0(마커 이전 판).\n// 재생산: T=$(mktemp) && node scripts/gen-legacy-fingerprints.mjs \"$T\" && cmp \"$T\" src/legacy-fingerprints.ts\n//\n// 지문은 **소유 근거가 아니다**(ADR-111 · 루트 CLAUDE.md \"하네스 몫은 설치 기록 하나가 정한다\"). 기록이 다 적지 못한 옛 판의 바이트를\n// 알아볼 뿐이다 — 백업을 생략하는 것은 그 자리가 현행 번들에 있을 때뿐이고, 그 밖은 백업하고 치운다.\nimport { createHash } from \"node:crypto\";\n\n/** 절(헤딩 포함 줄 배열)의 지문 — 줄 끝 `\\r` · 공백과 끝 빈 줄을 떼고 \"\\n\" 으로 이어 sha256. 아래 목록을 뽑은 규칙과 같다. */\nexport function legacySectionKey(lines: ReadonlyArray<string>): string {\n const out = lines.map((l) => l.replace(/\\r$/, \"\").trimEnd());\n while (out.length > 1 && out.at(-1) === \"\") out.pop();\n return createHash(\"sha256\").update(out.join(\"\\n\")).digest(\"hex\");\n}\n\n/**\n * `AGENTS.md` 머리의 지문 — 첫 `## ` 앞 줄 중 `# ` 제목(프로젝트 이름이 들어간다)이 아닌 줄을 빈 줄까지 모아 `legacySectionKey`.\n * 머리가 비었으면 `null`.\n */\nexport function legacyPreambleKey(text: string): string | null {\n const pre: string[] = [];\n for (const line of text.split(\"\\n\")) {\n if (/^## /.test(line)) break;\n pre.push(line);\n }\n const head = pre.filter((l) => !/^# /.test(l));\n return head.some((l) => l.trim() !== \"\") ? legacySectionKey(head) : null;\n}\n\n/** 한 줄 = `<첫 토큰> <둘째 토큰|주석…>` — 데이터는 템플릿 리터럴에 둔다(포매터가 줄을 바꾸지 않는다). */\nfunction rows(text: string): string[][] {\n return text\n .split(\"\\n\")\n .filter((l) => l !== \"\")\n .map((l) => l.split(\" \"));\n}\n\nfunction keySet(text: string): ReadonlySet<string> {\n return new Set(rows(text).map((r) => r[0] ?? \"\"));\n}\n\nconst FILES = `\n.claude/CLAUDE.md c8a0f19e0c84faec6fc0a999efbf205d72270a1f8bcb8d28894d564431405b52\n.claude/CLAUDE.md c833b9a2b3e762563bdf8984520257579408de387ce3021ac2c606551f39aa75\n.claude/CLAUDE.md 5a167acf64d246b278f878228f07cf76fe2a808e201d3d82c1c0274f50d736de\n.claude/CLAUDE.md 7fbfe6999a82a880a3a0d34bdaf8e263cfd9c927ef5fb33873059182094d3f7f\n.claude/CLAUDE.md 1dc65a4d8c0f2c47d541852f767d85dca41637d7b4009db6dbf41a0beb20ff68\n.claude/CLAUDE.md db2c8bc13b4d9882c71ab1977e5fc5aa66fb031d5943faba587ce9eba100a0b8\n.claude/CLAUDE.md dca32656dfd75c8676504836501607b7c821115a6dd14cba7baafb5c631cb1ff\n.claude/CLAUDE.md 3aeeea1767e0f910adff08340895a524df829240df93d298bb3d0e327d0711e0\n.claude/CLAUDE.md 16347e86e7c1699db212586685a640135891ec5787ba486e0a12562d85bc9b60\n.claude/CLAUDE.md 71db56ff054601a03f8e287a965c1eb1be64d9db1fccc3b03c1f499c2f23119a\n.claude/CLAUDE.md d92c6bbc4c2c1c7a2547e697f7f128b7918d4e2062c9af958b729e7cb0c58308\n.claude/CLAUDE.md 7f94cca4a8ca2f257164ba9b44360d10f99030d3b636c667f65fd0a1771e123c\n.claude/agents/build-error-resolver.md 28597d1e97a68701999704b789a97a20a28b121b7c27ee6a6e80ab43bcd514b8\n.claude/agents/build-error-resolver.md a050a74ed31acba06b36e8c34bc35bc0e684b6b7ab01c891dfc2d13ba94f9824\n.claude/agents/build-error-resolver.md 59f9eb1ae55c6a943d43610cc4e74d05018d681758ac19cb31d874c51605914e\n.claude/agents/code-reviewer.md e112e5e0bbc501788e8a3bfd2771d34e31a8564bbd87391bff7401af1509782c\n.claude/agents/data-analyst.md 1d75c07c51b8a000e536cd8a1c23e0609d93ea51b02cd48f9877610082cb2475\n.claude/agents/implementer.md ba0ee71bf5ae6f8aeb461b0d6161fe0869a7c45820d49145b2abf27264af7dd2\n.claude/agents/implementer.md a12f9132a55526208837e4960c96705df595c0e1cc2870eba1e56f2a87632de1\n.claude/agents/plan-checker.md 405be32ad0d95d8d4cbc8cddb94e7b2be74981a4dadc160aa9200156b56d40df\n.claude/agents/plan-checker.md 467ddec15189cc4273adacd08f97e7eabe1cc164901882f2dd5bba81fbf72327\n.claude/agents/plan-checker.md f605005ba6ebf1e3494e47e257d68a2d34317d15a47b5c8b52317d9cf914b004\n.claude/agents/plan-checker.md 86ad22297e6f1d75d42146685d759af10698cf763feb1c9484d1f0437262e833\n.claude/agents/plan-checker.md 451181ce44f67f747f7190aa53932565df1ce6c934da4584f03f339502fb9265\n.claude/agents/reviewer.md 4036905c0b8deff6555c9f6cbe962609c243d063c417196cca57fc6f87139cd6\n.claude/agents/reviewer.md 1a0153a7fcc8a170f3f08f2a4e392ca72117d413d690441b930028ce923a6094\n.claude/agents/reviewer.md 98c63bfe35afafb28b2543e5b3c662a5df58b66b877b753efc54b9af32208298\n.claude/agents/security-reviewer.md eacaa50c2b2fdee398ba2e3c896c018ea8887d34701e5b841278ea2c1472cc19\n.claude/agents/silent-failure-hunter.md 004ce0389356e0666d80c89cef39ad895ab326b3f7d19d34d23c70ab249a795d\n.claude/agents/strategist.md b217c7bd273e672de44d4df123201cc5c10203fa9bafad84a370d4b7d32b7fc6\n.claude/agents/strategist.md b8d0b387748b5403ee6a489c7e9969f864ebad9544d3d34ee9ff1b4d40f4662f\n.claude/commands/ecc/checkpoint.md 7462e42241b4e38ca4826750567002420f014089b81406279248e22e5879f608\n.claude/commands/ecc/e2e.md fdc040d9d32ca1c4bcc53ccca32042983f9132240fcaca5144de51e4385b9479\n.claude/commands/ecc/eval.md 586ffc66cf889428ff46a86c0d8067f925d58899758eea5f129c9fa62ca55f5f\n.claude/commands/ecc/evolve.md b4a635e1fc9e70f5f53c8bb36a1e8a385e249b5802afaffcc54e17cbacc1c8ca\n.claude/commands/ecc/harness-audit.md ba76cdd30f6fdcf9b6286de753881eebe0e15306829bc3cf53af1591e4126771\n.claude/commands/ecc/instinct-status.md 642b16d6b155f635b76800d78618f4525fc7db9fa298e947e7651e5b3d5fe85c\n.claude/commands/ecc/promote.md b8e6e79dd43053a183b426e88e31631dfe6c812efd37241e26798b88b3febdab\n.claude/commands/ecc/security-scan.md 440cb02294593a8cc86bb1e707fe43a28506f2833de6e20ec811cab12a543db3\n.claude/commands/uzys/auto.md 39e06b3164527fc59bd1cdbf156cdf1f953aab188c68603bce7a396f51921b40\n.claude/commands/uzys/build.md 3eed86d0f9bd8b60724d829a95578e7febab0043a6bdd6353993b2a55021a908\n.claude/commands/uzys/plan.md e1c11ecaa804b6a333f669a8a375b06fc8ba245d9683aed0f172c3fdadf8b4b7\n.claude/commands/uzys/review.md af4e3a4af894bc161f966c64fda60cdc83d815c174f3ad1f9c0a0d40d76a07b6\n.claude/commands/uzys/ship.md 572a644f37c41e1844b95701fe116b2b6e71f22d0ef8593725ab2de24d61207d\n.claude/commands/uzys/spec.md 91ec49031ec683dcd9c5a2befa9b8aca3c95ffb415928b95a2fefa113e102554\n.claude/commands/uzys/test.md 63e58f69bb431a3995b9baae6f6c55f958ae0c46a3d89740ec551fab4b3bb920\n.claude/hooks/agentshield-gate.sh 9493cb98409a5b7b4368307ecf535f39c32d933f8f012f79c9c2b6c2fa788012\n.claude/hooks/checkpoint-snapshot.sh 8ef77c09baa3e24740b19a7d119b83e58559cafcd8e6ab4edfaacc4e42caffef\n.claude/hooks/checkpoint-snapshot.sh f86e7f445cad48569353b9cbce6e162074fdb5224f4b0781734f5a200b94868c\n.claude/hooks/gate-check.sh f04831db7fbce538cc9c9cbcada62a7917e621e6275365686c4849e39da14583\n.claude/hooks/hito-counter.sh eb79ca20fcbaa070c621d5c985422e68e5dbe6b9f4c25f7bf81ae82fe77f6d49\n.claude/hooks/karpathy-gate.sh e72ea3c24cbb953ea7786504362a45f8ec07473a89aeb2006bde2fe0fb6b51c7\n.claude/hooks/mcp-pre-exec.sh 3a7b64149a45dca9e1c15a793e1e84cc28d1f7675d7b519c38cf4b4b27ef5219\n.claude/hooks/mcp-pre-exec.sh ad7f79a13eae3b59e530f00c5ca37b05b01df6629d2c09034bb4f3ac55429120\n.claude/hooks/protect-files.sh f18aef8b355be30afc14e097e8f152a0f00351342e9c1505859f400b445af242\n.claude/hooks/protect-files.sh 0190071614b9466bd4f29189b99f839eceecb08b480f397e5937f8087e6aac08\n.claude/hooks/protect-files.sh 416780a048c3bf674e4348e8982906ae768ca81480b46b56c0fad800d9f21d55\n.claude/hooks/protect-files.sh 5408a423eaade935d984d187f6f93387869cba9423b4644120f30025df4a7b5d\n.claude/hooks/session-start.sh 1c2ffddd804bc82bd456b6cf6e1566084f6c5b43d3596b418e982d073bdfdfb1\n.claude/hooks/session-start.sh b82d19dda66419967708a9937746d03bdd099886f9e7ed9103254209017f1147\n.claude/hooks/session-start.sh 894ebdc240b80b37da93b4124994471c1cb0a26d8592d7a621d8fa3a748e8c79\n.claude/hooks/session-start.sh dfad4b1add755a7efa89cacd370ba418c629f64911d1e0da1bcb19502bbc5948\n.claude/hooks/session-start.sh dcea99eab4c03341b2c571185e8433379e3c5bcfa5bed35d8f1979eee3a45c1a\n.claude/hooks/session-start.sh 490b1d99f6d901d63e48a8122d67225f5c48cc835db0f410fed91c364666ba2e\n.claude/hooks/session-start.sh 613156711d285ae2dc3ac6b1c26d72b7f92f90c80639c6aed1c5fe982bc68114\n.claude/hooks/session-start.sh 238e4a9565b2cde0809fed3b49059fa5b3189ad1c2e86d4963fcbf5e73de7152\n.claude/hooks/spec-drift-check.sh 48e2c5ed391086c842a9a647dcafe50d757513b993561c3137c9cf4ecf96f7b8\n.claude/hooks/spec-drift-check.sh d5bbbfbc3da3bc3eb2d3ac06cc10dc02426ec9472f4fba68300b895c3ffb24ec\n.claude/hooks/spec-drift-check.sh 43f01059a5cc1d779c9e80eb4207fb4e263d853f5a367dbfd45b0b77685d0e28\n.claude/hooks/spec-drift-check.sh d979fffa4f45c4ac03940a70abfa63860bf2a22fbdac9469a43f64565a6abdd2\n.claude/hooks/spec-drift-check.sh 1c7c81e4009866b204e03fd7c4eac87a1b66d7b467a747393dca2bab6793eee2\n.claude/hooks/task-brief-nudge.sh a5bca92a610efab0d770c96e12d3f6996cb184ae0175005724635a9a920fefac\n.claude/rules/api-contract.md ae12cbcacd6380d41d13ca87d0e6f5d589df9f8561f408618cb365e144afced3\n.claude/rules/benchmark-parity.md 595149aec4ed190e7ea96c11c271fa6b743f7ad8c38910dffd2cc180d9a51e8f\n.claude/rules/benchmark-parity.md ee33155ed0ac2c267d0ebacb5f89335cca7dd679c5b2d54ffec81c4738bd9be3\n.claude/rules/benchmark-parity.md b602bc9fc5627ec1e964915240f15748bd13e5bd99737346dbd8d3d2e7c633a8\n.claude/rules/benchmark-parity.md f98f9429c3250ba860b1d39ca304854de0c9e905cdd12fd068d57025e40350e3\n.claude/rules/change-management.md d21082487eeef59664899f85df6098bd46e3c74dbba3f2ebf9e49cf5bab64c34\n.claude/rules/change-management.md 62b7932f691664459d2d7b0971c6aaee22cfeb5f9103ca1145cc55631a072077\n.claude/rules/change-management.md 264b79b82de252cfc933d63708ac8bdf9f18c74f388063d62960456dfa764e2f\n.claude/rules/change-management.md 611bb3a89d47d5b43ac22de8be59edbdf9b700959901d1fa878dc0f89d9ec40e\n.claude/rules/change-management.md d2977a144259dc744835d9bb6c3cfa8d1b390f5393c34d883ba0f67a6bc4dc3e\n.claude/rules/change-management.md 20c548a1380f1e5e9aad5821e0bce0ae999a2dbcfc1f564a990bb30f3590d418\n.claude/rules/change-management.md 0c46f2fe8a9045c14a93895a1614b068d04a4b0e96c183d46ab688e57885c6a6\n.claude/rules/cli-development.md a0bfd2fe69336e8b3bf329a2029d926c96192bc24e784f22f7b99861358829b7\n.claude/rules/cli-development.md b116324d53c7ed3b36d51474f17bd61eede610dff7abea4d3533656dd5cf9a34\n.claude/rules/cli-development.md d5a267f7e22c9d292693857577c2e5819811147b561a38c15b450740d4dce98b\n.claude/rules/cli-development.md 158b76f589d0588f07453b4c47bf9368117467843cda37ddc8bec7197cd671b3\n.claude/rules/cli-development.md cc0545a04706c18304eb92c365d691986d511f02436f405894306e829b4f5b8e\n.claude/rules/cli-development.md 27f7696b1ed13ae15f9e0bfb4bdf2206325f42d87cca6e1a6efcc76f6c9d32bf\n.claude/rules/cli-development.md 73813a5f6f0111507430c1f83c9a099b870bc2a03c16a391817dc74444597eaa\n.claude/rules/code-style.md 2e67fc49d4c8eacaa6dac9064cf566d7f3039509a5b6c5f03b5b2126b0043def\n.claude/rules/data-analysis.md 81b87e41919f7a7a4b06ff459a127b1c44ff6804876856eac0b17da8b0568695\n.claude/rules/database.md a421fe0ffea76ec480cd9cc90ef753ceef7bff3ad4f49ee7837786960b634ba5\n.claude/rules/design-workflow.md 5b6a40c9004355420de2f647c782728e47eb6b01722d7453acf570b6aa002dc7\n.claude/rules/design-workflow.md 49fc2c7efedac467d52031b3bedd7d33fec9c73dff45d20071a5456b82015cd3\n.claude/rules/doc-governance.md 0618fa6f93d245cb46d42862b881fa840e54b196e759d748937d7098c60eed71\n.claude/rules/doc-governance.md e3b2197e19609724e6301ff6035ec9d34e5a60b5385bb4f48a2b633c3c3ca2d5\n.claude/rules/doc-governance.md 5569bb742cc2c0666b2431e3eb662d5fdeb99f8f7224506e0379ce618c4bdf61\n.claude/rules/doc-governance.md d83292a77146ae96e7adcb2dd796811ad8af979526e8d81b1a33bce8cebe6b7b\n.claude/rules/doc-governance.md 54157849bbf9897969745f1d162181b93f4dd9260ae18d4a890b6ad014232797\n.claude/rules/doc-governance.md 8d508bf15bbc1e87b8d0820a6e54a6d5de4b243ae8859ee98b6657d60dfbc7c7\n.claude/rules/doc-governance.md c6f2bb6210ae076acf15e6928a8bb20adbd9d40727433c462d5e1b5eb73803de\n.claude/rules/doc-governance.md 5257f74d94188b18977670a4299658673e00d7b227c54ebb19e00ab019d9adfb\n.claude/rules/doc-governance.md 364c147096985e1fad1a03457ac864a6e4839d8d9487a5c7f20fbaafeeb43eaa\n.claude/rules/doc-governance.md 589e70962b426503098c5d1a60da4b11f9a0c97a654d3ccac900d58f02b7fb52\n.claude/rules/doc-governance.md 7a79bba801a1fb3695566bc19c9bd2f89456219560d76ac4f2bcf405c238b6a1\n.claude/rules/doc-governance.md 3360574fb55f332cf3de18ea32506326fb2880651a593c0bec0246337633fe80\n.claude/rules/doc-governance.md c14e70d6fc5b0ca8e9385d293cccdc39512ec6e73fac6f34ed723572acbebd97\n.claude/rules/error-handling.md 5ea6a188e3a925d5f1f38c1defb766bf71af7a408b58ecce208c7fd3059aa843\n.claude/rules/gates-taxonomy.md 2276e03e41463f946d5b4867c953579aab91445bb781a4d24ecd9337d2f67e3d\n.claude/rules/gates-taxonomy.md 8a5162bd03f20c92230e2bd84a879de5f5d24a5746e6489f3dc895891055f4a2\n.claude/rules/git-policy.md 932e24935cdd20f088b4a3a75a493cb454e065c75e294283fd01a7a768bf9059\n.claude/rules/git-policy.md 3c0cf97e7ec6d921cb8b066d6d34a9f70f57fbdb0c78e9ab11cb919a4251da0d\n.claude/rules/git-policy.md 0d8d01c7b471b4623be31221e773b7298bf230aa102394e0384859db5393b0c6\n.claude/rules/git-policy.md 1c83e7e9acd5967d31406fec70a231cd8de09d5931eb7ca8ebed68f428c3d081\n.claude/rules/git-policy.md 421e837c323bed48fd7e51d02e3c27dd479037d67ecce9dbcc0da3b8c1e764b3\n.claude/rules/git-policy.md 02e127647bee532d0ffc5b994eb2f554f42e96b10529618f953e9b27a3ec0ab2\n.claude/rules/git-policy.md c4f5c1f4b33f3f91aebe673b736b3dc76db785a225d0214f29ab6ceccec26550\n.claude/rules/git-policy.md e1ebe8b7abf6e61015bef44c6f848f13ae8cafe16384b765b35905efecf0a21b\n.claude/rules/git-policy.md 469077325ea8c4a67dfe827014370bc099494998f55aa76f864f6411b40b07d8\n.claude/rules/htmx.md 4faf811dcf5b2b85f899507b249db4853b328c73b2d45760106c1b2132b53c83\n.claude/rules/nextjs.md 855eb2f49b830a89ebd9b92f29a05ea3348d6dda032d2a267c15a5c762371ea5\n.claude/rules/no-false-ship.md 964853f056a2ed898b09648faaf5302d471d3e723dc8e15d387e32aba82eee14\n.claude/rules/no-false-ship.md 2d760fd1f3387233703c04127bc595874a835ea46a50055d7c506cdbc7ece5cf\n.claude/rules/no-false-ship.md 9098f58fe799367cb6cfc02779715b02c4f0a2e5c24539fb2d65f5eb36332762\n.claude/rules/playwright-launch.md 770232379a9205f0466804b83edb4f53e1ccef2fcbc5e82b8e0c5b0a96fac783\n.claude/rules/playwright-launch.md 1bf90d35a050a1f0d712f406329d687bf9bc7661ed0a183e83613bfaef895820\n.claude/rules/playwright-launch.md 783be44da36bf9eaef56d0117d6cd19b7c0c1ef9242745b3f3f87721e84f2664\n.claude/rules/pyside6.md 19c092d5fd7691818e9a759bb23173c9aa1f511c4766a67a9e7fa918b7062852\n.claude/rules/shadcn.md af1fc4624cdede22a35d087b8a7fb4ef042302e2c998373c73f018736e68e5a4\n.claude/rules/ship-checklist.md 14319e7639aa4827690099efd5ad228c997e56a9002eeba6c90e7f274c44bb12\n.claude/rules/ship-checklist.md d371f417f9af32a72afa99e38378d5a95a085ea5c8ad106328cf41a5f61cf117\n.claude/rules/ship-checklist.md 27d3885cc3944a1a4d92dbd8e0d51ac943d7971d2284db77244deafe007c2906\n.claude/rules/ship-checklist.md 286fe3b971c1aa72bb41f610c576ef6d23a9778ed4ab4cbbb493d1f241494a44\n.claude/rules/ship-checklist.md e5d1dc357c54026f473ffba52b6ae3746ef49173b8270fea6faf16fbc2ec7b10\n.claude/rules/ship-checklist.md 6825d1ce40a817f9fa004256b17b0685eb1ccb69ea27641c97e53dd5e323e755\n.claude/rules/ship-checklist.md e713a2738dad8a03d16f0cb1b24a7b207bb4a1445766f883391a0b4a8ad6a766\n.claude/rules/ship-checklist.md 0d9988b3d6b387dd13738c5f1667a8f07e72929f5515168e2ef6e94ad4c0da88\n.claude/rules/ship-checklist.md 1b42169da6e29bfee8ea695831154f672d7cabd8816d1993678c4955a43ed1e4\n.claude/rules/ship-checklist.md 9423f862188ea0ddcb5ec9594e457988c23dc80a0bdba7ed6cc7630f8cc4623e\n.claude/rules/ship-checklist.md be7e93da56b688c9b90d5392dc29b16197d5d4857152f073eb39d1c737952a02\n.claude/rules/tauri.md dd429a144c71f1205384abb5c305b373908e8f6c2c4ae7d6b4dfa9fa707e972d\n.claude/rules/test-policy.md d775453a9b0c3fd7f65eba039e512ec6fc92eda448a2218738da6bb1c85a3023\n.claude/rules/test-policy.md a106e6952520eaa2cebedb9a20a47ee12a8bd0069639077f324cc1d93e3d5501\n.claude/rules/test-policy.md 1b6f0c2fab4842c974c11dd53b22320fb5593870390e0c375625ec584196e580\n.claude/rules/test-policy.md cc73a7e656c237c4313bfa814a9eadab5a44542ff8dbf250fceaf97d6ae1f05b\n.claude/rules/test-policy.md 87660a29cbf43f49e24fd7f53124f0383c94c8333138d1cc1ab9dc233b5e8a4a\n.claude/rules/test-policy.md 54426764c08cadc8c1b31bdaa5817f5c4aefc359d263bd13051949aec7858c0b\n.claude/rules/test-policy.md 9f1e71e12db41a3b20653854c66adcb224c47e86d6a5815d3b7002418cae458b\n.claude/rules/test-policy.md 95b9c6af6a63e4cc85488e109f42440b7f7d1892c73d5b96b7ab387354303cf4\n.claude/rules/test-policy.md 3460db5bbfaea281e417c55b5a76236b10c64c38cf38fe16b934f6799c1268b5\n.claude/rules/ui-components.md 836b66be19c773b3cfa2ee92ed29bd802d8883a55da21cf0be9b73e25a2c8ebe\n.claude/skills/agent-introspection-debugging/SKILL.md aff0d52785d3013d9f98de95ca778bfcab7c566db20352ead42852a1659292d2\n.claude/skills/agent-introspection-debugging/SKILL.md f03dc3838ba8b81ad6a87959a2d666f8d5915d3516cea60d12a65ce74685c54e\n.claude/skills/asis-tobe-decision/SKILL.md 8880ea604b8458acd30b8dc515efc54a6a1640590c572d5f4be7034cbd2d7242\n.claude/skills/audit-harness-fit/README.md 885c756b8fbe51d23747667d3d96ba8c76e1c0e5fb48030cd394b0dad4c304f5\n.claude/skills/audit-harness-fit/README.md e1295cd42a0671d373d7d071374918abdc37aa294052ed6bde8511ac07e80039\n.claude/skills/audit-harness-fit/README.md 2aa6eb69e9991d0156b140f5c4ca36dd87b32ad006aa8dc176f173e7a864ecb9\n.claude/skills/audit-harness-fit/SKILL.md 6919e5c6fce93b4262392ddd9e0476b700e921e39a91ab646bf48f05f4fbe39a\n.claude/skills/audit-harness-fit/SKILL.md ccf5817e3ebcc7575e409b4a1c397f2ef23d690e355490487b2bd829736d1ab5\n.claude/skills/audit-harness-fit/SKILL.md 85c489aec23eb0b9f907c3b10395dabbe03add8300f6d365e886e02f833fd769\n.claude/skills/audit-harness-fit/SKILL.md 5f20eaac20e77c3207978857e2424bcb86d5681362528eed7cc3a09afdffb845\n.claude/skills/audit-harness-fit/SKILL.md 3c348fe635f8b389a349db3f686ebca4f49e1ef7058aa3b4c6e0211e51c12562\n.claude/skills/audit-harness-fit/SKILL.md 9dfd4565013a630c0aa66c9396b4d4073fe70f8601bff89989ebe162da7beb76\n.claude/skills/audit-harness-fit/SKILL.md a23c98c78fd9c5f4660dd73058d58eaf78aa49c107bbc0e3c60de73231f5c163\n.claude/skills/audit-harness-fit/evals/scenarios.yaml 016257464f98f62433e2aba5a064405f41128b54eba05e3702cf44ce93d97ae6\n.claude/skills/audit-harness-fit/evals/scenarios.yaml ac6355013fb7be658196e135ac449bfa5afdc564adb1d5d1ed10d65a64d16e04\n.claude/skills/audit-harness-fit/evals/scenarios.yaml 714fe2ef457dd38fc9b3b354b7f2d616c5d7121770ef1dd889d008eaec661159\n.claude/skills/audit-harness-fit/references/apply.md e4803a5d6d6c0d32c9808172a03f0f555320ef29fdf73277dd4fc535c4474d77\n.claude/skills/audit-harness-fit/references/apply.md 97886bd748ef2e2d9a979dd0cf65c5acf81a90c2a19d08851ce87c66df933f92\n.claude/skills/audit-harness-fit/references/apply.md 6f38cb6673546bfe6609ff6ae18436e5487f93386ac1bfb4625f9c85997c36e6\n.claude/skills/audit-harness-fit/references/audit.md 39d077f20624e69582ac0e2df28d192c08e8246f8c7dfb8e471e7c461f1cc5f0\n.claude/skills/audit-harness-fit/references/audit.md 973605514d4eb560b79ff267058e6dbd47bd61b53752f3f91e7f49a4904eac4f\n.claude/skills/audit-harness-fit/references/audit.md 328d620140846005bf326998387e9305371bf95b5e95cb3a9e7272377e26277a\n.claude/skills/audit-harness-fit/references/audit.md 4b4eef5a9060ad0a7fb264b261c69802c1c3f0e7c61787ccca18be898c84c091\n.claude/skills/audit-harness-fit/references/audit.md b93eba13d00481e391fc05e70d894164661ae689f28b08d47514e2c35ebfc790\n.claude/skills/audit-harness-fit/references/official-criteria.md b80c77f12fdf1752f671eb1c0f8edb6f4f34c3777d6753b9af713f91cff77222\n.claude/skills/audit-harness-fit/references/populate.md 48227ddac79899f725769050f6105aff0b352d47f72b01341e836362db943ad3\n.claude/skills/audit-harness-fit/references/populate.md eabe235e5e097ad44a42588201ed89dca359e9fd1a301e29be02deb0338d38cf\n.claude/skills/audit-harness-fit/references/verification.md deff70dc0f419a760e8b280a2ae57b43a0ae859c17fb6627ea749741b855ff62\n.claude/skills/audit-harness-fit/references/verification.md aa367e0655a98ce575919e5c30687713187346a7976b97fc1081a99bbbef6ce3\n.claude/skills/audit-harness-fit/references/verification.md 18bd5f577b85cb043229ca9df160ec9c0f3fb70d01c3db5abb5ccbba7ae6dab1\n.claude/skills/audit-harness-fit/references/verification.md 695c1f08c6bee5de28d0edfec796df12ca5fbff8cdb473bafbda6fb7f3cff79a\n.claude/skills/audit-service-gaps/SKILL.md aefd2a47a0b9c2b6bd1846151e60cef0147d0bfbb4b1933da2431f980a0f2922\n.claude/skills/audit-service-gaps/SKILL.md ba9ac6df2315538935aff945f25a7596b3a9e478c4cdcb3a218ff404f4b612d7\n.claude/skills/audit-service-gaps/SKILL.md 81323cc7d4bda9f3739e8a72f566699d209f888aa216f77f106e77db975f231d\n.claude/skills/audit-service-gaps/SKILL.md bbc7782ea688ae74f12934145c1041f58a029eb07d9979c316849cb9fb88f56e\n.claude/skills/audit-service-gaps/references/modes-and-evidence.md fa73b3edf2c55f5caf47f1f2d930d8c2ccd737af0a2def5f40a1db942029275d\n.claude/skills/audit-service-gaps/references/worked-example.md e5f31f498e864dcaabe2a0d6a05fe1d64cd8be6a1b79a18de78bcbb42e06d658\n.claude/skills/chatgpt-delegate/SKILL.md 0e614e643bb8bad52db3663ff9394f77ef6c89a6c72c529796deeed824eca0f0\n.claude/skills/clear-korean-communication/SKILL.md a8afb5e0114332fd7831de12a12cd981e95726458df61f1a3fdb6160086aedc5\n.claude/skills/clear-korean-communication/SKILL.md fe480eb8357a79abcc1f6c5954c61f0a8b3e7479b9835b7fbdead18a9dac4b46\n.claude/skills/clear-korean-communication/SKILL.md a8a41bd88b2b04c931047f08614f8c431eb8e401fe366d110291201a9fb571e8\n.claude/skills/clear-korean-communication/references/pre-send-checklist.md 65f93177e9a442332c2c90f4e905c4d558b6349e47d5c86eee7705b76b71a1b9\n.claude/skills/clear-korean-communication/references/why-it-works.md d4439db6a1765fab5fa6875d0528fe715366c270ea35853ad8bb1404388200cb\n.claude/skills/clear-korean-communication/references/worked-examples.md d53369cf02c62147510bae3a0cfa878ce89013bed11a30c32cc675dd0a0df825\n.claude/skills/codex-consult/SKILL.md 0af4f6cd00739e62d73f7adc9c8ea85b0db3ee53432bc5605fbf6bf788a0cd13\n.claude/skills/codex-consult/SKILL.md 288d6d5f4135b8f3e1d8b06b42b68c238e63c9257f973ecdc4f86445d5619be1\n.claude/skills/codex-consult/SKILL.md 767b0d65ff96f0a10c2cd81335a5ded2a85394b8e8ab3aa1c7737c6296f22582\n.claude/skills/codex-consult/scripts/codex-ask.sh 64835a9e11094bc24d9e8f9044a98a699ca45155d516dba926ffb8c145cbf1f6\n.claude/skills/compaction-handoff/SKILL.md 1b19203ae45869dd43b7ca4b28c63bb5c4267aefc9a62abb17d29bdceee316a9\n.claude/skills/compaction-handoff/SKILL.md b484a4408f25f4b78bf2e7cd88b1b3fec7b969d6c4a03de8a3b8e072719fc74f\n.claude/skills/compaction-handoff/SKILL.md 42d8300cddc4bbe78b353c8ff846b5fd8ed2b4a68f1998982900fd31596c1755\n.claude/skills/compaction-handoff/SKILL.md 59191dc216233b74e5f5a72ed1041d8c236504a386e8b5df81dbf52062dbaad9\n.claude/skills/compaction-handoff/SKILL.md fe70142b0bbf21b5a7b6e9e5af8f0011e6b8e654746f97f4391384d5143c9701\n.claude/skills/continuous-learning-v2/SKILL.md 7629ed1a18ce078b57f22f407115a41a7f404aee73de17667ec5571b0001a8e4\n.claude/skills/continuous-learning-v2/SKILL.md 75485ddb37e2d1d6f056b404d9939bf46a2b409b1286aec209a510112e5b64b1\n.claude/skills/continuous-learning-v2/agents/observer-loop.sh 25ceefcc260c3599b6d12148d1a573110ace3091c723bdee3a3cf10692e5d4e2\n.claude/skills/continuous-learning-v2/agents/observer.md 320d50ff2965efeabd6f07c2be134e11da160293e958c1bc27c309d613839ad2\n.claude/skills/continuous-learning-v2/agents/session-guardian.sh ee6e4c0990f8d538cae1c85aa194e01e9ca2336e5d993ef68eb608b96cb0d82c\n.claude/skills/continuous-learning-v2/agents/start-observer.sh 6b1966e38eb0c96d61de5d204b87097592884364be59e5bea6007289e8068936\n.claude/skills/continuous-learning-v2/config.json 6f17e7a99aafe252ecd0b9212fb5f606be50b8ce1685f5a6435e46e87ffeb970\n.claude/skills/continuous-learning-v2/hooks/observe.sh 285c673af5a5f5fcc0839e924ccdb4b7326c593f9198f690c635b2fa7091feb6\n.claude/skills/continuous-learning-v2/hooks/observe.sh e632c2f486248b8b141059c23586d5ca0187df8c34f53d7597cd845713e06948\n.claude/skills/continuous-learning-v2/scripts/detect-project.sh 527917b87d8b35b38baf01ca96c6fc4cbeb91ed9a24c6f9c09e4f4d290555295\n.claude/skills/continuous-learning-v2/scripts/detect-project.sh a39677edd0ce1deb7628ed31610baafc16090a710233963d2b6a55db64167cf9\n.claude/skills/continuous-learning-v2/scripts/instinct-cli.py 777193745515c8dce35676b1696c90a85e2610705157205a0b54e8e453b69942\n.claude/skills/continuous-learning-v2/scripts/instinct-cli.py 0b060a762f0fc4ad56e45cbaa14c14fb9ab3f468763f6a32a17bb864fbd3fc30\n.claude/skills/continuous-learning-v2/scripts/lib/homunculus-dir.sh 99dfa99ae0f7c6e2e15ed5b5bf3c46e91a5f089f4060ba93d5da5e85e04fd308\n.claude/skills/continuous-learning-v2/scripts/migrate-homunculus.sh 34f121c143a676d330f06209618aae8cb08f8155298cb5fa70c893314bf9b303\n.claude/skills/continuous-learning-v2/scripts/test_parse_instinct.py acf556e8a8023347b35988bb490927a4e9f65f2579b03f26cad610687656605b\n.claude/skills/deep-research/SKILL.md 2d631427fc9aba27a318735f930bdb49f5c6ce416506340025eac01648f1df09\n.claude/skills/deep-research/SKILL.md 69be70af34e27112f192cbe201210c4cf541c24952670487213de715aa297b40\n.claude/skills/deep-research/SKILL.md b852bebb31908f352c5397bde6de06ee7ca3c0e1c866fae4dedbd5c4fbb27323\n.claude/skills/deep-research/agents/openai.yaml cb877e559cfe405d0e12bfc9bcaef657ab05a30ef698e6f3e486e39c5e0a6d2d\n.claude/skills/e2e-testing/SKILL.md f84ae93c159bf6ecef5a60c9735dc82d96bc5f977ef52eb71678b66722acf684\n.claude/skills/e2e-testing/agents/openai.yaml 5f50508a300616d77f39dc50a51de10cabd05ef7ae770e7948ef6c28f340cdf2\n.claude/skills/eval-harness/SKILL.md 03fc47842b35861231efdd1d15de291942915c372b8ffcc05a22e942d1233ced\n.claude/skills/eval-harness/SKILL.md 98f10c6cfc7fd041a358093f5c700b489d431879b90db778db7623a3c1c5404f\n.claude/skills/eval-harness/SKILL.md 12399b9de9e044273fd4dd6a8177bc4f17db008a33df83557ef56770b1466a56\n.claude/skills/eval-harness/agents/openai.yaml 6f2c19eb59062ca776286c2f6a6a7f1b552b4f927a2b48de888a7dbb2eeec6bd\n.claude/skills/explain-plainly/SKILL.md 7838e4f78b7d4d46d31a7c932f069fc66ec4bcd873e49fb57f37923be188fd0d\n.claude/skills/external-model-consult/SKILL.md b1d7db909a459708ed721f5a7eeeeebe48fb3d57448f763e7e0000df5426a241\n.claude/skills/external-model-consult/SKILL.md c33d6d2e02aa9631fcb4e91aae83b56834307b0290a984af55378d2f0011d7dd\n.claude/skills/external-model-consult/SKILL.md 24cafc7b50fde21eb874fc2178d3ebd7eca3b1c54e9278d60a088f9079b7b534\n.claude/skills/external-model-consult/SKILL.md 2d115e29ad53d778ac4d4f942ec2039b441ecc571d612d27cc725695641f4db1\n.claude/skills/external-model-consult/SKILL.md a4d429bcd4737fbb2a90de6b9beb9cdf0488fe385eb0f6fb395f0e76322dbb0e\n.claude/skills/external-model-consult/references/direct-calls.md 6bf094fe54bcb09285ba552371eabf1d743545eccdb118157a8aea987b7c8147\n.claude/skills/external-model-consult/references/direct-calls.md 966db9c1f80c367ffc81842b83326a67190cc97287e7d624c0c16ee65fd4b16e\n.claude/skills/external-model-consult/references/model-evidence.md 27dfc0eaac4bbf97dbe4430852e947d99a7bd895aeaa7f1d565915c2a2fdef1d\n.claude/skills/external-model-consult/references/prompt-templates.md 2277839d5eb9ecbf8535f51a1f1ca97d55816e47b6ea578f34e9637280b1b5ea\n.claude/skills/external-model-consult/scripts/codex-ask.sh 64835a9e11094bc24d9e8f9044a98a699ca45155d516dba926ffb8c145cbf1f6\n.claude/skills/external-model-consult/scripts/codex-ask.sh 75f24da435dff5ea7ed7636d8b121456d1e91806e9a114ea993862e173d84d72\n.claude/skills/external-model-consult/scripts/codex-ask.sh c644272c25f3d0d42ab63bc8c4acf81c1ae90ce1af5f7780977dd63f464fda7b\n.claude/skills/external-model-consult/scripts/gemini-ask.sh 6736c394de1d059908ebed770221b298f37d09e01aa3eb58991265c2d45915cc\n.claude/skills/external-model-consult/scripts/gemini-ask.sh a598276b4e2d9e4a54b704229822d8da49a6c54497c8f2471af5420b4c3017c3\n.claude/skills/external-model-consult/scripts/gemini-ask.sh 9c5eb72c61d140228dff796ff599f88982b76763abbf52aaa31486ddeb03e9e8\n.claude/skills/gap-analysis-e2e/SKILL.md ef28d245d354c07de60244778469fcc344385a584eddba33c67e9b096654ba4f\n.claude/skills/gap-analysis-e2e/SKILL.md 739e9a181eac99c749b585fc4f2b64a30dcd4783083422afde64ea85a9a2b062\n.claude/skills/gemini-consult/SKILL.md f096ad4e6a16e4e6d33ff7d817382e3f8c07311fd7c1f809ae91a951a9f58856\n.claude/skills/gemini-consult/SKILL.md 24c7f1a8855f5f44447015990991c47c80dc14fd9e373dd818084f8870ff8004\n.claude/skills/gemini-consult/SKILL.md 9ec900de7b699b73addd0e6259b4a2fc8bbf559d22662842424a1402cb581793\n.claude/skills/gemini-consult/SKILL.md 81cf4563f9fc790a64796a24e6f85f3b33bffc7e31989dbaebedcc2452984805\n.claude/skills/gemini-consult/scripts/gemini-ask.sh d9f4cc371e3e3b1919dd773d41b351484c9a3612487bee1ba314378a8486a39c\n.claude/skills/gemini-consult/scripts/gemini-ask.sh 87b065bec8c13b28dcc451235dcf9679d97af4f68ec678153e23ee14f7c94d0a\n.claude/skills/gemini-consult/scripts/gemini-ask.sh 6736c394de1d059908ebed770221b298f37d09e01aa3eb58991265c2d45915cc\n.claude/skills/gemini-delegate/SKILL.md e3d569add39fecf8c412d76c287e88054710f70b87ac38d88c20ad7b00b301fa\n.claude/skills/genspark-delegate/SKILL.md 3f495d55f1968730c9ce66009745530c16adee5110267e7b735f6a0333e2bcf2\n.claude/skills/gh-issue-workflow/ISSUE.template.md e004bcbf6199486d527feea43aa68be108bfc8fd3b13800bc543baa778291dc0\n.claude/skills/gh-issue-workflow/ISSUE.template.md c30fbe41d1d6ca15a82c031441c8b20a6db20cabd3d52c9c2c7ec70ca97d60fa\n.claude/skills/gh-issue-workflow/ISSUE.template.md 6c897058f7cfbc7b3eb5a3688f21a106fda39b7ded0f05570cff349214aca427\n.claude/skills/gh-issue-workflow/SKILL.md f2b906076945adce0cfd80dd9331420eea0bd1c1758b76e1f300c82811b06f50\n.claude/skills/gh-issue-workflow/SKILL.md 99736619cdb450870fb8b51e9ef3375fdd4c625b5d23e37c75078e40b1f1dd32\n.claude/skills/gh-issue-workflow/SKILL.md 00dc33d939b034dac6c653a07a64da5b362e350bd9133def2f11420c94353008\n.claude/skills/gh-issue-workflow/SKILL.md ea1012246121662e0604e296913b63e7ce5a02b902182c86102dabe5e13563fd\n.claude/skills/gh-issue-workflow/SKILL.md 0b1ce6a696c1388e928240ec787b449dd0b0a92749789fe28f8627fbb9aee880\n.claude/skills/gh-issue-workflow/SKILL.md 578c25a3687c7c6557c2bd115489f44a49d5668e2cb97e114a009d7066db7a72\n.claude/skills/gh-issue-workflow/SKILL.md 7128c3faddf72ce6285b3a0e7179da01d6a434659ccef6f9b51851b3f5f50d17\n.claude/skills/gh-issue-workflow/references/hierarchy.md 0e16e38d5919e5eed0aa044899895fb13c3846a333f3470d8001bda09ba959c4\n.claude/skills/gh-issue-workflow/references/operating-model.md c6feec997887d05d3385441258c464609e52c4bbebb7c0b7390a5d6016ad9a0a\n.claude/skills/gh-issue-workflow/references/readiness.md 08df321b014d96f8d5b5e0c7af7f36d09d719fb098bdaec7928af4a77c1866cb\n.claude/skills/harness-health-audit/SKILL.md 12932ba2e5b77a7efab0ae2d0fca334bf249503a583de5a3933105fba89c94ef\n.claude/skills/harness-health-audit/SKILL.md 6a843a002f9a7ca9c04816214509caccc1cc3f3661595939cc4096366847f09e\n.claude/skills/harness-health-audit/SKILL.md 16501e7db181780684612f4f38db80861e775cc1204f444169efdbc5d9165bb3\n.claude/skills/harness-health-audit/SKILL.md b481d7844303896a24a3a95269a035ee57690d8c15c5ad5e85f16227fa5f7f27\n.claude/skills/harness-health-audit/scripts/observation-digest.mjs cfedf96b819819142f2d69bdd6663bcbdd56301c429545b4f31cc2f358249cb8\n.claude/skills/harness-health-audit/scripts/observation-digest.mjs 7c9c42fed4c3ab1a0df6363386c34e03776a53f6b32aa1bef3c6899dcd77aa5f\n.claude/skills/humanize-korean/SKILL.md 7d0d77c25f5990f203480abc6a02772d534a40ebfff4ab4086cf5da3ed1b7afd\n.claude/skills/investor-materials/SKILL.md 0541ac8ede8d39c59a646834481d0de86312e598331b86dc7027ece0def3c193\n.claude/skills/investor-outreach/SKILL.md ab71ee37a1ff5f9b7f27908d33fc47f03e7897e6c87be3f8d7c0e4a8d73fefdd\n.claude/skills/market-research/SKILL.md 7ef3fa13d6272fd295eeb2aca9f6a2f09f6a8270cf33b0675fc5a0018cf6e406\n.claude/skills/market-research/agents/openai.yaml 42e1b1bf676506bc50027131cd8226b151f48a57551197308102dd01fd080943\n.claude/skills/model-orchestration/SKILL.md da6b448d0119e7dfd441f47c9afa267c227ac9301561ddf07c3fdc81bf494ff6\n.claude/skills/model-orchestration/SKILL.md d4ec28fa8e7bcf48607a17dc84a9fb873dd9f58f6ffef38ae5324f94477160a1\n.claude/skills/model-orchestration/SKILL.md 2282ac0ca6c06006d36616295dbbb78bb593b93e06712baf8be6b6466426c540\n.claude/skills/model-orchestration/SKILL.md 7a60184798f2c7ab28d81af37bf61d4785b0831836f0be1e933d279c1b552161\n.claude/skills/model-orchestration/SKILL.md 2210704bbb4ae972e74f1062512accd53882a405fb91adb0461ad9a2325774a6\n.claude/skills/model-orchestration/SKILL.md a4447594e45e598028c5f614cc46d1555c6cbf20e0edce1c9d8a30ca17798268\n.claude/skills/model-orchestration/SKILL.md d8d1982fbc76d572b2f6a3ffd07cba944d3e734987fc5bc66002de5f86accd3b\n.claude/skills/model-orchestration/SKILL.md a67b5948e8072a87669621feda866198ffadd7c76979fdc3a0b1d8cf2a4ac8bd\n.claude/skills/model-orchestration/SKILL.md 34add0d50a3ed2687b4ac092a4146928b841f5f6a8bbbfd09a42e2b59f4ef2cf\n.claude/skills/model-orchestration/SKILL.md 6320632e46180275ee36a9e0897fd616ceb1b8ce7ffdf0647c871b293ae5c846\n.claude/skills/model-orchestration/SKILL.md a22190744eccc76d1f2d8995a1d66395e4ee173935b2114a334eff95239c234b\n.claude/skills/model-orchestration/SKILL.md 19d64858ca267987eb9be129a588b7e76ed86b7f788accf019e3f2c2db493117\n.claude/skills/model-orchestration/SKILL.md 65f0e657979e6b6c0fdacdd5b2cc73f73bb0cdb866c7c8ae1f099c54747c46fc\n.claude/skills/model-orchestration/SKILL.md 78cb2ac0f153afc4deb3ccc88b40abc7bab1f2b9ab8138d2fd3998082f380178\n.claude/skills/multi-model-debate/SKILL.md b6e4e4855ca36bcc2a720d926be68008259e0604c8c9dcc8b00cdd65c2bc48ec\n.claude/skills/multi-model-debate/agents/openai.yaml 64eb836095ff28669ebfd41d133a63e29faec0542dfc90862e10ed7fd275071e\n.claude/skills/multi-model-debate/references/adapters.md 06d54103576b4de93b9856c26f773842cfbb81b9491fe95c55354bd324fe4c51\n.claude/skills/multi-model-debate/references/evaluation.md 34f14eeecf6912c8afe5eefa1431744351b7911525d0f6eb612120d31170b290\n.claude/skills/multi-model-debate/references/prompts.md 45b348552563a499e221edd6fd75fb1d8e77b389aa25a9cc56688547e68434dc\n.claude/skills/multi-model-debate/references/research.md d99201f4774c7b3648e5bb3953c2e2979af752c1ccd8fe5013c8b6bea155e949\n.claude/skills/multi-persona-review/SKILL.md 8fa6a0d476b50e1c6b14fc62c31b03c8ea983711d24cc21351589ebc8f1dcd57\n.claude/skills/multi-persona-review/SKILL.md f0ac01160ebf176617cbe9d606a2b258eef8741c05b4a972f813ac90dd6e9dba\n.claude/skills/multi-persona-review/SKILL.md 4f221fec547e838d4698c4e2eaf82d6ebabbc52004be8933131e84add66fb397\n.claude/skills/multi-persona-review/SKILL.md 5339755ba6c7d61b82a1e06f1dde8ef46f5ee263092a7cc5cc6d300ae9bc79b0\n.claude/skills/multi-persona-review/SKILL.md b23879e303b7d0c95d30a9369f20b1928f93c24114b891a29f8bb17816cfac20\n.claude/skills/multi-persona-review/SKILL.md 29b1022bf403005764c95670b6d29ea3695ca935261afbf248c452058b2e2d56\n.claude/skills/multi-persona-review/SKILL.md 17f9f1a0c224168fb4c773f87f8ad84a4d7db9082088e096262aa71e1916f0f6\n.claude/skills/multi-persona-review/SKILL.md 6da7f5ccfae30d3d7e4ee77f90a06d7de30a532b590482a01d779addd912864e\n.claude/skills/multi-persona-review/references/reviewer-design.md 327b70bdedff29868b6b3b88d2279db572a811eb19defe8ec0c724e3a007cb45\n.claude/skills/multi-persona-review/references/reviewer-design.md 4afc9d327f91139b4d272a54e1b1992766b7e0482b4479c76ca9b5284e28d7c8\n.claude/skills/multi-persona-review/references/worked-example.md 95b48adac8ae5a91cb60fb9d5c32c90365734225775f0f85f26fb72404345943\n.claude/skills/natural-korean/SKILL.md 0057cef32a4accff3efa155a4916ac65fc283ee766abb2951227da91fa8bdee0\n.claude/skills/nextjs-turbopack/SKILL.md f65da15aabeb9dca7ae08525739e72ab16e79a78ff4004f7cab6ad2de1c2a79b\n.claude/skills/north-star/NORTH_STAR.template.md f778da89c561cf92b987bc1d46e5eb8c979f2422882714b0a0602ccce62a7b5d\n.claude/skills/north-star/NORTH_STAR.template.md 401ac922beb41b5b3c6d19313d5a46184cc90808aa330e0e288108355b5f8aff\n.claude/skills/north-star/NORTH_STAR.template.md 23a071af1893e3be7860111d448d02a55f965519811bdadafdba4231607d66c3\n.claude/skills/north-star/NORTH_STAR.template.md 741efd7d4c477f95ecd1fca85656d4582a694b3753ece77378ddab8ba0f0b69e\n.claude/skills/north-star/SKILL.md f73b7ce6a73bb3666ff3c18557ba37248a844107d54ffbaf5bb3d562fc2e7f61\n.claude/skills/north-star/SKILL.md e335733ad2c6dab82d104f868c5ce3d3de99ee1f139330eba9ff497529bd9485\n.claude/skills/north-star/SKILL.md 5b1f8176bb264e5a426594704e640eb69605d6482d6ba93f53329e9fe08dadcb\n.claude/skills/north-star/SKILL.md e37ed854c29f26f64a96ccd4847e71723f5bab9a56d3c8f53a4f9815120085fe\n.claude/skills/north-star/SKILL.md b11245dfa084bcd8b1fa4f7eb186e7a6ed97b30905a1507992c35448af45cc62\n.claude/skills/north-star/SKILL.md fa4427fb57243aed96d639362d97c69202170dfa949bfb0818f4600b1cd8432f\n.claude/skills/north-star/SKILL.md 0616424076bb18c4388d9e48ac4e4bacb51106b0b8bdcf6be223e413e0c43bf9\n.claude/skills/north-star/references/roadmap-method.md 78b6d7e05e014c5c7ecd5680767ed2469c6a147faf053b09f448f63cebda9da7\n.claude/skills/north-star/references/roadmap-method.md fd58a20159eb0b14b69781ea2536be1fdebd78c9eb83658c2b76de78efd21bab\n.claude/skills/north-star/references/roadmap-method.md 32e812eac7356e15704d0eff162f3fe3d93f1de880c7107017c1d802463190b6\n.claude/skills/northstar-roadmap/SKILL.md 489e7a70f81d80c296dd9160debe7ef69381506f9ac00874cfb48e2b61e58736\n.claude/skills/northstar-roadmap/SKILL.md 9a632a49e6bbf877fee61d448046444d57127b75b4f76f9b8ddf8946c8b7990b\n.claude/skills/objective-brief/SKILL.md 4df0bbd72dd7ea698d7d9d3b10476c4c4ff3e6bc3d9a1d29531e083c9e9bf59a\n.claude/skills/objective-brief/SKILL.md 51fa706442849b5f74e71c23fab8952526dd3a60ab1d78a3fe53d2631f24e47d\n.claude/skills/objective-brief/SKILL.md 531c8f0f09492e4a1643a5de38dbc293be9981a5c81ebc7f783620d5380a5806\n.claude/skills/python-patterns/SKILL.md df0c0abbbd0e6a01811d1b1d10d8fa7a8a19ce73afdc9432d24843f62649cdd2\n.claude/skills/python-testing/SKILL.md b304a8c503af32644f97a889c5545ee4f90e91980462ad4ce33fee3dea6f414f\n.claude/skills/recurrence-prevention/SKILL.md ab537b5f35be053ffa930f99c5b3d972b80fd621d18eda8390506d01503f6d78\n.claude/skills/recurrence-prevention/SKILL.md 905324644b719f2cfc2744be0bb21922c9e4e6534a83c6b8cad980d6327b92be\n.claude/skills/recurrence-prevention/SKILL.md 40b8e5a14136cecf12a63cc2f709de2d80290e352aee88aa9a482200a3fd7295\n.claude/skills/recurrence-prevention/SKILL.md 8a26b9d530305497fe11b39e31eb7806d835953c2585fc15fcf51488d7fe6279\n.claude/skills/recurrence-prevention/SKILL.md 62fe94e2cd34d9c3c40871766619d5c37c957e89247c5d281e62c85de51326de\n.claude/skills/recurrence-prevention/SKILL.md a626f5760ffb7446695a578104baef9ba9259b3b41115b00d699c43d260c0295\n.claude/skills/recurrence-prevention/SKILL.md 9e44542d54006e02ae1d906346a90c0ad0e26c1797496e791c83f7bb0e867cc6\n.claude/skills/recurrence-prevention/SKILL.md c7c163aceb457535adb3c3052fe41355048716db2cbb0b4cc768ca6fd4533f26\n.claude/skills/recurrence-prevention/SKILL.md 46614922e61e79235493c80664da2f9e7d95d034c58c9a646b948a7c2a1f70b2\n.claude/skills/recurrence-prevention/references/failure-analysis.md 1dd840db9a2bc662ec9688833988d93e80d0be245e7edd727c2d065a9d7d9eca\n.claude/skills/recurrence-prevention/references/failure-analysis.md 5fc80d28b3f52e92843e9ef431a9dab74d8b43cb3cfd7ed5d2aa359ad00586bc\n.claude/skills/self-hosted-github-runner/SKILL.md 4e5b86b9c7c8527599cfd8be61f4ed3ab67d439bb791f4068d85e52c9327e5df\n.claude/skills/self-hosted-github-runner/SKILL.md a1394fde762c88fddc88db1502b15aabe88bdfc1c565e77bd3cdbb0512352d10\n.claude/skills/spec-scaling/SKILL.md fd28afb5558e2ba4168b0b16bc19b1c4d2fb732741649010a1601f751f9eafc5\n.claude/skills/spec-scaling/SKILL.md 93fe3ed443754bd368a45e71145434c1fd7efd349e25b0179bbb63774ac4ca0e\n.claude/skills/strategic-compact/SKILL.md c8e861a39fb84968aefa82783c1b25e61d1b625c29c0f5b2bb301f4652c93e9d\n.claude/skills/strategic-compact/SKILL.md 6bfc63c380eb89886335ad033f759a4ff3f1b1ae2c5a143598bb628e38c6e769\n.claude/skills/strategic-compact/suggest-compact.sh 62af1630b000713842e363447675ad5a94f72cddf47241df68fc9883de686689\n.claude/skills/task-brief/SKILL.md 69125822c0c7da8daa6e67117f0845e3388284b0d4b7cc2b69f440c9b865b454\n.claude/skills/task-brief/SKILL.md 0859fc0b6cf760edb83b4c41cb8156cc90b7c62f1d5b5dd364acd4c5f7935cc2\n.claude/skills/ui-visual-review/SKILL.md 4016297b69844f05a51c4669e62083b8d27a734f0daa24b3a03039979d6a28c7\n.claude/skills/ui-visual-review/SKILL.md 4c92c01e4d437150fe115406044d2ea9b8c942c2026f9e0d8d446667c4db7912\n.claude/skills/ui-visual-review/SKILL.md e9689dadb419979e2b62a3f1c84069f90b91ae95dd0a061dc759b3a60598aa32\n.claude/skills/ui-visual-review/SKILL.md 39371f46b46b7d8f51da0b9a28ca710156de634b6e066217e9618b1ecbe3f5ee\n.claude/skills/ui-visual-review/SKILL.md 2e23c9ba4e40a13db152ee750a33cadb6e32169daf51417c9a0a97b0ed021e45\n.claude/skills/ui-visual-review/SKILL.md 8d5c23460b4ba78c9b8077d9e6de59af5a38ea3035ca65f5cf645140ba523741\n.claude/skills/ultracode-service-audit/SKILL.md bdb3d151ca74e6129e547339b17192ba415701b46d889e0a1bf9f7fd0195663c\n.claude/skills/ultracode-service-audit/SKILL.md c7ca76cbda2f8d6b5750b68f524c92927ea375fb47a910ed02332e3a6fa1bca0\n.claude/skills/user-centered-explanation/SKILL.md eab47a91badce5d6314f63889950c69c43fa5ea4660d544828794ada3684e0b8\n.claude/skills/verification-loop/SKILL.md 01a9ae310ad4426bb05660dda6cefdeaac9513417585c09c8bb1b94738649a78\n.claude/skills/verification-loop/SKILL.md 41f481c1146b0b29ecbcf5c633090a6db3749ca4a814499d72b5b8941db694f5\n.claude/skills/verification-loop/SKILL.md 566fc81da559464d70af80a125dac60991ba5e26464643886c1dee04dd78db95\n.claude/skills/verification-loop/SKILL.md 1744f867f7868b83b480341c2e8d08bff3848c2408255871d3aba0c2f7eff2f2\n.claude/skills/verification-loop/agents/openai.yaml 1907fd04fea5c83d15faf14acfc978de90076d4351341cf9259639aa0c3b7bd3\n.claude/skills/verification-loop/references/tracks.md de11792d447c2e8ea67e8367c6655bd94646b1ba3f29184c4521cae01facc6ec\n.uzys-agent-harness/check-absence.sh b9bbc7bbdc69d51eac2a4e4748ee7c137dd585b4f3ca1d9bd2404eab9694c9b4\n.uzys-agent-harness/check-absence.sh c08d3a1df4188e5f37ddf7f7b53901184d87bdbed63c5c99afbddd0ef6a0442d\n.uzys-agent-harness/check-absence.sh 3655faf030e319e26a6386b316df004f4e2fee963fe0ebacb661030e85cda92e\n.uzys-agent-harness/protect-branch.sh 3356c4941ef1d5c174b10fd8858887f00b3b06c851788064e0e3cc6a6c8c56fd\n.uzys-agent-harness/protect-branch.sh ecd46450beae58c085a0d407c34441ae470092d9963bd4a37f09416a22cfad35\n.uzys-agent-harness/spec-drift-check.sh f35a64dd9954a776c3eabf356c0cda017db30e129d08022341a07c5a9d2b6f9a\n.uzys-agent-harness/spec-drift-check.sh 3c6925b03775454ec3a81e63dda400511824a9ac6e3f8f30b302ce1edf39d062\n`;\n\n/**\n * 옛 판이 `.claude/` · `.uzys-agent-harness/` 아래로 **그대로 복사**해 둔 하네스 파일 — 경로(프로젝트 상대) → 그 경로가 가졌던 바이트의 sha256 전부\n * (155 경로 · 379 쌍). `.agents/skills/<rel>` 은 `isLegacyHarnessFile` 이 `.claude/skills/<rel>` 로 조회한다.\n */\nexport const LEGACY_HARNESS_FILES: ReadonlyMap<string, ReadonlySet<string>> = (() => {\n const m = new Map<string, Set<string>>();\n for (const [path = \"\", sha256 = \"\"] of rows(FILES)) {\n const s = m.get(path) ?? new Set<string>();\n s.add(sha256);\n m.set(path, s);\n }\n return m;\n})();\n\n/** 옛 codex 변환은 번들 스킬을 `.agents/skills/<rel>` 에 바이트 그대로 복사했다 — 같은 지문으로 본다. */\nexport function isLegacyHarnessFile(path: string, sha256: string): boolean {\n const key = path.startsWith(\".agents/skills/\")\n ? `.claude/skills/${path.slice(\".agents/skills/\".length)}`\n : path;\n return LEGACY_HARNESS_FILES.get(key)?.has(sha256) === true;\n}\n\n/** 마커 이전 `AGENTS.md.template`(codex · opencode · antigravity)의 자리표시자 없는 `## ` 절 (37). */\nexport const LEGACY_AGENTS_MD_SECTION_KEYS: ReadonlySet<string> = keySet(`\n241678cd4951efaf8ee7bb0059e739c397af6b90b75fea636361501b7ae1cbe7 ## Workflow Gates (26.70.0)\n1e19e112dc2c9c85f31d7b8c1b65b5e35e8563db7f31115ba03af8c6ef1d5ea6 ## Command Reference (Antigravity workflows) (26.70.0)\nfe66384910daab3a0275c7c98d9a944f5f771d58ec2a9addcaf5f16a8d001ef9 ## Protected Files (DO NOT EDIT) (26.70.0)\nf7fbcaa082ffb260b11f48813a8f97f243663082aadb37e2137d2a99a143f65f ## Scopes (26.70.0)\n62225c82dd65fc0c01dba1fd6dbe8e9accb8d46d0f580f4d2a281dead3346997 ## Workflow Gates (26.70.0)\n0d509a214b74e5b3966722390279fe84fa20c8337fa7c9853c31fef0b42bf85f ## Session Start (26.70.0)\nfd29a37cc8231393739123a79728b31c0c4acde371ce502709550ed43837a2d4 ## Protected Files (DO NOT EDIT) (26.70.0)\n9c4a901cbad93ba04a5d511952e2e47213130c92ae2122e5f5649a17328d8915 ## Git Policy (26.70.0)\nc24a96fd321a9286debd618c9d95684c344c061d6a8e0558dce1595aa84e3a9f ## Command Reference (Codex slash) (26.70.0)\n23c25f2516a46dec187877d5d85f2e92257752193e287978650b3b112df083dd ## Agents (subagent, multi_agent stable) (26.70.0)\ndc230c795b307261ea4ee23126e6a209cc73ed474c97857d59e03a134232c4ec ## Hooks 현황 (Codex 0.124.0 실측 제약) (26.70.0)\n462ce33464e58a8ebd95ea5ed0f4cce167153fb2805ea6ba609734174a9027a2 ## Experience Accumulation (26.70.0)\ndc5629ad55f9800e4898a698650c8dbef5578505e7d5891745f0065f01d650a3 ## Context Management (26.70.0)\nc7d793aa3bf374b8bd390baf25bfc2c23c2d050377f673e72e53fe14f821c1f4 ## Workflow Gates (26.70.0)\ncf4cf430b633cb68b4128b03797999ed325f76ed92132fb8adcb60cccbc4e0c2 ## Session Start (26.70.0)\nef2c745821688a463d4435affaaa6baf404feaec69f081c68a794d847f72a9e7 ## Protected Files (DO NOT EDIT) (26.70.0)\nf328397dafd8eaff1eeff91dcda315db3da289e646eca49a6b977725867cea3d ## Command Reference (OpenCode slash) (26.70.0)\ne1dd38e41e5df06efea65b349376a1ea668ce74a408efaf5c1ce91f08063a666 ## Agents (subagent) (26.70.0)\n1ca6e8025be0d099df42fce9248673722fe327be2a77a0d9971bacc18a99a10e ## Hooks (Plugin lifecycle) (26.70.0)\nad834e2800b86a486d50efb9526657898a03919ee1b3fbf0cb0aaa89c9a6c095 ## Experience Accumulation (26.70.0)\n2cade5a88d6241fa7a50289fe0336b64cccd08d2c81b65e2df72acbdbc436a62 ## Context Management (26.70.0)\nb2532f7ae8302ee8aa5e0316f7bd9755abe06abeaa9639f7a9a062fc7d89b47f ## Scopes (26.89.0)\nc144f72dc32d524ae00b6026322439a5a3f2083d4e1781b927f374fe2b20d7c8 ## Session Start (26.89.0)\n0c719706bef388d3ac81a62431659bf817b0d4384846a0068d5768c8b3965e36 ## Protected Files (DO NOT EDIT) (26.89.0)\n6502c5abcf4cb9148c6dc5227401f7046b7dc8ab97ff7c292824de568b603942 ## Experience Accumulation (26.89.0)\n37106505641d9de7ae9e3ac5cc86efc3221abe5c969323325acc11e9b7d7cd04 ## Experience Accumulation (26.115.0)\n25baf8cc482b473d7f61e13ff95232484f95cc22063bb2ce9587f43ad242d5dc ## Scopes (26.141.0)\nd48603cb329fce58fd7ca9eb3b01dde3b223bdbfe79ee583627fbc15e22ba703 ## Session Start (26.141.0)\n29cb109e9a2cb4e3037a470d25d7dce4928d0e690c2c7c478d452f2133b65cd5 ## Git Policy (26.141.0)\nd1affcbe314d8ccd39ca956d467fbd43bcc859b4ffc23bf1d1f8f2f0a3e10f4e ## Context Management (26.141.0)\n9203c6d359a7ffc5a968be0645e1b827d6b4940312c27edb1c5fad6a68c9ca7e ## Session Start (26.141.0)\n39fdaaed39e0a39c537ebc3dcf5d349fb867740b126822eaa9333833d0de68eb ## Context Management (26.141.0)\n6f4bae4fb31bc54074d1fed021f4fd6dc3939844ac701eac8427904404b6daf3 ## Context Management (26.147.0)\n0569e0fb18d55a474fc8239dd60bb5684d22245c1ffa6f198cf6af468b5a3794 ## Protected Files (26.151.0)\n624b8878a9a17a5d4046b180297c53b27270ea75ed7d892d527939a7580adb19 ## Session Start (26.151.0)\n0f6658376e8bba3c31ffca010f24e9acee9f32953c00a41183dca9eaaa4f7913 ## Protected Files (26.151.0)\n55412b2ae275e9660f09c7cc286d3e92236e4ae61626cbf68727ae222edcb0e0 ## Protected Files (26.151.0)\n`);\n\n/**\n * 하네스 **이름 절**의 옛 렌더 바이트 (50) — 템플릿이 제목을 정한 절(`## Project Rules` 머리말 · `## Harness Rules` 안내 ·\n * `## Session Start` …, 같은 판의 앵커 본문을 넣고 `/uzys:` → `/uzys-` 를 건 모양)과 인라인 룰 절(v26.147~ codex). 이름이 하네스 것이어도\n * 바이트가 여기 없으면 설치자가 고친 절이다 — uninstall 은 남긴다(사용자 결정 ⓓ).\n */\nexport const LEGACY_AGENTS_MD_NAMED_KEYS: ReadonlySet<string> = keySet(`\n92dce1a73d484490c943ec3418f10b2370079ae42aa8d93159596f45d12e699a ## Project Rules (26.70.0)\nf59bdc7177a12aa86f235ada16b2407c40bd55ae9de8e96a851b77571bdde989 ## Command Reference (Antigravity workflows) (26.70.0)\nc7f9fa755bd66d80c2df496c6c759e8accd736cd8fef64460feb7ae9cd4c8006 ## Context Management (26.70.0)\ndc030a35a6e638aba5447b4d4e79ae028fafd1419fbae872b902661fa7787a58 ## Command Reference (Codex slash) (26.70.0)\ndf73823c02e6eee000388444964acfec233ffeec0e20fdc5d31eb018d2ca93c1 ## Context Management (26.77.0)\n6c900db77cb104f400091b44a7414e96427003860306a3007651b7a8b91a547c ## Project Rules (26.140.0)\nd9e3680462e64506ba4ef2df4f12e666855c3f458c1feeeeb623d62de2a5fa17 ## Project Rules (26.145.0)\n2da8ca3600045d85a9607ce270f9bc2d77b5ee54fe80cd0765caf7658f51127c ## Harness Rules (26.147.0)\nbbe5e1d9d368b15221269319b53dde4f87424904b80cd5af46a28de3d8ac923d ## Harness Rules (26.147.0)\n41eae7d6a8bb327e71cea2dbaa331d808f879b5ff4074c0bbdbb90bcfe066d9f ## Change Boundaries (rule 26.147.0)\nbef15b624ea34d08dc5833ceacd710f3c687385050958e5cd103378cf2fcf457 ## Change Boundaries (rule 26.147.0)\n9648c5a118c77a23a1a04ddcacd7708be162d65587769d47f51d2405b74c2f72 ## Shell Safety (rule 26.147.0)\nc2dee88e05c98e1482c99c1c01f59cafc6c6816fda1269d68179707f4ede0b8f ## Shell Safety (rule 26.147.0)\na6c3b31f60c80906e8bdb7a5ba699a4c1062b2e2f3e94f9f56b5064f74a722ef ## Documentation Boundaries (rule 26.147.0)\nb8fbe3bb2d5d790015ddbcff85c68922a122d91705e1cc946a14b30480bfb22b ## Documentation Boundaries (rule 26.147.0)\n0a0b44e4c8d10aca52f63a4dba40d9166ea067eb1599886f850dc37ec2bba2f2 ## Git Safety (rule 26.147.0)\n05efdb03d3e9d9fbcfd652ddf778d25bb1ab5ce237dd33f17b0be82cad08db5b ## Session Cleanup (rule 26.147.0)\n10bdfa993f15639a319e852cf59f7b279b7397d26a94c54e6ca6a94416cc6834 ## Enforcement (rule 26.147.0)\nb72afd685f3c63e481dc856890c8a9d89c4e36ca3163ab172661e9fd5c3658a5 ## Enforcement (rule 26.147.0)\n126a9a57aa42007c9c0bd3cb7f0ce75f2ad6a7497b464e7f6c7f45f3ae5f40d2 ## Delivery (rule 26.147.0)\nedfa8459a9863e355a080c759419f6f5ffb0ebd5fc418914fd38d79622e73131 ## Delivery (rule 26.147.0)\n55295cda2a572e0aab061449248123d9d43543b3045b1b156b95fe2fb6aae509 ## Testing (rule 26.147.0)\n6364a5d8727d33db914c7d7a1ac4108cff9ed1dbefffb212bd84c1fcd7584284 ## Testing (rule 26.147.0)\n8d407c73c2de630d77d605447f605a6a2511fc4598d500ac7a1ff1374e2e457d ## Delivery (rule 26.148.0)\n2c3f8e7c8408f103006e4ef8029dce4da1995bca01bd1e6baf7ca3f4e46e58fa ## Delivery (rule 26.148.0)\nae930fa7c8fa6f942d20715a51de43f33461aa5589f469f13a31ab90da1904e7 ## Delivery (rule 26.149.0)\n5b97fa550beb24e86c04af7fba2c97d8ff51702fd25abf223d97350db2a19ff1 ## Delivery (rule 26.149.0)\ne9b5814787b1e1cc066c217da0e344604477edfc0eb17304a5aeca73b0846637 ## Testing (rule 26.149.0)\nbafced984a2acf13f6fc7a3ca7c79edb99b5cecd4b21e351bd093f260d32fb02 ## Testing (rule 26.149.0)\n4972faf000e3433c0c07f7bb36e6b0463453be308dea31cfc177d692e23e3700 ## Project Rules (26.151.0)\n381a1d00394f803118575ecde42946585466c2da2ec36239fd6208a3c1b6bab8 ## Change Boundaries (rule 26.151.0)\n1a9dda0c66c7d82bdeb29c169f749a44c03b9bc112b3a1146898826b7d77b04e ## Change Boundaries (rule 26.151.0)\ne0d9ecb6916b563cfd0eaaf3f977106826136c4aaef518e705508c60f17b3cac ## Shell Safety (rule 26.151.0)\n053642f8951a2da28bca7d8d985a01c8fc2a6a91223a8cdfcd5e7ba5ef78af62 ## Shell Safety (rule 26.151.0)\n6543ff93b6c1f27f6d76f2a2a7599fd7ed0d1004f5419d5082321d1f4729bd1e ## Documentation Boundaries (rule 26.151.0)\n1741bf4e931d799c850ae45944ac2a9985800240d011a670ddb9400013b14856 ## Documentation Boundaries (rule 26.151.0)\ne527b3630786a074e321e1375b44f0870df3f7d4f124d5fdca9bbbd7e66f9315 ## Session Cleanup (rule 26.151.0)\n8ec2a06cfe3fa51e8187b218c290cf9ddc7680eac85427e2f6cf2a4a1a03ed4c ## Delivery (rule 26.151.0)\nd7fad0202a32aedb45717177ef9d3647109a0e77ee13f37b4c4a42f892182d4c ## Delivery (rule 26.151.0)\n2332b78bb1eb206ba1b08e28b3114d4919fb1384a9a5378f55dead61fdc3105a ## Testing (rule 26.151.0)\nd172feb48f54338d5d81e8b390debe3537538f0d2214f51e590272547e113cd2 ## Testing (rule 26.151.0)\n99bdd67b5ae37c10588f4ff852525ebe2ab59368723f56bd317818a89161c59a ## Change Boundaries (rule 26.152.0)\na07c989310055284328b4ca441e201740fc8158a38572e56ec9968dac44ced13 ## Change Boundaries (rule 26.152.0)\ne4d2c89e6c2189748bef2dccf8eb506b7f8cf264d5bf8626fbe4446c330e0b63 ## Shell Safety (rule 26.152.0)\n028c8514fa466e7f053088e37968ef07ce1bcb08d4c224e76ec807d0f01e4449 ## Shell Safety (rule 26.152.0)\n9f0ac9e1c32c486e4f318061e3be3c853217d03b3f837b6c6e425f016c8707e9 ## Documentation Boundaries (rule 26.152.0)\n992ca24bd1f15314fb53943af2856af73f0d31ad8c6cf98b25b840ebd017b86a ## Documentation Boundaries (rule 26.152.0)\n49ec458da7c57727c16ec8ffa01cb2e2631eb769f6041b3bc07d912ec1a2994b ## Project Rules (26.153.0)\n155abf346fea6c63a081d4db1a1e44d537c4a94a479a2affc96345b18928fde3 ## Testing (rule 26.157.0)\n675da728d15da072c17fe5e9ba3ca8b21811e99a09f390fed4a53f19dd12c555 ## Testing (rule 26.157.0)\n`);\n\n/** 같은 템플릿의 머리(제목 다음 블록인용) — `legacyPreambleKey` 로 대조한다 (6). */\nexport const LEGACY_AGENTS_MD_PREAMBLE_KEYS: ReadonlySet<string> = keySet(`\n720645dd313be16e6204290bfe0451fb237f77b8a7498a8a8af77dfc44ac0828 > **Generated from**: '.claude/CLAUDE.md' via TS CLI 'src/antigravity/transform.ts' (26.70.0)\nf95170e7780965de8562db0b5c7846d8e75ac7902a4fab695373c691333c7ff4 > **Generated from**: '.claude/CLAUDE.md' via 'scripts/claude-to-codex.sh' (Phase C) (26.70.0)\nfe621e2687dd489f3e9fb6ca1b97d480a83af435d99117315c24534d9c4bc169 > **Generated from**: '.claude/CLAUDE.md' via TS CLI 'src/opencode/transform.ts' (Phase C) (26.70.0)\n0d05490eb1360e2601d7e63b79c901a10da1dcfd6c9437151e4fb1ce00416bfc > **Generated from**: 'templates/CLAUDE.md' (4-CLI 단일 원본) via TS CLI 'src/antigravity/transform.ts' (26.141.0)\n1979722e483fba4441ffd4818134b3942cc97f21e39689316d6143e4732c98a7 > **Generated from**: 'templates/CLAUDE.md' (4-CLI 단일 원본) via 'scripts/claude-to-codex.sh' (Phase C) (26.141.0)\n230413c8211b1a9c3b204e315ff63f6ac3a7d7e5d01c8dd3d4ed294bf3c49f5c > **Generated from**: 'templates/CLAUDE.md' (4-CLI 단일 원본) via TS CLI 'src/opencode/transform.ts' (Phase C) (26.141.0)\n`);\n\n/**\n * #739 — 마커 이전 판이 `AGENTS.md` 의 `## Project Rules` 에 통째로 넣은 앵커(`templates/CLAUDE.md` — 첫 h1 제거 · `/uzys:` → `/uzys-`)\n * 절의 지문 (53). `src/retired-anchor-sections.ts` 가 다시 내보낸다.\n */\nexport const RETIRED_ANCHOR_SECTION_KEYS: ReadonlySet<string> = keySet(`\n522bac15bc17949697e291b837c0d9624f5e0c7666400a3baf94cdef7cfab8b5 ## Rule 1 — Think Before Coding (26.70.0)\nea10b7cd9b84a71949a071fae15256f490b3fe407159d7bddcf3f882310fa835 ## Rule 2 — Simplicity First (26.70.0)\nc486465d9554f2de1711cd056437f6a95a5d3243264b82d29a6957f24b5a6de3 ## Rule 3 — Surgical Changes (26.70.0)\n3d080574cc7ae81eda75c4aa019445b38b6f69c5f9eeeda9097e10b6041e7e94 ## Rule 4 — Goal-Driven Execution (26.70.0)\n11e43547f6b1613a5dabb3edaf5131d696a3fd375b3c22558f03555a0f5cf2ef ## Rule 5 — Use the model only for judgment calls (26.70.0)\n60d91718de6ae16ec86b50e7c9a939a6ca5fc0721422b592700cc9972f55aca2 ## Rule 6 — Token budgets are not advisory (26.70.0)\n9d482c7307cbcb02acc6fb35cd57b24baa9fce04528e2bd21e1e877603f8b85b ## Rule 7 — Surface conflicts, don't average them (26.70.0)\nabca09f13833ce18bd0ffc0793d59780c35447ca79c2a512afcbc9d7a7116ed8 ## Rule 8 — Read before you write (26.70.0)\n5b4eae6c3dc2d92cfb1772889650882a4a2115f08a36602da9a1d96697059b91 ## Rule 9 — Tests verify intent, not just behavior (26.70.0)\n632670b8897dc373d4a12f611c2436d9a8e86d8cdab917eecabc4bf2a56f8030 ## Rule 10 — Checkpoint after every significant step (26.70.0)\n252e99f25b05a47e5ce884ac9f1e1d34156116b9a76331e2510b1354a6c98cd4 ## Rule 11 — Match the codebase's conventions, even if you disagree (26.70.0)\nc28a12b589b6576379793c51fd01dbfaf91d391ac773f84ae0477ddb60a1e3e8 ## Rule 12 — Fail loud (26.70.0)\n72feaf96f998989351f8083edb891c7b0c3bccca65dbea2732fb2864b8f98bd4 ## 의사결정 및 컨펌 요청 시 (26.70.0)\n95c932ecb4f4fdcdd8d483f9d02166b906f6eb045ef33ee474a16913d0c3e6cd ## Phase/작업 완료 시 Self-Audit 실행: (26.70.0)\nc7f9fa755bd66d80c2df496c6c759e8accd736cd8fef64460feb7ae9cd4c8006 ## Context Management (26.70.0)\n6ec0b415a8f86c9940bccd3e6db895ea4a110185e76fef223a348f9553fd897c ## Rule 12 — Fail loud (26.77.0)\na78f0ffa6b51c9b45c964ecb92db951534336f7e558a3d39d11f015ee6c3466c ## Anti-Patterns (Forbidden) (26.77.0)\n50d00a511e87571d7ff85f6584b87b9f5c2eef06122d8ac5fdccf3f047643f12 ## When Requesting Decisions or Confirmation (26.77.0)\n574519e07f48bcc04f0e3d505c0f9c157ddd426b5eda2baa99656cd4712dad2b ## Run Self-Audit on Phase/Task Completion (26.77.0)\ndf73823c02e6eee000388444964acfec233ffeec0e20fdc5d31eb018d2ca93c1 ## Context Management (26.77.0)\n8f49f0a592575a65eadd3e33950c96616223aa8bb116707103affeef71db28c6 ## Delegate the Building, Keep the Deciding (26.139.0)\n6115eccc3b2702f436a9db7d6af9803fa7b1fbf7b9ff1110ea658fadd67ec5ea ## 1. Think before coding (26.140.0)\n15b120395ec8e757ecbe3e65775371a9120953617fb37eb062e0c176b52f045a ## 2. Prefer the simplest sufficient solution (26.140.0)\ncc466b4781444ca54aedebd20ba3aa531c3aa84cc438953df84b133bd32a0e1e ## 3. Make surgical changes and preserve existing work (26.140.0)\nf6d1f6f02cb3c19e61becf329f20335c54e684bccb06b33e4b7f66eb49f9c6b6 ## 4. Define success before editing (26.140.0)\n372bf30b282b861c6a89e355c94ffd239bb0202e2ef928f8af99d640579710f0 ## 5. Report evidence, not confidence (26.140.0)\n66ca0e3aae98d24c65afbc418645df29d8c570be81918c42192eb1beecd21f23 ## 6. Do not cross high-impact boundaries alone (26.140.0)\n9c6c27eb57a9790273fa3fdf0df3628d927b025f870f09933692d1b522d73a25 ## Decisions and explanations (26.140.0)\n922d100c2dc6ef8a53989a920c3554afa7efd148255bc144412403c0289439e6 ## Decisions and explanations (26.141.0)\n330a67775a28557431aebf36a44018fc5194a4b929bf2cf95184b1f0828a6360 ## 1. Understand First (26.145.0)\n3b2f0147dc7bd0f01fd12485790199ef4ee96a6b723e244ffbf3dc96933a1ab8 ## 2. Define Success and Keep It Simple (26.145.0)\n8802fb25c05879c98dbf8407001d239c410d25b4fa2521b11d9d7cc108f73b29 ## 3. Preserve Sound Boundaries (26.145.0)\n1d0d9bf05d8870dd0dc62e388ceffc98923f1dd2074ef8038f80a50c9b5b9a81 ## 4. Make Surgical Changes (26.145.0)\n05e08e2318061141464ae714d6770d35527576affa787740d9c6d9345d3462dd ## 5. Verify and Review (26.145.0)\n7b39f1f92a72517d6ae52599becfef29f675e48185a3483dc993d7accd4aee7c ## 6. Protect High-Impact Boundaries (26.145.0)\nd5d93c55de33e4cdcb38f76c6d6d70a6e0beac7a176c8aa1daa09905ccc5fe39 ## 7. Report Evidence (26.145.0)\ncefc8bb6194d4f1b6302345e96ffb3a28b8b992032fd40f9f5b4f18997657e7a ## Presenting a decision (26.145.0)\n7385ff5df34148278a8f4b164c11ba5f9355626abcc13a822dd1ebb206321c2f ## Skills that apply continuously (26.145.0)\nad9fd1ff225c57c9801a7d7472bf4ddf9c952c602f4a93eae86fbcb3bcf468bc ## 7. Report Evidence (26.146.0)\n868fe823fc40011d08816c9f1105ef6df7ca7f2479779303ce9583bba1ce6e0d ## Skills that apply continuously (26.146.0)\n3f8259ca7c92d79cca1a36bba38f36d4ef5a96fd6cafbd0edb5a79492082f46f ## Presenting a decision (26.147.0)\necfdcd50e97723af77cfe6889bbad784cdeb86dd0c52af3ff98ddf6adf80f445 ## 1. Resolve what matters, then act (26.151.0)\n9321cb1312a080bcf560940b8f86d3d59b39c667add4b8f3ad785ddae9bc8b20 ## 2. Choose the simplest sufficient solution (26.151.0)\na4c345996a8578cc24b62fde27084e5848a61e4a40d91b0c72a95da46203b43c ## 3. Keep changes focused and preserve existing work (26.151.0)\n1adbf1bf88bb8288744e1c1d73518ffdec12a5b668d5a225e2bedd006844466c ## 4. Define success and verify proportionally (26.151.0)\n7636e57a877d54039f0c2829fde84202c65378bd36b8c8abb7274a29eaa5f8ac ## 5. Report evidence and stop unproductive loops (26.151.0)\nddf2be9e04c04bf53ad9b5476e45639b33a60bf49bd3a83835796632052b17b4 ## 6. Keep authority explicit (26.151.0)\n16b0edc0f4886a9f9ec94e6fb97206c385286b2c9d5563b2324b7a2f4a434435 ## 1. Understand what success means (26.153.0)\nb10ab1eafe25bb7ba3b20b907b1d7eedde03e52fbd49905f24e192e30b4e1b9a ## 2. Choose and adapt the approach (26.153.0)\n69bcfb8b37ac184b1a30945a0dea59108ef1242c2f40fb778be118cbd524aea7 ## 3. Keep changes focused and coherent (26.153.0)\nf22a5a13c5ed8460c9370212d91354d0fea2fefc7f420a1eb3088504ae8da6c0 ## 4. Verify outcomes in proportion to consequences (26.153.0)\n7a335cde69e8055ee5e137a9a5c09fd7f7c2e551ab099a2cda87f0c3dcd95a19 ## 5. Let evidence determine readiness (26.153.0)\n79797568bb71bd0f7b14393f72e61721a4bfe99ecfb6126ffa38a6c03c53d765 ## 6. Act within clear authority (26.153.0)\n`);\n\n/**\n * 옛 `templates/settings.json` 이 `$CLAUDE_PROJECT_DIR/.claude/` 로 배선한 훅 핸들러의 command 원문 (8). 이 문자열과\n * 바이트로 같은 줄만 옛 하네스 줄이다 — 설치자가 한 글자라도 고쳤으면 설치자 줄이다(#781).\n */\nexport const LEGACY_HOOK_COMMANDS: ReadonlySet<string> = new Set([\n 'bash \"$CLAUDE_PROJECT_DIR/.claude/hooks/agentshield-gate.sh\"',\n 'bash \"$CLAUDE_PROJECT_DIR/.claude/hooks/gate-check.sh\"',\n 'bash \"$CLAUDE_PROJECT_DIR/.claude/hooks/hito-counter.sh\"',\n 'bash \"$CLAUDE_PROJECT_DIR/.claude/hooks/mcp-pre-exec.sh\"',\n 'bash \"$CLAUDE_PROJECT_DIR/.claude/hooks/protect-files.sh\"',\n 'bash \"$CLAUDE_PROJECT_DIR/.claude/hooks/session-start.sh\"',\n 'bash \"$CLAUDE_PROJECT_DIR/.claude/hooks/task-brief-nudge.sh\"',\n 'bash \"$CLAUDE_PROJECT_DIR/.claude/skills/strategic-compact/suggest-compact.sh\"',\n]);\n\n/** 옛 템플릿 `statusLine` 값의 `sha256(JSON.stringify(value))` (2). */\nexport const LEGACY_STATUSLINE_VALUES: ReadonlySet<string> = keySet(`\nf3de524065fd30f26e2d14b31fad0edc4c6689fb95b8ea6c59b1d48c5a90adc5 statusLine (26.70.0)\n94442f9703e367a12f6dc16612ad65ae5f0cd0c73b96dd683e584a18ac91eaea statusLine (26.164.0)\n`);\n","/**\n * 함께 쓰는 파일의 어댑터 계약 (#551 · ADR-097 · 설계 `docs/plans/one-principle-design-2026-09-27.md` §6.2).\n *\n * 어댑터 넷(`marker-md` · `json-keys` · `toml-region` · `lines`)은 **파일 형식만** 다르고, 키마다 무엇을\n * 할지는 여기 `planUpsert` · `planStrip` 한 벌이 정한다 — 규칙이 어댑터마다 따로 살면 한 곳만 고쳐졌을\n * 때 조용히 갈린다(`owned-write.ts` 서문과 같은 이유).\n *\n * 공통 규칙(§6.2):\n * ⓐ 파일을 파싱하지 못하면 **한 바이트도 쓰지 않는다** — `{ ok: false }` (#574).\n * ⓑ 몫은 키 단위. 키 sha = 기록 → 갱신/회수 · 다르면 남기고 알린다 · 기록에 없으면 설치자 것.\n * 설치 전에 이미 있던 키는 값이 하네스 판과 같아도 기록하지 않는다(Q3).\n * ⓒ 기록에 있는데 파일에 없는 키는 **빼 달라는 신호가 아니다**(ADR-099 R1) — 렌더에 있으면 다시 더하고,\n * 렌더에 없으면(설치자 값이 구간 밖에 있어 걸러짐 등) 아무것도 하지 않고 기록 sha 를 그대로 잇는다(되돌릴\n * 근거). 빼기는 `excluded`(설치자가 `--without` · 위저드로 명시한 것)로만 정해진다 — excluded 키는 더하지 않는다.\n * ⓓ strip 은 **기록된 키만** 뺀다 — 내용 식별은 쓰지 않는다(R3).\n */\n\n/** 이 파일에 대해 기록된 몫 — key → sha256. */\nexport type PortionShas = ReadonlyMap<string, string>;\n\nexport interface UpsertInput<V> {\n /** 이번 렌더의 하네스 몫(key → 값). 순서가 새로 붙이는 순서다. */\n render: ReadonlyMap<string, V>;\n /**\n * 기록된 이 파일의 몫. 옛 로그(몫 기록 이전)는 호출부가 §5 의 내용 식별로 채워 넘긴다 —\n * 어댑터는 내용 식별을 하지 않는다(R3). 파일이 없을 때(`existing === null`)는 읽지 않는다.\n */\n recorded: PortionShas;\n /** `excluded` 중 이 파일의 키(어댑터 키로 바꾼 것 — `excludedKeys`). */\n excluded: ReadonlySet<string>;\n /**\n * 파일을 **새로 만들 때만** 하네스 몫 앞에 둘 본문(컨텍스트 파일의 스캐폴드 등). 몫이 아니다 —\n * 쓰이는 순간 설치자 것이고, strip 은 건드리지 않는다.\n */\n seed?: string;\n /** 이 파일이 사는 프로젝트(절대경로). `json-keys` 의 훅 식별이 옛 절대경로 표기를 알아보는 데만 쓴다. */\n projectDir?: string;\n}\n\nexport interface StripInput {\n recorded: PortionShas;\n excluded: ReadonlySet<string>;\n /** `UpsertInput.projectDir` 와 같다. */\n projectDir?: string;\n}\n\nexport interface Unreadable {\n ok: false;\n /** 화면용 — \"invalid JSON\" 같은 형태. */\n reason: string;\n}\n\nexport interface UpsertOk {\n ok: true;\n /** 쓸 내용. `changed === false` 면 입력과 바이트 동일(= 쓰지 않아도 된다). */\n text: string;\n changed: boolean;\n /** 쓴 뒤 이 파일에 대해 기록할 몫 전체. */\n portions: Map<string, string>;\n /** 기록에 있었는데 파일에 없어 이번에 되돌린 하네스 키(ADR-099 R1) — 화면이 `was missing — restored` 로 알린다. */\n restored: string[];\n /**\n * 기록에 있고 파일에 없는데 되돌리지 못한 키 — `portions` 에 기록 sha 를 그대로 잇는다(되돌릴 근거). 파일에는 없으므로\n * 화면은 이것을 \"파일에 있는 하네스 몫\" 으로 세지 않는다. 까닭은 둘이다: 이번 렌더에도 없다(말할 것 없음) · 렌더에 있지만\n * 그 자리(컨테이너)를 모양이 다른 설치자 값이 차지해 쓰지 못했다(`json-keys`, #881) — 뒤의 것은 렌더에 있는 키라 호출부가\n * 가려 \"되돌리지 못했다\" 고 말한다.\n */\n missing: string[];\n /** 설치자 값이 이겨 하네스 판을 쓰지 않은 키 — 화면이 \"kept yours\" 로 알린다. */\n kept: string[];\n /** 파일에서 걷은 하네스 키(빼기 · 렌더에서 빠짐). 화면은 그중 `excluded` 인 것을 \"걷었다\" 로 말한다. */\n removed: string[];\n /** `removed` 중 설치자가 고친 값이었는데도 걷은 키(훅 핸들러 — N-f). 화면이 \"고친 것도 걷혔다\" 고 말한다. */\n removedEdited: string[];\n /** 기록 sha 그대로라 새 판으로 갈아 끼운 키. */\n replaced: string[];\n}\n\nexport interface StripOk {\n ok: true;\n text: string;\n changed: boolean;\n /** 뺀 키. */\n removed: string[];\n /** `removed` 중 지금 값이 기록 sha 와 달랐던 키(`alwaysStrip` — 훅 핸들러). 호출부가 걷기 전에 파일을 백업한다(#800). */\n removedEdited: string[];\n /** 기록에 있지만 설치자가 고쳐서 남긴 키 — 화면이 알린다. */\n kept: string[];\n /** strip 뒤에도 남는 몫(= `kept`). 기록은 이것만 이어간다. */\n portions: Map<string, string>;\n /** 남는 내용이 없다 — `rootFiles.change === \"created\"` 인 파일이면 호출부가 파일째 지운다(§1.2). */\n empty: boolean;\n}\n\nexport type UpsertResult = UpsertOk | Unreadable;\nexport type StripResult = StripOk | Unreadable;\n\n/**\n * 어댑터 하나. `V` = 키 하나의 값(마커 블록 본문 · JSON 값 · TOML 구간 본문 · `.gitignore` 줄 원문).\n *\n * 성질(설계 §6.2 ⓓ · §7A — `tests/adapters.test.ts` 가 문다): 설치자 원본 `x` 에 대해\n * `strip(upsert(x).text, upsert(x).portions)` 는 `x` 로 돌아온다 — `marker-md` · `lines` 는 **바이트 동일**,\n * `json-keys` · `toml-region` 은 **파싱 동치**(들여쓰기·끝 개행은 약속하지 않는다, N-a).\n */\nexport interface PortionAdapter<V> {\n /** 파싱 실패 시 화면에 쓸 말 — `⊘ left <path> — could not read it (<unreadable>)`. */\n readonly unreadable: string;\n /**\n * 파일을 읽는다. 못 읽으면 `null` — 그 파일에는 한 바이트도 쓰지 않는다.\n * `keys` 를 주면 그중 지금 파일에 있는 키의 sha 를 돌려준다(옛 로그의 몫 찾기 §5 에서 호출부가\n * 내용 식별로 고른 후보를 넘긴다).\n */\n read(text: string, keys?: Iterable<string>, projectDir?: string): Map<string, string> | null;\n upsert(existing: string | null, input: UpsertInput<V>): UpsertResult;\n strip(existing: string, input: StripInput): StripResult;\n}\n\n/** 키 하나를 어떻게 할지 — 어댑터는 이 계획을 자기 형식으로 실행만 한다. */\nexport interface UpsertPlan {\n /** 파일에 있는 하네스 키를 새 값으로 바꾼다(기록 sha 그대로였고 새 값이 다를 때). */\n replace: string[];\n /** 파일에 없던 키를 더한다(렌더 순서). */\n add: string[];\n /** 하네스가 더는 렌더하지 않는(또는 excluded 가 된) 키를 뺀다. */\n remove: string[];\n kept: string[];\n /** `add` 중 기록에 있던 키 — 사라졌던 하네스 몫을 되돌렸다(화면 `was missing — restored`). */\n restored: string[];\n /** 기록에 있고 파일에 없는데 렌더에도 없는 키 — 기록 sha 만 잇는다(`UpsertOk.missing`). */\n missing: string[];\n /** `remove` 중 지금 값이 기록 sha 와 달랐던 키(`alwaysStrip`) — `UpsertOk.removedEdited`. */\n removedEdited: string[];\n portions: Map<string, string>;\n}\n\n/**\n * upsert 의 키별 판정 — 네 어댑터가 공유한다.\n *\n * @param render 이번 렌더의 몫 key → **새 값의 sha**\n * @param present 지금 파일에 있는 키 key → **지금 값의 sha**(렌더·기록에 나오는 키만 보면 된다)\n * @param alwaysStrip 기록된 키면 sha 와 무관하게 빼는 키(settings.json 훅 항목 — 스크립트가 함께\n * 사라지므로 남기면 죽은 참조다, N-f · N13)\n */\nexport function planUpsert(args: {\n render: ReadonlyMap<string, string>;\n recorded: PortionShas;\n present: ReadonlyMap<string, string>;\n excluded: ReadonlySet<string>;\n alwaysStrip?: (key: string) => boolean;\n}): UpsertPlan {\n const { recorded, present, excluded } = args;\n const alwaysStrip = args.alwaysStrip ?? (() => false);\n const render = new Map([...args.render].filter(([k]) => !excluded.has(k)));\n const plan: UpsertPlan = {\n replace: [],\n add: [],\n remove: [],\n kept: [],\n restored: [],\n missing: [],\n removedEdited: [],\n portions: new Map(),\n };\n for (const [key, sha] of recorded) {\n const now = present.get(key);\n if (now === undefined) {\n // ⓒ 사라진 하네스 몫 — 렌더에 있으면 아래 렌더 순회가 되돌린다(add). 렌더에 없으면 기록 sha 를 잇는다 —\n // 단 설치자가 뺀(excluded) 키는 이제 하네스 몫이 아니라 잇지 않는다(파일에 없으니 걷을 것도 없다).\n if (!render.has(key) && !excluded.has(key)) {\n plan.missing.push(key);\n plan.portions.set(key, sha);\n }\n continue;\n }\n const next = render.get(key);\n if (next !== undefined) {\n if (now === sha) {\n if (next !== sha) plan.replace.push(key);\n plan.portions.set(key, next);\n } else {\n plan.kept.push(key); // 설치자가 고쳤다 — 남기고 알린다. 기록은 옛 sha 그대로(회수 판정 근거)\n plan.portions.set(key, sha);\n }\n } else if (now === sha || alwaysStrip(key)) {\n plan.remove.push(key);\n if (now !== sha) plan.removedEdited.push(key);\n } else {\n plan.kept.push(key);\n plan.portions.set(key, sha);\n }\n }\n for (const [key, sha] of render) {\n if (recorded.has(key)) {\n if (present.has(key)) continue;\n plan.add.push(key); // ⓒ 기록에 있는데 파일에 없다 — 되돌린다\n plan.restored.push(key);\n plan.portions.set(key, sha);\n continue;\n }\n if (present.has(key)) {\n plan.kept.push(key); // 설치 전부터 있던 키 — 설치자 것(Q3). 기록하지 않는다\n } else {\n plan.add.push(key);\n plan.portions.set(key, sha);\n }\n }\n return plan;\n}\n\nexport interface StripPlan {\n remove: string[];\n /** `remove` 중 지금 값이 기록 sha 와 달랐던 키(`alwaysStrip`) — `StripOk.removedEdited`. */\n removedEdited: string[];\n kept: string[];\n portions: Map<string, string>;\n}\n\n/** strip 의 키별 판정 — **기록된 키만** 본다(ⓓ). excluded 키는 건너뛴다. */\nexport function planStrip(args: {\n recorded: PortionShas;\n present: ReadonlyMap<string, string>;\n excluded: ReadonlySet<string>;\n alwaysStrip?: (key: string) => boolean;\n}): StripPlan {\n const alwaysStrip = args.alwaysStrip ?? (() => false);\n const plan: StripPlan = { remove: [], removedEdited: [], kept: [], portions: new Map() };\n for (const [key, sha] of args.recorded) {\n if (args.excluded.has(key)) continue;\n const now = args.present.get(key);\n if (now === undefined) continue;\n if (now === sha || alwaysStrip(key)) {\n plan.remove.push(key);\n if (now !== sha) plan.removedEdited.push(key);\n } else {\n plan.kept.push(key);\n plan.portions.set(key, sha);\n }\n }\n return plan;\n}\n","/**\n * `lines` — `.gitignore` (#551 · ADR-097 §6.2).\n *\n * 몫의 키 = 줄 원문(`.env` · `.factory/`), 값 = 그 줄 **앞에 딸린 주석 줄 + 그 줄**(설계: \"`gitignore:` 의\n * 주석 줄은 뒤따르는 줄의 몫에 딸리고 따로 id 를 갖지 않는다\"). 예: `\"# Secret env (…)\\n.env\"`.\n *\n * 기록에는 sha 만 남는다 — 빼거나 바꿀 때 어디까지가 그 몫인지는 키 줄 바로 위의 주석 줄을 0개부터\n * 늘려 가며 **기록 sha 와 맞는 범위**로 찾는다. 맞는 범위가 없으면 설치자가 고친 것이다(남기고 알린다).\n * 새 줄은 한 번에 모아 파일 끝에 붙인다(`text-runs.ts` 규칙 — 빼면 바이트 동일).\n *\n * 기존 `env-files.ts` 의 `addGitignoreEnv` · `addGitignoreAgentArtifacts` 는 호출부 그대로 남는다(더하기만\n * 있고 빼기가 없다 — #569). 배선은 PR-3.\n */\n\nimport { hashContent } from \"../install-log.js\";\nimport {\n type PortionAdapter,\n planStrip,\n planUpsert,\n type StripResult,\n type UpsertResult,\n} from \"./contract.js\";\nimport { appendRun, applyEdits, type Edit, type Line, splitLines } from \"./text-runs.js\";\n\ninterface Extent {\n start: number;\n end: number;\n sha: string;\n}\n\nfunction norm(line: string): string {\n return line.trimEnd();\n}\n\n/**\n * 키 줄의 몫 범위. `want` 가 있으면 그 sha 와 맞는 범위를 찾고(없으면 키 줄 하나를 \"고쳐진 몫\"으로),\n * 없으면 키 줄 하나. 같은 줄이 여럿이면 뒤의 것부터 본다 — 하네스는 파일 끝에 붙인다.\n */\nfunction extentOf(lines: ReadonlyArray<Line>, key: string, want?: string): Extent | undefined {\n let bare: Extent | undefined;\n for (let i = lines.length - 1; i >= 0; i--) {\n const keyLine = lines[i];\n if (keyLine === undefined || norm(keyLine.text) !== key) continue;\n for (let from = i; from >= 0; from--) {\n const first = lines[from];\n if (first === undefined) break;\n if (from < i && !first.text.trimStart().startsWith(\"#\")) break; // 딸린 주석은 바로 위 주석 줄뿐\n const text = lines\n .slice(from, i + 1)\n .map((l) => norm(l.text))\n .join(\"\\n\");\n const extent = { start: first.start, end: keyLine.end, sha: hashContent(text) };\n bare ??= extent;\n if (want === undefined) return extent;\n if (extent.sha === want) return extent;\n }\n }\n return bare;\n}\n\nfunction presentFor(\n lines: ReadonlyArray<Line>,\n keys: Iterable<string>,\n recorded: ReadonlyMap<string, string>,\n): Map<string, Extent> {\n const out = new Map<string, Extent>();\n for (const k of keys) {\n const e = extentOf(lines, k, recorded.get(k));\n if (e) out.set(k, e);\n }\n return out;\n}\n\nfunction shaMap(m: ReadonlyMap<string, Extent>): Map<string, string> {\n return new Map([...m].map(([k, e]) => [k, e.sha]));\n}\n\nfunction at<T>(map: ReadonlyMap<string, T>, key: string): T {\n const v = map.get(key);\n if (v === undefined) throw new Error(`lines: no entry for ${key}`);\n return v;\n}\n\nexport const lines: PortionAdapter<string> = {\n unreadable: \"unreadable\",\n\n read(text, keys) {\n const ls = splitLines(text);\n const all = keys ?? ls.map((l) => norm(l.text)).filter((t) => t !== \"\" && !t.startsWith(\"#\"));\n return shaMap(presentFor(ls, all, new Map()));\n },\n\n upsert(existing, input): UpsertResult {\n const fresh = existing === null;\n const base = existing ?? input.seed ?? \"\";\n const ls = splitLines(base);\n const recorded = fresh ? new Map<string, string>() : input.recorded;\n const found = presentFor(ls, [...recorded.keys(), ...input.render.keys()], recorded);\n const plan = planUpsert({\n render: new Map([...input.render].map(([k, v]) => [k, hashContent(v)])),\n recorded,\n present: shaMap(found),\n excluded: input.excluded,\n });\n const edits: Edit[] = [\n ...plan.replace.map((k) => {\n const e = at(found, k);\n const newline = base[e.end - 1] === \"\\n\" ? \"\\n\" : \"\";\n return { start: e.start, end: e.end, replacement: `${at(input.render, k)}${newline}` };\n }),\n ...plan.remove.map((k) => ({\n start: at(found, k).start,\n end: at(found, k).end,\n replacement: null,\n })),\n ];\n let text = applyEdits(base, edits);\n if (plan.add.length > 0) {\n text = appendRun(text, plan.add.map((k) => at(input.render, k)).join(\"\\n\"));\n }\n return {\n ok: true,\n text,\n changed: fresh || text !== base,\n portions: plan.portions,\n restored: plan.restored,\n missing: plan.missing,\n removed: plan.remove,\n removedEdited: plan.removedEdited,\n replaced: plan.replace,\n kept: plan.kept,\n };\n },\n\n strip(existing, input): StripResult {\n const ls = splitLines(existing);\n const found = presentFor(ls, input.recorded.keys(), input.recorded);\n const plan = planStrip({\n recorded: input.recorded,\n present: shaMap(found),\n excluded: input.excluded,\n });\n const text = applyEdits(\n existing,\n plan.remove.map((k) => ({\n start: at(found, k).start,\n end: at(found, k).end,\n replacement: null,\n })),\n );\n return {\n ok: true,\n text,\n changed: text !== existing,\n removed: plan.remove,\n removedEdited: plan.removedEdited,\n kept: plan.kept,\n portions: plan.portions,\n empty: text.trim() === \"\",\n };\n },\n};\n","/**\n * 줄 단위 텍스트 편집 — `marker-md` · `lines` · `toml-region` 이 공유한다 (#551 · ADR-097).\n *\n * 세 어댑터의 약속은 **upsert∘strip = 설치자 원본**이다(`marker-md` · `lines` 는 바이트 동일). 그러려면\n * 붙이는 규칙과 빼는 규칙이 서로의 정확한 역이어야 하고, 그 짝을 한 곳에만 둔다:\n *\n * 붙이기(파일 끝) : \"\" → run\n * \"…\" → \"…\" + \"\\n\" + run (끝 개행이 있든 없든 \"\\n\" 하나를 앞에 둔다)\n * 끼우기(줄 시작) : 앞 + run + 뒤 (구분 줄 없음)\n * 빼기 : 파일 끝에 닿는 구간은 앞의 \"\\n\" 하나도 함께 뺀다(CRLF 로 바뀐 파일이면 \"\\r\\n\"). 가운데 구간은 그것만 뺀다.\n *\n * 끝 개행이 없는 `\"x\"` 는 `\"x\\n\" + run` 이 되고 빼면 `\"x\"` 로, 끝 개행이 있는 `\"x\\n\"` 은 빈 줄 하나를\n * 사이에 두고 붙었다가 `\"x\\n\"` 로 돌아온다 — 두 입력이 같은 출력을 내지 않으므로(단사) 되돌릴 수 있다.\n * 구간을 **뒤에서부터** 빼는 이유: 여러 번에 걸쳐 파일 끝에 붙은 run 들은 마지막 것을 빼야 그 앞의\n * 것이 파일 끝이 된다.\n */\n\n/** 한 줄 — `start`/`end` 는 원문 오프셋(`end` 는 줄바꿈 포함), `text` 는 줄바꿈·`\\r` 제외. */\nexport interface Line {\n start: number;\n end: number;\n text: string;\n}\n\nexport function splitLines(text: string): Line[] {\n const out: Line[] = [];\n let start = 0;\n while (start < text.length) {\n const nl = text.indexOf(\"\\n\", start);\n const end = nl === -1 ? text.length : nl + 1;\n const raw = text.slice(start, nl === -1 ? text.length : nl);\n out.push({ start, end, text: raw.endsWith(\"\\r\") ? raw.slice(0, -1) : raw });\n start = end;\n }\n return out;\n}\n\n/** run 이 개행으로 끝나게 한다 — 붙이는 단위는 항상 완결된 줄들이다. */\nfunction asRun(run: string): string {\n return run.endsWith(\"\\n\") ? run : `${run}\\n`;\n}\n\nexport function appendRun(text: string, run: string): string {\n return text === \"\" ? asRun(run) : `${text}\\n${asRun(run)}`;\n}\n\n/** `pos` 는 줄 시작 오프셋이어야 한다. */\nexport function insertRun(text: string, pos: number, run: string): string {\n return `${text.slice(0, pos)}${asRun(run)}${text.slice(pos)}`;\n}\n\n/** 원문 구간 하나의 편집 — `replacement === null` 이면 뺀다. 구간은 줄 경계여야 하고 서로 겹치지 않는다. */\nexport interface Edit {\n start: number;\n end: number;\n replacement: string | null;\n}\n\n/**\n * 편집을 뒤에서부터 적용한다. 맞닿은 빼기는 한 구간으로 합친다 — 따로 빼면 뒤 구간의 \"앞 개행\"\n * 규칙이 앞 구간의 끝 개행을 먹는다.\n */\nexport function applyEdits(text: string, edits: ReadonlyArray<Edit>): string {\n const sorted = [...edits].sort((a, b) => a.start - b.start);\n const merged: Edit[] = [];\n for (const e of sorted) {\n const last = merged.at(-1);\n if (last && last.replacement === null && e.replacement === null && last.end === e.start) {\n last.end = e.end;\n } else {\n merged.push({ ...e });\n }\n }\n let out = text;\n for (const e of merged.reverse()) {\n if (e.replacement !== null) {\n out = `${out.slice(0, e.start)}${e.replacement}${out.slice(e.end)}`;\n } else if (e.end >= out.length && e.start > 0 && out[e.start - 1] === \"\\n\") {\n // 체크아웃이 CRLF 로 바꿨으면(뺄 구간 안이 `\\r\\n` 이면) 붙일 때 넣은 구분 개행도 `\\r\\n` 이 됐다 —\n // `\\n` 하나만 빼면 `\\r` 이 남는다(#551 리뷰 N6)\n const crlf = out.slice(e.start, e.end).includes(\"\\r\\n\") && out[e.start - 2] === \"\\r\";\n out = out.slice(0, e.start - (crlf ? 2 : 1));\n } else {\n out = `${out.slice(0, e.start)}${out.slice(e.end)}`;\n }\n }\n return out;\n}\n\n/** 마커로 감싼 하네스 구간 — `start`/`end` 는 시작 마커 줄의 시작 ~ 끝 마커 줄의 끝(개행 포함). */\nexport interface Region {\n start: number;\n end: number;\n body: string;\n /** 끝 마커 줄 뒤에 개행이 있었나 — 갈아 끼울 때 그대로 둔다. */\n newline: boolean;\n}\n\n/**\n * 마커 구간을 찾는다. 마커는 **한 줄을 통째로** 차지할 때만 마커다(`markerLine` 은 trim 한 줄에 대고\n * `(이름)(start|end)` 두 그룹을 잡는다). 같은 이름이 두 번 · 시작만 있고 끝이 없음 · 구간 안에서 다른\n * 구간이 열림 → **읽지 못한 파일**(`null`) — 한 바이트도 쓰지 않는다.\n */\nexport function parseRegions(text: string, markerLine: RegExp): Map<string, Region> | null {\n const regions = new Map<string, Region>();\n let open: { name: string; start: number; bodyStart: number } | null = null;\n for (const line of splitLines(text)) {\n const m = markerLine.exec(line.text.trim());\n if (!m) continue;\n const [, name = \"\", edge] = m;\n if (edge === \"start\") {\n if (open !== null || regions.has(name)) return null;\n open = { name, start: line.start, bodyStart: line.end };\n continue;\n }\n if (open === null || open.name !== name) return null;\n const raw = text.slice(open.bodyStart, line.start);\n regions.set(name, {\n start: open.start,\n end: line.end,\n body: raw.endsWith(\"\\n\") ? raw.slice(0, -1) : raw,\n newline: text[line.end - 1] === \"\\n\",\n });\n open = null;\n }\n return open === null ? regions : null;\n}\n","/**\n * `marker-md` — 루트 `CLAUDE.md` · `AGENTS.md` (#551 · ADR-097 §6.2).\n *\n * 몫의 키 = 마커 블록 이름(`import` · `agents` · `anchor` · `skills` …), 값 = 블록 본문. 마커 형식은\n * `agents-md-merge.ts` 의 `marker` 가 SSOT 이고(`<!-- uzys-harness:<name>:start -->`), 루트 `CLAUDE.md` 의\n * import 블록도 같은 형식이다.\n *\n * 지금 코드(`upsertHarnessImport` · `stripHarnessImport`)는 호출부를 그대로 둔 채 남는다 — 그 짝은 끝\n * 개행이 없는 파일에서 원본으로 돌아오지 않는다(`\"x\"` → 붙였다 빼면 `\"x\\n\"`). 이 어댑터는 붙이기·빼기를\n * `text-runs.ts` 한 짝으로 해서 **바이트 동일**을 지킨다. 배선(어느 파일에 어느 블록을)은 PR-3 · PR-4 다 —\n * `AGENTS.md` 의 하네스가 만든 파일(절 모델, `mergeAgentsMd`)과 첫 접촉(이 블록 모델)의 분기는 PR-4.\n *\n * 블록은 마커가 **한 줄을 통째로** 차지할 때만 블록이다. 같은 이름이 두 번 나오거나 · 시작만 있고 끝이\n * 없거나 · 블록 안에 블록이 열리면 **읽지 못한 파일**이다 — 한 바이트도 쓰지 않는다(ⓐ).\n */\n\nimport { marker, wrapHarnessBlock } from \"../agents-md-merge.js\";\nimport { hashContent } from \"../install-log.js\";\nimport {\n type PortionAdapter,\n planStrip,\n planUpsert,\n type StripResult,\n type UpsertResult,\n} from \"./contract.js\";\nimport { appendRun, applyEdits, type Edit, parseRegions, type Region } from \"./text-runs.js\";\n\nconst MARKER_LINE = /^<!-- uzys-harness:([A-Za-z0-9_-]+):(start|end) -->$/;\n\nfunction parseBlocks(text: string): Map<string, Region> | null {\n return parseRegions(text, MARKER_LINE);\n}\n\nfunction blockText(name: string, body: string): string {\n return wrapHarnessBlock(marker(name), body);\n}\n\n/** CRLF 로 체크아웃돼도 같은 블록으로 읽는다(#551 리뷰 N6) — 안 그러면 \"설치자가 고쳤다\" 로 읽혀 갱신도 회수도 안 된다. */\nfunction normEol(s: string): string {\n return s.replace(/\\r\\n/g, \"\\n\").replace(/\\r$/, \"\");\n}\n\n/** 몫의 sha 는 쓸 때도 읽을 때도 \\r 을 뺀 본문으로 잰다(#841) — 렌더 값이 CRLF 여도 기록과 재읽기가 같은 규약이다. */\nfunction sha(body: string): string {\n return hashContent(normEol(body));\n}\n\n/**\n * `recorded` 를 주면 블록 본문 **원문** sha 가 기록과 같은 키는 그 기록을 지금 값으로 본다 — #841 이전 판이 CRLF 렌더\n * 원문으로 잰 기록도 손대지 않은 블록이면 하네스 몫으로 알아봐, 다음 갱신이 \\r 없는 규약으로 다시 적는다(자가 치유).\n */\nfunction shas(\n blocks: ReadonlyMap<string, Region>,\n recorded: ReadonlyMap<string, string> = new Map(),\n): Map<string, string> {\n return new Map(\n [...blocks].map(([k, b]) => {\n const raw = hashContent(b.body);\n return [k, recorded.get(k) === raw ? raw : sha(b.body)];\n }),\n );\n}\n\nfunction at<T>(map: ReadonlyMap<string, T>, key: string): T {\n const v = map.get(key);\n if (v === undefined) throw new Error(`marker-md: no entry for ${key}`);\n return v;\n}\n\nconst UNREADABLE = \"harness markers are broken\";\n\nexport const markerMd: PortionAdapter<string> = {\n unreadable: UNREADABLE,\n\n read(text, keys) {\n const blocks = parseBlocks(text);\n if (blocks === null) return null;\n const all = shas(blocks);\n return keys === undefined\n ? all\n : new Map([...keys].flatMap((k) => (all.has(k) ? [[k, at(all, k)] as const] : [])));\n },\n\n upsert(existing, input): UpsertResult {\n const fresh = existing === null;\n const base = existing ?? input.seed ?? \"\";\n const blocks = parseBlocks(base);\n if (blocks === null) return { ok: false, reason: UNREADABLE };\n const plan = planUpsert({\n render: new Map([...input.render].map(([k, v]) => [k, sha(v)])),\n recorded: fresh ? new Map() : input.recorded,\n present: shas(blocks, fresh ? new Map() : input.recorded),\n excluded: input.excluded,\n });\n const edits: Edit[] = [\n ...plan.replace.map((k) => {\n const b = at(blocks, k);\n const run = `${blockText(k, at(input.render, k))}${b.newline ? \"\\n\" : \"\"}`;\n return { start: b.start, end: b.end, replacement: run };\n }),\n ...plan.remove.map((k) => ({\n start: at(blocks, k).start,\n end: at(blocks, k).end,\n replacement: null,\n })),\n ];\n let text = applyEdits(base, edits);\n for (const k of plan.add) text = appendRun(text, blockText(k, at(input.render, k)));\n return {\n ok: true,\n text,\n changed: fresh || text !== base,\n portions: plan.portions,\n restored: plan.restored,\n missing: plan.missing,\n removed: plan.remove,\n removedEdited: plan.removedEdited,\n replaced: plan.replace,\n kept: plan.kept,\n };\n },\n\n strip(existing, input): StripResult {\n const blocks = parseBlocks(existing);\n if (blocks === null) return { ok: false, reason: UNREADABLE };\n const plan = planStrip({\n recorded: input.recorded,\n present: shas(blocks, input.recorded),\n excluded: input.excluded,\n });\n const text = applyEdits(\n existing,\n plan.remove.map((k) => ({\n start: at(blocks, k).start,\n end: at(blocks, k).end,\n replacement: null,\n })),\n );\n return {\n ok: true,\n text,\n changed: text !== existing,\n removed: plan.remove,\n removedEdited: plan.removedEdited,\n kept: plan.kept,\n portions: plan.portions,\n empty: text.trim() === \"\",\n };\n },\n};\n","/**\n * `AGENTS.md` 병합 — 설치자가 채운 절은 그대로 두고 하네스 소유분만 갱신한다 (#503).\n *\n * 루트 `CLAUDE.md` 는 `project-claude-merge.ts` 가 마커 블록만 갈아 끼우는 모델로 이미 그렇게\n * 한다. `AGENTS.md` 만 매번 통째로 렌더해 `owned-write` 에 넘겼고, 그래서 `update` 를 돌릴\n * 때마다 설치자가 채운 `## Project Context` 가 빈 스캐폴드로 되돌아갔다 — 백업에는 남지만\n * 옮겨 적는 것은 사람 몫이었다.\n *\n * 소유 경계:\n * - `## Project Context` · `## Project Rules` = **설치자 소유**. 본문을 디스크에서 이어받는다.\n * - 그 안의 하네스 조각만 마커로 감싸 갈아 끼운다 — `skills`(ADR-085 상시 스킬 안내) ·\n * `anchor`(`templates/CLAUDE.md` 본문). 룰·원칙이 바뀌면 여기가 바뀌어야 update 가 산다.\n * - 나머지(제목 · `## Harness Rules` · `## Session Start` · `## Protected Files`)는 하네스\n * 소유라 매번 최신 렌더가 자리를 차지한다.\n *\n * 절 경계는 **템플릿이 정의한 최상위 절 이름**으로만 판정한다. \"다음 `## ` 까지\"로 자르면\n * 안 된다 — 스캐폴드(`## Identity & Purpose` …)도 앵커(`## 1. …`)도 룰도 본문에 `## ` 를\n * 갖고 있어서, Project Context 가 첫 하위 헤딩에서 끊긴다(실측).\n */\n\nimport {\n LEGACY_AGENTS_MD_NAMED_KEYS,\n LEGACY_AGENTS_MD_PREAMBLE_KEYS,\n LEGACY_AGENTS_MD_SECTION_KEYS,\n legacyPreambleKey,\n legacySectionKey,\n RETIRED_ANCHOR_SECTION_KEYS,\n} from \"./legacy-fingerprints.js\";\nimport {\n CONTINUOUS_SKILLS_HEADING,\n continuousNoteEnd,\n dropHarnessQuoteBlocks,\n isUnfilledScaffold,\n renderContinuousSkillsNote,\n} from \"./project-claude-merge.js\";\n\nexport interface HarnessBlockMarker {\n start: string;\n end: string;\n}\n\n/** 마커 형식의 SSOT — `src/adapters/marker-md.ts` 도 이것을 쓴다(#551). */\nexport const marker = (name: string): HarnessBlockMarker => ({\n start: `<!-- uzys-harness:${name}:start -->`,\n end: `<!-- uzys-harness:${name}:end -->`,\n});\n\n/** `## Project Rules` 안의 하네스 앵커 본문(= `templates/CLAUDE.md`)이 사는 자리. */\nexport const ANCHOR_BLOCK = marker(\"anchor\");\n/** `## Project Context` 안의 상시 스킬 안내(ADR-085)가 사는 자리. */\nexport const SKILLS_BLOCK = marker(\"skills\");\n\n/**\n * #558 — 첫 접촉 `AGENTS.md`(하네스 기록에 없는 설치자 파일)의 하네스 블록 이름(설계 §6.2 `marker-md`). 루트\n * `CLAUDE.md` 의 import 블록과 같은 모델이다 — 설치자 본문은 바이트 그대로, 하네스 몫은 파일 끝의 이 블록 하나.\n */\nexport const AGENTS_BLOCK_NAME = \"agents\";\nexport const AGENTS_BLOCK = marker(AGENTS_BLOCK_NAME);\n\n/** 설치자 소유 절 — 이 둘만 디스크 본문을 이어받는다. 템플릿의 절 이름과 같아야 한다. */\nconst CONTEXT_SECTION = \"Project Context\";\nconst RULES_SECTION = \"Project Rules\";\n/** 하네스 소유 절 — 인라인 룰이 사는 자리(그 안의 `## ` 는 룰 제목이다). */\nconst HARNESS_RULES_SECTION = \"Harness Rules\";\n\n/** 하네스 조각을 마커로 감싼다 — 감싸야 다음 렌더가 그 조각만 갈아 끼울 수 있다. */\nexport function wrapHarnessBlock(m: HarnessBlockMarker, body: string): string {\n return `${m.start}\\n${body}\\n${m.end}`;\n}\n\n/**\n * ADR-085 상시 스킬 안내를 **마커로 감싸** 프로젝트 맥락 뒤에 붙인다 —\n * `withContinuousSkillsNote` 의 AGENTS.md 판.\n *\n * 왜 따로 두나: 루트 `CLAUDE.md` 는 이미 자기 import 마커 블록 안에 안내를 담고 있고\n * (`project-claude-merge.ts`), Antigravity 룰 파일은 통째로 하네스 소유라 마커가 필요 없다.\n * 마커가 필요한 것은 **사용자 본문과 한 절을 나눠 쓰는** AGENTS.md 뿐이다.\n */\nexport function withMarkedContinuousSkillsNote(\n scaffold: string,\n selectedInternalSkills: ReadonlyArray<string>,\n): string {\n const note = renderContinuousSkillsNote(selectedInternalSkills);\n return note ? `${scaffold}\\n\\n${wrapHarnessBlock(SKILLS_BLOCK, note)}` : scaffold;\n}\n\n/** 템플릿이 정의한 최상위 절 이름 — 경계 판정의 SSOT (열거 사본을 두지 않는다). */\nfunction sectionNames(template: string): ReadonlySet<string> {\n return new Set([...template.matchAll(/^## (.+)$/gm)].map((m) => (m[1] ?? \"\").trim()));\n}\n\n/** 이 줄이 **알려진** 절의 헤딩인가. 본문 속 `## ` 는 절이 아니다. */\nfunction headingName(line: string, names: ReadonlySet<string>): string | null {\n const name = /^## (.+?)\\s*$/.exec(line.replace(/\\r$/, \"\"))?.[1];\n return name !== undefined && names.has(name) ? name : null;\n}\n\n/** 절 본문(헤딩 다음 줄부터 다음 절 헤딩 전까지)을 줄 단위로. 절이 없으면 `null`. */\nfunction sectionBody(text: string, names: ReadonlySet<string>, want: string): string[] | null {\n const lines = text.split(\"\\n\");\n let start = -1;\n for (const [i, line] of lines.entries()) {\n const name = headingName(line, names);\n if (name === null) continue;\n if (start === -1) {\n if (name === want) start = i;\n } else {\n return lines.slice(start + 1, i);\n }\n }\n return start === -1 ? null : lines.slice(start + 1);\n}\n\n/** 렌더 결과를 한 번만 훑으며 지정된 절의 본문을 갈아 끼운다. */\nfunction assemble(\n rendered: string,\n names: ReadonlySet<string>,\n bodies: ReadonlyMap<string, string[]>,\n): string {\n const out: string[] = [];\n let replacing = false;\n for (const line of rendered.split(\"\\n\")) {\n const name = headingName(line, names);\n if (name !== null) {\n out.push(line);\n const override = bodies.get(name);\n replacing = override !== undefined;\n if (override !== undefined) out.push(...override);\n continue;\n }\n if (!replacing) out.push(line);\n }\n return out.join(\"\\n\");\n}\n\nfunction blockRange(lines: ReadonlyArray<string>, m: HarnessBlockMarker): [number, number] | null {\n const start = lines.findIndex((l) => l.trim() === m.start);\n if (start === -1) return null;\n const end = lines.findIndex((l, i) => i > start && l.trim() === m.end);\n return end === -1 ? null : [start, end];\n}\n\nfunction trimTrailingBlanks(lines: ReadonlyArray<string>): string[] {\n const out = [...lines];\n while (out.length > 0 && (out.at(-1) ?? \"\").trim() === \"\") out.pop();\n return out;\n}\n\n/**\n * #774 — 옛 판이 바이트 그대로 쓴 `## ` 절인가: #739 앵커 절(`RETIRED_ANCHOR_SECTION_KEYS`) ∪ 마커 이전 `AGENTS.md.template` 의\n * 절(`LEGACY_AGENTS_MD_SECTION_KEYS` — `Session Start` 뒤 꼬리 절 등) ∪ 하네스 이름 절의 옛 렌더(`LEGACY_AGENTS_MD_NAMED_KEYS` —\n * `Project Rules` 머리말 · `Harness Rules` 안내 · 인라인 룰 절). 어디 있든 하네스가 쓴 것이다 — 설치자가 한 줄이라도 고쳤으면 지문이\n * 달라 설치자 절로 남는다(잃는 쪽보다 남는 쪽). 소유 근거가 아니라 걷을 근거다(ADR-111): 걷는 쓰기는 호출부가 파일 1회 백업을 남긴다.\n */\nfunction isLegacyHarnessSection(lines: ReadonlyArray<string>): boolean {\n const key = legacySectionKey(lines);\n return (\n RETIRED_ANCHOR_SECTION_KEYS.has(key) ||\n LEGACY_AGENTS_MD_SECTION_KEYS.has(key) ||\n LEGACY_AGENTS_MD_NAMED_KEYS.has(key)\n );\n}\n\n/**\n * #774 (N2) — 첫 접촉 블록(`agents`)을 뗀 본문. v26.169 update 는 옛 하네스 `AGENTS.md`(블록 모델로 읽혔다)의 끝에 이 블록을\n * 붙였다 — 옛 파일인지는 블록 밖 본문으로 판정하고, 절 모델로 갈아탈 때도 블록 밖 본문을 이어받는다(블록 안은 렌더가 다시 쓴다).\n */\nexport function withoutAgentsBlock(text: string): string {\n const lines = text.split(\"\\n\");\n const at = blockRange(lines, AGENTS_BLOCK);\n if (at === null) return text;\n const head = trimTrailingBlanks(lines.slice(0, at[0]));\n const tail = lines.slice(at[1] + 1);\n while (tail.length > 0 && (tail[0] ?? \"\").trim() === \"\") tail.shift();\n return tail.length > 0 ? [...head, \"\", ...tail].join(\"\\n\") : `${head.join(\"\\n\")}\\n`;\n}\n\n/**\n * #774 — 마커 이전 판(v26.70 ~ v26.159)의 하네스가 쓴 `AGENTS.md` 인가. 술어는 **머리 지문 하나**다 — 제목 다음 블록인용\n * (`> **Generated from** …`)이 옛 템플릿 그대로일 때만 참(`LEGACY_AGENTS_MD_PREAMBLE_KEYS`). 첫 접촉 블록이 있으면 떼고 본다(N2).\n * 절 모델 마커(`anchor` · `skills`)가 있으면 거짓 — 지금 판이 아는 파일이다. 머리를 지우거나 고친 파일은 거짓(설치자 파일로 남긴다).\n * 소유를 주장하지 않는다(ADR-111) — 이 파일을 통째로 바꾸거나 걷는 쓰기는 1회 백업을 남긴다.\n */\nexport function isLegacyHarnessAgentsMd(text: string): boolean {\n const body = withoutAgentsBlock(text);\n const lines = body.split(\"\\n\");\n if (blockRange(lines, ANCHOR_BLOCK) !== null || blockRange(lines, SKILLS_BLOCK) !== null) {\n return false;\n }\n const key = legacyPreambleKey(body);\n return key !== null && LEGACY_AGENTS_MD_PREAMBLE_KEYS.has(key);\n}\n\n/**\n * 설치자 본문은 그대로 두고 그 안의 하네스 조각만 최신판으로.\n *\n * @param legacyHeading 마커가 없던 시절 그 조각이 시작하던 헤딩. 옛 설치본에서 조각을 찾아\n * 걷어내는 유일한 단서다 — 없으면 조각이 두 벌 쌓인다.\n */\nfunction refreshBlock(\n existing: ReadonlyArray<string>,\n rendered: ReadonlyArray<string>,\n m: HarnessBlockMarker,\n legacyHeading?: string,\n): string[] {\n const at = blockRange(rendered, m);\n const fresh = at === null ? null : rendered.slice(at[0], at[1] + 1);\n const here = blockRange(existing, m);\n if (here !== null) {\n const head = existing.slice(0, here[0]);\n const tail = existing.slice(here[1] + 1);\n if (fresh === null) {\n const kept = trimTrailingBlanks(head);\n return tail.length > 0 ? [...kept, ...tail] : [...kept, \"\"];\n }\n return [...head, ...fresh, ...tail];\n }\n // 마커 없는 옛 설치본 — 헤딩으로 조각을 찾아 **조각의 끝까지만** 걷어내고 그 자리에 마커째\n // 다시 넣는다. 절 끝까지 잘라내면 설치자가 안내 *뒤*에 적어 둔 문단이 첫 update 에서 사라진다\n // (#503 독립 리뷰 B1 — 백업에는 남지만 문서는 \"그대로 남는다\"고 약속한다).\n const legacyAt =\n legacyHeading === undefined\n ? -1\n : existing.findIndex((l) => l.replace(/\\r$/, \"\").trim() === legacyHeading);\n if (legacyAt === -1) {\n const kept = trimTrailingBlanks(existing);\n return fresh === null ? [...kept, \"\"] : [...kept, \"\", ...fresh, \"\"];\n }\n const head = trimTrailingBlanks(existing.slice(0, legacyAt));\n const tail = existing.slice(continuousNoteEnd(existing, legacyAt));\n while (tail.length > 0 && (tail[0] ?? \"\").trim() === \"\") tail.shift();\n const body = fresh === null ? [...head] : [...head, \"\", ...fresh];\n return tail.length > 0 ? [...body, \"\", ...tail] : [...body, \"\"];\n}\n\n/** 앵커 제목을 못 읽었을 때 — `templates/CLAUDE.md` 의 첫 줄과 같은 말. */\nconst ANCHOR_TITLE_FALLBACK = \"Working Principles\";\n\n/** 앵커(`templates/CLAUDE.md`)의 제목 — 첫 줄 `# …`. 절 모델 렌더는 이 줄을 떼고 `## Project Rules` 아래에 둔다. */\nexport function anchorTitle(claudeMd: string): string {\n const first = claudeMd.split(\"\\n\")[0] ?? \"\";\n return /^#\\s+(.+?)\\s*\\r?$/.exec(first)?.[1] ?? ANCHOR_TITLE_FALLBACK;\n}\n\nfunction markerInner(body: ReadonlyArray<string> | null, m: HarnessBlockMarker): string[] {\n if (body === null) return [];\n const at = blockRange(body, m);\n return at === null ? [] : body.slice(at[0] + 1, at[1]);\n}\n\n/**\n * #558 — 첫 접촉 블록 본문. 절 모델 렌더(`renderAgentsMd` 결과)에서 **하네스 몫만** 뽑아 한 덩어리로 만든다.\n *\n * - 설치자 절(`## Project Context` · `## Project Rules`)은 설치자 파일에 이미 있으니 뺀다 — 스캐폴드도 넣지 않는다.\n * - 그 절 안에 살던 하네스 조각(앵커 본문 · 상시 스킬 안내)은 마커를 벗겨 블록 안으로 옮긴다 — 블록 안에서 블록을\n * 열면 읽지 못한 파일이 된다(`marker-md`).\n * - 앵커에는 앵커 자신의 제목(`## Working Principles`)을 단다 — 설치자의 `## Project Rules` 와 같은 이름의 절이 두 개\n * 생기면 에이전트가 어느 쪽이 설치자 규칙인지 가를 수 없다.\n * - 나머지 하네스 절(`## Harness Rules` · `## Session Start` · `## Protected Files`)은 렌더 그대로.\n */\nexport function renderAgentsBlock(params: {\n rendered: string;\n template: string;\n anchorTitle: string;\n}): string {\n const { rendered, template } = params;\n const names = sectionNames(template);\n const anchor = markerInner(sectionBody(rendered, names, RULES_SECTION), ANCHOR_BLOCK);\n const skills = markerInner(sectionBody(rendered, names, CONTEXT_SECTION), SKILLS_BLOCK);\n const harness: string[] = [];\n let keep = false;\n for (const line of rendered.split(\"\\n\")) {\n const name = headingName(line, names);\n if (name !== null) keep = name !== CONTEXT_SECTION && name !== RULES_SECTION;\n if (keep) harness.push(line);\n }\n return [\n [`## ${params.anchorTitle}`, \"\", ...trimBlankEdges(anchor)],\n trimBlankEdges(skills),\n trimBlankEdges(harness),\n ]\n .filter((part) => part.length > 0)\n .map((part) => part.join(\"\\n\"))\n .join(\"\\n\\n\");\n}\n\nexport interface MergeAgentsMdParams {\n /** 이번 렌더 결과 — 하네스 소유 절의 최신판이자 결과물의 골격. */\n rendered: string;\n /** 디스크의 현재 내용. 파일이 없으면 `null` (신규 설치 → 렌더 그대로). */\n existing: string | null;\n /** 절 이름의 SSOT. 렌더에 쓴 그 템플릿을 그대로 넘긴다. */\n template: string;\n /**\n * v26.165.0 (ADR-103 · 독립 검증 BLOCKER-3) — 디스크의 `## Project Context` 가 **손대지 않은 스캐폴드**\n * (`isUnfilledScaffold`)면 이어받지 않고 렌더의 절(= 호출부가 넣은 씨)로 바꾼다. 옛 Antigravity 앵커에 채운 글을\n * 이미 있는 `AGENTS.md` 로 한 번 옮겨 심는 자리다. 채워져 있으면 그대로 이어받는다(두 문장을 합치지 않는다).\n * 쓰기는 이 결과 한 번 — 기록 sha 가 최종 바이트라 다음 update 가 \"고쳤다\" 로 읽지 않는다.\n */\n replaceUnfilledContext?: boolean;\n /**\n * 리뷰 #735 — 하네스가 `## Harness Rules` 에 쓸 수 있는 룰 제목 전체(`ruleHeadings` — 배포 룰 전부의 `## ` 줄). 렌더에서\n * 빠진 룰 제목(Antigravity 만 남은 집합 · 더 좁은 트랙)을 설치자 절로 되살리지 않는 근거다. **위치가 아니라 정체**로\n * 가른다 — 같은 자리에 설치자가 적은 `## Team rules` 는 이 집합에 없으므로 설치자 절로 남는다. 생략 = 빈 집합.\n */\n harnessRuleHeadings?: ReadonlySet<string>;\n}\n\n/**\n * @returns 기록할 내용. 설치자가 아무것도 안 고쳤으면 **렌더 결과와 바이트 동일**하다\n * (= 디스크와 같으면 `owned-write` 가 쓰지도 백업하지도 않는다).\n */\nexport function mergeAgentsMd(params: MergeAgentsMdParams): string {\n return mergeAgentsMdDetailed(params).text;\n}\n\nexport interface MergeAgentsMdResult {\n text: string;\n /**\n * #774 — 옛 판이 바이트 그대로 쓴 절(`isLegacyHarnessSection`)이라 되살리지 않고 떨어뜨린 수. 0 보다 크면 호출부가 쓰기 전\n * `AGENTS.md.backup-<시각>` 1회를 남긴다(ADR-111 — 현행 번들에 없는 바이트라 다시 깔아도 돌아오지 않는다).\n */\n droppedLegacySections: number;\n /**\n * #774 — 옛 판 파일(`isLegacyHarnessAgentsMd`)의 하네스 이름 절 중 설치자가 고친 것(바이트가 옛 렌더와 다르다)의 제목. 절 모델에서\n * 이 절은 하네스 몫이라 지금 판으로 바뀐다 — 비어 있지 않으면 호출부가 쓰기 전 1회 백업을 남기고 화면이 \"고친 절은 백업에 있다\" 고\n * 말한다(ADR-111). 옛 판 파일이 아니면 빈 배열.\n */\n editedLegacySections: string[];\n}\n\n/** `mergeAgentsMd` 와 같은 병합 — 떨어뜨린 옛 하네스 절 수까지 돌려준다. */\nexport function mergeAgentsMdDetailed(params: MergeAgentsMdParams): MergeAgentsMdResult {\n const { rendered, existing, template } = params;\n if (existing === null) {\n return { text: rendered, droppedLegacySections: 0, editedLegacySections: [] };\n }\n const names = sectionNames(template);\n const bodies = new Map<string, string[]>();\n\n const contextNow = sectionBody(existing, names, CONTEXT_SECTION);\n const contextNew = sectionBody(rendered, names, CONTEXT_SECTION);\n const seedOverUnfilled =\n params.replaceUnfilledContext === true &&\n contextNow !== null &&\n isUnfilledScaffold(installerLines(contextNow, SKILLS_BLOCK));\n if (contextNow !== null && contextNew !== null && !seedOverUnfilled) {\n bodies.set(\n CONTEXT_SECTION,\n refreshBlock(contextNow, contextNew, SKILLS_BLOCK, CONTINUOUS_SKILLS_HEADING),\n );\n }\n\n const rulesNow = sectionBody(existing, names, RULES_SECTION);\n const rulesNew = sectionBody(rendered, names, RULES_SECTION);\n // 마커 없는 옛 설치본의 `## Project Rules` 는 통째로 하네스가 렌더한 앵커 본문이고, 어디까지가\n // 설치자가 덧쓴 줄인지 판정할 근거가 없다 — 그때는 최신판으로 간다(편집분은 `owned-write` 의\n // `.backup-<stamp>` 에 남는다). 마커가 있는 판부터는 블록만 갈아 끼우므로 덧쓴 줄이 남는다.\n // 다만 그 본문 안에 설치자가 적은 `## ` 절은 라이브에 남긴다(ADR-097 결정 4 · #739) — 렌더의 같은 절에 없는 제목이\n // 그것이다(집합 대조). 아래 `extraTopLevelSections` 가 다른 설치자 절과 같이 파일 끝에 되살린다.\n let replacedRulesHeadings: ReadonlySet<string> | null = null;\n if (rulesNow !== null && rulesNew !== null && blockRange(rulesNow, ANCHOR_BLOCK) !== null) {\n bodies.set(RULES_SECTION, refreshBlock(rulesNow, rulesNew, ANCHOR_BLOCK));\n } else if (rulesNow !== null && rulesNew !== null) {\n replacedRulesHeadings = headingLines(rulesNew);\n }\n\n const merged = bodies.size === 0 ? rendered : assemble(rendered, names, bodies);\n // #643 — 스켈리톤이 모르는 상위 절(사용자가 파일 끝에 붙인 `## My Team Conventions` 등)은\n // 렌더에 자리가 없어 조용히 사라졌다. ADR-095 가 설치자 소유로 정한 것은 **아는 절들**이다 —\n // 모르는 절을 지우는 권한까지는 없다. 원래 순서 그대로 파일 끝에 되살린다.\n // 어느 절이 설치자 것인지는 uninstall 의 걷기와 같은 판정(`headingOwner`)이 정한다(#739). 아는 절 본문\n // (`## Project Context`·`## Project Rules` 아래)의 `## ` 는 그 본문의 소유 규칙(마커 블록)이 맡으므로 다시 모으지 않는다 —\n // 모으면 update 마다 한 벌씩 늘거나, 옛 판 앵커·스캐폴드 제목이 파일 끝에 되살아난다.\n const mergedText = merged.replace(/\\r/g, \"\");\n const candidates = extraTopLevelSections(\n existing,\n names,\n rendered,\n params.harnessRuleHeadings ?? new Set(),\n replacedRulesHeadings,\n );\n const droppedLegacySections = candidates.legacy;\n // #774 — 옛 판 파일의 하네스 이름 절을 설치자가 고쳤으면(옛 렌더 바이트와 다르다) 지금 판으로 바뀌는 사실을 호출부가 말하게 한다.\n // uninstall 의 걷기와 같은 판정이다(그쪽은 남긴다 — 바꿔 쓸 지금 판이 없다)\n const editedLegacySections = isLegacyHarnessAgentsMd(existing)\n ? installerSectionsInHarnessArea(\n existing,\n names,\n new Set([...(params.harnessRuleHeadings ?? []), ...renderedRuleHeadings(rendered, names)]),\n true,\n ).keptEdited\n : [];\n const extras = candidates.sections.filter(\n (e) => !mergedText.includes(e.join(\"\\n\").replace(/\\r/g, \"\")),\n );\n if (extras.length === 0) return { text: merged, droppedLegacySections, editedLegacySections };\n return {\n text: `${merged.replace(/\\n+$/, \"\\n\")}${extras.map((e) => e.join(\"\\n\")).join(\"\\n\")}`.replace(\n /\\n+$/,\n \"\\n\",\n ),\n droppedLegacySections,\n editedLegacySections,\n };\n}\n\n/** `headingOwner` 의 판정 — 하네스 것 · 설치자 것 · 아는 절 본문(그 절의 소유 규칙이 맡는다). */\nexport type HeadingOwner = \"harness\" | \"installer\" | \"body\";\n\n/**\n * #739 — `AGENTS.md` 의 `## ` 줄 하나가 누구 것인가. update 의 병합(`extraTopLevelSections`)과 uninstall 의 걷기\n * (`installerSectionsInHarnessArea`)가 **이 함수 하나**로 가른다 — 한쪽만 바뀌면 #736 처럼 설치자 글이 사라진다.\n *\n * - 템플릿의 절 이름 → `harness`(절 경계)\n * - `## Project Context` · `## Project Rules` 안 → `body`(그 절 본문 — 소유는 마커 블록 · `installerLines` 가 정한다)\n * - `## Harness Rules` 안이고 하네스 룰 제목 집합에 든다 → `harness`(정체로 가른다 — 위치가 아니다, 리뷰 #735)\n * - 그 밖 → `installer`\n *\n * @param bare 줄 끝 `\\r` 을 뗀 줄. `region` 은 호출부가 줄을 훑으며 넘기는 직전 절 이름(아직 없으면 `null`).\n */\nexport function headingOwner(\n bare: string,\n ctx: {\n region: string | null;\n names: ReadonlySet<string>;\n harnessHeadings: ReadonlySet<string>;\n },\n): HeadingOwner {\n const name = /^## (.+?)\\s*$/.exec(bare)?.[1];\n if (name !== undefined && ctx.names.has(name)) return \"harness\";\n if (ctx.region === CONTEXT_SECTION || ctx.region === RULES_SECTION) return \"body\";\n if (ctx.region === HARNESS_RULES_SECTION && ctx.harnessHeadings.has(bare.trim())) {\n return \"harness\";\n }\n return \"installer\";\n}\n\n/** 절 본문 줄들 중 `## ` 제목 줄(끝 공백을 뗀 것). */\nfunction headingLines(body: ReadonlyArray<string>): Set<string> {\n return new Set(body.filter((l) => /^## /.test(l)).map((l) => l.replace(/\\r$/, \"\").trim()));\n}\n\n/** 렌더의 `## Harness Rules` 절에 든 `## ` 제목 — 병합이 하네스 룰 제목으로 더 넘기는 몫. */\nfunction renderedRuleHeadings(rendered: string, names: ReadonlySet<string>): Set<string> {\n return headingLines(sectionBody(rendered, names, HARNESS_RULES_SECTION) ?? []);\n}\n\n/**\n * 설치자가 하네스 절 자리에 적은 `## ` 절을 [헤딩 포함 줄 배열] 단위로 원래 순서대로 — 병합이 파일 끝에 되살린다.\n * `replacedRulesHeadings` 가 있으면 `## Project Rules` 본문을 통째로 갈아 끼우는 중이다(마커 없는 옛 판) — 그 본문의\n * `## ` 중 이 집합(렌더의 같은 절 제목)에 없는 것도 설치자 절로 모은다. 단 옛 판이 바이트 그대로 쓴 절(앵커 절 · 옛 템플릿\n * 꼬리 절 — `isLegacyHarnessSection`)은 **어느 후보 자리에 있든** 하네스가 쓰고 은퇴시킨 것이다 — 되살리지 않고 `legacy` 로\n * 센다(#774 — 꼬리 절은 `## Project Rules` 밖에 있어 그 본문만 거르면 못 잡는다).\n */\nfunction extraTopLevelSections(\n existing: string,\n names: ReadonlySet<string>,\n rendered: string,\n harnessRuleHeadings: ReadonlySet<string>,\n replacedRulesHeadings: ReadonlySet<string> | null,\n): { sections: string[][]; legacy: number } {\n // 하네스 룰 제목 = 넘겨받은 배포 룰 제목 전부(렌더에서 빠진 룰도 설치자 절로 되살리지 않는다 — #642) ∪ 이번 렌더의\n // 룰 제목(호출부가 집합을 생략해도 지금 렌더한 룰은 하네스 것이다).\n const harnessHeadings = new Set([\n ...harnessRuleHeadings,\n ...renderedRuleHeadings(rendered, names),\n ]);\n const out: string[][] = [];\n let current: string[] | null = null;\n let region: string | null = null;\n for (const line of existing.split(\"\\n\")) {\n const bare = line.replace(/\\r$/, \"\");\n const name = /^## (.+?)\\s*$/.exec(bare)?.[1];\n if (name !== undefined) {\n if (current !== null) out.push(current);\n if (names.has(name)) region = name;\n const owner = headingOwner(bare, { region, names, harnessHeadings });\n const replacedBody =\n owner === \"body\" &&\n region === RULES_SECTION &&\n replacedRulesHeadings !== null &&\n !replacedRulesHeadings.has(bare.trim());\n current = owner === \"installer\" || replacedBody ? [line] : null;\n continue;\n }\n if (current !== null) current.push(line);\n }\n if (current !== null) out.push(current);\n const legacy = out.filter(isLegacyHarnessSection).length;\n // 끝의 빈 줄 껍데기는 되살릴 필요 없다 — 병합 결과가 정리한다.\n const sections = out\n .filter((lines) => !isLegacyHarnessSection(lines))\n .map((lines) => {\n const trimmed = [...lines];\n while (trimmed.length > 1 && (trimmed.at(-1) ?? \"\").trim() === \"\") trimmed.pop();\n return trimmed;\n })\n .filter((lines) => lines.length > 1 || /^## /.test(lines[0] ?? \"\"));\n return { sections, legacy };\n}\n\n/** 절 본문에서 하네스 마커 블록 하나를 뺀 나머지 = 설치자가 적은 줄. 절이 없으면 빈 배열. */\nfunction installerLines(body: ReadonlyArray<string> | null, m: HarnessBlockMarker): string[] {\n if (body === null) return [];\n const at = blockRange(body, m);\n return at === null ? [...body] : [...body.slice(0, at[0]), ...body.slice(at[1] + 1)];\n}\n\n/** 비교용 정규화 — 빈 줄과 줄 끝 공백은 \"내용\"이 아니다. */\nfunction compact(lines: ReadonlyArray<string>): string[] {\n return lines.map((l) => l.replace(/\\r$/, \"\").trimEnd()).filter((l) => l.trim() !== \"\");\n}\n\nfunction trimBlankEdges(lines: ReadonlyArray<string>): string[] {\n const out = trimTrailingBlanks(lines);\n while (out.length > 0 && (out[0] ?? \"\").trim() === \"\") out.shift();\n return out;\n}\n\n/**\n * 설치자가 `## Project Context` 에 적어 둔 줄 (하네스 마커 블록 제외). 절이 없으면 `null`.\n *\n * #528 앵커 씨 뿌리기가 다른 앵커로 옮길 때 읽는 자리 — 경계는 `stripHarnessFromAgentsMd` 와\n * 같다(템플릿의 최상위 절 이름). 판정(\"채워졌는가\")은 하지 않는다: 그 판정을 두 곳에 두면\n * 갈리고, 여기 호출부는 자기 스캐폴드와 비교할 줄 안다.\n */\nexport function projectContextInstallerLines(params: StripAgentsMdParams): string[] | null {\n const { existing, template } = params;\n const body = sectionBody(existing, sectionNames(template), CONTEXT_SECTION);\n return body === null ? null : installerLines(body, SKILLS_BLOCK);\n}\n\nexport interface StripAgentsMdParams {\n /** 디스크의 현재 내용. */\n existing: string;\n /** 절 이름의 SSOT — 이 파일을 렌더한 그 템플릿. */\n template: string;\n /**\n * #736 — 하네스가 `## Harness Rules` 에 쓸 수 있는 룰 제목 전체(`ruleHeadings`). 병합의 같은 이름 인자와 같은 판정이다:\n * 그 자리의 `## ` 중 이 집합에 든 것만 하네스 것이고, 나머지는 설치자가 이어 쓴 절이다. 생략 = 빈 집합.\n */\n harnessRuleHeadings?: ReadonlySet<string>;\n}\n\n/**\n * #736 — 하네스 절 자리(`## Harness Rules` · `## Session Start` · `## Protected Files` · 파일 끝)에 설치자가 적은 `## ` 절을\n * [헤딩 포함 줄 배열] 단위로 원래 순서대로. update 가 `extraTopLevelSections` 로 살려 둔 절이 여기 든다 — 걷기가 이 절을\n * 모르면 uninstall 이 그 글을 지운다(파일째이거나 그 절만).\n *\n * 하네스 것 = 템플릿의 절 이름 · `## Harness Rules` 안의 하네스 룰 제목. 그 밖의 `## ` 는 설치자 절이다(정체로 가른다 —\n * 위치가 아니다). `## Project Context` · `## Project Rules` 본문은 `installerLines` 가 맡으므로 여기서 세지 않는다.\n *\n * #774 — 병합(`extraTopLevelSections`)과 같은 지문으로 옛 판이 바이트 그대로 쓴 절은 걷는다(`legacy` 로 센다 — 호출부가 1회\n * 백업). `legacyFile` = 마커 이전 판의 파일이다: 하네스 **이름 절**(템플릿 제목 절 — `## Project Rules` 머리말 · `## Harness Rules`\n * 안내 · `## Session Start` · 그리고 하네스 룰 제목 절)도 이름이 아니라 바이트로 가른다 — 옛 렌더 그대로(지문 일치)면 걷고, 다르면\n * 설치자가 고친 절로 남기고 `keptEdited` 에 제목을 싣는다(사용자 결정 ⓓ \"설치자가 고친 절은 남는다\"). `## Project Rules` 본문의 `## `\n * 는 옛 앵커 절 후보다(지문 일치 = 앵커 절 · 불일치 = 설치자 절). `## Project Context` 본문은 호출부가 맡는다.\n */\nfunction installerSectionsInHarnessArea(\n existing: string,\n names: ReadonlySet<string>,\n harnessRuleHeadings: ReadonlySet<string>,\n legacyFile = false,\n): { sections: string[][]; legacy: number; keptEdited: string[] } {\n const out: Array<{ lines: string[]; named: boolean }> = [];\n let current: { lines: string[]; named: boolean } | null = null;\n let region: string | null = null;\n for (const line of existing.split(\"\\n\")) {\n const bare = line.replace(/\\r$/, \"\");\n const name = /^## (.+?)\\s*$/.exec(bare)?.[1];\n if (name !== undefined) {\n if (current !== null) out.push(current);\n if (names.has(name)) region = name;\n const owner = headingOwner(bare, { region, names, harnessHeadings: harnessRuleHeadings });\n if (!legacyFile) current = owner === \"installer\" ? { lines: [bare], named: false } : null;\n else if (region === CONTEXT_SECTION) current = null;\n else current = { lines: [bare], named: owner === \"harness\" };\n continue;\n }\n if (current !== null) current.lines.push(bare);\n }\n if (current !== null) out.push(current);\n const sections: string[][] = [];\n const keptEdited: string[] = [];\n let legacy = 0;\n for (const { lines, named } of out) {\n if (isLegacyHarnessSection(lines)) {\n legacy++;\n continue;\n }\n if (named) {\n // 본문이 빈 이름 절(머리말 없는 앵커의 `## Project Rules`)은 걷을 것도 남길 것도 없다\n if (lines.slice(1).every((l) => l.trim() === \"\")) continue;\n keptEdited.push((lines[0] ?? \"\").replace(/^## /, \"\").trim());\n }\n sections.push(trimTrailingBlanks(lines));\n }\n return { sections, legacy, keptEdited };\n}\n\n/** `stripAgentsMd` 의 결과. */\nexport interface AgentsMdStripResult {\n /** 남길 내용 — `null` = 파일째 삭제(마커도 머리 지문도 없는 파일은 호출부의 기준선 sha 가 소유를 말한다). */\n remainder: string | null;\n /**\n * #774 — 걷은 것에 옛 판의 하네스 바이트(지문 일치 절 · 옛 파일 통째)가 들었다. 참이면 호출부가 쓰거나 지우기 **전**\n * `AGENTS.md.backup-<시각>` 1회를 남긴다(ADR-111 — 현행 번들에 없는 바이트라 다시 깔아도 돌아오지 않는다).\n */\n legacyRemoved: boolean;\n /** 걷은 옛 하네스 절 수(지문 일치) — 화면이 센다. */\n legacySections: number;\n /**\n * #774 — 하네스 이름 절(`Session Start` · `Project Rules` 머리말 · 하네스 룰 제목 절 …)인데 바이트가 옛 렌더와 달라 **설치자가 고친\n * 절로 남긴** 것의 제목(사용자 결정 ⓓ). 화면이 \"고친 절 N 개를 남겼다\" 고 말한다.\n */\n keptEdited: string[];\n}\n\n/**\n * uninstall 용 — 하네스 몫을 걷어내고 설치자 절만 남긴다 (#516).\n *\n * 루트 `CLAUDE.md` 의 `stripHarnessImport` 와 같은 자리다. `update` 가 설치자 절을 이어받아 다시\n * 쓰면서(#503) 그 문장이 기준선 sha 안으로 들어갔고, uninstall 은 \"기준선과 같으면 우리 것\"으로\n * 보고 파일을 통째로 지웠다 — 설치자가 채운 `## Project Context` 가 살아 있는 파일에서 사라졌다.\n *\n * 남기는 것 = 제목 줄 + `## Project Context` · `## Project Rules` 의 설치자 줄(마커 블록은 뺀다) + 하네스 절 자리에\n * 설치자가 적은 `## ` 절(#736 — `installerSectionsInHarnessArea`).\n * 걷어내는 것 = 하네스 소유 절 전부(`## Harness Rules` · `## Session Start` · `## Protected Files`)와\n * 두 마커 블록. 절 경계는 렌더 때와 같이 템플릿의 최상위 절 이름으로 판정한다(ADR-095 D3).\n *\n * @returns 남길 내용. 설치자가 아무것도 안 채웠으면(Project Context 가 스캐폴드 그대로이고\n * Project Rules 에 덧쓴 줄이 없고 덧붙인 절도 없으면) `null` = 파일째 삭제. **마커도 옛 판 머리 지문도 없는 파일도 `null`**\n * — 그 파일의 소유는 기준선 sha 가 그대로 말한다(호출부는 sha 가 다르면 이 함수를 부르지 않고 파일을 남긴다).\n */\nexport function stripHarnessFromAgentsMd(params: StripAgentsMdParams): string | null {\n return stripAgentsMd(params).remainder;\n}\n\n/** `stripHarnessFromAgentsMd` 와 같은 걷기 — 옛 판의 하네스 바이트를 걷었는지까지 돌려준다(#774). */\nexport function stripAgentsMd(params: StripAgentsMdParams): AgentsMdStripResult {\n const { existing, template } = params;\n const lines = existing.split(\"\\n\");\n const names = sectionNames(template);\n const headings = params.harnessRuleHeadings ?? new Set<string>();\n if (blockRange(lines, ANCHOR_BLOCK) === null && blockRange(lines, SKILLS_BLOCK) === null) {\n if (!isLegacyHarnessAgentsMd(existing)) {\n return {\n remainder: null,\n legacyRemoved: false,\n legacySections: 0,\n keptEdited: [],\n };\n }\n const body = withoutAgentsBlock(existing);\n return stripLegacyAgentsMd(body, names, headings);\n }\n const context = installerLines(sectionBody(existing, names, CONTEXT_SECTION), SKILLS_BLOCK);\n const rules = installerLines(sectionBody(existing, names, RULES_SECTION), ANCHOR_BLOCK);\n // BLOCKER-2 — 하네스가 쓴 줄(배너 · FILL 주석 · 상시 스킬 안내)을 뗀 뒤 판정한다. FILL 문구가 바뀐 릴리즈에서\n // 옛 판의 안 채운 파일을 \"채웠다\" 로 읽으면 스캐폴드만 든 파일이 남는다.\n const contextFilled = !isUnfilledScaffold(context);\n const rulesFilled = compact(rules).length > 0;\n const extras = installerSectionsInHarnessArea(existing, names, headings);\n const legacy = {\n legacyRemoved: extras.legacy > 0,\n legacySections: extras.legacy,\n keptEdited: [],\n };\n if (!contextFilled && !rulesFilled && extras.sections.length === 0) {\n return { remainder: null, ...legacy };\n }\n\n const out: string[] = [];\n const title = (lines[0] ?? \"\").replace(/\\r$/, \"\");\n if (/^# /.test(title)) out.push(title, \"\");\n // 안 채운 스캐폴드는 하네스가 쓴 글이다 — 남길 설치자 몫이 덧붙인 절뿐이면 Project Context 를 싣지 않는다.\n if (contextFilled || rulesFilled) {\n out.push(`## ${CONTEXT_SECTION}`, \"\", ...trimBlankEdges(context), \"\");\n }\n if (rulesFilled) out.push(`## ${RULES_SECTION}`, \"\", ...trimBlankEdges(rules), \"\");\n for (const section of extras.sections) out.push(...section, \"\");\n return { remainder: out.join(\"\\n\"), ...legacy };\n}\n\n/**\n * #774 — 마커 이전 판의 하네스 `AGENTS.md`(머리 지문 일치, 첫 접촉 블록은 뗀 본문)를 걷는다: 머리 블록인용과 지문 일치 절(앵커 ·\n * 꼬리 · 하네스 이름 절의 옛 렌더)은 하네스 것이고, 지문이 다른 절은 — 이름이 하네스 것(`Session Start` · `Project Rules` 머리말 ·\n * 하네스 룰 제목)이어도 — 설치자가 쓰거나 고친 절로 남는다(사용자 결정 ⓓ). `## Project Context` 는 안 채운 스캐폴드면 걷고, 채웠으면\n * 하네스 배너만 떼고 남긴다. 남는 것이 없으면 `null`(파일째 삭제). 어느 쪽이든 옛 판 바이트를 걷으므로 `legacyRemoved` 는 참이다.\n */\nfunction stripLegacyAgentsMd(\n existing: string,\n names: ReadonlySet<string>,\n headings: ReadonlySet<string>,\n): AgentsMdStripResult {\n // `## Project Context` 가 없던 판(v26.70~v26.95)은 채울 자리도 없었다 — 빈 본문을 \"채웠다\" 로 읽지 않는다\n const contextBody = sectionBody(existing, names, CONTEXT_SECTION);\n const context = dropHarnessQuoteBlocks(contextBody ?? []);\n const contextFilled = contextBody !== null && !isUnfilledScaffold(context);\n const extras = installerSectionsInHarnessArea(existing, names, headings, true);\n const result = {\n legacyRemoved: true,\n legacySections: extras.legacy,\n keptEdited: extras.keptEdited,\n };\n if (!contextFilled && extras.sections.length === 0) return { remainder: null, ...result };\n const out: string[] = [];\n const title = (existing.split(\"\\n\")[0] ?? \"\").replace(/\\r$/, \"\");\n if (/^# /.test(title)) out.push(title, \"\");\n if (contextFilled) out.push(`## ${CONTEXT_SECTION}`, \"\", ...trimBlankEdges(context), \"\");\n for (const section of extras.sections) out.push(...section, \"\");\n return { remainder: out.join(\"\\n\"), ...result };\n}\n","/**\n * `toml-region` — `.codex/config.toml` (#551 · ADR-097 §6.2).\n *\n * 몫 = 이름 붙은 구간(`# uzys-harness:<name>:start` ~ `:end`). 설계의 두 구간:\n * `top` 최상위 키(`approval_policy` · `sandbox_mode`) — **파일 맨 앞**(BOM 이 있으면 그 뒤)에 끼운다. 파일 끝이면 설치자의 마지막\n * `[table]` 안으로 들어가 Codex 가 조용히 무시하고(B4 · `strict_config.rs`), \"첫 `[table]` 앞\" 을 줄로\n * 찾으면 여러 줄 문자열 안의 `[…]` 줄에 속는다(#551 리뷰 N7). 파일 시작은 늘 루트 표다.\n * 그 밖 표 구간(`[sandbox_workspace_write]` · `[[hooks.*]]` · `[mcp_servers.*]` …) — 파일 끝에 붙인다.\n *\n * **판정과 사후 검증은 실제 TOML 파서(`smol-toml`)로 한다**(#551 리뷰 B2 — 구조 스캐너는 인라인 표 · 점 키가 만드는\n * 암묵 표를 못 봐 Codex 가 못 읽는 파일을 `ok:true` 로 냈다). 쓰기는 구간 텍스트 그대로다 — 직렬화로 설치자 파일을\n * 다시 쓰지 않는다.\n * - 충돌: 구간 밖(설치자 것)을 파싱한 객체에 렌더 항목의 키 경로가 명시 · 암묵 · 인라인 · 점 키 어느 형태로든 있으면\n * 그 항목을 구간에서 뺀다(설치자 값 우선, `kept` 로 알린다). 경로가 없어도 그 항목을 더한 문서가 파싱되지 않으면\n * (닫힌 인라인 표를 넓히는 등) 같이 뺀다. `[[배열 표]]` 는 더하기라 경로 충돌로 보지 않는다.\n * - 설치자 값 불변(#605 · ADR-097 §6.2 ⓑ): 그 항목을 더한 문서에서 설치자의 값이 하나라도 달라지면 뺀다 — 배열은\n * **끝에 붙는 것만** 더하기다(설치자 `[[hooks]]` 원소 안으로 하네스 표가 들어가면 뺀다). 뺀 항목을 경로 접두로\n * 갖는 같은 구간의 뒤 항목도 함께 뺀다 — 부모 없이 자식만 들어가면 뜻이 달라진다.\n * - 사후 검증: 쓴 결과를 다시 파싱해 실패하면 쓰지 않는다(`ok:false`).\n * - 기존 파일이 파싱되지 않으면 unreadable — 한 바이트도 쓰지 않는다(#574).\n */\n\nimport { parse } from \"smol-toml\";\nimport { hashContent } from \"../install-log.js\";\nimport {\n type PortionAdapter,\n planStrip,\n planUpsert,\n type StripResult,\n type UpsertResult,\n} from \"./contract.js\";\nimport {\n appendRun,\n applyEdits,\n type Edit,\n insertRun,\n parseRegions,\n type Region,\n} from \"./text-runs.js\";\n\n/** 파일 맨 앞에 들어가는 구간 이름. 나머지 이름은 파일 끝. */\nexport const TOP_REGION = \"top\";\n\nconst MARKER_LINE = /^# uzys-harness:([A-Za-z0-9_-]+):(start|end)$/;\nconst UNREADABLE = \"invalid TOML\";\nconst BOM = \"\\uFEFF\";\nconst MARKERS_BROKEN = \"harness markers are broken\";\nconst WOULD_BREAK = \"the merged result would not be readable TOML\";\n\ntype Table = Record<string, unknown>;\n\n/** TOML 로 읽히면 그 객체, 아니면 null. */\nexport function readToml(text: string): Table | null {\n try {\n return parse(text) as Table;\n } catch {\n return null;\n }\n}\n\nfunction isTable(v: unknown): v is Table {\n return typeof v === \"object\" && v !== null && !Array.isArray(v) && !(v instanceof Date);\n}\n\n/** CRLF 로 체크아웃돼도 같은 구간으로 읽는다(#551 리뷰 N6). */\nfunction normEol(s: string): string {\n return s.replace(/\\r\\n/g, \"\\n\").replace(/\\r$/, \"\");\n}\n\nfunction shas(regions: ReadonlyMap<string, Region>): Map<string, string> {\n return new Map([...regions].map(([k, r]) => [k, hashContent(normEol(r.body))]));\n}\n\nfunction at<T>(map: ReadonlyMap<string, T>, key: string): T {\n const v = map.get(key);\n if (v === undefined) throw new Error(`toml-region: no entry for ${key}`);\n return v;\n}\n\n/** 구간 줄을 공백으로 가려(줄 수는 그대로) 설치자 것만 남긴다. */\nfunction maskRegions(text: string, regions: ReadonlyMap<string, Region>): string {\n let out = text;\n for (const r of regions.values()) {\n out = `${out.slice(0, r.start)}${out.slice(r.start, r.end).replace(/[^\\n]/g, \" \")}${out.slice(r.end)}`;\n }\n return out;\n}\n\n/** 렌더 구간 안의 항목 하나 — 최상위 키 하나, 또는 표 헤더와 그 아래 키들. */\ninterface Item {\n text: string;\n /** 화면에 쓰는 이름(`approval_policy` · `[mcp_servers.context7]`). */\n name: string;\n path: string[];\n header: \"table\" | \"array\" | null;\n}\n\nfunction isStatement(line: string): boolean {\n const t = line.trim();\n return t !== \"\" && !t.startsWith(\"#\");\n}\n\n/** 한 항목만 담은 객체에서 키 경로를 뽑는다 — 키가 하나뿐인 표를 따라 내려간다. */\nfunction soloPath(obj: Table): { path: string[]; array: boolean } {\n const path: string[] = [];\n let cur: unknown = obj;\n while (isTable(cur)) {\n const keys = Object.keys(cur);\n const only = keys[0];\n if (keys.length !== 1 || only === undefined) break;\n path.push(only);\n cur = cur[only];\n }\n return { path, array: Array.isArray(cur) };\n}\n\n/**\n * 렌더 구간을 항목으로 자른다 — 경계는 **파서가 정한다**: 다음 문장 줄에서 끊으려면 그 앞까지가 TOML 로 읽혀야 한다\n * (여러 줄 배열·문자열 안의 줄에서는 끊기지 않는다). 표 항목은 다음 헤더에서만 끊는다.\n */\nfunction segment(body: string): Item[] {\n const lines = body.split(\"\\n\");\n const cuts: number[] = [0];\n let from = 0;\n for (let i = 1; i < lines.length; i++) {\n const line = lines[i] ?? \"\";\n if (!isStatement(line)) continue;\n const first = lines.slice(from, i).find(isStatement);\n if (first === undefined) continue;\n if (first.trim().startsWith(\"[\") && !line.trim().startsWith(\"[\")) continue;\n if (readToml(lines.slice(from, i).join(\"\\n\")) === null) continue;\n cuts.push(i);\n from = i;\n }\n cuts.push(lines.length);\n const items: Item[] = [];\n for (let c = 0; c + 1 < cuts.length; c++) {\n const text = lines.slice(cuts[c], cuts[c + 1]).join(\"\\n\");\n const first = text.split(\"\\n\").find(isStatement);\n if (first === undefined) continue;\n const obj = readToml(text);\n if (obj === null) throw new Error(\"toml-region: rendered region is not readable TOML\");\n const isHeader = first.trim().startsWith(\"[\");\n const solo = soloPath(isHeader ? (readToml(first.trim()) ?? {}) : obj);\n const header = isHeader ? (solo.array ? \"array\" : \"table\") : null;\n const dotted = solo.path.join(\".\");\n const name = header === \"array\" ? `[[${dotted}]]` : header === \"table\" ? `[${dotted}]` : dotted;\n items.push({ text, name, path: solo.path, header });\n }\n return items;\n}\n\n/** 값이 같은가 — 표는 키 집합까지, 배열은 길이까지. 날짜는 표기로(`TomlDate` 는 객체라 참조로 비교하면 늘 다르다). */\nfunction same(a: unknown, b: unknown): boolean {\n if (a instanceof Date || b instanceof Date) {\n return a instanceof Date && b instanceof Date && a.toISOString() === b.toISOString();\n }\n if (Array.isArray(a)) {\n return Array.isArray(b) && a.length === b.length && a.every((v, i) => same(v, b[i]));\n }\n if (isTable(a)) {\n const keys = Object.keys(a);\n return (\n isTable(b) &&\n keys.length === Object.keys(b).length &&\n keys.every((k) => Object.hasOwn(b, k) && same(a[k], b[k]))\n );\n }\n return Object.is(a, b);\n}\n\n/**\n * #605 — 설치자 것(`before`)이 합친 문서(`after`)에 그대로 남는가. 표는 키마다 재귀(형제 키가 늘어나는 것은 괜찮다),\n * 배열은 앞부분이 그대로이고 끝에 붙는 것만 허용, 그 밖은 같은 값.\n */\nfunction unchangedUnder(before: unknown, after: unknown): boolean {\n if (isTable(before)) {\n return (\n isTable(after) &&\n Object.keys(before).every(\n (k) => Object.hasOwn(after, k) && unchangedUnder(before[k], after[k]),\n )\n );\n }\n if (Array.isArray(before)) {\n return (\n Array.isArray(after) &&\n after.length >= before.length &&\n before.every((v, i) => same(v, after[i]))\n );\n }\n return same(before, after);\n}\n\nfunction startsWith(path: ReadonlyArray<string>, prefix: ReadonlyArray<string>): boolean {\n return prefix.length > 0 && prefix.length <= path.length && prefix.every((s, i) => path[i] === s);\n}\n\nfunction hasPath(obj: Table, path: ReadonlyArray<string>): boolean {\n let cur: unknown = obj;\n for (const seg of path) {\n if (!isTable(cur) || !(seg in cur)) return false;\n cur = cur[seg];\n }\n return path.length > 0;\n}\n\n/**\n * 렌더 구간들에서 설치자와 겹치는 항목을 뺀다. 판정 문서 = `top` 몫 + 설치자 것 + 표 몫 — 최종 배치와 같다.\n * @returns 남은 본문(항목이 하나도 안 남은 구간은 빠진다)과 뺀 항목 이름\n */\nfunction filterRender(\n render: ReadonlyMap<string, string>,\n outside: string,\n): { render: Map<string, string>; kept: string[] } {\n const installer = readToml(outside) ?? {};\n const kept: string[] = [];\n const out = new Map<string, string>();\n const top: string[] = [];\n const tail: string[] = [];\n const doc = () => [...top, outside, ...tail].join(\"\\n\");\n const ordered = [...render].sort(\n ([a], [b]) => Number(b === TOP_REGION) - Number(a === TOP_REGION),\n );\n for (const [name, body] of ordered) {\n const side = name === TOP_REGION ? top : tail;\n const items = segment(body);\n if (name !== TOP_REGION && items[0] !== undefined && items[0].header === null) {\n // 파일 끝 구간이 최상위 키로 시작하면 설치자의 마지막 표 안으로 들어간다 — 렌더가 잘못됐다\n throw new Error(`toml-region: region ${name} must start with a [table] header`);\n }\n const mine: string[] = [];\n const dropped: string[][] = [];\n for (const item of items) {\n if (dropped.some((p) => startsWith(item.path, p))) {\n // 부모가 빠진 자식(`[[hooks.SessionStart.hooks]]`) — 혼자 들어가면 부모가 배열 아닌 표로 태어난다\n kept.push(item.name);\n continue;\n }\n const taken = item.header !== \"array\" && hasPath(installer, item.path);\n side.push(item.text);\n const merged = taken ? null : readToml(doc());\n if (merged === null || !unchangedUnder(installer, merged)) {\n side.pop();\n kept.push(item.name);\n dropped.push(item.path);\n continue;\n }\n mine.push(item.text);\n }\n if (mine.length > 0) out.set(name, mine.join(\"\\n\"));\n }\n return { render: out, kept: [...new Set(kept)] };\n}\n\nfunction regionText(name: string, body: string): string {\n return `# uzys-harness:${name}:start\\n${body}\\n# uzys-harness:${name}:end`;\n}\n\n/** 파일 머리의 BOM 길이(0 · 1). smol-toml 은 BOM 을 받는다 — 구간은 BOM **뒤**에 둔다(#551 PR-1 인계 ①). */\nfunction bomLength(text: string): number {\n return text.startsWith(BOM) ? BOM.length : 0;\n}\n\n/** 파일을 읽는다 — 파서가 못 읽으면 unreadable, 마커가 깨졌으면 markers. */\nfunction load(text: string): Map<string, Region> | \"unreadable\" | \"markers\" {\n if (readToml(text) === null) return \"unreadable\";\n const regions = parseRegions(text, MARKER_LINE);\n if (regions === null) return \"markers\";\n // 첫 줄의 구간은 줄 시작(0)에서 시작한다고 읽히는데 그 자리의 BOM 은 구간이 아니다 — 갈아 끼우거나 뺄 때\n // BOM 이 함께 사라지지 않게 구간을 BOM 뒤에서 시작시킨다\n const bom = bomLength(text);\n for (const r of regions.values()) if (r.start < bom) r.start = bom;\n return regions;\n}\n\nfunction fail(state: \"unreadable\" | \"markers\"): { ok: false; reason: string } {\n return { ok: false, reason: state === \"unreadable\" ? UNREADABLE : MARKERS_BROKEN };\n}\n\nexport const tomlRegion: PortionAdapter<string> = {\n unreadable: UNREADABLE,\n\n read(text, keys) {\n const regions = load(text);\n if (typeof regions === \"string\") return null;\n const all = shas(regions);\n return new Map(\n [...(keys ?? all.keys())].flatMap((k) => (all.has(k) ? [[k, at(all, k)] as const] : [])),\n );\n },\n\n upsert(existing, input): UpsertResult {\n const fresh = existing === null;\n const base = existing ?? input.seed ?? \"\";\n const regions = load(base);\n if (typeof regions === \"string\") return fail(regions);\n const wanted = new Map([...input.render].filter(([k]) => !input.excluded.has(k)));\n // 판정 문서는 `top` 몫 + 설치자 것을 잇는다 — 설치자 것에 BOM 이 남아 있으면 문서 한가운데 BOM 이 와 파싱이\n // 깨지고, 그러면 설치자에게 없는 키까지 \"kept\" 로 보고된다(#551 PR-1 인계 ①)\n const bom = bomLength(base);\n const filtered = filterRender(wanted, maskRegions(base, regions).slice(bom));\n const plan = planUpsert({\n render: new Map([...filtered.render].map(([k, v]) => [k, hashContent(v)])),\n recorded: fresh ? new Map() : input.recorded,\n present: shas(regions),\n excluded: input.excluded,\n });\n const edits: Edit[] = [\n ...plan.replace.map((k) => {\n const r = at(regions, k);\n const run = `${regionText(k, at(filtered.render, k))}${r.newline ? \"\\n\" : \"\"}`;\n return { start: r.start, end: r.end, replacement: run };\n }),\n ...plan.remove.map((k) => ({\n start: at(regions, k).start,\n end: at(regions, k).end,\n replacement: null,\n })),\n ];\n let text = applyEdits(base, edits);\n for (const k of plan.add) {\n const run = regionText(k, at(filtered.render, k));\n text = k === TOP_REGION ? insertRun(text, bom, run) : appendRun(text, run);\n }\n if (readToml(text) === null) return { ok: false, reason: WOULD_BREAK };\n return {\n ok: true,\n text,\n changed: fresh || text !== base,\n portions: plan.portions,\n restored: plan.restored,\n missing: plan.missing,\n removed: plan.remove,\n removedEdited: plan.removedEdited,\n replaced: plan.replace,\n kept: [...filtered.kept, ...plan.kept],\n };\n },\n\n strip(existing, input): StripResult {\n const regions = load(existing);\n if (typeof regions === \"string\") return fail(regions);\n const plan = planStrip({\n recorded: input.recorded,\n present: shas(regions),\n excluded: input.excluded,\n });\n const text = applyEdits(\n existing,\n plan.remove.map((k) => ({\n start: at(regions, k).start,\n end: at(regions, k).end,\n replacement: null,\n })),\n );\n return {\n ok: true,\n text,\n changed: text !== existing,\n removed: plan.remove,\n removedEdited: plan.removedEdited,\n kept: plan.kept,\n portions: plan.portions,\n empty: text.trim() === \"\",\n };\n },\n};\n","/*!\n * Copyright (c) Squirrel Chat et al., All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the\n * documentation and/or other materials provided with the distribution.\n * 3. Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\nimport { parse } from './parse.js';\nimport { stringify } from './stringify.js';\nimport { TomlDate } from './date.js';\nimport { TomlError } from './error.js';\nexport { parse, stringify, TomlDate, TomlError };\n/** @deprecated use `import * as ... from 'smol-toml'` instead */\nexport default { parse, stringify, TomlDate, TomlError };\n","/*!\n * Copyright (c) Squirrel Chat et al., All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the\n * documentation and/or other materials provided with the distribution.\n * 3. Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\nimport { parseKey } from './struct.js';\nimport { extractValue } from './extract.js';\nimport { skipVoid } from './util.js';\nimport { TomlError } from './error.js';\nfunction peekTable(ctx, key, table, meta, type) {\n let t = table;\n let m = meta;\n let k;\n let hasOwn = false;\n let state;\n for (let i = 0; i < key.length; i++) {\n if (i) {\n t = hasOwn ? t[k] : (t[k] = Object.create(null));\n m = (state = m[k]).c;\n if (type === 0 /* Type.DOTTED */ && (state.t === 1 /* Type.EXPLICIT */ || state.t === 2 /* Type.ARRAY */)) {\n return null;\n }\n if (state.t === 2 /* Type.ARRAY */) {\n let l = t.length - 1;\n t = t[l];\n m = m[l].c;\n }\n }\n k = key[i];\n if ((hasOwn = Object.hasOwn(t, k)) && m[k]?.t === 0 /* Type.DOTTED */ && m[k]?.d) {\n return null;\n }\n if (!hasOwn) {\n let unsafe = k === '__proto__';\n if (ctx.uk && (unsafe || k === 'constructor'))\n return false;\n if (unsafe) {\n Object.defineProperty(t, k, { enumerable: true, configurable: true, writable: true });\n Object.defineProperty(m, k, { enumerable: true, configurable: true, writable: true });\n }\n m[k] = {\n t: i < key.length - 1 && type === 2 /* Type.ARRAY */\n ? 3 /* Type.ARRAY_DOTTED */ : type,\n d: false,\n i: 0,\n c: Object.create(null),\n };\n }\n }\n state = m[k];\n if (state.t !== type && !(type === 1 /* Type.EXPLICIT */ && state.t === 3 /* Type.ARRAY_DOTTED */)) {\n // Bad key type!\n return null;\n }\n if (type === 2 /* Type.ARRAY */) {\n if (!state.d) {\n state.d = true;\n t[k] = [];\n }\n t[k].push(t = Object.create(null));\n state.c[state.i++] = (state = { t: 1 /* Type.EXPLICIT */, d: false, i: 0, c: Object.create(null) });\n }\n if (state.d) {\n // Redefining a table!\n return null;\n }\n state.d = true;\n if (type === 1 /* Type.EXPLICIT */) {\n t = hasOwn ? t[k] : (t[k] = Object.create(null));\n }\n else if (type === 0 /* Type.DOTTED */ && hasOwn) {\n return null;\n }\n return [k, t, state.c];\n}\nfunction validateTablePeek(ctx, peek, ptr) {\n if (peek === null || ctx.uk === 2)\n TomlError.x(peek === null\n ? 'trying to redefine an already defined table or value'\n : 'document contains an unsafe property', ctx, ptr);\n}\nexport function parse(toml, options = {}) {\n let ctx = {\n s: toml,\n p: 0,\n d: options.maxDepth ?? 1000,\n bi: options.integersAsBigInt ?? false,\n ld: options.useLegacyDate ?? true,\n uk: options.unsafeKeyBehaviour === 'throw' ? 2 : options.unsafeKeyBehaviour === 'drop' ? 1 : 0,\n };\n let res = Object.create(null);\n let meta = Object.create(null);\n let tmp;\n let skipping = false;\n let tbl = res;\n let m = meta;\n // BOM is allowed, skip.\n // JS is UTF-16, so we have to check for the UTF-16 BOM instead of the UTF-8 BOM sequence!\n if (toml.charCodeAt(0) === 0xfeff)\n ctx.p++;\n skipVoid(ctx);\n while (ctx.p < toml.length) {\n if (toml.charCodeAt(ctx.p) === 0x5b /* [ */) {\n let isTableArray = toml.charCodeAt(++ctx.p) === 0x5b; /* [ */\n tmp = ctx.p += +isTableArray;\n skipping = false;\n let k = parseKey(ctx, 0x5d /* ] */);\n if (isTableArray) {\n if (toml.charCodeAt(ctx.p) !== 0x5d /* ] */) {\n TomlError.x('expected end of table array declaration', ctx);\n }\n ctx.p++;\n }\n let p = peekTable(ctx, k, res, meta, isTableArray ? 2 /* Type.ARRAY */ : 1 /* Type.EXPLICIT */);\n if (!p) {\n validateTablePeek(ctx, p, tmp);\n skipping = true;\n }\n else {\n m = p[2];\n tbl = p[1];\n }\n }\n else {\n tmp = ctx.p;\n let k = parseKey(ctx);\n let p = peekTable(ctx, k, tbl, m, 0 /* Type.DOTTED */);\n if (!p && !skipping)\n validateTablePeek(ctx, p, tmp);\n skipVoid(ctx, true, true);\n let v = extractValue(ctx, void 0);\n if (p && !skipping)\n p[1][p[0]] = v;\n }\n skipVoid(ctx, true);\n if (ctx.p < toml.length && (tmp = toml.charCodeAt(ctx.p)) !== 0xa /* \\n */ && (tmp !== 0xd /* \\r */ || toml.charCodeAt(ctx.p + 1) !== 0xa /* \\n */)) {\n TomlError.x('each key-value declaration must be followed by an end-of-line', ctx);\n }\n skipVoid(ctx);\n }\n return res;\n}\n","/*!\n * Copyright (c) Squirrel Chat et al., All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the\n * documentation and/or other materials provided with the distribution.\n * 3. Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\nimport { parseString } from './primitive.js';\nimport { extractValue } from './extract.js';\nimport { skipVoid } from './util.js';\nimport { TomlError } from './error.js';\n/** @internal */\nexport function parseKey(ctx, end = 0x3d /* = */) {\n // States:\n // 0: before first char\n // 1: parsing bare key\n // 2: after key component\n let startPtr;\n let state = 0;\n let parsed = [];\n let sliceStart;\n let c = ctx.s.charCodeAt(startPtr = ctx.p);\n do {\n // End of key\n if (c === end) {\n if (!state)\n TomlError.x('unexpected end of key', ctx);\n if (state === 1)\n parsed.push(ctx.s.slice(sliceStart, ctx.p));\n return ctx.p++, parsed;\n }\n // Dotted key separator\n else if (c === 0x2e /* . */) {\n if (!state)\n TomlError.x('illegal empty bare key', ctx);\n if (state === 1)\n parsed.push(ctx.s.slice(sliceStart, ctx.p));\n state = 0;\n }\n // Quoted key\n else if (!state && (c === 0x22 /* \" */ || c === 0x27 /* ' */)) {\n if (c === ctx.s.charCodeAt(ctx.p + 1) && c === ctx.s.charCodeAt(ctx.p + 2))\n TomlError.x('illegal quoted key: multiline strings are not allowed', ctx);\n parsed.push(parseString(ctx));\n state = 2;\n ctx.p--;\n }\n // Whitespace; no-op in state 0 and 2, end of bare key in state 1\n else if (c === 0x20 || c === 0x9 /* \\t */) {\n if (state === 1) {\n parsed.push(ctx.s.slice(sliceStart, ctx.p));\n state = 2;\n }\n }\n // If state is post-key, no character is allowed; otherwise ensure it's a bare-key component\n else if (state === 2 || (c < 0x30 && c !== 0x2d /* - */) || (c > 0x39 && c < 0x41) || (c > 0x5a && c < 0x61 && c !== 0x5f /* _ */) || c > 0x7a) {\n TomlError.x('illegal character in key', ctx);\n }\n // Illegal character\n else if (!state) {\n state = 1;\n sliceStart = ctx.p;\n }\n } while (c = ctx.s.charCodeAt(++ctx.p));\n TomlError.x('incomplete key-value: cannot find end of key', ctx, startPtr);\n}\n/** @internal */\nexport function parseInlineTable(ctx) {\n let startPtr = ctx.p++;\n let res = Object.create(null);\n let seen = new Set();\n let c;\n while (ctx.p < ctx.s.length) {\n skipVoid(ctx);\n if ((c = ctx.s.charCodeAt(ctx.p)) === 0x7d /* } */) {\n ctx.p++;\n return res;\n }\n let k;\n let t = res;\n let hasOwn = false;\n let errPtr = ctx.p;\n let key = parseKey(ctx);\n for (let i = 0; i < key.length; i++) {\n if (i)\n t = hasOwn ? t[k] : (t[k] = Object.create(null));\n k = key[i];\n if ((hasOwn = Object.hasOwn(t, k)) && (typeof t[k] !== 'object' || seen.has(t[k]))) {\n TomlError.x('trying to redefine an already defined value', ctx, errPtr);\n }\n let unsafe = k === '__proto__';\n if (ctx.uk && (unsafe || k === 'constructor')) {\n t = ctx.uk !== 1 && TomlError.x('document contains an unsafe property', ctx, errPtr);\n break;\n }\n if (!hasOwn && unsafe) {\n Object.defineProperty(t, k, { enumerable: true, configurable: true, writable: true });\n }\n }\n if (hasOwn) {\n TomlError.x('trying to redefine an already defined value', ctx, errPtr);\n }\n skipVoid(ctx, true, true);\n let value = extractValue(ctx, 0x7d /* } */);\n if (t && typeof (t[k] = value) === 'object')\n seen.add(value);\n skipVoid(ctx);\n if ((c = ctx.s.charCodeAt(ctx.p++)) === 0x7d /* } */) {\n return res;\n }\n if (c !== 0x2c /* , */)\n TomlError.x('expected comma or end of structure', ctx, ctx.p - 1);\n }\n TomlError.x('unfinished table', ctx, startPtr);\n}\n/** @internal */\nexport function parseArray(ctx) {\n let startPtr = ctx.p++;\n let res = [];\n let c;\n while (ctx.p < ctx.s.length) {\n skipVoid(ctx);\n if ((c = ctx.s.charCodeAt(ctx.p)) === 0x5d /* ] */) {\n ctx.p++;\n return res;\n }\n res.push(extractValue(ctx, 0x5d /* ] */));\n skipVoid(ctx);\n if ((c = ctx.s.charCodeAt(ctx.p++)) === 0x5d /* ] */) {\n return res;\n }\n if (c !== 0x2c /* , */)\n TomlError.x('expected comma or end of structure', ctx, ctx.p - 1);\n }\n TomlError.x('unfinished array', ctx, startPtr);\n}\n","/*!\n * Copyright (c) Squirrel Chat et al., All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the\n * documentation and/or other materials provided with the distribution.\n * 3. Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\nimport { TomlError } from './error.js';\n/** @internal */\nexport function parseString(ctx) {\n let startPtr = ctx.p;\n let c = ctx.s.charCodeAt(ctx.p++);\n let first = c;\n let isLiteral = c === 0x27; /* ' */\n let isMultiline = c === ctx.s.charCodeAt(ctx.p) && c === ctx.s.charCodeAt(ctx.p + 1);\n if (isMultiline) {\n // Trim initial newline\n if ((c = ctx.s.charCodeAt(ctx.p += 2)) === 0xa /* \\n */)\n ctx.p++;\n else if (c === 0xd /* \\r */ && ctx.s.charCodeAt(ctx.p + 1) === 0xa /* \\n */)\n ctx.p += 2;\n }\n // For the record: it is not worth it to use a fast-path for literal strings using `indexOf`.\n // Doing a char-by-char iteration ends up dispatching less instructions, for the same wall-clock runtime.\n let parsed = '';\n let sliceStart = ctx.p;\n // states:\n // 0 - decoding\n // 1 - decoding escape\n // 2 - whitespace escape (no newline encountered yet, must fail on non-whitespace)\n // 3 - whitespace escape (newline encountered, allowed to transition back to normal decode)\n let state = 0;\n for (; ctx.p < ctx.s.length; ctx.p++) {\n c = ctx.s.charCodeAt(ctx.p);\n // Deal with newlines first, since that simplifies control character checking and handling across all states\n if (isMultiline && (c === 0xa /* \\n */ || (c === 0xd /* \\r */ && ctx.s.charCodeAt(ctx.p + 1) === 0xa /* \\n */))) {\n state = state && 3;\n }\n // Control characters are banned in TOML, so we throw an error if we encounter them\n else if ((c < 0x20 && c !== 0x9 /* \\t */) || c === 0x7f) {\n TomlError.x('control characters are not allowed in strings', ctx);\n }\n // The string might terminate while we're parsing through a newline escape.\n // It must have encountered a newline; otherwise, it'll simply fail in another branch.\n else if ((!state || state === 3) && c === first && (!isMultiline || (ctx.s.charCodeAt(ctx.p + 1) === first && ctx.s.charCodeAt(ctx.p + 2) === first))) {\n if (isMultiline) {\n // If the string ends with 4-5 quotes, then the first 1-2 are part of the string\n if (ctx.s.charCodeAt(ctx.p + 3) === first)\n ctx.p++;\n if (ctx.s.charCodeAt(ctx.p + 3) === first)\n ctx.p++;\n }\n // If we're in a newline escape still, then there's nothing to add.\n if (!state) {\n // Avoid a useless concat operation if the string can be used as-is.\n let s = ctx.s.slice(sliceStart, ctx.p);\n parsed = parsed ? parsed + s : s;\n }\n ctx.p += isMultiline ? 3 : 1;\n return parsed;\n }\n // Baseline state; keep moving forward unless an escape sequence starts\n else if (!state) {\n if (!isLiteral && c === 0x5c /* \\ */) {\n parsed += ctx.s.slice(sliceStart, (sliceStart = ctx.p));\n state = 1;\n }\n }\n else if (state === 1) {\n if (c === 0x78 /* x */ || c === 0x75 /* u */ || c === 0x55 /* U */) { // Unicode escape\n let errPtr = ctx.p++ - 1;\n let value = 0;\n let len = c === 0x78 /* x */ ? 2 : c === 0x75 /* u */ ? 4 : 8;\n for (let j = 0; j < len; j++, ctx.p++) {\n let hex = ctx.s.charCodeAt(ctx.p);\n let digit = \n /* 0-9 */ hex >= 0x30 && hex <= 0x39 ? hex - 0x30 :\n /* A-F */ hex >= 0x41 && hex <= 0x46 ? hex - 0x41 + 10 :\n /* a-f */ hex >= 0x61 && hex <= 0x66 ? hex - 0x61 + 10 : -1;\n if (digit < 0)\n TomlError.x('invalid non-hex character in unicode escape', ctx);\n value = (value << 4) | digit;\n }\n // Because JS does bitwise on signed 32bit integers, all 0xfzzzzzzz values are actually seen as negative\n if (value < 0 || value > 0x10ffff || (value >= 0xd800 && value <= 0xdfff)) {\n TomlError.x('invalid unicode escape', ctx, errPtr);\n }\n parsed += String.fromCodePoint(value);\n sliceStart = ctx.p--;\n state = 0;\n }\n // Newline escape sequence. We only need to care about spaces and tabs; newlines are dealt with earlier\n else if (isMultiline && (c === 0x20 || c === 0x9 /* \\t */)) {\n state = 2;\n }\n // Basic escape sequence\n else {\n if (c === 0x62 /* b */)\n parsed += '\\b';\n else if (c === 0x74 /* t */)\n parsed += '\\t';\n else if (c === 0x6e /* n */)\n parsed += '\\n';\n else if (c === 0x66 /* f */)\n parsed += '\\f';\n else if (c === 0x72 /* r */)\n parsed += '\\r';\n else if (c === 0x65 /* e */)\n parsed += '\\x1b';\n else if (c === 0x22 /* \" */)\n parsed += '\"';\n else if (c === 0x5c /* \\ */)\n parsed += '\\\\';\n else\n TomlError.x('unrecognised escape sequence', ctx);\n sliceStart = ctx.p + 1;\n state = 0;\n }\n }\n // Newline escape continuation: keep moving forward until the first non-whitespace char\n else if (c !== 0x20 && c !== 0x9 /* \\t */) {\n if (state === 2)\n TomlError.x('invalid escape: only line-ending whitespace may be escaped', ctx, sliceStart);\n // State cannot be zero, or we'd have branched earlier already.\n // If it's a backslash, immediately transition to the escape state so it can be processed.\n state = !isLiteral && c === 0x5c /* \\ */ ? 1 : 0;\n sliceStart = ctx.p;\n }\n }\n TomlError.x('unfinished string', ctx, startPtr);\n}\n","/*!\n * Copyright (c) Squirrel Chat et al., All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the\n * documentation and/or other materials provided with the distribution.\n * 3. Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\nfunction getLineColFromPtr(string, ptr) {\n let lines = string.slice(0, ptr).split(/\\r?\\n/);\n return [lines.length, lines.pop().length + 1];\n}\nfunction makeCodeBlock(string, line, column) {\n let lines = string.split(/\\r?\\n/);\n let codeblock = '';\n let numberLen = (Math.log10(line + 1) | 0) + 1;\n for (let i = line - 1; i <= line + 1; i++) {\n let l = lines[i - 1];\n if (!l)\n continue;\n codeblock += i.toString().padEnd(numberLen, ' ');\n codeblock += ': ';\n codeblock += l;\n codeblock += '\\n';\n if (i === line) {\n codeblock += ' '.repeat(numberLen + column + 2);\n codeblock += '^\\n';\n }\n }\n return codeblock;\n}\nexport class TomlError extends Error {\n line;\n column;\n codeblock;\n constructor(message, options) {\n const [line, column] = getLineColFromPtr(options.toml, options.ptr);\n const codeblock = makeCodeBlock(options.toml, line, column);\n super(`Invalid TOML document: ${message}\\n\\n${codeblock}`, options);\n this.line = line;\n this.column = column;\n this.codeblock = codeblock;\n }\n /** @internal */\n static x(message, ctx, ptr) {\n throw new TomlError(message, { toml: ctx.s, ptr: ptr ?? ctx.p });\n }\n}\n","/*!\n * Copyright (c) Squirrel Chat et al., All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the\n * documentation and/or other materials provided with the distribution.\n * 3. Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\nimport { parseString } from './primitive.js';\nimport { parseArray, parseInlineTable } from './struct.js';\nimport { TomlError } from './error.js';\nimport { TomlDate } from './date.js';\nfunction isDigit(char, base = 10) {\n return base === 16\n ? (char > 0x2f && char < 0x3a) || (char > 0x40 && char < 0x47) || (char > 0x60 && char < 0x67)\n : (char > 0x2f && char < 0x30 + base);\n}\nfunction isEndOfValue(char, delim) {\n // Whitespace -- we're permissive on `\\r` for performance; it'll be dealt with later anyway\n return char === 0x20 || char === 0x9 /* \\t */ || char === 0xa /* \\n */ || char === 0xd /* \\r */ ||\n // Structure end or next value delimiter\n (delim && (char === delim || char === 0x2c /* , */)) ||\n // Comment\n char === 0x23; /* # */\n}\n/** @internal */\nexport function extractValue(ctx, end) {\n let errPtr = ctx.p;\n let c = ctx.s.charCodeAt(ctx.p);\n // Structs\n if (c === 0x5b /* [ */ || c === 0x7b /* { */) {\n ctx.d-- || TomlError.x('document contains excessively nested structures. aborting.', ctx);\n let value = c === 0x5b /* [ */\n ? parseArray(ctx)\n : parseInlineTable(ctx);\n ctx.d++;\n return value;\n }\n // Strings\n if (c === 0x22 /* \" */ || c === 0x27 /* ' */) {\n return parseString(ctx);\n }\n // Booleans\n // We can fast-path because the first character is enough to know the only possible value\n if (c === 0x74 /* t */) { // Only possible valid value is `true`\n if (ctx.s.charCodeAt(++ctx.p) !== 0x72 || ctx.s.charCodeAt(++ctx.p) !== 0x75 || ctx.s.charCodeAt(++ctx.p) !== 0x65)\n TomlError.x('invalid value', ctx, errPtr);\n return ctx.p++, true;\n }\n if (c === 0x66 /* f */) { // Only possible valid value is `false`\n if (ctx.s.charCodeAt(++ctx.p) !== 0x61 || ctx.s.charCodeAt(++ctx.p) !== 0x6c || ctx.s.charCodeAt(++ctx.p) !== 0x73 || ctx.s.charCodeAt(++ctx.p) !== 0x65)\n TomlError.x('invalid value', ctx, errPtr);\n return ctx.p++, false;\n }\n if (c === 0x2b /* + */ || c === 0x2d /* - */) {\n return parseNumber(ctx, ctx.p, ctx.s.charCodeAt(++ctx.p), 0x2c - c, end);\n }\n // Rough heuristic, but `parseDate` falls back to number parsing if it's a false positive.\n // e.g.: `a = 1\\n-------------- = 1` would match this heuristic, but gracefully fallback to numbers.\n if (ctx.s.charCodeAt(ctx.p + 4) === 0x2d /* - */ && ctx.s.charCodeAt(ctx.p + 7) === 0x2d /* - */) {\n return parseDate(ctx, c, end);\n }\n // Same logic as above; `parseTime` falls back to number parsing if it's a false positive.\n if (ctx.s.charCodeAt(ctx.p + 2) === 0x3a /* : */) {\n return parseTime(ctx, c, end);\n }\n return parseNumber(ctx, ctx.p, c, 0, end);\n}\n// State:\n// 0: init\n// 1: integer\n// 2: fractional dot\n// 3: fractional\n// 4: exponent letter\n// 5: exponent\n// >9: underscore; see below\n// -> 12: was integer\n// -> 14: was fractional\n// -> 16: was exponent\n// states 2-5, 14, 16 are ONLY permitted iif base === 10\nfunction parseNumber(ctx, startPtr, startChr, sign, endChr) {\n let c = startChr;\n let state = 0;\n let hasUnderscores = false;\n // (+/-)inf\n if (c === 0x69 /* i */) {\n if (ctx.s.charCodeAt(++ctx.p) !== 0x6e || ctx.s.charCodeAt(++ctx.p) !== 0x66)\n TomlError.x('invalid value', ctx, startPtr);\n return ctx.p++, (sign || 1) / 0;\n }\n // (+/-)nan\n if (c === 0x6e /* n */) {\n if (ctx.s.charCodeAt(++ctx.p) !== 0x61 || ctx.s.charCodeAt(++ctx.p) !== 0x6e)\n TomlError.x('invalid value', ctx, startPtr);\n return ctx.p++, NaN;\n }\n // Leading zero\n // Only allowed cases: `0<EOV>`, `0.(...)`, `0e(...)`, `0x(...)`, `0b(...)`, `0o(...)`\n // FWIW, `0e(...)` is a stupid case, but it's not banned per-se so we have to parse it\n if (c === 0x30 /* 0 */) {\n if (++ctx.p >= ctx.s.length || isEndOfValue(c = ctx.s.charCodeAt(ctx.p), endChr))\n return ctx.bi === true ? 0n : 0; // note: conveniently deals with `-0`\n if (!sign) {\n if (c === 0x78 /* x */)\n return parseIntegerBaseN(ctx, startPtr, 16, endChr);\n else if (c === 0x62 /* b */)\n return parseIntegerBaseN(ctx, startPtr, 2, endChr);\n else if (c === 0x6f /* o */)\n return parseIntegerBaseN(ctx, startPtr, 8, endChr);\n }\n if (c === 0x2e /* . */)\n state = 2;\n else if (c === 0x65 /* e */ || c === 0x45 /* E */)\n state = 4;\n else\n TomlError.x('illegal leading zero', ctx, startPtr);\n }\n // If the 1st char is not a digit by now, then it's not a valid TOML value at all\n else if (!isDigit(c))\n TomlError.x('invalid value', ctx, startPtr);\n while (++ctx.p < ctx.s.length && (c = ctx.s.charCodeAt(ctx.p), !isEndOfValue(c, endChr))) {\n if (!state)\n state = 1; // Detects single-digit numbers we can use a fast parse path for\n // The way the states are numbered is not random: underscores are always permitted in odd-numbered states and\n // never permitted in even-numbered ones.\n if (c === 0x5f /* _ */) {\n if (!(state & 1))\n TomlError.x('illegal underscore', ctx);\n state += 11; // 11 is a marker that makes the state even and greater than 9; see the numbering + rationale above\n hasUnderscores = true;\n }\n // Transition to fractional part.\n else if (state === 1 && c === 0x2e /* . */)\n state = 2;\n // Transition to exponent part.\n else if ((state === 1 || state === 3) && (c === 0x65 /* e */ || c === 0x45 /* E */))\n state = 4;\n // + and - are permitted in state 4 only (handled before entering the function for state 0)\n else if (state === 4 && (c === 0x2b /* + */ || c === 0x2d /* - */)) { /* no-op */ }\n // All special cases have been handled; only digits are allowed here\n else if (!isDigit(c))\n TomlError.x(`illegal character in numeric literal`, ctx);\n // Clear state flags\n else if (state > 9)\n state -= 11;\n else if (!(state & 1))\n state++;\n }\n // Single-char number; we can fast-path these very easily.\n if (!state) {\n let val = (startChr - 0x30 /* 0 */) * (sign || 1);\n return ctx.bi === true ? BigInt(val) : val;\n }\n // Even-numbered states absolutely require a digit next; not even end of value is permitted\n if (!(state & 1))\n TomlError.x('unfinished numeric value', ctx, startPtr);\n let str = ctx.s.slice(startPtr, ctx.p);\n if (hasUnderscores)\n str = str.replaceAll('_', ''); // perf: replaceAll 1.25x faster than replace with a regex\n return state > 1\n ? parseFloat(str)\n : parseInteger(ctx, str, 10, startPtr);\n}\nfunction parseIntegerBaseN(ctx, startPtr, base, endChr) {\n let c, underscore = 1;\n while (++ctx.p < ctx.s.length && (c = ctx.s.charCodeAt(ctx.p), !isEndOfValue(c, endChr))) {\n if (c === 0x5f /* _ */) {\n if (underscore & 1)\n TomlError.x('illegal underscore', ctx);\n underscore = 3;\n }\n // We only need to check if the number is a valid digit, nothing else is permitted\n else if (!isDigit(c, base))\n TomlError.x(`illegal character in numeric literal`, ctx);\n // Clear underscore flag\n else if (underscore & 1)\n underscore--;\n }\n // Trailing underscore is not allowed\n if (underscore & 1)\n TomlError.x('unfinished numeric value', ctx);\n let str = ctx.s.slice(startPtr + 2, ctx.p);\n if (underscore)\n str = str.replaceAll('_', ''); // perf: replaceAll 1.25x faster than replace with a regex\n return parseInteger(ctx, str, base, startPtr);\n}\nfunction parseInteger(ctx, str, base, startPtr) {\n if (ctx.bi !== true)\n int: {\n let val = parseInt(str, base);\n if (!Number.isSafeInteger(val)) {\n if (ctx.bi)\n break int;\n TomlError.x('integer value cannot be represented losslessly', ctx, startPtr);\n }\n return val;\n }\n return base === 10 ? BigInt(str) : BigInt((base === 2 ? '0b' : base === 8 ? '0o' : '0x') + str);\n}\nfunction parseDate(ctx, c, endChr) {\n let startPtr = ctx.p++, unsafeSeparator;\n if (!isDigit(c) ||\n !isDigit(ctx.s.charCodeAt(ctx.p++)) ||\n !isDigit(ctx.s.charCodeAt(ctx.p++)) ||\n !isDigit(ctx.s.charCodeAt(ctx.p++))) {\n return parseNumber(ctx, ctx.p = startPtr, c, 0, endChr);\n }\n ctx.p += 5;\n if (!isDigit(ctx.s.charCodeAt(ctx.p++)))\n TomlError.x('invalid date-time: date part is malformed', ctx, startPtr);\n if (ctx.p >= ctx.s.length || (((c = ctx.s.charCodeAt(ctx.p)) !== 0x20 || (unsafeSeparator = true, !isDigit(ctx.s.charCodeAt(ctx.p + 1)))) && c !== 0x54 /* T */ && c !== 0x74 /* t */)) {\n let t = ctx.s.slice(startPtr, ctx.p);\n return readDate(ctx, t, 3, false, startPtr);\n }\n if (ctx.s.charCodeAt(ctx.p += 3) !== 0x3a /* : */)\n TomlError.x('invalid date-time: time part is malformed', ctx, startPtr);\n if (ctx.s.charCodeAt(ctx.p += 3) === 0x3a /* : */)\n ctx.p += 3;\n if (ctx.s.charCodeAt(ctx.p) === 0x2e /* . */)\n while (isDigit(ctx.s.charCodeAt(++ctx.p)))\n ;\n if (c = ctx.s.charCodeAt(ctx.p)) {\n if (c === 0x5a /* Z */ || c === 0x7a /* z */) {\n let t = ctx.s.slice(startPtr, ++ctx.p);\n return readDate(ctx, t, 1, unsafeSeparator, startPtr, '[+00:00]');\n }\n if (c === 0x2b /* + */ || c === 0x2d /* - */) {\n // Temporal's ZonedDateTime is weird asf when it comes to dealing with traditional offsets...\n // It's 1.2x faster to allocate a new string to pass to ZDT than use Instant.toZonedDateTimeISO\n let t = ctx.s.slice(startPtr, ctx.p += 6);\n return readDate(ctx, t, 1, unsafeSeparator, startPtr, !ctx.ld && ('[' + ctx.s.slice(ctx.p - 6, ctx.p) + ']'));\n }\n }\n let t = ctx.s.slice(startPtr, ctx.p);\n return readDate(ctx, t, 2, unsafeSeparator, startPtr);\n}\nfunction parseTime(ctx, c, endChr) {\n let start = ctx.p;\n if (!isDigit(c) || !isDigit(ctx.s.charCodeAt(++ctx.p))) {\n return parseNumber(ctx, --ctx.p, c, 0, endChr);\n }\n if (ctx.s.charCodeAt(ctx.p += 4) === 0x3a /* : */)\n ctx.p += 3;\n if (ctx.s.charCodeAt(ctx.p) === 0x2e /* . */)\n while (isDigit(ctx.s.charCodeAt(++ctx.p)))\n ;\n let t = ctx.s.slice(start, ctx.p);\n return readDate(ctx, t, 4, false, start);\n}\nfunction readDate(ctx, str, type, unsafeDelim, errPtr, temporalSuffix) {\n if (ctx.ld) {\n let date = new TomlDate(str, type, unsafeDelim);\n if (!date.isValid())\n TomlError.x('invalid date', ctx, errPtr);\n return date;\n }\n try {\n if (temporalSuffix)\n str += temporalSuffix;\n switch (type) {\n case 1: return Temporal.ZonedDateTime.from(str);\n case 2: return Temporal.PlainDateTime.from(str);\n case 3: return Temporal.PlainDate.from(str);\n case 4: return Temporal.PlainTime.from(str);\n }\n }\n catch (e) {\n TomlError.x(e instanceof Error ? e.message : '' + e, ctx, errPtr);\n }\n}\n","/*!\n * Copyright (c) Squirrel Chat et al., All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the\n * documentation and/or other materials provided with the distribution.\n * 3. Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\nlet DATE_TIME_RE = /^(\\d{4}-\\d{2}-\\d{2})?[Tt ]?(?:(\\d{2}):\\d{2}(?::\\d{2}(?:\\.\\d+)?)?)?(Z|z|[-+]\\d{2}:\\d{2})?$/i;\nexport class TomlDate extends Date {\n #hasDate = false;\n #hasTime = false;\n #offset = null;\n constructor(date, fasttype, unsafeDelim) {\n let hasDate = true;\n let hasTime = true;\n let offset = 'Z';\n let c;\n if (typeof date === 'string') {\n if (fasttype)\n prep: {\n // Date-time\n if (fasttype < 3) {\n if (+date.slice(11, 13) > 23) {\n date = '';\n break prep;\n }\n // Local\n if (fasttype === 2) {\n offset = null;\n date += 'Z';\n }\n // Offset; keep track of offset if not Z\n else if ((c = date.charCodeAt(date.length - 1)) !== 0x5a /* Z */ && c !== 0x7a /* z */) {\n offset = date.slice(date.length - 6);\n }\n if (unsafeDelim)\n date = date.slice(0, 10) + 'T' + date.slice(11);\n }\n // Time\n else if (fasttype === 4) {\n date = +date.slice(0, 2) > 23 ? '' : `0000-01-01T${date}Z`;\n }\n hasDate = fasttype !== 4;\n hasTime = fasttype !== 3;\n }\n else {\n let match = date.match(DATE_TIME_RE);\n if (match) {\n if (!match[1]) {\n hasDate = false;\n date = `0000-01-01T${date}`;\n }\n hasTime = !!match[2];\n // Make sure to use T instead of a space. Breaks in case of extreme values otherwise.\n hasTime && date[10] === ' ' && (date = date.replace(' ', 'T'));\n // Do not allow rollover hours.\n if (match[2] && +match[2] > 23) {\n date = '';\n }\n else {\n offset = match[3] || null;\n if (!offset && hasTime)\n date += 'Z';\n }\n }\n else {\n date = '';\n }\n }\n }\n super(date);\n if (!isNaN(this.getTime())) {\n this.#hasDate = hasDate;\n this.#hasTime = hasTime;\n this.#offset = offset;\n }\n }\n isDateTime() {\n return this.#hasDate && this.#hasTime;\n }\n isLocal() {\n return !this.#hasDate || !this.#hasTime || !this.#offset;\n }\n isDate() {\n return this.#hasDate && !this.#hasTime;\n }\n isTime() {\n return this.#hasTime && !this.#hasDate;\n }\n isValid() {\n return this.#hasDate || this.#hasTime;\n }\n toISOString() {\n let iso = super.toISOString();\n // Local Date\n if (this.isDate())\n return iso.slice(0, 10);\n // Local Time\n if (this.isTime())\n return iso.slice(11, 23);\n // Local DateTime\n if (this.#offset === null)\n return iso.slice(0, -1);\n // Offset DateTime\n if (this.#offset === 'Z' || this.#offset === 'z')\n return iso;\n // This part is quite annoying: JS strips the original timezone from the ISO string representation\n // Instead of using a \"modified\" date and \"Z\", we restore the representation \"as authored\"\n let offset = (+(this.#offset.slice(1, 3)) * 60) + +(this.#offset.slice(4, 6));\n offset = this.#offset[0] === '-' ? offset : -offset;\n let offsetDate = new Date(this.getTime() - (offset * 60e3));\n return offsetDate.toISOString().slice(0, -1) + this.#offset;\n }\n static wrapAsOffsetDateTime(jsDate, offset = 'Z') {\n let date = new TomlDate(jsDate);\n date.#offset = offset;\n return date;\n }\n static wrapAsLocalDateTime(jsDate) {\n let date = new TomlDate(jsDate);\n date.#offset = null;\n return date;\n }\n static wrapAsLocalDate(jsDate) {\n let date = new TomlDate(jsDate);\n date.#hasTime = false;\n date.#offset = null;\n return date;\n }\n static wrapAsLocalTime(jsDate) {\n let date = new TomlDate(jsDate);\n date.#hasDate = false;\n date.#offset = null;\n return date;\n }\n}\n","/*!\n * Copyright (c) Squirrel Chat et al., All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the\n * documentation and/or other materials provided with the distribution.\n * 3. Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\nimport { TomlError } from './error.js';\n/** @internal */\nexport function skipComment(ctx) {\n for (; ctx.p < ctx.s.length; ctx.p++) {\n let c = ctx.s.charCodeAt(ctx.p);\n if (c === 0xa /* \\n */)\n break;\n if (c === 0xd /* \\r */ && ctx.s.charCodeAt(ctx.p + 1) === 0xa /* \\n */) {\n ctx.p++;\n break;\n }\n if ((c < 0x20 && c !== 0x9 /* \\t */) || c === 0x7f) {\n TomlError.x('control characters are not allowed in comments', ctx);\n }\n }\n}\n/** @internal */\nexport function skipVoid(ctx, banNewLines, banComments) {\n let c;\n while (ctx.p < ctx.s.length) {\n while (ctx.p < ctx.s.length && ((c = ctx.s.charCodeAt(ctx.p)) === 0x20 ||\n c === 0x9 /* \\t */ ||\n (!banNewLines &&\n (c === 0xa /* \\n */ || (c === 0xd /* \\r */ && ctx.s.charCodeAt(ctx.p + 1) === 0xa /* \\n */)))))\n ctx.p++;\n if (banComments || c !== 0x23 /* # */)\n break;\n skipComment(ctx);\n }\n}\n","/*!\n * Copyright (c) Squirrel Chat et al., All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the\n * documentation and/or other materials provided with the distribution.\n * 3. Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\nlet BARE_KEY = /^[a-z0-9-_]+$/i;\nlet HAS_WELLFORMED = !!(''.isWellFormed);\nfunction extendedTypeOf(obj) {\n let type = typeof obj;\n if (type === 'object') {\n if (Array.isArray(obj))\n return 'array';\n if (typeof obj.getUTCDate === 'function' && obj instanceof Date)\n return 'date';\n if (globalThis.Temporal) {\n if (obj.until) {\n if (obj instanceof Temporal.ZonedDateTime)\n return 'temporal/tz+uc';\n if (obj instanceof Temporal.PlainDateTime || obj instanceof Temporal.PlainDate)\n return 'temporal/uc';\n if (obj instanceof Temporal.PlainTime || obj instanceof Temporal.Instant)\n return 'temporal';\n if (obj instanceof Temporal.PlainYearMonth)\n return 'temporal/x';\n }\n else if ((obj.toPlainDate && obj instanceof Temporal.PlainMonthDay) ||\n (obj.negated && obj instanceof Temporal.Duration)) {\n return 'temporal/x';\n }\n }\n }\n return type;\n}\nfunction isArrayOfTables(obj) {\n for (let i = 0; i < obj.length; i++) {\n if (extendedTypeOf(obj[i]) !== 'object')\n return false;\n }\n return obj.length != 0;\n}\nfunction formatWellFormedStringUnchecked(s) {\n return JSON.stringify(s).replaceAll('\\x7f', '\\\\u007f');\n}\nfunction formatString(s) {\n return formatWellFormedStringUnchecked(HAS_WELLFORMED ? s.toWellFormed() : s);\n}\nfunction formatKey(s) {\n if (BARE_KEY.test(s))\n return s;\n if (HAS_WELLFORMED && !s.isWellFormed())\n throw new RangeError('key contains illegal lone surrogates');\n return formatWellFormedStringUnchecked(s);\n}\nfunction stringifyValue(val, type, depth, numberAsFloat, strictTemporal) {\n if (depth === 0) {\n throw new Error('Could not stringify the object: maximum object depth exceeded');\n }\n switch (type) {\n // @ts-expect-error -- intentional fallthrough case\n case 'number':\n if (isNaN(val))\n return 'nan';\n if (val === Infinity)\n return 'inf';\n if (val === -Infinity)\n return '-inf';\n if (Number.isInteger(val) && (numberAsFloat || !Number.isSafeInteger(val)))\n return val.toFixed(1);\n case 'bigint':\n case 'boolean':\n case 'temporal':\n return val.toString();\n case 'string':\n return formatString(val);\n case 'date':\n if (isNaN(val.getTime()))\n throw new TypeError('cannot serialize invalid date');\n return val.toISOString();\n case 'object':\n return stringifyInlineTable(val, depth, numberAsFloat, strictTemporal);\n case 'array':\n return stringifyArray(val, depth, numberAsFloat, strictTemporal);\n // @ts-expect-error -- intentional fallthrough case\n case 'temporal/tz+uc':\n if (strictTemporal) {\n let tz = val.timeZoneId;\n let tzc = tz.charCodeAt(0);\n if (\n // Classic offset\n tzc !== 0x2b /* + */ && tzc !== 0x2d /* - */ &&\n (\n // Fast pre-check pass; see below for the actually accepted values\n (tzc !== 0x55 /* U */ && tzc !== 0x47 /* G */ && tzc !== 0x5a /* Z */ && tzc !== 0x45 /* E */) ||\n (\n // UTC and its aliases; Temporal implementations don't all canonicalise unfortunately\n tz !== 'UTC' && tz !== 'UCT' && tz !== 'Universal' && tz !== 'Zulu' &&\n // GMT is a TZ but it's for all intents and purposes equivalent to UTC. Safe to downgrade.\n !tz.startsWith('GMT') && tz !== 'Greenwich' &&\n // Etc/* are all safe to downgrade to offset (either UTC, GMT, or offset)\n !tz.startsWith('Etc/')))) {\n throw new TypeError('Temporal objects with an IANA timezone are not allowed in Temporal strict mode');\n }\n }\n case 'temporal/uc':\n if (strictTemporal && val.calendarId !== 'iso8601')\n throw new TypeError('Temporal objects with a non-default calendar are not allowed in Temporal strict mode');\n return val.toString({\n calendarName: 'never',\n timeZoneName: 'never',\n });\n case 'temporal/x':\n throw new TypeError('Unsupported ' + val[Symbol.toStringTag]);\n }\n}\nfunction stringifyInlineTable(obj, depth, numberAsFloat, strictTemporal) {\n let keys = Object.keys(obj);\n if (keys.length === 0)\n return '{}';\n let res = '{ ';\n for (let i = 0; i < keys.length; i++) {\n let k = keys[i];\n if (i)\n res += ', ';\n res += formatKey(k) + ' = ' + stringifyValue(obj[k], extendedTypeOf(obj[k]), depth - 1, numberAsFloat, strictTemporal);\n }\n return res + ' }';\n}\nfunction stringifyArray(array, depth, numberAsFloat, strictTemporal) {\n if (array.length === 0)\n return '[]';\n let res = '[ ';\n for (let i = 0; i < array.length; i++) {\n if (i)\n res += ', ';\n if (array[i] === null || array[i] === void 0) {\n throw new TypeError('arrays cannot contain null or undefined values');\n }\n res += stringifyValue(array[i], extendedTypeOf(array[i]), depth - 1, numberAsFloat, strictTemporal);\n }\n return res + ' ]';\n}\nfunction stringifyArrayTable(array, key, depth, numberAsFloat, strictTemporal) {\n if (depth === 0) {\n throw new Error('Could not stringify the object: maximum object depth exceeded');\n }\n let res = '';\n for (let i = 0; i < array.length; i++) {\n res += `${res && '\\n'}[[${key}]]\\n`;\n res += stringifyTable(0, array[i], key, depth, numberAsFloat, strictTemporal);\n }\n return res;\n}\nfunction stringifyTable(tableKey, obj, prefix, depth, numberAsFloat, strictTemporal) {\n if (depth === 0) {\n throw new Error('Could not stringify the object: maximum object depth exceeded');\n }\n let preamble = '';\n let tables = '';\n let keys = Object.keys(obj);\n for (let i = 0; i < keys.length; i++) {\n let k = keys[i];\n if (obj[k] !== null && obj[k] !== void 0) {\n let type = extendedTypeOf(obj[k]);\n if (type === 'symbol' || type === 'function') {\n throw new TypeError(`cannot serialize values of type '${type}'`);\n }\n let key = formatKey(k);\n if (type === 'array' && isArrayOfTables(obj[k])) {\n tables += (tables && '\\n') + stringifyArrayTable(obj[k], prefix ? `${prefix}.${key}` : key, depth - 1, numberAsFloat, strictTemporal);\n }\n else if (type === 'object') {\n let tblKey = prefix ? `${prefix}.${key}` : key;\n tables += (tables && '\\n') + stringifyTable(tblKey, obj[k], tblKey, depth - 1, numberAsFloat, strictTemporal);\n }\n else {\n preamble += key;\n preamble += ' = ';\n preamble += stringifyValue(obj[k], type, depth, numberAsFloat, strictTemporal);\n preamble += '\\n';\n }\n }\n }\n if (tableKey && (preamble || !tables)) // Create table only if necessary\n preamble = preamble ? `[${tableKey}]\\n${preamble}` : `[${tableKey}]`;\n return preamble && tables\n ? `${preamble}\\n${tables}`\n : preamble || tables;\n}\nexport function stringify(obj, { maxDepth = 1000, numbersAsFloat = false, strictTemporal = false } = {}) {\n if (extendedTypeOf(obj) !== 'object') {\n throw new TypeError('stringify can only be called with an object');\n }\n let str = stringifyTable(0, obj, '', maxDepth, numbersAsFloat, strictTemporal);\n if (str[str.length - 1] !== '\\n')\n return str + '\\n';\n return str;\n}\n","/**\n * #722 (ADR-106) — 하네스가 **전에** `.mcp.json` 에 쓴 서버 값. 옛 판이 쓴 값을 알아보는 한 자리다(ADR-100 의\n * `RETIRED_GITIGNORE_LINES` 와 같은 자리 · 같은 뜻):\n *\n * ① 옛 로그(몫 기록 전 — v26.161 이하)의 내용 식별(`legacyMcpSeed` · `legacyMcpServers`) — 하네스가 만든 파일\n * (`rootFiles.change === \"created\"` 기록)에서 이 값 그대로 남은 서버만 하네스 몫으로 본다.\n * ② uninstall 의 잔존 안내(`remnantFor`) — 알리기만 한다.\n * ③ 화면 — 은퇴(`retired`)·교체(`updated`) 한 줄의 까닭.\n *\n * **지우는 근거가 되는 것은 은퇴한 이름뿐이다**(`retired` 가 있는 항목 — 하네스가 더는 어떤 트랙에도 렌더하지 않는다).\n * 트랙 표에 살아 있는 이름은 옛 로그에서 알아봐도 **선택 근거로만** 넘긴다 — 같은 디스크가 이전 판과 무관하게 기록 있는\n * 설치본과 같은 결과(남기고 새 값으로 갈아 끼움)를 받는다(설계 검증 B1 ⓐ).\n */\n\nimport type { McpServerConfig } from \"./mcp-merge.js\";\n\nexport interface PriorMcpServer {\n name: string;\n /** 그때 하네스가 쓴 값(내부 모델 — stdio 는 Claude `.mcp.json` 형식과 같다). */\n value: McpServerConfig;\n /** 은퇴한 이름이면 화면이 덧붙이는 까닭과 대안. 없으면 살아 있는 이름(트랙 표 행)이다. */\n retired?: string;\n /** 살아 있는 이름을 새 값으로 갈아 끼웠을 때 화면이 덧붙이는 한 마디. */\n updated?: string;\n /**\n * #777 — 이 값은 처음부터 뜬 적이 없다(npm 에 없는 패키지). update 뒤에도 `.mcp.json` 에 그대로 남아 있으면(기록이 하네스 몫이라\n * 말하지 않아 갈아 끼우지 않은 설치본) 화면이 이 한 마디로 알린다.\n */\n dead?: string;\n}\n\nexport const PRIOR_MCP_SERVERS: ReadonlyArray<PriorMcpServer> = [\n {\n name: \"github\",\n value: { type: \"stdio\", command: \"npx\", args: [\"-y\", \"@modelcontextprotocol/server-github\"] },\n retired:\n \"the old @modelcontextprotocol/server-github is deprecated upstream and ran without a token. The agent uses the gh CLI for GitHub; to use GitHub's official MCP instead: agent-harness install … --with github-mcp (needs GITHUB_PAT)\",\n },\n {\n name: \"railway-mcp-server\",\n value: { type: \"stdio\", command: \"npx\", args: [\"-y\", \"@railway/mcp-server\"] },\n updated: \"now Railway's remote server — sign in on first use, see NEXT\",\n },\n {\n // #766 — `@supabase/mcp-server` 는 npm 에 없는 이름이라(404) 서버가 뜬 적이 없다\n name: \"supabase\",\n value: { type: \"stdio\", command: \"npx\", args: [\"-y\", \"@supabase/mcp-server\"] },\n updated: \"now Supabase's remote server — sign in on first use, see NEXT\",\n dead: 'runs @supabase/mcp-server, which is not on npm — this server never started. To use Supabase\\'s official remote server, set it to { \"type\": \"http\", \"url\": \"https://mcp.supabase.com/mcp\" } (signs in on first use)',\n },\n {\n name: \"chrome-devtools\",\n value: { type: \"stdio\", command: \"npx\", args: [\"-y\", \"chrome-devtools-mcp@latest\"] },\n updated: \"usage statistics off\",\n },\n];\n\n/** 이 이름으로 하네스가 전에 쓴 값들. */\nexport function priorMcpValues(name: string): McpServerConfig[] {\n return PRIOR_MCP_SERVERS.filter((p) => p.name === name).map((p) => p.value);\n}\n\n/** 은퇴한 이름 — 옛 로그에서 값이 그대로면 지우는 근거가 된다. */\nexport function retiredMcpNames(): string[] {\n return PRIOR_MCP_SERVERS.filter((p) => p.retired !== undefined).map((p) => p.name);\n}\n","/**\n * Category-based installer (v26.43.0) — 분야별 대표 스킬 큐레이션.\n *\n * 카테고리 목록은 아래 `CATEGORIES` 가 SSOT (여기 열거하지 않는다 — 사본은 썩는다).\n * 각 카테고리 안에서 자산은 (출처, ExternalAsset.id) 로 식별. Step 2 UI 그룹화 기준.\n *\n * Source labels = 정확한 GitHub org/user (Phase A의 SSOT).\n * `[3rd-party]` 같은 generic 라벨 금지 (SPEC R6).\n */\n\nexport const CATEGORIES = [\n \"frontend\",\n \"backend\",\n \"data\",\n \"business\",\n \"dev-tools\",\n \"understanding\",\n \"visual-media\",\n \"workflow\",\n] as const;\nexport type Category = (typeof CATEGORIES)[number];\n\nexport const CATEGORY_TITLES: Record<Category, string> = {\n frontend: \"🎨 Frontend (UI · Design)\",\n backend: \"🗄️ Backend (API · DB · Deploy)\",\n data: \"📊 Data\",\n business: \"💼 Business (Documents)\",\n \"dev-tools\": \"🛡️ Dev Tools (Security · Quality)\",\n // v26.78.0 — 에이전트 인지 증강: 환경에 대한 지각(영상·웹·코드) + 기억(memory).\n understanding: \"🧠 Understanding (Perception · Memory)\",\n // v26.85.0 — 코드-퍼스트 비주얼/미디어 제작: 슬라이드·다이어그램·모션·동영상.\n \"visual-media\": \"🎬 Visual & Media (Slides · Diagrams · Motion · Video)\",\n workflow: \"🔄 Workflow (Development Cycle)\",\n};\n\n/** Source labels — GitHub org/user. Maps the canonical owner shown to the user. */\nexport const SOURCE_LABELS = {\n anthropics: \"anthropics\",\n \"vercel-labs\": \"vercel-labs\",\n \"shadcn-ui\": \"shadcn/ui\",\n vercel: \"vercel\",\n netlify: \"netlify\",\n supabase: \"supabase\",\n railwayapp: \"railwayapp\",\n trailofbits: \"trailofbits\",\n obra: \"obra\",\n addyosmani: \"addyosmani\",\n pbakaus: \"pbakaus\",\n \"K-Dense-AI\": \"K-Dense-AI\",\n wshobson: \"wshobson\",\n \"testdino-hq\": \"testdino-hq\",\n yonatangross: \"yonatangross\",\n alirezarezvani: \"alirezarezvani\",\n \"affaan-m\": \"affaan-m\",\n \"get-shit-done-cc\": \"get-shit-done-cc\",\n \"fission-ai\": \"Fission-AI\",\n \"bmad-code-org\": \"bmad-code-org\",\n // v26.78.0 — Understanding 카테고리 신규 출처\n bradautomates: \"bradautomates\",\n Lum1104: \"Lum1104\",\n rohitg00: \"rohitg00\",\n // v26.85.0 — Visual & Media 카테고리 출처 (Docker 실설치 검증 PASS).\n zarazhangrui: \"zarazhangrui\",\n greensock: \"greensock\",\n softaworks: \"softaworks\",\n \"remotion-dev\": \"remotion-dev\",\n // v26.86.0 — Visual & Media 프레젠테이션 4종 (Issue #176, Docker 실설치 4/4 PASS).\n hugohe3: \"hugohe3\",\n bytedance: \"bytedance\",\n ConardLi: \"ConardLi\",\n ryanbbrown: \"ryanbbrown\",\n // v26.91.0 — Business 카테고리 마케팅 번들 (coreyhaines31/marketingskills 35k★, MIT).\n coreyhaines31: \"coreyhaines31\",\n // 2026-08-02 정비 — Frontend 카테고리 신규 출처 (star 실측 2026-08-02, gh api).\n jakubkrehel: \"jakubkrehel\",\n Leonxlnx: \"Leonxlnx\",\n oso95: \"oso95\",\n // 2026-08-16 — Preline UI (6,386★ 실측, MIT + Preline UI Fair Use 듀얼 라이선스로 상업 이용 가능).\n htmlstreamofficial: \"Preline\",\n // 2026-08-17 — GitHub 공식 조직의 awesome-copilot (37,909★ 실측, MIT).\n github: \"GitHub\",\n // 2026-08-17 — Claude-Code-Game-Studios (23,924★ 실측, MIT).\n Donchitos: \"Donchitos\",\n // #722 — Chrome DevTools MCP (ChromeDevTools/chrome-devtools-mcp 53k★ 실측 2026-10-11).\n ChromeDevTools: \"ChromeDevTools\",\n // #751 — Context7 MCP (upstash/context7 62.9k★ 실측 2026-10-12).\n upstash: \"upstash\",\n // #726 — HyperFrames (Anthropic 공식 마켓플레이스 등재, 내용 저자 HeyGen).\n \"heygen-com\": \"heygen-com\",\n // #822 — ast-grep 공식 조직(본체 ast-grep/ast-grep 16k★ MIT · 스킬 리포 893★, 실측 2026-10-14).\n \"ast-grep\": \"ast-grep\",\n uzys: \"this project\",\n} as const;\nexport type Source = keyof typeof SOURCE_LABELS;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AACA,OAAO,UAAU;AACjB,SAAS,qBAAqB;AAF9B;AAAA;AAAA;AAAA;AAAA;;;ACAA;AACO,IAAM,SAAS;AAAA;AAAA,EAEpB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAGO,SAAS,QAAQ,OAAgC;AACtD,SAAO,OAAO,UAAU,YAAa,OAA6B,SAAS,KAAK;AAClF;AASO,IAAM,YAAY,CAAC,UAAU,SAAS,YAAY,aAAa;AAG/D,SAAS,UAAU,OAAkC;AAC1D,SAAO,OAAO,UAAU,YAAa,UAAgC,SAAS,KAAK;AACrF;AAeO,IAAM,iBAAiB,CAAC,WAAW,QAAQ;AAG3C,SAAS,eAAe,OAAuC;AACpE,SAAO,OAAO,UAAU,YAAa,eAAqC,SAAS,KAAK;AAC1F;AAMO,SAAS,aAAa,OAA+C;AAC1E,SAAO,SAAS;AAClB;AAmBO,IAAM,kBAA+B;AAAA,EAC1C,gBAAgB;AAClB;AAMO,IAAM,gBAAgB;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;;;AClGA;;;ACAA;AAGO,SAAS,WAAW,OAAc,SAA0B;AACjE,SAAO,QAAQ,MAAM,GAAG,EAAE,KAAK,CAAC,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,KAAK,KAAK,CAAC;AACzE;AAEO,SAAS,SAAS,QAA8B,SAA0B;AAC/E,SAAO,OAAO,KAAK,CAAC,MAAM,WAAW,GAAG,OAAO,CAAC;AAClD;AAEO,SAAS,YAAY,QAAuC;AAKjE,SAAO,SAAS,QAAQ,oCAAoC;AAC9D;AAEO,SAAS,WAAW,QAAuC;AAChE,SAAO,SAAS,QAAQ,kBAAkB;AAC5C;AAGA,SAAS,YAAY,SAAyB;AAC5C,QAAM,UAAU,QAAQ,QAAQ,sBAAsB,MAAM,EAAE,QAAQ,OAAO,IAAI;AACjF,SAAO,IAAI,OAAO,IAAI,OAAO,GAAG;AAClC;;;ADiHA,IAAM,YAAqB,OAAO,OAAO,CAAC,MAAM,WAAW,CAAC,CAAC,CAAC,CAAC;AAE/D,IAAM,sBAA+B;AAAA,EACnC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAiDO,IAAM,kBAAgD;AAAA;AAAA,EAE3D;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA;AAAA,IACN,aACE;AAAA,IACF,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,WAAW,EAAE,MAAM,aAAa,QAAQ,CAAC,QAAQ,MAAM,EAAE;AAAA,IACzD,QAAQ;AAAA,MACN,MAAM;AAAA,MACN,aAAa;AAAA,MACb,UAAU;AAAA,IACZ;AAAA,EACF;AAAA;AAAA,EAGA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA;AAAA,IACN,aAAa;AAAA,IACb,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,WAAW,EAAE,MAAM,SAAS;AAAA,IAC5B,QAAQ,EAAE,MAAM,YAAY,KAAK,gBAAgB;AAAA,EACnD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA;AAAA,IACN,aACE;AAAA,IACF,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,WAAW,EAAE,MAAM,gBAAgB;AAAA,IACnC,QAAQ,EAAE,MAAM,YAAY,KAAK,cAAc;AAAA,EACjD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA;AAAA,IACN,aACE;AAAA,IACF,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,WAAW;AAAA,MACT,MAAM;AAAA,MACN,QAAQ,CAAC,gBAAgB,eAAe,eAAe,cAAc,MAAM;AAAA,IAC7E;AAAA,IACA,QAAQ,EAAE,MAAM,YAAY,KAAK,mBAAmB;AAAA,EACtD;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA;AAAA,IACN,aACE;AAAA,IACF,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,WAAW,EAAE,MAAM,gBAAgB;AAAA,IACnC,QAAQ,EAAE,MAAM,YAAY,KAAK,qBAAqB;AAAA,EACxD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA;AAAA;AAAA;AAAA,IAGE,IAAI;AAAA,IACJ,MAAM;AAAA;AAAA,IACN,aACE;AAAA,IACF,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,WAAW,EAAE,MAAM,gBAAgB;AAAA,IACnC,QAAQ,EAAE,MAAM,YAAY,KAAK,4BAA4B;AAAA,EAC/D;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA;AAAA,IACN,aACE;AAAA,IACF,UAAU;AAAA,IACV,QAAQ;AAAA;AAAA;AAAA,IAGR,WAAW,EAAE,MAAM,aAAa,QAAQ,CAAC,GAAG,MAAM,EAAE;AAAA,IACpD,QAAQ,EAAE,MAAM,YAAY,KAAK,aAAa;AAAA,EAChD;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA;AAAA,IACN,aACE;AAAA,IACF,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,WAAW,EAAE,MAAM,gBAAgB;AAAA,IACnC,QAAQ,EAAE,MAAM,YAAY,KAAK,qBAAqB;AAAA,EACxD;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA;AAAA,IACN,aACE;AAAA,IACF,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,WAAW,EAAE,MAAM,gBAAgB;AAAA,IACnC,QAAQ,EAAE,MAAM,YAAY,KAAK,uBAAuB;AAAA,EAC1D;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA;AAAA,IACN,aACE;AAAA,IACF,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,WAAW,EAAE,MAAM,gBAAgB;AAAA,IACnC,QAAQ,EAAE,MAAM,YAAY,KAAK,wBAAwB;AAAA,EAC3D;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA;AAAA,IACN,aACE;AAAA,IACF,UAAU;AAAA,IACV,QAAQ;AAAA;AAAA,IAER,WAAW,EAAE,MAAM,aAAa,QAAQ,CAAC,GAAG,MAAM,EAAE;AAAA,IACpD,QAAQ,EAAE,MAAM,YAAY,KAAK,oBAAoB;AAAA,EACvD;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA;AAAA,IACN,aACE;AAAA,IACF,UAAU;AAAA,IACV,QAAQ;AAAA;AAAA,IAER,WAAW,EAAE,MAAM,gBAAgB;AAAA,IACnC,QAAQ,EAAE,MAAM,YAAY,KAAK,sBAAsB;AAAA,EACzD;AAAA,EACA;AAAA;AAAA;AAAA,IAGE,IAAI;AAAA,IACJ,MAAM;AAAA;AAAA,IACN,aACE;AAAA,IACF,UAAU;AAAA,IACV,QAAQ;AAAA;AAAA;AAAA,IAGR,WAAW,EAAE,MAAM,gBAAgB;AAAA,IACnC,QAAQ,EAAE,MAAM,YAAY,KAAK,yBAAyB;AAAA,EAC5D;AAAA,EACA;AAAA;AAAA;AAAA;AAAA;AAAA,IAKE,IAAI;AAAA,IACJ,MAAM;AAAA;AAAA,IACN,aACE;AAAA,IACF,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,WAAW,EAAE,MAAM,gBAAgB;AAAA,IACnC,QAAQ,EAAE,MAAM,YAAY,KAAK,qBAAqB;AAAA,EACxD;AAAA;AAAA;AAAA;AAAA,EAIA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA;AAAA,IACN,aACE;AAAA,IACF,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,WAAW,EAAE,MAAM,SAAS;AAAA,IAC5B,QAAQ,EAAE,MAAM,YAAY,KAAK,kBAAkB;AAAA,EACrD;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA;AAAA,IACN,aACE;AAAA,IACF,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,WAAW,EAAE,MAAM,SAAS;AAAA,IAC5B,QAAQ,EAAE,MAAM,YAAY,KAAK,mBAAmB;AAAA,EACtD;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA;AAAA,IACN,aACE;AAAA,IACF,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,WAAW,EAAE,MAAM,SAAS;AAAA,IAC5B,QAAQ,EAAE,MAAM,YAAY,KAAK,oBAAoB;AAAA,EACvD;AAAA,EACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOE,IAAI;AAAA,IACJ,MAAM;AAAA;AAAA,IACN,aACE;AAAA,IACF,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,WAAW,EAAE,MAAM,aAAa,QAAQ,CAAC,GAAG,MAAM,EAAE;AAAA,IACpD,QAAQ,EAAE,MAAM,YAAY,KAAK,kBAAkB;AAAA,EACrD;AAAA,EACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAQE,IAAI;AAAA,IACJ,MAAM;AAAA;AAAA,IACN,aACE;AAAA,IACF,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,WAAW,EAAE,MAAM,aAAa,QAAQ,CAAC,GAAG,MAAM,EAAE;AAAA,IACpD,QAAQ,EAAE,MAAM,YAAY,KAAK,oBAAoB;AAAA,EACvD;AAAA,EAEA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOE,IAAI;AAAA,IACJ,MAAM;AAAA;AAAA,IACN,aACE;AAAA,IACF,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,WAAW,EAAE,MAAM,gBAAgB;AAAA,IACnC,QAAQ,EAAE,MAAM,YAAY,KAAK,4BAA4B;AAAA,EAC/D;AAAA,EAEA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IASE,IAAI;AAAA,IACJ,MAAM;AAAA;AAAA,IACN,aACE;AAAA,IACF,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,WAAW,EAAE,MAAM,gBAAgB;AAAA,IACnC,QAAQ,EAAE,MAAM,YAAY,KAAK,iBAAiB;AAAA,EACpD;AAAA;AAAA,EAGA;AAAA;AAAA,IAEE,IAAI;AAAA,IACJ,MAAM;AAAA;AAAA,IACN,aACE;AAAA,IACF,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,WAAW,EAAE,MAAM,SAAS;AAAA,IAC5B,QAAQ,EAAE,MAAM,OAAO,KAAK,wBAAwB,SAAS,QAAQ;AAAA,EACvE;AAAA,EACA;AAAA;AAAA;AAAA,IAGE,IAAI;AAAA,IACJ,MAAM;AAAA;AAAA,IACN,aACE;AAAA,IACF,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,WAAW,EAAE,MAAM,SAAS;AAAA;AAAA;AAAA;AAAA,IAI5B,oBAAoB,CAAC,QAAQ;AAAA,IAC7B,QAAQ;AAAA,MACN,MAAM;AAAA,MACN,KAAK;AAAA,MACL,SAAS;AAAA,MACT,MAAM,CAAC,WAAW,eAAe,KAAK,WAAW,eAAe,OAAO;AAAA,IACzE;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA;AAAA,IACN,aACE;AAAA,IACF,UAAU;AAAA,IACV,QAAQ;AAAA;AAAA,IAER,WAAW,EAAE,MAAM,SAAS;AAAA,IAC5B,QAAQ;AAAA,MACN,MAAM;AAAA,MACN,aAAa;AAAA,MACb,UAAU;AAAA,IACZ;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA;AAAA,IACN,aACE;AAAA,IACF,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,WAAW,EAAE,MAAM,SAAS;AAAA;AAAA;AAAA,IAG5B,oBAAoB,CAAC,UAAU,SAAS,UAAU;AAAA,IAClD,QAAQ,EAAE,MAAM,YAAY,KAAK,qBAAqB;AAAA,EACxD;AAAA;AAAA;AAAA,EAGA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA;AAAA,IACN,aACE;AAAA,IACF,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,WAAW,EAAE,MAAM,SAAS;AAAA,IAC5B,oBAAoB,CAAC,UAAU,SAAS,UAAU;AAAA,IAClD,QAAQ,EAAE,MAAM,YAAY,KAAK,SAAS;AAAA,EAC5C;AAAA;AAAA;AAAA;AAAA,EAIA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA;AAAA,IACN,aACE;AAAA,IACF,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,WAAW,EAAE,MAAM,SAAS;AAAA,IAC5B,oBAAoB,CAAC,UAAU,SAAS,UAAU;AAAA,IAClD,QAAQ,EAAE,MAAM,YAAY,KAAK,aAAa;AAAA,EAChD;AAAA;AAAA;AAAA,EAGA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA;AAAA,IACN,aACE;AAAA,IACF,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,WAAW,EAAE,MAAM,SAAS;AAAA,IAC5B,oBAAoB,CAAC,UAAU,SAAS,UAAU;AAAA,IAClD,QAAQ,EAAE,MAAM,YAAY,KAAK,kBAAkB;AAAA,EACrD;AAAA;AAAA;AAAA,EAGA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA;AAAA,IACN,aACE;AAAA,IACF,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,WAAW,EAAE,MAAM,SAAS;AAAA,IAC5B,oBAAoB,CAAC,UAAU,SAAS,UAAU;AAAA,IAClD,QAAQ,EAAE,MAAM,YAAY,KAAK,WAAW;AAAA,EAC9C;AAAA;AAAA;AAAA,EAGA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA;AAAA,IACN,aACE;AAAA,IACF,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,WAAW,EAAE,MAAM,SAAS;AAAA,IAC5B,oBAAoB,CAAC,UAAU,SAAS,UAAU;AAAA,IAClD,QAAQ,EAAE,MAAM,YAAY,KAAK,WAAW;AAAA,EAC9C;AAAA;AAAA,EAGA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA;AAAA,IACN,aACE;AAAA,IACF,UAAU;AAAA,IACV,QAAQ;AAAA;AAAA;AAAA;AAAA,IAIR,WAAW,EAAE,MAAM,SAAS;AAAA,IAC5B,QAAQ,EAAE,MAAM,OAAO,KAAK,UAAU,SAAS,SAAS;AAAA,EAC1D;AAAA,EACA;AAAA;AAAA;AAAA,IAGE,IAAI;AAAA,IACJ,MAAM;AAAA;AAAA,IACN,aACE;AAAA,IACF,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,WAAW,EAAE,MAAM,SAAS;AAAA,IAC5B,QAAQ,EAAE,MAAM,OAAO,KAAK,eAAe,SAAS,SAAS;AAAA,EAC/D;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA;AAAA,IACN,aACE;AAAA,IACF,UAAU;AAAA,IACV,QAAQ;AAAA;AAAA,IAER,WAAW,EAAE,MAAM,SAAS;AAAA,IAC5B,QAAQ,EAAE,MAAM,OAAO,KAAK,YAAY,SAAS,UAAU;AAAA,EAC7D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA;AAAA,IACN,aACE;AAAA,IACF,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,WAAW,EAAE,MAAM,aAAa,QAAQ,UAAU;AAAA,IAClD,QAAQ,EAAE,MAAM,SAAS,QAAQ,qBAAqB,OAAO,kBAAkB;AAAA,EACjF;AAAA;AAAA;AAAA;AAAA,EAIA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA;AAAA,IACN,aACE;AAAA,IACF,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,WAAW,EAAE,MAAM,SAAS;AAAA;AAAA;AAAA;AAAA,IAI5B,QAAQ,EAAE,MAAM,SAAS,QAAQ,qBAAqB;AAAA,EACxD;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA;AAAA,IACN,aACE;AAAA,IACF,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,WAAW,EAAE,MAAM,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAM5B,QAAQ,EAAE,MAAM,SAAS,QAAQ,wBAAwB,OAAO,wBAAwB;AAAA,EAC1F;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA;AAAA,IACN,aACE;AAAA,IACF,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,WAAW,EAAE,MAAM,SAAS;AAAA,IAC5B,QAAQ,EAAE,MAAM,SAAS,QAAQ,sBAAsB,OAAO,eAAe;AAAA,EAC/E;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA;AAAA,IACN,aACE;AAAA,IACF,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,WAAW,EAAE,MAAM,SAAS;AAAA,IAC5B,QAAQ;AAAA,MACN,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,OAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA;AAAA,IACN,aACE;AAAA,IACF,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,WAAW,EAAE,MAAM,SAAS;AAAA,IAC5B,QAAQ,EAAE,MAAM,SAAS,QAAQ,wBAAwB,OAAO,WAAW;AAAA,EAC7E;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA;AAAA,IACN,aACE;AAAA,IACF,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,WAAW,EAAE,MAAM,SAAS;AAAA,IAC5B,QAAQ;AAAA,MACN,MAAM;AAAA,MACN,aAAa;AAAA,MACb,UAAU;AAAA,IACZ;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAKA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA;AAAA,IACN,aACE;AAAA;AAAA,IAEF,UAAU;AAAA,IACV,QAAQ;AAAA;AAAA;AAAA;AAAA,IAIR,WAAW,EAAE,MAAM,SAAS;AAAA,IAC5B,QAAQ,EAAE,MAAM,OAAO,KAAK,iBAAiB,SAAS,SAAS;AAAA,EACjE;AAAA;AAAA,EAEA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA;AAAA,IACN,aACE;AAAA,IACF,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,WAAW,EAAE,MAAM,SAAS;AAAA,IAC5B,QAAQ;AAAA,MACN,MAAM;AAAA,MACN,aAAa;AAAA,MACb,UAAU;AAAA,IACZ;AAAA,EACF;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA;AAAA,IACN,aACE;AAAA,IACF,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,WAAW,EAAE,MAAM,SAAS;AAAA,IAC5B,QAAQ;AAAA,MACN,MAAM;AAAA,MACN,aAAa;AAAA,MACb,UAAU;AAAA,IACZ;AAAA,EACF;AAAA;AAAA;AAAA,EAIA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA;AAAA,IACN,aACE;AAAA,IACF,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,WAAW,EAAE,MAAM,SAAS;AAAA,IAC5B,QAAQ;AAAA,MACN,MAAM;AAAA,MACN,aAAa;AAAA,MACb,UAAU;AAAA,IACZ;AAAA,EACF;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA;AAAA,IACN,aACE;AAAA,IACF,UAAU;AAAA,IACV,QAAQ;AAAA;AAAA,IAER,WAAW,EAAE,MAAM,SAAS;AAAA,IAC5B,QAAQ,EAAE,MAAM,SAAS,QAAQ,4BAA4B,OAAO,aAAa;AAAA,EACnF;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA;AAAA,IACN,aACE;AAAA,IACF,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,WAAW,EAAE,MAAM,SAAS;AAAA,IAC5B,QAAQ;AAAA,MACN,MAAM;AAAA,MACN,aAAa;AAAA,MACb,UAAU;AAAA,IACZ;AAAA,EACF;AAAA,EACA;AAAA;AAAA;AAAA;AAAA,IAIE,IAAI;AAAA,IACJ,MAAM;AAAA;AAAA,IACN,aACE;AAAA,IACF,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,WAAW,EAAE,MAAM,SAAS;AAAA,IAC5B,QAAQ,EAAE,MAAM,SAAS,QAAQ,uBAAuB,OAAO,0BAA0B;AAAA,EAC3F;AAAA;AAAA,EAEA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA;AAAA,IACN,aACE;AAAA,IACF,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,WAAW,EAAE,MAAM,SAAS;AAAA,IAC5B,QAAQ,EAAE,MAAM,SAAS,QAAQ,sBAAsB,OAAO,aAAa;AAAA,EAC7E;AAAA,EACA;AAAA;AAAA;AAAA;AAAA;AAAA,IAKE,IAAI;AAAA,IACJ,MAAM;AAAA;AAAA,IACN,aACE;AAAA,IACF,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,WAAW,EAAE,MAAM,SAAS;AAAA,IAC5B,QAAQ;AAAA,MACN,MAAM;AAAA,MACN,aAAa;AAAA,MACb,UAAU;AAAA,IACZ;AAAA,EACF;AAAA;AAAA,EAGA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA;AAAA,IACN,aACE;AAAA,IACF,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,WAAW,EAAE,MAAM,aAAa,QAAQ,CAAC,gBAAgB,MAAM,EAAE;AAAA,IACjE,QAAQ;AAAA,MACN,MAAM;AAAA,MACN,aAAa;AAAA,MACb,UAAU;AAAA,IACZ;AAAA,EACF;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA;AAAA,IACN,aACE;AAAA,IACF,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,WAAW,EAAE,MAAM,aAAa,QAAQ,CAAC,gBAAgB,MAAM,EAAE;AAAA,IACjE,QAAQ;AAAA,MACN,MAAM;AAAA,MACN,aAAa;AAAA,MACb,UAAU;AAAA,IACZ;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA;AAAA,IACN,aACE;AAAA,IACF,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,WAAW,EAAE,MAAM,aAAa,QAAQ,oBAAoB;AAAA,IAC5D,QAAQ;AAAA,MACN,MAAM;AAAA,MACN,QAAQ;AAAA;AAAA;AAAA,MAGR,OAAO;AAAA,IACT;AAAA,EACF;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA;AAAA,IACN,aACE;AAAA,IACF,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,WAAW,EAAE,MAAM,aAAa,QAAQ,oBAAoB;AAAA;AAAA,IAE5D,QAAQ,EAAE,MAAM,SAAS,QAAQ,aAAa,OAAO,SAAS;AAAA,EAChE;AAAA,EACA;AAAA;AAAA;AAAA,IAGE,IAAI;AAAA,IACJ,MAAM;AAAA;AAAA,IACN,aACE;AAAA,IACF,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,WAAW,EAAE,MAAM,SAAS;AAAA,IAC5B,QAAQ;AAAA,MACN,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,OAAO;AAAA,IACT;AAAA,EACF;AAAA,EACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAME,IAAI;AAAA,IACJ,MAAM;AAAA;AAAA,IACN,aACE;AAAA,IACF,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,WAAW,EAAE,MAAM,SAAS;AAAA,IAC5B,QAAQ;AAAA,MACN,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,OAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA,EAEA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA;AAAA,IACN,aACE;AAAA,IACF,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,WAAW,EAAE,MAAM,aAAa,QAAQ,CAAC,aAAa,MAAM,EAAE;AAAA,IAC9D,QAAQ;AAAA,MACN,MAAM;AAAA,MACN,aAAa;AAAA,MACb,UAAU;AAAA,IACZ;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAIA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA;AAAA,IACN,aACE;AAAA,IACF,UAAU;AAAA,IACV,QAAQ;AAAA;AAAA,IAER,WAAW,EAAE,MAAM,SAAS;AAAA,IAC5B,QAAQ;AAAA,MACN,MAAM;AAAA,MACN,aAAa;AAAA,MACb,UAAU;AAAA,IACZ;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA;AAAA,IACN,aACE;AAAA,IACF,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,WAAW,EAAE,MAAM,SAAS;AAAA,IAC5B,QAAQ;AAAA,MACN,MAAM;AAAA,MACN,aAAa;AAAA,MACb,UAAU;AAAA,IACZ;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA;AAAA;AAAA;AAAA,IAGE,IAAI;AAAA,IACJ,MAAM;AAAA;AAAA,IACN,aACE;AAAA,IACF,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,WAAW,EAAE,MAAM,SAAS;AAAA,IAC5B,QAAQ;AAAA,MACN,MAAM;AAAA,MACN,aAAa;AAAA,MACb,UAAU;AAAA,IACZ;AAAA,EACF;AACF;AAUO,IAAM,yBAA4E;AAAA,EACvF,kBAAkB,CAAC;AAAA,EACnB,aAAa,CAAC;AAAA,EACd,UAAU,CAAC;AAAA,EACX,sBAAsB,EAAE,YAAY,kBAAkB;AAAA,EACtD,0BAA0B,EAAE,YAAY,cAAc;AACxD;AAcO,IAAM,uBAA8C;AAAA,EACzD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAaO,IAAM,6BAAoD;AAAA,EAC/D,GAAG;AAAA;AAAA;AAAA;AAAA,EAIH;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAkBO,IAAM,oBAAwE;AAAA,EACnF;AAAA;AAAA;AAAA,IAGE,IAAI;AAAA,IACJ,aACE;AAAA,EACJ;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,aACE;AAAA,EACJ;AACF;AASO,IAAM,oBAAsD;AAAA,EACjE,cAAc;AAAA,EACd,mBAAmB;AAAA;AAAA,EAEnB,8BAA8B;AAChC;AAQO,IAAM,oBAA2C;AAAA,EACtD;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAOO,IAAM,aAAwC,OAAO;AAAA,EAC1D,gBAAgB,IAAI,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC;AAC3C;AAGO,SAAS,eAAe,SAA4B;AACzD,SAAO,WAAW,OAAO,KAAK;AAChC;AAiBO,SAAS,mBACd,OACA,KAKS;AAET,MAAI,IAAI,cAAc,aAAa,SAAS,MAAM,EAAE,EAAG,QAAO;AAC9D,MAAI,IAAI,cAAc,aAAa,SAAS,MAAM,EAAE,EAAG,QAAO;AAM9D,MAAI,WAAW,MAAM,EAAE,MAAM,eAAgB,QAAO;AACpD,SAAO,iBAAiB,OAAO,GAAG;AACpC;AAMA,SAAS,iBACP,OACA,KACS;AACT,QAAM,OAAO,MAAM;AACnB,UAAQ,KAAK,MAAM;AAAA,IACjB,KAAK;AACH,aAAO,IAAI,OAAO,KAAK,CAAC,MAAM,KAAK,OAAO,SAAS,CAAC,CAAC;AAAA,IACvD,KAAK;AAGH,aAAO,YAAY,IAAI,MAAM;AAAA,IAC/B,KAAK;AACH,aAAO,IAAI,QAAQ,KAAK,IAAI,MAAM;AAAA,IACpC,KAAK;AAGH,aAAO;AAAA,EACX;AACF;AAQO,SAAS,gBACd,SACA,KAKS;AACT,QAAM,QAAQ,gBAAgB,KAAK,CAAC,MAAM,EAAE,OAAO,OAAO;AAC1D,SAAO,QAAQ,mBAAmB,OAAO,GAAG,IAAI;AAClD;AAOO,SAAS,4BAA4B,KAIX;AAC/B,SAAO,gBAAgB;AAAA,IACrB,CAAC,MACC,WAAW,EAAE,EAAE,MAAM,kBACrB,CAAC,IAAI,cAAc,aAAa,SAAS,EAAE,EAAE,KAC7C,iBAAiB,GAAG,GAAG;AAAA,EAC3B;AACF;AAeO,SAAS,gBAAgB,OAAkC;AAGhE,MAAI,MAAM,oBAAoB;AAC5B,WAAO,CAAC,GAAG,MAAM,kBAAkB;AAAA,EACrC;AACA,UAAQ,MAAM,OAAO,MAAM;AAAA,IACzB,KAAK;AACH,aAAO,CAAC,QAAQ;AAAA,IAClB,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAGH,aAAO,CAAC,GAAG,SAAS;AAAA,EACxB;AACF;AAMO,SAAS,gBAAgB,OAAsB,KAA0B;AAC9E,MAAI,IAAI,WAAW,GAAG;AACpB,WAAO;AAAA,EACT;AACA,QAAM,UAAU,gBAAgB,KAAK;AACrC,SAAO,IAAI,KAAK,CAAC,MAAM,QAAQ,SAAS,CAAC,CAAC;AAC5C;AAEO,SAAS,uBACd,QACA,KAK8B;AAC9B,SAAO,OAAO,OAAO,CAAC,MAAM,mBAAmB,GAAG,GAAG,CAAC;AACxD;;;AEx3CA;;;ACAA;AASA,SAAS,kBAAkB;AAC3B,SAAS,cAAAA,aAAY,aAAAC,YAAW,gBAAAC,eAAc,QAAQ,UAAU,qBAAqB;AACrF,SAAS,WAAAC,UAAS,QAAAC,aAAY;;;ACX9B;AAAA,EACE,MAAQ;AAAA,EACR,SAAW;AAAA,EACX,aAAe;AAAA,EACf,MAAQ;AAAA,EACR,eAAiB;AAAA,IACf,QAAU;AAAA,EACZ;AAAA,EACA,SAAW;AAAA,IACT,MAAQ;AAAA,EACV;AAAA,EACA,KAAO;AAAA,IACL,iBAAiB;AAAA,EACnB;AAAA,EACA,OAAS;AAAA,IACP;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,SAAW;AAAA,IACT,OAAS;AAAA,IACT,KAAO;AAAA,IACP,cAAc;AAAA,IACd,2BAA2B;AAAA,IAC3B,MAAQ;AAAA,IACR,cAAc;AAAA,IACd,iBAAiB;AAAA,IACjB,WAAa;AAAA,IACb,MAAQ;AAAA,IACR,YAAY;AAAA,IACZ,QAAU;AAAA,IACV,IAAM;AAAA,IACN,SAAW;AAAA,IACX,eAAe;AAAA,IACf,iBAAiB;AAAA,IACjB,kBAAkB;AAAA,IAClB,iBAAiB;AAAA,IACjB,UAAY;AAAA,IACZ,qBAAqB;AAAA,EACvB;AAAA,EACA,cAAgB;AAAA,IACd,kBAAkB;AAAA,IAClB,KAAO;AAAA,IACP,aAAa;AAAA,EACf;AAAA,EACA,iBAAmB;AAAA,IACjB,kBAAkB;AAAA,IAClB,eAAe;AAAA,IACf,uBAAuB;AAAA,IACvB,MAAQ;AAAA,IACR,YAAc;AAAA,IACd,QAAU;AAAA,EACZ;AAAA,EACA,SAAW;AAAA,EACX,YAAc;AAAA,IACZ,MAAQ;AAAA,IACR,KAAO;AAAA,EACT;AACF;;;AC3DA;AAgBO,IAAM,eAAqD;AAAA,EAChE,aAAa,EAAE,SAAS,aAAa,QAAQ,aAAa;AAAA,EAC1D,aAAa,EAAE,SAAS,aAAa,QAAQ,aAAa;AAAA,EAC1D,yBAAyB,EAAE,SAAS,aAAa,QAAQ,YAAY;AAAA,EACrE,aAAa,EAAE,SAAS,aAAa,QAAQ,QAAQ,OAAO,cAAc;AAAA,EAC1E,iBAAiB,EAAE,SAAS,aAAa,QAAQ,YAAY;AAAA,EAC7D,sBAAsB,EAAE,SAAS,eAAe,QAAQ,SAAS;AAAA,EACjE,cAAc,EAAE,SAAS,SAAS,QAAQ,aAAa;AACzD;AAGO,IAAM,kBAAyC,OAAO,OAAO,YAAY,EAAE;AAAA,EAChF,CAAC,MAAM,EAAE;AACX;AAGO,SAAS,QAAQ,IAAqB;AAC3C,SAAO,gBAAgB,KAAK,CAAC,MAAM,GAAG,WAAW,CAAC,CAAC;AACrD;AAGO,SAAS,kBAAkB,IAA2B;AAC3D,aAAW,CAACC,OAAM,IAAI,KAAK,OAAO,QAAQ,YAAY;AACpD,QAAI,GAAG,WAAW,KAAK,MAAM,EAAG,QAAOA;AACzC,SAAO;AACT;;;ACzCA;AAqBO,IAAM,sBAA+C;AAAA,EAC1D,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,UAAU;AAAA,EACV,aAAa;AACf;AAMO,IAAM,oBAA6C;AAAA,EACxD,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,UAAU;AAAA,EACV,aAAa;AACf;AAmBO,SAAS,gBAAgB,OAA6D;AAC3F,QAAM,QAAQ,eAAe,KAAK;AAClC,MAAI,MAAM,WAAW,GAAG;AACtB,WAAO,EAAE,IAAI,MAAM,SAAS,CAAC,QAAQ,GAAG,UAAU,CAAC,EAAE;AAAA,EACvD;AAEA,QAAM,YAAY,oBAAI,IAAa;AACnC,QAAM,WAAqB,CAAC;AAE5B,aAAW,QAAQ,OAAO;AACxB,QAAI,CAAC,UAAU,IAAI,GAAG;AAEpB,UAAI,OAAO;AACX,UAAI,SAAS,QAAQ;AACnB,eAAO;AAAA,MACT,WAAW,SAAS,OAAO;AACzB,eACE;AAAA,MACJ,WAAW,KAAK,SAAS,GAAG,GAAG;AAE7B,eAAO;AAAA,MACT;AACA,aAAO;AAAA,QACL,IAAI;AAAA,QACJ,SAAS,CAAC,QAAQ;AAAA,QAClB;AAAA,QACA,OAAO,wBAAwB,IAAI,qBAAqB,UAAU,KAAK,KAAK,CAAC,GAAG,IAAI;AAAA,MACtF;AAAA,IACF;AACA,cAAU,IAAI,IAAI;AAAA,EACpB;AAEA,QAAM,UAAU,CAAC,GAAG,SAAS,EAAE,KAAK,CAAC,GAAG,MAAM,oBAAoB,CAAC,IAAI,oBAAoB,CAAC,CAAC;AAC7F,SAAO,EAAE,IAAI,MAAM,SAAS,SAAS;AACvC;AAEA,SAAS,eAAe,OAAgD;AACtE,MAAI,UAAU,UAAa,UAAU,KAAM,QAAO,CAAC;AACnD,MAAI,OAAO,UAAU,UAAU;AAC7B,UAAM,UAAU,MAAM,KAAK;AAC3B,WAAO,YAAY,KAAK,CAAC,IAAI,CAAC,OAAO;AAAA,EACvC;AACA,SAAO,MAAM,OAAO,CAAC,MAAM,OAAO,MAAM,YAAY,EAAE,KAAK,MAAM,EAAE,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC;AAC1F;AAGO,SAAS,eAAe,SAAqB,MAAwB;AAC1E,SAAO,QAAQ,SAAS,IAAI;AAC9B;;;ACxGA;AAAA;AAAA,EACE;AAAA,EACA;AAAA,EACA,aAAa;AAAA,EACb,aAAAC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,gBAAAC;AAAA,EACA;AAAA,OACK;AACP,SAAS,YAAAC,WAAU,WAAAC,UAAS,QAAAC,aAAY;;;ACXxC;AASA,SAAS,WAAW,cAAc,oBAAoB;AACtD,SAAS,UAAU,SAAS,YAAY,MAAM,UAAU,SAAS,WAAW;AAerE,SAAS,iBAAiB,YAAoB,QAAyB;AAG5E,QAAM,OAAO,mBAAmB,UAAU,KAAK;AAC/C,QAAM,MAAM,SAAS,MAAM,MAAM;AACjC,SAAO,QAAQ,QAAQ,IAAI,WAAW,KAAK,GAAG,EAAE,KAAK,WAAW,GAAG;AACrE;AAOO,SAAS,mBAAmB,KAA4B;AAC7D,QAAM,OAAiB,CAAC;AACxB,QAAM,OAAO,oBAAI,IAAY;AAC7B,MAAIC,QAAO;AACX,aAAS;AACP,QAAI;AACF,aAAO,KAAK,aAAaA,KAAI,GAAG,GAAG,IAAI;AAAA,IACzC,QAAQ;AAAA,IAER;AACA,QAAI,SAAS;AACb,QAAI;AACF,YAAM,KAAK,UAAUA,OAAM,EAAE,gBAAgB,MAAM,CAAC;AACpD,UAAI,OAAO,UAAa,CAAC,GAAG,eAAe,EAAG,QAAO;AACrD,eAAS,OAAO;AAAA,IAClB,QAAQ;AACN,aAAO;AAAA,IACT;AACA,QAAI,QAAQ;AACV,UAAI,KAAK,IAAIA,KAAI,EAAG,QAAO;AAC3B,WAAK,IAAIA,KAAI;AACb,MAAAA,QAAO,QAAQ,QAAQA,KAAI,GAAG,aAAaA,KAAI,CAAC;AAChD;AAAA,IACF;AACA,UAAM,SAAS,QAAQA,KAAI;AAC3B,QAAI,WAAWA,MAAM,QAAO;AAC5B,SAAK,QAAQ,SAASA,KAAI,CAAC;AAC3B,IAAAA,QAAO;AAAA,EACT;AACF;AAMO,SAAS,qBAAqB,YAAoB,KAAiC;AACxF,QAAM,SAAS,mBAAmB,GAAG;AACrC,MAAI,WAAW,QAAQ,CAAC,iBAAiB,YAAY,MAAM,EAAG,QAAO;AACrE,QAAM,MAAM,SAAS,YAAY,GAAG;AACpC,QAAM,QAAQ,IAAI,MAAM,GAAG;AAE3B,MAAI,OAAO;AACX,MAAI,aAAa;AACjB,WAAS,IAAI,GAAG,KAAK,MAAM,QAAQ,KAAK;AACtC,UAAM,SAAS,KAAK,YAAY,GAAG,MAAM,MAAM,GAAG,CAAC,CAAC;AACpD,UAAM,KAAK,UAAU,QAAQ,EAAE,gBAAgB,MAAM,CAAC;AACtD,QAAI,IAAI,eAAe,MAAM,KAAM;AACnC,UAAM,WAAW,mBAAmB,MAAM;AAC1C,QAAI,aAAa,QAAQ,CAAC,iBAAiB,YAAY,QAAQ,EAAG;AAClE,WAAO,MAAM,MAAM,GAAG,CAAC,EAAE,KAAK,GAAG;AACjC,iBAAa;AACb;AAAA,EACF;AACA,QAAM,QAAQ,CAAC,MAAsB,EAAE,MAAM,GAAG,EAAE,KAAK,GAAG;AAC1D,SAAO,EAAE,MAAM,MAAM,GAAG,GAAG,QAAQ,MAAM,MAAM,IAAI,GAAG,WAAW;AACnE;AAMO,SAAS,cAAc,YAAoB,KAAiC;AACjF,QAAM,MAAM,KAAK,YAAY,GAAG;AAChC,MAAI;AACF,QAAI,CAAC,UAAU,KAAK,EAAE,gBAAgB,MAAM,CAAC,GAAG,eAAe,EAAG,QAAO;AAAA,EAC3E,QAAQ;AACN,WAAO;AAAA,EACT;AACA,SAAO,qBAAqB,YAAY,GAAG;AAC7C;AAQO,SAAS,mBAAmB,YAAkC;AACnE,QAAM,OAAO,oBAAI,IAAyB;AAC1C,SAAO;AAAA,IACL,KAAK,KAAK;AACR,YAAM,MAAM,qBAAqB,YAAY,GAAG;AAChD,UAAI,QAAQ,KAAM,QAAO;AACzB,UAAI,CAAC,KAAK,IAAI,IAAI,IAAI,EAAG,MAAK,IAAI,IAAI,MAAM,GAAG;AAC/C,aAAO;AAAA,IACT;AAAA,IACA,MAAM,MAAM,CAAC,GAAG,KAAK,OAAO,CAAC;AAAA,EAC/B;AACF;AAGO,SAAS,gBAAgB,OAAiE;AAC/F,QAAM,MAAM,oBAAI,IAAyB;AACzC,aAAW,QAAQ,MAAO,YAAW,KAAK,KAAM,KAAI,CAAC,IAAI,IAAI,EAAE,IAAI,EAAG,KAAI,IAAI,EAAE,MAAM,CAAC;AACvF,SAAO,CAAC,GAAG,IAAI,OAAO,CAAC;AACzB;;;ADtHO,SAAS,UAAUC,OAAoB;AAC5C,YAAUA,OAAM,EAAE,WAAW,KAAK,CAAC;AACrC;AAQO,SAAS,SAAS,QAAgB,QAAsB;AAC7D,MAAI,CAAC,WAAW,MAAM,GAAG;AACvB,UAAM,IAAI,MAAM,qBAAqB,MAAM,EAAE;AAAA,EAC/C;AACA,YAAUC,SAAQ,MAAM,GAAG,EAAE,WAAW,KAAK,CAAC;AAC9C,eAAa,QAAQ,MAAM;AAC7B;AA+CA,IAAM,oBAAoB;AAc1B,SAAS,gBAAgB,MAAc,OAA4C;AACjF,WAAS,IAAI,GAAG,KAAK,mBAAmB,KAAK;AAC3C,UAAM,YAAY,MAAM,IAAI,OAAO,GAAG,IAAI,IAAI,CAAC;AAC/C,QAAI;AACF,YAAM,SAAS;AACf,aAAO;AAAA,IACT,SAAS,KAAK;AAEZ,UAAK,IAA8B,SAAS,SAAU,OAAM;AAAA,IAC9D;AAAA,EACF;AACA,QAAM,IAAI,MAAM,qCAAqC,IAAI,KAAK,iBAAiB,SAAS;AAC1F;AAaO,SAAS,WAAW,QAAgB,MAAY,oBAAI,KAAK,GAAW;AACzE,SAAO;AAAA,IAAgB,GAAG,MAAM,WAAW,YAAY,GAAG,CAAC;AAAA,IAAI,CAAC,MAC9D,aAAa,QAAQ,GAAG,YAAY,aAAa;AAAA,EACnD;AACF;AAOO,SAAS,eAAe,QAAgB,QAAgB,MAAY,oBAAI,KAAK,GAAW;AAC7F,YAAUC,SAAQ,MAAM,GAAG,EAAE,WAAW,KAAK,CAAC;AAC9C,SAAO;AAAA,IAAgB,GAAG,MAAM,WAAW,YAAY,GAAG,CAAC;AAAA,IAAI,CAAC,MAC9D,aAAa,QAAQ,GAAG,YAAY,aAAa;AAAA,EACnD;AACF;AAGO,SAAS,WAAW,QAAgB,QAAgB,MAAY,oBAAI,KAAK,GAAW;AACzF,YAAUA,SAAQ,MAAM,GAAG,EAAE,WAAW,KAAK,CAAC;AAC9C,SAAO;AAAA,IAAgB,GAAG,MAAM,WAAW,YAAY,GAAG,CAAC;AAAA,IAAI,CAAC,MAC9D,YAAYC,cAAa,MAAM,GAAG,CAAC;AAAA,EACrC;AACF;AAGO,SAAS,eAAe,QAAgB,MAAY,oBAAI,KAAK,GAAW;AAC7E,SAAO,gBAAgB,GAAG,MAAM,WAAW,YAAY,GAAG,CAAC,IAAI,CAAC,MAAM,UAAU,CAAC,CAAC;AACpF;AAMO,SAAS,eAAe,YAAmC;AAChE,QAAM,IAAI,sCAAsC,KAAK,UAAU;AAC/D,SAAO,IAAI,CAAC,KAAK;AACnB;AASO,SAAS,gBAAgB,MAAc,UAAwC;AACpF,SAAO,KAAK,SAAS,KAAK,KAAK,WAAW,IAAI,KAAK,SAAS,IAAI,KAAK,MAAM,CAAC,CAAC;AAC/E;AAMO,SAAS,kBAAkB,KAAsB;AACtD,QAAM,OAAOC,UAAS,GAAG;AACzB,MAAI;AACF,WACE,gBAAgB,MAAM,oBAAI,IAAI,CAAC,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,KAC9CC,WAAUC,MAAKJ,SAAQ,GAAG,GAAG,KAAK,MAAM,CAAC,CAAC,GAAG,EAAE,gBAAgB,MAAM,CAAC,MAAM;AAAA,EAEhF,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAGO,SAAS,mBACd,SACK;AACL,QAAM,SAAS,CAAC,MAAkB,OAAO,MAAM,WAAW,IAAI,EAAE;AAChE,QAAM,QAAQ,IAAI,IAAI,QAAQ,IAAI,MAAM,CAAC;AACzC,SAAO,QAAQ,OAAO,CAAC,MAAM,CAAC,gBAAgB,OAAO,CAAC,GAAG,KAAK,CAAC;AACjE;AAQO,SAAS,mBAAmB,KAAa,SAAS,IAAc;AACrE,MAAI,CAAC,WAAW,GAAG,EAAG,QAAO,CAAC;AAC9B,QAAM,MAAgB,CAAC;AACvB,aAAW,SAAS,mBAAmB,YAAY,KAAK,EAAE,eAAe,KAAK,CAAC,CAAC,GAAG;AACjF,UAAM,MAAM,SAAS,GAAG,MAAM,IAAI,MAAM,IAAI,KAAK,MAAM;AACvD,QAAI,MAAM,YAAY,GAAG;AACvB,UAAI,KAAK,GAAG,mBAAmBI,MAAK,KAAK,MAAM,IAAI,GAAG,GAAG,CAAC;AAAA,IAC5D,WAAW,MAAM,OAAO,GAAG;AACzB,UAAI,KAAK,GAAG;AAAA,IACd;AAAA,EACF;AACA,SAAO;AACT;AAOO,SAAS,qBACd,KACA,SAAS,IAC8B;AACvC,MAAI,CAAC,WAAW,GAAG,EAAG,QAAO,CAAC;AAC9B,QAAM,MAA6C,CAAC;AACpD,aAAW,SAAS,mBAAmB,YAAY,KAAK,EAAE,eAAe,KAAK,CAAC,CAAC,GAAG;AACjF,UAAM,MAAM,SAAS,GAAG,MAAM,IAAI,MAAM,IAAI,KAAK,MAAM;AACvD,QAAI,MAAM,YAAY,GAAG;AACvB,UAAI,KAAK,GAAG,qBAAqBA,MAAK,KAAK,MAAM,IAAI,GAAG,GAAG,CAAC;AAAA,IAC9D,WAAW,MAAM,OAAO,KAAK,MAAM,eAAe,GAAG;AACnD,UAAI,KAAK,EAAE,KAAK,MAAM,MAAM,eAAe,EAAE,CAAC;AAAA,IAChD;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,YAAY,KAAmB;AACtC,SAAO,IACJ,YAAY,EACZ,QAAQ,SAAS,EAAE,EACnB,QAAQ,WAAW,GAAG,EACtB,MAAM,GAAG,EAAE;AAChB;AAOO,IAAM,wBAA+C;AAAA,EAC1D;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEO,SAAS,sBAAsB,YAA0B;AAC9D,aAAW,KAAK,uBAAuB;AAErC,QAAI,qBAAqB,YAAYA,MAAK,YAAY,CAAC,CAAC,MAAM,KAAM;AACpE,cAAUA,MAAK,YAAY,CAAC,GAAG,EAAE,WAAW,KAAK,CAAC;AAAA,EACpD;AACF;AAUO,SAAS,kBAAkB,QAAgB,MAAY,oBAAI,KAAK,GAAkB;AACvF,MAAI,CAAC,WAAW,MAAM,EAAG,QAAO;AAChC,MAAI;AACJ,MAAI;AACF,UAAM,aAAa,MAAM;AAAA,EAC3B,QAAQ;AACN,WAAO;AAAA,EACT;AACA,MAAI,OAAO,KAAK,IAAI,SAAS,MAAM,CAAC,EAAE,OAAO,GAAG,EAAG,QAAO;AAC1D,QAAM,SAAS,WAAW,QAAQ,GAAG;AACrC,UAAQ;AAAA,IACN,UAAK,MAAM,mHAA8G,MAAM;AAAA,EACjI;AACA,SAAO;AACT;;;AE/RA;AAmBA,SAAS,aAAAC,YAAW,gBAAAC,qBAAoB;AACxC,SAAS,QAAAC,aAAY;AAKrB,IAAM,YAAY;AAElB,IAAM,WAAW;AAEV,SAAS,YACd,QACuD;AACvD,QAAM,QAAQ,oBAAI,IAAiC;AACnD,SAAO,CAAC,WAAW;AACjB,UAAM,MAAM,CAAC,GAAG,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG;AACvC,UAAM,MAAM,MAAM,IAAI,GAAG,KAAK,IAAI,IAAI,OAAO,MAAM,CAAC;AACpD,UAAM,IAAI,KAAK,GAAG;AAClB,WAAO;AAAA,EACT;AACF;AAGO,IAAM,gBAAgB,YAAY,CAAC,WAAW;AACnD,QAAM,OAAO,eAAe,EAAE,QAAQ,SAAS,gBAAgB,CAAC;AAChE,SAAO,cAAc,IAAI,EACtB,OAAO,CAAC,MAAM,EAAE,QAAQ,IAAI,MAAM,EAAE,SAAS,UAAU,EAAE,OAAO,WAAW,iBAAiB,EAAE,EAC9F,IAAI,CAAC,MAAM,EAAE,MAAM;AACxB,CAAC;AAGM,SAAS,eAAe,KAA8B;AAC3D,SAAO,oBAAI,IAAI;AAAA,IACb,IAAI,IAAI,eAAe,CAAC,GAAG,IAAI,CAAC,MAAM,WAAW,EAAE,IAAI,EAAE;AAAA,IACzD,IAAI,IAAI,cAAc,CAAC,GAAG,IAAI,CAAC,MAAM,kBAAkB,EAAE,KAAK,MAAM,GAAG,EAAE,CAAC,KAAK,EAAE,EAAE;AAAA,EACrF,CAAC;AACH;AAGO,SAAS,mBACd,KACA,YACA,MACY;AACZ,MAAI,CAAC,KAAK,SAAS,QAAQ,EAAG,QAAO;AACrC,QAAM,WAAW,IAAI,KAAK,OAAO,OAAO,OAAO;AAC/C,QAAM,QAAQ,eAAe,KAAK,UAAU,EAAE,OAAO,CAAC,MAAM,CAAC,SAAS,SAAS,CAAC,CAAC;AACjF,MAAI,MAAM,WAAW,EAAG,QAAO;AAC/B,SAAO;AAAA,IACL,GAAG;AAAA,IACH,MAAM,EAAE,GAAG,IAAI,MAAM,QAAQ,CAAC,GAAG,oBAAI,IAAI,CAAC,GAAG,IAAI,KAAK,QAAQ,GAAG,KAAK,CAAC,CAAC,EAAE,KAAK,EAAE;AAAA,EACnF;AACF;AAEA,SAAS,eAAe,KAAiB,YAA6B;AACpE,QAAM,MAAMC,MAAK,YAAY,SAAS;AACtC,MAAIC,WAAU,KAAK,EAAE,gBAAgB,MAAM,CAAC,GAAG,OAAO,MAAM,KAAM,QAAO,CAAC;AAC1E,QAAM,UAAUC,cAAa,KAAK,MAAM;AACxC,QAAMC,OAAM,IAAI,aAAa,KAAK,CAAC,MAAM,EAAE,SAAS,QAAQ,GAAG;AAC/D,MAAIA,SAAQ,UAAa,YAAY,OAAO,MAAMA,KAAK,QAAO,CAAC;AAC/D,SAAO,QACJ,MAAM,IAAI,EACV,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EACnB,OAAO,OAAO;AACnB;;;AN1DO,IAAM,uBAAuB;AAO7B,IAAM,kBAAkB;AAExB,IAAM,yBAAyB;AAC/B,IAAM,sBAAsB;AAmF5B,IAAM,cAAc;AAAA,EACzB,EAAE,KAAK,SAAS,KAAK,MAAM;AAAA,EAC3B,EAAE,KAAK,UAAU,KAAK,MAAM;AAAA,EAC5B,EAAE,KAAK,iBAAiB,KAAK,MAAM;AAAA,EACnC,EAAE,KAAK,SAAS,KAAK,MAAM;AAC7B;AAmLA,IAAM,wBAAwB;AAqCvB,SAAS,cAAc,KAAgD;AAC5E,MAAI,QAAQ,KAAM,QAAO,CAAC;AAC1B,MAAI,IAAI,KAAK,KAAM,QAAO,SAAS,IAAI,KAAK,IAAI;AAChD,QAAM,QAAQ,IAAI,IAAa,IAAI,KAAK,IAAI,OAAO,SAAS,CAAC;AAC7D,MAAI,IAAI,UAAU,iBAAiB,OAAW,OAAM,IAAI,QAAQ;AAChE,MAAI,IAAI,UAAU,aAAa,OAAW,OAAM,IAAI,OAAO;AAC3D,MAAI,IAAI,UAAU,gBAAgB,OAAW,OAAM,IAAI,UAAU;AACjE,OAAK,IAAI,iBAAiB,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,SAAS,qBAAqB,GAAG;AAC3E,UAAM,IAAI,aAAa;AAAA,EACzB;AACA,SAAO,SAAS,CAAC,GAAG,KAAK,CAAC;AAC5B;AAGA,SAAS,SAAS,MAAyC;AACzD,SAAO,CAAC,GAAG,IAAI,IAAI,IAAI,CAAC,EACrB,OAAO,SAAS,EAChB,KAAK,CAAC,GAAG,MAAM,oBAAoB,CAAC,IAAI,oBAAoB,CAAC,CAAC;AACnE;AAMO,SAAS,kBACd,QACA,OACA,UACmB;AACnB,MAAI,CAAC,OAAQ,QAAO,CAAC;AACrB,SAAO,OAAO,UACX,OAAO,CAAC,MAAM,EAAE,EAAE,EAClB,IAAI,CAAC,MAAM;AACV,UAAM,iBAAiB,UAAU,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE;AAChE,UAAM,QAAQ;AAAA,MACZ,GAAG,gBAAgB,EAAE,OAAO,OAAO,EAAE,OAAO;AAAA,MAC5C,GAAG,WAAW,gBAAgB,OAAO,EAAE,MAAM;AAAA,IAC/C;AACA,QAAI,EAAE,UAAU,OAAW,QAAO;AAIlC,QAAI,mBAAmB,UAAa,eAAe,UAAU,OAAW,QAAO;AAC/E,WAAO,EAAE,GAAG,OAAO,OAAO,eAAe,gBAAgB,OAAO,EAAE,KAAK,EAAE;AAAA,EAC3E,CAAC;AACL;AAMO,SAAS,WACd,OACA,OACA,KACuB;AACvB,MAAI,UAAU,SAAU,QAAO,CAAC;AAChC,QAAM,OAAO,OAAO,UAAU,YAAY,MAAM,QAAQ,MAAM,MAAM,IAAI,MAAM,SAAS,CAAC;AACxF,QAAM,QAAQ,CAAC,GAAG,oBAAI,IAAI,CAAC,GAAG,MAAM,GAAI,OAAO,CAAC,CAAE,CAAC,CAAC,EAAE,KAAK;AAC3D,SAAO,MAAM,SAAS,IAAI,EAAE,QAAQ,MAAM,IAAI,CAAC;AACjD;AAOA,SAAS,eACP,OACA,KACuB;AACvB,QAAM,UAAU,IAAI,IAAI,IAAI,QAAQ,IAAI,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;AAClE,QAAM,SAAS,oBAAI,IAAoB;AACvC,aAAW,KAAK,SAAS,CAAC,EAAG,QAAO,IAAI,EAAE,MAAM,QAAQ,IAAI,EAAE,IAAI,KAAK,EAAE,MAAM;AAC/E,aAAW,KAAK,IAAI,QAAS,QAAO,IAAI,EAAE,MAAM,EAAE,MAAM;AACxD,SAAO,CAAC,GAAG,MAAM,EAAE,IAAI,CAAC,CAACC,OAAM,MAAM,OAAO,EAAE,MAAAA,OAAM,OAAO,EAAE;AAC/D;AAEA,SAAS,gBACP,OACA,OACA,SACiB;AACjB,QAAM,SAAS,aAAa,MAAM,MAAM;AACxC,QAAM,QAAyB;AAAA,IAC7B,IAAI,MAAM;AAAA,IACV,UAAU,MAAM;AAAA,IAChB,QAAQ,MAAM,OAAO;AAAA,IACrB;AAAA,IACA;AAAA,EACF;AACA,MAAI,QAAS,OAAM,UAAU;AAC7B,SAAO;AACT;AAEA,SAAS,aAAa,QAAqD;AACzE,UAAQ,OAAO,MAAM;AAAA,IACnB,KAAK;AACH,aAAO,EAAE,aAAa,OAAO,aAAa,UAAU,OAAO,SAAS;AAAA,IACtE,KAAK;AACH,aAAO,EAAE,QAAQ,OAAO,QAAQ,GAAI,OAAO,QAAQ,EAAE,OAAO,OAAO,MAAM,IAAI,CAAC,EAAG;AAAA,IACnF,KAAK;AACH,aAAO,EAAE,KAAK,OAAO,IAAI;AAAA,IAC3B,KAAK;AACH,aAAO,EAAE,KAAK,OAAO,KAAK,OAAO,OAAO,QAAQ,CAAC,GAAG,KAAK,GAAG,EAAE;AAAA,IAChE,KAAK;AAEH,aAAO,EAAE,KAAK,OAAO,IAAI;AAAA,EAC7B;AACF;AAGA,IAAM,oBAAyC,IAAI,IAAI,0BAA0B;AAkB1E,SAAS,gBACd,MACA,UACA,OACA,cACA,UACA,sBAAsB,OACtB,YAA+C,CAAC,GAChD,YACY;AAGZ,QAAM,gBAAgB,KAAK,cAAc,gBAAgB,CAAC,GAAG;AAAA,IAAO,CAAC,OACnE,kBAAkB,IAAI,EAAE;AAAA,EAC1B;AAIA,QAAM,YAAqC;AAAA,IACzC,GAAI,KAAK,IAAI,SAAS,QAAQ,IAAI,EAAE,WAAW,WAAW,IAAI,CAAC;AAAA,IAC/D,GAAI,KAAK,IAAI,SAAS,OAAO,IAAI,EAAE,UAAU,UAAU,IAAI,CAAC;AAAA,IAC5D,GAAI,KAAK,IAAI,SAAS,UAAU,IAAI,EAAE,aAAa,aAAa,IAAI,CAAC;AAAA,IACrE,GAAI,eAAe,EAAE,aAAa,IAAI,CAAC;AAAA,EACzC;AAIA,QAAM,OAAO,SAAS,CAAC,GAAG,cAAc,YAAY,IAAI,GAAG,GAAG,KAAK,IAAI,OAAO,SAAS,CAAC,CAAC;AAGzF,QAAM,SAAS;AAAA,IACb,GAAG,oBAAI,IAAI,CAAC,IAAI,UAAU,KAAK,UAAU,CAAC,GAAG,OAAO,OAAO,GAAG,GAAG,KAAK,MAAM,CAAC;AAAA,EAC/E,EAAE,KAAK;AACP,QAAM,MAAkB;AAAA,IACtB,eAAe;AAAA,IACf,cAAa,oBAAI,KAAK,GAAE,YAAY;AAAA,IACpC;AAAA,IACA,MAAM;AAAA,MACJ;AAAA,MACA,KAAK,KAAK;AAAA,MACV;AAAA,MACA,GAAI,KAAK,mBAAmB,KAAK,gBAAgB,SAAS,IACtD,EAAE,iBAAiB,KAAK,gBAAgB,IACxC,CAAC;AAAA,MACL,GAAI,aAAa,SAAS,IAAI,EAAE,aAAa,IAAI,CAAC;AAAA,IACpD;AAAA;AAAA;AAAA,IAGA,WAAW,EAAE,GAAG,UAAU,WAAW,GAAG,UAAU;AAAA,IAClD,QAAQ;AAAA,MACN,sBAAsB,UAAU,QAAQ,OAAO,uBAAuB,IAAI,UAAU;AAAA,MACpF,kBAAkB,UAAU,OAAO,UAAU,MAAM;AAAA,IACrD;AAAA,EACF;AAEA,QAAM,kBAAkB,eAAe,UAAU,WAAW,SAAS;AACrE,MAAI,gBAAgB,SAAS,EAAG,KAAI,YAAY;AAEhD,QAAM,QAAQ,cAAc,UAAU;AACtC,MAAI,MAAO,KAAI,aAAa;AAC5B,SAAO;AACT;AAWO,SAAS,eACd,UACA,SACsB;AACtB,QAAM,SAAS,oBAAI,IAAgC;AACnD,aAAW,QAAQ,CAAC,GAAI,YAAY,CAAC,GAAI,GAAG,OAAO,GAAG;AACpD,UAAM,QAAQ,OAAO,IAAI,KAAK,IAAI;AAClC,WAAO;AAAA,MACL,KAAK;AAAA,MACL,SAAS,KAAK,WAAW,cACrB;AAAA,QACE,MAAM,KAAK;AAAA,QACX,QAAQ,MAAM,WAAW,YAAY,YAAY,KAAK;AAAA,QACtD,OAAO,CAAC,GAAG,oBAAI,IAAI,CAAC,GAAG,MAAM,OAAO,GAAG,KAAK,KAAK,CAAC,CAAC;AAAA,MACrD,IACA;AAAA,IACN;AAAA,EACF;AACA,SAAO,CAAC,GAAG,OAAO,OAAO,CAAC;AAC5B;AAUA,SAAS,wBAAwB,OAAiC;AAChE,MAAI,MAAM,UAAU,SAAU,QAAO;AACrC,UAAQ,MAAM,QAAQ;AAAA,IACpB,KAAK;AACH,aAAO;AAAA;AAAA,IACT,KAAK;AACH,aAAO;AAAA;AAAA,IACT,KAAK;AACH,aAAO;AAAA;AAAA,IACT,KAAK;AACH,aAAO;AAAA;AAAA,IACT,KAAK;AAIH,aAAO;AAAA,IACT,KAAK;AAEH,aAAO;AAAA,EACX;AACF;AAGA,SAAS,YACP,UACA,SACmB;AACnB,MAAI,CAAC,YAAY,SAAS,WAAW,EAAG,QAAO,CAAC,GAAG,OAAO;AAC1D,QAAM,cAAc,IAAI,IAAI,QAAQ,IAAI,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;AACzD,QAAM,cAAc,IAAI,IAAI,SAAS,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;AACrD,SAAO;AAAA,IACL,GAAG,SAAS,IAAI,CAAC,MAAM,YAAY,IAAI,EAAE,EAAE,KAAK,CAAC;AAAA,IACjD,GAAG,QAAQ,OAAO,CAAC,MAAM,CAAC,YAAY,IAAI,EAAE,EAAE,CAAC;AAAA,EACjD;AACF;AAGO,SAAS,YAAY,SAAyB;AACnD,SAAO,WAAW,QAAQ,EAAE,OAAO,SAAS,MAAM,EAAE,OAAO,KAAK;AAClE;AAaO,SAAS,mBACd,UACA,SACuB;AACvB,QAAM,SAAS,oBAAI,IAAiC;AACpD,aAAW,QAAQ,CAAC,GAAI,YAAY,CAAC,GAAI,GAAG,OAAO,EAAG,QAAO,IAAI,KAAK,MAAM,IAAI;AAChF,SAAO,CAAC,GAAG,OAAO,OAAO,CAAC;AAC5B;AAYO,SAAS,gBAAgB,YAAoB,KAAyB;AAC3E,QAAMA,QAAO,eAAe,UAAU;AACtC,EAAAC,WAAUC,SAAQF,KAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AAE5C,QAAM,SAAqB,EAAE,GAAG,kBAAkB,GAAG,GAAG,sBAAsB,KAAK;AACnF,gBAAcA,OAAM,GAAG,KAAK,UAAU,QAAQ,MAAM,CAAC,CAAC;AAAA,GAAM,MAAM;AAClE,uBAAqB,UAAU;AAC/B,SAAOA;AACT;AAOA,SAAS,kBAAkB,KAA6B;AACtD,QAAM,MAAM,CAAC,GAAW,MAAuB,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI;AACxE,QAAM,SAAS,CAA6B,OAC1C,CAAC,GAAG,EAAE,EAAE,KAAK,CAAC,GAAG,MAAM,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC;AAC5C,QAAM,MAAkB,EAAE,GAAG,IAAI;AACjC,MAAI,IAAI,gBAAgB,OAAW,KAAI,cAAc,OAAO,IAAI,WAAW;AAC3E,MAAI,IAAI,eAAe,OAAW,KAAI,aAAa,OAAO,IAAI,UAAU;AACxE,MAAI,IAAI,kBAAkB,OAAW,KAAI,gBAAgB,OAAO,IAAI,aAAa;AACjF,MAAI,IAAI,cAAc,OAAW,KAAI,YAAY,OAAO,IAAI,SAAS;AACrE,MAAI,IAAI,aAAa,QAAW;AAC9B,QAAI,WAAW,CAAC,GAAG,IAAI,QAAQ,EAAE,KAAK,CAAC,GAAG,MAAM,IAAI,EAAE,MAAM,EAAE,IAAI,KAAK,IAAI,EAAE,KAAK,EAAE,GAAG,CAAC;AAAA,EAC1F;AACA,SAAO;AACT;AAOA,SAAS,qBAAqB,YAA0B;AACtD,QAAM,SAAS,qBAAqB,UAAU;AAC9C,MAAI,CAACG,YAAW,MAAM,EAAG;AACzB,MAAI;AACF,WAAO,MAAM;AAAA,EACf,QAAQ;AAAA,EAER;AACF;AAWO,SAAS,eAAe,YAAuC;AACpE,SAAO,qBAAqB,UAAU,EAAE;AAC1C;AAQO,SAAS,qBAAqB,YAAsC;AACzE,QAAMH,QAAO,CAAC,eAAe,UAAU,GAAG,qBAAqB,UAAU,CAAC,EAAE;AAAA,IAAK,CAAC,MAChFG,YAAW,CAAC;AAAA,EACd;AACA,MAAI,CAACH,MAAM,QAAO,EAAE,QAAQ,WAAW,KAAK,KAAK;AACjD,MAAI;AACJ,MAAI;AACF,aAAS,KAAK,MAAMI,cAAaJ,OAAM,MAAM,CAAC;AAAA,EAChD,QAAQ;AACN,WAAO,EAAE,QAAQ,aAAa,KAAK,KAAK;AAAA,EAC1C;AACA,MAAI,CAAC,uBAAuB,MAAM,EAAG,QAAO,EAAE,QAAQ,aAAa,KAAK,KAAK;AAC7E,QAAM,MAAM;AAGZ,MAAI,MAAM,QAAQ,IAAI,MAAM,GAAG;AAC7B,QAAI,SAAS,IAAI,OAAO;AAAA,MAAI,CAAC,MAC1B,EAAE,WAAsB,eAAe,EAAE,GAAG,GAAG,QAAQ,MAAM,IAAI;AAAA,IACpE;AAAA,EACF;AACA,QAAM,WAAW,gBAAgB,KAAK,UAAU;AAEhD,QAAM,SAAS,mBAAmB,SAAS,KAAK,YAAY,cAAc,SAAS,GAAG,CAAC;AAEvF,MAAI,SAAS,YAAY,SAAS,GAAG;AACnC,oBAAgB,IAAI,QAAQ;AAAA,MAC1B,UAAU,SAAS;AAAA,MACnB,SAAS,SAAS;AAAA,IACpB,CAAC;AAAA,EACH;AACA,SAAO,EAAE,QAAQ,MAAM,KAAK,OAAO;AACrC;AAGA,IAAM,kBAAkB,oBAAI,QAA+D;AAMpF,SAAS,kBAAkB,KAA+C;AAC/E,SAAO,QAAQ,OAAO,CAAC,IAAK,gBAAgB,IAAI,GAAG,GAAG,YAAY,CAAC;AACrE;AAGO,SAAS,sBAAsB,KAA+C;AACnF,SAAO,QAAQ,OAAO,CAAC,IAAK,gBAAgB,IAAI,GAAG,GAAG,WAAW,CAAC;AACpE;AAoBO,IAAM,iBAAiB;AAGvB,IAAM,kBAA0B,gBAAY;AAG5C,SAAS,gBAAgB,KAAiB,IAAgC;AAC/E,QAAM,QAAQ,CAAC,MACb,MAAM,UAAa,EAAE,WAAW;AAClC,MAAI,MAAM,GAAG,IAAI,KAAK,MAAM,GAAG,OAAO,KAAK,MAAM,GAAG,QAAQ,KAAK,MAAM,GAAG,IAAI,EAAG,QAAO;AACxF,QAAM,QAAwB;AAAA,IAC5B,IAAI,GAAG;AAAA,IACP,SAAS,GAAG;AAAA,IACZ,IAAI,GAAG;AAAA,IACP,GAAI,GAAG,QAAQ,SAAY,EAAE,KAAK,GAAG,IAAI,IAAI,CAAC;AAAA,IAC9C,GAAI,MAAM,GAAG,IAAI,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,GAAI,GAAG,QAAQ,CAAC,CAAE,EAAE;AAAA,IACvD,GAAI,MAAM,GAAG,OAAO,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,GAAI,GAAG,WAAW,CAAC,CAAE,EAAE;AAAA,IAChE,GAAI,MAAM,GAAG,QAAQ,IAAI,CAAC,IAAI,EAAE,UAAU,CAAC,GAAI,GAAG,YAAY,CAAC,CAAE,EAAE;AAAA,IACnE,GAAI,MAAM,GAAG,IAAI,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,GAAI,GAAG,QAAQ,CAAC,CAAE,EAAE;AAAA,IACvD,GAAI,GAAG,cAAc,EAAE,aAAa,KAAc,IAAI,CAAC;AAAA,EACzD;AACA,SAAO,EAAE,GAAG,KAAK,YAAY,CAAC,GAAI,IAAI,cAAc,CAAC,GAAI,KAAK,EAAE,MAAM,CAAC,cAAc,EAAE;AACzF;AAQO,IAAM,6BAA6B;AAG1C,SAAS,WAAW,YAAoB,OAAkC;AACxE,MAAI,MAAM,UAAU,QAAW;AAC7B,WAAO,MAAM,MAAM,IAAI,CAAC,MAAMK,MAAK,YAAY,EAAE,IAAI,CAAC,EAAE,OAAO,CAAC,MAAMF,YAAW,CAAC,CAAC;AAAA,EACrF;AACA,QAAM,MAAM,MAAM,OAAO,SAAS,MAAM;AACxC,SAAO,CAAC,kBAAkB,gBAAgB,EAAE;AAAA,IAAQ,CAAC,MACnD,mBAAmBE,MAAK,YAAY,GAAG,GAAG,CAAC,EAAE,IAAI,CAAC,QAAQA,MAAK,YAAY,GAAG,KAAK,GAAG,CAAC;AAAA,EACzF;AACF;AAQA,SAAS,sBACP,YACA,KACA,OACS;AACT,MAAI,MAAM,WAAW,WAAW,MAAM,UAAU,SAAU,QAAO;AACjE,QAAM,QAAQ,WAAW,YAAY,KAAK;AAC1C,MAAI,MAAM,WAAW,EAAG,QAAO;AAC/B,QAAM,IAAI,KAAK,MAAM,IAAI,WAAW;AACpC,MAAI,OAAO,MAAM,CAAC,EAAG,QAAO;AAC5B,QAAM,IAAI,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC,MAAM,SAAS,CAAC,EAAE,OAAO,CAAC;AAC3D,SAAO,KAAK,KAAK,KAAK,IAAI;AAC5B;AAaO,SAAS,gBACd,KACA,YACuE;AACvE,MAAI,IAAI,yBAAyB,KAAM,QAAO,EAAE,KAAK,aAAa,CAAC,GAAG,iBAAiB,CAAC,EAAE;AAC1F,QAAM,WAAW,IAAI,IAAI,IAAI,KAAK,mBAAmB,CAAC,CAAC;AACvD,QAAM,SAAS,IAAI,IAAI,IAAI,KAAK,gBAAgB,CAAC,CAAC;AAClD,QAAM,WAAqB,CAAC;AAC5B,QAAM,kBAA4B,CAAC;AACnC,QAAM,cAAwB,CAAC;AAC/B,QAAM,OAAiB,CAAC;AACxB,aAAW,MAAM,IAAI,YAAY,CAAC,GAAG;AACnC,QAAI,QAAQ,EAAE,GAAG;AACf,eAAS,KAAK,EAAE;AAChB;AAAA,IACF;AACA,QAAI,GAAG,WAAW,WAAW,KAAK,CAAC,SAAS,IAAI,EAAE,GAAG;AACnD,eAAS,KAAK,EAAE;AAChB;AAAA,IACF;AACA,QAAI,kBAAkB,IAAI,EAAE,KAAK,CAAC,OAAO,IAAI,EAAE,GAAG;AAChD,eAAS,KAAK,EAAE;AAChB;AAAA,IACF;AACA,UAAM,QAAQ,IAAI,OAAO,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE;AAChD,QAAI,UAAU,UAAa,CAAC,kBAAkB,IAAI,EAAE,KAAK,CAAC,GAAG,WAAW,WAAW,GAAG;AACpF,UAAI,eAAe,UAAa,sBAAsB,YAAY,KAAK,KAAK,GAAG;AAC7E,iBAAS,KAAK,EAAE;AAChB,wBAAgB,KAAK,EAAE;AACvB;AAAA,MACF;AACA,kBAAY,KAAK,EAAE;AAAA,IACrB;AACA,SAAK,KAAK,EAAE;AAAA,EACd;AACA,MAAI,OAAmB,EAAE,GAAG,KAAK,sBAAsB,KAAK;AAC5D,MAAI,KAAK,SAAS,EAAG,MAAK,WAAW;AAAA,MAChC,QAAO,KAAK;AACjB,SAAO,gBAAgB,MAAM;AAAA,IAC3B,KAAI,oBAAI,KAAK,GAAE,YAAY;AAAA,IAC3B,SAAS;AAAA,IACT,IAAI;AAAA,IACJ;AAAA,IACA,MAAM;AAAA,EACR,CAAC;AACD,SAAO,EAAE,KAAK,MAAM,aAAa,UAAU,gBAAgB;AAC7D;AAMA,SAAS,uBAAuB,OAAqC;AACnE,MAAI,OAAO,UAAU,YAAY,UAAU,KAAM,QAAO;AACxD,QAAM,IAAI;AACV,QAAM,OAAO,EAAE;AACf,MAAI,OAAO,SAAS,YAAY,SAAS,KAAM,QAAO;AACtD,MAAI,CAAC,MAAM,QAAQ,KAAK,MAAM,EAAG,QAAO;AAExC,MAAI,CAAC,MAAM,QAAQ,KAAK,GAAG,KAAK,CAAC,MAAM,QAAQ,KAAK,IAAI,EAAG,QAAO;AAClE,MAAI,CAAC,MAAM,QAAQ,EAAE,MAAM,EAAG,QAAO;AACrC,MAAI,OAAO,EAAE,cAAc,YAAY,EAAE,cAAc,KAAM,QAAO;AACpE,SAAO;AACT;AAOO,SAAS,2BAA2B,YAA4B;AACrE,SAAO,+BAA+B,eAAe,UAAU,CAAC;AAClE;AAEO,SAAS,eAAe,YAA4B;AACzD,SAAOA,MAAK,YAAY,iBAAiB,oBAAoB;AAC/D;AAGO,SAAS,qBAAqB,YAA4B;AAC/D,SAAOA,MAAK,YAAY,wBAAwB,oBAAoB;AACtE;;;AO/5BA;AAuBO,IAAM,sBAAsB;AAG5B,IAAM,sBAAsB,IAAI,mBAAmB;AAO1D,IAAM,sBAAsB;AAC5B,IAAM,oBAAoB;AAMnB,IAAM,4BAA4B;AAYlC,SAAS,2BAA2B,wBAAuD;AAChG,QAAMC,SAAQ,kBAAkB,OAAO,CAAC,MAAM,uBAAuB,SAAS,EAAE,EAAE,CAAC,EAAE;AAAA,IACnF,CAAC,MAAM,OAAO,EAAE,EAAE,aAAQ,EAAE,WAAW;AAAA,EACzC;AACA,MAAIA,OAAM,WAAW,EAAG,QAAO;AAC/B,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAGA;AAAA,EACL,EAAE,KAAK,IAAI;AACb;AAGO,SAAS,yBACd,UACA,wBACQ;AACR,QAAM,OAAO,2BAA2B,sBAAsB;AAC9D,SAAO,OAAO,GAAG,QAAQ;AAAA;AAAA,EAAO,IAAI,KAAK;AAC3C;AAGA,SAAS,YAAY,kBAAiD;AACpE,QAAM,OAAO,2BAA2B,gBAAgB;AACxD,QAAM,OAAO,OAAO,GAAG,mBAAmB;AAAA;AAAA,EAAO,IAAI,KAAK;AAC1D,SAAO,GAAG,mBAAmB;AAAA,EAAK,IAAI;AAAA,EAAK,iBAAiB;AAC9D;AAOO,IAAM,gBAAgB;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAYA,IAAM,aAA4C;AAAA,EAChD,UAAU;AAAA,IACR,OAAO;AAAA,IACP,QACE;AAAA,IACF,aAAa;AAAA,EACf;AAAA,EACA,OAAO;AAAA,IACL,OAAO;AAAA,IACP,QACE;AAAA,IACF,aAAa;AAAA,EACf;AAAA,EACA,cAAc;AAAA,IACZ,OAAO;AAAA,IACP,QACE;AAAA,IACF,aAAa;AAAA,EACf;AAAA,EACA,oBAAoB;AAAA,IAClB,OAAO;AAAA,IACP,QACE;AAAA,IACF,aAAa;AAAA,EACf;AAAA,EACA,YAAY;AAAA,IACV,OAAO;AAAA,IACP,QACE;AAAA,IACF,aAAa;AAAA,EACf;AAAA,EACA,QAAQ;AAAA,IACN,OAAO;AAAA,IACP,QACE;AAAA,IACF,aAAa;AAAA,EACf;AACF;AAuBA,SAAS,WAAW,SAAkC;AACpD,MAAI,YAAY,UAAU;AACxB,WAAO,oEAAoE,mBAAmB;AAAA,EAChG;AACA,SAAO;AACT;AAIO,SAAS,eAAe,SAAkC;AAC/D,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,WAAW,OAAO;AAAA,EACpB,EAAE,KAAK,IAAI;AACb;AASO,IAAM,wBAA6C,oBAAI,IAAI;AAAA,EAChE,GAAG,eAAe,QAAQ,EAAE,MAAM,IAAI;AAAA,EACtC,GAAG,eAAe,WAAW,EAAE,MAAM,IAAI;AAAA;AAAA,EAEzC;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AACF,CAAC;AAQM,SAAS,mBAAmB,UAA2B,UAAkB;AAC9E,QAAM,SAAS,cAAc,IAAI,CAAC,OAAO;AACvC,UAAM,OAAO,WAAW,EAAE;AAC1B,WAAO,MAAM,KAAK,KAAK;AAAA;AAAA,YAAiB,EAAE,WAAM,KAAK,MAAM;AAAA;AAAA,0BAA8B,KAAK,WAAW;AAAA,EAC3G,CAAC;AACD,SAAO,GAAG,eAAe,OAAO,CAAC;AAAA;AAAA,EAAO,OAAO,KAAK,MAAM,CAAC;AAC7D;AAGO,IAAM,gBAAgB;AAE7B,IAAM,kBAAkB;AASjB,SAAS,uBAAuBA,QAAwC;AAC7E,QAAM,MAAgB,CAAC;AACvB,MAAI,IAAI;AACR,SAAO,IAAIA,OAAM,QAAQ;AACvB,QAAI,EAAEA,OAAM,CAAC,KAAK,IAAI,KAAK,EAAE,WAAW,GAAG,GAAG;AAC5C,UAAI,KAAKA,OAAM,CAAC,KAAK,EAAE;AACvB,WAAK;AACL;AAAA,IACF;AACA,QAAI,MAAM;AACV,WAAO,MAAMA,OAAM,WAAWA,OAAM,GAAG,KAAK,IAAI,KAAK,EAAE,WAAW,GAAG,EAAG;AACxE,UAAM,MAAMA,OAAM,MAAM,GAAG,GAAG;AAC9B,UAAM,OAAO,IAAI,KAAK,IAAI;AAC1B,QAAI,CAAC,KAAK,SAAS,aAAa,KAAK,CAAC,KAAK,SAAS,eAAe,EAAG,KAAI,KAAK,GAAG,GAAG;AACrF,QAAI;AAAA,EACN;AACA,SAAO;AACT;AAOO,SAAS,kBAAkBA,QAA8B,WAA2B;AACzF,MAAI,IAAI,YAAY;AACpB,SAAO,IAAIA,OAAM,QAAQ;AACvB,UAAM,KAAKA,OAAM,CAAC,KAAK,IAAI,QAAQ,OAAO,EAAE,EAAE,KAAK;AACnD,UAAM,SAAS,MAAM,MAAM,EAAE,WAAW,KAAK,KAAK,EAAE,WAAW,8BAA8B;AAC7F,QAAI,CAAC,OAAQ;AACb;AAAA,EACF;AACA,SAAO;AACT;AAGO,SAAS,yBAAyBA,QAAwC;AAC/E,QAAMC,MAAKD,OAAM,UAAU,CAAC,MAAM,EAAE,QAAQ,OAAO,EAAE,EAAE,KAAK,MAAM,yBAAyB;AAC3F,MAAIC,QAAO,GAAI,QAAO,CAAC,GAAGD,MAAK;AAC/B,SAAO,CAAC,GAAGA,OAAM,MAAM,GAAGC,GAAE,GAAG,GAAGD,OAAM,MAAM,kBAAkBA,QAAOC,GAAE,CAAC,CAAC;AAC7E;AAGA,SAAS,iBAAiBD,QAAwC;AAChE,QAAM,MAAgB,CAAC;AACvB,MAAI,SAAS;AACb,aAAW,QAAQA,QAAO;AACxB,QAAI,CAAC,UAAU,KAAK,KAAK,EAAE,WAAW,YAAY,EAAG,UAAS;AAC9D,QAAI,QAAQ;AACV,UAAI,KAAK,SAAS,KAAK,EAAG,UAAS;AACnC;AAAA,IACF;AACA,QAAI,KAAK,IAAI;AAAA,EACf;AACA,SAAO;AACT;AAOA,SAAS,uBAAuBA,QAAwC;AACtE,SAAOA,OAAM,OAAO,CAAC,SAAS;AAC5B,UAAM,OAAO,KAAK,QAAQ,OAAO,EAAE;AACnC,WAAO,CAAC,sBAAsB,IAAI,IAAI,KAAK,CAAC,KAAK,WAAW,KAAK,eAAe,EAAE;AAAA,EACpF,CAAC;AACH;AAGA,SAAS,mBAAmBA,QAAwC;AAClE,SAAO,iBAAiB,yBAAyB,uBAAuBA,MAAK,CAAC,CAAC,EAC5E,IAAI,CAAC,MAAM,EAAE,QAAQ,OAAO,EAAE,EAAE,QAAQ,CAAC,EACzC,OAAO,CAAC,MAAM,EAAE,KAAK,MAAM,EAAE;AAClC;AAaO,SAAS,mBAAmB,MAAsC;AACvE,QAAM,WAAW,mBAAmB,mBAAmB,WAAW,EAAE,MAAM,IAAI,CAAC;AAC/E,SAAO,mBAAmB,IAAI,EAAE,KAAK,IAAI,MAAM,SAAS,KAAK,IAAI;AACnE;AAyBO,SAAS,mBAAmB,MAA4B;AAC7D,SAAO,KAAK,KAAK,WAAW;AAAA;AAAA,EAAO,KAAK,kBAAkB,mBAAmB,CAAC;AAAA;AAChF;AAcO,SAAS,oBAAoB,UAAyB,MAA4B;AACvF,QAAM,QAAQ,YAAY,KAAK,oBAAoB,CAAC,CAAC;AACrD,MAAI,aAAa,MAAM;AACrB,WAAO,GAAG,mBAAmB,IAAI,CAAC;AAAA,EAAK,KAAK;AAAA;AAAA,EAC9C;AAIA,QAAM,QAAQ,SAAS,QAAQ,mBAAmB;AAClD,MAAI,UAAU,IAAI;AAChB,UAAM,QAAQ,SAAS,QAAQ,mBAAmB,KAAK;AACvD,QAAI,UAAU,GAAI,QAAO;AACzB,UAAM,UAAU,SAAS,MAAM,OAAO,QAAQ,kBAAkB,MAAM;AAEtE,QAAI,YAAY,MAAO,QAAO;AAC9B,WAAO,GAAG,SAAS,MAAM,GAAG,KAAK,CAAC,GAAG,KAAK,GAAG,SAAS,MAAM,QAAQ,kBAAkB,MAAM,CAAC;AAAA,EAC/F;AACA,MAAI,iBAAiB,QAAQ,GAAG;AAG9B,WAAO;AAAA,EACT;AACA,QAAM,OAAO,SAAS,SAAS,IAAI,IAAI,WAAW,GAAG,QAAQ;AAAA;AAC7D,SAAO,GAAG,IAAI;AAAA,EAAK,KAAK;AAAA;AAC1B;AAUO,SAAS,mBAAmB,MAA6B;AAG9D,MAAI,CAAC,KAAK,SAAS,mBAAmB,KAAK,CAAC,KAAK,SAAS,iBAAiB,EAAG,QAAO;AACrF,QAAM,kBAAkB,KAAK,SAAS,IAAI;AAC1C,QAAM,MAAgB,CAAC;AACvB,QAAMA,SAAQ,KAAK,MAAM,IAAI;AAC7B,MAAI,IAAI;AACR,SAAO,IAAIA,OAAM,QAAQ;AACvB,UAAM,OAAOA,OAAM,CAAC,KAAK;AACzB,QAAI,KAAK,SAAS,mBAAmB,GAAG;AAGtC,UAAI,IAAI;AACR,aAAO,IAAIA,OAAM,UAAU,EAAEA,OAAM,CAAC,KAAK,IAAI,SAAS,iBAAiB,EAAG;AAC1E,UAAI,IAAIA,OAAM,SAAS,IAAI,IAAI,IAAI;AAGnC,UAAI,IAAI,SAAS,KAAK,IAAI,IAAI,SAAS,CAAC,MAAM,GAAI,KAAI,IAAI;AAC1D;AAAA,IACF;AACA,QAAI,KAAK,SAAS,iBAAiB,KAAK,KAAK,KAAK,MAAM,qBAAqB;AAE3E;AACA;AAAA,IACF;AACA,QAAI,KAAK,IAAI;AACb;AAAA,EACF;AAEA,MAAI,SAAS,IAAI,KAAK,IAAI;AAC1B,MAAI,mBAAmB,WAAW,MAAM,CAAC,OAAO,SAAS,IAAI,EAAG,WAAU;AAC1E,MAAI,CAAC,mBAAmB,OAAO,SAAS,IAAI,EAAG,UAAS,OAAO,MAAM,GAAG,EAAE;AAC1E,SAAO,WAAW,OAAO,OAAO;AAClC;AASA,SAAS,iBAAiB,MAAuB;AAC/C,MAAI,KAAK,SAAS,mBAAmB,EAAG,QAAO;AAC/C,MAAI,UAAU;AACd,aAAW,QAAQ,KAAK,MAAM,IAAI,GAAG;AACnC,QAAI,kBAAkB,KAAK,IAAI,GAAG;AAChC,gBAAU,CAAC;AACX;AAAA,IACF;AACA,QAAI,CAAC,WAAW,KAAK,KAAK,MAAM,oBAAqB,QAAO;AAAA,EAC9D;AACA,SAAO;AACT;;;AR/XO,SAAS,eAAe,KAIP;AACtB,SAAO;AAAA,IACL,QAAQ,IAAI;AAAA,IACZ,WAAW,gBAAgB,iBAAiB,GAAG;AAAA;AAAA;AAAA,IAG/C,wBAAwB,2BAA2B,OAAO,CAAC,OAAO,gBAAgB,IAAI,GAAG,CAAC;AAAA,EAC5F;AACF;AASA,IAAM,MAAM,MAAe;AAC3B,IAAM,MAAM,CAAC,MAA0B,YAAY,EAAE,MAAM;AAC3D,IAAM,WACJ,CAAC,YACD,CAAC,MACC,SAAS,EAAE,QAAQ,OAAO;AAM9B,IAAM,eAAe,CAAC,cAAc,qBAAqB,gBAAgB;AACzE,IAAM,YAAY,CAAC,eAAe,gBAAgB;AAclD,IAAM,WAAqB,CAAC,eAAe;AAC3C,IAAM,kBAAkB;AAMxB,IAAM,cAAuC;AAAA,EAC3C,MAAM,CAAC;AAAA;AAAA,EACP,gBAAgB,CAAC;AAAA,EACjB,eAAe,CAAC;AAAA,EAChB,eAAe,CAAC;AAAA;AAAA;AAAA,EAGhB,YAAY,CAAC,iBAAiB;AAAA,EAC9B,oBAAoB,CAAC,iBAAiB;AAAA,EACtC,cAAc,CAAC;AAAA,EACf,MAAM,CAAC;AAAA,EACP,WAAW,CAAC;AAAA,EACZ,SAAS,CAAC,iBAAiB;AAAA;AAAA;AAAA,EAG3B,MAAM,CAAC,iBAAiB;AAAA;AAAA,EAExB,sBAAsB,CAAC;AAAA,EACvB,oBAAoB,CAAC;AACvB;AAGO,SAAS,aAAa,MAA2B;AACtD,QAAM,MAAM,IAAI,IAAY,YAAY;AACxC,MAAI,YAAY,KAAK,MAAM,GAAG;AAC5B,eAAW,KAAK,WAAW;AACzB,UAAI,IAAI,CAAC;AAAA,IACX;AAAA,EACF;AACA,MAAI,SAAS,KAAK,QAAQ,eAAe,GAAG;AAC1C,eAAW,KAAK,UAAU;AACxB,UAAI,IAAI,CAAC;AAAA,IACX;AAAA,EACF;AACA,aAAW,KAAK,KAAK,QAAQ;AAC3B,eAAW,KAAK,YAAY,CAAC,GAAG;AAC9B,UAAI,IAAI,CAAC;AAAA,IACX;AAAA,EACF;AACA,SAAO,CAAC,GAAG,GAAG,EAAE,KAAK;AACvB;AAOO,IAAM,YAAmC,aAAa,EAAE,QAAQ,CAAC,GAAG,MAAM,EAAE,CAAC;AAQpF,IAAM,cAAc,CAAC,UAAU;AAO/B,IAAM,aAAa,CAAC,aAAa;AAe1B,IAAM,eAAyD;AAAA,EACpE,CAAC,gBAAgB,WAAW;AAAA,EAC5B,CAAC,cAAc,gBAAgB;AACjC;AAgBO,IAAM,iBAAiE;AAAA,EAC5E,EAAE,IAAI,iBAAiB,SAAS,kFAA0C;AAAA,EAC1E,EAAE,IAAI,qBAAqB,SAAS,sFAA8C;AAAA;AAAA;AAAA,EAGlF,EAAE,IAAI,gBAAgB,SAAS,mHAAmC;AAAA,EAClE,EAAE,IAAI,yBAAyB,SAAS,kFAA0C;AAAA,EAClF,EAAE,IAAI,wBAAwB,SAAS,2HAA4B;AACrE;AAmBO,IAAM,gBAAuC;AAAA;AAAA,EAElD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAeO,IAAM,2BAAkD;AAAA;AAAA;AAAA,EAG7D;AAAA;AAAA;AAAA,EAGA;AACF;AAMO,IAAM,oBAA2C,cAAc;AAAA,EAAO,CAAC,MAC5E,EAAE,WAAW,SAAS;AACxB,EAAE,IAAI,CAAC,MAAM,EAAE,MAAM,UAAU,MAAM,EAAE,QAAQ,SAAS,EAAE,CAAC;AAuBpD,IAAM,eAAe,CAAC,oBAAoB,kBAAkB;AAQnE,IAAM,oBAA8B,CAAC;AACrC,IAAM,iBAA2B,CAAC;AAY3B,SAAS,mBAAmB,QAAyB;AAC1D,SAAO,OAAO,WAAW,GAAG,eAAe,GAAG;AAChD;AAGO,SAAS,cAAc,MAA+B;AAC3D,QAAM,IAAkB,CAAC;AAGzB,aAAW,KAAK,aAAa,IAAI,GAAG;AAClC,MAAE,KAAK;AAAA,MACL,QAAQ,SAAS,CAAC;AAAA,MAClB,QAAQ,iBAAiB,CAAC;AAAA,MAC1B,MAAM;AAAA,MACN,SAAS;AAAA,IACX,CAAC;AAAA,EACH;AAeA,IAAE,KAAK;AAAA,IACL,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,SAAS;AAAA,EACX,CAAC;AAKD,IAAE,KAAK;AAAA,IACL,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,SAAS;AAAA,EACX,CAAC;AAOD,IAAE,KAAK;AAAA,IACL,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,SAAS;AAAA,EACX,CAAC;AAMD,IAAE,KAAK;AAAA,IACL,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,SAAS;AAAA,EACX,CAAC;AAGD,aAAW,KAAK,aAAa;AAC3B,MAAE,KAAK;AAAA,MACL,QAAQ,UAAU,CAAC;AAAA,MACnB,QAAQ,kBAAkB,CAAC;AAAA,MAC3B,MAAM;AAAA,MACN,SAAS;AAAA,IACX,CAAC;AAAA,EACH;AACA,aAAW,KAAK,YAAY;AAC1B,MAAE,KAAK;AAAA,MACL,QAAQ,UAAU,CAAC;AAAA,MACnB,QAAQ,kBAAkB,CAAC;AAAA,MAC3B,MAAM;AAAA,MACN,SAAS;AAAA,IACX,CAAC;AAAA,EACH;AAEA,aAAW,CAAC,GAAG,OAAO,KAAK,cAAc;AACvC,MAAE,KAAK;AAAA,MACL,QAAQ,UAAU,CAAC;AAAA,MACnB,QAAQ,kBAAkB,CAAC;AAAA,MAC3B,MAAM;AAAA,MACN,SAAS,SAAS,OAAO;AAAA,IAC3B,CAAC;AAAA,EACH;AAOA,aAAW,MAAM,mBAAmB;AAClC,MAAE,KAAK;AAAA,MACL,QAAQ,UAAU,EAAE;AAAA,MACpB,QAAQ,kBAAkB,EAAE;AAAA,MAC5B,MAAM;AAAA,MACN,SAAS;AAAA,IACX,CAAC;AAAA,EACH;AAKA,aAAW,MAAM,4BAA4B;AAC3C,MAAE,KAAK;AAAA,MACL,QAAQ,UAAU,EAAE;AAAA,MACpB,QAAQ,kBAAkB,EAAE;AAAA,MAC5B,MAAM;AAAA;AAAA;AAAA;AAAA,MAIN,SAAS,CAAC,MACR,EAAE,2BAA2B,SACzB,EAAE,uBAAuB,SAAS,EAAE;AAAA;AAAA;AAAA,QAGpC,OAAO,sBACP,gBAAgB,IAAI,EAAE,QAAQ,EAAE,QAAQ,SAAS,EAAE,gBAAgB,MAAM,EAAE,CAAC;AAAA;AAAA,IACpF,CAAC;AAAA,EACH;AACA,aAAW,MAAM,gBAAgB;AAC/B,MAAE,KAAK;AAAA,MACL,QAAQ,UAAU,EAAE;AAAA,MACpB,QAAQ,kBAAkB,EAAE;AAAA,MAC5B,MAAM;AAAA,MACN,SAAS;AAAA,IACX,CAAC;AAAA,EACH;AAGA,aAAW,KAAK,cAAc;AAC5B,MAAE,KAAK;AAAA,MACL,QAAQ,SAAS,CAAC;AAAA,MAClB,QAAQ,iBAAiB,CAAC;AAAA,MAC1B,MAAM;AAAA,MACN,SAAS;AAAA,IACX,CAAC;AAAA,EACH;AAGA,IAAE,KAAK;AAAA,IACL,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,SAAS;AAAA,EACX,CAAC;AAKD,SAAO;AACT;;;AS/gBA;AAAA,SAAS,cAAAE,aAAY,gBAAAC,qBAAoB;AACzC,SAAS,QAAAC,OAAM,WAAAC,gBAAe;AAC9B,SAAS,iBAAAC,sBAAqB;;;ACF9B;AAAA,SAAS,cAAAC,aAAY,gBAAAC,qBAAoB;AACzC,SAAS,QAAAC,aAAY;;;ACDrB;;;ACAA;AA0BA,SAAS,QAAAC,aAAY;;;AC1BrB;AAAA,SAAS,cAAAC,aAAY,gBAAAC,eAAc,iBAAAC,sBAAqB;AACxD,SAAS,QAAAC,aAAY;;;ACDrB;AAMA,SAAS,cAAAC,mBAAkB;AAGpB,SAAS,iBAAiBC,QAAsC;AACrE,QAAM,MAAMA,OAAM,IAAI,CAAC,MAAM,EAAE,QAAQ,OAAO,EAAE,EAAE,QAAQ,CAAC;AAC3D,SAAO,IAAI,SAAS,KAAK,IAAI,GAAG,EAAE,MAAM,GAAI,KAAI,IAAI;AACpD,SAAOD,YAAW,QAAQ,EAAE,OAAO,IAAI,KAAK,IAAI,CAAC,EAAE,OAAO,KAAK;AACjE;AAMO,SAAS,kBAAkB,MAA6B;AAC7D,QAAM,MAAgB,CAAC;AACvB,aAAW,QAAQ,KAAK,MAAM,IAAI,GAAG;AACnC,QAAI,OAAO,KAAK,IAAI,EAAG;AACvB,QAAI,KAAK,IAAI;AAAA,EACf;AACA,QAAM,OAAO,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC,CAAC;AAC7C,SAAO,KAAK,KAAK,CAAC,MAAM,EAAE,KAAK,MAAM,EAAE,IAAI,iBAAiB,IAAI,IAAI;AACtE;AAGA,SAAS,KAAK,MAA0B;AACtC,SAAO,KACJ,MAAM,IAAI,EACV,OAAO,CAAC,MAAM,MAAM,EAAE,EACtB,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,CAAC;AAC5B;AAEA,SAAS,OAAO,MAAmC;AACjD,SAAO,IAAI,IAAI,KAAK,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,CAAC;AAClD;AAEA,IAAM,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA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","names":["existsSync","mkdirSync","readFileSync","dirname","join","path","lstatSync","readlinkSync","basename","dirname","join","path","path","dirname","dirname","readlinkSync","basename","lstatSync","join","lstatSync","readFileSync","join","join","lstatSync","readFileSync","sha","path","mkdirSync","dirname","existsSync","readFileSync","join","lines","at","existsSync","readFileSync","join","resolve","fileURLToPath","existsSync","readFileSync","join","join","existsSync","readFileSync","writeFileSync","join","createHash","lines","path","at","existsSync","join","readFileSync","writeFileSync","present","alwaysStrip","sha","path","sha","all","at","join","lines","all","lines","at","head","tail","lines","at","lines","at","UNREADABLE","all","lines","t","parseKey","parseKey","MARKER_LINE","UNREADABLE","normEol","shas","at","path","lines","all","path","rows","readFileSync","existsSync","join","path","resolve","fileURLToPath","join","existsSync","readFileSync","path"]}