@uzysjung/agent-harness 26.157.0 → 26.158.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../node_modules/sisteransi/src/index.js","../src/index.ts","../src/cli.ts","../node_modules/cac/dist/index.js","../package.json","../src/commands/install.ts","../src/baseline-targets.ts","../src/cli-targets.ts","../src/design.ts","../src/installer.ts","../src/ci-scaffold.ts","../src/cli-transforms.ts","../src/antigravity/transform.ts","../src/codex/agents-md.ts","../src/codex/skills.ts","../src/owned-write.ts","../src/foreign-slot.ts","../src/rules-port.ts","../src/codex/opt-in.ts","../src/codex/trust-entry.ts","../src/codex/transform.ts","../src/codex/config-toml.ts","../src/opencode/transform.ts","../src/opencode/agents-md.ts","../src/opencode/opencode-json.ts","../src/env-files.ts","../src/external-installer.ts","../src/mcp-merge.ts","../src/update-mode.ts","../src/commands/install-render.ts","../src/preset-recommend.ts","../src/commands/list.ts","../src/commands/uninstall.ts","../src/uninstall-interactive.ts","../node_modules/fast-wrap-ansi/src/main.ts","../node_modules/fast-string-width/dist/index.js","../node_modules/fast-string-truncated-width/dist/index.js","../node_modules/fast-string-truncated-width/dist/utils.js","../node_modules/@clack/core/src/utils/cursor.ts","../node_modules/@clack/core/src/utils/settings.ts","../node_modules/@clack/core/src/utils/string.ts","../node_modules/@clack/core/src/utils/index.ts","../node_modules/@clack/core/src/prompts/prompt.ts","../node_modules/@clack/core/src/prompts/autocomplete.ts","../node_modules/@clack/core/src/prompts/confirm.ts","../node_modules/@clack/core/src/prompts/date.ts","../node_modules/@clack/core/src/prompts/group-multiselect.ts","../node_modules/@clack/core/src/prompts/multi-line.ts","../node_modules/@clack/core/src/prompts/multi-select.ts","../node_modules/@clack/core/src/prompts/password.ts","../node_modules/@clack/core/src/prompts/select.ts","../node_modules/@clack/core/src/prompts/select-key.ts","../node_modules/@clack/core/src/prompts/text.ts","../node_modules/node_modules/.pnpm/is-unicode-supported@1.3.0/node_modules/is-unicode-supported/index.js","../node_modules/@clack/prompts/src/common.ts","../node_modules/@clack/prompts/src/limit-options.ts","../node_modules/@clack/prompts/src/autocomplete.ts","../node_modules/@clack/prompts/src/box.ts","../node_modules/@clack/prompts/src/confirm.ts","../node_modules/@clack/prompts/src/date.ts","../node_modules/@clack/prompts/src/group.ts","../node_modules/@clack/prompts/src/group-multi-select.ts","../node_modules/@clack/prompts/src/log.ts","../node_modules/@clack/prompts/src/messages.ts","../node_modules/@clack/prompts/src/multi-line.ts","../node_modules/@clack/prompts/src/multi-select.ts","../node_modules/@clack/prompts/src/note.ts","../node_modules/@clack/prompts/src/password.ts","../node_modules/@clack/prompts/src/path.ts","../node_modules/@clack/prompts/src/spinner.ts","../node_modules/@clack/prompts/src/progress-bar.ts","../node_modules/@clack/prompts/src/select.ts","../node_modules/@clack/prompts/src/select-key.ts","../node_modules/@clack/prompts/src/stream.ts","../node_modules/@clack/prompts/src/task.ts","../node_modules/@clack/prompts/src/task-log.ts","../node_modules/@clack/prompts/src/text.ts","../src/commands/update.ts","../src/state.ts","../src/interactive.ts","../src/prompts.ts","../src/router.ts","../src/wizard-steps.ts"],"sourcesContent":["'use strict';\n\nconst ESC = '\\x1B';\nconst CSI = `${ESC}[`;\nconst beep = '\\u0007';\n\nconst cursor = {\n to(x, y) {\n if (!y) return `${CSI}${x + 1}G`;\n return `${CSI}${y + 1};${x + 1}H`;\n },\n move(x, y) {\n let ret = '';\n\n if (x < 0) ret += `${CSI}${-x}D`;\n else if (x > 0) ret += `${CSI}${x}C`;\n\n if (y < 0) ret += `${CSI}${-y}A`;\n else if (y > 0) ret += `${CSI}${y}B`;\n\n return ret;\n },\n up: (count = 1) => `${CSI}${count}A`,\n down: (count = 1) => `${CSI}${count}B`,\n forward: (count = 1) => `${CSI}${count}C`,\n backward: (count = 1) => `${CSI}${count}D`,\n nextLine: (count = 1) => `${CSI}E`.repeat(count),\n prevLine: (count = 1) => `${CSI}F`.repeat(count),\n left: `${CSI}G`,\n hide: `${CSI}?25l`,\n show: `${CSI}?25h`,\n save: `${ESC}7`,\n restore: `${ESC}8`\n}\n\nconst scroll = {\n up: (count = 1) => `${CSI}S`.repeat(count),\n down: (count = 1) => `${CSI}T`.repeat(count)\n}\n\nconst erase = {\n screen: `${CSI}2J`,\n up: (count = 1) => `${CSI}1J`.repeat(count),\n down: (count = 1) => `${CSI}J`.repeat(count),\n line: `${CSI}2K`,\n lineEnd: `${CSI}K`,\n lineStart: `${CSI}1K`,\n lines(count) {\n let clear = '';\n for (let i = 0; i < count; i++)\n clear += this.line + (i < count - 1 ? cursor.up() : '');\n if (count)\n clear += cursor.left;\n return clear;\n }\n}\n\nmodule.exports = { cursor, scroll, erase, beep };\n","import { buildCli } from \"./cli.js\";\n\nconst cli = buildCli();\ncli.parse(process.argv);\n","import { cac } from \"cac\";\nimport packageJson from \"../package.json\";\nimport { type ExecuteSpecDeps, executeSpec, registerInstallCommand } from \"./commands/install.js\";\nimport { registerListCommand } from \"./commands/list.js\";\nimport { registerUninstallCommand } from \"./commands/uninstall.js\";\nimport { registerUpdateCommand } from \"./commands/update.js\";\nimport { type InteractiveResult, runInteractive } from \"./interactive.js\";\n\n// v26.72.1 — CalVer 정합 (cli --version / package.json / git tag 단일 버전).\n// v26.82.1 — 하드코딩 → package.json derive. v26.82.0 ship 때 package.json 만 bump 되고\n// 본 상수(26.81.0)가 남아 npm 게시 패키지가 --version 을 거짓 보고 (수동 동기화 주석은\n// 못 막는다는 본 repo 3번째 증명) → 빌드 시 esbuild 가 값 인라인, 동기화 자체를 소멸.\nexport const VERSION: string = packageJson.version;\n\nexport type Cli = ReturnType<typeof cac>;\n\nexport interface DefaultActionDeps {\n log?: (msg: string) => void;\n err?: (msg: string) => void;\n exit?: (code: number) => never;\n run?: (cwd: string) => Promise<InteractiveResult>;\n /** Override the install pipeline + report renderer (used by tests). */\n execute?: (spec: NonNullable<InteractiveResult[\"spec\"]>, deps: ExecuteSpecDeps) => void;\n}\n\n/**\n * Default action — runs the interactive flow, then executes the install\n * pipeline with the captured spec. Mirrors the `install` flag-mode command's\n * post-install report.\n */\nexport async function defaultAction(deps: DefaultActionDeps = {}): Promise<void> {\n const log = deps.log ?? console.log;\n const err = deps.err ?? console.error;\n /* v8 ignore next — process.exit default. tests 는 exit 주입. */\n const exit = deps.exit ?? ((code: number) => process.exit(code) as never);\n const run = deps.run ?? ((cwd: string) => runInteractive(cwd));\n const execute = deps.execute ?? executeSpec;\n\n const result = await run(process.cwd());\n if (!result.ok) {\n if (result.message) {\n err(result.message);\n }\n // exit-code mapping: no-tty=2; cancelled/exit/disabled/declined=0\n exit(result.reason === \"no-tty\" ? 2 : 0);\n return;\n }\n if (!result.spec) {\n err(\"Internal error: interactive returned ok=true without a spec.\");\n exit(1);\n return;\n }\n // v26.63.0 — wizard 모드 표시. install header (TARGET 등) 출력 skip → Step 5 sub-section 으로 자연 흐름.\n const execDeps: import(\"./commands/install.js\").ExecuteSpecDeps = {\n log,\n err,\n exit,\n fromWizard: true,\n };\n if (result.mode) execDeps.mode = result.mode;\n execute(result.spec, execDeps);\n}\n\nexport function buildCli(): Cli {\n const cli = cac(\"agent-harness\");\n\n cli.help();\n cli.version(VERSION);\n\n registerInstallCommand(cli);\n registerUpdateCommand(cli);\n registerListCommand(cli);\n registerUninstallCommand(cli);\n\n cli\n .command(\"\", \"Interactive installer (state detection + prompts)\")\n /* v8 ignore next — cac action callback. defaultAction 자체는 별도 tests 로 검증. */\n .action(() => defaultAction());\n\n return cli;\n}\n","//#region node_modules/.pnpm/mri@1.2.0/node_modules/mri/lib/index.mjs\nfunction toArr(any) {\n\treturn any == null ? [] : Array.isArray(any) ? any : [any];\n}\nfunction toVal(out, key, val, opts) {\n\tvar x, old = out[key], nxt = !!~opts.string.indexOf(key) ? val == null || val === true ? \"\" : String(val) : typeof val === \"boolean\" ? val : !!~opts.boolean.indexOf(key) ? val === \"false\" ? false : val === \"true\" || (out._.push((x = +val, x * 0 === 0) ? x : val), !!val) : (x = +val, x * 0 === 0) ? x : val;\n\tout[key] = old == null ? nxt : Array.isArray(old) ? old.concat(nxt) : [old, nxt];\n}\nfunction lib_default(args, opts) {\n\targs = args || [];\n\topts = opts || {};\n\tvar k, arr, arg, name, val, out = { _: [] };\n\tvar i = 0, j = 0, idx = 0, len = args.length;\n\tconst alibi = opts.alias !== void 0;\n\tconst strict = opts.unknown !== void 0;\n\tconst defaults = opts.default !== void 0;\n\topts.alias = opts.alias || {};\n\topts.string = toArr(opts.string);\n\topts.boolean = toArr(opts.boolean);\n\tif (alibi) for (k in opts.alias) {\n\t\tarr = opts.alias[k] = toArr(opts.alias[k]);\n\t\tfor (i = 0; i < arr.length; i++) (opts.alias[arr[i]] = arr.concat(k)).splice(i, 1);\n\t}\n\tfor (i = opts.boolean.length; i-- > 0;) {\n\t\tarr = opts.alias[opts.boolean[i]] || [];\n\t\tfor (j = arr.length; j-- > 0;) opts.boolean.push(arr[j]);\n\t}\n\tfor (i = opts.string.length; i-- > 0;) {\n\t\tarr = opts.alias[opts.string[i]] || [];\n\t\tfor (j = arr.length; j-- > 0;) opts.string.push(arr[j]);\n\t}\n\tif (defaults) for (k in opts.default) {\n\t\tname = typeof opts.default[k];\n\t\tarr = opts.alias[k] = opts.alias[k] || [];\n\t\tif (opts[name] !== void 0) {\n\t\t\topts[name].push(k);\n\t\t\tfor (i = 0; i < arr.length; i++) opts[name].push(arr[i]);\n\t\t}\n\t}\n\tconst keys = strict ? Object.keys(opts.alias) : [];\n\tfor (i = 0; i < len; i++) {\n\t\targ = args[i];\n\t\tif (arg === \"--\") {\n\t\t\tout._ = out._.concat(args.slice(++i));\n\t\t\tbreak;\n\t\t}\n\t\tfor (j = 0; j < arg.length; j++) if (arg.charCodeAt(j) !== 45) break;\n\t\tif (j === 0) out._.push(arg);\n\t\telse if (arg.substring(j, j + 3) === \"no-\") {\n\t\t\tname = arg.substring(j + 3);\n\t\t\tif (strict && !~keys.indexOf(name)) return opts.unknown(arg);\n\t\t\tout[name] = false;\n\t\t} else {\n\t\t\tfor (idx = j + 1; idx < arg.length; idx++) if (arg.charCodeAt(idx) === 61) break;\n\t\t\tname = arg.substring(j, idx);\n\t\t\tval = arg.substring(++idx) || i + 1 === len || (\"\" + args[i + 1]).charCodeAt(0) === 45 || args[++i];\n\t\t\tarr = j === 2 ? [name] : name;\n\t\t\tfor (idx = 0; idx < arr.length; idx++) {\n\t\t\t\tname = arr[idx];\n\t\t\t\tif (strict && !~keys.indexOf(name)) return opts.unknown(\"-\".repeat(j) + name);\n\t\t\t\ttoVal(out, name, idx + 1 < arr.length || val, opts);\n\t\t\t}\n\t\t}\n\t}\n\tif (defaults) {\n\t\tfor (k in opts.default) if (out[k] === void 0) out[k] = opts.default[k];\n\t}\n\tif (alibi) for (k in out) {\n\t\tarr = opts.alias[k] || [];\n\t\twhile (arr.length > 0) out[arr.shift()] = out[k];\n\t}\n\treturn out;\n}\n\n//#endregion\n//#region src/utils.ts\nfunction removeBrackets(v) {\n\treturn v.replace(/[<[].+/, \"\").trim();\n}\nfunction findAllBrackets(v) {\n\tconst ANGLED_BRACKET_RE_GLOBAL = /<([^>]+)>/g;\n\tconst SQUARE_BRACKET_RE_GLOBAL = /\\[([^\\]]+)\\]/g;\n\tconst res = [];\n\tconst parse = (match) => {\n\t\tlet variadic = false;\n\t\tlet value = match[1];\n\t\tif (value.startsWith(\"...\")) {\n\t\t\tvalue = value.slice(3);\n\t\t\tvariadic = true;\n\t\t}\n\t\treturn {\n\t\t\trequired: match[0].startsWith(\"<\"),\n\t\t\tvalue,\n\t\t\tvariadic\n\t\t};\n\t};\n\tlet angledMatch;\n\twhile (angledMatch = ANGLED_BRACKET_RE_GLOBAL.exec(v)) res.push(parse(angledMatch));\n\tlet squareMatch;\n\twhile (squareMatch = SQUARE_BRACKET_RE_GLOBAL.exec(v)) res.push(parse(squareMatch));\n\treturn res;\n}\nfunction getMriOptions(options) {\n\tconst result = {\n\t\talias: {},\n\t\tboolean: []\n\t};\n\tfor (const [index, option] of options.entries()) {\n\t\tif (option.names.length > 1) result.alias[option.names[0]] = option.names.slice(1);\n\t\tif (option.isBoolean) if (option.negated) {\n\t\t\tif (!options.some((o, i) => {\n\t\t\t\treturn i !== index && o.names.some((name) => option.names.includes(name)) && typeof o.required === \"boolean\";\n\t\t\t})) result.boolean.push(option.names[0]);\n\t\t} else result.boolean.push(option.names[0]);\n\t}\n\treturn result;\n}\nfunction findLongest(arr) {\n\treturn arr.sort((a, b) => {\n\t\treturn a.length > b.length ? -1 : 1;\n\t})[0];\n}\nfunction padRight(str, length) {\n\treturn str.length >= length ? str : `${str}${\" \".repeat(length - str.length)}`;\n}\nfunction camelcase(input) {\n\treturn input.replaceAll(/([a-z])-([a-z])/g, (_, p1, p2) => {\n\t\treturn p1 + p2.toUpperCase();\n\t});\n}\nfunction setDotProp(obj, keys, val) {\n\tlet current = obj;\n\tfor (let i = 0; i < keys.length; i++) {\n\t\tconst key = keys[i];\n\t\tif (i === keys.length - 1) {\n\t\t\tcurrent[key] = val;\n\t\t\treturn;\n\t\t}\n\t\tif (current[key] == null) {\n\t\t\tconst nextKeyIsArrayIndex = +keys[i + 1] > -1;\n\t\t\tcurrent[key] = nextKeyIsArrayIndex ? [] : {};\n\t\t}\n\t\tcurrent = current[key];\n\t}\n}\nfunction setByType(obj, transforms) {\n\tfor (const key of Object.keys(transforms)) {\n\t\tconst transform = transforms[key];\n\t\tif (transform.shouldTransform) {\n\t\t\tobj[key] = [obj[key]].flat();\n\t\t\tif (typeof transform.transformFunction === \"function\") obj[key] = obj[key].map(transform.transformFunction);\n\t\t}\n\t}\n}\nfunction getFileName(input) {\n\tconst m = /([^\\\\/]+)$/.exec(input);\n\treturn m ? m[1] : \"\";\n}\nfunction camelcaseOptionName(name) {\n\treturn name.split(\".\").map((v, i) => {\n\t\treturn i === 0 ? camelcase(v) : v;\n\t}).join(\".\");\n}\nvar CACError = class extends Error {\n\tconstructor(message) {\n\t\tsuper(message);\n\t\tthis.name = \"CACError\";\n\t\tif (typeof Error.captureStackTrace !== \"function\") this.stack = new Error(message).stack;\n\t}\n};\n\n//#endregion\n//#region src/option.ts\nvar Option = class {\n\trawName;\n\tdescription;\n\t/** Option name */\n\tname;\n\t/** Option name and aliases */\n\tnames;\n\tisBoolean;\n\trequired;\n\tconfig;\n\tnegated;\n\tconstructor(rawName, description, config) {\n\t\tthis.rawName = rawName;\n\t\tthis.description = description;\n\t\tthis.config = Object.assign({}, config);\n\t\trawName = rawName.replaceAll(\".*\", \"\");\n\t\tthis.negated = false;\n\t\tthis.names = removeBrackets(rawName).split(\",\").map((v) => {\n\t\t\tlet name = v.trim().replace(/^-{1,2}/, \"\");\n\t\t\tif (name.startsWith(\"no-\")) {\n\t\t\t\tthis.negated = true;\n\t\t\t\tname = name.replace(/^no-/, \"\");\n\t\t\t}\n\t\t\treturn camelcaseOptionName(name);\n\t\t}).sort((a, b) => a.length > b.length ? 1 : -1);\n\t\tthis.name = this.names.at(-1);\n\t\tif (this.negated && this.config.default == null) this.config.default = true;\n\t\tif (rawName.includes(\"<\")) this.required = true;\n\t\telse if (rawName.includes(\"[\")) this.required = false;\n\t\telse this.isBoolean = true;\n\t}\n};\n\n//#endregion\n//#region src/runtime.ts\nlet runtimeProcessArgs;\nlet runtimeInfo;\nif (typeof process !== \"undefined\") {\n\tlet runtimeName;\n\tif (typeof Deno !== \"undefined\" && typeof Deno.version?.deno === \"string\") runtimeName = \"deno\";\n\telse if (typeof Bun !== \"undefined\" && typeof Bun.version === \"string\") runtimeName = \"bun\";\n\telse runtimeName = \"node\";\n\truntimeInfo = `${process.platform}-${process.arch} ${runtimeName}-${process.version}`;\n\truntimeProcessArgs = process.argv;\n} else if (typeof navigator === \"undefined\") runtimeInfo = `unknown`;\nelse runtimeInfo = `${navigator.platform} ${navigator.userAgent}`;\n\n//#endregion\n//#region src/command.ts\nvar Command = class {\n\trawName;\n\tdescription;\n\tconfig;\n\tcli;\n\toptions;\n\taliasNames;\n\tname;\n\targs;\n\tcommandAction;\n\tusageText;\n\tversionNumber;\n\texamples;\n\thelpCallback;\n\tglobalCommand;\n\tconstructor(rawName, description, config = {}, cli) {\n\t\tthis.rawName = rawName;\n\t\tthis.description = description;\n\t\tthis.config = config;\n\t\tthis.cli = cli;\n\t\tthis.options = [];\n\t\tthis.aliasNames = [];\n\t\tthis.name = removeBrackets(rawName);\n\t\tthis.args = findAllBrackets(rawName);\n\t\tthis.examples = [];\n\t}\n\tusage(text) {\n\t\tthis.usageText = text;\n\t\treturn this;\n\t}\n\tallowUnknownOptions() {\n\t\tthis.config.allowUnknownOptions = true;\n\t\treturn this;\n\t}\n\tignoreOptionDefaultValue() {\n\t\tthis.config.ignoreOptionDefaultValue = true;\n\t\treturn this;\n\t}\n\tversion(version, customFlags = \"-v, --version\") {\n\t\tthis.versionNumber = version;\n\t\tthis.option(customFlags, \"Display version number\");\n\t\treturn this;\n\t}\n\texample(example) {\n\t\tthis.examples.push(example);\n\t\treturn this;\n\t}\n\t/**\n\t* Add a option for this command\n\t* @param rawName Raw option name(s)\n\t* @param description Option description\n\t* @param config Option config\n\t*/\n\toption(rawName, description, config) {\n\t\tconst option = new Option(rawName, description, config);\n\t\tthis.options.push(option);\n\t\treturn this;\n\t}\n\talias(name) {\n\t\tthis.aliasNames.push(name);\n\t\treturn this;\n\t}\n\taction(callback) {\n\t\tthis.commandAction = callback;\n\t\treturn this;\n\t}\n\t/**\n\t* Check if a command name is matched by this command\n\t* @param name Command name\n\t*/\n\tisMatched(name) {\n\t\treturn this.name === name || this.aliasNames.includes(name);\n\t}\n\tget isDefaultCommand() {\n\t\treturn this.name === \"\" || this.aliasNames.includes(\"!\");\n\t}\n\tget isGlobalCommand() {\n\t\treturn this instanceof GlobalCommand;\n\t}\n\t/**\n\t* Check if an option is registered in this command\n\t* @param name Option name\n\t*/\n\thasOption(name) {\n\t\tname = name.split(\".\")[0];\n\t\treturn this.options.find((option) => {\n\t\t\treturn option.names.includes(name);\n\t\t});\n\t}\n\toutputHelp() {\n\t\tconst { name, commands } = this.cli;\n\t\tconst { versionNumber, options: globalOptions, helpCallback } = this.cli.globalCommand;\n\t\tlet sections = [{ body: `${name}${versionNumber ? `/${versionNumber}` : \"\"}` }];\n\t\tsections.push({\n\t\t\ttitle: \"Usage\",\n\t\t\tbody: ` $ ${name} ${this.usageText || this.rawName}`\n\t\t});\n\t\tif ((this.isGlobalCommand || this.isDefaultCommand) && commands.length > 0) {\n\t\t\tconst longestCommandName = findLongest(commands.map((command) => command.rawName));\n\t\t\tsections.push({\n\t\t\t\ttitle: \"Commands\",\n\t\t\t\tbody: commands.map((command) => {\n\t\t\t\t\treturn ` ${padRight(command.rawName, longestCommandName.length)} ${command.description}`;\n\t\t\t\t}).join(\"\\n\")\n\t\t\t}, {\n\t\t\t\ttitle: `For more info, run any command with the \\`--help\\` flag`,\n\t\t\t\tbody: commands.map((command) => ` $ ${name}${command.name === \"\" ? \"\" : ` ${command.name}`} --help`).join(\"\\n\")\n\t\t\t});\n\t\t}\n\t\tlet options = this.isGlobalCommand ? globalOptions : [...this.options, ...globalOptions || []];\n\t\tif (!this.isGlobalCommand && !this.isDefaultCommand) options = options.filter((option) => option.name !== \"version\");\n\t\tif (options.length > 0) {\n\t\t\tconst longestOptionName = findLongest(options.map((option) => option.rawName));\n\t\t\tsections.push({\n\t\t\t\ttitle: \"Options\",\n\t\t\t\tbody: options.map((option) => {\n\t\t\t\t\treturn ` ${padRight(option.rawName, longestOptionName.length)} ${option.description} ${option.config.default === void 0 ? \"\" : `(default: ${option.config.default})`}`;\n\t\t\t\t}).join(\"\\n\")\n\t\t\t});\n\t\t}\n\t\tif (this.examples.length > 0) sections.push({\n\t\t\ttitle: \"Examples\",\n\t\t\tbody: this.examples.map((example) => {\n\t\t\t\tif (typeof example === \"function\") return example(name);\n\t\t\t\treturn example;\n\t\t\t}).join(\"\\n\")\n\t\t});\n\t\tif (helpCallback) sections = helpCallback(sections) || sections;\n\t\tconsole.info(sections.map((section) => {\n\t\t\treturn section.title ? `${section.title}:\\n${section.body}` : section.body;\n\t\t}).join(\"\\n\\n\"));\n\t}\n\toutputVersion() {\n\t\tconst { name } = this.cli;\n\t\tconst { versionNumber } = this.cli.globalCommand;\n\t\tif (versionNumber) console.info(`${name}/${versionNumber} ${runtimeInfo}`);\n\t}\n\tcheckRequiredArgs() {\n\t\tconst minimalArgsCount = this.args.filter((arg) => arg.required).length;\n\t\tif (this.cli.args.length < minimalArgsCount) throw new CACError(`missing required args for command \\`${this.rawName}\\``);\n\t}\n\t/**\n\t* Check if the parsed options contain any unknown options\n\t*\n\t* Exit and output error when true\n\t*/\n\tcheckUnknownOptions() {\n\t\tconst { options, globalCommand } = this.cli;\n\t\tif (!this.config.allowUnknownOptions) {\n\t\t\tfor (const name of Object.keys(options)) if (name !== \"--\" && !this.hasOption(name) && !globalCommand.hasOption(name)) throw new CACError(`Unknown option \\`${name.length > 1 ? `--${name}` : `-${name}`}\\``);\n\t\t}\n\t}\n\t/**\n\t* Check if the required string-type options exist\n\t*/\n\tcheckOptionValue() {\n\t\tconst { options: parsedOptions, globalCommand } = this.cli;\n\t\tconst options = [...globalCommand.options, ...this.options];\n\t\tfor (const option of options) {\n\t\t\tconst value = parsedOptions[option.name.split(\".\")[0]];\n\t\t\tif (option.required) {\n\t\t\t\tconst hasNegated = options.some((o) => o.negated && o.names.includes(option.name));\n\t\t\t\tif (value === true || value === false && !hasNegated) throw new CACError(`option \\`${option.rawName}\\` value is missing`);\n\t\t\t}\n\t\t}\n\t}\n\t/**\n\t* Check if the number of args is more than expected\n\t*/\n\tcheckUnusedArgs() {\n\t\tconst maximumArgsCount = this.args.some((arg) => arg.variadic) ? Infinity : this.args.length;\n\t\tif (maximumArgsCount < this.cli.args.length) throw new CACError(`Unused args: ${this.cli.args.slice(maximumArgsCount).map((arg) => `\\`${arg}\\``).join(\", \")}`);\n\t}\n};\nvar GlobalCommand = class extends Command {\n\tconstructor(cli) {\n\t\tsuper(\"@@global@@\", \"\", {}, cli);\n\t}\n};\n\n//#endregion\n//#region src/cac.ts\nvar CAC = class extends EventTarget {\n\t/** The program name to display in help and version message */\n\tname;\n\tcommands;\n\tglobalCommand;\n\tmatchedCommand;\n\tmatchedCommandName;\n\t/**\n\t* Raw CLI arguments\n\t*/\n\trawArgs;\n\t/**\n\t* Parsed CLI arguments\n\t*/\n\targs;\n\t/**\n\t* Parsed CLI options, camelCased\n\t*/\n\toptions;\n\tshowHelpOnExit;\n\tshowVersionOnExit;\n\t/**\n\t* @param name The program name to display in help and version message\n\t*/\n\tconstructor(name = \"\") {\n\t\tsuper();\n\t\tthis.name = name;\n\t\tthis.commands = [];\n\t\tthis.rawArgs = [];\n\t\tthis.args = [];\n\t\tthis.options = {};\n\t\tthis.globalCommand = new GlobalCommand(this);\n\t\tthis.globalCommand.usage(\"<command> [options]\");\n\t}\n\t/**\n\t* Add a global usage text.\n\t*\n\t* This is not used by sub-commands.\n\t*/\n\tusage(text) {\n\t\tthis.globalCommand.usage(text);\n\t\treturn this;\n\t}\n\t/**\n\t* Add a sub-command\n\t*/\n\tcommand(rawName, description, config) {\n\t\tconst command = new Command(rawName, description || \"\", config, this);\n\t\tcommand.globalCommand = this.globalCommand;\n\t\tthis.commands.push(command);\n\t\treturn command;\n\t}\n\t/**\n\t* Add a global CLI option.\n\t*\n\t* Which is also applied to sub-commands.\n\t*/\n\toption(rawName, description, config) {\n\t\tthis.globalCommand.option(rawName, description, config);\n\t\treturn this;\n\t}\n\t/**\n\t* Show help message when `-h, --help` flags appear.\n\t*\n\t*/\n\thelp(callback) {\n\t\tthis.globalCommand.option(\"-h, --help\", \"Display this message\");\n\t\tthis.globalCommand.helpCallback = callback;\n\t\tthis.showHelpOnExit = true;\n\t\treturn this;\n\t}\n\t/**\n\t* Show version number when `-v, --version` flags appear.\n\t*\n\t*/\n\tversion(version, customFlags = \"-v, --version\") {\n\t\tthis.globalCommand.version(version, customFlags);\n\t\tthis.showVersionOnExit = true;\n\t\treturn this;\n\t}\n\t/**\n\t* Add a global example.\n\t*\n\t* This example added here will not be used by sub-commands.\n\t*/\n\texample(example) {\n\t\tthis.globalCommand.example(example);\n\t\treturn this;\n\t}\n\t/**\n\t* Output the corresponding help message\n\t* When a sub-command is matched, output the help message for the command\n\t* Otherwise output the global one.\n\t*\n\t*/\n\toutputHelp() {\n\t\tif (this.matchedCommand) this.matchedCommand.outputHelp();\n\t\telse this.globalCommand.outputHelp();\n\t}\n\t/**\n\t* Output the version number.\n\t*\n\t*/\n\toutputVersion() {\n\t\tthis.globalCommand.outputVersion();\n\t}\n\tsetParsedInfo({ args, options }, matchedCommand, matchedCommandName) {\n\t\tthis.args = args;\n\t\tthis.options = options;\n\t\tif (matchedCommand) this.matchedCommand = matchedCommand;\n\t\tif (matchedCommandName) this.matchedCommandName = matchedCommandName;\n\t\treturn this;\n\t}\n\tunsetMatchedCommand() {\n\t\tthis.matchedCommand = void 0;\n\t\tthis.matchedCommandName = void 0;\n\t}\n\t/**\n\t* Parse argv\n\t*/\n\tparse(argv, { run = true } = {}) {\n\t\tif (!argv) {\n\t\t\tif (!runtimeProcessArgs) throw new Error(\"No argv provided and runtime process argv is not available.\");\n\t\t\targv = runtimeProcessArgs;\n\t\t}\n\t\tthis.rawArgs = argv;\n\t\tif (!this.name) this.name = argv[1] ? getFileName(argv[1]) : \"cli\";\n\t\tlet shouldParse = true;\n\t\tfor (const command of this.commands) {\n\t\t\tconst parsed = this.mri(argv.slice(2), command);\n\t\t\tconst commandName = parsed.args[0];\n\t\t\tif (command.isMatched(commandName)) {\n\t\t\t\tshouldParse = false;\n\t\t\t\tconst parsedInfo = {\n\t\t\t\t\t...parsed,\n\t\t\t\t\targs: parsed.args.slice(1)\n\t\t\t\t};\n\t\t\t\tthis.setParsedInfo(parsedInfo, command, commandName);\n\t\t\t\tthis.dispatchEvent(new CustomEvent(`command:${commandName}`, { detail: command }));\n\t\t\t}\n\t\t}\n\t\tif (shouldParse) {\n\t\t\tfor (const command of this.commands) if (command.isDefaultCommand) {\n\t\t\t\tshouldParse = false;\n\t\t\t\tconst parsed = this.mri(argv.slice(2), command);\n\t\t\t\tthis.setParsedInfo(parsed, command);\n\t\t\t\tthis.dispatchEvent(new CustomEvent(\"command:!\", { detail: command }));\n\t\t\t}\n\t\t}\n\t\tif (shouldParse) {\n\t\t\tconst parsed = this.mri(argv.slice(2));\n\t\t\tthis.setParsedInfo(parsed);\n\t\t}\n\t\tif (this.options.help && this.showHelpOnExit) {\n\t\t\tthis.outputHelp();\n\t\t\trun = false;\n\t\t\tthis.unsetMatchedCommand();\n\t\t}\n\t\tif (this.options.version && this.showVersionOnExit && this.matchedCommandName == null) {\n\t\t\tthis.outputVersion();\n\t\t\trun = false;\n\t\t\tthis.unsetMatchedCommand();\n\t\t}\n\t\tconst parsedArgv = {\n\t\t\targs: this.args,\n\t\t\toptions: this.options\n\t\t};\n\t\tif (run) this.runMatchedCommand();\n\t\tif (!this.matchedCommand && this.args[0]) this.dispatchEvent(new CustomEvent(\"command:*\", { detail: this.args[0] }));\n\t\treturn parsedArgv;\n\t}\n\tmri(argv, command) {\n\t\tconst cliOptions = [...this.globalCommand.options, ...command ? command.options : []];\n\t\tconst mriOptions = getMriOptions(cliOptions);\n\t\tlet argsAfterDoubleDashes = [];\n\t\tconst doubleDashesIndex = argv.indexOf(\"--\");\n\t\tif (doubleDashesIndex !== -1) {\n\t\t\targsAfterDoubleDashes = argv.slice(doubleDashesIndex + 1);\n\t\t\targv = argv.slice(0, doubleDashesIndex);\n\t\t}\n\t\tlet parsed = lib_default(argv, mriOptions);\n\t\tparsed = Object.keys(parsed).reduce((res, name) => {\n\t\t\treturn {\n\t\t\t\t...res,\n\t\t\t\t[camelcaseOptionName(name)]: parsed[name]\n\t\t\t};\n\t\t}, { _: [] });\n\t\tconst args = parsed._;\n\t\tconst options = { \"--\": argsAfterDoubleDashes };\n\t\tconst ignoreDefault = command && command.config.ignoreOptionDefaultValue ? command.config.ignoreOptionDefaultValue : this.globalCommand.config.ignoreOptionDefaultValue;\n\t\tconst transforms = Object.create(null);\n\t\tfor (const cliOption of cliOptions) {\n\t\t\tif (!ignoreDefault && cliOption.config.default !== void 0) for (const name of cliOption.names) options[name] = cliOption.config.default;\n\t\t\tif (Array.isArray(cliOption.config.type) && transforms[cliOption.name] === void 0) {\n\t\t\t\ttransforms[cliOption.name] = Object.create(null);\n\t\t\t\ttransforms[cliOption.name].shouldTransform = true;\n\t\t\t\ttransforms[cliOption.name].transformFunction = cliOption.config.type[0];\n\t\t\t}\n\t\t}\n\t\tfor (const key of Object.keys(parsed)) if (key !== \"_\") {\n\t\t\tsetDotProp(options, key.split(\".\"), parsed[key]);\n\t\t\tsetByType(options, transforms);\n\t\t}\n\t\treturn {\n\t\t\targs,\n\t\t\toptions\n\t\t};\n\t}\n\trunMatchedCommand() {\n\t\tconst { args, options, matchedCommand: command } = this;\n\t\tif (!command || !command.commandAction) return;\n\t\tcommand.checkUnknownOptions();\n\t\tcommand.checkOptionValue();\n\t\tcommand.checkRequiredArgs();\n\t\tcommand.checkUnusedArgs();\n\t\tconst actionArgs = [];\n\t\tcommand.args.forEach((arg, index) => {\n\t\t\tif (arg.variadic) actionArgs.push(args.slice(index));\n\t\t\telse actionArgs.push(args[index]);\n\t\t});\n\t\tactionArgs.push(options);\n\t\treturn command.commandAction.apply(this, actionArgs);\n\t}\n};\n\n//#endregion\n//#region src/index.ts\n/**\n* @param name The program name to display in help and version message\n*/\nconst cac = (name = \"\") => new CAC(name);\n\n//#endregion\nexport { CAC, Command, cac, cac as default };","{\n \"name\": \"@uzysjung/agent-harness\",\n \"version\": \"26.157.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.0.0\"\n },\n \"bin\": {\n \"agent-harness\": \"./dist/index.js\"\n },\n \"files\": [\n \"dist\",\n \"templates\",\n \"scripts/prune-ecc.sh\",\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 \"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 },\n \"devDependencies\": {\n \"@biomejs/biome\": \"^2.4.13\",\n \"@types/node\": \"^25.6.0\",\n \"@vitest/coverage-v8\": \"^4.1.10\",\n \"tsup\": \"^8.3.0\",\n \"typescript\": \"^5.6.0\",\n \"vitest\": \"^4.1.10\"\n },\n \"license\": \"MIT\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/uzysjung/uzys-agent-harness.git\"\n }\n}\n","/**\n * `install` subcommand — spec 검증 + 파이프라인 오케스트레이션 (v26.82.0, Phase R).\n * 화면 출력(헤더/Phase rows/산출물/Summary)은 `install-render.ts` 로 분리.\n */\n\nimport { resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { BASELINE_PREFIX, listBaselineTargets } from \"../baseline-targets.js\";\nimport type { Cli } from \"../cli.js\";\nimport { parseCliTargets } from \"../cli-targets.js\";\nimport { c, status, unifiedSection } from \"../design.js\";\nimport { EXTERNAL_ASSETS } from \"../external-assets.js\";\nimport { type InstallReport, runInstall as runInstallPipeline } from \"../installer.js\";\nimport {\n type CliTargets,\n type InstallScope,\n type InstallSpec,\n isInstallScope,\n isTrack,\n type Track,\n} from \"../types.js\";\nimport {\n createInstallRenderer,\n type PipelineCallbacks,\n renderCliArtifacts,\n renderFinalSummary,\n renderInstallHeader,\n renderUpdateSummary,\n} from \"./install-render.js\";\n\nexport interface InstallOptions {\n track?: string[];\n /** v0.7.0 — repeatable. cac type: [String]. v0.8.0 — legacy alias 'both'/'all' 제거됨. */\n cli?: string | string[];\n /** v26.63.0 — Phase 1 templates 의 files 라인 표시 (default: counts only). */\n verbose?: boolean;\n projectDir?: string;\n // v26.81.0 (ADR-022, BREAKING) — 자산 1:1 플래그 13종(withTauri/withGsd/withEcc/withTob/\n // withAddyAgentSkills/withUzysHarness/withSuperpowers/withWshobsonAgents/withOpenspec/\n // withBmad/withClaudeVideo/withUnderstandAnything/withAgentmemory) 완전 삭제.\n // 자산 선택 = generic `--with <id>` / `--without <id>` 만. 아래는 동작 옵션.\n withPrune?: boolean;\n withCodexTrust?: boolean;\n /**\n * v26.47.0 (Phase C full) — External Asset 직접 추가 (preset condition 무관 강제 포함).\n * cac repeatable. 예: `--with railway-skills --with impeccable`.\n * 옵션-키 동작 flag (예: `--with-prune`) 와 별개 — External Asset id 만.\n */\n with?: string | string[];\n /**\n * v26.47.0 (Phase C full) — External Asset 직접 제외 (preset 추천에서 unchecked).\n * cac repeatable. 예: `--without netlify-cli`.\n */\n without?: string | string[];\n /**\n * v26.64.0 (ADR-020) — Installation scope. `project` (default) | `global`.\n * 명시 안 하면 wizard 의 scope prompt → 비대화형은 \"project\".\n */\n scope?: string;\n}\n\nexport interface RunInstallResult {\n ok: boolean;\n cli: CliTargets;\n /** Deprecation warnings (alias 사용 시 emit). caller가 stderr로 출력. */\n warnings: ReadonlyArray<string>;\n message: string;\n report?: InstallReport;\n}\n\n/**\n * Lift raw flag options to a typed InstallSpec.\n * Returns a Result-shaped value so callers can render errors uniformly.\n */\nexport function specFromOptions(options: InstallOptions): RunInstallResult {\n const parsed = parseCliTargets(options.cli);\n if (!parsed.ok) {\n return {\n ok: false,\n cli: [\"claude\"],\n warnings: parsed.warnings,\n message: parsed.error ?? \"Invalid --cli value\",\n };\n }\n const trackInputs = options.track ?? [];\n if (trackInputs.length === 0) {\n return {\n ok: false,\n cli: parsed.targets,\n warnings: parsed.warnings,\n // v26.56.0 (F6) — wizard 진입 안내. `install` subcommand 는 non-interactive.\n message:\n \"At least one --track is required (e.g. --track tooling)\\n Interactive wizard: run without subcommand → `agent-harness` (drop the `install` word)\",\n };\n }\n for (const t of trackInputs) {\n if (!isTrack(t)) {\n return {\n ok: false,\n cli: parsed.targets,\n warnings: parsed.warnings,\n message: `Unknown track: ${t}`,\n };\n }\n }\n return {\n ok: true,\n cli: parsed.targets,\n warnings: parsed.warnings,\n message: \"spec valid\",\n };\n}\n\nexport interface InstallActionDeps {\n log?: (msg: string) => void;\n err?: (msg: string) => void;\n exit?: (code: number) => never;\n /** Override the install pipeline (used by tests to avoid real fs side effects). */\n runPipeline?: (\n spec: InstallSpec,\n harnessRoot: string,\n mode?: import(\"../installer.js\").InstallMode,\n callbacks?: PipelineCallbacks,\n ) => InstallReport;\n /** Override the harness root resolver (defaults to a path relative to this file). */\n resolveHarnessRoot?: () => string;\n}\n\nexport function installAction(options: InstallOptions, deps: InstallActionDeps = {}): void {\n const log = deps.log ?? console.log;\n const err = deps.err ?? console.error;\n const exit = deps.exit ?? ((code: number) => process.exit(code) as never);\n const runPipeline = deps.runPipeline ?? defaultRunPipeline;\n const resolveHarnessRoot = deps.resolveHarnessRoot ?? defaultHarnessRoot;\n\n const validated = specFromOptions(options);\n // Deprecation warnings to stderr (alias 사용 시), regardless of ok/fail.\n for (const w of validated.warnings) {\n err(c.yellow(`[WARN] ${w}`));\n }\n if (!validated.ok) {\n err(status.failure(c.red(`ERROR: ${validated.message}`)));\n exit(1);\n return;\n }\n\n // v26.47.0 — Phase C full: --with/--without repeatable → userOverride.\n const forceInclude = normalizeRepeatable(options.with);\n const forceExclude = normalizeRepeatable(options.without);\n // 2026-08-16 — `--without` 는 두 목록을 받는다: 외부 자산 id 와 트랙 baseline id\n // (`baseline:<kind>/<name>`). 위저드에서 체크를 풀 수 있는 것을 플래그로는 못 뺀다면 같은\n // 기능이 진입점마다 다른 것이고, 이 리포가 세 번 적발당한 표면 비대칭이다.\n const baselineIds = new Set(\n listBaselineTargets({ tracks: (options.track as Track[]) ?? [] }).map((t) => t.id),\n );\n const baselineExclude = forceExclude.filter((id) => baselineIds.has(id));\n\n // v26.49.0 — unknown asset id validation (silent ignore 방지).\n const validIds = new Set(EXTERNAL_ASSETS.map((a) => a.id));\n // `--with` 는 baseline id 를 받지 않는다 — 트랙 baseline 은 이미 기본 설치라 \"추가\"할 것이\n // 없다. 그런데 두 플래그를 한 루프에서 검사하던 탓에 `--with baseline:<id>` 가 경고 없이\n // 통과하고 아무 일도 안 했다: **조용히 no-op 하는 지시**는 ADR-074 가 두 목록을 안 섞은\n // 바로 그 이유다.\n for (const id of forceInclude) {\n if (validIds.has(id)) continue;\n err(\n c.yellow(\n id.startsWith(BASELINE_PREFIX)\n ? `[WARN] '${id}' cannot be used with --with — track baseline assets install by default. Use --without to drop one.`\n : `[WARN] Unknown asset id '${id}' (--with). Skipping. Use one of: ${[...validIds].sort().join(\", \")}`,\n ),\n );\n }\n for (const id of forceExclude) {\n if (validIds.has(id) || baselineIds.has(id)) continue;\n // 갈림은 **id 의 생김새**로 한다(사유가 아니다 — 오타와 트랙 밖은 여기서 구분되지 않는다).\n // `baseline:` 꼴이면 이 트랙의 후보 전체를 함께 보여 주는 편이 카탈로그 전체를 쏟는 것보다\n // 낫다. 반대로 카탈로그 id 오타에 baseline 목록을 보이면 엉뚱한 곳을 뒤지게 된다.\n err(\n c.yellow(\n id.startsWith(BASELINE_PREFIX)\n ? `[WARN] '${id}' is not installed by the selected track(s) — nothing to exclude. Available: ${[...baselineIds].sort().join(\", \")}`\n : `[WARN] Unknown asset id '${id}' (--without). Skipping. Use one of: ${[...validIds].sort().join(\", \")}`,\n ),\n );\n }\n const filteredInclude = forceInclude.filter((id) => validIds.has(id));\n const filteredExclude = forceExclude.filter((id) => validIds.has(id));\n const userOverride =\n filteredInclude.length > 0 || filteredExclude.length > 0\n ? { forceInclude: filteredInclude, forceExclude: filteredExclude }\n : undefined;\n\n const spec: InstallSpec = {\n tracks: (options.track as Track[]) ?? [],\n ...(userOverride ? { userOverride } : {}),\n ...(baselineExclude.length > 0 ? { baselineExclude } : {}),\n // v26.81.0 (ADR-022, BREAKING) — 자산 1:1 boolean 13종 삭제. 자산 선택은 위\n // userOverride(--with <id>)로 일원화. 잔존 = 설치 동작 옵션만.\n options: {\n withPrune: options.withPrune === true,\n withCodexTrust: options.withCodexTrust === true,\n },\n cli: validated.cli,\n projectDir: resolve(options.projectDir ?? process.cwd()),\n scope: resolveScopeOption(options.scope, err),\n };\n\n executeSpec(spec, {\n log,\n err,\n exit,\n runPipeline,\n resolveHarnessRoot,\n verbose: options.verbose === true,\n });\n}\n\nexport interface ExecuteSpecDeps {\n log?: (msg: string) => void;\n err?: (msg: string) => void;\n exit?: (code: number) => never;\n runPipeline?: (\n spec: InstallSpec,\n harnessRoot: string,\n mode?: import(\"../installer.js\").InstallMode,\n callbacks?: PipelineCallbacks,\n ) => InstallReport;\n resolveHarnessRoot?: () => string;\n /** Router action mode (forwarded to runInstall). Default \"fresh\". */\n mode?: import(\"../installer.js\").InstallMode;\n /**\n * v26.63.0 — wizard 모드 (Step 1~4 통과 후 호출) 식별. true 시:\n * - install header (TARGET / TRACKS / CLI / OPTIONS / ASSETS) 출력 skip\n * (Step 3 review + Step 4 confirm 에서 이미 표시)\n * - \"Step 5/5 — Installing\" 흐름에 자연 연결\n */\n fromWizard?: boolean;\n /**\n * v26.63.0 — verbose 출력 (Phase 1 templates 의 files 라인 표시).\n * Default false — 카운트 + use 만 표시 (cognitive load 감소).\n */\n verbose?: boolean;\n}\n\n/**\n * Run the install pipeline for a fully-validated InstallSpec and render the\n * report. Shared by the `install` flag-mode command and the default\n * (interactive) action so both have identical post-install output.\n */\nexport function executeSpec(spec: InstallSpec, deps: ExecuteSpecDeps = {}): void {\n const log = deps.log ?? console.log;\n const err = deps.err ?? console.error;\n const exit = deps.exit ?? ((code: number) => process.exit(code) as never);\n const runPipeline = deps.runPipeline ?? defaultRunPipeline;\n const resolveHarnessRoot = deps.resolveHarnessRoot ?? defaultHarnessRoot;\n\n // v26.63.0 — wizard 모드는 header (TARGET ~ ASSETS) 출력 skip — Step 3/4 에서 이미 표시.\n // non-interactive (--track ...) 모드는 기존 header 유지 — 사용자 spec 확인 cue 필요.\n if (!deps.fromWizard) {\n renderInstallHeader(log, spec, deps.mode);\n }\n\n // v26.63.0 — phaseHeader → unifiedSection. Phase 카운터 (1/2/3) 제거 — 5-step 통합 시\n // wizard step 5/5 안 sub-section 으로 자연 흐름. Update mode 도 동일.\n log(unifiedSection(deps.mode === \"update\" ? \"Update Mode\" : \"Templates\"));\n log(\"\");\n\n // Streaming progress: baseline 완료 시 즉시 Phase 1 rows 출력, external은 per-asset 스트리밍.\n const renderer = createInstallRenderer(log, spec, deps.verbose === true);\n\n let report: InstallReport;\n try {\n report = runPipeline(spec, resolveHarnessRoot(), deps.mode, renderer.callbacks);\n } catch (e: unknown) {\n const detail = e instanceof Error ? e.message : String(e);\n log(\"\");\n err(status.failure(c.red(`install failed — ${detail}`)));\n exit(1);\n return;\n }\n\n // Update mode 단축 출력 — manifest copy / external 모두 skip\n if (report.updateMode) {\n renderUpdateSummary(log, spec, report);\n return;\n }\n\n // Phase 2 trailing newline (if header was printed)\n if (renderer.phase2HeaderPrinted()) {\n log(\"\");\n }\n\n renderCliArtifacts(log, spec, report);\n renderFinalSummary(log, spec, report, deps.fromWizard === true);\n}\n\n/**\n * v26.64.0 (ADR-020) — `--scope` flag 해석. invalid 값은 warn + \"project\" default.\n * 비대화형 (--track 명시) 진입에서만 호출. wizard 는 별도 prompt.\n */\nfunction resolveScopeOption(value: string | undefined, err: (msg: string) => void): InstallScope {\n if (value === undefined) return \"project\";\n if (isInstallScope(value)) return value;\n err(\n c.yellow(`[WARN] Unknown --scope value '${value}' (expected: project, global). Using project.`),\n );\n return \"project\";\n}\n\n/**\n * v26.47.0 — Normalize cac repeatable flag (string | string[] | undefined) → string[].\n * Trim 빈 문자열 + dedup.\n */\nfunction normalizeRepeatable(value: string | string[] | undefined): string[] {\n if (!value) return [];\n const arr = Array.isArray(value) ? value : [value];\n return [...new Set(arr.map((s) => s.trim()).filter((s) => s.length > 0))];\n}\n\n/* v8 ignore start — thin dep-inject defaults. tests 는 항상 runPipeline / resolveHarnessRoot 주입. */\nfunction defaultRunPipeline(\n spec: InstallSpec,\n harnessRoot: string,\n mode?: import(\"../installer.js\").InstallMode,\n callbacks?: PipelineCallbacks,\n): InstallReport {\n const ctx: import(\"../installer.js\").InstallContext = {\n harnessRoot,\n projectDir: spec.projectDir,\n spec,\n };\n if (mode) ctx.mode = mode;\n if (callbacks?.onProgress) ctx.onProgress = callbacks.onProgress;\n if (callbacks?.externalDeps) ctx.externalDeps = callbacks.externalDeps;\n return runInstallPipeline(ctx);\n}\n\nfunction defaultHarnessRoot(): string {\n // The bundled CLI lives at <root>/dist/index.js. import.meta.url + ../ resolves to <root>.\n // fileURLToPath 필수 — `.pathname` 은 공백/비ASCII 경로를 percent-encoded 로 남겨\n // \"Templates dir not found\" 로 install 이 실패한다 (v26.103.0 SOD 리뷰 2기 독립 수렴).\n return resolve(fileURLToPath(new URL(\".\", import.meta.url)), \"..\");\n}\n\n/* v8 ignore stop */\n\nexport { defaultHarnessRoot };\n\nexport function registerInstallCommand(cli: Cli): void {\n cli\n .command(\"install\", \"Install harness assets into a project\")\n // === Track / CLI / Project ===\n .option(\"--track <name>\", \"[Track] Track to install (repeatable)\", { type: [String] })\n .option(\n \"--cli <target>\",\n \"[CLI] Target CLI (repeatable): claude | codex | opencode | antigravity\",\n {\n type: [String],\n default: \"claude\",\n },\n )\n .option(\"--project-dir <path>\", \"[Project] Target project directory\", {\n default: process.cwd(),\n })\n .option(\"--scope <scope>\", \"[Scope] Installation scope: project (default) | global\", {\n default: \"project\",\n })\n // === Asset selection (Phase C full, v26.47.0+) ===\n .option(\n \"--with <asset-id>\",\n \"[Asset] Force-include External Asset id (regardless of preset). Repeatable. v26.47.0+\",\n )\n .option(\n \"--without <asset-id>\",\n \"[Asset] Force-exclude External Asset id (drop from preset recommendation). Repeatable. v26.47.0+\",\n )\n // === Codex global (v26.46.0+) ===\n .option(\n \"--with-codex-trust\",\n \"[Codex] Codex global opt-in: register trust entry in ~/.codex/config.toml\",\n )\n // v26.81.0 (ADR-022, BREAKING) — 자산 1:1 플래그 13종 삭제. 자산 opt-in 은 전부\n // generic `--with <asset-id>` (위) — 자산 id 목록은 docs/COMPATIBILITY.md 표 참조.\n // 아래는 자산이 아닌 설치 동작 옵션만.\n .option(\n \"--with-prune\",\n \"[Behavior] Prune ECC items beyond curated 89 (use with --with ecc-plugin)\",\n )\n // === Misc ===\n .option(\"--verbose\", \"[Misc] Show installed file lists per category (default: counts only)\")\n // === Examples (v26.50.0+) ===\n .example(\"install --track tooling --with marketingskills\")\n .example(\"install --track csr-supabase --cli claude --cli codex\")\n .example(\"install --track csr-supabase --without netlify-cli --with railway-skills\")\n /* v8 ignore next — cac action callback. installAction 자체는 별도 tests 로 검증. */\n .action((options: InstallOptions) => installAction(options));\n}\n","/**\n * baseline-targets.ts — 트랙이 고르는 자산을 **선택 가능한 항목으로** 드러낸다.\n *\n * 왜 있는가: 트랙을 고르면 룰·에이전트·훅·스킬이 화면에 한 번도 안 나온 채 깔렸다. 위저드\n * 3단계는 외부 자산(`EXTERNAL_ASSETS`)만 물었고, `manifest.ts` 가 `applies()` 로 고르는 것들은\n * 설치가 끝난 뒤 요약에서 처음 보였다 — 사용자 지적: *\"기술스택 선택했다고 알아서 설치되는\n * 구조는 개선해. 설치항목에 대해서는 모두 투명하게 다 보여줘야 해.\"*\n *\n * 이 모듈은 그 목록을 **manifest 에서 derive** 한다. 이름을 여기 옮겨 적으면 그게 곧 두 번째\n * 하드코딩 사본이 되고, 자산이 늘어도 화면은 그대로인 상태가 조용히 생긴다 — 이 리포가\n * 반복해서 당한 실패 모드다.\n *\n * **고를 수 있는 것만 낸다.** `settings.json`·앵커·`.mcp.json`·스켈레톤은 빼는 순간 설치가\n * 반쪽이 되므로 목록에 없다. 판별은 대상 경로 접두 하나로 하고, 그 접두는 설치 화면의 분류기\n * (`installer.ts` categorize)와 같은 술어다.\n */\n\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { extractFrontmatter, resolveBundleRoot } from \"./context-cost.js\";\nimport { type AssetSpec, buildManifest } from \"./manifest.js\";\n\n/** 사용자가 해제할 수 있는 baseline 자산 종류. */\nexport type BaselineKind = \"rules\" | \"agents\" | \"hooks\" | \"skills\";\n\nexport const BASELINE_KINDS: ReadonlyArray<BaselineKind> = [\"rules\", \"agents\", \"hooks\", \"skills\"];\n\n/** 선택 항목 id 의 접두. `asset:` / `option:` 과 같은 자리의 세 번째 네임스페이스다. */\nexport const BASELINE_PREFIX = \"baseline:\";\n\nexport interface BaselineTarget {\n /** `baseline:<kind>/<name>` — 위저드 체크 값이자 `--without` 인자. */\n id: string;\n kind: BaselineKind;\n /** 사용자에게 보이는 이름 (`git-policy`, `reviewer`, `protect-files`, `north-star`). */\n name: string;\n /**\n * #421 — 위저드 행 옆의 한 줄 설명. 파일 자체에서 뽑는다(룰 = 제목 + 첫 문장 · 에이전트·스킬 =\n * frontmatter description · 훅 = 머리 주석) — 여기 따로 적으면 파일이 바뀔 때 썩는다.\n * `withBaselineHints` 가 채운다. 없으면 이름만 보인다.\n */\n hint?: string;\n}\n\nconst PREFIX_BY_KIND: ReadonlyArray<[BaselineKind, string]> = [\n [\"rules\", \".claude/rules/\"],\n [\"agents\", \".claude/agents/\"],\n [\"hooks\", \".claude/hooks/\"],\n [\"skills\", \".claude/skills/\"],\n];\n\n/**\n * 설치 대상 경로 → 선택 항목. 고를 수 없는 자산이면 null.\n *\n * 스킬은 **첫 경로 조각**으로 이름을 잡는다. #409 이전에는 manifest 에 디렉터리 엔트리와\n * 파일 엔트리(`.claude/skills/<id>/SKILL.md`)가 섞여 있었고, 마지막 조각을 쓰면 후자가\n * `SKILL.md` 라는 이름의 항목으로 떴다. **지금은 전부 디렉터리 단위라 그 혼재는 없다**\n * (`tests/skill-registration-uniform.test.ts` 가 강제). 첫 조각을 계속 쓰는 이유는 그래도\n * 스킬 하위 파일이 개별 엔트리로 들어올 여지가 남기 때문이고, 그때도 같은 스킬의 여러 형태가\n * **한 체크박스로 합쳐진다** — 해제하면 둘 다 빠지는 것이 맞다.\n */\nexport function classifyBaselineTarget(target: string): BaselineTarget | null {\n for (const [kind, prefix] of PREFIX_BY_KIND) {\n if (!target.startsWith(prefix)) continue;\n const rest = target.slice(prefix.length);\n if (rest === \"\") return null;\n const first = rest.split(\"/\")[0] ?? \"\";\n const name = kind === \"skills\" ? first : first.replace(/\\.(md|sh)$/, \"\");\n if (name === \"\") return null;\n return { id: `${BASELINE_PREFIX}${kind}/${name}`, kind, name };\n }\n return null;\n}\n\n/**\n * 이 spec 에서 실제로 깔릴 baseline 자산 목록 (kind → name 순 정렬, 중복 제거).\n *\n * `applies()` 를 여기서 평가한다 — 안 깔릴 것을 화면에 내면 사용자는 해제할 수 없는 항목을\n * 보게 되고, 그건 투명성이 아니라 소음이다.\n *\n * **알려진 한계: `withEcc` 는 이 시점에 모른다** (독립 리뷰 F5). `ecc-plugin` 은 같은 3단계의\n * **뒤쪽 페이지**에서 고르는데 baseline 페이지가 앞에 있어, 여기서는 `withEcc` 가 undefined 다.\n * 결과는 **한 방향으로만 틀린다**: `!s.withEcc` 폴백 자산이 화면에 뜨는데 `ecc-plugin` 을 고르면\n * 안 깔린다. 그 반대(안 보여 준 것이 깔리는 것)는 일어나지 않으므로 **조용한 설치는 생기지 않고**,\n * 체크를 남겨 둔 항목은 제외 목록에도 안 들어가 오제외도 없다.\n *\n * **대상은 에이전트만이 아니다** — 스킬이 더 많다. 전 트랙을 켜면 14종(에이전트 4 · 스킬 10),\n * `full` 도 같고, 좁은 트랙은 더 적다(`tooling` 7 · `project-management` 4. 실측 2026-08-26).\n * 여기 이름을 옮겨 적지 않는 이유는 `listBaselineTargets` 를 `withEcc` 없이/`true` 로 두 번 불러\n * **diff 하면 그 자리에서 나오기** 때문이다 — 목록 사본은 게이트가 늘 때마다 썩는다. 실제로 옛\n * 문안이 \"에이전트 4종\"으로 열거했다가 #340 이 스킬 4종에 같은 게이트를 붙이면서 틀려졌다.\n *\n * 고치려면 baseline 페이지를 자산 페이지 **뒤로** 옮겨야 한다 — 사용자가 확정한 \"맨 앞에서 전부\n * 보여준다\"를 뒤집는 결정이라 여기서 임의로 하지 않는다 (ADR-074 Consequences).\n *\n * `selectedInternalSkills` 는 한계가 아니다. 그 12종은 전부 `EXTERNAL_ASSETS` 항목이라 자산\n * 페이지에서 이미 개별 선택되고, 여기 또 내면 **한 스킬에 체크박스가 둘** 생긴다.\n */\nexport function listBaselineTargets(spec: AssetSpec): BaselineTarget[] {\n const byId = new Map<string, BaselineTarget>();\n for (const entry of buildManifest(spec)) {\n if (!entry.applies(spec)) continue;\n const t = classifyBaselineTarget(entry.target);\n if (t && !byId.has(t.id)) byId.set(t.id, t);\n }\n const order = new Map(BASELINE_KINDS.map((k, i) => [k, i]));\n return [...byId.values()].sort(\n (a, b) => (order.get(a.kind) ?? 0) - (order.get(b.kind) ?? 0) || a.name.localeCompare(b.name),\n );\n}\n\n/**\n * 제외 판정. 대상 경로가 해제된 항목에 속하면 true.\n *\n * 고를 수 없는 자산(`settings.json` 등)은 `classifyBaselineTarget` 이 null 을 내므로 **어떤\n * 제외 목록으로도 빠지지 않는다** — 잘못된 id 가 들어와도 설치가 반쪽이 되지 않는다.\n */\nexport function isBaselineExcluded(target: string, excluded: ReadonlySet<string>): boolean {\n if (excluded.size === 0) return false;\n const t = classifyBaselineTarget(target);\n return t !== null && excluded.has(t.id);\n}\n\nconst HINT_MAX = 96;\n\nfunction clip(text: string): string {\n const t = text.replace(/\\s+/g, \" \").trim();\n return t.length > HINT_MAX ? `${t.slice(0, HINT_MAX - 1)}…` : t;\n}\n\n/**\n * #421 — 대상 파일에서 한 줄 설명을 뽑는다. 사용자 관측: \"Rule, Agent 에 대한 설명이 불친절\" —\n * 이름(`git-policy`)만으로는 체크를 풀지 말지 판단할 수 없었다.\n */\nexport function describeBaselineTarget(\n t: BaselineTarget,\n templatesDir: string = join(resolveBundleRoot(), \"templates\"),\n): string | undefined {\n const file =\n t.kind === \"skills\"\n ? join(templatesDir, \"skills\", t.name, \"SKILL.md\")\n : join(templatesDir, t.kind, `${t.name}.${t.kind === \"hooks\" ? \"sh\" : \"md\"}`);\n if (!existsSync(file)) return undefined;\n const content = readFileSync(file, \"utf8\");\n if (t.kind === \"agents\" || t.kind === \"skills\") {\n const fm = extractFrontmatter(content) ?? \"\";\n const m = /^description:\\s*(.+)$/m.exec(fm);\n const raw = m?.[1]?.trim().replace(/^[\"']|[\"']$/g, \"\");\n return raw ? clip(raw) : undefined;\n }\n if (t.kind === \"hooks\") {\n // shebang 다음의 주석 줄들 — 첫 빈 줄이나 코드가 나오기 전까지\n const lines = content.split(\"\\n\").slice(1);\n const comment: string[] = [];\n for (const l of lines) {\n if (!l.startsWith(\"#\")) break;\n comment.push(l.replace(/^#\\s?/, \"\"));\n }\n return comment.length > 0 ? clip(comment.join(\" · \")) : undefined;\n }\n // rules — `# 제목` + 첫 본문 줄\n const body = content.split(\"\\n\");\n const heading = body\n .find((l) => l.startsWith(\"# \"))\n ?.slice(2)\n .trim();\n const first = body\n .slice(body.findIndex((l) => l.startsWith(\"# \")) + 1)\n .find((l) => l.trim() !== \"\")\n ?.replace(/^[-*]\\s+/, \"\")\n .replace(/\\*\\*/g, \"\");\n if (!heading) return undefined;\n return clip(first ? `${heading} · ${first}` : heading);\n}\n\n/** 목록 전체에 설명을 붙인다 — 위저드가 부른다. 파일이 없으면 그 행은 이름만 남는다. */\nexport function withBaselineHints(\n targets: ReadonlyArray<BaselineTarget>,\n templatesDir?: string,\n): BaselineTarget[] {\n return targets.map((t) => {\n const hint = describeBaselineTarget(t, templatesDir);\n return hint ? { ...t, hint } : t;\n });\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\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","/**\n * design.ts — CLI visual design tokens (color, symbols, layout helpers).\n *\n * Aesthetic direction: **refined ops-report**. Mission control feel without\n * decoration noise. Phase markers + aligned 2-column rows + structured summary.\n *\n * Goals:\n * 1. Make input-wait points visually obvious (handled by @clack/prompts).\n * 2. Make the install pipeline output legible:\n * - phase-segmented progress (━━━ Phase N · Title ━━━)\n * - aligned per-asset rows (✓/⊘/✗ + id + meta)\n * - explicit skipped/failed reporting (no silent skips)\n * - terminal-width responsive (default 78)\n * 3. Zero runtime dependencies — emit raw ANSI escapes.\n *\n * `NO_COLOR` is honored per https://no-color.org.\n */\n\nconst isColorEnabled = (() => {\n if (process.env.NO_COLOR && process.env.NO_COLOR !== \"\") {\n return false;\n }\n // stdout may be missing in some test contexts\n return Boolean(process.stdout?.isTTY);\n})();\n\nfunction wrap(open: number, close: number) {\n return (s: string): string => {\n if (!isColorEnabled) {\n return s;\n }\n return `\\x1b[${open}m${s}\\x1b[${close}m`;\n };\n}\n\nexport const c = {\n bold: wrap(1, 22),\n dim: wrap(2, 22),\n red: wrap(31, 39),\n green: wrap(32, 39),\n yellow: wrap(33, 39),\n cyan: wrap(36, 39),\n gray: wrap(90, 39),\n};\n\nexport const symbol = {\n success: \"✓\",\n failure: \"✗\",\n skip: \"⊘\",\n arrow: \"›\",\n pointer: \"▸\",\n bullet: \"•\",\n warn: \"⚠\",\n /** Heavy horizontal box-drawing — phase dividers. */\n rule: \"━\",\n /** Middle dot — section separator. */\n mid: \"·\",\n};\n\n/** Default width for phase headers / dividers. Terminal default ≥ 78. */\nexport const DEFAULT_WIDTH = 78;\n\n/** Render a legacy section header. Bold cyan with a leading arrow. (kept for backward compat) */\nexport function header(title: string): string {\n return c.bold(c.cyan(`${symbol.arrow} ${title}`));\n}\n\n/**\n * Render a phase header — `━━━ Phase N · Title ━━━━━━━...` (full-width).\n * v26.63.0 (deprecated): kept for non-interactive mode + backward compat.\n * wizard 모드는 unifiedSection() 사용 (5-step 통합 — phase 카운터 무관).\n */\nexport function phaseHeader(n: number | string, title: string, width = DEFAULT_WIDTH): string {\n const label = `${symbol.rule}${symbol.rule}${symbol.rule} Phase ${n} ${symbol.mid} ${title} `;\n const fill = symbol.rule.repeat(Math.max(0, width - visibleLength(label)));\n return c.bold(c.cyan(`${label}${fill}`));\n}\n\n/**\n * v26.63.0 — Step 5 (Installing) 안의 sub-section 헤더.\n * `━━ Templates ━━` / `━━ External assets (n) ━━` / `━━ Codex artifacts ━━`.\n * phaseHeader (3 rule + Phase N + ·) 대비 단순 — 2 rule + title.\n */\nexport function unifiedSection(title: string, width = DEFAULT_WIDTH): string {\n const label = `${symbol.rule}${symbol.rule} ${title} `;\n const fill = symbol.rule.repeat(Math.max(0, width - visibleLength(label)));\n return c.bold(c.cyan(`${label}${fill}`));\n}\n\n/**\n * Render a section header (non-phase) — `━━━ Title ━━━━━━━...`.\n * Used for TARGET / SUMMARY / NEXT etc.\n */\nexport function sectionHeader(title: string, width = DEFAULT_WIDTH): string {\n const label = `${symbol.rule}${symbol.rule}${symbol.rule} ${title} `;\n const fill = symbol.rule.repeat(Math.max(0, width - visibleLength(label)));\n return c.bold(c.cyan(`${label}${fill}`));\n}\n\n/** Plain horizontal divider — `━━━...━━━` (no label). */\nexport function divider(width = DEFAULT_WIDTH): string {\n return c.dim(symbol.rule.repeat(width));\n}\n\n/**\n * Render a `key: value` row with a fixed-width left column for alignment.\n * Used in pre-flight / summary blocks (▸ TRACKS executive, tooling).\n */\nexport function infoRow(key: string, value: string, width = 12): string {\n const label = `${symbol.pointer} ${key}`.padEnd(width + 2, \" \");\n return ` ${c.dim(label)} ${value}`;\n}\n\n/** Backward-compat — `keyValue` (used by older install report). */\nexport function keyValue(key: string, value: string, width = 16): string {\n const padded = `${key}:`.padEnd(width, \" \");\n return ` ${c.dim(padded)} ${value}`;\n}\n\n/**\n * Render an asset row — ` ✓ asset-id meta`.\n * symbol = success/skip/failure. label = stable id (left-pad). meta = dim right column.\n */\nexport function assetRow(\n kind: \"success\" | \"skip\" | \"failure\",\n label: string,\n meta = \"\",\n labelWidth = 40,\n): string {\n const sym = renderSymbol(kind);\n const labelPadded = label.padEnd(labelWidth, \" \");\n const metaText = meta ? c.dim(meta) : \"\";\n return ` ${sym} ${labelPadded} ${metaText}`.trimEnd();\n}\n\nfunction renderSymbol(kind: \"success\" | \"skip\" | \"failure\"): string {\n switch (kind) {\n case \"success\":\n return c.green(symbol.success);\n case \"skip\":\n return c.yellow(symbol.skip);\n case \"failure\":\n return c.red(symbol.failure);\n }\n}\n\nexport const status = {\n success: (msg: string): string => `${c.green(symbol.success)} ${msg}`,\n failure: (msg: string): string => `${c.red(symbol.failure)} ${msg}`,\n warn: (msg: string): string => `${c.yellow(symbol.warn)} ${msg}`,\n info: (msg: string): string => `${c.cyan(symbol.bullet)} ${msg}`,\n};\n\n/**\n * Strip ANSI escape sequences so visible width can be measured (for header padding).\n */\nfunction visibleLength(s: string): number {\n // biome-ignore lint/suspicious/noControlCharactersInRegex: ANSI escape stripping requires \\x1b\n return s.replace(/\\x1b\\[[0-9;]*m/g, \"\").length;\n}\n\n/**\n * v26.63.2 — Pad to fixed display width (ANSI-aware). spacing scale 정렬 용.\n */\nexport function padDisplay(s: string, width: number): string {\n const visible = visibleLength(s);\n return visible >= width ? s : s + \" \".repeat(width - visible);\n}\n","import {\n chmodSync,\n existsSync,\n mkdirSync,\n readdirSync,\n readFileSync,\n writeFileSync,\n} from \"node:fs\";\nimport { basename, dirname, join, resolve } from \"node:path\";\nimport type { AntigravityTransformReport } from \"./antigravity/transform.js\";\nimport { isBaselineExcluded } from \"./baseline-targets.js\";\nimport { type CiScaffoldReport, installCiScaffold } from \"./ci-scaffold.js\";\nimport { runCliTransforms } from \"./cli-transforms.js\";\nimport type { CodexOptInReport } from \"./codex/opt-in.js\";\nimport type { CodexTransformReport } from \"./codex/transform.js\";\nimport { addGitignoreAgentArtifacts, addGitignoreEnv, writeEnvExample } from \"./env-files.js\";\nimport { EXTERNAL_ASSETS, isAssetSelected } from \"./external-assets.js\";\nimport {\n type ExternalInstallerDeps,\n type ExternalInstallReport,\n runExternalInstall,\n selectExternalTargets,\n} from \"./external-installer.js\";\nimport { foreignOwnedTarget } from \"./foreign-slot.js\";\nimport {\n backupDir,\n backupFile,\n backupFileIfChanged,\n copyBackupDir,\n copyDir,\n copyFile,\n ensureProjectSkeleton,\n} from \"./fs-ops.js\";\nimport {\n buildInstallLog,\n collectPolicyHashes,\n collectSkillHashes,\n hashContent,\n type InstallLog,\n type InstallLogRootFile,\n type InstallLogSkillFile,\n mergeExternalFiles,\n POLICY_DIRS,\n readInstallLog,\n writeInstallLog,\n} from \"./install-log.js\";\nimport {\n type AssetSpec,\n buildAssetSpec,\n buildManifest,\n isCliNeutralTarget,\n resolveRules,\n} from \"./manifest.js\";\nimport { composeMcpJson, writeMcpJson } from \"./mcp-merge.js\";\nimport type { OpencodeTransformReport } from \"./opencode/transform.js\";\nimport { HARNESS_ANCHOR_FILE, upsertHarnessImport } from \"./project-claude-merge.js\";\nimport { type InstallSpec, type OptionFlags, resolveScope, type Track } from \"./types.js\";\nimport { cleanStaleHookRefs, runUpdateMode, type UpdateModeReport } from \"./update-mode.js\";\n\n/**\n * Install mode — Router action 매핑.\n * - \"fresh\" : 첫 설치 (기본값)\n * - \"add\" : 기존 위에 Track union 추가 (backup 없음)\n * - \"update\" : 정책 파일만 templates로 갱신 (backup + orphan prune + stale hook)\n * - \"reinstall\" : 기존 .claude/ backup 후 처음부터 (backup 강제)\n */\nexport type InstallMode = \"fresh\" | \"add\" | \"update\" | \"reinstall\";\n\n/**\n * 각 mode 를 **비대화형으로 도달하는 CLI 명령** (없으면 null = 위저드 전용).\n *\n * 왜 코드로 두나: `install` 은 플래그로 되는데 `update` 는 위저드로만 되던 상태가 오래\n * 방치됐다. \"CI 로 깔 수는 있는데 갱신할 수는 없다\"는 수요 문제가 아니라 계열 비대칭이고,\n * 사람이 매번 계열 전체를 기억해서 대조해야 하면 그 규약은 이미 실패한 것이다.\n *\n * `Record<InstallMode, ...>` 라서 **mode 를 추가하면 여기 분류하기 전에는 컴파일이 안 된다.**\n * null 을 고르는 건 허용하지만 그 순간 \"위저드 전용\"이 명시적 선언이 되고, 아래 테스트가\n * 그 목록을 화면에 내보낸다 — 침묵으로 빠지는 경로가 없다.\n */\nexport const MODE_ENTRY_POINT: Record<InstallMode, string | null> = {\n fresh: \"install\",\n // 기존 설치 위에 `install --track <new>` = add. mode 는 헤더 라벨만 다르고 동작은 fresh 와 같다\n // (backup 없음 · manifest copy 동일) — 별도 명령이 필요 없다.\n add: \"install\",\n update: \"update\",\n // 미제공 — `.claude/` 를 통째로 backup 으로 **옮기는** 파괴적 경로다. 비대화형 진입점을\n // 붙일지는 별도 판단 사항이라 열어둔다 (열어둔 것 자체가 이 표에 보인다).\n reinstall: null,\n};\n\nexport interface InstallContext {\n /** Path to the harness repo (where `templates/` lives). */\n harnessRoot: string;\n /** Target project directory. */\n projectDir: string;\n spec: InstallSpec;\n /**\n * Router action mode. Defaults to \"fresh\".\n * - \"add\"/\"update\"/\"reinstall\" trigger different install paths.\n * - reinstall + update force backup=true.\n */\n mode?: InstallMode;\n /**\n * When true, an existing .claude/ is renamed to a timestamped backup before install.\n * Auto-true when mode ∈ {update, reinstall}.\n */\n backup?: boolean;\n /**\n * External install (claude plugin / npm -g / npx skills) injection point.\n * Default: real `runExternalInstall`. Tests inject mock to avoid real spawn.\n * Pass `null` to disable external install entirely.\n */\n runExternal?:\n | ((\n // v26.77.0 — projectDir: 외부 설치기 spawn cwd (자산 착지 위치). Bug B fix.\n // v26.81.0 (ADR-022) — userOverride: 자산 opt-in(--with <id>) 전파 (flag 13종 대체).\n ctx: {\n tracks: ReadonlyArray<Track>;\n options: OptionFlags;\n projectDir?: string;\n userOverride?: {\n forceInclude: ReadonlyArray<string>;\n forceExclude: ReadonlyArray<string>;\n };\n },\n deps: ExternalInstallerDeps,\n ) => ExternalInstallReport)\n | null;\n /**\n * Progress callback fired between stages so renderers can stream output\n * (avoids \"Phase 1 header → 5 minutes silence\" UX problem).\n */\n onProgress?: (event: ProgressEvent) => void;\n /** External installer streaming hooks (forwarded to runExternalInstall). */\n externalDeps?: Pick<ExternalInstallerDeps, \"onAssetStart\" | \"onAssetResult\">;\n}\n\n/** Progress event types fired during runInstall. */\nexport type ProgressEvent =\n /** Baseline (manifest copy + mcp + envFiles + Codex/OpenCode transforms) finished. External not yet started. */\n | { type: \"baseline-complete\"; baseline: BaselineReport }\n /** External install phase about to begin. */\n | { type: \"external-start\"; assetCount: number }\n /** External install phase finished (with report). */\n | { type: \"external-complete\"; report: ExternalInstallReport }\n /** v26.64.0 — install log write 실패 (non-fatal). */\n | { type: \"install-log-error\"; message: string };\n\n/**\n * v0.6.1 — Phase 1 output 카테고리별 분류. install renderer가 각 카테고리별로 row를 출력한다.\n * Names는 description용 (display only); 빈 배열이면 row 출력 skip.\n */\nexport interface BaselineCategoryCounts {\n /** rule 파일 names (확장자 제외) — git-policy, change-management 등 */\n rules: string[];\n /** agent 파일 names */\n agents: string[];\n /** hook 파일 names (확장자 제외) */\n hooks: string[];\n /** commands 디렉토리 카운트 (uzys + ecc) — names는 디렉토리라 무의미 */\n commands: number;\n /** skill 디렉토리 names */\n skills: string[];\n}\n\n/** Baseline phase result (everything except external assets). */\nexport interface BaselineReport {\n filesCopied: number;\n dirsCopied: number;\n skipped: number;\n backup: string | null;\n installedTracks: string[];\n mcpServers: string[];\n codex: CodexTransformReport | null;\n codexOptIn: CodexOptInReport | null;\n opencode: OpencodeTransformReport | null;\n /** v26.66.0 — Present when spec.cli includes \"antigravity\". */\n antigravity: AntigravityTransformReport | null;\n updateMode: UpdateModeReport | null;\n mode: InstallMode;\n envFiles: {\n envExampleCreated: boolean;\n gitignoreEnvAdded: boolean;\n /**\n * v0.8.0 — `.gitignore`에 추가된 자동 생성물 디렉토리 패턴\n * (`.factory/`, `.goose/`, 2026-08-02부터 `.uzys-agent-harness/`).\n * 필드명은 v0.8.0 당시 범위(npx skills)를 그대로 쓴다 — 개명은 표면 3곳을 함께 건드린다.\n */\n gitignoreNpxSkillsAdded: string[];\n };\n /**\n * v26.108.0 (ADR-037) — CI 스캐폴드 결과. opt-in 미선택 시 null. `.github/workflows/`\n * 는 CLI-agnostic 이라 claude baseline 밖의 전용 단계 (ci-scaffold.ts) 가 설치 주체.\n */\n ciScaffold: CiScaffoldReport | null;\n /** v0.6.1 — Phase 1 카테고리별 카운트 + names. Update mode에서는 빈 객체. */\n categories?: BaselineCategoryCounts;\n /**\n * Root CLAUDE.md 처리 결과. null when claude baseline disabled.\n * `created` = 없던 파일을 fill-in 스캐폴드로 만들었다. false 면 기존 사용자 파일에 앵커\n * import 한 줄만 얹었다는 뜻 — 두 경우의 보고 문구가 달라야 한다 (스캐폴드를 쓰지도 않고\n * \"fill-in scaffold\" 라고 보고하면 그게 거짓 보고다).\n */\n rootClaudeMd: { tracks: ReadonlyArray<Track>; created: boolean } | null;\n /**\n * 2026-08-16 — 사용자가 위저드에서 체크를 푼 트랙 자산의 대상 경로.\n *\n * 화면에 내는 이유는 설치 화면이 **무엇이 깔렸는가**만 말하면 해제가 먹혔는지 확인할 길이\n * 없기 때문이다. 0건이면 아무것도 안 뜬다.\n */\n baselineExcluded: string[];\n /** `baselineExcluded` 중 **디스크에 그대로 남은** 것 (`add`·`reinstall`). 화면이 이걸 표시한다. */\n baselineExcludedOnDisk: string[];\n /**\n * #343 — 깔릴 자리가 디렉터리가 아니라 건너뛴 대상 (`.claude/` 포함 상대경로).\n * 화면에 이름을 내지 않으면 사용자는 **고른 자산이 왜 없는지** 알 방법이 없다.\n */\n baselineForeignOwned: string[];\n /** 덮어쓰기 전 보존한 사용자 파일 백업 경로 (settings.json·CLAUDE.md, fresh/add 모드). audit SEC-1/CODE-2. */\n backups?: string[];\n}\n\nexport interface InstallReport {\n filesCopied: number;\n dirsCopied: number;\n skipped: number;\n backup: string | null;\n installedTracks: string[];\n mcpServers: string[];\n /** Present when spec.cli includes \"codex\". */\n codex: CodexTransformReport | null;\n /** Present when Codex transform ran AND user opted-in to global skills/trust/prompts. null otherwise. */\n codexOptIn: CodexOptInReport | null;\n /** Present when spec.cli includes \"opencode\". */\n opencode: OpencodeTransformReport | null;\n /** v26.66.0 — Present when spec.cli includes \"antigravity\". */\n antigravity: AntigravityTransformReport | null;\n /** v26.108.0 (ADR-037) — CI 스캐폴드 결과 (opt-in 미선택 시 null). */\n ciScaffold: CiScaffoldReport | null;\n /** External install report (claude plugin / npm -g / npx skills). null when disabled or empty. */\n external: ExternalInstallReport | null;\n /** Update-mode report (rules/agents/commands/hooks/skills 갱신 + orphan prune + stale hook). null when not update mode. */\n updateMode: UpdateModeReport | null;\n /**\n * M-1 — settings.json 이 가리키는 없는 스크립트를 지운 결과 (`.claude/` 기준 상대경로).\n * install 은 settings.json 을 매번 템플릿으로 덮어쓰므로 치유도 매번 다시 해야 한다.\n * claude 미선택 시 `.claude/settings.json` 자체가 없어 항상 `[]`.\n */\n staleHookRefs: string[];\n /**\n * 2026-08-16 — 사용자가 위저드 3단계에서 **체크를 푼** 트랙 자산의 대상 경로.\n *\n * `BaselineReport` 와 같은 필드를 여기 다시 선언하는 이유는 두 타입이 별개이기 때문이다\n * (런타임은 `{...baseline}` 로 이미 흐른다). 타입에만 없으면 호출자가 결과를 못 읽고,\n * 그러면 \"해제가 먹혔는지\" 를 프로그램으로 확인할 방법이 사라진다.\n */\n baselineExcluded: string[];\n /** `baselineExcluded` 중 디스크에 남은 것. `BaselineReport` 와 같은 이유로 여기도 선언한다. */\n baselineExcludedOnDisk: string[];\n /** 자리가 디렉터리가 아니라 건너뛴 대상. `BaselineReport` 와 같은 이유로 여기도 선언한다. */\n baselineForeignOwned: string[];\n /** Install mode dispatched (echo of ctx.mode, default \"fresh\"). */\n mode: InstallMode;\n /** Environment file generation results (always present). */\n envFiles: {\n /** true if .env.example was created (csr-supabase/full only). */\n envExampleCreated: boolean;\n /** true if .gitignore got `.env` line appended. */\n gitignoreEnvAdded: boolean;\n /**\n * v0.8.0 — `.gitignore`에 추가된 자동 생성물 디렉토리 패턴\n * (`.factory/`, `.goose/`, 2026-08-02부터 `.uzys-agent-harness/`).\n * 필드명은 v0.8.0 당시 범위(npx skills)를 그대로 쓴다 — 개명은 표면 3곳을 함께 건드린다.\n */\n gitignoreNpxSkillsAdded: string[];\n };\n}\n\n/**\n * Run the installation pipeline. Pure function modulo filesystem side effects.\n * v26.82.0 (Phase R) — 276줄 단일 함수를 단계별 블록 함수로 분해 (동작 변경 0):\n * update 단축 / claude baseline / CLI transforms / external / install log.\n */\nexport function runInstall(ctx: InstallContext): InstallReport {\n const { harnessRoot, projectDir, spec } = ctx;\n const mode: InstallMode = ctx.mode ?? \"fresh\";\n const templatesDir = join(harnessRoot, \"templates\");\n\n if (!existsSync(templatesDir)) {\n throw new Error(`Templates dir not found: ${templatesDir}`);\n }\n\n const claudeDir = join(projectDir, \".claude\");\n\n // v26.123.0 (F-1a) — 추가 설치가 이전 설치 기록을 지우지 않도록 기존 로그를 먼저 읽는다.\n // reinstall 은 아래에서 `.claude/` 를 통째로 backup 으로 옮기므로 그 뒤엔 읽을 수 없다.\n const previousLog = readInstallLog(projectDir);\n\n // Update mode pre-flight — 갱신할 **설치**가 있어야 한다. backup 전에 검증.\n //\n // 판정 기준은 `.claude/` 가 아니다. update 는 v26.134.0(ADR-049)부터 외부 CLI 산출물도\n // 갱신하므로 `.claude/` 가 없는 codex/opencode/antigravity 단독 설치도 정당한 대상이고,\n // `src/commands/update.ts` 의 pre-flight 는 이미 그렇게 판정한다(#253). **파이프라인만\n // `.claude/` 를 요구해 그 사용자를 거절하고 있었다** — 명령은 통과시키고 파이프라인이\n // throw 하니, 비 Claude 단독 사용자는 새 자산을 받을 길이 재설치뿐이었다(독립 검증 C-2c).\n // 설치의 CLI 중립 증거는 install log 다.\n //\n // 단, **claude 를 고른 설치인데 `.claude/` 가 없으면** 그건 정상 상태가 아니라 깨진 설치다 —\n // 그대로 진행하면 룰만 복원되고 `settings.json`·훅이 없는 반쪽 `.claude/` 가 만들어진다\n // (독립 재검증 M-R2). 그 경우는 예전처럼 막고 재설치로 보낸다.\n const claudeWasSelected = previousLog?.spec.cli.includes(\"claude\") ?? false;\n if (mode === \"update\" && !existsSync(claudeDir) && (previousLog === null || claudeWasSelected)) {\n // 두 상황을 같은 문장으로 말하지 않는다 — 하나는 \"깔린 게 없다\", 다른 하나는 \"깔렸는데\n // 일부가 사라졌다\"이고, 사용자가 할 일이 다르다. 후자를 \"설치가 없다\"고 하면 로그를 눈으로\n // 본 사람은 도구가 틀렸다고 생각한다.\n throw new Error(\n claudeWasSelected\n ? `Update mode found a broken install at ${projectDir} — this project installed Claude Code assets but \\`.claude/\\` is gone. Reinstall instead: agent-harness install --track <name>`\n : `Update mode requires an existing install at ${projectDir}`,\n );\n }\n\n const backupPath = resolveBackupPath(ctx, mode, claudeDir);\n\n // Update mode 단축 — 정책 파일만 갱신하고 종료 (manifest copy / external 모두 skip)\n if (mode === \"update\") {\n return runUpdateInstall(ctx, templatesDir, backupPath);\n }\n\n const manifestSpec = buildManifestSpec(spec);\n\n // v0.8.0 — `.claude/` baseline은 spec.cli에 \"claude\" 포함 시에만 생성.\n // Codex/OpenCode 단독 사용자는 dead weight 회피.\n // ADR-047 — 덮어쓰기 전 소유 판정에 쓸 기준선. `.claude/` 를 옮겨낸 뒤(reinstall)엔 대조할\n // 대상이 없으므로 previousLog 를 그대로 쓰되, 그 경우 아래 existsSync 가 자연히 걸러낸다.\n const policyBase = new Map((previousLog?.policyFiles ?? []).map((f) => [f.path, f.sha256]));\n\n // 위저드 3단계에서 사용자가 **해제한** 트랙 자산. 비어 있으면(기본) 아무것도 안 거른다.\n const baselineExcluded = new Set(spec.baselineExclude ?? []);\n\n const base = spec.cli.includes(\"claude\")\n ? installClaudeBaseline(manifestSpec, projectDir, templatesDir, policyBase, baselineExcluded)\n : // claude 미선택이어도 CLI 중립 자산은 깔린다. manifest 전체가 `.claude/` baseline 안에서만\n // 돌던 탓에 이 자산들이 claude 설치에만 도달했는데, **배포 룰 본문이 이 스크립트들을\n // 호출 지점으로 지목한다** — 즉 없는 도구를 있다고 안내하고 있었다(#300 과 같은 형태).\n installCliNeutralAssets(manifestSpec, projectDir, templatesDir, baselineExcluded);\n\n // Compose .mcp.json from template + track-mcp-map.tsv (Codex/OpenCode도 사용 — claude 무관)\n const mcpResult = composeAndWriteMcp(harnessRoot, projectDir, spec);\n\n // v26.108.0 (ADR-037) — CI 스캐폴드 (opt-in 전용). `.github/` 은 CLI-agnostic 이라\n // claude baseline 조건 밖에서 설치. 기존 워크플로 파일은 절대 덮어쓰지 않는다.\n const ciScaffold = isAssetSelected(\"ci-scaffold\", {\n tracks: spec.tracks,\n options: spec.options,\n ...(spec.userOverride ? { userOverride: spec.userOverride } : {}),\n })\n ? installCiScaffold({ harnessRoot, projectDir, tracks: spec.tracks })\n : null;\n\n // v26.133.0 (ADR-048) — 외부 CLI transform 도 소유자 판정을 받는다. 기준선은 `.claude/` 와\n // 별도 필드(`externalFiles`)다: 저기는 templates 복사라 사후에 디스크를 훑어 만들지만\n // (`collectPolicyHashes`), 여기는 **렌더 결과**라 훑어서는 무엇이 하네스 것인지 알 수 없다.\n const {\n externalFiles,\n externalBackups,\n externalUpdated: _externalUpdated,\n externalBackedUp: _externalBackedUp,\n externalForeignOwned,\n ...cliTransforms\n } = runCliTransforms({\n harnessRoot,\n projectDir,\n cli: spec.cli,\n selectedInternalSkills: manifestSpec.selectedInternalSkills,\n // 룰 목록의 SSOT 는 하나다 — `.claude/rules/` 를 채우는 것과 같은 `resolveRules` 결과가\n // 나머지 세 CLI 로도 간다. 여기서 다시 고르면 CLI 마다 다른 룰이 깔린다.\n rules: resolveRules(manifestSpec).filter(\n (r) => !isBaselineExcluded(`.claude/rules/${r}.md`, baselineExcluded),\n ),\n previousExternal: previousLog?.externalFiles ?? [],\n codexTrust: (spec.scope ?? \"project\") === \"global\" && spec.options.withCodexTrust,\n });\n\n const baseline: BaselineReport = {\n filesCopied: base.filesCopied,\n dirsCopied: base.dirsCopied,\n skipped: base.skipped,\n backup: backupPath,\n installedTracks: [...spec.tracks].sort(),\n mcpServers: Object.keys(mcpResult.mcpServers).sort(),\n ...cliTransforms,\n ciScaffold,\n updateMode: null,\n mode,\n envFiles: writeEnvironmentFiles(projectDir, spec.tracks),\n categories: base.categories,\n rootClaudeMd: base.rootClaudeMd,\n // 외부 CLI 백업도 같은 줄에 노출한다 — 백업이 화면에 안 보이면 사용자는 자기 편집분이\n // 어디 갔는지 알 수 없고, 그러면 백업은 있어도 없는 것과 같다 (ADR-046/047 과 같은 이유).\n backups: [...base.backups, ...externalBackups],\n baselineExcluded: base.excluded,\n baselineExcludedOnDisk: base.excludedOnDisk,\n // `.claude/` baseline 과 외부 CLI 산출물의 같은 판정을 **한 목록으로** 낸다.\n baselineForeignOwned: [\n ...base.foreignOwned,\n ...externalForeignOwned.filter((f) => !base.foreignOwned.includes(f)),\n ],\n };\n\n // ━━━ Baseline complete — emit progress event so renderer can show Phase 1 rows ━━━\n ctx.onProgress?.({ type: \"baseline-complete\", baseline });\n\n // ━━━ External assets (claude plugin / npm -g / npx skills) ━━━\n const external = runExternalPhase(ctx);\n\n // ━━━ M-1 — settings.json stale hook ref 치유 (baseline·external 뒤 1회) ━━━\n // 여기서 부르는 이유: 앞 단계들이 settings.json 과 참조 대상(스킬/훅 파일)을 모두 확정한\n // 뒤여야 \"무엇이 없는가\"가 답이 된다. 판정하지 않고 **디스크가 답하게 한다** — 설치자에\n // withEcc 사본이 생기지 않는다 (ADR-049 와 같은 형태).\n const staleHookRefs = healStaleHookRefs(spec, projectDir);\n\n // ━━━ v26.64.0 (ADR-020) — Install log write ━━━\n // backupPath 가 있으면 `.claude/` 를 rename 으로 밀어냈다는 뜻 — 그 안에 살던 이전 자산은\n // 실제로 사라졌으므로 누적에서 빠져야 한다 (fresh/add 는 backupPath=null → 전부 유지).\n writeInstallLogSafe(\n ctx,\n externalFiles,\n external,\n base.rootClaudeMdLog,\n previousLog,\n backupPath !== null,\n collectRootFiles(baseline.envFiles, ciScaffold, mcpResult.created),\n );\n\n return { ...baseline, external, staleHookRefs };\n}\n\n/**\n * M-1 — settings.json 이 참조하는 없는 `.claude/**` 스크립트를 제거한다.\n *\n * update 쪽 치유기(`cleanStaleHookRefs`)를 그대로 재사용한다. install 이 settings.json 을\n * 매번 템플릿으로 덮어쓰므로(`copyFile`) 치유는 설치마다 다시 필요하고, 같은 술어를 두 벌\n * 두면 그 사본이 다음 drift 서식지가 된다.\n */\nfunction healStaleHookRefs(spec: InstallSpec, projectDir: string): string[] {\n // claude 미선택이면 `.claude/settings.json` 자체가 없다.\n if (!spec.cli.includes(\"claude\")) return [];\n const settingsPath = join(projectDir, \".claude/settings.json\");\n if (!existsSync(settingsPath)) return [];\n return cleanStaleHookRefs(settingsPath, join(projectDir, \".claude\"));\n}\n\n/**\n * Backup auto-on for update + reinstall (sourced from router action).\n * Update: copy backup (preserve original .claude/ for in-place update).\n * Reinstall + others: rename backup (move .claude/ aside, then full install).\n */\nfunction resolveBackupPath(\n ctx: InstallContext,\n mode: InstallMode,\n claudeDir: string,\n): string | null {\n const wantBackup = ctx.backup ?? (mode === \"update\" || mode === \"reinstall\");\n if (!wantBackup) return null;\n return mode === \"update\" ? copyBackupDir(claudeDir) : backupDir(claudeDir);\n}\n\n/**\n * Update mode 단축 경로 — manifest copy 는 건너뛴다.\n *\n * 갱신 대상은 ⓐ 정책 파일 ⓑ 외부 CLI 산출물(ADR-049) ⓒ **설치 기록에 적힌 외부 스킬**(#374).\n * 세 번째가 오래 빠져 있었고, 그동안 화면은 다 갱신된 것처럼 보였다.\n */\nfunction runUpdateInstall(\n ctx: InstallContext,\n templatesDir: string,\n backupPath: string | null,\n): InstallReport {\n const updateReport = runUpdateMode(\n ctx.projectDir,\n templatesDir,\n ctx.harnessRoot,\n {},\n ctx.spec.updateOnly,\n );\n const baseline: BaselineReport = {\n filesCopied: 0,\n dirsCopied: 0,\n skipped: 0,\n baselineExcluded: [],\n baselineExcludedOnDisk: [],\n baselineForeignOwned: [],\n backup: backupPath,\n installedTracks: [...ctx.spec.tracks].sort(),\n mcpServers: [],\n codex: null,\n codexOptIn: null,\n opencode: null,\n antigravity: null,\n ciScaffold: null,\n updateMode: updateReport,\n mode: \"update\",\n envFiles: {\n envExampleCreated: false,\n gitignoreEnvAdded: false,\n gitignoreNpxSkillsAdded: [],\n },\n rootClaudeMd: null,\n };\n ctx.onProgress?.({ type: \"baseline-complete\", baseline });\n // update 경로의 치유 결과는 `updateMode.staleHookRefs` 가 이미 싣는다 — 여기서 다시 담으면\n // 같은 사실이 두 필드가 되고 렌더가 중복 보고한다.\n return { ...baseline, external: null, staleHookRefs: [] };\n}\n\n/**\n * v26.81.0 (ADR-022) — manifest 게이팅 입력. 내부 자산 선택 판정 — 이전\n * OptionFlags.withTauri/withUzysHarness/withEcc boolean 자리를 카탈로그 선택\n * (wizard 체크 / --with <id> → forceInclude)으로 대체 (manifest 필드명은 유지).\n */\nexport function buildManifestSpec(spec: InstallSpec): Required<AssetSpec> {\n // derive 본체는 `manifest.ts` 의 `buildAssetSpec` 하나다 (#320) — 계측 경로가 같은 것을 부른다.\n // 여기 다시 조립하면 그 순간 사본이 둘이 되고, 그게 #320 의 원인이었다.\n return buildAssetSpec({\n tracks: spec.tracks,\n options: spec.options,\n ...(spec.userOverride ? { userOverride: spec.userOverride } : {}),\n });\n}\n\n/** `.claude/` baseline (manifest copy) 결과. claude 미선택 시 emptyClaudeBaseline(). */\ninterface ClaudeBaselineResult {\n filesCopied: number;\n dirsCopied: number;\n skipped: number;\n categories: BaselineCategoryCounts;\n rootClaudeMd: { tracks: ReadonlyArray<Track>; created: boolean } | null;\n /** 하네스 앵커 파일 무결성 기록 — uninstall 시 사용자 수정 여부 판별 (install 원본과 sha 비교). */\n rootClaudeMdLog: { path: string; sha256: string } | null;\n /** 덮어쓰기 전 보존한 사용자 파일 백업 경로 (settings.json·CLAUDE.md). audit SEC-1/CODE-2. */\n backups: string[];\n /**\n * 2026-08-16 — 사용자가 위저드에서 **체크를 푼** 자산의 대상 경로.\n *\n * `skipped` 와 나눈다: 저건 \"원본이 없어서 못 깔았다\"는 결함 신호이고 이건 정상 선택이다.\n * 한 숫자에 담으면 설치 화면이 사용자의 선택을 결함으로 보고한다.\n */\n excluded: string[];\n /**\n * 해제했는데 **디스크에 그대로 남아 있는** 대상 (`excluded` 의 부분집합).\n *\n * `add`·`reinstall` 은 이전 설치본을 지우지 않으므로 \"제외됨\"만 찍으면 화면이 디스크와 다른\n * 말을 한다 — 사용자는 파일이 사라진 줄 알고, 실제로는 그 룰이 계속 상주한다. 체크 해제는\n * 제거가 아니라는 기존 규약(v26.125.0 `● installed` 마커)을 baseline 항목에도 적용한다.\n */\n excludedOnDisk: string[];\n /**\n * #343 — 깔릴 자리가 **디렉터리가 아닌 것**으로 이미 차 있어 건너뛴 대상.\n *\n * `excluded`(사용자가 체크를 풂) · `skipped`(원본 부재)와 셋 다 다른 사실이라 따로 센다:\n * 이건 **디스크 쪽 사정**이고, 사용자가 고를 때는 보이지 않던 것이다.\n */\n foreignOwned: string[];\n}\n\nfunction emptyClaudeBaseline(): ClaudeBaselineResult {\n return {\n filesCopied: 0,\n dirsCopied: 0,\n skipped: 0,\n categories: { rules: [], agents: [], hooks: [], commands: 0, skills: [] },\n rootClaudeMd: null,\n rootClaudeMdLog: null,\n backups: [],\n excluded: [],\n excludedOnDisk: [],\n foreignOwned: [],\n };\n}\n\n/**\n * CLI 중립 자산(`.uzys-agent-harness/`)만 설치한다 — claude 를 고르지 않은 설치용.\n *\n * manifest 는 통째로 `.claude/` baseline 안에서만 돌았고, 그래서 `protect-branch.sh` ·\n * `spec-drift-check.sh` 는 두 entry 의 주석이 \"CLI 중립 슬롯\"이라 적어 두었음에도 claude\n * 설치에만 도달했다. 배포 룰 본문이 이 스크립트들을 호출 지점으로 지목하므로, 도달하지 않으면\n * 룰이 **없는 도구를 있다고 안내**하게 된다 (#300 과 같은 형태).\n *\n * `.claude/` 를 만들지 않는 것이 이 함수의 존재 이유다 — 스켈레톤·훅 chmod·루트 CLAUDE.md\n * 병합은 전부 claude 전용이라 여기서 하지 않는다.\n *\n * **해제한 룰은 여기서도 보고한다** (ADR-074). 이 경로에서도 제외는 실제로 작동한다 —\n * `runCliTransforms` 로 가는 `rules` 가 걸러지므로 `AGENTS.md`·`.agents/rules/` 에서 빠진다.\n * 그런데 보고가 없으면 **제외가 가장 안 보이는 곳에서 화면도 침묵한다**: 눈으로 확인할\n * `.claude/rules/` 디렉터리조차 없는 설치다. 룰만 세는 이유는 룰이 `.claude/` 밖으로 나가는\n * 유일한 baseline 종류이기 때문이다(에이전트·훅·트랙 스킬은 비 Claude 표면이 없다).\n */\nfunction installCliNeutralAssets(\n manifestSpec: Required<AssetSpec>,\n projectDir: string,\n templatesDir: string,\n baselineExcluded: ReadonlySet<string>,\n): ClaudeBaselineResult {\n const result = emptyClaudeBaseline();\n for (const entry of buildManifest(manifestSpec)) {\n if (!entry.applies(manifestSpec)) continue;\n if (\n entry.target.startsWith(\".claude/rules/\") &&\n isBaselineExcluded(entry.target, baselineExcluded)\n ) {\n result.excluded.push(entry.target);\n continue;\n }\n if (!isCliNeutralTarget(entry.target)) continue;\n const source = join(templatesDir, entry.source);\n if (!existsSync(source)) {\n result.skipped += 1;\n continue;\n }\n copyFile(source, join(projectDir, entry.target));\n result.filesCopied += 1;\n }\n return result;\n}\n\n/** `.claude/` baseline — manifest copy + hook chmod + .installed-tracks + root CLAUDE.md merge. */\n/**\n * 정책 파일(rules/agents/commands/hooks)을 덮어쓰기 전 사용자 편집분 보호 (v26.132.0 · ADR-047).\n *\n * 판정은 update 와 **같은 기준선**(install log `policyFiles`)을 쓴다 — 두 명령이 서로 다른\n * 기준으로 \"사용자가 고쳤다\"를 판정하면 한쪽이 백업한 걸 다른 쪽이 조용히 밀 수 있다.\n *\n * @returns 백업 경로. 백업이 불필요했으면 null.\n */\nfunction backupEditedPolicyFile(\n entryTarget: string,\n target: string,\n source: string,\n baseline: ReadonlyMap<string, string>,\n): string | null {\n const prefix = \".claude/\";\n if (!entryTarget.startsWith(prefix)) return null;\n const rel = entryTarget.slice(prefix.length);\n if (!POLICY_DIRS.some(({ dir, ext }) => rel.startsWith(`${dir}/`) && rel.endsWith(ext))) {\n return null;\n }\n if (!existsSync(target)) return null;\n const current = readFileSync(target, \"utf-8\");\n if (current === readFileSync(source, \"utf-8\")) return null; // 이미 최신 — 백업 불필요\n const recorded = baseline.get(rel);\n if (recorded !== undefined && recorded === hashContent(current)) return null; // 하네스가 놓아둔 그대로\n return backupFile(target);\n}\n\nfunction installClaudeBaseline(\n manifestSpec: Required<AssetSpec>,\n projectDir: string,\n templatesDir: string,\n policyBase: ReadonlyMap<string, string>,\n baselineExcluded: ReadonlySet<string>,\n): ClaudeBaselineResult {\n ensureProjectSkeleton(projectDir);\n\n const result = emptyClaudeBaseline();\n const manifest = buildManifest(manifestSpec);\n\n for (const entry of manifest) {\n if (!entry.applies(manifestSpec)) {\n continue;\n }\n const target = join(projectDir, entry.target);\n // 사용자가 3단계에서 체크를 푼 자산. `skipped` 로 세지 않는다 — 저 카운터는 \"원본이 없어서\n // 못 깔았다\"는 결함 신호이고, 이쪽은 사용자가 그러라고 한 것이다. 둘을 한 숫자에 담으면\n // 설치 화면이 정상 선택을 결함으로 보고한다.\n if (isBaselineExcluded(entry.target, baselineExcluded)) {\n result.excluded.push(entry.target);\n // 이미 있던 파일은 지우지 않는다(체크 해제 ≠ 제거). 그 사실을 여기서 잡아 두지 않으면\n // `add`·`reinstall` 화면이 \"제외됨\"이라 적고 파일은 그대로 남는다 — 화면이 디스크와\n // 다른 말을 하는 것이고, 그게 이 PR 이 없애려던 상태다.\n if (existsSync(target)) result.excludedOnDisk.push(entry.target);\n continue;\n }\n const source = join(templatesDir, entry.source);\n if (!existsSync(source)) {\n result.skipped += 1;\n continue;\n }\n // #343 — 남의 도구가 소유한 스킬 슬롯에는 쓰지 않는다 (판정 SSOT = foreign-slot.ts).\n // 슬롯이 링크인 경우와 슬롯 안 파일이 링크인 경우를 한 술어가 함께 본다.\n const foreign = foreignOwnedTarget(projectDir, entry.target);\n if (foreign !== null) {\n // 슬롯 단위로 한 번만 보고한다 — 사용자가 옮겨야 할 대상은 파일이 아니라 그 자리다.\n if (!result.foreignOwned.includes(foreign)) result.foreignOwned.push(foreign);\n continue;\n }\n if (entry.type === \"file\") {\n // 사용자 편집 가능 파일은 덮어쓰기 전 백업 (audit SEC-1 — settings.json hook/statusLine 소실 방지).\n if (entry.target === \".claude/settings.json\") {\n const backup = backupFileIfChanged(target, readFileSync(source, \"utf-8\"));\n if (backup) {\n result.backups.push(backup);\n }\n } else {\n // v26.132.0 (ADR-047) — 룰·훅·에이전트도 같은 보호를 받는다. 그 전까지 install 이\n // 백업한 건 settings.json 하나뿐이라, 기존 설치 위 `install` 이 사용자가 고친 룰을\n // 흔적 없이 밀었다 (add 모드는 `.claude/` 통짜 백업도 없다 — resolveBackupPath).\n const backup = backupEditedPolicyFile(entry.target, target, source, policyBase);\n if (backup) {\n result.backups.push(backup);\n }\n }\n copyFile(source, target);\n result.filesCopied += 1;\n } else {\n // #343 — 디렉터리 자산은 **파일 단위로** 판정한다. 슬롯이 우리 것이어도 그 **안의 파일**이\n // 링크일 수 있고, 통짜 복사는 그것을 그대로 따라가 남의 파일을 덮었다. 스킬 14종 중\n // 13종이 이 경로(dir 엔트리)라 슬롯 판정만으로는 대부분이 안 막혔다.\n for (const foreign of copyDir(source, target, (relFile) =>\n foreignOwnedTarget(projectDir, `${entry.target}/${relFile}`),\n )) {\n if (!result.foreignOwned.includes(foreign)) result.foreignOwned.push(foreign);\n }\n result.dirsCopied += 1;\n }\n accumulateCategory(result.categories, entry);\n }\n\n // chmod +x on hook scripts (cp does not preserve exec bit when source is non-exec)\n const hookDir = join(projectDir, \".claude/hooks\");\n if (existsSync(hookDir)) {\n chmodHooksSync(hookDir);\n }\n\n // Write metadata file used by detect_install_state on next run (.claude/.installed-tracks)\n writeInstalledTracks(projectDir, manifestSpec.tracks);\n\n // Project root CLAUDE.md — 없으면 fill-in 스캐폴드로 만들고, 있으면 앵커 import 한 줄만 얹는다.\n const rootClaudeMd = writeRootClaudeMd(\n projectDir,\n manifestSpec.tracks,\n manifestSpec.selectedInternalSkills ?? [],\n );\n result.rootClaudeMd = { tracks: manifestSpec.tracks, created: rootClaudeMd.created };\n // 무결성 기록의 대상은 **하네스 앵커 파일**이다 (루트 CLAUDE.md 가 아니다) — uninstall 이\n // 회수하는 것도, update 가 갱신하는 것도 그 파일뿐이라 소유를 주장할 수 있는 것도 그것뿐이다.\n // 방금 manifest copy 가 놓아둔 디스크 내용을 읽는다: 렌더를 다시 하면 기준선이 두 벌이 된다.\n result.rootClaudeMdLog = harnessAnchorLog(projectDir);\n return result;\n}\n\n/** 앵커 파일의 설치 시점 sha. manifest 에서 빠졌거나 source 부재로 skip 됐으면 null (정직 기록). */\nfunction harnessAnchorLog(projectDir: string): { path: string; sha256: string } | null {\n const anchor = join(projectDir, HARNESS_ANCHOR_FILE);\n if (!existsSync(anchor)) return null;\n return { path: HARNESS_ANCHOR_FILE, sha256: hashContent(readFileSync(anchor, \"utf-8\")) };\n}\n\n/** Environment files (F7/F8 — bash setup-harness.sh L880~890 + L954~996 등가). */\nfunction writeEnvironmentFiles(\n projectDir: string,\n tracks: ReadonlyArray<Track>,\n): BaselineReport[\"envFiles\"] {\n return {\n envExampleCreated: writeEnvExample(projectDir, tracks),\n gitignoreEnvAdded: addGitignoreEnv(projectDir),\n // v0.8.0 — `.factory/`, `.goose/` ignore (npx skills universal install 사용자 #3).\n // 2026-08-02 — `.uzys-agent-harness/` 합류 (설치 로그 + 훅 차단 로그).\n gitignoreNpxSkillsAdded: addGitignoreAgentArtifacts(projectDir),\n };\n}\n\n/**\n * External assets (claude plugin / npm -g / npx skills) 설치 단계.\n * Default = real runExternalInstall. Tests inject mock or `null` to skip.\n * log/warn은 silent (renderer가 onAssetStart/Result로 스트리밍).\n */\nfunction runExternalPhase(ctx: InstallContext): ExternalInstallReport | null {\n if (ctx.runExternal === null) {\n return null;\n }\n const { harnessRoot, projectDir, spec } = ctx;\n const runExt = ctx.runExternal ?? runExternalInstall;\n const externalDeps: ExternalInstallerDeps = {\n harnessRoot,\n log: () => {},\n warn: () => {},\n };\n if (ctx.externalDeps?.onAssetStart) {\n externalDeps.onAssetStart = ctx.externalDeps.onAssetStart;\n }\n if (ctx.externalDeps?.onAssetResult) {\n externalDeps.onAssetResult = ctx.externalDeps.onAssetResult;\n }\n const filterCtx = {\n tracks: spec.tracks,\n options: spec.options,\n ...(spec.userOverride ? { userOverride: spec.userOverride } : {}),\n };\n // v26.102.0 (ADR-031) — 헤더 카운트 = 실제 시도될 자산 수. runExternalInstall 과 **같은\n // selector** 를 호출해 \"External assets (N)\" 의 N 이 시도 목록과 구조적으로 일치\n // (이전엔 internal 8종을 포함해 dev 트랙 전부에서 헤더가 과대였다 — SOD 리뷰 F1 실측).\n const applicableCount = selectExternalTargets(EXTERNAL_ASSETS, {\n ...filterCtx,\n cli: spec.cli,\n }).targets.length;\n ctx.onProgress?.({ type: \"external-start\", assetCount: applicableCount });\n const external = runExt(\n { ...filterCtx, cli: spec.cli, projectDir, ...(spec.scope ? { scope: spec.scope } : {}) },\n externalDeps,\n );\n ctx.onProgress?.({ type: \"external-complete\", report: external });\n return external;\n}\n\n/**\n * Install log write — `.uzys-agent-harness/.harness-install.json` (자산 list + scope + timestamp,\n * uninstall command 의 source). 실패는 install 자체를 fail 시키지 않음 (D16 — install 성공 우선).\n */\nfunction writeInstallLogSafe(\n ctx: InstallContext,\n externalFiles: ReadonlyArray<InstallLogSkillFile>,\n external: ExternalInstallReport | null,\n rootClaudeMdLog: { path: string; sha256: string } | null,\n previousLog: InstallLog | null,\n claudeDirMovedAside: boolean,\n rootFiles: ReadonlyArray<InstallLogRootFile>,\n): void {\n try {\n const log = buildInstallLog(\n ctx.spec,\n external,\n resolveScope(ctx.spec.scope),\n rootClaudeMdLog,\n previousLog,\n claudeDirMovedAside,\n rootFiles,\n );\n // v26.126.0 (ADR-046) — 스킬 기준선은 **이력이 아니라 스냅샷**이라 buildInstallLog 의 누적\n // 경로를 타지 않는다. manifest copy 가 끝난 뒤 디스크를 읽어야 값이 맞다.\n const skillFiles = collectSkillHashes(ctx.projectDir, join(ctx.harnessRoot, \"templates\"));\n // v26.132.0 (ADR-047) — 정책 파일 기준선도 같은 이유로 여기서 찍는다. 이게 없으면\n // 다음 update 가 소유를 판정하지 못해 ⓐ 멀쩡한 파일을 전부 백업하고 ⓑ 폐기 룰을 회수 못 한다.\n const policyFiles = collectPolicyHashes(ctx.projectDir, join(ctx.harnessRoot, \"templates\"));\n // v26.133.0 (ADR-048) — 외부 CLI 기준선은 transform 이 **쓰면서 만든 값**이라 여기서 다시\n // 훑지 않는다. 이번에 안 건드린 산출물의 기록은 유지하고(다음 실행이 판정 불가로 떨어지지\n // 않게), 디스크에서 사라진 항목만 뺀다.\n const merged = mergeExternalFiles(ctx.projectDir, previousLog?.externalFiles, externalFiles);\n writeInstallLog(ctx.projectDir, {\n ...log,\n ...(skillFiles.length > 0 ? { skillFiles } : {}),\n ...(policyFiles.length > 0 ? { policyFiles } : {}),\n ...(merged.length > 0 ? { externalFiles: merged } : {}),\n });\n } catch (e) {\n ctx.onProgress?.({\n type: \"install-log-error\",\n message: e instanceof Error ? e.message : String(e),\n });\n }\n}\n\nfunction composeAndWriteMcp(\n harnessRoot: string,\n projectDir: string,\n spec: InstallSpec,\n): { mcpServers: Record<string, unknown>; created: boolean } {\n const mcpPath = join(projectDir, \".mcp.json\");\n // 쓰기 전에 본다 — 쓰고 나면 \"우리가 만든 것\"과 \"사용자 것에 병합한 것\"을 구분할 수 없다.\n const created = !existsSync(mcpPath);\n const composed = composeMcpJson({\n templateMcpPath: join(harnessRoot, \"templates/mcp.json\"),\n trackMapPath: join(harnessRoot, \"templates/track-mcp-map.tsv\"),\n existingPath: mcpPath,\n tracks: spec.tracks,\n });\n writeMcpJson(mcpPath, composed);\n return { ...composed, created };\n}\n\n/**\n * v26.124.0 (F-1f) — 이번 설치가 `.claude/` **밖**에 만들거나 고친 루트 파일 목록.\n *\n * uninstall 은 이걸 지우지 않고 **안내만** 한다 (사용자 내용이 섞임). 그러려면 무엇을 건드렸는지\n * 기록이 있어야 하는데 v26.123.0 까지 아무 기록이 없어서 안내조차 못 했다.\n *\n * **이번 설치가 실제로 바꾼 것만 넣는다** — idempotent skip(이미 있어서 안 건드림)은 넣지 않는다.\n * 이전 설치분은 install-log 의 누적(mergeRootFiles)이 살려 준다.\n */\nfunction collectRootFiles(\n envFiles: BaselineReport[\"envFiles\"],\n ciScaffold: CiScaffoldReport | null,\n mcpCreated: boolean,\n): InstallLogRootFile[] {\n const files: InstallLogRootFile[] = [\n {\n path: \".mcp.json\",\n change: mcpCreated ? \"created\" : \"modified\",\n notes: [mcpCreated ? \"MCP 서버 정의 생성\" : \"MCP 서버 정의 병합 (기존 항목 보존)\"],\n },\n ];\n if (envFiles.envExampleCreated) {\n files.push({ path: \".env.example\", change: \"created\", notes: [\"Supabase 토큰 가이드\"] });\n }\n const gitignoreAdded = [\n ...(envFiles.gitignoreEnvAdded ? [\".env\"] : []),\n ...envFiles.gitignoreNpxSkillsAdded,\n ];\n if (gitignoreAdded.length > 0) {\n files.push({\n path: \".gitignore\",\n change: \"modified\",\n notes: [`추가된 줄: ${gitignoreAdded.join(\", \")}`],\n });\n }\n for (const workflow of ciScaffold?.written ?? []) {\n files.push({ path: workflow, change: \"created\", notes: [\"CI 워크플로 스캐폴드\"] });\n }\n return files;\n}\n\n/**\n * v0.6.1 — manifest entry를 카테고리별로 누적. install renderer Phase 1 row 출력에 사용.\n * `entry.target` prefix로 분류. file은 basename(.확장자 제거), dir은 dir name.\n */\nfunction accumulateCategory(\n cats: BaselineCategoryCounts,\n entry: import(\"./manifest.js\").AssetEntry,\n): void {\n const target = entry.target;\n if (target.startsWith(\".claude/rules/\") && target.endsWith(\".md\")) {\n const name = target.replace(/^\\.claude\\/rules\\//, \"\").replace(/\\.md$/, \"\");\n cats.rules.push(name);\n } else if (target.startsWith(\".claude/agents/\") && target.endsWith(\".md\")) {\n const name = target.replace(/^\\.claude\\/agents\\//, \"\").replace(/\\.md$/, \"\");\n cats.agents.push(name);\n } else if (target.startsWith(\".claude/hooks/\") && target.endsWith(\".sh\")) {\n const name = target.replace(/^\\.claude\\/hooks\\//, \"\").replace(/\\.sh$/, \"\");\n cats.hooks.push(name);\n } else if (target.startsWith(\".claude/commands/\")) {\n cats.commands += 1;\n } else if (target.startsWith(\".claude/skills/\") && entry.type === \"dir\") {\n const name = target.replace(/^\\.claude\\/skills\\//, \"\").replace(/\\/?$/, \"\");\n cats.skills.push(name);\n }\n}\n\nfunction writeInstalledTracks(projectDir: string, tracks: ReadonlyArray<string>): void {\n const path = join(projectDir, \".claude/.installed-tracks\");\n mkdirSync(dirname(path), { recursive: true });\n const sorted = [...new Set(tracks)].sort().join(\"\\n\");\n writeFileSync(path, `${sorted}\\n`);\n}\n\n/**\n * 루트 `CLAUDE.md` — **덮어쓰지 않는다** (P5 · ADR-060). 하네스 내용은 앵커 파일\n * (`HARNESS_ANCHOR_FILE`)로 따로 나가고, 여기엔 그것을 끌어오는 마커 import 한 줄만 얹는다.\n *\n * 그래서 백업도 사라졌다 — 백업은 \"덮어쓰기 전 원본 보존\"의 대응물인데 이제 덮어쓰기가 없다.\n * 사용자 본문은 그대로 남고 우리 블록만 추가되며, uninstall 이 그 블록만 도로 걷어간다.\n * (`.mcp.json`·`.gitignore` 처럼 사용자 파일에 병합하는 다른 산출물과 같은 방침이다.)\n */\nfunction writeRootClaudeMd(\n projectDir: string,\n tracks: ReadonlyArray<Track>,\n continuousSkills: ReadonlyArray<string>,\n): { created: boolean } {\n const target = join(projectDir, \"CLAUDE.md\");\n const existing = existsSync(target) ? readFileSync(target, \"utf-8\") : null;\n const content = upsertHarnessImport(existing, {\n projectName: basename(projectDir),\n tracks,\n continuousSkills,\n });\n // 이미 import 가 있으면 upsert 가 입력을 그대로 돌려준다 — 그때는 파일을 만지지 않는다.\n if (content !== existing) {\n writeFileSync(target, content);\n }\n return { created: existing === null };\n}\n\nfunction chmodHooksSync(hookDir: string): void {\n for (const file of listHookFiles(hookDir)) {\n try {\n chmodSync(file, 0o755);\n } catch {\n // Best-effort; many platforms (Windows in particular) ignore mode bits.\n }\n }\n}\n\nfunction listHookFiles(hookDir: string): string[] {\n // Hooks are flat shell scripts — avoid pulling glob deps.\n return readdirSync(hookDir, { withFileTypes: true })\n .filter((e) => e.isFile() && e.name.endsWith(\".sh\"))\n .map((e) => resolve(hookDir, e.name));\n}\n","/**\n * CI scaffold (v26.108.0, 라이프사이클 자산화 ② — ADR-037) — `.github/workflows/` fill-in\n * 워크플로 템플릿 설치. 실무 CI 패턴(실DB 서비스 컨테이너 · tag-only 트리거 +\n * 로컬 검증 1차 게이트 · Playwright E2E · coverage 게이트)의 도메인 중립 일반화\n * (docs/plans/lifecycle-codification-2026-07-18.md ② — CI 0 인 실프로젝트가 갭의 존재 증명).\n *\n * 본 하네스가 `.claude/` 밖에 쓰는 첫 자산 — 안전 계약 2가지:\n * 1. opt-in 전용 (`--with ci-scaffold` / wizard 체크) — 무인지 설치 없음.\n * 2. **기존 파일 절대 덮어쓰지 않음** — 이미 존재하는 워크플로 파일은 skip 하고\n * `skippedExisting` 으로 정직 보고 (manifest copyFile 의 무조건 overwrite 와 다른 이유).\n * uninstall 도 `.github/` 를 건드리지 않는다 (install-log 미기록 — 설치 후 사용자 소유물).\n */\nimport { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { copyFile } from \"./fs-ops.js\";\nimport { anyTrack, hasUiTrack } from \"./track-match.js\";\nimport type { Track } from \"./types.js\";\n\nexport interface CiScaffoldReport {\n /** 새로 쓴 워크플로 파일 (project-relative). */\n written: string[];\n /** 이미 존재해 보존한 파일 (project-relative) — no-clobber 정직 보고용. */\n skippedExisting: string[];\n}\n\ninterface WorkflowVariant {\n /** `templates/github-workflows/` 안의 소스 파일명. */\n source: string;\n /** `.github/workflows/` 안의 타깃 파일명. */\n target: string;\n applies: (tracks: ReadonlyArray<Track>) => boolean;\n}\n\nconst PYTHON_TRACKS = \"data|csr-fastapi|full\";\n\n/**\n * Track → 변형 매핑 (결정론 — 코드로 되는 건 코드로. 사용자가 받는 파일을 사전에 알 수 있어야 한다):\n * - node CI: node 계열 트랙, 또는 python 계열이 전혀 없을 때의 fallback — 명시 opt-in 이\n * 트랙 매핑 밖(예: PM 단독)이라고 무설치가 되면 silent no-op (no-false-ship 위반).\n * - python CI: python 계열 트랙. csr-fastapi·full 은 polyglot — node 와 양쪽 설치.\n * - e2e(Playwright): UI 트랙만 (`hasUiTrack` = csr-*|ssr-*|full).\n */\nconst VARIANTS: ReadonlyArray<WorkflowVariant> = [\n {\n source: \"ci-node.yml\",\n target: \"ci.yml\",\n applies: (tracks) =>\n anyTrack(tracks, \"csr-*|ssr-*|tooling|full\") || !anyTrack(tracks, PYTHON_TRACKS),\n },\n {\n source: \"ci-python.yml\",\n target: \"ci-python.yml\",\n applies: (tracks) => anyTrack(tracks, PYTHON_TRACKS),\n },\n {\n source: \"e2e.yml\",\n target: \"e2e.yml\",\n applies: (tracks) => hasUiTrack(tracks),\n },\n];\n\nexport function installCiScaffold(ctx: {\n harnessRoot: string;\n projectDir: string;\n tracks: ReadonlyArray<Track>;\n}): CiScaffoldReport {\n const report: CiScaffoldReport = { written: [], skippedExisting: [] };\n for (const v of VARIANTS) {\n if (!v.applies(ctx.tracks)) continue;\n const relTarget = `.github/workflows/${v.target}`;\n const target = join(ctx.projectDir, relTarget);\n if (existsSync(target)) {\n report.skippedExisting.push(relTarget);\n continue;\n }\n copyFile(join(ctx.harnessRoot, \"templates/github-workflows\", v.source), target);\n report.written.push(relTarget);\n }\n return report;\n}\n","/**\n * cli-transforms.ts — 외부 CLI(codex · opencode · antigravity) transform 실행 (v26.134.0 · ADR-049).\n *\n * `installer.ts` 안에 있던 것을 옮겼다. `update` 도 **같은 함수**를 써야 하기 때문이다 —\n * 소유자 판정이 세 자산 종류에 세 번 나눠 붙어 같은 버그가 세 번 난 게 ADR-046~048 의 역사이고\n * (`feedback_surface_symmetry`), 그 교훈은 \"공통 규칙은 자산별 코드가 아니라 한 도구에\" 였다.\n * install 과 update 가 각자 transform 을 부르면 기준선을 잇는 규칙이 두 벌이 되고, 그건 곧\n * 네 번째 재발이다. 겸사로 `update-mode.ts` → `installer.ts` 순환 import 도 생기지 않는다.\n */\n\nimport {\n type AntigravityTransformReport,\n runAntigravityTransform,\n} from \"./antigravity/transform.js\";\nimport { type CodexOptInReport, runCodexOptIn } from \"./codex/opt-in.js\";\nimport { type CodexTransformReport, runCodexTransform } from \"./codex/transform.js\";\nimport type { InstallLogSkillFile } from \"./install-log.js\";\nimport { type OpencodeTransformReport, runOpencodeTransform } from \"./opencode/transform.js\";\nimport type { OwnedWriteResult } from \"./owned-write.js\";\nimport { CLI_BASES, type CliBase } from \"./types.js\";\n\n/** Codex / OpenCode / Antigravity per-CLI transforms (+ scope=global opt-in) 결과. */\nexport interface CliTransformResults {\n codex: CodexTransformReport | null;\n codexOptIn: CodexOptInReport | null;\n opencode: OpencodeTransformReport | null;\n antigravity: AntigravityTransformReport | null;\n /** v26.133.0 (ADR-048) — 세 transform 이 쓴 산출물의 기준선 (install log `externalFiles`). */\n externalFiles: InstallLogSkillFile[];\n /** v26.133.0 (ADR-048) — 사용자 편집분이라 만든 백업 파일 경로. 설치 화면에 노출한다. */\n externalBackups: string[];\n /** v26.134.0 (ADR-049) — 실제로 디스크가 바뀐 파일 수. update 화면의 카운트. */\n externalUpdated: number;\n /** v26.134.0 (ADR-049) — 백업된 사용자 편집분의 project-relative 경로. */\n externalBackedUp: string[];\n /**\n * #343 — 다른 도구가 소유한 스킬 슬롯이라 쓰지 않은 자리 (`.agents/skills/<id>` 등).\n * `.claude/` baseline 의 같은 판정과 **한 줄로 합쳐** 화면에 낸다 — 사용자에게는 어느\n * 단계가 건너뛰었는지가 아니라 \"어느 자리를 옮겨야 하는지\"가 필요하다.\n */\n externalForeignOwned: string[];\n}\n\nexport interface CliTransformParams {\n harnessRoot: string;\n projectDir: string;\n /** 대상 CLI. install 은 `spec.cli`, update 는 전부(`CLI_BASES`) — 아래 refreshOnly 주석 참조. */\n cli: ReadonlyArray<CliBase>;\n selectedInternalSkills: ReadonlyArray<string>;\n /**\n * 2026-08-12 — 이 설치에 깔릴 룰 이름들(`resolveRules`). 룰이 `.claude/rules/` 하나로만\n * 나가던 탓에 비 Claude 단독 설치는 룰을 한 종도 못 받았다. `selectedInternalSkills` 와 같은\n * 형태다 — 무엇을 깔지는 installer 가 정하고, 어디에 놓을지는 각 transform 이 정한다.\n */\n rules: ReadonlyArray<string>;\n /** install log 의 `externalFiles`. 없으면 빈 배열 = 판정 불가 → 보수적 백업. */\n previousExternal: ReadonlyArray<InstallLogSkillFile>;\n /**\n * v26.134.0 (ADR-049) — 이미 있는 산출물만 갱신하고 없는 것은 만들지 않는다 (`update` 경로).\n *\n * **이 플래그가 \"어느 CLI 가 설치돼 있나\"라는 질문 자체를 없앤다.** 안 깐 CLI 는 대상 파일이\n * 하나도 없어 자연히 아무것도 안 쓰므로, update 는 `CLI_BASES` 전부를 그냥 넘기면 된다 —\n * CLI 목록이나 스킬 목록의 **열거 사본을 만들 필요가 없다** (`no-false-ship` §게이트는\n * 열거하지 말고 훑어라와 같은 취지: 사람이 기억해야 하는 목록은 다음 drift 의 서식지다).\n */\n refreshOnly?: boolean;\n /** codex global trust opt-in — `scope=global` + `withCodexTrust` 일 때만. update 는 안 쓴다. */\n codexTrust?: boolean;\n}\n\n/**\n * update 가 넘기는 CLI 목록 — 전부. 판정은 `refreshOnly` 가 디스크로 대신한다.\n * `CLI_BASES` 에서 파생하므로 CLI 가 늘어도 여기를 고칠 필요가 없다.\n */\nexport const ALL_CLI_TARGETS: ReadonlyArray<CliBase> = CLI_BASES;\n\nexport function runCliTransforms(params: CliTransformParams): CliTransformResults {\n const {\n harnessRoot,\n projectDir,\n cli,\n selectedInternalSkills,\n rules,\n previousExternal,\n refreshOnly = false,\n codexTrust = false,\n } = params;\n\n // v26.133.0 (ADR-048) — 기준선을 transform 사이로 **이어준다**. codex 와 opencode 는 같은\n // `AGENTS.md` 를, codex 와 antigravity 는 같은 `.agents/skills/<id>/SKILL.md` 를 쓴다.\n // 안 이어주면 뒤 단계가 앞 단계의 산출물을 '사용자 편집'으로 오판해 **실행할 때마다**\n // 백업이 생긴다 — 백업 노이즈는 진짜 백업을 눈에 안 띄게 만들어 보호 자체를 무력화한다.\n const baseline = new Map(previousExternal.map((f) => [f.path, f.sha256]));\n const externalFiles: InstallLogSkillFile[] = [];\n const externalBackups: string[] = [];\n const externalBackedUp: string[] = [];\n const externalForeignOwned: string[] = [];\n let externalUpdated = 0;\n const absorb = (report: { ownership: OwnedWriteResult }): void => {\n for (const f of report.ownership.files) {\n baseline.set(f.path, f.sha256);\n externalFiles.push(f);\n }\n externalBackups.push(...report.ownership.backupPaths);\n externalBackedUp.push(...report.ownership.backedUp);\n for (const f of report.ownership.foreignOwned) {\n // 세 transform 이 같은 `.agents/skills/` 를 쓰므로 중복이 실제로 난다.\n if (!externalForeignOwned.includes(f)) externalForeignOwned.push(f);\n }\n externalUpdated += report.ownership.updated;\n };\n\n let codex: CodexTransformReport | null = null;\n let codexOptIn: CodexOptInReport | null = null;\n if (cli.includes(\"codex\")) {\n // v26.87.0 — dev-method skills 는 selectedInternalSkills 로 게이팅.\n codex = runCodexTransform({\n harnessRoot,\n projectDir,\n selectedInternalSkills,\n rules,\n baseline,\n refreshOnly,\n });\n absorb(codex);\n // v26.64.0 (ADR-020) — Codex global trust opt-in 은 scope=global 일 때만 의미.\n // scope=project (default) 시 ~/.codex/ write skip (config.toml trust entry 만).\n if (codexTrust) {\n codexOptIn = runCodexOptIn({ projectDir });\n }\n }\n\n let opencode: OpencodeTransformReport | null = null;\n if (cli.includes(\"opencode\")) {\n opencode = runOpencodeTransform({\n harnessRoot,\n projectDir,\n selectedInternalSkills,\n rules,\n baseline,\n refreshOnly,\n });\n absorb(opencode);\n }\n\n // v26.66.0 — Antigravity transform: `.agents/rules/uzys-harness.md` + dev-method skills.\n let antigravity: AntigravityTransformReport | null = null;\n if (cli.includes(\"antigravity\")) {\n antigravity = runAntigravityTransform({\n harnessRoot,\n projectDir,\n selectedInternalSkills,\n rules,\n baseline,\n refreshOnly,\n });\n absorb(antigravity);\n }\n\n return {\n codex,\n codexOptIn,\n opencode,\n antigravity,\n externalFiles,\n externalBackups,\n externalUpdated,\n externalBackedUp,\n externalForeignOwned,\n };\n}\n","/**\n * Antigravity transform — v26.66.0 (skills/workflows) + v26.69.0 (rules).\n *\n * Google Antigravity 2.0 (I/O 2026-05-19) 공식 spec (codelabs):\n * - Workspace skills: .agents/skills/<name>/SKILL.md (Anthropic skill format — codex 와 공유)\n * - Workspace workflows: .agents/workflows/<name>.md (`/<name>` 슬래시로 호출)\n * - Workspace rules: .agents/rules/<name>.md (디렉토리, plain markdown)\n * - Global rules: ~/.gemini/GEMINI.md (사용자 글로벌 — harness 미터치)\n * - Global skills: ~/.gemini/antigravity/skills/ (Phase C opt-in — antigravity/opt-in.ts)\n *\n * 본 transform 의 책임 (모두 project-scope):\n * 1. `.agents/rules/uzys-harness.md` — project context (CLAUDE.md → Antigravity rule).\n * foundational context (CLAUDE.md/AGENTS.md 처럼 항상 작성).\n * cli=antigravity 단독 선택 시 이게 없으면 Antigravity 가 프로젝트 컨벤션을 모름.\n * 2. `.agents/skills/<id>/**` — dev-method skills 디렉터리 전체 (frontmatter 보존, codex 와 공유).\n *\n * SAFETY: `~/.gemini/` 글로벌 write 없음.\n */\n\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { basename, join } from \"node:path\";\nimport { renderAgentsMd } from \"../codex/agents-md.js\";\nimport { writeBundledSkillDirs } from \"../codex/skills.js\";\nimport { createOwnedWriter, type OwnedWriteResult, type OwnedWriter } from \"../owned-write.js\";\nimport { renderFillScaffold, withContinuousSkillsNote } from \"../project-claude-merge.js\";\nimport { portRules } from \"../rules-port.js\";\n\nexport interface AntigravityTransformParams {\n /** harness root (templates/CLAUDE.md source 위치). */\n harnessRoot: string;\n /** 사용자 프로젝트 root. `.agents/` 가 만들어질 위치. */\n projectDir: string;\n /**\n * v26.87.0 — dev-method skill ids 선택 목록. 각 id 의 `templates/skills/<id>/` 를\n * Antigravity native `.agents/skills/<id>/` 로 (SKILL.md frontmatter 보존) 출력.\n */\n selectedInternalSkills?: ReadonlyArray<string>;\n /** 2026-08-12 — 이 설치의 배포 룰 이름들. `.agents/rules/<name>.md` 로 나간다. */\n rules?: ReadonlyArray<string>;\n /**\n * v26.133.0 (ADR-048) — 설치 시점 기준선 (install log `externalFiles`).\n * codex/opencode 와 같은 이유로 **required**. 레거시는 빈 Map 을 명시적으로 넘긴다.\n */\n baseline: ReadonlyMap<string, string>;\n /**\n * v26.134.0 (ADR-049) — `update` 경로. 이미 있는 산출물만 갱신하고 없는 것은 만들지 않는다.\n * antigravity 를 안 깐 프로젝트에서 돌려도 `.agents/rules/` 가 생기지 않는다.\n */\n refreshOnly?: boolean;\n}\n\nexport interface AntigravityTransformReport {\n /** v26.69.0 — 작성된 **앵커** 파일 경로 (.agents/rules/uzys-harness.md). null = template 부재. */\n rulesFile: string | null;\n /** 2026-08-12 — 작성된 배포 룰 경로 (.agents/rules/<name>.md). 앵커와 형제. */\n harnessRuleFiles: string[];\n /**\n * `.agents/skills/<id>/` 에 쓴 **모든** 파일 경로 — `SKILL.md` + 형제(references/scripts 등).\n * #431 이전에는 `SKILL.md` 하나뿐이었다.\n */\n skillFiles: ReadonlyArray<string>;\n /** v26.133.0 (ADR-048) — 소유권 결과 (기준선 · 백업된 사용자 편집분). */\n ownership: OwnedWriteResult;\n}\n\n/**\n * Antigravity 용 project-scope 자산 생성 (rules + dev-method skills).\n */\nexport function runAntigravityTransform(\n params: AntigravityTransformParams,\n): AntigravityTransformReport {\n const {\n harnessRoot,\n projectDir,\n selectedInternalSkills = [],\n rules = [],\n baseline,\n refreshOnly,\n } = params;\n const writer = createOwnedWriter(projectDir, baseline, { refreshOnly: refreshOnly ?? false });\n\n // 1. .agents/rules/uzys-harness.md — project context (CLAUDE.md → Antigravity rule, 항상).\n const rulesFile = writeRules(harnessRoot, projectDir, writer, selectedInternalSkills);\n\n // 1a. 2026-08-12 — 배포 룰을 같은 워크스페이스 룰 디렉터리에 형제 파일로 놓는다.\n // Antigravity 는 `.agents/rules/*.md` 를 네이티브로 읽으므로 변환이 필요 없다(파일당 12,000자\n // 상한 — 배포 룰은 전부 그 아래다). 위 `uzys-harness.md` 는 이름과 달리 **앵커**라서,\n // 그것만으로는 룰이 도달하지 않았다.\n //\n // `createInRefresh` 를 켜는 근거는 **바로 위 줄의 반환값**이다. 앵커를 담당했다는 것은\n // 이 프로젝트에 Antigravity 가 설치돼 있다는 뜻이고(refresh 모드에서 앵커가 없으면\n // `rulesFile === null`), 그때만 새 룰 파일을 만든다. 이 예외가 없으면 기존 설치자는\n // update 를 아무리 돌려도 룰을 못 받는다 — 없는 파일은 refresh 가 건너뛰기 때문이다.\n const antigravityInstalled = rulesFile !== null;\n const harnessRuleFiles: string[] = [];\n for (const rule of portRules(harnessRoot, rules)) {\n const target = join(projectDir, \".agents\", \"rules\", `${rule.name}.md`);\n if (!writer.write(target, `${rule.body}\\n`, { createInRefresh: antigravityInstalled })) {\n continue;\n }\n harnessRuleFiles.push(target);\n }\n\n // 1b. v26.87.0 — dev-method skills → .agents/skills/<id>/ (frontmatter 보존).\n // renderBundledSkill 이 source frontmatter(name: <id>)를 보존.\n // 2026-09-13 (#431) — `SKILL.md` 한 파일이 아니라 디렉터리 전체다. 루프는 세 transform\n // 공용 helper 가 소유한다(codex·opencode 와 같은 산출물).\n const skillFiles = writeBundledSkillDirs({\n harnessRoot,\n projectDir,\n skillIds: selectedInternalSkills,\n writer,\n });\n\n return {\n rulesFile,\n harnessRuleFiles,\n skillFiles,\n ownership: writer.result(),\n };\n}\n\n/**\n * v26.69.0 — `.agents/rules/uzys-harness.md` 작성. CLAUDE.md → Antigravity workspace rule.\n *\n * Source: templates/CLAUDE.md (전문) + templates/antigravity/AGENTS.md.template.\n * v26.70.0 — renderAgentsMd 재사용 (codex/opencode 와 동일 전문 embed). `{PROJECT_RULES}` 에\n * CLAUDE.md 본문 전체 삽입 + `/uzys:` → `/uzys-` rename.\n *\n * template 또는 CLAUDE.md 부재 시 null (graceful — install 진행).\n */\nfunction writeRules(\n harnessRoot: string,\n projectDir: string,\n writer: OwnedWriter,\n selectedInternalSkills: ReadonlyArray<string>,\n): string | null {\n const claudeMdPath = join(harnessRoot, \"templates/CLAUDE.md\");\n const templatePath = join(harnessRoot, \"templates/antigravity/AGENTS.md.template\");\n if (!existsSync(claudeMdPath) || !existsSync(templatePath)) {\n return null;\n }\n const claudeMd = readFileSync(claudeMdPath, \"utf8\");\n const template = readFileSync(templatePath, \"utf8\");\n const target = join(projectDir, \".agents\", \"rules\", \"uzys-harness.md\");\n const rulesOut = renderAgentsMd({\n template,\n claudeMd,\n projectName: basename(projectDir),\n // ADR-085 — 상시 스킬 안내는 앵커가 아니라 여기(프로젝트 맥락)에, 깔린 것만.\n projectContext: withContinuousSkillsNote(\n renderFillScaffold(\"antigravity-rule\"),\n selectedInternalSkills,\n ),\n });\n // 사용자가 채운 rules 파일을 재설치(add 모드) 덮어쓰기 전 보존 — 루트 CLAUDE.md 와 대칭.\n // v26.133.0 (ADR-048) — 내용 비교에서 소유자 판정으로 (릴리즈마다 백업 쌓임 방지).\n // refresh 모드가 건너뛰면 null — 안 만든 파일을 만들었다고 보고하지 않는다.\n return writer.write(target, rulesOut) ? target : null;\n}\n","/**\n * AGENTS.md transform — CLAUDE.md → AGENTS.md.\n *\n * v26.70.0 — section 추출(Identity/Direction/Principles) → CLAUDE.md **전문 embed**.\n * 실 `templates/CLAUDE.md` 에 Identity/Direction/Principles 헤딩이 없어\n * extractSection 이 빈 결과 → AGENTS.md 가 빈 섹션으로 shipping 되던 버그 fix.\n * `{PROJECT_RULES}` placeholder 에 CLAUDE.md 본문 전체를 삽입 (heading 구조 의존 0).\n */\n\n/** Rename Claude slash conventions (`/uzys:foo`) to Codex (`/uzys-foo`). */\nexport function renameSlashes(text: string): string {\n return text.replaceAll(\"/uzys:\", \"/uzys-\");\n}\n\nexport interface AgentsMdParams {\n template: string;\n claudeMd: string;\n projectName: string;\n /** Project-context fill scaffold — the same body shipped to the Claude Code CLAUDE.md. */\n projectContext: string;\n /**\n * 2026-08-12 — 배포 룰 본문(이어 붙인 한 덩어리). **Codex 전용**이다: Codex 는 룰 디렉터리가\n * 없고 `AGENTS.md` 계층만 읽으므로 룰이 본문에 들어가야 도달한다. OpenCode · Antigravity 는\n * 각자 룰 디렉터리를 쓰므로 이 값을 넘기지 않고, 그 템플릿에는 placeholder 도 없다.\n */\n harnessRules?: string;\n}\n\n/**\n * Render AGENTS.md by embedding the full CLAUDE.md body into the template.\n *\n * Placeholders:\n * - {PROJECT_NAME} — basename of project dir\n * - {PROJECT_RULES} — full CLAUDE.md body (first h1 stripped; template provides its own h1)\n * - {PROJECT_CONTEXT} — project-specific fill scaffold (renderFillScaffold())\n *\n * 마지막에 `/uzys:` → `/uzys-` rename (Codex/Antigravity 는 slash namespace 미지원).\n */\nexport function renderAgentsMd(params: AgentsMdParams): string {\n // CLAUDE.md 의 첫 h1 (# title) 제거 — 템플릿이 자체 h1 보유.\n const body = params.claudeMd.replace(/^#\\s+.*\\r?\\n/, \"\").trim();\n const replaced = params.template\n .replaceAll(\"{PROJECT_NAME}\", params.projectName)\n .replaceAll(\"{PROJECT_RULES}\", body)\n .replaceAll(\"{PROJECT_CONTEXT}\", params.projectContext)\n .replaceAll(\"{HARNESS_RULES}\", params.harnessRules ?? \"\");\n return renameSlashes(replaced);\n}\n","/**\n * Bundled SKILL.md → non-Claude CLI native skill transform (dev-method skills).\n */\nimport { chmodSync, existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { listFilesRecursive } from \"../fs-ops.js\";\nimport type { OwnedWriter } from \"../owned-write.js\";\nimport { renameSlashes } from \"./agents-md.js\";\n\n/**\n * v26.87.0 — render a bundled, already-complete SKILL.md (dev-method skills) for a\n * non-Claude CLI (Codex / Antigravity native `.agents/skills/<id>/SKILL.md`).\n *\n * These sources are full Anthropic skills with their OWN frontmatter (`name: <id>`,\n * full description). We MUST preserve that frontmatter verbatim — only the BODY is\n * ported: `/uzys:` → `/uzys-` slash rename + `CLAUDE_PROJECT_DIR` → `CODEX_PROJECT_DIR`\n * env-var rename (Codex/Antigravity share the `.agents/` format + `CODEX_PROJECT_DIR`).\n */\nexport function renderBundledSkill(source: string): string {\n const trimmed = source.trimEnd();\n const lines = trimmed.split(/\\r?\\n/);\n // No frontmatter → emit body as-is (port slashes/env only). Defensive: bundled\n // dev-method skills always have frontmatter, but never silently drop content.\n if (lines[0] !== \"---\") {\n return `${portBody(trimmed)}\\n`;\n }\n let secondDelimAt = -1;\n for (let i = 1; i < lines.length; i++) {\n if (lines[i] === \"---\") {\n secondDelimAt = i;\n break;\n }\n }\n if (secondDelimAt < 0) {\n // Malformed frontmatter (no closing ---) → port whole thing as body.\n return `${portBody(trimmed)}\\n`;\n }\n const frontmatter = lines.slice(0, secondDelimAt + 1).join(\"\\n\");\n const body = lines.slice(secondDelimAt + 1).join(\"\\n\");\n return `${frontmatter}\\n${portBody(body)}\\n`;\n}\n\n/** Port a skill body for Codex/Antigravity: slash + project-dir env-var rename. */\nfunction portBody(body: string): string {\n return renameSlashes(body)\n .replace(/CLAUDE_PROJECT_DIR/g, \"CODEX_PROJECT_DIR\")\n .trimEnd();\n}\n\nexport interface BundledSkillDirParams {\n /** harness root — 원본은 `templates/skills/<id>/`. */\n harnessRoot: string;\n /** 대상 프로젝트 root — 산출물은 `.agents/skills/<id>/`. */\n projectDir: string;\n /** 이 설치에서 고른 번들 스킬 id (installer 가 채운 `selectedInternalSkills`). */\n skillIds: ReadonlyArray<string>;\n /** 소유자 판정을 쥔 writer — 쓴 파일이 전부 `ownership.files` 에 실린다. */\n writer: OwnedWriter;\n}\n\n/**\n * 2026-09-13 (#431) — 번들 스킬을 **디렉터리 전체**로 `.agents/skills/<id>/` 에 쓴다.\n *\n * 이전에는 세 transform(codex·opencode·antigravity)이 `SKILL.md` **한 파일**만 보내는 같은\n * 루프를 각자 갖고 있었다. 번들 스킬 다수가 본문을 `references/`·`scripts/`·`agents/` 로\n * 나눠 들고 `SKILL.md` 가 \"그 파일을 읽어라\"로 라우팅하는데, 비-Claude CLI 설치자에게는 그\n * 자리가 **비어 있었다** — 안내판만 가고 본문이 안 갔다. Claude Code 쪽(`manifest.ts`)은\n * 처음부터 디렉터리 단위 복사라 이 비대칭은 한쪽 계열에만 있었다.\n *\n * 루프를 여기 하나로 모은 이유: 같은 루프가 셋이면 다음 수정이 또 하나를 빠뜨린다(#340·\n * `feedback_surface_symmetry` 와 같은 형태).\n *\n * 파일 종류별 처리 —\n * - `SKILL.md`: `renderBundledSkill` 그대로. 이전 판본과 **바이트 동일**해야 한다.\n * - 형제 `.md`·`.sh`: `SKILL.md` 본문과 같은 포팅(`/uzys:` → `/uzys-` ·\n * `CLAUDE_PROJECT_DIR` → `CODEX_PROJECT_DIR`). 참조 문서가 Claude 전용 슬래시를 들고\n * 가면 그 CLI 에서 존재하지 않는 커맨드를 불러 보게 된다.\n * - 그 밖(`.py`·`.yaml`·`.json` 등): 그대로. 코드·데이터를 문자열 치환하면 깨진다.\n * - 이름이 `.` 로 시작하는 파일·디렉터리: 건너뛴다(`.DS_Store` 같은 OS 부산물).\n *\n * @returns 이번에 쓴 **모든** 파일의 절대 경로 (`SKILL.md` + 형제). 호출부가 그대로\n * `skillFiles` 로 보고한다 — 안 쓴 경로는 들어가지 않는다(건너뛴 것을 싣는 순간 거짓 보고).\n */\nexport function writeBundledSkillDirs(params: BundledSkillDirParams): string[] {\n const { harnessRoot, projectDir, skillIds, writer } = params;\n const written: string[] = [];\n for (const id of skillIds) {\n const srcDir = join(harnessRoot, \"templates/skills\", id);\n const srcSkill = join(srcDir, \"SKILL.md\");\n if (!existsSync(srcSkill)) {\n continue;\n }\n const targetDir = join(projectDir, \".agents\", \"skills\", id);\n const targetSkill = join(targetDir, \"SKILL.md\");\n // 건너뛴 경로를 report 에 실으면 \"깔았다\"는 거짓 보고가 된다. 형제도 함께 건너뛴다 —\n // refresh 모드에서 `SKILL.md` 가 없다는 것은 이 스킬이 이 프로젝트에 없다는 뜻이고,\n // 남의 도구가 소유한 슬롯이면 그 트리에 우리 파일을 흘리지 않아야 한다(#343).\n if (!writer.write(targetSkill, renderBundledSkill(readFileSync(srcSkill, \"utf8\")))) {\n continue;\n }\n written.push(targetSkill);\n for (const rel of listFilesRecursive(srcDir)) {\n if (rel === \"SKILL.md\") continue;\n if (rel.split(\"/\").some((segment) => segment.startsWith(\".\"))) continue;\n const segments = rel.split(\"/\");\n const raw = readFileSync(join(srcDir, ...segments), \"utf8\");\n const ported = rel.endsWith(\".md\") || rel.endsWith(\".sh\") ? `${portBody(raw)}\\n` : raw;\n const target = join(targetDir, ...segments);\n // `createInRefresh`: 바로 위에서 `SKILL.md` 를 담당했다는 것이 **이 CLI 가 설치돼 있고\n // 이 스킬이 선택됐다**는 증거다(antigravity 룰이 앵커 반환값을 쓰는 것과 같은 논거).\n // 그 증거 없이 켜면 안 깐 CLI 에 `.agents/` 가 생겨 refreshOnly 가 무의미해진다.\n if (!writer.write(target, ported, { createInRefresh: true })) continue;\n // refresh 가 건너뛴 경로에는 파일이 없다 — chmod 를 무조건 걸면 ENOENT 로 터진다.\n if (rel.endsWith(\".sh\")) chmodSync(target, 0o755);\n written.push(target);\n }\n }\n return written;\n}\n","/**\n * owned-write.ts — 소유자 판정을 붙인 파일 쓰기 (v26.133.0 · ADR-048).\n *\n * ADR-046(스킬) → ADR-047(`.claude/` 정책 파일)이 정한 규칙을 **하네스가 렌더해서 내보내는\n * 모든 산출물**에 쓰기 위한 공통 도구. 그 두 ADR 의 구현이 각각 자기 자산 종류에만 걸려서\n * 같은 버그가 두 번 났다 — 세 번째를 막으려면 규칙이 자산별 코드가 아니라 **한 도구**에 있어야\n * 한다 (`feedback_surface_symmetry` \"결정의 범위 ≠ 구현의 범위\").\n *\n * `.claude/` 쪽과 다른 점 하나: 저기는 templates 파일을 **복사**하므로 기준선을 나중에\n * 디스크에서 다시 훑지만(`collectPolicyHashes`), 외부 CLI 산출물은 템플릿을 **렌더**한\n * 결과라 디스크를 훑어도 \"무엇이 하네스 것인지\" 알 수 없다. 대신 쓰는 쪽이 방금 쓴 내용을\n * 알고 있으므로 **그 자리에서 기준선을 만든다** — 경로 목록을 따로 하드코딩할 필요가 없고,\n * 그래서 `POLICY_DIRS` 같은 열거 사본이 여기엔 생기지 않는다.\n */\n\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname, relative, sep } from \"node:path\";\nimport { foreignOwnedTarget } from \"./foreign-slot.js\";\nimport { backupFile } from \"./fs-ops.js\";\nimport { hashContent, type InstallLogSkillFile, isHarnessOwned } from \"./install-log.js\";\n\n/**\n * 한 transform 실행의 소유권 결과. **세 transform 이 같은 타입을 반환한다** — 필드가 늘 때\n * 세 곳을 따로 고쳐야 하는 구조를 만들지 않기 위해서다 (이 파일이 존재하는 이유와 같다).\n */\nexport interface OwnedWriteResult {\n /** 이번 실행이 쓴 산출물의 기준선 (projectDir 상대경로 → sha256). install log 에 실린다. */\n files: InstallLogSkillFile[];\n /** 사용자 편집분이라 백업한 **원본**의 project-relative 경로 — 사용자가 알아보는 이름. */\n backedUp: string[];\n /**\n * #343 — 다른 도구가 소유한 스킬 슬롯이라 **쓰지 않은** 자리 (project-relative).\n *\n * `.agents/skills/<id>` 는 codex·antigravity 산출물의 자리이자 `npx skills add --agent` 의\n * 설치처다. 그동안 이 writer 는 그 자리가 링크여도 따라 써서 사용자의 다른 저장소를 덮고,\n * 백업까지 **그 저장소 안에** 떨궜다 — 사용자가 찾을 자리가 아니다 (ADR-062 와 같은 논거).\n */\n foreignOwned: string[];\n /** 생성된 백업 파일의 절대경로 — 설치 화면의 `backup` 행이 이걸 쓴다. */\n backupPaths: string[];\n /**\n * 실제로 디스크가 바뀐 파일 수 (신규 + 내용이 달라 덮어쓴 것). 이미 최신이라 아무것도 안 한\n * 파일은 세지 않는다 — update 화면이 \"N files updated\" 로 쓰므로, 안 한 일을 셌다가는\n * 매번 같은 숫자가 떠서 갱신이 일어난 것처럼 보인다.\n */\n updated: number;\n}\n\nexport interface OwnedWriter {\n /**\n * 소유자 판정을 거쳐 쓴다.\n *\n * | 디스크 vs 기준선 | 뜻 | 처리 |\n * |---|---|---|\n * | 파일 없음 | 신규 | 그냥 쓴다 (refresh 모드에서는 **건너뛴다**) |\n * | 내용 동일 | 이미 최신 | 아무것도 안 한다 (백업도 쓰기도) |\n * | 기준선과 같다 | 사용자가 안 고쳤다 | 조용히 덮어쓴다 |\n * | 기준선과 다르다 | 사용자가 고쳤다 | `.backup-<stamp>` 남기고 최신판을 자리에 |\n * | 기록 없음 | 판정 불가 | 보수적으로 백업 |\n *\n * @returns 이 경로를 이번에 담당했는가. `false` 는 **안 썼다**는 뜻이고 경우는 둘이다 —\n * refresh 모드에서 없는 파일을 건너뛴 경우, 그리고 #343 이후 **남의 도구가 소유한 슬롯**인 경우.\n * 호출자가 쓰기 성공을 전제로 하는 후속 동작(예: `chmod`)을 그때 건너뛰게 하려는 것이고,\n * 반환을 무시해도 되는 호출부는 무시해도 안전하다. `void` 로 두면 건너뛴 경로에\n * `chmodSync` 가 걸려 ENOENT 로 터진다.\n */\n write(absPath: string, content: string, opts?: WriteOptions): boolean;\n /** 이번 실행의 소유권 결과 — transform 이 그대로 report 에 실어 반환한다. */\n result(): OwnedWriteResult;\n}\n\n/**\n * @param now 백업 파일명 stamp — 테스트가 고정값을 주입한다.\n * @param refreshOnly 이미 있는 파일만 갱신하고 **없는 파일은 만들지 않는다** (v26.134.0 · ADR-049).\n *\n * `update` 가 쓰는 모드다. update 는 사용자가 고르지 않은 것을 새로 깔면 안 된다 —\n * `.claude/` 쪽 `updateDir` 이 \"target 에 이미 있는 파일만\"으로 오래 지켜 온 규율(Track 혼입\n * 방지)과 같은 것이고, 외부 CLI 에 그대로 적용하면 **\"어느 CLI 가 설치돼 있나\"를 판정할 필요\n * 자체가 없어진다** — 안 깔린 CLI 는 대상 파일이 하나도 없어 자연히 아무것도 안 쓴다.\n * 판정을 안 하니 CLI 목록·스킬 목록의 열거 사본도 생기지 않는다.\n */\n/** 개별 write 의 refresh 정책 예외. */\nexport interface WriteOptions {\n /**\n * refresh 모드에서도 **없으면 만든다**.\n *\n * `refreshOnly` 의 규율은 \"사용자가 안 고른 CLI 의 자산을 새로 깔지 않는다\"이고, 그 판정을\n * 파일 존재로 대신해 왔다. 그런데 **이미 설치된 CLI 에 하네스가 새 자산을 추가한 경우**엔\n * 그 판정이 반대로 작동한다 — 파일이 없는 이유가 \"안 골라서\"가 아니라 \"그때는 없던\n * 자산이라서\"인데, update 가 영원히 건너뛴다(독립 검증 C-2 실측: 기존 설치자는 새 룰을\n * 재설치 전에는 못 받는다).\n *\n * 그래서 호출부가 **그 CLI 가 설치돼 있다는 별도 증거**를 잡았을 때만 이 예외를 쓴다.\n * 증거 없이 쓰면 안 깐 CLI 에 디렉터리가 생겨 `refreshOnly` 자체가 무의미해진다.\n */\n createInRefresh?: boolean;\n}\n\nexport interface OwnedWriterOptions {\n now?: Date;\n refreshOnly?: boolean;\n}\n\n/**\n * @param projectDir 기준 디렉터리 — 기록되는 경로는 전부 여기 기준 상대경로다.\n * 절대경로를 기록하면 프로젝트를 옮긴 순간 기준선이 통째로 무효가 된다.\n * @param baseline 설치 시점 기준선 (install log `externalFiles`). 빈 Map = 판정 불가.\n *\n * writer 하나는 **transform 하나**를 담당한다. codex 와 opencode 가 같은 `AGENTS.md` 를 쓰는\n * 것 같은 교차 누적은 호출자(installer)가 다음 writer 를 만들기 전에 기준선을 갱신해서\n * 처리한다 — 누적을 여기와 거기 양쪽에 두면 한쪽이 죽은 코드가 되고, 죽은 줄은 테스트가\n * 잡지 못한다 (이 파일의 첫 판이 실제로 그랬고 음성 대조에서 드러났다).\n */\nexport function createOwnedWriter(\n projectDir: string,\n baseline: ReadonlyMap<string, string>,\n options: OwnedWriterOptions = {},\n): OwnedWriter {\n const { now = new Date(), refreshOnly = false } = options;\n const written = new Map<string, string>();\n const backedUp: string[] = [];\n const backupPaths: string[] = [];\n const foreignOwned: string[] = [];\n let updated = 0;\n\n return {\n write(absPath, content, opts) {\n const rel = relative(projectDir, absPath).split(sep).join(\"/\");\n // #343 — 남의 도구가 소유한 스킬 슬롯이면 쓰지 않는다 (판정 SSOT = foreign-slot.ts).\n // `false` 반환은 기존 계약 그대로다 — 호출부는 이미 \"안 썼으면 report 에 안 싣는다\".\n const foreign = foreignOwnedTarget(projectDir, rel);\n if (foreign !== null) {\n if (!foreignOwned.includes(foreign)) foreignOwned.push(foreign);\n return false;\n }\n const digest = hashContent(content);\n\n if (!existsSync(absPath)) {\n // refresh 모드: 없는 파일은 사용자가 안 고른 것 → 새로 깔지 않는다.\n // 기준선에도 넣지 않는다 — 디스크에 없는 파일의 해시를 기록하면 그게 곧 거짓 기록이다.\n // 예외 = `createInRefresh` (호출부가 그 CLI 의 설치 증거를 이미 잡은 경우).\n if (refreshOnly && !opts?.createInRefresh) return false;\n mkdirSync(dirname(absPath), { recursive: true });\n writeFileSync(absPath, content);\n updated++;\n } else {\n const current = readFileSync(absPath, \"utf8\");\n if (current !== content) {\n if (!isHarnessOwned(baseline, rel, current)) {\n backupPaths.push(backupFile(absPath, now));\n backedUp.push(rel);\n }\n writeFileSync(absPath, content);\n updated++;\n }\n }\n\n written.set(rel, digest);\n return true;\n },\n result() {\n return {\n foreignOwned: [...foreignOwned],\n files: [...written].map(([path, sha256]) => ({ path, sha256 })),\n backedUp: [...backedUp],\n backupPaths: [...backupPaths],\n updated,\n };\n },\n };\n}\n","import { lstatSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\n/**\n * foreign-slot.ts — \"그 자리는 우리 것이 아니다\" 판정 (#343 · ADR-062 적용 표면 확장).\n *\n * 규칙을 **한 도구**에 둔다. `owned-write.ts` 서문이 같은 이유를 이미 적었다 — 소유자 판정이\n * 자산 종류별 코드에 흩어지면 같은 버그가 종류마다 다시 난다. 실제로 그렇게 났다:\n * `update` 는 ADR-062 로 링크를 건너뛰었는데 `install` 은 같은 자리에서 죽었고(#343),\n * `.agents/skills/` 는 두 명령 어느 쪽에서도 판정을 안 받아 조용히 덮고 있었다.\n *\n * **적용 범위는 스킬 슬롯뿐이다.** `.claude/settings.json` · `.claude/rules/*.md` 처럼 슬롯 밖\n * 파일을 사용자가 공유 dotfiles 로 링크해 두는 것은 지원 케이스이고(`fs-ops.ts` copyBackupDir\n * 주석), 거기까지 넓히면 정상 설치가 통째로 건너뛰어진다.\n */\n\n/**\n * 다른 도구(`npx skills add`)가 소유할 수 있는 스킬 슬롯. **두 자리를 함께 본다** —\n * `.claude/skills/<id>` (Claude Code) 와 `.agents/skills/<id>` (codex·antigravity 공용,\n * `external-installer.ts` 의 SKILLS_CLI_AGENT_MAP 이 그 자리로 보낸다).\n * 슬롯 자체(`…/<id>`)와 그 안의 파일(`…/<id>/SKILL.md`) 둘 다 매치한다.\n */\nconst SKILL_SLOT = /^((?:\\.claude|\\.agents)\\/skills\\/[^/]+)(?:\\/|$)/;\n\n/**\n * 그 자리가 **디렉터리가 아닌 것**으로 이미 차 있는가 (비어 있으면 false).\n *\n * `existsSync` 로는 판정할 수 없다 — 링크를 따라가므로 \"디렉터리가 있다\"와 \"디렉터리를 가리키는\n * 링크가 있다\"에 같은 답을 내고, 깨진 링크는 아예 없는 것으로 보인다. `lstatSync` 는 링크를\n * 따라가지 않아 셋을 구분한다. 심링크뿐 아니라 파일·FIFO 등 **디렉터리가 아닌 모든 것**을 잡는다:\n * 우리가 디렉터리를 만들 자리를 차지한 것은 무엇이든 우리 것이 아니다.\n */\nexport function occupiedByNonDirectory(path: string): boolean {\n // throwIfNoEntry:false → 없으면 undefined. EACCES 같은 진짜 오류는 계속 던지게 둔다.\n const stat = lstatSync(path, { throwIfNoEntry: false });\n return stat !== undefined && !stat.isDirectory();\n}\n\n/**\n * 이 대상에 쓰면 **남의 것을 건드리는가**. 건드리면 사용자에게 보여 줄 경로를, 아니면 null.\n *\n * 두 가지를 본다:\n * ⓐ 슬롯 자체가 디렉터리가 아니다 → 그 자리 전체가 남의 것 (`npx skills add` 의 링크가 이 모양)\n * ⓑ 슬롯 안의 **중간 디렉터리**가 디렉터리가 아니다 → 그 중간 자리가 남의 것\n * ⓒ 최종 경로가 **일반 파일이 아니다**(심링크·FIFO·소켓·디렉터리) → 그 자리만 남의 것\n * ⓑⓒ 가 따로 필요한 이유: `copyFileSync`·`writeFileSync` 는 링크를 따라가므로 죽지 않고\n * 조용히 링크 대상을 덮는다 — 크래시가 없어 아무도 신고하지 않는 형태다. 심링크만 보지 않는\n * 이유는 나머지가 더 나쁘기 때문이다: FIFO 면 읽기·쓰기가 **영영 블록**되고(설치가 멈춘다),\n * 디렉터리면 EISDIR 로 죽는다.\n *\n * @param rel `projectDir` 기준 상대경로 (슬래시 구분)\n */\nexport function foreignOwnedTarget(projectDir: string, rel: string): string | null {\n const slot = SKILL_SLOT.exec(rel)?.[1];\n if (slot === undefined) return null;\n if (occupiedByNonDirectory(join(projectDir, slot))) return slot;\n if (rel !== slot) {\n // 슬롯과 최종 경로 **사이의 디렉터리**부터 본다. 중간 성분이 링크면 마지막 것만 보는\n // 판정은 그 링크를 따라간 자리를 \"평범한 파일\"로 읽고 통과시킨다.\n const parts = rel.slice(slot.length + 1).split(\"/\");\n let cursor = slot;\n for (const part of parts.slice(0, -1)) {\n cursor = `${cursor}/${part}`;\n if (occupiedByNonDirectory(join(projectDir, cursor))) return cursor;\n }\n const leaf = lstatSync(join(projectDir, rel), { throwIfNoEntry: false });\n if (leaf !== undefined && !leaf.isFile()) return rel;\n }\n return null;\n}\n","/**\n * 배포 룰(`templates/rules/*.md`)을 **Claude Code 가 아닌 CLI 로** 옮기는 공용 층.\n *\n * WHY (실측 2026-08-12): 룰은 `.claude/rules/` 하나로만 나갔고, 그 디렉터리는 `spec.cli` 에\n * claude 가 있을 때만 만들어진다. 그래서 Codex · OpenCode · Antigravity 단독 설치자는 룰을\n * **한 종도 못 받았다** — 네 CLI 에 룰·훅·스킬을 설치한다는 것이 이 저장소가 파는 것인데.\n * 세 CLI 모두 룰을 받을 자리가 있으므로(각 CLI 문서 확인) 공백은 능력 한계가 아니라 배선이었다.\n *\n * 여기 있는 것은 **한 벌의 변환 규칙**이다. CLI 별 목적지는 각 transform 이 정한다:\n * - Antigravity → `.agents/rules/<name>.md` (워크스페이스 룰, 네이티브)\n * - OpenCode → `.opencode/rules/<name>.md` + `opencode.json` `instructions` 글롭\n * - Codex → `AGENTS.md` 본문 embed (Codex 는 룰 디렉터리가 없다 — AGENTS.md 계층뿐)\n */\n\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\n/** 룰 하나의 이식 결과. `name` 은 확장자 없는 파일명 그대로다. */\nexport interface PortedRule {\n name: string;\n body: string;\n}\n\n/**\n * Claude Code 전용 frontmatter 를 벗긴다.\n *\n * `paths:` 는 Claude Code 의 조건부 로드 키다(`cli-development` 이 유일한 사용처). 다른 CLI 는\n * 그 키를 모르므로 본문 맨 앞에 남으면 **그냥 이상한 텍스트**가 되고, Codex 처럼 AGENTS.md\n * 한복판에 끼워 넣는 경우엔 문서를 깨뜨린다. 벗겨서 상시 룰로 보낸다 — 그 대가로 세 CLI 에서\n * 그 룰이 항상 상주하지만, 문서에 확인되지 않은 활성 문법을 추측해 쓰는 것보다는 낫다.\n * (Antigravity 는 glob 활성 모드가 있다고 문서가 말하지만 frontmatter 문법을 공개하지 않는다.)\n */\nexport function stripClaudeFrontmatter(source: string): string {\n if (!source.startsWith(\"---\\n\")) return source.trim();\n const end = source.indexOf(\"\\n---\", 4);\n if (end === -1) return source.trim();\n return source.slice(end + 4).trim();\n}\n\n/**\n * 선택된 룰들을 읽어 이식형으로 돌려준다. 원본이 없는 이름은 **조용히 건너뛴다** —\n * 설치를 세우는 것보다 낫고, 없는 파일을 깔았다고 보고하지 않는 쪽이 정직하다.\n */\nexport function portRules(harnessRoot: string, ruleNames: ReadonlyArray<string>): PortedRule[] {\n const ported: PortedRule[] = [];\n for (const name of ruleNames) {\n const src = join(harnessRoot, \"templates/rules\", `${name}.md`);\n if (!existsSync(src)) continue;\n ported.push({ name, body: stripClaudeFrontmatter(readFileSync(src, \"utf8\")) });\n }\n return ported;\n}\n\n/**\n * Codex 용 — 룰 전체를 AGENTS.md 에 끼울 한 덩어리로 잇는다.\n *\n * Codex 는 룰 디렉터리가 없고 `AGENTS.md` 계층만 읽는다. 파일 경계가 사라지므로 각 룰의 h1 을\n * h2 로 한 단 낮춰 문서 구조를 유지한다 — 그러지 않으면 h1 이 여러 개인 문서가 되고, 어디까지가\n * 한 룰인지 읽는 쪽이 알 수 없다.\n */\nexport function renderRulesBlock(rules: ReadonlyArray<PortedRule>): string {\n if (rules.length === 0) return \"\";\n return rules.map((r) => r.body.replace(/^#\\s+/, \"## \")).join(\"\\n\\n---\\n\\n\");\n}\n","/**\n * Codex global opt-in — ~/.codex/config.toml trust entry 등록.\n *\n * SPEC: docs/specs/cli-rewrite-completeness.md F11 (Reviewer HIGH-4)\n * Source: bash setup-harness.sh@911c246~1 L1389~1429\n *\n * SAFETY: 사용자 명시 opt-in 없이 ~/.codex/ 글로벌 수정 금지 (D16 / ADR-002 v2 D4).\n * 호출자(installer)는 OptionFlags.withCodexTrust 가 true 일 때만 호출.\n */\n\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { registerTrustEntry } from \"./trust-entry.js\";\n\nexport interface CodexOptInReport {\n /** ~/.codex/config.toml trust entry 등록 결과 */\n trustEntry: {\n enabled: boolean;\n status: \"registered\" | \"already-present\" | \"error\" | \"skipped\";\n message?: string;\n };\n}\n\nexport interface CodexOptInContext {\n /** 사용자 프로젝트 root (trust entry 경로). */\n projectDir: string;\n /** 글로벌 ~/.codex/ 경로 (테스트 override 가능). */\n codexHome?: string;\n}\n\n/** ~/.codex/config.toml 에 프로젝트 trust entry 등록. */\nexport function runCodexOptIn(ctx: CodexOptInContext): CodexOptInReport {\n const codexHome = ctx.codexHome ?? join(homedir(), \".codex\");\n const configPath = join(codexHome, \"config.toml\");\n const result = registerTrustEntry({ configPath, projectDir: ctx.projectDir });\n return {\n trustEntry: {\n enabled: true,\n status: result.status,\n ...(result.message ? { message: result.message } : {}),\n },\n };\n}\n","/**\n * Codex trust entry — `~/.codex/config.toml [projects.\"<dir>\"]` (parity with\n * setup-harness.sh L1404-1422).\n *\n * SAFETY: never modify the global `~/.codex/config.toml` without an explicit\n * opt-in (D16 / ADR-002 v2 D4). Callers must verify user consent first.\n */\n\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\n\nexport interface RegisterTrustResult {\n status: \"registered\" | \"already-present\" | \"error\";\n message?: string;\n}\n\nconst TRUST_BLOCK_REGEX = /\\[projects\\.\"([^\"]+)\"\\]/g;\n\n/** Append a `[projects.\"<projectDir>\"]` block to the user `config.toml` (idempotent). */\nexport function registerTrustEntry(opts: {\n configPath: string;\n projectDir: string;\n}): RegisterTrustResult {\n const { configPath, projectDir } = opts;\n try {\n mkdirSync(dirname(configPath), { recursive: true });\n const existing = existsSync(configPath) ? readFileSync(configPath, \"utf8\") : \"\";\n if (hasTrustEntry(existing, projectDir)) {\n return { status: \"already-present\" };\n }\n const block = `\\n[projects.\"${projectDir}\"]\\ntrust_level = \"trusted\"\\n`;\n writeFileSync(configPath, existing + block);\n return { status: \"registered\" };\n } catch (e: unknown) {\n return {\n status: \"error\",\n message: e instanceof Error ? e.message : String(e),\n };\n }\n}\n\nexport function hasTrustEntry(configContent: string, projectDir: string): boolean {\n const matches = [...configContent.matchAll(TRUST_BLOCK_REGEX)].map((m) => m[1]);\n return matches.includes(projectDir);\n}\n","/**\n * Codex transform orchestrator — wraps the 5-step pipeline.\n *\n * Replaces `scripts/claude-to-codex.sh` (Phase D, OQ4 = TS port).\n *\n * Inputs:\n * - harnessRoot: repository root (templates/ + .mcp.json)\n * - projectDir: target project to receive AGENTS.md + .codex/ + .agents/skills/\n *\n * Outputs (under projectDir):\n * - AGENTS.md\n * - .codex/config.toml\n * - .codex/hooks/*.sh (hooks ported from templates/hooks/)\n * - .agents/skills/<id>/** (dev-method skills — SKILL.md frontmatter 보존 + 형제 파일, #431)\n *\n * v0.6.4 — skill 출력 경로는 Codex 공식 표준 `.agents/skills/<name>/SKILL.md` (repo-level scope).\n * 참조: https://developers.openai.com/codex/skills\n */\n\nimport { chmodSync, existsSync, readFileSync } from \"node:fs\";\nimport { basename, join } from \"node:path\";\nimport { ensureDir } from \"../fs-ops.js\";\nimport type { McpJson } from \"../mcp-merge.js\";\nimport { createOwnedWriter, type OwnedWriteResult } from \"../owned-write.js\";\nimport { renderFillScaffold, withContinuousSkillsNote } from \"../project-claude-merge.js\";\nimport { portRules, renderRulesBlock } from \"../rules-port.js\";\nimport { renderAgentsMd } from \"./agents-md.js\";\nimport { renderConfigToml } from \"./config-toml.js\";\nimport { writeBundledSkillDirs } from \"./skills.js\";\n\nexport interface CodexTransformParams {\n harnessRoot: string;\n projectDir: string;\n /**\n * v26.87.0 — dev-method skill ids 선택 목록 (installer 가 `DEV_METHOD_SKILL_IDS` 를\n * `isAssetSelected` 로 필터). 각 id 의 `templates/skills/<id>/` 를 Codex native\n * `.agents/skills/<id>/` 로 (SKILL.md frontmatter 보존) 출력 — #431 이후 디렉터리 전체다.\n */\n selectedInternalSkills?: ReadonlyArray<string>;\n /** 2026-08-12 — 이 설치의 배포 룰 이름들. Codex 는 룰 디렉터리가 없어 AGENTS.md 본문에 embed 한다. */\n rules?: ReadonlyArray<string>;\n /**\n * v26.133.0 (ADR-048) — 설치 시점 기준선 (install log `externalFiles`).\n *\n * **required 로 둔다.** 옵셔널이면 호출부 하나가 안 넘겨도 컴파일이 통과하고, 그 경로만\n * 조용히 판정 불가로 떨어져 사용자에게 매 설치마다 백업이 쌓인다. 기준선이 없는 상황\n * (레거시 설치)은 빈 Map 을 **명시적으로** 넘겨서 표현한다.\n */\n baseline: ReadonlyMap<string, string>;\n /**\n * v26.134.0 (ADR-049) — `update` 경로. 이미 있는 산출물만 갱신하고 없는 것은 만들지 않는다.\n * 그래서 codex 를 안 깐 프로젝트에서 이 transform 을 돌려도 `.codex/` 가 생기지 않는다.\n */\n refreshOnly?: boolean;\n}\n\nexport interface CodexTransformReport {\n agentsMdPath: string;\n configTomlPath: string;\n hookFiles: string[];\n /** `.agents/skills/<id>/` 에 쓴 **모든** 파일 — `SKILL.md` + 형제(references/scripts 등, #431). */\n skillFiles: string[];\n /** v26.133.0 (ADR-048) — 소유권 결과 (기준선 · 백업된 사용자 편집분). */\n ownership: OwnedWriteResult;\n}\n\nconst HOOK_NAMES = [\"session-start\"];\n\nconst ENV_VAR_RENAME = /CLAUDE_PROJECT_DIR/g;\n\nexport function runCodexTransform(params: CodexTransformParams): CodexTransformReport {\n const {\n harnessRoot,\n projectDir,\n selectedInternalSkills = [],\n rules = [],\n baseline,\n refreshOnly,\n } = params;\n const writer = createOwnedWriter(projectDir, baseline, { refreshOnly: refreshOnly ?? false });\n\n const claudeMd = readRequired(join(harnessRoot, \"templates/CLAUDE.md\"));\n const agentsTemplate = readRequired(join(harnessRoot, \"templates/codex/AGENTS.md.template\"));\n const configTemplate = readRequired(join(harnessRoot, \"templates/codex/config.toml.template\"));\n const projectName = basename(projectDir);\n const mcp = readOptionalJson(join(harnessRoot, \".mcp.json\"));\n\n // 1. AGENTS.md\n const agentsMdPath = join(projectDir, \"AGENTS.md\");\n ensureDir(projectDir);\n const agentsMdOut = renderAgentsMd({\n template: agentsTemplate,\n claudeMd,\n projectName,\n // ADR-085 — 상시 스킬 안내는 앵커가 아니라 여기(프로젝트 맥락)에, 깔린 것만.\n projectContext: withContinuousSkillsNote(\n renderFillScaffold(\"agents-md\"),\n selectedInternalSkills,\n ),\n // Codex 는 룰 디렉터리가 없다 — 룰이 AGENTS.md 본문에 들어가야 도달한다(§Harness Rules).\n harnessRules: renderRulesBlock(portRules(harnessRoot, rules)),\n });\n // 사용자가 채운 AGENTS.md 를 재설치(add 모드) 덮어쓰기 전 보존 — 루트 CLAUDE.md 와 대칭.\n // v26.133.0 (ADR-048) — 내용 비교(backupFileIfChanged)에서 소유자 판정으로 바꿨다. 내용\n // 비교는 하네스가 템플릿을 고친 릴리즈마다 전 사용자에게 백업을 쌓는다 (ADR-047 기각 사유).\n writer.write(agentsMdPath, agentsMdOut);\n\n // 2. .codex/config.toml\n const configTomlPath = join(projectDir, \".codex/config.toml\");\n writer.write(\n configTomlPath,\n renderConfigToml({\n template: configTemplate,\n projectName,\n projectDir,\n mcp,\n }),\n );\n\n // 3. .codex/hooks/session-start.sh\n const hookDir = join(projectDir, \".codex/hooks\");\n const hookFiles: string[] = [];\n for (const hook of HOOK_NAMES) {\n const src = join(harnessRoot, \"templates/hooks\", `${hook}.sh`);\n if (!existsSync(src)) {\n continue;\n }\n const ported = readFileSync(src, \"utf8\").replace(ENV_VAR_RENAME, \"CODEX_PROJECT_DIR\");\n const target = join(hookDir, `${hook}.sh`);\n // refresh 모드가 건너뛴 경로에는 파일이 없다 — chmod 를 무조건 걸면 ENOENT 로 터진다.\n if (!writer.write(target, ported)) continue;\n chmodSync(target, 0o755);\n hookFiles.push(target);\n }\n\n // 4. v26.87.0 — dev-method skills → .agents/skills/<id>/ (frontmatter 보존).\n // renderBundledSkill 이 source frontmatter(name: <id>)를 그대로 보존하고 body 만 포팅.\n // 2026-09-13 (#431) — `SKILL.md` 한 파일이 아니라 **디렉터리 전체**다. 루프는 세 transform\n // 공용 helper 가 소유한다(사본 셋이면 다음 수정이 또 하나를 빠뜨린다).\n const skillFiles = writeBundledSkillDirs({\n harnessRoot,\n projectDir,\n skillIds: selectedInternalSkills,\n writer,\n });\n\n return {\n agentsMdPath,\n configTomlPath,\n hookFiles,\n skillFiles,\n ownership: writer.result(),\n };\n}\n\nfunction readRequired(path: string): string {\n if (!existsSync(path)) {\n throw new Error(`Codex transform: required source missing: ${path}`);\n }\n return readFileSync(path, \"utf8\");\n}\n\nfunction readOptionalJson(path: string): McpJson | null {\n if (!existsSync(path)) {\n return null;\n }\n try {\n return JSON.parse(readFileSync(path, \"utf8\")) as McpJson;\n } catch {\n return null;\n }\n}\n","/**\n * config.toml transform — fill placeholders + append [mcp_servers.X] from .mcp.json.\n * Mirrors `claude-to-codex.sh` steps 2 + 5.\n */\n\nimport type { McpJson } from \"../mcp-merge.js\";\n\nexport interface RenderConfigTomlParams {\n template: string;\n projectName: string;\n projectDir: string;\n /** Source `.mcp.json` (parsed). When provided, [mcp_servers.X] blocks replace defaults. */\n mcp?: McpJson | null;\n}\n\nconst DEFAULT_MCP_BLOCK_RE = /\\n# =+\\n# MCP Servers — .*?\\n# =+[\\s\\S]*$/;\n\n/**\n * Substitute placeholders + replace the MCP servers section with blocks\n * derived from the supplied `.mcp.json` (or leave the template default).\n */\nexport function renderConfigToml(params: RenderConfigTomlParams): string {\n const substituted = params.template\n .replaceAll(\"{PROJECT_NAME}\", params.projectName)\n .replaceAll(\"{PROJECT_DIR}\", params.projectDir)\n .replaceAll(\"{GITHUB_TOKEN}\", \"${GITHUB_TOKEN}\");\n\n if (!params.mcp) {\n return substituted;\n }\n\n const stripped = stripExistingMcpSection(substituted);\n const fresh = renderMcpServers(params.mcp);\n return `${stripped.trimEnd()}\\n${fresh}\\n`;\n}\n\nfunction stripExistingMcpSection(toml: string): string {\n // Drop default [mcp_servers.X] blocks shipped in the template (we replace from .mcp.json)\n const lines = toml.split(/\\r?\\n/);\n const out: string[] = [];\n let skipping = false;\n for (const line of lines) {\n if (line.startsWith(\"[mcp_servers.\")) {\n skipping = true;\n continue;\n }\n if (skipping && line.startsWith(\"[\") && !line.startsWith(\"[mcp_servers.\")) {\n skipping = false;\n }\n if (skipping) {\n continue;\n }\n if (\n /^# .*MCP Servers/.test(line) ||\n /^# Railway MCP/.test(line) ||\n /^# github MCP/.test(line)\n ) {\n continue;\n }\n out.push(line);\n }\n return out.join(\"\\n\").replace(DEFAULT_MCP_BLOCK_RE, \"\");\n}\n\nfunction renderMcpServers(mcp: McpJson): string {\n const stamp = new Date().toISOString().slice(0, 10);\n const header = [\n \"# ============================================================\",\n `# MCP Servers — generated from .mcp.json (${stamp})`,\n \"# ============================================================\",\n ].join(\"\\n\");\n\n const blocks = Object.entries(mcp.mcpServers).map(([name, cfg]) => {\n const lines = [`[mcp_servers.${quoteIfNeeded(name)}]`];\n lines.push(`command = ${jsonString(cfg.command)}`);\n lines.push(`args = ${JSON.stringify(cfg.args)}`);\n if (cfg.env && Object.keys(cfg.env).length > 0) {\n const envBody = Object.entries(cfg.env)\n .map(([k, v]) => `${k} = ${jsonString(v)}`)\n .join(\", \");\n lines.push(`env = { ${envBody} }`);\n }\n return lines.join(\"\\n\");\n });\n\n return [header, \"\", ...blocks].join(\"\\n\");\n}\n\nfunction jsonString(s: string): string {\n return JSON.stringify(s);\n}\n\nfunction quoteIfNeeded(name: string): string {\n return /^[A-Za-z0-9_-]+$/.test(name) ? name : `\"${name}\"`;\n}\n","/**\n * OpenCode transform orchestrator — SSOT (templates/CLAUDE.md, .mcp.json) →\n * OpenCode 자산.\n *\n * Inputs:\n * - harnessRoot: repository root (templates/ + .mcp.json)\n * - projectDir: target project to receive AGENTS.md + opencode.json + .opencode/\n *\n * Outputs (under projectDir):\n * - AGENTS.md\n * - opencode.json\n * - .agents/skills/<id>/** (dev-method skills 디렉터리 전체 — codex·antigravity 와 같은 자리)\n *\n * SPEC: docs/specs/opencode-compat.md\n * Phase: C1 (transform orchestrator)\n */\n\nimport { existsSync, readdirSync, readFileSync, unlinkSync } from \"node:fs\";\nimport { basename, join } from \"node:path\";\nimport { writeBundledSkillDirs } from \"../codex/skills.js\";\nimport { backupFile, ensureDir } from \"../fs-ops.js\";\nimport type { McpJson } from \"../mcp-merge.js\";\nimport { createOwnedWriter, type OwnedWriteResult } from \"../owned-write.js\";\nimport { renderFillScaffold, withContinuousSkillsNote } from \"../project-claude-merge.js\";\nimport { portRules, renderRulesBlock } from \"../rules-port.js\";\nimport { renderAgentsMd } from \"./agents-md.js\";\nimport { renderOpencodeJson } from \"./opencode-json.js\";\n\nexport interface OpencodeTransformParams {\n harnessRoot: string;\n projectDir: string;\n /**\n * dev-method skill ids 선택 목록. installer 가 `DEV_METHOD_SKILL_IDS` 필터로 채움.\n *\n * 2026-08-29 (ADR-081) — `.agents/skills/<id>/SKILL.md` 로 보낸다. v26.87.0 이 커맨드로\n * 변환하던 근거(\"OpenCode 는 native skill 개념이 없다\")가 더는 사실이 아니다.\n */\n selectedInternalSkills?: ReadonlyArray<string>;\n /** 2026-08-12 — 이 설치의 배포 룰 이름들. codex 와 공유하는 `AGENTS.md` 본문에 embed 된다. */\n rules?: ReadonlyArray<string>;\n /**\n * v26.133.0 (ADR-048) — 설치 시점 기준선 (install log `externalFiles`).\n * codex 쪽과 같은 이유로 **required** 다 — 안 넘긴 호출부가 조용히 판정 불가로 떨어지면\n * 그 경로만 매 설치마다 백업이 쌓인다. 레거시는 빈 Map 을 명시적으로 넘긴다.\n */\n baseline: ReadonlyMap<string, string>;\n /**\n * v26.134.0 (ADR-049) — `update` 경로. 이미 있는 산출물만 갱신하고 없는 것은 만들지 않는다.\n * opencode 를 안 깐 프로젝트에서 돌려도 `opencode.json`/`.opencode/` 가 생기지 않는다.\n */\n refreshOnly?: boolean;\n}\n\nexport interface OpencodeTransformReport {\n agentsMdPath: string;\n opencodeJsonPath: string;\n /**\n * `.agents/skills/<id>/` 에 쓴 **모든** 파일 — codex·antigravity 와 같은 자리(같은 파일)다.\n * #431 이후 `SKILL.md` 의 형제(references/scripts 등)도 함께 들어온다.\n */\n skillFiles: string[];\n /** 옛 `.opencode/commands/<id>.md` 를 백업하고 지운 경로들 (ADR-081 전환 뒷정리). */\n retiredCommands: string[];\n /** v26.133.0 (ADR-048) — 소유권 결과 (기준선 · 백업된 사용자 편집분). */\n ownership: OwnedWriteResult;\n}\n\nexport function runOpencodeTransform(params: OpencodeTransformParams): OpencodeTransformReport {\n const {\n harnessRoot,\n projectDir,\n selectedInternalSkills = [],\n rules = [],\n baseline,\n refreshOnly,\n } = params;\n const writer = createOwnedWriter(projectDir, baseline, { refreshOnly: refreshOnly ?? false });\n\n const claudeMd = readRequired(join(harnessRoot, \"templates/CLAUDE.md\"));\n const agentsTemplate = readRequired(join(harnessRoot, \"templates/opencode/AGENTS.md.template\"));\n const opencodeTemplate = readRequired(\n join(harnessRoot, \"templates/opencode/opencode.json.template\"),\n );\n const projectName = basename(projectDir);\n const mcp = readOptionalJson(join(harnessRoot, \".mcp.json\"));\n\n // 1. AGENTS.md\n ensureDir(projectDir);\n const agentsMdPath = join(projectDir, \"AGENTS.md\");\n const agentsMdOut = renderAgentsMd({\n template: agentsTemplate,\n claudeMd,\n projectName,\n // ADR-085 — 상시 스킬 안내는 앵커가 아니라 여기(프로젝트 맥락)에, 깔린 것만.\n projectContext: withContinuousSkillsNote(\n renderFillScaffold(\"agents-md\"),\n selectedInternalSkills,\n ),\n // codex 와 **같은 파일**(프로젝트 루트 `AGENTS.md`)이다. 두 transform 이 서로 다른 본문을\n // 쓰면 나중에 도는 쪽이 앞선 쪽을 덮어써, codex+opencode 조합에서 룰이 통째로 사라진다\n // (독립 검증 C-1 실측). 같은 내용을 쓰면 순서가 결과를 바꾸지 않는다.\n harnessRules: renderRulesBlock(portRules(harnessRoot, rules)),\n });\n // 사용자가 채운 AGENTS.md 를 재설치(add 모드) 덮어쓰기 전 보존 — 루트 CLAUDE.md 와 대칭.\n // v26.133.0 (ADR-048) — 내용 비교에서 소유자 판정으로. codex 가 같은 install 안에서 이미\n // 쓴 AGENTS.md 를 여기서 '사용자 편집'으로 오판하면 매 설치마다 백업이 생긴다.\n writer.write(agentsMdPath, agentsMdOut);\n\n // 2. opencode.json\n const opencodeJsonPath = join(projectDir, \"opencode.json\");\n writer.write(opencodeJsonPath, renderOpencodeJson({ template: opencodeTemplate, mcp }));\n\n // 3. dev-method skills → `.agents/skills/<id>/SKILL.md` (ADR-081).\n //\n // 실측 2026-08-29 (`opencode 1.18.23`, 컨테이너, 대조군 포함): OpenCode 는 프로젝트\n // 스코프 `.agents/skills/<id>/SKILL.md` 를 **자동 로드**하고, 그렇게 실린 스킬이\n // 커맨드 목록에도 `source: \"skill\"` 로 함께 뜬다. 즉 슬래시 호출을 잃지 않으면서\n // 모델이 description 을 보고 스스로 부를 수 있게 된다.\n //\n // codex·antigravity 와 **같은 파일**이다. 셋이 한 벌을 공유하므로 조합 설치에서\n // 같은 스킬이 두 판본으로 깔리는 일이 없다 — 그것이 #340 의 형태였다.\n //\n // 2026-09-13 (#431) — `SKILL.md` 한 파일이 아니라 디렉터리 전체를 보낸다. 루프는 세\n // transform 공용 helper 가 소유한다.\n const skillFiles = writeBundledSkillDirs({\n harnessRoot,\n projectDir,\n skillIds: selectedInternalSkills,\n writer,\n });\n\n // 4. 옛 커맨드 사본 은퇴. 안 지우면 OpenCode 커맨드 목록에 같은 이름이 **두 줄**로 뜬다\n // (옛 `source: \"command\"` + 새 `source: \"skill\"`). 대상은 번들 스킬 이름인 것만 —\n // 목록을 적지 않고 `templates/skills/<이름>/SKILL.md` 존재로 유도한다. 사용자가 직접\n // 쓴 커맨드는 그 조건에 안 걸린다.\n //\n // 판정 대신 **백업하고 지운다**: 우리가 렌더한 파일이지만 사용자가 고쳤을 수 있고,\n // 그 편집분을 되살릴 방법이 없다(`retireMcpAllowlist` 와 같은 이유). 파일이 다시\n // 생기지 않으므로 이 백업은 프로젝트당 한 번뿐이다.\n const retiredCommands: string[] = [];\n const cmdDir = join(projectDir, \".opencode/commands\");\n if (existsSync(cmdDir)) {\n for (const entry of readdirSync(cmdDir)) {\n if (!entry.endsWith(\".md\")) continue;\n const id = entry.slice(0, -3);\n if (!existsSync(join(harnessRoot, \"templates/skills\", id, \"SKILL.md\"))) continue;\n const victim = join(cmdDir, entry);\n try {\n backupFile(victim);\n unlinkSync(victim);\n retiredCommands.push(victim);\n } catch {\n // 뒷정리라 실패해도 설치를 세우지 않는다 (read-only 디렉터리 등).\n }\n }\n }\n\n return {\n agentsMdPath,\n opencodeJsonPath,\n skillFiles,\n retiredCommands,\n ownership: writer.result(),\n };\n}\n\nfunction readRequired(path: string): string {\n if (!existsSync(path)) {\n throw new Error(`OpenCode transform: required source missing: ${path}`);\n }\n return readFileSync(path, \"utf8\");\n}\n\nfunction readOptionalJson(path: string): McpJson | null {\n if (!existsSync(path)) {\n return null;\n }\n try {\n return JSON.parse(readFileSync(path, \"utf8\")) as McpJson;\n } catch {\n return null;\n }\n}\n","/**\n * AGENTS.md transform — CLAUDE.md → AGENTS.md (OpenCode flavor).\n *\n * Mirrors `src/codex/agents-md.ts` logic (Codex와 OpenCode 둘 다 콜론 namespace\n * 미사용으로 slash rename 동일). 별도 파일로 유지 — 모듈 독립성.\n *\n * v26.70.0 — section 추출 → CLAUDE.md 전문 embed (`{PROJECT_RULES}`). codex/agents-md 와 동일 fix.\n */\n\n/** Rename Claude slash conventions (`/uzys:foo`) to OpenCode (`/uzys-foo`). */\nexport function renameSlashes(text: string): string {\n return text.replaceAll(\"/uzys:\", \"/uzys-\");\n}\n\nexport interface AgentsMdParams {\n template: string;\n claudeMd: string;\n projectName: string;\n /** Project-context fill scaffold — the same body shipped to the Claude Code CLAUDE.md. */\n projectContext: string;\n /**\n * 2026-08-12 — 배포 룰 본문. **codex 와 같은 파일**(프로젝트 루트 `AGENTS.md`)을 쓰므로 두\n * 렌더러가 같은 값을 넣어야 한다 — 한쪽만 넣으면 나중에 도는 transform 이 룰을 지운다.\n */\n harnessRules?: string;\n}\n\n/**\n * Render the OpenCode AGENTS.md output by embedding the full CLAUDE.md body.\n *\n * Placeholders (matches templates/opencode/AGENTS.md.template):\n * - {PROJECT_NAME} — basename of project dir\n * - {PROJECT_RULES} — full CLAUDE.md body (first h1 stripped)\n * - {PROJECT_CONTEXT} — project-specific fill scaffold (renderFillScaffold())\n */\nexport function renderAgentsMd(params: AgentsMdParams): string {\n const body = params.claudeMd.replace(/^#\\s+.*\\r?\\n/, \"\").trim();\n const replaced = params.template\n .replaceAll(\"{PROJECT_NAME}\", params.projectName)\n .replaceAll(\"{PROJECT_RULES}\", body)\n .replaceAll(\"{PROJECT_CONTEXT}\", params.projectContext)\n .replaceAll(\"{HARNESS_RULES}\", params.harnessRules ?? \"\");\n return renameSlashes(replaced);\n}\n","/**\n * opencode.json transform — fill template + merge mcp.<name> from .mcp.json.\n *\n * Output: opencode.json (top-level `mcp.<name>` map; OpenCode 1:1 매핑).\n *\n * Codex `[mcp_servers.X]` TOML과 다르게 OpenCode는 JSON이라 Object spread\n * 만으로 충분 (TOML 직렬화 없음).\n */\n\nimport type { McpJson, McpServerConfig } from \"../mcp-merge.js\";\n\nexport interface RenderOpencodeJsonParams {\n /** Template content (templates/opencode/opencode.json.template). */\n template: string;\n /** Source `.mcp.json` (parsed). When provided, top-level `mcp.<name>` is replaced. */\n mcp?: McpJson | null;\n}\n\ninterface OpencodeConfig {\n $schema?: string;\n instructions?: string[];\n mcp?: Record<string, McpServerConfig>;\n command?: Record<string, unknown>;\n agent?: Record<string, unknown>;\n plugin?: string[];\n permission?: Record<string, unknown>;\n [key: string]: unknown;\n}\n\n/**\n * Substitute `mcp` in the template with entries from `.mcp.json`.\n * Other keys (`agent`, `command`, `plugin`, `permission`, `instructions`,\n * `$schema`) are preserved from the template.\n */\nexport function renderOpencodeJson(params: RenderOpencodeJsonParams): string {\n const config = parseTemplate(params.template);\n\n if (params.mcp) {\n config.mcp = { ...params.mcp.mcpServers };\n }\n\n return `${JSON.stringify(config, null, 2)}\\n`;\n}\n\nfunction parseTemplate(template: string): OpencodeConfig {\n try {\n return JSON.parse(template) as OpencodeConfig;\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n throw new Error(`opencode.json template invalid JSON: ${message}`);\n }\n}\n","/**\n * env-files.ts — 환경 파일 자동 생성.\n *\n * SPEC: docs/specs/cli-rewrite-completeness.md F7, F8\n * Source: bash setup-harness.sh@911c246~1 L880~890 + L954~996.\n *\n * 2 종 산출:\n * 1. .env.example (csr-supabase / full Track) — Supabase 토큰 가이드\n * 2. .gitignore .env 라인 추가 (없을 때만)\n *\n * 모두 idempotent — 이미 있으면 skip.\n *\n * 2026-08-16 (ADR-072) — `.mcp-allowlist` 생성기 제거. 그 파일을 읽던 `mcp-pre-exec.sh` 훅이\n * 목적 부적합으로 빠지면서(루트 `CLAUDE.md` §판정은 목적에서 시작한다) 읽는 쪽이 없어졌다.\n * 생성기만 남기면 아무도 안 보는 파일을 남의 저장소에 계속 만든다.\n */\n\nimport { appendFileSync, existsSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { Track } from \"./types.js\";\n\nconst ENV_EXAMPLE_BODY = `# .env.example — csr-supabase Track\n# Copy to .env (gitignored) and fill in values: cp .env.example .env\n\n# ===== Supabase Management API (MCP server용) =====\n# Personal Access Token — @supabase/mcp-server가 프로젝트 생성/마이그레이션/Edge Functions 배포에 사용\n# 발급: https://supabase.com/dashboard/account/tokens\nSUPABASE_ACCESS_TOKEN=\n\n# 프로젝트 참조 ID (예: \"abcdefghijklmnop\")\n# 위치: Supabase Dashboard → Project Settings → General\nSUPABASE_PROJECT_REF=\n\n# DB 패스워드 (supabase db push 등 직접 DB 접근용)\n# 위치: Supabase Dashboard → Project Settings → Database\nSUPABASE_DB_PASSWORD=\n\n# ===== Frontend (public, 클라이언트 노출 OK) =====\n# 위치: Supabase Dashboard → Project Settings → API\nNEXT_PUBLIC_SUPABASE_URL=\nNEXT_PUBLIC_SUPABASE_ANON_KEY=\n\n# ===== Optional — 앱 측 AI 기능용 =====\n# OPENAI_API_KEY=\n# ANTHROPIC_API_KEY=\n\n# ===== Note =====\n# - Vercel/Netlify는 별도 CLI login 사용 (env 불필요): vercel login / netlify login\n# - Supabase CLI(supabase login)는 OAuth로 ~/.config/supabase/에 토큰 저장 — env 별개\n# - .env는 .gitignore됨 (자동 추가). 절대 commit 금지.\n`;\n\nconst ENV_EXAMPLE_TRACKS: ReadonlyArray<Track> = [\"csr-supabase\", \"full\"];\n\nconst GITIGNORE_ENV_PATTERN = /^\\.env$|^\\.env\\s/m;\n\n/**\n * .env.example 생성 (csr-supabase/full Track 한정, idempotent).\n * @returns true if created, false if skipped (already exists or non-applicable track)\n */\nexport function writeEnvExample(projectDir: string, tracks: ReadonlyArray<Track>): boolean {\n if (!tracks.some((t) => ENV_EXAMPLE_TRACKS.includes(t))) {\n return false;\n }\n const path = join(projectDir, \".env.example\");\n if (existsSync(path)) {\n return false;\n }\n writeFileSync(path, ENV_EXAMPLE_BODY);\n return true;\n}\n\n/**\n * .gitignore에 `.env` 라인 추가. 이미 있으면 skip.\n * @returns true if appended, false if skipped (no .gitignore or .env already listed)\n */\nexport function addGitignoreEnv(projectDir: string): boolean {\n const path = join(projectDir, \".gitignore\");\n if (!existsSync(path)) {\n return false;\n }\n const content = readFileSync(path, \"utf8\");\n if (GITIGNORE_ENV_PATTERN.test(content)) {\n return false;\n }\n // append with separator (avoid double newlines)\n const sep = content.endsWith(\"\\n\") ? \"\" : \"\\n\";\n appendFileSync(path, `${sep}\\n# Secret env (auto-added by agent-harness install)\\n.env\\n`);\n return true;\n}\n\nconst AGENT_ARTIFACT_DIRS = [\".factory/\", \".goose/\", \".uzys-agent-harness/\"];\nconst GITIGNORE_AGENT_ARTIFACT_HEADER =\n \"# agent CLI / harness 자동 생성물 (auto-added by agent-harness)\";\n\n/**\n * v0.8.0 — `.gitignore`에 `.factory/`, `.goose/` 패턴 추가 (사용자 보고 #3).\n *\n * `npx skills add`가 multi-CLI universal install 동작 — Codex 사용자 환경에서\n * `.factory/skills/`, `.goose/skills/` 자동 생성. 사용자 git noise 회피용 ignore.\n *\n * 2026-08-02 — `.uzys-agent-harness/` 추가. 설치 로그가 살던 자리에 훅 **차단 로그**\n * (`hook-blocks.log`)가 합류하면서, 차단이 일어날 때마다 사용자 리포에 추적되는 파일이\n * 늘어나게 됐다. 계측이 남의 저장소를 더럽히면 안 된다.\n *\n * idempotent — 이미 있으면 skip.\n * @returns added pattern list (empty if all already present or no .gitignore)\n */\nexport function addGitignoreAgentArtifacts(projectDir: string): string[] {\n const path = join(projectDir, \".gitignore\");\n if (!existsSync(path)) {\n return [];\n }\n const content = readFileSync(path, \"utf8\");\n const missing = AGENT_ARTIFACT_DIRS.filter((pattern) => {\n // exact line match (이스케이프 후 줄 단위 비교 — 단순화: 문자열 포함)\n const lineRegex = new RegExp(`^${pattern.replace(/\\./g, \"\\\\.\").replace(/\\//g, \"/\")}\\\\s*$`, \"m\");\n return !lineRegex.test(content);\n });\n if (missing.length === 0) {\n return [];\n }\n const sep = content.endsWith(\"\\n\") ? \"\" : \"\\n\";\n const block = [GITIGNORE_AGENT_ARTIFACT_HEADER, ...missing].join(\"\\n\");\n appendFileSync(path, `${sep}\\n${block}\\n`);\n return [...missing];\n}\n","/**\n * External installer — `EXTERNAL_ASSETS` 매트릭스를 실제 호출로 변환.\n *\n * SPEC: docs/specs/cli-rewrite-completeness.md F1\n *\n * Decision (OQ1): 실패는 warn-skip. 종료 시 누락 자산 목록 보고.\n * abort는 첫 실행 신뢰성을 깨뜨리므로 채택 안 함 (vibe killer).\n *\n * Spawning은 `child_process.spawnSync` 사용. command/args 분리로 shell injection 차단.\n * stdout/stderr는 captured — 사용자에게 한 줄 요약만 노출 (verbose-log는 별도 옵션 후속).\n */\n\nimport { type SpawnSyncReturns, spawnSync } from \"node:child_process\";\nimport { existsSync, readdirSync, readFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { CATEGORIES as CATEGORY_ORDER } from \"./categories.js\";\nimport {\n assetReachesCli,\n EXTERNAL_ASSETS,\n type ExternalAsset,\n type ExternalAssetMethod,\n filterApplicableAssets,\n} from \"./external-assets.js\";\nimport { type InstallLog, readInstallLog } from \"./install-log.js\";\nimport {\n type CliTargets,\n DEFAULT_OPTIONS,\n type InstallScope,\n type OptionFlags,\n resolveScope,\n type Track,\n} from \"./types.js\";\n\nexport interface ExternalInstallerDeps {\n /** Override `spawnSync` for tests (mock으로 호출 횟수 + args 검증). */\n spawn?: (cmd: string, args: ReadonlyArray<string>, opts: SpawnOpts) => SpawnSyncReturns<string>;\n /** harness root (prune-ecc.sh script 위치 resolve용). */\n harnessRoot?: string;\n /** asset 매트릭스 override (테스트용, 기본 EXTERNAL_ASSETS 전체). */\n assets?: ReadonlyArray<ExternalAsset>;\n /** 진행 상황 로그 stream (기본 console.log). 일반 로그용. */\n log?: (msg: string) => void;\n /** 경고 메시지 stream (기본 console.error). */\n warn?: (msg: string) => void;\n /**\n * 자산 설치 시작 직전 호출 (streaming UI용).\n * Renderer가 \"→ asset (installing...)\" 라인 출력에 사용.\n */\n onAssetStart?: (asset: ExternalAsset) => void;\n /**\n * 자산 설치 완료 후 호출 (streaming UI용).\n * Renderer가 \"✓/⊘ asset meta\" 라인 출력에 사용.\n */\n onAssetResult?: (result: AssetInstallResult) => void;\n}\n\ninterface SpawnOpts {\n encoding: \"utf8\";\n stdio: (\"ignore\" | \"pipe\")[] | \"ignore\" | \"pipe\";\n timeout?: number;\n /** v26.77.0 — 작업 디렉토리. projectDir 로 고정해 자산이 올바른 프로젝트에 착지. */\n cwd?: string;\n}\n\nexport interface AssetInstallResult {\n asset: ExternalAsset;\n ok: boolean;\n /** ok=false 시 user-facing 메시지 */\n message?: string;\n /**\n * v26.59.0 — 설치된 자산 version. install 후 detectVersion 으로 path 기반 추출.\n * plugin: ~/.claude/plugins/cache/<marketplace>/<plugin>/<VERSION>/ 디렉토리명\n * npm-global: <npm root -g>/<pkg>/package.json 의 version\n * 그 외 method (skill, npx-run, shell-script): 표준 metadata 없음 → undefined.\n */\n version?: string;\n}\n\nexport interface ExternalInstallReport {\n /** 적용 시도된 자산 (조건 통과한 것만) */\n attempted: ReadonlyArray<AssetInstallResult>;\n /** 성공 갯수 */\n succeeded: number;\n /** warn-skip 된 갯수 */\n skipped: number;\n /**\n * v26.102.0 (ADR-031) — 조건은 통과했으나 선택 CLI 와 도달 범위(assetCliSupport)의\n * 교집합이 없어 **시도조차 하지 않은** 자산 (예: codex 단독 설치의 claude 전용 plugin).\n * 침묵 제외 금지 — render 가 이 목록을 사용자에게 고지한다 (no-false-ship).\n */\n excludedByCli: ReadonlyArray<ExternalAsset>;\n}\n\nconst DEFAULT_SPAWN_TIMEOUT_MS = 120_000;\n/**\n * #422 — `npm install` 계열은 큰 CLI(netlify-cli 26.x 는 413 MB · 컨테이너 실측 86초)를 받는다.\n * 120초는 느린 망·기존 lockfile 해석에서 넘기고, 그동안 화면은 침묵이라 설치자에게는 \"멈춤\"이다.\n * 진행 표시는 렌더러(`onAssetStart`)가, 시간 상한은 여기서 — 넘기면 직접 돌릴 명령을 알려 준다.\n */\nconst NPM_SPAWN_TIMEOUT_MS = 600_000;\n\n/** method.kind 별 spawn 시간 상한 — 패키지 설치는 길고, 그 밖은 기본값. */\nfunction spawnTimeoutFor(kind: ExternalAsset[\"method\"][\"kind\"]): number {\n return kind === \"npm\" || kind === \"npx-run\" ? NPM_SPAWN_TIMEOUT_MS : DEFAULT_SPAWN_TIMEOUT_MS;\n}\n\n/**\n * v26.102.0 (ADR-031) — external 단계의 대상/배제 판정 **단일 지점**. 규칙 = 조건 통과 ∧\n * non-internal(Phase 1 담당) ∧ 선택 CLI 도달. runExternalInstall(시도 목록)과\n * runExternalPhase(헤더 카운트)가 이 함수만 호출한다 — 같은 규칙을 두 파일에 각각 기술하면\n * 3번째 조건이 생길 때 카운트만 조용히 어긋난다 (SOD 리뷰 Important-6, no-false-ship\n * \"동일 목록 2곳 하드코딩 금지\").\n */\nexport function selectExternalTargets(\n assets: ReadonlyArray<ExternalAsset>,\n ctx: {\n tracks: ReadonlyArray<Track>;\n options: OptionFlags;\n cli: CliTargets;\n userOverride?: { forceInclude: ReadonlyArray<string>; forceExclude: ReadonlyArray<string> };\n },\n): { targets: ExternalAsset[]; excludedByCli: ExternalAsset[] } {\n const conditionPassed = filterApplicableAssets(assets, ctx).filter(\n (a) => a.method.kind !== \"internal\",\n );\n return {\n targets: conditionPassed.filter((a) => assetReachesCli(a, ctx.cli)),\n excludedByCli: conditionPassed.filter((a) => !assetReachesCli(a, ctx.cli)),\n };\n}\n\n/**\n * spec에 적용 가능한 자산을 모두 시도. 실패는 warn-skip (기본).\n */\nexport function runExternalInstall(\n ctx: {\n tracks: ReadonlyArray<Track>;\n options: OptionFlags;\n cli: CliTargets;\n /** v26.47.0 — Phase C full user override (forceInclude/forceExclude). */\n userOverride?: { forceInclude: ReadonlyArray<string>; forceExclude: ReadonlyArray<string> };\n /** v26.64.0 (ADR-020) — Install scope. undefined → default \"project\". */\n scope?: InstallScope;\n /**\n * v26.77.0 — 외부 설치기 spawn 의 작업 디렉토리.\n * 미지정 시 process.cwd(). 핵심: npm(--save-dev)·npx-run(bmad --directory .)·\n * plugin(--scope project, claude 의 cwd 기반 project 탐지)·skill 이 모두 cwd 기준으로\n * 착지하므로, --project-dir 가 cwd 와 다르면 cwd 를 projectDir 로 맞춰야 자산이 올바른\n * 프로젝트에 떨어진다 (이 누락 = 2026-06-07 probe 가 repo 를 오염시킨 근본 원인).\n */\n projectDir?: string;\n },\n deps: ExternalInstallerDeps = {},\n): ExternalInstallReport {\n const log = deps.log ?? console.log;\n const warn = deps.warn ?? console.error;\n const spawn = deps.spawn ?? defaultSpawn;\n const assets = deps.assets ?? EXTERNAL_ASSETS;\n const harnessRoot = deps.harnessRoot ?? process.cwd();\n const projectDir = ctx.projectDir ?? process.cwd();\n\n // v26.81.0 (ADR-022) — internal 자산(tauri-desktop)은 Phase 1 의\n // manifest/transform 이 설치 주체 — external(spawn) 단계에서 제외. Phase 1 의\n // templates 행으로 사용자에게 이미 가시화됨 (중복 보고 방지).\n // v26.102.0 (ADR-031, Batch3) — CLI 도달 범위 필터. 선택 CLI 와 교집합 없는 자산은\n // spawn 자체를 배제한다: codex 단독 설치가 claude 전용 plugin 을 `claude plugin ...` 으로\n // spawn 해 ~/.claude/plugins 를 오염시키던 P0 [4cli-asymmetry-cluster] 의 구조적 fix.\n // 제외분은 excludedByCli 로 보고 — 침묵 제외 금지 (no-false-ship).\n const { targets: applicable, excludedByCli } = selectExternalTargets(assets, ctx);\n // v26.55.0 — Phase 2 grouped progress UX. 카테고리 순서로 정렬 → install.ts 의 onAssetStart\n // callback 이 category 변경 감지로 헤더 출력 가능. ADR-016.\n const sorted = [...applicable].sort((a, b) => {\n const ai = CATEGORY_ORDER.indexOf(a.category);\n const bi = CATEGORY_ORDER.indexOf(b.category);\n return ai - bi;\n });\n const attempted: AssetInstallResult[] = [];\n const cli = ctx.cli;\n const scope = resolveScope(ctx.scope);\n\n for (const asset of sorted) {\n deps.onAssetStart?.(asset);\n log(` → ${asset.description}`);\n const baseResult = installOne(asset, { spawn, harnessRoot, cli, scope, projectDir });\n let result: AssetInstallResult = baseResult;\n if (baseResult.ok) {\n const v = detectVersion(asset.method, spawn);\n if (v) result = { ...baseResult, version: v };\n }\n deps.onAssetResult?.(result);\n\n if (!result.ok) {\n // v26.79.0 — 모든 실패는 warn-skip (abort 는 vibe killer 라 미채택). 죽은\n // failureMode/aborted 메커니즘 제거 (사용 자산 0 + 렌더러 미참조).\n warn(` [warn-skip] ${asset.id}: ${result.message ?? \"failed\"}`);\n }\n\n attempted.push(result);\n }\n\n return {\n attempted,\n succeeded: attempted.filter((r) => r.ok).length,\n skipped: attempted.filter((r) => !r.ok).length,\n excludedByCli,\n };\n}\n\n/**\n * 자산 1개 설치. method.kind 별 적절한 명령 실행.\n */\nfunction installOne(\n asset: ExternalAsset,\n ctx: {\n spawn: NonNullable<ExternalInstallerDeps[\"spawn\"]>;\n harnessRoot: string;\n cli: CliTargets;\n scope: InstallScope;\n /** v26.77.0 — spawn cwd. 자산이 올바른 프로젝트에 착지하도록 projectDir 로 고정. */\n projectDir: string;\n },\n): AssetInstallResult {\n const { method } = asset;\n const cwd = ctx.projectDir;\n switch (method.kind) {\n case \"skill\":\n return runSpawn(asset, ctx.spawn, \"npx\", buildSkillArgs(method, ctx.cli, ctx.scope), cwd);\n case \"plugin\":\n return installPlugin(asset, ctx.spawn, method, ctx.scope, cwd);\n case \"npm\": {\n // v26.64.0 (ADR-020) — scope=project 시 devDep, scope=global 시 -g.\n // v26.68.0 — method.kind \"npm-global\" → \"npm\" rename (scope 분기와 무관 의미).\n // v26.80.0 — pinned 버전 설치 (`pkg@version`). vetting 시점의 코드만 실행 (보안 wedge).\n const pinned = `${method.pkg}@${method.version}`;\n return runSpawn(\n asset,\n ctx.spawn,\n \"npm\",\n ctx.scope === \"global\" ? [\"install\", \"-g\", pinned] : [\"install\", \"--save-dev\", pinned],\n cwd,\n );\n }\n case \"npx-run\":\n // v26.80.0 — pinned 버전 실행 (`cmd@version`). 이전 `cmd` 에 \"@latest\" 인라인이던 것을\n // 구조 필드로 분리 (cmd 는 bare 이름 — drift override/라벨이 이름 그대로 사용).\n return runSpawn(\n asset,\n ctx.spawn,\n \"npx\",\n [`${method.cmd}@${method.version}`, ...(method.args ?? [])],\n cwd,\n );\n case \"shell-script\": {\n const scriptPath = join(ctx.harnessRoot, method.script);\n if (!existsSync(scriptPath)) {\n return {\n asset,\n ok: false,\n message: `script not found: ${scriptPath}`,\n };\n }\n return runSpawn(asset, ctx.spawn, \"bash\", [scriptPath, ...method.args], cwd);\n }\n case \"internal\":\n // v26.81.0 (ADR-022) — 도달 불가 (runExternalInstall 이 사전 필터). exhaustive switch\n // + 방어: 도달해도 spawn 없이 ok (Phase 1 manifest 가 실 설치 주체).\n return { asset, ok: true, message: \"internal template (installed by Phase 1 manifest)\" };\n }\n}\n\n/**\n * v26.39.5 fix — `--agent <list>` 명시 추가 (사용자 보고 #3 진짜 fix).\n *\n * `npx skills add` default 동작은 `*` (all installed agents) → universal install →\n * `.factory/skills/`, `.goose/skills/` 자동 생성. v0.8.0 의 `.gitignore` 패턴 추가만으론\n * git noise 만 차단하고 disk 디렉토리 생성은 막지 못함.\n *\n * 본 fix: `spec.cli` 의 base CLI 만 콤마 구분 명시 → 의도된 agent 만 install.\n *\n * v26.39.6 fix — skills CLI agent name 매핑.\n * skills CLI 1.5.5 valid agent 이름은 `claude-code` 인데 우리 CliBase 는 `claude`.\n * 매핑 누락 시 `Invalid agents: claude` 로 exit 1 → 외부 사용자 (실사용 리포\n * reproduce 2026-05-06) 환경에서 7건 skill 자산 100% skip.\n */\nconst SKILLS_CLI_AGENT_MAP: Record<CliTargets[number], string> = {\n claude: \"claude-code\",\n codex: \"codex\",\n opencode: \"opencode\",\n // v26.66.0 — Antigravity (Google) skills agent. `.agents/skills/` 표준 공유 (codex transform 산출과 동일).\n antigravity: \"antigravity\",\n};\n\n/**\n * `npx skills` CLI 고정 버전 — unpinned 면 upstream breaking 이 설치·검증을 동시에 깬다\n * (1.5.5→1.5.7 multi-agent `--agent` 플래그 파손 전례). bump 시 Docker 시나리오 재검증 필수.\n * audit CODE-4/D-1. scripts/verify-catalog.mjs 와 동일 값 유지 (drift 테스트 가드).\n */\nexport const SKILLS_CLI_VERSION = \"1.5.11\";\n\n/** `npx skills <subcommand>` 의 첫 인자 — 항상 버전 고정. */\nexport function skillsCliSpec(): string {\n return `skills@${SKILLS_CLI_VERSION}`;\n}\n\n/** `update` 가 외부 스킬을 갱신했는지에 대한 보고 (#374). */\nexport interface ExternalSkillRefresh {\n /** 갱신을 시도한 자산 수. 0 = 설치 기록에 외부 스킬이 없다. */\n attempted: number;\n /** 그중 성공한 수. */\n refreshed: number;\n /** 실패한 자산 — 화면에 이름을 낸다. 실패해도 update 는 계속한다. */\n failed: ReadonlyArray<{ id: string; message: string }>;\n /**\n * 설치 기록에는 있는데 **지금 카탈로그에 없는** 스킬 자산 id — 갱신 대상이 아니다.\n *\n * 침묵하면 사용자는 깔린 3종 중 2종만 갱신됐다는 사실을 알 수 없다. 이 저장소는 실제로\n * 자산을 지운 적이 있다(`north-star-skill`) — 그 릴리즈 뒤 옛 설치본이 도는 자리다.\n */\n notInCatalog: ReadonlyArray<string>;\n /**\n * **설치 기록이 없어 무엇을 갱신해야 하는지 판정할 수 없다** (레거시 설치본).\n *\n * `attempted: 0` 과 구분한다 — 그쪽은 \"갱신할 게 없다\"이고 이쪽은 \"모른다\"다. 둘을 같은\n * 침묵으로 합치면 이 이슈가 고치려던 실패 형태(조용한 무동작)가 그대로 남는다.\n */\n unknown: boolean;\n}\n\n/**\n * 이미 깔린 외부 스킬을 상류 최신판으로 다시 받는다 (#374).\n *\n * **왜 있나**: `update` 는 우리가 놓아둔 정책 파일과 우리가 렌더한 CLI 산출물만 새로 썼다.\n * `npx skills add` 로 깐 스킬 본문은 그 경로에 없어서 **한 번도 갱신되지 않았다**(실측\n * 2026-08-27, npx 호출 추적: install 2회 · update 0회). 그런데 화면에는\n * `✓ external CLI artifacts` 가 떠서 다 된 것처럼 보였다.\n *\n * **`skills update` 를 쓰지 않는 이유** (독립 리뷰가 잡은 CRITICAL, 실측 2026-08-27):\n * 그 서브명령은 **`--copy` 도 `--agent` 도 받지 않는다**(도움말의 *Update Options* = `-g`·`-p`·`-y`\n * 뿐). 그래서 자기 기본 배치로 스킬 자리를 다시 만들고, 결과가 이렇다:\n *\n * claude 단독 설치 · install 직후 → `.claude/skills/<id>` = 실제 디렉터리 · `.agents/` 없음\n * 같은 프로젝트에 `skills update` → `.claude/skills/<id>` = **`.agents/` 로의 심링크**,\n * 고른 적 없는 `.agents/` 트리가 **생성**됨\n *\n * 이건 #372 가 세운 \"두 자리에 각각 실제 사본\" 계약을 되돌린다. 측정된 해악은 셋이다:\n * ⓐ 고른 적 없는 CLI 의 자산 트리가 생긴다(ADR-031 이 P0 로 닫은 형태) ⓑ 사용자가 정체 모를\n * `.agents/` 를 지우면 `.claude/skills/*` 는 **끊긴 링크**가 되어 본문이 사라진다\n * ⓒ 슬롯 이름이 번들 스킬과 겹치면 `foreign-slot.ts` 가 디렉터리 아닌 자리를 \"남의 것\"으로\n * 판정해(#343) 하네스 writer 가 건너뛴다.\n *\n * *(초판은 여기에 \"그 뒤로 우리 최신본이 영영 그 자리에 안 들어간다\"고 적었는데 **틀렸다** —\n * 외부 스킬 id 는 `templates/skills/` 에 없어 우리 writer 가 그 자리를 겨냥하지 않고, 실측에서\n * 이 구현은 이미 심링크가 된 슬롯을 오히려 디렉터리로 **복구한다**. 독립 리뷰가 반증했다.)*\n *\n * **그래서 install 과 같은 호출**(`add … --agent … --copy --yes`)을 다시 돌린다. 실측으로\n * 재실행은 상류 최신판을 받아오고(변이 1 → 0) 두 자리가 실제 디렉터리로 유지된다.\n * 같은 함수(`runExternalInstall`)를 쓰는 이유는 ADR-049 와 같다 — 인자 조립을 두 벌 두면\n * 한쪽만 고쳐지는 순간 조용히 갈린다.\n *\n * **대상은 설치 기록에 적힌 스킬 자산뿐이다.** 트랙·옵션에서 다시 유도하지 않는다 — 그러면\n * 사용자가 고른 적 없는 자산이 update 로 새로 깔릴 수 있고, 반대로 opt-in 으로 깐 자산이\n * 조건 불일치로 조용히 빠진다. `forceInclude` 로 조건 판정을 우회해 **깐 것을 깐 그대로**\n * 다시 받는다.\n *\n * **그 우회는 신뢰 티어 게이트도 지난다** — `shouldInstallAsset` 이 `forceInclude` 를 tier\n * 검사보다 앞에서 본다. 즉 자산이 나중에 experimental(T3)로 강등돼도 이미 깐 사람은 갱신을\n * 계속 받는다. 이건 부작용이 아니라 **결정**이다(ADR-078 Consequences): update 는 무엇을\n * 깔지 다시 묻는 자리가 아니고, 티어 강등을 이유로 조용히 갱신을 끊으면 사용자는 낡은 본문을\n * 쥔 채 그 사실을 모른다. 강등 자체는 `trust-tier-drift` 가 따로 감시한다.\n */\nexport function refreshExternalSkills(\n projectDir: string,\n deps: Pick<ExternalInstallerDeps, \"spawn\" | \"assets\" | \"log\" | \"warn\"> & {\n /** 설치 기록 읽기 주입점 (테스트용). 기본 `readInstallLog`. */\n readLog?: (projectDir: string) => InstallLog | null;\n /** 실 설치 주입점 (테스트용). 기본 `runExternalInstall`. */\n run?: typeof runExternalInstall;\n } = {},\n): ExternalSkillRefresh {\n const none = { attempted: 0, refreshed: 0, failed: [] as const, notInCatalog: [] as const };\n const log = (deps.readLog ?? readInstallLog)(projectDir);\n if (!log) {\n return { ...none, unknown: true };\n }\n const installedIds = new Set(log.assets.filter((a) => a.method === \"skill\").map((a) => a.id));\n const catalog = deps.assets ?? EXTERNAL_ASSETS;\n const targets = catalog.filter((a) => a.method.kind === \"skill\" && installedIds.has(a.id));\n // 기록에는 있는데 카탈로그에서 사라진 자산 — 갱신할 방법이 없다. 화면에 이름을 낸다.\n const inCatalog = new Set(targets.map((a) => a.id));\n const notInCatalog = [...installedIds].filter((id) => !inCatalog.has(id));\n if (targets.length === 0) {\n return { ...none, notInCatalog, unknown: false };\n }\n const report = (deps.run ?? runExternalInstall)(\n {\n tracks: log.spec.tracks as ReadonlyArray<Track>,\n // `forceInclude` 가 조건 판정을 앞지르므로 옵션 값은 결과에 영향을 주지 않는다.\n options: DEFAULT_OPTIONS,\n cli: log.spec.cli as CliTargets,\n userOverride: { forceInclude: [...installedIds], forceExclude: [] },\n scope: log.scope,\n projectDir,\n },\n {\n assets: targets,\n ...(deps.spawn ? { spawn: deps.spawn } : {}),\n ...(deps.log ? { log: deps.log } : {}),\n ...(deps.warn ? { warn: deps.warn } : {}),\n },\n );\n return {\n attempted: report.attempted.length,\n refreshed: report.succeeded,\n failed: report.attempted\n .filter((r) => !r.ok)\n .map((r) => ({ id: r.asset.id, message: r.message ?? \"failed\" })),\n notInCatalog,\n unknown: false,\n };\n}\n\n/**\n * `npx skills add` 인자.\n *\n * **`--copy` 가 핵심이다** (#372, 실측 2026-08-27 `skills@1.5.11` 컨테이너). 기본 모드에서\n * 에이전트를 여럿 넘기면 skill 이 `.agents/skills/`(codex·opencode·antigravity 공용)에만 깔리고\n * **Claude Code 몫의 `.claude/skills/` 가 조용히 빠진다** — exit 0 이라 설치 화면에는 ✓ 로 뜬다.\n * `--copy`(help: \"Copy files instead of symlinking to agent directories\")를 붙이면 한 호출로\n * 두 자리가 다 생긴다.\n *\n * 재 본 것들:\n * `--agent a --agent b` (반복) → `.agents/` 만 ❌\n * `--agent a b` (variadic, 문서 형태) → `.agents/` 만 ❌ (1.5.23 도 동일)\n * `--agent \"a,b\"` (콤마) → `Invalid agents:` **exit 1**, 아무것도 안 깔림\n * 위 어느 형태든 **+ `--copy`** → 두 자리 다 생김 ✅ 1회 5초\n * 콤마는 1.5.5 에서만 되던 형태다(1.5.7 폐지, v26.55.1 회귀). 버전 bump 때 이 표를 다시 잰다.\n *\n * **`--copy` 는 에이전트를 명시할 때만 붙인다.** 미지정(`cli: []`, 레거시 \"전체\")에 붙이면\n * 설치 대상이 `.agents/` 한 곳에서 **약 50개 도구 디렉터리로 폭발**한다(실측). 그 경로는 종전\n * 그대로 둔다.\n */\nfunction buildSkillArgs(\n method: { kind: \"skill\"; source: string; skill?: string },\n cli: CliTargets,\n scope: InstallScope,\n): string[] {\n const args = [skillsCliSpec(), \"add\", method.source];\n if (method.skill) {\n args.push(\"--skill\", method.skill);\n }\n if (cli.length > 0) {\n for (const c of cli) {\n args.push(\"--agent\", SKILLS_CLI_AGENT_MAP[c] ?? c);\n }\n // 에이전트를 고른 경우에만. 위 주석의 폭발 사유.\n args.push(\"--copy\");\n }\n // v26.64.0 (ADR-020) — global scope 시 -g. project 는 skills CLI default (project) 따름.\n if (scope === \"global\") {\n args.push(\"-g\");\n }\n args.push(\"--yes\");\n return args;\n}\n\n/**\n * Plugin 은 marketplace add → install 두 단계. marketplace add 실패는 무시 (이미 등록 케이스).\n *\n * v26.64.0 (ADR-020) — `--scope <project|user>` 분기. claude CLI native:\n * - project: --scope project (현재 projectPath 격리, installed_plugins.json 메타 매칭)\n * - global: --scope user (모든 projectPath 에서 활성)\n * fs 적으로는 양쪽 모두 ~/.claude/plugins/cache/ + ~/.claude/plugins/marketplaces/ 에 write\n * (claude CLI 자체 디자인). 격리는 메타데이터.\n */\nfunction installPlugin(\n asset: ExternalAsset,\n spawn: NonNullable<ExternalInstallerDeps[\"spawn\"]>,\n method: { kind: \"plugin\"; marketplace: string; pluginId: string },\n scope: InstallScope,\n cwd: string,\n): AssetInstallResult {\n const claudeScope = scope === \"global\" ? \"user\" : \"project\";\n // v26.77.0 — cwd=projectDir: --scope project 시 claude 가 cwd 기준으로 프로젝트를 탐지하므로\n // installed_plugins.json 의 projectPath 가 올바른 프로젝트로 기록된다.\n spawn(\n \"claude\",\n [\"plugin\", \"marketplace\", \"add\", \"--scope\", claudeScope, method.marketplace],\n spawnOpts(cwd),\n );\n return runSpawn(\n asset,\n spawn,\n \"claude\",\n [\"plugin\", \"install\", \"--scope\", claudeScope, method.pluginId],\n cwd,\n );\n}\n\nfunction runSpawn(\n asset: ExternalAsset,\n spawn: NonNullable<ExternalInstallerDeps[\"spawn\"]>,\n cmd: string,\n args: ReadonlyArray<string>,\n cwd?: string,\n): AssetInstallResult {\n const timeout = spawnTimeoutFor(asset.method.kind);\n const result = spawn(cmd, args, spawnOpts(cwd, timeout));\n if (result.error) {\n // #422 — 시간 초과는 \"실패\"가 아니라 \"덜 끝남\"이다. 설치자가 같은 명령을 직접 이어 돌릴 수\n // 있게 명령을 그대로 낸다 — 메시지에 원인(ETIMEDOUT)만 있으면 무엇을 해야 하는지 모른다.\n const code = (result.error as NodeJS.ErrnoException).code;\n if (code === \"ETIMEDOUT\") {\n return {\n asset,\n ok: false,\n message: `timed out after ${Math.round(timeout / 1000)}s — finish it yourself: ${cmd} ${args.join(\" \")}`,\n };\n }\n return { asset, ok: false, message: result.error.message };\n }\n if ((result.status ?? 1) !== 0) {\n const stderr = (result.stderr ?? \"\").trim();\n const tail = stderr.length > 200 ? `${stderr.slice(0, 200)}…` : stderr;\n return {\n asset,\n ok: false,\n message: `${cmd} exited ${result.status}${tail ? `: ${tail}` : \"\"}`,\n };\n }\n return { asset, ok: true };\n}\n\nfunction spawnOpts(cwd?: string, timeout: number = DEFAULT_SPAWN_TIMEOUT_MS): SpawnOpts {\n return {\n encoding: \"utf8\",\n stdio: \"pipe\",\n timeout,\n ...(cwd ? { cwd } : {}),\n };\n}\n\n/* v8 ignore next 7 — thin dep-inject default. tests 는 항상 spawn 주입. */\nfunction defaultSpawn(\n cmd: string,\n args: ReadonlyArray<string>,\n opts: SpawnOpts,\n): SpawnSyncReturns<string> {\n return spawnSync(cmd, [...args], opts);\n}\n\n/**\n * v26.59.0 — install 후 path 기반 version 추출.\n *\n * 안전 원칙: 실패 시 undefined 반환 (silent). install 성공 자체는 이미 검증됨.\n *\n * - plugin: ~/.claude/plugins/cache/<marketplace>/<plugin>/<VERSION>/ 디렉토리명 (semver-like 만)\n * - npm-global: <npm root -g>/<pkg>/package.json 의 version\n * - skill / npx-run / shell-script: 표준 metadata 위치 없음 → undefined\n */\nfunction detectVersion(\n method: ExternalAssetMethod,\n spawn: NonNullable<ExternalInstallerDeps[\"spawn\"]>,\n): string | undefined {\n try {\n switch (method.kind) {\n case \"plugin\": {\n // pluginId = \"<plugin>@<marketplace-short>\". cache path:\n // ~/.claude/plugins/cache/<marketplace-short>/<plugin>/<VERSION>/\n // method.marketplace 는 GH `<user>/<repo>` (다른 값) 이라 path 에 사용 X.\n const at = method.pluginId.lastIndexOf(\"@\");\n if (at <= 0) return undefined;\n const plugin = method.pluginId.slice(0, at);\n const marketplaceShort = method.pluginId.slice(at + 1);\n const cacheBase = join(homedir(), \".claude/plugins/cache\", marketplaceShort, plugin);\n if (!existsSync(cacheBase)) return undefined;\n const versions = readdirSync(cacheBase)\n .filter((v) => /^\\d/.test(v))\n .sort();\n return versions.at(-1);\n }\n case \"npm\": {\n const npmRoot = getNpmGlobalRoot(spawn);\n if (!npmRoot) return undefined;\n const pkgJson = join(npmRoot, method.pkg, \"package.json\");\n if (!existsSync(pkgJson)) return undefined;\n const parsed = JSON.parse(readFileSync(pkgJson, \"utf8\")) as { version?: string };\n return parsed.version;\n }\n default:\n return undefined;\n }\n } catch {\n return undefined;\n }\n}\n\nlet npmGlobalRootCache: string | undefined;\n\n/* v8 ignore start — npm CLI 실행 + cache. 실 시스템 의존. detectVersion (plugin 외 method) 가 본 함수 호출. */\nfunction getNpmGlobalRoot(spawn: NonNullable<ExternalInstallerDeps[\"spawn\"]>): string | undefined {\n if (npmGlobalRootCache !== undefined) return npmGlobalRootCache || undefined;\n try {\n const r = spawn(\"npm\", [\"root\", \"-g\"], spawnOpts());\n if ((r.status ?? 1) === 0) {\n npmGlobalRootCache = (r.stdout ?? \"\").trim();\n return npmGlobalRootCache || undefined;\n }\n } catch {\n // fallthrough\n }\n npmGlobalRootCache = \"\";\n return undefined;\n}\n/* v8 ignore stop */\n\n/**\n * 누락(skip) 자산 목록을 사용자 보고용 텍스트로 포맷.\n */\nexport function formatSkippedReport(report: ExternalInstallReport): string {\n const failed = report.attempted.filter((r) => !r.ok);\n if (failed.length === 0) return \"\";\n const lines = failed.map((r) => ` • ${r.asset.id} — ${r.message ?? \"failed\"}`);\n return [\n `${failed.length}개 외부 자산이 설치되지 않았습니다 (warn-skip):`,\n ...lines,\n \"\",\n \"Manual install or retry needed. See docs/REFERENCE.md or README.md for details.\",\n ].join(\"\\n\");\n}\n","import { existsSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { anyTrack } from \"./track-match.js\";\nimport type { Track } from \"./types.js\";\n\nexport interface McpServerConfig {\n type?: \"stdio\" | \"http\";\n command: string;\n args: string[];\n env?: Record<string, string>;\n}\n\nexport interface McpJson {\n mcpServers: Record<string, McpServerConfig>;\n _comment?: string;\n}\n\nexport interface TrackMcpRow {\n name: string;\n pattern: string;\n command: string;\n args: string[];\n}\n\n/** Parse `templates/track-mcp-map.tsv` (tab-separated, comment-aware). */\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 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, command, args: args as string[] });\n }\n return rows;\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): McpJson {\n const out: McpJson = {\n ...base,\n mcpServers: { ...base.mcpServers },\n };\n for (const row of rows) {\n if (!anyTrack(tracks, row.pattern)) {\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] = {\n type: \"stdio\",\n command: row.command,\n args: row.args,\n };\n }\n // Strip _comment marker (parity with the bash `jq 'del(._comment)'`).\n delete out._comment;\n return out;\n}\n\n/**\n * Compose the final `.mcp.json` for a project install.\n * Read base template + track map, merge with optional existing user file (additive).\n */\nexport function composeMcpJson(opts: {\n templateMcpPath: string;\n trackMapPath: string;\n existingPath?: string;\n tracks: ReadonlyArray<Track>;\n}): McpJson {\n const base = JSON.parse(readFileSync(opts.templateMcpPath, \"utf8\")) as McpJson;\n const merged =\n opts.existingPath && existsSync(opts.existingPath)\n ? mergeUserBase(base, opts.existingPath)\n : base;\n const mapRaw = existsSync(opts.trackMapPath) ? readFileSync(opts.trackMapPath, \"utf8\") : \"\";\n const rows = parseTrackMcpMap(mapRaw);\n return mergeMcpServers(merged, rows, opts.tracks);\n}\n\nfunction mergeUserBase(base: McpJson, existingPath: string): McpJson {\n try {\n const existing = JSON.parse(readFileSync(existingPath, \"utf8\")) as McpJson;\n return {\n ...base,\n mcpServers: { ...base.mcpServers, ...existing.mcpServers },\n };\n } catch {\n return base;\n }\n}\n\n/** Write the composed `.mcp.json` to disk (2-space pretty). */\nexport function writeMcpJson(path: string, mcp: McpJson): void {\n writeFileSync(path, `${JSON.stringify(mcp, null, 2)}\\n`);\n}\n","/**\n * update-mode.ts — Update / Add / Reinstall router 액션 처리.\n *\n * SPEC: docs/specs/cli-rewrite-completeness.md F5, F6\n * Source: bash setup-harness.sh@911c246~1 L460~573 (update mode 113 LOC)\n *\n * Update 모드 동작:\n * 1. backup: .claude/ → .claude.backup-<timestamp>/\n * 2. update_dir: target에 이미 존재하는 파일만 templates로 덮어쓰기 (Track 혼입 방지)\n * 3. prune_orphans: templates에 없는데 target에 있는 파일 제거 (예: 폐기된 rule)\n * 4. clean_stale_hook_refs: settings.json hook 참조 중 실존 파일 없는 것 제거\n *\n * 보존: .mcp.json (사용자 추가 항목), docs/SPEC.md, settings.local.json\n */\n\nimport {\n copyFileSync,\n existsSync,\n mkdirSync,\n readdirSync,\n readFileSync,\n rmdirSync,\n statSync,\n unlinkSync,\n writeFileSync,\n} from \"node:fs\";\nimport { basename, dirname, join } from \"node:path\";\nimport { isBaselineExcluded } from \"./baseline-targets.js\";\nimport { ALL_CLI_TARGETS, runCliTransforms } from \"./cli-transforms.js\";\nimport {\n CONTINUOUS_SKILLS,\n INTERNAL_BUNDLED_SKILL_IDS,\n RENAMED_SKILL_IDS,\n RETIRED_SKILL_IDS,\n} from \"./external-assets.js\";\nimport { type ExternalSkillRefresh, refreshExternalSkills } from \"./external-installer.js\";\nimport { foreignOwnedTarget, occupiedByNonDirectory } from \"./foreign-slot.js\";\nimport { backupFile, copyDir, listFilesRecursive } from \"./fs-ops.js\";\nimport {\n collectPolicyHashes,\n collectSkillHashes,\n hashContent,\n type InstallLog,\n isHarnessOwned as isOwnedByBaseline,\n mergeExternalFiles,\n POLICY_DIRS,\n readInstallLog,\n writeInstallLog,\n} from \"./install-log.js\";\nimport {\n ALL_RULES,\n type AssetEntry,\n type AssetSpec,\n buildAssetSpec,\n buildManifest,\n RETIRED_AGENT_IDS,\n TRACK_AGENTS,\n} from \"./manifest.js\";\nimport { HARNESS_ANCHOR_FILE, upsertHarnessImport } from \"./project-claude-merge.js\";\nimport { anyTrack } from \"./track-match.js\";\nimport {\n DEFAULT_OPTIONS,\n type InstallSpec,\n TRACKS,\n type Track,\n UPDATE_GROUPS,\n type UpdateGroup,\n} from \"./types.js\";\n\n/**\n * v26.140.0 **이전** 설치본의 앵커 위치 (P5 · ADR-060 이행 대상).\n *\n * 이행 안내에만 쓴다 — 하네스는 더 이상 이 경로에 아무것도 쓰지 않는다.\n */\nconst LEGACY_ANCHOR_FILE = \".claude/CLAUDE.md\";\n\nexport interface UpdateModeReport {\n /** 덮어쓰기된 파일 갯수 (디렉토리별). */\n updated: Record<string, number>;\n /** 제거된 orphan 파일명 목록 (디렉토리별). */\n pruned: Record<string, string[]>;\n /** 제거된 stale hook ref 파일명 목록. */\n staleHookRefs: string[];\n /** 갱신된 CLAUDE.md (true if updated). */\n claudeMdUpdated: boolean;\n /**\n * #480 ③ — 이번 update 가 남긴 백업 쌍(원본 ↔ `*.backup-<time>`, projectDir 상대경로). 화면\n * 마지막 줄과 `.uzys-agent-harness/update-backups.json` 에 낸다 — 설치자가 편집분을 새 판에\n * 다시 얹을 때(audit-harness-fit 에 요청) 그 파일이 증거다. 범용 스킬에 이 하네스의 백업\n * 규약을 심지 않는다(사용자 정정 2026-09-20) — 증거를 내는 쪽이 하네스다.\n */\n backups: Array<{ path: string; backup: string }>;\n /**\n * #480 — 설치자가 고르지 않아 **건드리지 않은** 묶음 (`update --only` · 위저드 체크박스).\n * 화면에 낸다 — 안 보이면 \"update 를 돌렸는데 룰이 그대로다\"가 결함으로 읽힌다.\n */\n skippedGroups: UpdateGroup[];\n /**\n * #480 — 앵커(`CLAUDE-uzys-harness.md`)를 설치자가 고친 상태에서 갱신해 `*.backup-<time>` 을\n * 남겼다. 전에는 기준선 대조 없이 덮어써 편집이 **백업 없이 사라졌다**(컨테이너 실측\n * 2026-09-20) — 룰·스킬은 ADR-046 대로 백업하는데 앵커만 예외였다. 화면에 노출한다.\n */\n anchorBackedUp: boolean;\n /**\n * P5 · ADR-060 이행 — 루트 앵커가 **없어서 이번에 만들었다** (v26.140.0 이전 설치본).\n *\n * `claudeMdUpdated` 와 배타적이다: 갱신은 이미 있던 파일, 생성은 이번에 생긴 파일.\n */\n anchorCreated: boolean;\n /** 이행 중 루트 `CLAUDE.md` 에 앵커 import 줄을 새로 얹었나 (이미 있었으면 false). */\n rootImportAdded: boolean;\n /**\n * ADR-085 — 이미 있던 관리 블록의 **안**이 바뀌었나(상시 스킬 안내가 깔린 스킬을 따라 현행화됐다,\n * 또는 지워진 import 줄이 돌아왔다). 블록이 처음 생긴 경우는 `rootImportAdded` 다.\n */\n rootBlockRefreshed: boolean;\n /**\n * 디스크에 아직 남아 있는 구 앵커 경로(`.claude/CLAUDE.md`). 없으면 null.\n *\n * **지우지 않는다** — 사용자가 그 파일을 고쳤는지 update 시점엔 판정할 수 없다. 대신 화면에\n * \"이제 갱신되지 않는다\"를 알린다. 침묵하면 옛 하네스 내용이 담긴 사본이 계속 상주하는데\n * 사용자는 그게 죽은 사본인 줄 모른다.\n */\n legacyAnchor: string | null;\n /**\n * v26.126.0 (R-3a) — 사용자가 고쳐서 백업본을 남긴 스킬 파일 (`.claude/skills/` 상대경로).\n * 화면에 그대로 노출한다. 안 보이면 사용자는 자기 편집분이 어디 갔는지 알 수 없다.\n */\n skillsBackedUp: string[];\n /**\n * 2026-08-02 (ADR-062) — `.claude/skills/<id>` 가 **디렉터리가 아니라** 건너뛴 스킬 id\n * (심볼릭 링크가 대표적이지만 일반 파일·FIFO 도 들어온다 — #343 에서 술어를 install 과 통일).\n * 필드명의 `Links` 는 도입 당시 범위를 그대로 쓴다 — 개명은 화면·픽스처 표면을 함께 건드린다.\n * 그 자리는 `npx skills add` 등 **다른 도구가 소유**하므로 하네스가 쓰지 않는다.\n * 화면에 남기는 이유는 위와 같다 — 안 보이면 사용자는 \"왜 이 스킬만 안 갱신되지\"를 알 수 없다.\n */\n skillsSkippedLinks: string[];\n /**\n * #477 — 번들에서 사라져 설치본에서 지운 스킬 파일 (`.claude/skills/` 상대경로). 번들 스킬\n * 디렉터리의 파일 목록이 \"최신본이 가져야 할 파일\"의 정의다 — 그 목록에 없는 파일이 남으면\n * 갱신된 SKILL.md 가 가리키지 않는 옛 서식이 디스크에 살아 있다(실측: 26.152→26.153 에서\n * `clear-korean-communication/references/` 3파일). 지우기 전에 기준선과 다르거나 기록이 없는\n * 파일은 `*.backup-<time>` 으로 남긴다(`skillsBackedUp` 에도 든다) — 덮어쓸 때와 같은 잣대다.\n */\n skillsPruned: string[];\n /**\n * v26.132.0 (ADR-047) — 사용자가 고쳐서 백업본을 남긴 정책 파일 (`.claude/` 상대경로).\n * `skillsBackedUp` 과 같은 이유로 화면에 노출한다 — 안 보이면 사용자는 자기 편집분이\n * 어디 갔는지 알 수 없고, 그러면 백업은 있어도 없는 것과 같다.\n */\n policyBackedUp: string[];\n /**\n * v26.134.0 (R-3j-A · ADR-049) — 갱신된 외부 CLI 산출물 수\n * (`.codex/` · `.opencode/` · `.agents/` · `AGENTS.md` · `opencode.json`).\n *\n * 그전까지 update 는 `.claude/` 만 갱신했다. codex/opencode 사용자는 install 이 개선한 룰을\n * `update` 로는 영영 못 받았고, 그 비대칭이 **문서 어디에도 안 적혀 있었다**.\n */\n externalUpdated: number;\n /** 사용자가 고쳐서 백업본을 남긴 외부 CLI 산출물 (projectDir 상대경로). */\n externalBackedUp: string[];\n /**\n * #374 — `npx skills add` 로 깐 **외부 스킬** 중 이번 update 가 상류 최신판으로 다시 받은 수.\n *\n * `externalUpdated` 와 다른 것이다. 위 필드는 **우리가 렌더한** CLI 산출물이고 이쪽은\n * **남의 저장소에서 받아온 스킬 본문**이다. 둘을 한 행으로 합치지 않는 이유는 이 결함이\n * 정확히 그 혼동이었기 때문이다 — 화면의 `external CLI artifacts` 를 보고 스킬도 갱신된 줄\n * 알았지만 그쪽은 한 번도 안 돌았다.\n */\n externalSkillsRefreshed: number;\n /**\n * 갱신에 실패한 외부 스킬. **실패해도 update 는 계속한다** — 네트워크 하나 때문에 정책 파일\n * 갱신까지 잃는 것은 사용자에게 더 나쁘다. 대신 침묵하지 않는다(화면에 이름을 낸다).\n */\n externalSkillsFailed: ReadonlyArray<{ id: string; message: string }>;\n /**\n * 설치 기록에는 있는데 **지금 카탈로그에 없는** 스킬 자산 id. 갱신 대상이 아니라는 사실을\n * 화면에 낸다 — 침묵하면 사용자는 깔린 것 중 일부만 갱신됐다는 사실을 알 수 없다.\n */\n externalSkillsNotInCatalog: ReadonlyArray<string>;\n /**\n * 설치 기록이 없어 **무엇을 갱신해야 하는지 판정할 수 없었다** (레거시 설치본).\n *\n * `externalSkillsRefreshed: 0` 과 구분한다 — 그쪽은 \"갱신할 게 없다\"이고 이쪽은 \"모른다\"다.\n * 둘을 같은 침묵으로 합치면 이 이슈가 고치려던 실패 형태가 그대로 남는다(독립 리뷰 MEDIUM-1).\n */\n externalSkillsUnknown: boolean;\n /**\n * #343 — 다른 도구가 소유한 자리라 **쓰지 않은** 경로. 출처를 나누지 않는다 —\n * 외부 CLI 산출물(`.agents/skills/<id>`)이든 이번 릴리즈 신규 자산이든, 사용자에게\n * 필요한 것은 \"어느 자리를 옮겨야 하는가\" 하나다. `skillsSkippedLinks` 는 스킬 id 를\n * 내는 기존 행이라 그대로 두고, 이쪽은 경로를 낸다.\n */\n foreignOwned: string[];\n /**\n * 이번 update 가 **새로 깔아 준** 자산 (projectDir 상대경로) — #283.\n *\n * 갱신(`updated`)과 배타적이다: 갱신은 이미 있던 파일, 이쪽은 릴리즈로 새로 생겨서\n * 이 설치본에 아직 없던 파일이다. 화면에 남기는 이유는 `anchorCreated` 와 같다 — 조용히\n * 늘어난 파일은 사용자가 자기 것으로 오인한다.\n */\n installedNew: string[];\n /**\n * **전에 깔아 줬는데 디스크에 없어서 다시 깐** 자산 (projectDir 상대경로).\n *\n * `installedNew` 와 가르는 이유는 원인이 다르기 때문이다 — 이쪽은 사용자가 지웠거나 유실된\n * 것이다. 둘을 한 문구(\"이번 릴리즈에 추가됨\")로 보고하면 한쪽에는 거짓이 되고, 자기가 지운\n * 파일이 왜 돌아왔는지 추적할 수 없다. 판별 신호는 install log 의 `policyFiles` 기준선이다.\n */\n restored: string[];\n /**\n * 이 릴리즈에 새로 생겼지만 **update 가 깔 수 없는** 자산 (projectDir 상대경로).\n *\n * 훅과 `settings.json` 이 그쪽이다 — 훅은 `settings.json` 의 배선이 있어야 발화하는데 update 는\n * 그 파일을 동기화하지 않는다(죽은 참조를 지울 뿐이다). 파일만 놓고 \"추가됨\"이라 보고하면\n * 안 도는 기능을 받았다고 읽히므로(거짓출하), 깔지 않고 **재설치가 필요하다는 사실을 낸다.**\n */\n needsReinstall: string[];\n /**\n * ADR-089 (#445) — `.claude/agents/` 에 남아 있는 **은퇴한** 에이전트 id.\n *\n * **지우지 않는다** — 사용자가 그 파일을 고쳤는지 update 시점엔 판정할 수 없다(`legacyAnchor`\n * 와 같은 규율). 대신 화면이 \"이 릴리즈에서 은퇴했고 벤더 기본 기능이 같은 일을 한다\"를 낸다.\n * 침묵하면 옛 릴리즈로 깐 설치본은 죽은 에이전트 descriptor 를 매 세션 상주시키면서 그 사실을\n * 영영 모른다.\n */\n retiredAgents: string[];\n /**\n * ADR-090 (#452 · #458) — 이 설치본의 트랙에서 **빠진**(강등된) 에이전트 id.\n *\n * 은퇴(`retiredAgents`)와 가르는 이유는 사실이 다르기 때문이다: 은퇴한 것은 아무에게도 안 가고,\n * 강등된 것은 **다른 트랙에는 여전히 간다**. 한 문구로 뭉치면 data 트랙 설치자가 자기 자산을\n * 없어진 것으로 읽는다. 그래서 화면이 \"어느 트랙 전용인지\"를 함께 말한다.\n *\n * 은퇴와 같은 것: **지우지 않는다**(ADR-046 · 사용자 결정 2026-09-14). 그 파일을 사용자가\n * 고쳤는지 update 시점엔 판정할 수 없다. `pruneOrphans` 도 못 지운다 — 템플릿에 원본이\n * 그대로 있어 orphan 이 아니다. 말하고 손은 사용자가.\n */\n demotedAgents: string[];\n /**\n * 2026-08-16 (ADR-072) — 이번 update 가 물러낸 `.mcp-allowlist` 의 **백업 경로**. 없었으면 null.\n *\n * 이 파일은 `.claude/` 밖(프로젝트 루트)이라 `pruneOrphans` 의 사정거리에 없다. 읽던 훅\n * (`mcp-pre-exec.sh`)은 `hooks` 가 `POLICY_DIRS` 에 있어 자동으로 물러나는데, 이쪽만 남으면\n * **아무도 안 읽는 파일이 사용자 저장소에 영구히 남는다.** 지우기 전에 백업하는 이유는\n * 자동 생성물이라도 사용자가 서버를 주석 처리해 자기 정책을 적어 넣는 파일이었기 때문이다\n * — 그건 사용자 것이다.\n */\n mcpAllowlistRetired: string | null;\n}\n\n/**\n * Update 진입점이 쓰는 InstallSpec — **위저드와 `update` 명령이 공유한다.**\n *\n * `cli`/`options` 는 타입을 채우기 위한 값이라 어느 진입점이든 같아야 하고, 두 곳에서 각자\n * 리터럴로 쓰면 한쪽만 바뀌었을 때 조용히 갈린다 — 이 repo 가 반복해서 당한 실패 모드라\n * 처음부터 한 곳에 둔다.\n *\n * **`scope` 는 설치 기록에서 읽는다** (#374, 독립 리뷰 HIGH-1). update 는 이제 `.claude/` 만\n * 건드리지 않는다 — ADR-049 이후 `.agents/`·`.codex/` 를, #374 이후 **글로벌 설치본에서는\n * `~/.claude/` 까지** 갱신한다. 그런데 여기서 스코프를 안 실으면 화면 헤더가 렌더러 기본값인\n * *\"Project — current directory only (no global write)\"* 를 찍는다. 홈에 쓰면서 안 쓴다고\n * 적는 것은 이 저장소가 반복해서 당한 거짓출하 그 형태다.\n */\nexport function buildUpdateSpec(\n projectDir: string,\n tracks: ReadonlyArray<Track>,\n only?: ReadonlyArray<UpdateGroup>,\n): InstallSpec {\n return {\n tracks: [...tracks],\n options: DEFAULT_OPTIONS,\n cli: [\"claude\"],\n projectDir,\n scope: readInstallLog(projectDir)?.scope ?? \"project\",\n // 전부 골랐으면 \"제한 없음\"과 같다 — 화면·기록에 제한이 있었던 것처럼 남기지 않는다.\n ...(only !== undefined && only.length > 0 && only.length < UPDATE_GROUPS.length\n ? { updateOnly: [...only] }\n : {}),\n };\n}\n\n/**\n * `--only <group>` 값 검증 (#480). 모르는 이름은 조용히 무시하지 않는다 — 오타 하나로 \"전부\n * 갱신\"이 되면 설치자가 룰을 안 건드리려고 쓴 플래그가 정반대로 동작한다.\n */\nexport function parseUpdateOnly(\n raw: ReadonlyArray<string>,\n): { ok: true; groups: UpdateGroup[] } | { ok: false; invalid: string[] } {\n const invalid = raw.filter((v) => !(UPDATE_GROUPS as ReadonlyArray<string>).includes(v));\n if (invalid.length > 0) return { ok: false, invalid };\n return { ok: true, groups: [...new Set(raw as UpdateGroup[])] };\n}\n\n/**\n * `runUpdateMode` 주입점. 기본은 실 구현 — 테스트만 바꿔 끼운다.\n *\n * 인터페이스를 함수 JSDoc **앞**에 두는 이유: 사이에 끼우면 함수 문서 블록이 함수에 안 붙는다.\n */\nexport interface UpdateModeDeps {\n /**\n * #374 — 외부 스킬 갱신. 기본은 `refreshExternalSkills`.\n * 테스트가 네트워크를 타지 않게 하는 유일한 지점이다.\n */\n refreshSkills?: (projectDir: string) => ExternalSkillRefresh;\n}\n\n/**\n * Update mode 메인 — backup은 caller가 별도 처리.\n *\n * @param projectDir 대상 프로젝트 root\n * @param templatesDir templates/ 디렉토리 (sync source)\n * @param harnessRoot harness repo root — 외부 CLI transform 의 렌더 소스.\n *\n * `templatesDir` 에서 파생시킬 수도 있지만 **required 인자로 받는다**: 옵셔널이면 안 넘긴\n * 호출부만 조용히 외부 CLI 갱신을 건너뛰고, 그건 지금 고치고 있는 바로 그 버그다\n * (ADR-048 의 `baseline` 을 required 로 둔 것과 같은 이유).\n */\nexport function runUpdateMode(\n projectDir: string,\n templatesDir: string,\n harnessRoot: string,\n deps: UpdateModeDeps = {},\n only?: ReadonlyArray<UpdateGroup>,\n): UpdateModeReport {\n const claudeDir = join(projectDir, \".claude\");\n const startedAt = Date.now();\n // #480 — 고른 묶음만 돈다. 안 고르면(undefined) 전부 — 기존 호출부의 동작 그대로.\n const wants = (g: UpdateGroup): boolean => only === undefined || only.includes(g);\n const report: UpdateModeReport = {\n updated: {},\n pruned: {},\n staleHookRefs: [],\n claudeMdUpdated: false,\n backups: [],\n skippedGroups: only === undefined ? [] : UPDATE_GROUPS.filter((g) => !only.includes(g)),\n anchorBackedUp: false,\n anchorCreated: false,\n rootImportAdded: false,\n rootBlockRefreshed: false,\n legacyAnchor: null,\n skillsBackedUp: [],\n skillsSkippedLinks: [],\n skillsPruned: [],\n policyBackedUp: [],\n externalUpdated: 0,\n externalBackedUp: [],\n externalSkillsRefreshed: 0,\n externalSkillsFailed: [],\n externalSkillsNotInCatalog: [],\n externalSkillsUnknown: false,\n foreignOwned: [],\n installedNew: [],\n restored: [],\n needsReinstall: [],\n retiredAgents: [],\n demotedAgents: [],\n mcpAllowlistRetired: null,\n };\n\n // 0) 릴리즈로 **새로 생긴** 자산 설치 (#283). 정책 동기화보다 먼저 도는 이유는\n // `refreshPolicyBaseline` 이 아래에서 기준선을 다시 찍기 때문이다 — 순서를 뒤집으면 방금 깐\n // 파일이 기준선에 없어 다음 update 가 \"사용자가 만든 파일\"로 오판한다.\n const fresh = installNewAssets(projectDir, templatesDir, wants);\n report.installedNew = fresh.installed;\n report.restored = fresh.restored;\n report.needsReinstall = fresh.needsReinstall;\n\n // 1) 정책 디렉터리 동기화 — 대상 목록은 POLICY_DIRS 가 SSOT (install-log.ts).\n // v26.132.0 (ADR-047) — 사용자 편집분 판정이 붙었다. 기준선은 install log 의 policyFiles.\n const policyBase = policyBaseline(projectDir);\n for (const { dir, ext } of POLICY_DIRS) {\n if (!wants(dir === \"hooks\" ? \"hooks\" : \"rules\")) continue;\n const target = join(claudeDir, dir);\n const source = join(templatesDir, dir);\n const label = `.claude/${dir}`;\n const ctx = { prefix: dir, baseline: policyBase };\n const synced = updateDir(target, source, ext, ctx);\n report.updated[label] = synced.updated;\n report.policyBackedUp.push(...synced.backedUp);\n report.pruned[label] = pruneOrphans(target, source, ext, ctx);\n }\n // 기준선은 **동기화한 디렉터리만** 다시 찍는다 — 건너뛴 디렉터리의 사용자 편집을 지금 디스크\n // 그대로 \"하네스가 놓아둔 것\"으로 기록하면 다음 update 가 백업 없이 덮어쓴다(리뷰 ⓒ). 반대로\n // 동기화한 디렉터리를 안 찍으면 다음 전체 update 가 방금 놓은 최신판을 \"편집분\"으로 백업한다\n // (리뷰 N-1 실측). 그래서 전부-아니면-전무가 아니라 디렉터리 단위다.\n const syncedDirs = POLICY_DIRS.filter((d) => wants(d.dir === \"hooks\" ? \"hooks\" : \"rules\")).map(\n (d) => d.dir,\n );\n if (syncedDirs.length > 0) refreshPolicyBaseline(projectDir, templatesDir, syncedDirs);\n\n // 1.5) `.claude/skills/` — v26.126.0 (R-3a · ADR-046).\n // 위 4개와 달리 스킬은 디렉터리 단위라 재귀가 필요하고, 사용자 편집분 판정이 붙는다.\n const skillSync = wants(\"skills\")\n ? syncSkills(\n join(claudeDir, \"skills\"),\n join(templatesDir, \"skills\"),\n skillBaseline(projectDir),\n new Date(),\n (relInSkills) => foreignOwnedTarget(projectDir, `.claude/skills/${relInSkills}`),\n )\n : { updated: 0, backedUp: [], skippedLinks: [], foreignOwned: [], pruned: [] };\n if (wants(\"skills\")) report.updated[\".claude/skills\"] = skillSync.updated;\n report.skillsBackedUp = skillSync.backedUp;\n report.skillsSkippedLinks = skillSync.skippedLinks;\n report.skillsPruned = skillSync.pruned;\n // #480 ① — 릴리즈로 새로 생긴 번들 스킬을 깐다. `syncSkills` 는 이미 깔린 것만 다루고\n // `installNewAssets` 는 파일 자산만 다뤄, 기존 설치본은 새 스킬을 영영 못 받았다.\n const newSkills = wants(\"new-skills\")\n ? installNewSkillDirs(projectDir, templatesDir, installedTracks(projectDir))\n : { installed: [], foreignOwned: [] };\n report.installedNew.push(...newSkills.installed);\n if (wants(\"skills\")) refreshSkillBaseline(projectDir, templatesDir);\n else if (newSkills.installed.length > 0)\n recordNewSkillBaseline(projectDir, templatesDir, newSkills.installed);\n\n // 2) 하네스 앵커 (프로젝트 루트 `CLAUDE-uzys-harness.md` — P5 · ADR-060).\n //\n // **claude 를 고른 설치에서만** 돈다. 앵커 파일과 루트 `CLAUDE.md` 의 `@import` 한 줄은\n // Claude Code 전용 로딩 경로이고, 다른 CLI 사용자는 같은 내용을 `AGENTS.md` ·\n // `.agents/rules/` 로 이미 받는다 — 만들면 중복이고, 루트 `CLAUDE.md` 는 **사용자 소유\n // 이름**이라 고른 적 없는 CLI 때문에 사용자 파일이 수정된다. install 은 이 조건을 이미\n // 지키는데(`.claude/` baseline 게이트) update 만 무조건 돌고 있었다 — 비 Claude 단독\n // 설치가 update 에 도달할 수 있게 되면서 드러난 비대칭이다(독립 재검증 HIGH-1).\n //\n // 증거는 **둘을 함께** 본다. `.claude/` 존재만으로 판정하면 비 Claude 설치에 어떤 이유로든\n // `.claude/` 가 있을 때 같은 증상이 돌아오고(사용자가 만든 디렉터리 · 이전 설치의 잔재),\n // install log 만으로 판정하면 **로그가 없는 레거시 설치본**이 claude 미설치로 오판돼 앵커\n // 이행 자체가 죽는다(그 이행이 이 함수의 원래 목적이다 — 로그 단독 판정은 update-mode\n // 테스트 6건이 red 로 잡았다). 그래서 디렉터리가 있고, **로그가 claude 를 말하거나 로그가\n // 아예 없을 때**만 돈다. 로그가 있는데 claude 가 없다 = 명시적으로 안 고른 것이다.\n const installLog = readInstallLog(projectDir);\n if (existsSync(claudeDir) && (installLog === null || installLog.spec.cli.includes(\"claude\"))) {\n if (wants(\"anchor\")) syncHarnessAnchor(projectDir, templatesDir, report);\n }\n\n // 3) settings.json stale hook ref cleanup\n const settingsPath = join(claudeDir, \"settings.json\");\n if (wants(\"hooks\") && existsSync(settingsPath)) {\n report.staleHookRefs = cleanStaleHookRefs(settingsPath, claudeDir);\n }\n\n // 3.5) `.mcp-allowlist` 회수 (ADR-072). 3) 바로 뒤인 이유는 같은 은퇴의 나머지 절반이기\n // 때문이다 — 위가 배선을 지우고 여기가 그 배선이 읽던 데이터를 지운다.\n if (wants(\"hooks\")) report.mcpAllowlistRetired = retireMcpAllowlist(projectDir);\n\n // 3.6) 은퇴한 에이전트 (ADR-089 · #445). 바로 위 두 절과 같은 축이다 — **더는 갱신되지 않는\n // 것이 디스크에 남아 있다**. 다만 지우는 것은 위 둘과 다르다: `.mcp-allowlist` 는 우리가\n // 만든 자동 생성물이라 회수했지만, 에이전트 파일은 사용자가 고쳤을 수 있고 update 시점에\n // 그것을 판정할 기준선이 없다. 그래서 말만 한다.\n report.retiredAgents = staleAgentFiles(claudeDir);\n // 3.7) 트랙에서 **강등된** 에이전트 (ADR-090 · #458). 같은 자리에 두는 이유는 사용자가 할 일이\n // 같아서다(지워도 된다). 판정 근거만 다르다 — 은퇴는 목록, 강등은 **이 설치본의 트랙**이다.\n report.demotedAgents = demotedAgentFiles(claudeDir, installedTracks(projectDir));\n\n // 4) 외부 CLI 산출물 — v26.134.0 (R-3j-A · ADR-049).\n // install 과 **같은 함수**를 refresh 모드로 부른다. 여기서 transform 을 따로 부르면\n // 기준선을 잇는 규칙이 두 벌이 되고, 그게 ADR-046~048 을 세 번 반복하게 만든 구조다.\n const external = wants(\"external\")\n ? refreshExternalCli(projectDir, harnessRoot)\n : { externalUpdated: 0, externalBackedUp: [], externalForeignOwned: [] };\n report.externalUpdated = external.externalUpdated;\n report.externalBackedUp = external.externalBackedUp;\n // 한 자리를 두 행이 말하지 않게 한다 — 위 `skillsSkippedLinks` 행이 이미 낸 슬롯은 뺀다.\n // 사용자는 두 줄을 서로 다른 두 사건으로 읽는다(리뷰 3라운드 지적).\n const saidBySlotRow = new Set(report.skillsSkippedLinks.map((id) => `.claude/skills/${id}`));\n const merged: string[] = [];\n for (const f of [\n ...skillSync.foreignOwned,\n ...fresh.foreignOwned,\n ...newSkills.foreignOwned,\n ...external.externalForeignOwned,\n ]) {\n if (!saidBySlotRow.has(f) && !merged.includes(f)) merged.push(f);\n }\n report.foreignOwned = merged;\n\n // 5) 외부 스킬 (#374). 4) 는 **우리가 렌더한** 산출물만 새로 쓴다 — `npx skills add` 로 깐\n // 스킬 본문은 그 경로에 없어서 update 를 몇 번 돌려도 첫 설치 판본 그대로였다.\n // 실패해도 여기서 멈추지 않는다(위 필드 주석의 사유). 화면 행은 install-render 가 낸다.\n const skillRefresh = wants(\"external\")\n ? (deps.refreshSkills ?? refreshExternalSkills)(projectDir)\n : { attempted: 0, refreshed: 0, failed: [], notInCatalog: [], unknown: false };\n report.externalSkillsRefreshed = skillRefresh.refreshed;\n report.externalSkillsFailed = skillRefresh.failed;\n // 개명·은퇴한 스킬은 **디스크에서** 찾는다. 위 refresh 는 `method: \"skill\"`(npx 외부 스킬)만\n // 훑으므로 번들 스킬(`method: \"internal\"`)은 기록에 그 부류로 남지 않는다 — 그쪽만 보면\n // 옛 이름의 디렉터리를 그대로 들고 있는 설치자에게 화면이 한 마디도 안 한다.\n // 지우지는 않는다(ADR-046 — 스킬 디렉터리 안에는 사용자 파일이 섞인다). 말해 주고 손은 사용자가.\n report.externalSkillsNotInCatalog = [\n ...skillRefresh.notInCatalog,\n ...staleSkillDirs(claudeDir).filter((id) => !skillRefresh.notInCatalog.includes(id)),\n ];\n report.externalSkillsUnknown = skillRefresh.unknown;\n\n report.backups = collectRunBackups(projectDir, startedAt);\n writeBackupList(projectDir, report.backups);\n return report;\n}\n\n/**\n * `.claude/skills/` 에 남아 있는 **개명·은퇴한** 스킬 id. 화면 문구는 `install-render` 가 가른다.\n *\n * 열거하지 않는다 — 카탈로그의 `RENAMED_SKILL_IDS`·`RETIRED_SKILL_IDS` 를 읽는다. 여기에 이름을\n * 적으면 다음 개명에서 이 파일이 조용히 뒤처진다.\n */\nfunction staleSkillDirs(claudeDir: string): string[] {\n const dir = join(claudeDir, \"skills\");\n if (!existsSync(dir)) return [];\n const stale = (id: string): boolean =>\n RENAMED_SKILL_IDS[id] !== undefined || RETIRED_SKILL_IDS.includes(id);\n return readdirSync(dir, { withFileTypes: true })\n .filter((e) => e.isDirectory() && stale(e.name))\n .map((e) => e.name);\n}\n\n/**\n * `.claude/agents/` 에 남아 있는 **은퇴한** 에이전트 id (ADR-089 · #445). 문구는 `install-render`.\n *\n * `staleSkillDirs` 와 같은 형태로 **디스크를 본다** — manifest 에서 사라진 자산은 설치 기록이나\n * 카탈로그를 훑어서는 안 보인다. 열거하지 않는 것도 같다: 배선 SSOT 의 `RETIRED_AGENT_IDS` 를\n * 읽으므로 다음 은퇴에서 이 함수가 뒤처지지 않는다.\n */\nfunction staleAgentFiles(claudeDir: string): string[] {\n const dir = join(claudeDir, \"agents\");\n if (!existsSync(dir)) return [];\n return readdirSync(dir, { withFileTypes: true })\n .filter((e) => e.isFile() && e.name.endsWith(\".md\"))\n .map((e) => basename(e.name, \".md\"))\n .filter((id) => RETIRED_AGENT_IDS.includes(id));\n}\n\n/**\n * `.claude/agents/` 에 남아 있는 **이 트랙에서 강등된** 에이전트 id (ADR-090 · #458).\n *\n * `staleAgentFiles` 와 같은 형태로 디스크를 보되, 판정은 **배선 SSOT 의 `TRACK_AGENTS` 패턴을\n * 이 설치본의 트랙으로 `anyTrack` 한 결과**다. id 를 여기 적지 않는 이유는 은퇴 쪽과 같다 —\n * 다음 강등에서 이 함수가 조용히 뒤처진다.\n *\n * **트랙을 모르면 아무 말도 하지 않는다.** install log 가 없는 레거시 설치본이 그쪽이고, 거기서\n * 추측으로 \"지워도 된다\"고 말하면 실제로 그 트랙의 자산인 파일을 지우게 만든다.\n */\nfunction demotedAgentFiles(claudeDir: string, tracks: ReadonlyArray<Track>): string[] {\n if (tracks.length === 0) return [];\n const dir = join(claudeDir, \"agents\");\n if (!existsSync(dir)) return [];\n const demoted = TRACK_AGENTS.filter(([, pattern]) => !anyTrack(tracks, pattern)).map(\n ([id]) => id,\n );\n return readdirSync(dir, { withFileTypes: true })\n .filter((e) => e.isFile() && e.name.endsWith(\".md\"))\n .map((e) => basename(e.name, \".md\"))\n .filter((id) => demoted.includes(id));\n}\n\n/**\n * 릴리즈로 **새로 생긴** 자산을 설치한다 (#283).\n *\n * update 의 기본 규율은 \"이미 있는 것만 갱신\"이고(`updateDir` Track 혼입 방지), 그 규율에는\n * 반대편 구멍이 있었다: 릴리즈가 자산을 **추가하면** 기존 설치본은 update 를 몇 번 돌려도 그\n * 파일을 영영 못 받는다. 실제로 `.uzys-agent-harness/` 의 두 스크립트가 그 자리였고, 배포판\n * 룰(`ship-checklist`·`doc-governance`)이 그 경로를 호출하라고 적는 동안 파일은 없었다 —\n * 훅·에이전트·룰도 같은 경로로 새로 추가되므로 증상은 스크립트에만 있는 것이 아니다.\n *\n * **무엇을 깔지는 manifest 가 SSOT** 다. 여기에 파일 목록을 적으면 그게 두 번째 사본이 되어\n * 다음에 추가되는 자산이 똑같이 빠진다 (이 repo 가 반복해서 당한 열거-사본 실패 모드).\n *\n * **이미 있는 파일은 건드리지 않는다** — 갱신은 `updateDir`·`syncSkills` 의 몫이고 그쪽은\n * 사용자 편집분 판정과 백업을 한다. 여기서 덮어쓰면 그 판정을 우회하게 된다.\n *\n * **`.claude/` 는 claude 를 고른 설치본에만 넣는다.** install 은 `spec.cli` 에 claude 가 있을 때만\n * 그 baseline 을 만든다(`installer.ts` — codex/opencode 단독 사용자의 dead weight 회피). update 에\n * 그 술어가 없으면 codex 전용 설치본에 `.claude/` 하네스가 통째로 들어가고, 그중 `settings.json`\n * 은 훅 배선과 외부 실행까지 딸려 온다 — 고른 적 없는 CLI 의 설정이다.\n *\n * **훅과 settings.json 은 대상이 아니다.** 훅은 파일만으로는 안 돈다: `settings.json` 의 배선이\n * 있어야 발화하는데 update 는 그 파일을 동기화하지 않는다(`cleanStaleHookRefs` 로 죽은 참조를\n * 지울 뿐이다). 깔아 놓고 \"추가됨\"이라 보고하면 사용자는 안 도는 기능을 받았다고 읽는다 —\n * `hook-wiring-parity` 가 templates 쪽에서 막는 바로 그 상태를 update 가 만드는 셈이다.\n * 대신 **재설치가 필요하다고 알린다**(`needsReinstall`).\n *\n * **사용자가 해제한 자산은 다시 깔지 않는다** (ADR-074). 이 목록을 안 보면 이 함수가 곧\n * 해제의 취소 장치가 된다 — 트랙에서 manifest 를 다시 유도할 뿐이라 뺀 룰·에이전트가 돌아오고,\n * 화면은 그것을 *\"added by this release\"* 로 보고한다(같은 릴리즈에서 방금 설치한 파일인데도).\n * 게다가 되살아나는 종류가 룰·에이전트뿐이라(스킬은 `dir` 엔트리, 훅은 `needsReinstall`)\n * 사용자는 \"해제\"가 무슨 뜻인지 모델을 세울 수조차 없다.\n */\nfunction installNewAssets(\n projectDir: string,\n templatesDir: string,\n wants: (g: UpdateGroup) => boolean = () => true,\n): {\n installed: string[];\n restored: string[];\n needsReinstall: string[];\n foreignOwned: string[];\n} {\n const installed: string[] = [];\n const restored: string[] = [];\n const needsReinstall: string[] = [];\n const foreignOwned: string[] = [];\n const log = readInstallLog(projectDir);\n // 기록이 없으면 `.claude/` 를 건드리지 않는다 — 고르지 않은 CLI 의 자산을 들이는 쪽이\n // 안 깔아 주는 쪽보다 비싸다. CLI 중립 자산(`.uzys-agent-harness/`)은 그대로 대상이다.\n const claudeSelected = log?.spec.cli.includes(\"claude\") ?? false;\n const baselineExcluded = new Set(log?.spec.baselineExclude ?? []);\n // 전에 깔아 준 적이 있는가 — \"이번 릴리즈 신규\"와 \"사용자가 지운 것\"을 가르는 유일한 신호다.\n // 디스크만 보면 둘이 같아 보이고, 그 둘을 한 문구로 보고하면 한쪽에는 거짓말이 된다.\n const priorBaseline = policyBaseline(projectDir);\n\n for (const entry of trackOnlyFileAssets(installedTracks(projectDir))) {\n // #480 — 파일 자산의 묶음: 훅·settings 는 \"hooks\", 나머지(룰·에이전트·명령·스크립트)는 \"rules\".\n const group: UpdateGroup =\n entry.target.startsWith(\".claude/hooks/\") || entry.target === \".claude/settings.json\"\n ? \"hooks\"\n : \"rules\";\n if (!wants(group)) continue;\n // 하네스 앵커는 제외 — `syncHarnessAnchor` 가 **이행 로직과 함께** 소유한다. 여기서 먼저\n // 만들면 그쪽이 \"이미 있었다\"로 보고 루트 `CLAUDE.md` 의 import 줄을 얹지 않아, ADR-060\n // 이행이 조용히 반쪽이 된다.\n if (entry.target === HARNESS_ANCHOR_FILE) continue;\n // 설치 때 해제한 자산 — 없는 것이 사용자가 원한 상태다. 여기서 깔면 해제가 취소된다.\n if (isBaselineExcluded(entry.target, baselineExcluded)) continue;\n\n const target = join(projectDir, entry.target);\n // #343 — **existsSync 보다 먼저** 본다. 슬롯이 남의 저장소로의 링크면 그 저장소에 그 파일이\n // 없을 때 existsSync 가 false 를 내고, 아래 복사가 **남의 저장소 안에 우리 파일을 새로\n // 만든다**. 그 상태에서 화면은 같은 자리를 \"추가했다\"(이 함수)와 \"안 건드렸다\"(syncSkills)로\n // 동시에 말한다. 슬롯이 깨진 링크면 `mkdirSync` 가 ENOENT 로 죽어 update 자체가 끝난다.\n const foreign = foreignOwnedTarget(projectDir, entry.target);\n if (foreign !== null) {\n if (!foreignOwned.includes(foreign)) foreignOwned.push(foreign);\n continue;\n }\n if (existsSync(target)) continue;\n const source = join(templatesDir, entry.source);\n if (!existsSync(source)) continue;\n\n if (entry.target.startsWith(\".claude/\")) {\n if (!claudeSelected) continue;\n // 배선이 있어야 사는 자산 — 파일만 놓으면 침묵하는 거짓 설치가 된다.\n if (entry.target.startsWith(\".claude/hooks/\") || entry.target === \".claude/settings.json\") {\n needsReinstall.push(entry.target);\n continue;\n }\n }\n\n mkdirSync(dirname(target), { recursive: true });\n copyFileSync(source, target);\n // `policyFiles` 키는 `.claude/` 상대다 (`rules/git-policy.md`).\n const recordedAs = entry.target.startsWith(\".claude/\") ? entry.target.slice(8) : entry.target;\n if (priorBaseline.has(recordedAs)) restored.push(entry.target);\n else installed.push(entry.target);\n }\n return { installed, restored, needsReinstall, foreignOwned };\n}\n\n/** `backupFile` 이 만드는 이름 — `<원본>.backup-<YYYYMMDDTHHMMSS>[-n]` (`fs-ops.ts` claimBackupPath). */\nconst BACKUP_SUFFIX = /\\.backup-\\d{8}T\\d{6}(-\\d+)?$/;\n\n/** 백업이 생길 수 있는 자리 — 하네스가 쓰는 디렉터리 + 루트의 앵커·AGENTS.md. */\nconst BACKUP_SCAN_DIRS = [\".claude\", \".codex\", \".opencode\", \".agents\"] as const;\n\n/**\n * #480 ③ — 이번 실행이 남긴 백업 쌍. 만든 자리마다 경로를 모아 올리지 않고 **디스크를 본다**:\n * 백업을 만드는 함수가 넷(룰·스킬·앵커·외부 산출물)이라 하나가 빠지면 목록이 조용히 거짓이 된다.\n * 판정은 이름 규칙 + 시작 시각 이후의 mtime.\n */\nexport function collectRunBackups(\n projectDir: string,\n startedAt: number,\n): Array<{ path: string; backup: string }> {\n const out: Array<{ path: string; backup: string }> = [];\n const consider = (rel: string): void => {\n if (!BACKUP_SUFFIX.test(rel)) return;\n const abs = join(projectDir, rel);\n if (statSync(abs).mtimeMs < startedAt - 1000) return;\n out.push({ path: rel.replace(BACKUP_SUFFIX, \"\"), backup: rel });\n };\n for (const e of readdirSync(projectDir, { withFileTypes: true }))\n if (e.isFile()) consider(e.name);\n for (const dir of BACKUP_SCAN_DIRS) {\n for (const rel of listFilesRecursive(join(projectDir, dir))) consider(`${dir}/${rel}`);\n }\n return out.sort((a, b) => a.path.localeCompare(b.path));\n}\n\n/**\n * `.uzys-agent-harness/update-backups.json` — 이번 실행의 백업 목록. 백업이 없으면 지운다\n * (옛 목록이 남으면 \"지금도 백업이 있다\"로 읽힌다). 설치 기록 디렉터리가 없으면 만들지 않는다.\n */\nfunction writeBackupList(\n projectDir: string,\n backups: ReadonlyArray<{ path: string; backup: string }>,\n): void {\n const dir = join(projectDir, \".uzys-agent-harness\");\n const file = join(dir, \"update-backups.json\");\n try {\n if (backups.length === 0) {\n if (existsSync(file)) unlinkSync(file);\n return;\n }\n if (!existsSync(dir)) return;\n writeFileSync(\n file,\n `${JSON.stringify({ updatedAt: new Date().toISOString(), note: \"update 가 남긴 백업 쌍. 원본(path)은 최신 번들판, 백업(backup)은 당신의 편집분. 편집을 새 판에 다시 얹으려면 두 파일의 diff 를 보라.\", backups }, null, 2)}\\n`,\n );\n } catch {\n // 목록 기록 실패가 update 자체를 실패시키지는 않는다.\n }\n}\n\n/**\n * #480 ① — 릴리즈로 새로 생긴 **번들 스킬 디렉터리**를 깐다. 대상 = 이 트랙 구성이 기본\n * 옵션으로 설치했을 스킬(`buildAssetSpec` 의 기본 선택 — opt-in 스킬은 안 들인다) 중 디스크에\n * 없는 것. 설치 때 해제한 것(ADR-074)·남의 자리(#343)·이미 있는 것은 건너뛴다.\n */\nfunction installNewSkillDirs(\n projectDir: string,\n templatesDir: string,\n tracks: ReadonlyArray<Track>,\n): { installed: string[]; foreignOwned: string[] } {\n const installed: string[] = [];\n const foreignOwned: string[] = [];\n const log = readInstallLog(projectDir);\n const excluded = new Set(log?.spec.baselineExclude ?? []);\n if (!(log?.spec.cli ?? [\"claude\"]).includes(\"claude\")) return { installed, foreignOwned };\n const spec = buildAssetSpec({ tracks, options: DEFAULT_OPTIONS });\n for (const entry of buildManifest(spec)) {\n if (entry.type !== \"dir\" || !entry.target.startsWith(\".claude/skills/\")) continue;\n // ADR-074 — 설치 때 해제한 스킬은 다시 들이지 않는다. 판정은 파일 자산과 같은 헬퍼로\n // (`tests/baseline-targets.test.ts` F1 이 이 줄이 빠졌던 것을 잡았다 — python-patterns 부활).\n if (!entry.applies(spec) || isBaselineExcluded(entry.target, excluded)) continue;\n const target = join(projectDir, entry.target);\n const foreign = foreignOwnedTarget(projectDir, entry.target);\n if (foreign !== null) {\n if (!foreignOwned.includes(foreign)) foreignOwned.push(foreign);\n continue;\n }\n if (existsSync(target)) continue;\n const source = join(templatesDir, entry.source);\n if (!existsSync(source)) continue;\n copyDir(source, target, (rel) => foreignOwnedTarget(projectDir, `${entry.target}/${rel}`));\n installed.push(entry.target);\n }\n return { installed, foreignOwned };\n}\n\n/**\n * 새로 깐 스킬만 기준선에 **더한다** — `refreshSkillBaseline` 처럼 통째로 다시 찍으면 이번에\n * 건너뛴(`--only new-skills`) 기존 스킬의 사용자 편집이 \"하네스가 놓아둔 것\"으로 기록된다.\n */\nfunction recordNewSkillBaseline(\n projectDir: string,\n templatesDir: string,\n targets: ReadonlyArray<string>,\n): void {\n const log = readInstallLog(projectDir);\n if (!log) return;\n const ids = new Set(targets.map((t) => t.slice(\".claude/skills/\".length)));\n const added = collectSkillHashes(projectDir, templatesDir).filter((f) =>\n ids.has(f.path.split(\"/\")[0] ?? \"\"),\n );\n const kept = (log.skillFiles ?? []).filter((f) => !ids.has(f.path.split(\"/\")[0] ?? \"\"));\n try {\n writeInstallLog(projectDir, { ...log, skillFiles: [...kept, ...added] });\n } catch {\n // 기록 실패가 update 자체를 실패시키지는 않는다 (다른 기준선 갱신과 같은 방침).\n }\n}\n\n/**\n * opt-in 선택과 무관하게 이 트랙 구성이 받아야 하는 **파일** 자산.\n *\n * update 는 사용자가 어떤 opt-in 을 골랐는지 모른다 — install log 의 spec 은 tracks·cli 뿐이다.\n * 그래서 opt-in 축을 **양극단으로 평가해 교집합**을 취한다: 어느 쪽에서도 설치 대상이면 선택과\n * 무관한 자산이고, 한쪽에서만 대상이면 선택에 달린 자산이라 update 가 임의로 들일 수 없다\n * (plugin 을 켠 사용자에게 ECC fallback 에이전트를 깔면 그게 곧 원치 않는 자산이다).\n * 축 이름을 열거하지 않는 이유는 늘 같다 — 열거는 곧 두 번째 사본이고, 다음에 늘어나는 축이\n * 여기서 빠진다.\n *\n * `type: \"dir\"` 은 제외한다. 그쪽은 스킬 디렉터리이고 설치 여부를 `selectedInternalSkills` 가\n * 가르는데 update 는 그 선택을 복원할 수 없다 (`syncSkills` 가 \"이미 깔린 것만\" 다루는 것과\n * 같은 이유).\n *\n * 트랙 기록이 없으면 빈 배열이 들어와 `applies: all` 자산만 남는다 — 모르는 트랙의 자산을\n * 들이지 않으면서 전 트랙 공통분은 복구된다.\n */\nfunction trackOnlyFileAssets(tracks: ReadonlyArray<Track>): AssetEntry[] {\n const base = { tracks, selectedInternalSkills: [] as ReadonlyArray<string> };\n const optedOut: AssetSpec = { ...base, withEcc: false, withTauri: false };\n const optedIn: AssetSpec = { ...base, withEcc: true, withTauri: true };\n // `buildManifest` 는 목록만 만들고 게이팅은 하지 않는다 — 거르는 것은 각 엔트리의 `applies`\n // 술어이고, 그건 호출자가 평가한다 (`installer.ts` 의 manifest 루프와 같은 계약).\n return buildManifest(optedOut).filter(\n (e) => e.type === \"file\" && e.applies(optedOut) && e.applies(optedIn),\n );\n}\n\n/**\n * 하네스 앵커 동기화 + 레거시 설치본 이행 (P5 · ADR-060).\n *\n * 루트 `CLAUDE.md` 는 **덮어쓰지 않는다** — 그건 사용자 소유이고 우리 몫은 그 안의 import 한\n * 줄뿐이다. 갱신 대상은 하네스가 통째로 소유한 앵커 파일이다.\n *\n * | 루트 앵커 | 처리 |\n * |---|---|\n * | 있다 | 템플릿으로 갱신 (`claudeMdUpdated`) |\n * | 없다 | **만들고** 루트 CLAUDE.md 에 import 를 얹는다 (`anchorCreated` · `rootImportAdded`) |\n *\n * 아랫줄이 이번에 생겼다. v26.140.0 이전 설치본의 앵커는 `.claude/CLAUDE.md` 라 루트엔 아무것도\n * 없고, \"있을 때만 갱신\"으로 두면 그 사용자는 update 를 몇 번을 돌려도 ⓐ 새 앵커가 안 생기고\n * ⓑ import 줄도 안 붙고 ⓒ 구 앵커가 옛 내용 그대로 굳는데 **화면엔 아무 말도 안 뜬다**.\n * ADR-060 「적용 범위」가 단언한 이행이 코드에 없던 자리다.\n */\nfunction syncHarnessAnchor(\n projectDir: string,\n templatesDir: string,\n report: UpdateModeReport,\n now: Date = new Date(),\n): void {\n // 구 앵커는 **지우지 않고 알린다** — 사용자가 고쳤는지 판정할 근거가 update 에는 없다.\n // 이행이 끝난 뒤에도 파일이 남아 있는 한 계속 알린다 (지워야 없어지는 안내).\n if (existsSync(join(projectDir, LEGACY_ANCHOR_FILE))) {\n report.legacyAnchor = LEGACY_ANCHOR_FILE;\n }\n\n const templateMd = join(templatesDir, \"CLAUDE.md\");\n if (!existsSync(templateMd)) return;\n\n const anchor = join(projectDir, HARNESS_ANCHOR_FILE);\n const existed = existsSync(anchor);\n if (existed) {\n // #480 — 룰·스킬과 같은 잣대(ADR-046): 설치 시점 sha 와 다르면 설치자가 고친 것이니 먼저\n // 백업한다. 기준선이 없으면(레거시) 보수적으로 백업 — 증명 없이 편집분을 지우지 않는다.\n // 내용이 이미 같으면 아무것도 안 한다(재실행이 백업 노이즈를 쌓으면 안 된다).\n const current = readFileSync(anchor, \"utf8\");\n if (current !== readFileSync(templateMd, \"utf8\")) {\n const recorded = readInstallLog(projectDir)?.templates.rootClaudeMd;\n const owned =\n recorded?.path === HARNESS_ANCHOR_FILE && hashContent(current) === recorded.sha256;\n if (!owned) {\n backupFile(anchor, now);\n report.anchorBackedUp = true;\n }\n copyFileSync(templateMd, anchor);\n }\n report.claudeMdUpdated = true;\n // 갱신 뒤 기준선도 다시 찍는다 — 안 찍으면 다음 update 가 방금 놓은 최신판을 \"사용자가\n // 고쳤다\"로 읽어 매번 백업한다(전에는 생성 때 한 번만 기록했다).\n recordAnchorBaseline(projectDir, anchor);\n // ADR-085 — 앵커가 있는 정상 설치본에서도 루트 CLAUDE.md 의 관리 블록은 매 update 현행화한다\n // (상시 스킬 안내 = 지금 깔린 스킬). upsert 는 내용이 같으면 입력을 그대로 돌려주므로 파일을\n // 만지지 않는다. 독립 리뷰(#433 B-1)가 이 분기가 빠져 있던 것을 실측으로 잡았다 — 그 전에는\n // 이행 분기에서만 불려 \"현행화한다\"는 단언이 정상 설치본에서 거짓이었다.\n report.rootBlockRefreshed = upsertRootImport(projectDir);\n return;\n }\n\n copyFileSync(templateMd, anchor);\n report.anchorCreated = true;\n report.rootImportAdded = upsertRootImport(projectDir);\n // 이번에 만든 앵커는 install log 에 남긴다 — uninstall 이 회수를 주장하는 근거가 그 기록\n // 하나뿐이라(`commands/uninstall.ts` templates.rootClaudeMd), 빼면 아무도 못 지우는 파일을\n // 새로 만들어 놓는 셈이 된다. 로그가 없으면 만들지 않는다 (설치 기록 날조 금지 — 다른\n // 기준선 갱신과 같은 방침).\n recordAnchorBaseline(projectDir, anchor);\n}\n\n/**\n * 루트 `CLAUDE.md` 에 앵커 import 를 얹는다 — install 과 **같은 함수·같은 계약**\n * (`installer.ts` writeRootClaudeMd). 파일이 없으면 fill-in 스캐폴드로 만든다.\n *\n * @returns 실제로 파일을 고쳤으면 true. 이미 import 가 살아 있으면 upsert 가 입력을 그대로\n * 돌려주고, 그때는 파일을 만지지 않는다.\n */\nfunction upsertRootImport(projectDir: string): boolean {\n const target = join(projectDir, \"CLAUDE.md\");\n const existing = existsSync(target) ? readFileSync(target, \"utf8\") : null;\n const next = upsertHarnessImport(existing, {\n projectName: basename(projectDir),\n tracks: installedTracks(projectDir),\n // ADR-085 — update 는 선택 목록을 인자로 받지 않는다. `syncSkills` 와 같은 판정을 쓴다:\n // `.claude/skills/<id>` 가 있으면 그 스킬은 이 설치가 고른 것이다.\n continuousSkills: CONTINUOUS_SKILLS.map((s) => s.id).filter((id) =>\n existsSync(join(projectDir, \".claude/skills\", id)),\n ),\n });\n if (next === existing) return false;\n writeFileSync(target, next);\n return true;\n}\n\n/**\n * 스캐폴드가 적는 \"Active track(s)\" — **설치 기록이 SSOT** 다. update 는 트랙을 인자로 받지\n * 않으므로 여기서 따로 추론하면 그 추론이 두 번째 사본이 된다. 기록이 없거나 지금 어휘에 없는\n * 트랙이면 빼고 쓴다 — 모르는 것을 지어내지 않는다.\n */\nfunction installedTracks(projectDir: string): ReadonlyArray<Track> {\n const recorded = readInstallLog(projectDir)?.spec.tracks ?? [];\n return recorded.filter((t): t is Track => (TRACKS as ReadonlyArray<string>).includes(t));\n}\n\n/** 이행으로 만든 앵커의 설치 시점 sha 를 install log 에 기록 (uninstall 회수 근거). */\nfunction recordAnchorBaseline(projectDir: string, anchor: string): void {\n const log = readInstallLog(projectDir);\n if (!log) return;\n const next: InstallLog = {\n ...log,\n templates: {\n ...log.templates,\n rootClaudeMd: {\n path: HARNESS_ANCHOR_FILE,\n sha256: hashContent(readFileSync(anchor, \"utf8\")),\n },\n },\n };\n try {\n writeInstallLog(projectDir, next);\n } catch {\n // 기록 실패가 update 자체를 실패시키지는 않는다 (D16 과 같은 방침).\n }\n}\n\n/**\n * 외부 CLI 산출물 갱신 + 기준선 재기록 (v26.134.0 · ADR-049).\n *\n * **어느 CLI 가 설치돼 있는지 판정하지 않는다.** `refreshOnly` 가 \"디스크에 이미 있는 파일만\"\n * 으로 걸러 주므로, 안 깐 CLI 는 대상 파일이 없어 자연히 아무것도 안 쓴다. 선택 스킬도 같다 —\n * 전체 목록을 넘겨도 안 깔린 스킬은 파일이 없어 건너뛴다. 그래서 update 쪽에 CLI 목록이나\n * 스킬 선택 상태의 **사본이 생기지 않는다** (이 repo 가 반복해서 당한 열거-사본 실패 모드).\n *\n * **룰만 예외로 거른다** (ADR-074). `AGENTS.md` 는 룰을 파일 하나에 **합쳐 렌더**하므로\n * refreshOnly 의 \"디스크에 있는 것만\" 규칙이 룰 단위로는 작동하지 않는다 — 파일이 있으니\n * 통째로 다시 써지고, 그 안에 사용자가 뺀 룰이 되돌아온다. 여기가 `.claude/` 밖으로 룰이\n * 나가는 유일한 경로다.\n */\nfunction refreshExternalCli(\n projectDir: string,\n harnessRoot: string,\n): { externalUpdated: number; externalBackedUp: string[]; externalForeignOwned: string[] } {\n const log = readInstallLog(projectDir);\n const baselineExcluded = new Set(log?.spec.baselineExclude ?? []);\n const result = runCliTransforms({\n harnessRoot,\n projectDir,\n cli: ALL_CLI_TARGETS,\n selectedInternalSkills: INTERNAL_BUNDLED_SKILL_IDS,\n // 스킬은 **전부** 넘긴다 — 안 깔린 스킬은 대상 파일이 없어 refreshOnly 가 건너뛴다.\n rules: ALL_RULES.filter((r) => !isBaselineExcluded(`.claude/rules/${r}.md`, baselineExcluded)),\n previousExternal: log?.externalFiles ?? [],\n refreshOnly: true,\n });\n\n // 기준선 재기록 — `refreshPolicyBaseline` 과 같은 이유로 필수다. 빼면 다음 update 가 방금\n // 자기가 덮어쓴 파일을 \"사용자가 고쳤다\"로 오판해 백업을 매번 쌓는다.\n // 이번에 안 건드린 산출물의 기록은 `mergeExternalFiles` 가 유지한다 (지우면 그 파일들이\n // 다음 실행에서 판정 불가로 떨어진다). 로그가 없으면 만들지 않는다 — 설치 기록 날조 금지.\n if (log) {\n const merged = mergeExternalFiles(projectDir, log.externalFiles, result.externalFiles);\n const next: InstallLog = { ...log };\n if (merged.length > 0) next.externalFiles = merged;\n else delete next.externalFiles;\n try {\n writeInstallLog(projectDir, next);\n } catch {\n // 기록 실패가 update 자체를 실패시키지는 않는다 (D16 과 같은 방침).\n }\n }\n\n return {\n externalUpdated: result.externalUpdated,\n externalBackedUp: result.externalBackedUp,\n externalForeignOwned: result.externalForeignOwned,\n };\n}\n\n/**\n * 정책 디렉터리 하나에 대한 소유 판정 컨텍스트 (v26.132.0 · ADR-047).\n *\n * `prefix` 는 `.claude/` 기준 디렉터리명(`rules` 등) — install log 의 `policyFiles` 키가\n * 그 형식이라 baseline 조회에 필요하다.\n */\nexport interface PolicySyncCtx {\n prefix: string;\n /** 설치 시점 기준선 (`.claude/` 상대경로 → sha256). 빈 Map = 판정 불가. */\n baseline: ReadonlyMap<string, string>;\n}\n\n/**\n * 디스크 내용이 하네스가 놓아둔 것 그대로인가.\n *\n * v26.134.0 — 판정식 자체는 `install-log.ts` 의 `isHarnessOwned` **하나뿐**이고 여기서는\n * 키 조립(`<prefix>/<rel>`)만 한다. ADR-048 이 \"술어는 한 곳에\"라고 적고도 이 파일에 사본이\n * 남아 있었다 — 소유 판정은 사용자 파일 삭제 여부를 가르므로 두 벌이 갈리면 피해가 크다.\n */\nfunction isHarnessOwned(ctx: PolicySyncCtx, rel: string, current: string): boolean {\n return isOwnedByBaseline(ctx.baseline, `${ctx.prefix}/${rel}`, current);\n}\n\n/**\n * target 에 이미 있는 파일만 templates 기준으로 갱신한다 (Track 혼입 방지).\n *\n * v26.132.0 (ADR-047) — 사용자 편집분 판정이 붙었다. 그 전까지는 `copyFileSync` 로 무조건\n * 덮어써서, 같은 update 실행 안에서 스킬은 백업을 받고 룰·훅은 조용히 밀렸다.\n *\n * | 디스크 vs 기준선 | 뜻 | 처리 |\n * |---|---|---|\n * | 같다 | 사용자가 안 고쳤다 | 조용히 덮어쓴다 |\n * | 다르다 | 사용자가 고쳤다 | `.backup-<stamp>` 남기고 최신판을 자리에 |\n * | 기록 없음 | 판정 불가 | 보수적으로 백업 (레거시 설치의 첫 update 1회) |\n *\n * 내용 비교로 대신할 수 없다: 하네스가 개선해서 달라진 파일도 \"다르다\"로 잡혀 릴리즈마다\n * 전 사용자에게 백업본이 쌓인다 (`fs-ops.ts` backupFile 주석과 같은 이유).\n */\nexport function updateDir(\n target: string,\n source: string,\n ext: string,\n ctx: PolicySyncCtx,\n now: Date = new Date(),\n): { updated: number; backedUp: string[] } {\n if (!existsSync(target) || !existsSync(source)) return { updated: 0, backedUp: [] };\n let updated = 0;\n const backedUp: string[] = [];\n\n for (const file of readdirSync(target)) {\n if (!file.endsWith(ext)) continue;\n const targetFile = join(target, file);\n const sourceFile = join(source, file);\n if (!existsSync(sourceFile)) continue;\n\n const next = readFileSync(sourceFile, \"utf8\");\n const current = readFileSync(targetFile, \"utf8\");\n if (current === next) continue; // 이미 최신 — 백업도 쓰기도 불필요\n\n if (!isHarnessOwned(ctx, file, current)) {\n backupFile(targetFile, now);\n backedUp.push(`${ctx.prefix}/${file}`);\n }\n writeFileSync(targetFile, next);\n updated++;\n }\n return { updated, backedUp };\n}\n\n/**\n * `.claude/skills/` 를 templates 기준으로 갱신한다 (v26.126.0 · R-3a · ADR-046).\n *\n * **설치된 스킬만 손댄다** — templates 에 있어도 target 에 그 스킬 디렉터리가 없으면 건너뛴다.\n * 스킬은 트랙/opt-in 으로 게이팅돼 설치되므로(`installer.ts` selectedInternalSkills), 전부\n * 복사하면 사용자가 고르지 않은 스킬이 딸려 들어간다 (`updateDir` 의 \"Track 혼입 방지\"와 같은 취지).\n *\n * 파일 단위 판정:\n * | 디스크 vs 기준선 | 뜻 | 처리 |\n * |---|---|---|\n * | 같다 | 사용자가 안 고쳤다 | 조용히 덮어쓴다 |\n * | 다르다 | 사용자가 고쳤다 | `.backup-<stamp>` 남기고 덮어쓴다 |\n * | 기준선 기록 없음 | 판정 불가 | 보수적으로 백업 (레거시 설치의 첫 update 1회) |\n *\n * **번들에 없는 파일은 지운다** (#477, 사용자 결정 2026-09-20 — \"있으면 백업하고 다시 깐다\").\n * 번들 스킬 디렉터리의 파일 목록이 최신본의 정의이고, 사용자 것(기준선 없음·다름)은 지우기 전에\n * 덮어쓸 때와 같은 잣대로 `.backup-<stamp>` 을 남긴다. 기준선은 번들에 있는 파일만 담는다\n * (`collectSkillHashes`) — 그래야 사용자 파일이 \"하네스가 깔았던 것\"으로 오판되지 않는다.\n *\n * **심볼릭 링크는 건너뛴다** (2026-08-02 · ADR-062). `existsSync` 는 링크를 따라가므로 그것만\n * 보면 \"설치돼 있다\"와 \"다른 저장소를 가리키는 링크가 있다\"가 구분되지 않는다. `npx skills add`\n * 로 받은 스킬의 프로젝트 스코프 설치처가 바로 `.claude/skills/<id>` 이고 그 실체는 skills\n * 저장소로의 링크다 — 그대로 쓰면 하네스가 **사용자의 다른 저장소 본문을 우리 판본으로\n * 덮어쓴다**. 쓰기 대상이 `.claude/` 밖이라 백업이 남아도 사용자가 찾을 자리가 아니다.\n * 소유자가 우리가 아닌 것은 손대지 않고 건너뛴 사실을 보고한다(침묵 금지).\n */\nexport function syncSkills(\n targetDir: string,\n sourceDir: string,\n baseline: ReadonlyMap<string, string>,\n now: Date = new Date(),\n /**\n * #343 — `<id>/<rel>` 를 받아 그 파일을 쓰면 남의 것을 건드리는 경우 **그 자리**를 돌려준다.\n * `copyDir` 과 같은 모양이다: 판정(정책)은 `foreign-slot.ts` 가, 경로 조립은 호출자가 갖는다.\n * 슬롯만 보는 위 판정으로는 **슬롯 안쪽**(파일 링크·중간 디렉터리 링크·FIFO)이 안 걸린다.\n * **필수**다 — 옵셔널이면 다음 호출자가 조용히 빠뜨린다(`copyDir` 과 같은 이유).\n */\n foreignOf: (relInSkills: string) => string | null,\n): {\n updated: number;\n backedUp: string[];\n skippedLinks: string[];\n foreignOwned: string[];\n pruned: string[];\n} {\n if (!existsSync(targetDir) || !existsSync(sourceDir))\n return { updated: 0, backedUp: [], skippedLinks: [], foreignOwned: [], pruned: [] };\n let updated = 0;\n const backedUp: string[] = [];\n const skippedLinks: string[] = [];\n const foreignOwned: string[] = [];\n const pruned: string[] = [];\n\n for (const skill of readdirSync(sourceDir, { withFileTypes: true })) {\n if (!skill.isDirectory()) continue;\n const targetSkill = join(targetDir, skill.name);\n // #343 — 판정은 `install` 과 **같은 술어**를 쓴다 (SSOT = foreign-slot.ts). 전에는 여기만\n // `isSymbolicLink()` 라 심링크는 건너뛰면서 **일반 파일·FIFO 는 그대로 통과**했고, 아래\n // `mkdirSync` 가 EEXIST 로 죽어 update 전체가 끝났다. install 이 그 상태를 화면으로\n // 승인하게 된 지금은 그 크래시가 곧 \"install 은 되는데 update 만 죽는다\"가 된다.\n // existsSync 보다 **먼저** 본다 — 깨진 링크는 existsSync 가 false 라 조용히 빠져나갔다.\n if (occupiedByNonDirectory(targetSkill)) {\n skippedLinks.push(skill.name);\n continue;\n }\n if (!existsSync(targetSkill)) continue; // 사용자가 선택하지 않은 스킬 — 새로 깔지 않는다\n\n for (const rel of listFilesRecursive(join(sourceDir, skill.name))) {\n const targetFile = join(targetSkill, rel);\n // 슬롯이 우리 디렉터리여도 **그 안**이 남의 것일 수 있다. 판정 없이 쓰면 링크를 따라\n // 남의 파일을 덮고(보고 0줄), FIFO 면 `readFileSync` 가 영영 블록된다.\n const foreign = foreignOf(`${skill.name}/${rel}`);\n if (foreign !== null) {\n if (!foreignOwned.includes(foreign)) foreignOwned.push(foreign);\n continue;\n }\n const next = readFileSync(join(sourceDir, skill.name, rel), \"utf8\");\n\n if (!existsSync(targetFile)) {\n // 스킬 안에 새로 생긴 파일 (예: references/ 추가) — 스킬 자체는 이미 설치돼 있다.\n mkdirSync(dirname(targetFile), { recursive: true });\n writeFileSync(targetFile, next);\n updated++;\n continue;\n }\n\n const current = readFileSync(targetFile, \"utf8\");\n if (current === next) continue; // 이미 최신 — 백업도 갱신도 불필요\n\n const recorded = baseline.get(`${skill.name}/${rel}`);\n if (recorded === undefined || hashContent(current) !== recorded) {\n backupFile(targetFile, now);\n backedUp.push(`${skill.name}/${rel}`);\n }\n writeFileSync(targetFile, next);\n updated++;\n }\n\n // #477 — 번들 목록이 곧 \"최신본이 가져야 할 파일\" 이다. 그 밖의 파일은 지운다 — 결과는\n // \"디렉터리를 지우고 다시 까는 것\"과 같고, 사용자 편집분·출처 미상 파일은 덮어쓸 때와 같은\n // 잣대(기준선 sha)로 먼저 백업한다. 우리가 남긴 `*.backup-*` 은 목록 밖이지만 지우지\n // 않는다 — 지우면 매 update 가 직전 백업을 먹는다. 남의 자리(링크·FIFO)도 건드리지 않는다.\n const bundled = new Set(listFilesRecursive(join(sourceDir, skill.name)));\n for (const rel of listFilesRecursive(targetSkill)) {\n if (bundled.has(rel) || basename(rel).includes(\".backup-\")) continue;\n if (foreignOf(`${skill.name}/${rel}`) !== null) continue;\n const targetFile = join(targetSkill, rel);\n const recorded = baseline.get(`${skill.name}/${rel}`);\n if (recorded === undefined || hashContent(readFileSync(targetFile, \"utf8\")) !== recorded) {\n backupFile(targetFile, now);\n backedUp.push(`${skill.name}/${rel}`);\n }\n unlinkSync(targetFile);\n pruned.push(`${skill.name}/${rel}`);\n // 비게 된 중간 디렉터리(예: references/)도 걷는다 — 빈 디렉터리는 \"파일이 있었다\"는 흔적만 남긴다.\n for (\n let d = dirname(targetFile);\n d !== targetSkill && readdirSync(d).length === 0;\n d = dirname(d)\n )\n rmdirSync(d);\n }\n }\n return { updated, backedUp, skippedLinks, foreignOwned, pruned };\n}\n\n/** 설치 시점 기준선을 Map 으로. 기록이 없으면 빈 Map — 그때는 보수적 백업으로 폴백한다. */\nfunction skillBaseline(projectDir: string): ReadonlyMap<string, string> {\n const log = readInstallLog(projectDir);\n return new Map((log?.skillFiles ?? []).map((f) => [f.path, f.sha256]));\n}\n\n/** 정책 파일 기준선 (v26.132.0 · ADR-047). 빈 Map = 덮어쓰기는 보수적 백업, prune 은 전면 중단. */\nfunction policyBaseline(projectDir: string): ReadonlyMap<string, string> {\n const log = readInstallLog(projectDir);\n return new Map((log?.policyFiles ?? []).map((f) => [f.path, f.sha256]));\n}\n\n/**\n * 정책 파일 기준선을 갱신한다 — `refreshSkillBaseline` 과 같은 이유로 필수다.\n * 이걸 빼면 다음 update 가 방금 자기가 덮어쓴 파일을 \"사용자가 고쳤다\"로 오판해 백업을 매번 쌓는다.\n *\n * 로그가 없으면 만들지 않는다 (설치 기록 날조 금지 — uninstall 이 그걸 믿는다).\n */\nfunction refreshPolicyBaseline(\n projectDir: string,\n templatesDir: string,\n /** #480 — 이번에 동기화한 디렉터리(`POLICY_DIRS` 의 `dir`). 안 주면 전부. */\n dirs?: ReadonlyArray<string>,\n): void {\n const log = readInstallLog(projectDir);\n if (!log) return;\n const inDirs = (path: string): boolean =>\n dirs === undefined || dirs.some((d) => path.startsWith(`${d}/`));\n const policyFiles = [\n ...(log.policyFiles ?? []).filter((f) => !inDirs(f.path)),\n ...collectPolicyHashes(projectDir, templatesDir).filter((f) => inDirs(f.path)),\n ];\n const next: InstallLog = { ...log };\n if (policyFiles.length > 0) next.policyFiles = policyFiles;\n else delete next.policyFiles;\n try {\n writeInstallLog(projectDir, next);\n } catch {\n // 기록 실패가 update 자체를 실패시키지는 않는다 (D16 과 같은 방침).\n }\n}\n\n/**\n * 갱신 직후 기준선을 다시 찍는다.\n *\n * 이걸 빼면 다음 update 가 **방금 자기가 덮어쓴 파일**을 전부 \"사용자가 고쳤다\"로 오판해\n * 백업본을 매번 새로 쌓는다. update 는 install log 를 안 쓰는 단축 경로라\n * (`installer.ts` runUpdateInstall) 여기서 안 하면 아무도 안 한다.\n *\n * 로그가 없으면 **만들지 않는다** — update 가 설치 기록을 날조하면 uninstall 이 그걸 믿는다.\n */\nfunction refreshSkillBaseline(projectDir: string, templatesDir: string): void {\n const log = readInstallLog(projectDir);\n if (!log) return;\n const skillFiles = collectSkillHashes(projectDir, templatesDir);\n const next: InstallLog = { ...log };\n if (skillFiles.length > 0) next.skillFiles = skillFiles;\n else delete next.skillFiles;\n try {\n writeInstallLog(projectDir, next);\n } catch {\n // 기록 실패가 update 자체를 실패시키지는 않는다 (D16 — 설치/갱신 성공 우선과 같은 방침).\n }\n}\n\n/**\n * Templates 에 없는데 target 에 있는 파일 제거 — **하네스가 깔았던 것만** (v26.132.0 · ADR-047).\n *\n * 그 전까지는 \"templates 에 없다\"만으로 지웠다. 그러면 사용자가 직접 만든 커스텀 룰·훅이\n * 백업도 없이 사라진다 — templates 에 없는 건 폐기된 하네스 룰도, 사용자가 쓴 팀 규칙도\n * 똑같이 \"없음\"이기 때문이다. 소유는 install log 기준선으로만 주장할 수 있다.\n *\n * 기록이 없으면(레거시 로그·로그 부재) **아무것도 지우지 않는다.** 폐기 룰이 남는 비용보다\n * 사용자 파일을 지우는 비용이 크다. 그쪽은 다음 install 이 기준선을 채우면 자연히 회수된다.\n *\n * 지우기 전 사용자가 고친 흔적이 있으면 백업을 남긴다 — 하네스가 깔았더라도 그 위에 쓴 내용은\n * 사용자 것이다.\n */\nexport function pruneOrphans(\n target: string,\n source: string,\n ext: string,\n ctx: PolicySyncCtx,\n now: Date = new Date(),\n): string[] {\n if (!existsSync(target) || !existsSync(source)) return [];\n const removed: string[] = [];\n for (const file of readdirSync(target)) {\n if (!file.endsWith(ext)) continue;\n const sourceFile = join(source, file);\n if (!existsSync(sourceFile)) {\n const targetFile = join(target, file);\n // 소유를 주장할 수 있는 것만 지운다. 기준선에 없으면 사용자가 만든 파일이다.\n if (!ctx.baseline.has(`${ctx.prefix}/${file}`)) continue;\n try {\n const current = readFileSync(targetFile, \"utf8\");\n if (!isHarnessOwned(ctx, file, current)) backupFile(targetFile, now);\n unlinkSync(targetFile);\n removed.push(file);\n } catch {\n // best-effort — read-only? 다음 update 시 재시도\n }\n }\n }\n return removed;\n}\n\n/**\n * 프로젝트 루트의 `.mcp-allowlist` 를 백업 후 제거한다 (ADR-072). 없었으면 null.\n *\n * `pruneOrphans` 로 못 덮는 이유는 대상이 `.claude/` 밖이고 디렉터리 단위 동기화의 짝이 없기\n * 때문이다. 그래서 소유 판정도 다르게 한다 — 저쪽은 install log 기준선으로 \"우리가 깔았나\"를\n * 묻지만, 이 파일은 **경로 하나가 곧 소유 증거다**(하네스 말고 이 이름을 쓰는 것이 없다).\n *\n * 백업은 무조건 남긴다. 자동 생성물이지만 사용자가 서버 줄을 주석 처리해 정책을 적어 넣는\n * 파일이었고, 그 편집분을 우리가 판정할 수단이 없다 — 판정할 수 없으면 지우기 전에 남긴다.\n *\n * 실패는 삼킨다(read-only 디렉터리 등). 이 정리는 update 의 목적이 아니라 뒷정리라, 여기서\n * 던지면 갱신 전체가 실패한다.\n */\nexport function retireMcpAllowlist(projectDir: string, now: Date = new Date()): string | null {\n const target = join(projectDir, \".mcp-allowlist\");\n if (!existsSync(target)) return null;\n try {\n const backup = backupFile(target, now);\n unlinkSync(target);\n return backup;\n } catch {\n return null;\n }\n}\n\n/**\n * settings.json의 PreToolUse/PostToolUse hooks 중 실존 파일 없는 hook script 참조 제거.\n * bash clean_stale_hook_refs 등가 (jq 의존 없이 JSON 직접 파싱).\n *\n * @param claudeDir `.claude/` 자신. 이전엔 `.claude/hooks/` 였다 — M-1 으로 한 층 넓혔다.\n * @returns 제거된 hook script 의 `.claude/` 기준 상대경로 목록\n */\nexport function cleanStaleHookRefs(settingsPath: string, claudeDir: string): string[] {\n let settings: SettingsJson;\n try {\n settings = JSON.parse(readFileSync(settingsPath, \"utf8\")) as SettingsJson;\n } catch {\n return [];\n }\n const hookEvents = settings.hooks ?? {};\n const removed: string[] = [];\n const cleanedHooks: Record<string, HookEntry[]> = {};\n\n for (const [eventName, eventEntries] of Object.entries(hookEvents)) {\n if (!Array.isArray(eventEntries)) {\n cleanedHooks[eventName] = eventEntries; // non-array event — 그대로 보존\n continue;\n }\n cleanedHooks[eventName] = eventEntries\n .filter((entry) => Array.isArray(entry?.hooks))\n .map((entry) => ({\n ...entry,\n hooks: entry.hooks.filter((hook) => keepHookRef(hook, claudeDir, removed)),\n }))\n .filter((entry) => entry.hooks.length > 0); // stale 제거 후 hooks 빈 entry 제거\n }\n\n if (removed.length > 0) {\n const next: SettingsJson = { ...settings, hooks: cleanedHooks };\n writeFileSync(settingsPath, `${JSON.stringify(next, null, 2)}\\n`);\n }\n return removed;\n}\n\n/**\n * hook command 가 **이 프로젝트에 앵커된** 실존 `.sh` 참조면 true.\n * 앵커돼 있는데 파일이 없으면 removed 에 상대경로를 수집하고 false (= 제거).\n * 앵커가 없으면 파일 부재와 무관하게 true (= 보존, `removed` 수집도 안 한다).\n *\n * M-1 — 탐지 범위가 `.claude/hooks/` 한 층에서 **`.claude/` 이하 임의 깊이**로 넓어졌다.\n * `templates/settings.json` 은 `applies: all` 인데 거기 배선된 훅이 **스킬 디렉터리 안의 사이드카\n * 스크립트**(`.claude/skills/<id>/*.sh`)를 참조하고 그 스킬은 좁게 깔릴 수 있다 — plugin 을 켠\n * 설치자는 Write/Edit 마다 없는 파일을 bash 로 부른다(exit 127). 치유기는 이미 있었지만 이\n * 부류를 regex 가 못 물었을 뿐이다. (그 훅은 ADR-088 에서 스킬과 함께 은퇴했다 — 부류는 남는다.)\n *\n * H-2 — 그 확장이 경로 세그먼트 `/.claude/` 만 봐서 **홈 `~/.claude/`** 까지 사정권에 넣었다.\n * 앵커 판정(`projectAnchoredRef`)이 그 경계를 되돌린다.\n *\n * 캡처와 기준 디렉터리는 **원자적으로 같이** 간다 — 기준만 `claudeDir` 로 옮기고 캡처가\n * 파일명이면 `.claude/alive.sh` 를 찾다 못 찾아 멀쩡한 훅을 지운다.\n *\n * 기존 한계 유지: 한 command 안에서 **앵커 배열 순서상 먼저 걸린 앵커**가 뽑아낸 참조 하나만\n * 본다 — 문자열상 먼저 나오는 참조가 아니다. 앵커가 여럿 섞인 command 에서는 뒤쪽 참조가\n * 판정 대상이 될 수 있고, 나머지는 검사 없이 보존된다(안전한 쪽).\n *\n * export 사유: `tests/settings-reference-parity.test.ts` 가 \"이 참조는 치유기가 실제로 무는가\"를\n * 직접 호출해 면제 판정에 쓴다 — 면제는 말이 아니라 기계적 계약으로 증명한다.\n */\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 * `.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 * @returns `.claude/` 기준 상대경로. 앵커가 없거나 `.sh` 참조가 아니면 undefined.\n */\nfunction 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}/`,\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\ninterface HookCommand {\n type?: string;\n command?: string;\n}\n\ninterface HookEntry {\n matcher?: string;\n hooks: HookCommand[];\n}\n\ninterface SettingsJson {\n hooks?: Record<string, HookEntry[]>;\n [key: string]: unknown;\n}\n","/**\n * Install 출력 렌더 레이어 (v26.82.0, Phase R).\n *\n * `commands/install.ts` 가 979줄(cap 800 초과 — repo 최대 위반)로 비대해진 원인이\n * 렌더 함수 누적이었음 → 본 파일로 추출. install.ts 는 spec 검증 + 파이프라인\n * 오케스트레이션만, 여기는 화면 출력만. 동작 변경 0 (순수 이동).\n */\n\nimport { existsSync, readdirSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { CATEGORY_TITLES, type Category } from \"../categories.js\";\nimport { targetsInclude } from \"../cli-targets.js\";\nimport {\n formatResidentCostLine,\n landsOnDisk,\n residentCost,\n summarizeContextCost,\n} from \"../context-cost.js\";\nimport { assetRow, c, infoRow, padDisplay, sectionHeader, unifiedSection } from \"../design.js\";\nimport {\n assetCliSupport,\n assetReachesCli,\n EXTERNAL_ASSETS,\n type ExternalAsset,\n experimentalOptInCandidates,\n isAssetSelected,\n RENAMED_SKILL_IDS,\n RETIRED_SKILL_IDS,\n} from \"../external-assets.js\";\nimport type { AssetInstallResult } from \"../external-installer.js\";\nimport {\n type BaselineReport,\n buildManifestSpec,\n type InstallMode,\n type InstallReport,\n type ProgressEvent,\n} from \"../installer.js\";\nimport { buildManifest, RETIRED_AGENTS, TRACK_AGENTS } from \"../manifest.js\";\nimport { finalSelectedAssets, groupAssetsByCategory } from \"../preset-recommend.js\";\nimport { HARNESS_ANCHOR_FILE, HARNESS_IMPORT_LINE } from \"../project-claude-merge.js\";\nimport type { CliBase, CliTargets, InstallSpec, OptionFlags } from \"../types.js\";\n\n/**\n * v26.78.1 — Summary `CLI` 행 라벨 (SSOT). spec.cli 에서 derive → 헤더와 일관.\n * 이전 pairwise if-chain 은 codex/opencode 만 열거해 `--cli antigravity` 가 \"Claude\" 로\n * 잘못 출력 (R2). 4 base 전부 매핑.\n */\nconst CLI_SUMMARY_LABELS: Record<CliBase, string> = {\n claude: \"Claude\",\n codex: \"Codex\",\n opencode: \"OpenCode\",\n antigravity: \"Antigravity\",\n};\n\n/** Callbacks for progressive rendering during runInstall (avoids \"Phase 1 silence\" UX). */\nexport interface PipelineCallbacks {\n onProgress?: (event: ProgressEvent) => void;\n externalDeps?: {\n onAssetStart?: (asset: ExternalAsset) => void;\n onAssetResult?: (result: AssetInstallResult) => void;\n };\n}\n\n/** createInstallRenderer 반환 — 스트리밍 콜백 + 렌더 상태 조회. */\nexport interface InstallRenderer {\n callbacks: PipelineCallbacks;\n /** External assets 헤더 출력 여부 — Summary 직전 trailing newline 판단용. */\n phase2HeaderPrinted(): boolean;\n}\n\n/**\n * install header (TARGET / TRACKS / CLI / SCOPE / OPTIONS / ASSETS) 렌더.\n * wizard 모드는 Step 3 review + Step 4 confirm 에서 이미 표시하므로 호출 안 함.\n */\nexport function renderInstallHeader(\n log: (msg: string) => void,\n spec: InstallSpec,\n mode?: InstallMode,\n): void {\n const headerLabel =\n mode === \"update\"\n ? \"uzys-agent-harness · update\"\n : mode === \"add\"\n ? \"uzys-agent-harness · add\"\n : mode === \"reinstall\"\n ? \"uzys-agent-harness · reinstall\"\n : \"uzys-agent-harness · install\";\n log(\"\");\n log(sectionHeader(headerLabel));\n log(\"\");\n log(infoRow(\"TARGET\", shortenPath(spec.projectDir)));\n log(infoRow(\"TRACKS\", spec.tracks.join(\", \")));\n log(infoRow(\"CLI\", spec.cli.join(\" · \")));\n // v26.64.0 (ADR-020) — SCOPE row. 사용자가 매 install 시 어디에 write 되는지 인지 (D16).\n {\n const effectiveScope = spec.scope ?? \"project\";\n const scopeMsg =\n effectiveScope === \"global\"\n ? \"Global — writes to ~/.claude/, ~/.codex/, npm -g\"\n : \"Project — current directory only (no global write)\";\n log(infoRow(\"SCOPE\", scopeMsg));\n }\n log(infoRow(\"OPTIONS\", formatOptions(spec)));\n // v26.82.0 (Phase R, S6) — merge 는 preset-recommend.ts 단일 구현 (이전 computeFinalAssets 중복).\n const finalAssets = finalSelectedAssets(spec.tracks, spec.userOverride);\n if (finalAssets.length > 0) {\n // v26.102.0 (ADR-031) — 선택 수와 실제 설치 수의 어긋남을 약속 시점에 고지 (SOD 리뷰 F3:\n // executive/codex 가 \"4 selected\" 약속 후 0 설치이던 불일치). 숨김 없이 분해만 병기.\n // 미지 id(검증은 install.ts 담당)는 도달 가능으로 취급 — 여기서 이중 판정하지 않는다.\n const unreachable = finalAssets.filter((id) => {\n const asset = EXTERNAL_ASSETS.find((a) => a.id === id);\n return asset ? !assetReachesCli(asset, spec.cli) : false;\n });\n const label =\n unreachable.length > 0\n ? `${finalAssets.length} selected (${unreachable.length} outside [${spec.cli.join(\", \")}] reach — not installed)`\n : `${finalAssets.length} selected`;\n log(infoRow(\"ASSETS\", label));\n for (const [cat, ids] of groupAssetsByCategory(finalAssets)) {\n log(` ${c.dim(`· ${cat}:`)} ${ids.join(\", \")}`);\n }\n // v26.103.0 (ADR-032) — Session-Start Context Cost NSM. 번들 스킬 = frontmatter 실측(~),\n // 외부 자산 = unmeasured 명시 (추정치를 실측처럼 표기 금지).\n // #320 H1 — **설치기와 같은 spec 으로 센다.** `InstallSpec` 을 그대로 넘기면\n // `selectedInternalSkills` 가 없어 번들 스킬이 전부 미설치로 계산되고, 설치자에게\n // 실제보다 작은 숫자가 나간다(track=tooling 에서 23 vs 실제 34). 계측·문서만 고치고\n // 이 줄을 두면 화면과 내부 수치가 어긋난다 — 일관되게 틀린 것보다 나쁘다.\n // #458 — **update 모드는 여기서 안 찍는다.** 이 줄은 manifest 계획, 즉 \"지금 깔면 이렇게\n // 된다\"이고 update 화면에서는 사실이 될 수 없다: 트랙에서 강등·은퇴한 에이전트 파일이\n // 디스크에 남아 매 세션 상주하는데 계획에는 없어서, 설치자는 실제보다 작은 숫자를 본다\n // (실측: 화면 agents 2 · 디스크 4). 갱신 **후 디스크**로 잰 줄을 `renderUpdateSummary` 가 낸다.\n if (mode !== \"update\") {\n const assetSpec = buildManifestSpec(spec);\n const cost = formatResidentCostLine(\n residentCost(\n buildManifest(assetSpec).filter(\n (e) => e.applies(assetSpec) && landsOnDisk(e.target, spec.cli),\n ),\n ),\n summarizeContextCost(finalAssets).unmeasuredCount,\n );\n if (cost) log(` ${c.dim(`· ${cost}`)}`);\n }\n }\n log(\"\");\n}\n\n/**\n * runInstall 스트리밍 렌더 콜백 생성 — baseline 완료 시 즉시 Phase 1 rows 출력,\n * external 은 per-asset 스트리밍 + 카테고리 헤더 (ADR-016 grouped progress UX).\n */\nexport function createInstallRenderer(\n log: (msg: string) => void,\n spec: InstallSpec,\n verbose: boolean,\n): InstallRenderer {\n let phase2HeaderPrinted = false;\n // v26.55.0 — Phase 2 grouped progress UX (ADR-016). category 변경 시 ━━ <Title> ━━ 헤더 출력.\n // external-installer 가 카테고리 순서로 정렬해 호출 → 첫 번째 호출이 category 1 의 첫 자산.\n let currentCategory: Category | null = null;\n const callbacks: PipelineCallbacks = {\n onProgress: (event) => {\n if (event.type === \"baseline-complete\") {\n // v26.81.0 (ADR-022) — withEcc boolean 삭제 → ecc-plugin 자산 선택으로 판정 (hint 게이팅).\n // v26.102.0 (ADR-031) — \"선택 = 설치됨\" 은 claude 도달 시에만 성립: codex 단독에선\n // ecc-plugin 이 배제되므로 fallback 힌트가 계속 진실이어야 한다 (SOD 리뷰 F2).\n const claudeSelected = targetsInclude(spec.cli, \"claude\");\n const eccWillInstall =\n claudeSelected &&\n (isAssetSelected(\"ecc-plugin\", spec) || spec.options.withPrune === true);\n renderPhase1Rows(log, event.baseline, verbose, eccWillInstall, claudeSelected);\n } else if (event.type === \"external-start\" && event.assetCount > 0) {\n // v26.63.0 — phaseHeader → unifiedSection. count 헤더에 inline 표시.\n log(unifiedSection(`External assets (${event.assetCount})`));\n log(\"\");\n phase2HeaderPrinted = true;\n } else if (event.type === \"external-complete\") {\n // v26.102.0 (ADR-031, Batch3) — CLI 도달 불가로 시도조차 안 한 자산 고지.\n // 침묵 제외는 \"4-CLI 지원\" 광고와 실동작의 어긋남을 숨긴다 (no-false-ship).\n // 어휘 주의: \"skipped\"(설치 실패)와 구분해 \"not installed\" 사용, 사유는 각 자산의\n // 실 도달 범위에서 derive — \"claude-only\" 하드코딩 금지 (SOD 리뷰 F4/F7/Nit-4).\n const excluded = event.report.excludedByCli;\n if (excluded.length > 0) {\n if (!phase2HeaderPrinted) {\n // attempted=0 인 트랙(executive 등)에서 고지가 헤더 없이 떠도는 것 방지 (F8).\n log(unifiedSection(\"External assets (0)\"));\n phase2HeaderPrinted = true;\n }\n const bySupport = new Map<string, string[]>();\n for (const a of excluded) {\n const key = assetCliSupport(a).join(\"/\");\n bySupport.set(key, [...(bySupport.get(key) ?? []), a.id]);\n }\n log(\"\");\n for (const [support, ids] of bySupport) {\n log(\n ` ${c.dim(`· ${ids.length} asset(s) not installed — requires ${support}, selected [${spec.cli.join(\", \")}]: ${ids.join(\", \")}`)}`,\n );\n }\n }\n }\n },\n externalDeps: {\n onAssetStart: (asset) => {\n // v26.57.0 (F2) — 카테고리 헤더만 출력. 자산 시작 라인 (→) 제거 — ✓ 결과 한 라인으로 1 단위 명확화.\n if (asset.category !== currentCategory) {\n if (currentCategory !== null) log(\"\");\n log(` ${c.bold(`━━ ${CATEGORY_TITLES[asset.category]} ━━`)}`);\n currentCategory = asset.category;\n }\n // #422 — 패키지 설치(npm · npx)는 수 분 걸리는데 결과 행이 뜰 때까지 화면이 침묵해\n // 설치자에게 \"멈춤\"으로 보였다(netlify-cli 413 MB). 시작을 한 줄로 알린다 — 다른 종류는\n // 초 단위라 F2 결정(자산당 결과 1행) 그대로 둔다.\n if (asset.method.kind === \"npm\" || asset.method.kind === \"npx-run\") {\n const what =\n asset.method.kind === \"npm\"\n ? `npm install ${asset.method.pkg}@${asset.method.version}`\n : `npx ${asset.method.cmd}@${asset.method.version}`;\n log(` ${c.dim(`… ${asset.id} ${what} — running, may take a few minutes`)}`);\n }\n },\n onAssetResult: (result) => {\n const meta = result.ok\n ? formatAssetMeta(result.asset, result.version)\n : (result.message ?? \"failed\");\n log(` ${assetRow(result.ok ? \"success\" : \"skip\", result.asset.id, meta)}`);\n },\n },\n };\n return { callbacks, phase2HeaderPrinted: () => phase2HeaderPrinted };\n}\n\n/** Update mode 단축 Summary — manifest copy / external 모두 skip 된 경로. */\nexport function renderUpdateSummary(\n log: (msg: string) => void,\n spec: InstallSpec,\n report: InstallReport,\n): void {\n log(\"\");\n // v26.63.2 — Summary 도 unifiedSection 으로 통일 (━━ marker). Step 5 안 sub-section 들과 일관.\n log(unifiedSection(\"Summary\"));\n log(\"\");\n log(infoRow(\"STATUS\", c.green(\"Update complete\")));\n log(infoRow(\"MODE\", \"update\"));\n if (report.backup) {\n log(infoRow(\"BACKUP\", shortenPath(report.backup)));\n log(infoRow(\"ROLLBACK\", `rm -rf .claude && mv ${shortenPath(report.backup)} .claude`));\n }\n // #458 — 상주 계측은 **갱신이 끝난 뒤** 낸다. 헤더 자리(계획)에서 옮겨온 이유는 위 주석에.\n // 문구는 헤더·wizard 와 같은 `formatResidentCostLine` 하나에서 온다 (표면별 조립 금지).\n const cost = formatResidentCostLine(\n residentCost(residentEntriesOnDisk(spec)),\n summarizeContextCost(finalSelectedAssets(spec.tracks, spec.userOverride)).unmeasuredCount,\n );\n if (cost) log(infoRow(\"CONTEXT\", cost));\n // #480 ③ — 백업이 있으면 **다음 행동**을 지목한다. 백업 사실만 알리면 설치자는 파일을 열어\n // 손으로 옮긴다 — 그게 이 이슈가 말한 스트레스다.\n const backups = report.updateMode?.backups ?? [];\n if (backups.length > 0) {\n log(\n infoRow(\n \"BACKUPS\",\n `${backups.length} edited file(s) kept as *.backup-<time> · list: .uzys-agent-harness/update-backups.json`,\n ),\n );\n log(\n infoRow(\n \"NEXT\",\n 'to re-apply your edits on the new version, ask audit-harness-fit: \"update 백업본의 내 편집을 새 판에 다시 얹어줘\"',\n ),\n );\n }\n log(\"\");\n}\n\n/**\n * update 가 재는 상주 엔트리 — **에이전트만 디스크에서** 읽는다 (#458).\n *\n * 나머지(룰·스킬·CLAUDE.md)는 update 가 방금 배포판으로 동기화한 것이라 계획 = 디스크다.\n * 에이전트만 갈리는 이유는 **트랙 조건화(ADR-090)와 은퇴(ADR-089)가 파일을 안 지우기 때문**이다:\n * manifest 에는 없는데 파일은 남아 descriptor 가 매 세션 상주한다. 그 차이를 안 반영하면 화면이\n * 실제보다 작은 숫자를 내고, 바로 그 숫자를 근거로 \"지워도 된다\" 안내가 붙는 화면이다.\n */\nfunction residentEntriesOnDisk(\n spec: InstallSpec,\n): Array<{ source: string; target: string; file?: string }> {\n const assetSpec = buildManifestSpec(spec);\n const planned = buildManifest(assetSpec).filter((e) => e.applies(assetSpec));\n const agentsDir = join(spec.projectDir, \".claude\", \"agents\");\n const onDisk = existsSync(agentsDir)\n ? readdirSync(agentsDir, { withFileTypes: true })\n .filter((e) => e.isFile() && e.name.endsWith(\".md\"))\n .map((e) => ({\n source: `agents/${e.name}`,\n target: `.claude/agents/${e.name}`,\n // 배포판이 아니라 **이 프로젝트의 파일**을 잰다 — 사용자가 고친 descriptor 도,\n // 배포판에 더는 없는 에이전트도 실제로 상주하는 것은 이쪽이다.\n file: join(agentsDir, e.name),\n }))\n : [];\n return [...planned.filter((e) => !e.target.startsWith(\".claude/agents/\")), ...onDisk];\n}\n\n/**\n * Codex / OpenCode / Antigravity 산출물 sub-section.\n * v26.78.1 (R2): antigravity 추가 — `--cli antigravity` 시 산출물 invisible 이던 버그 fix.\n * 산출물 report 가 없거나 해당 CLI 미선택 시 출력 없음 (이전 executeSpec 의 게이트 if 이동).\n */\nexport function renderCliArtifacts(\n log: (msg: string) => void,\n spec: InstallSpec,\n report: InstallReport,\n): void {\n const hasArtifacts = Boolean(report.codex || report.opencode || report.antigravity);\n const cliSelected =\n targetsInclude(spec.cli, \"codex\") ||\n targetsInclude(spec.cli, \"opencode\") ||\n targetsInclude(spec.cli, \"antigravity\");\n if (!hasArtifacts || !cliSelected) {\n return;\n }\n log(unifiedSection(formatCliPhaseTitle(spec.cli)));\n log(\"\");\n // AGENTS.md is shared across Codex/OpenCode — render once with shared note\n if (report.codex && report.opencode) {\n log(assetRow(\"success\", \"AGENTS.md\", \"shared (Codex + OpenCode)\"));\n } else if (report.codex || report.opencode) {\n log(assetRow(\"success\", \"AGENTS.md\", `from ${HARNESS_ANCHOR_FILE}`));\n }\n if (report.codex) {\n log(assetRow(\"success\", \".codex/config.toml\", \"settings + [mcp_servers.*]\"));\n log(assetRow(\"success\", \".codex/hooks/\", `${report.codex.hookFiles.length} files`));\n if (report.codex.skillFiles.length > 0) {\n log(\n assetRow(\n \"success\",\n \".agents/skills/<id>/\",\n `${countSkillDirs(report.codex.skillFiles)} skills`,\n ),\n );\n }\n // Codex global opt-in (D16) — config.toml trust entry, only when explicitly enabled.\n if (report.codexOptIn?.trustEntry.enabled) {\n const trust = report.codexOptIn.trustEntry;\n const kind = trust.status === \"error\" ? \"skip\" : \"success\";\n const meta =\n trust.status === \"registered\"\n ? '[projects.\"<dir>\"] trust_level=\"trusted\"'\n : trust.status === \"already-present\"\n ? \"already present\"\n : (trust.message ?? \"error\");\n log(assetRow(kind, \"~/.codex/config.toml trust entry\", meta));\n }\n }\n if (report.opencode) {\n log(assetRow(\"success\", \"opencode.json\", \"$schema + 5 keys\"));\n if (report.opencode.skillFiles.length > 0) {\n log(\n assetRow(\n \"success\",\n \".agents/skills/\",\n `${countSkillDirs(report.opencode.skillFiles)} dev-method skills (codex·antigravity 와 같은 자리)`,\n ),\n );\n }\n // ADR-081 — 옛 커맨드 사본을 지웠으면 말한다. 조용히 지우면 사용자는 자기가 쓰던\n // 슬래시 커맨드가 왜 사라졌는지 알 방법이 없다 (스킬로 옮겨져 이름은 그대로 뜬다).\n if (report.opencode.retiredCommands.length > 0) {\n log(\n assetRow(\n \"skip\",\n \".opencode/commands/\",\n `${report.opencode.retiredCommands.length} retired · 같은 스킬이 .agents/skills/ 로 옮겨졌다 (백업 남김)`,\n ),\n );\n }\n }\n // v26.78.1 (R2) — Antigravity 산출물: rules (항상) + dev-method skills.\n if (report.antigravity) {\n if (report.antigravity.rulesFile) {\n log(assetRow(\"success\", \".agents/rules/uzys-harness.md\", `from ${HARNESS_ANCHOR_FILE}`));\n }\n if (report.antigravity.skillFiles.length > 0) {\n log(\n assetRow(\n \"success\",\n \".agents/skills/<id>/\",\n `${countSkillDirs(report.antigravity.skillFiles)} skills`,\n ),\n );\n }\n }\n log(\"\");\n}\n\n/** 최종 Summary (STATUS / TRACKS / CLI / HOOK / WARN / OPT-IN / NEXT). */\nexport function renderFinalSummary(\n log: (msg: string) => void,\n spec: InstallSpec,\n report: InstallReport,\n fromWizard: boolean,\n): void {\n // v26.63.2 — Summary 도 unifiedSection 으로 통일 (━━ marker).\n log(unifiedSection(\"Summary\"));\n log(\"\");\n log(infoRow(\"STATUS\", c.green(\"Install complete\")));\n log(infoRow(\"TRACKS\", report.installedTracks.join(\", \")));\n // v26.63.4 (P3): install header `CLI` 와 Summary `CLIs` 라벨 불일치 → `CLI` 로 통일.\n // v26.78.1 (R2): pairwise if-chain → spec.cli derive. antigravity 누락 + claude 무조건\n // prepend(claude 미선택 시에도 \"Claude\" 표기) 버그 fix. 헤더와 동일 SSOT.\n log(infoRow(\"CLI\", spec.cli.map((b) => CLI_SUMMARY_LABELS[b]).join(\" · \")));\n // M-1 — settings.json 이 가리키던 없는 스크립트를 지웠으면 **소리를 낸다.** 무음 no-op 은\n // 이 처방을 채택할 때 명시적 기각 사유였다: 지금 유일한 파손 신호(bash exit 127)를 지우면서\n // 아무 말도 안 하면, 다음에 참조가 깨져도 아무도 모른다 (`no-false-ship` 원칙 5).\n // update 분기(아래 renderPhase1Rows — renderUpdateSummary 는 STATUS/BACKUP 만 찍는다)와\n // **같은 라벨·같은 정보량**을 쓰고, 어느 파일이 지워졌는지\n // `.claude/` 기준 상대경로로 함께 보여준다 — 파일명만으로는 사용자가 못 찾는다.\n if (report.staleHookRefs.length > 0) {\n log(\n infoRow(\n \"HOOK\",\n c.yellow(\n `settings.json stale hook refs · ${report.staleHookRefs.length} removed ` +\n `(${report.staleHookRefs.join(\", \")})`,\n ),\n ),\n );\n }\n if (report.external && report.external.skipped > 0) {\n log(\"\");\n log(\n infoRow(\n \"WARN\",\n c.yellow(\n `${report.external.skipped} external asset${report.external.skipped > 1 ? \"s\" : \"\"} skipped (see Phase 2 above)`,\n ),\n ),\n );\n }\n // v26.102.0 (ADR-031) — v26.88.0 의 NOTE(plugin-kind 만 자체 재계산)를 대체: SSOT =\n // report.external.excludedByCli. 구 NOTE 는 ⊘ 고지와 다른 계산식(shell-script 누락)이라\n // 같은 화면에서 숫자가 어긋났고, claude 를 함께 골라 실제 설치된 경우에도 \"not installed\"\n // 를 찍었다 (SOD 리뷰 F4 — no-false-ship \"동일 목록 2곳 하드코딩 금지\").\n if (report.external && report.external.excludedByCli.length > 0) {\n const excluded = report.external.excludedByCli;\n log(\"\");\n log(\n infoRow(\n \"EXCLUDED\",\n c.dim(\n `${excluded.length} asset${excluded.length > 1 ? \"s\" : \"\"} not installed — outside [${spec.cli.join(\", \")}] reach: ${excluded.map((a) => a.id).join(\", \")}`,\n ),\n ),\n );\n }\n // v26.71.1 — experimental(T3) opt-in discoverability (Transparent Defaults — 숨김 0건).\n // 비대화형(--track) 에서 condition 은 맞지만 T3 라 default 제외된 자산을 --with 안내.\n // wizard 모드는 이미 ⚠ 배지로 노출하므로 skip.\n if (!fromWizard) {\n const optIn = experimentalOptInCandidates(spec);\n if (optIn.length > 0) {\n log(\"\");\n log(\n infoRow(\n \"OPT-IN\",\n c.dim(\n `${optIn.length} experimental available — add with --with <id>: ${optIn.map((a) => a.id).join(\", \")}`,\n ),\n ),\n );\n }\n }\n log(\"\");\n const primary = (spec.cli.includes(\"claude\") ? \"claude\" : spec.cli[0]) ?? \"claude\";\n const label = CLI_SUMMARY_LABELS[primary];\n log(infoRow(\"NEXT\", `Open ${c.bold(label)} — installed rules & skills are now active`));\n const scaffoldFiles = scaffoldFilesForCli(spec.cli);\n if (scaffoldFiles.length > 0) {\n // ADR-084 — `audit-harness-fit` 의 populate 모드가 같은 스캐폴드를 리포 근거로 채운다.\n // **실제로 깔린 경우에만** 말한다: 안 깔린 스킬을 부르라는 안내는 \"advertised ≠ real\" 이다.\n // 설치 여부는 설치기와 같은 spec 으로 센다(위 ASSETS 줄과 같은 이유).\n const auditInstalled =\n buildManifestSpec(spec).selectedInternalSkills?.includes(\"audit-harness-fit\");\n const populateHint = auditInstalled\n ? `, or ask the ${c.bold(\"audit-harness-fit\")} skill to fill it from repository evidence`\n : \"\";\n log(\n infoRow(\n \"FILL\",\n `${scaffoldFiles.map((f) => c.bold(f)).join(\" · \")} — a fill-in scaffold. Open and paste each ${c.bold(\"<!-- FILL: … -->\")} prompt to your agent to tailor it to this project${populateHint}`,\n ),\n );\n }\n log(\"\");\n}\n\n/**\n * ADR-086 — `skillFiles` 는 이제 스킬 **디렉터리 전체**의 파일이다(SKILL.md + references/ …).\n * 화면의 \"N skills\" 는 파일 수가 아니라 `<id>` 디렉터리 수여야 한다 — 8종을 골랐는데 \"13 skills\"\n * 로 뜨면 설치자가 자기가 안 고른 것이 깔렸다고 읽는다.\n */\nexport function countSkillDirs(skillFiles: ReadonlyArray<string>): number {\n const ids = new Set<string>();\n for (const f of skillFiles) {\n const m = /[\\\\/]\\.agents[\\\\/]skills[\\\\/]([^\\\\/]+)[\\\\/]/.exec(f);\n if (m?.[1] !== undefined) ids.add(m[1]);\n }\n return ids.size;\n}\n\n/**\n * Which project-context scaffold files a given CLI selection actually writes:\n * `CLAUDE.md` only for a claude install, `AGENTS.md` only for a non-claude CLI.\n * The FILL hint must name only files that were written — advertising a file that\n * a given `--cli` never produced is the no-false-ship \"advertised ≠ real\" trap.\n */\nexport function scaffoldFilesForCli(cli: ReadonlyArray<CliBase>): string[] {\n const files: string[] = [];\n if (cli.includes(\"claude\")) {\n files.push(\"CLAUDE.md\");\n }\n if (cli.some((target) => target !== \"claude\")) {\n files.push(\"AGENTS.md\");\n }\n return files;\n}\n\nfunction formatAssetMeta(asset: ExternalAsset, version?: string): string {\n // v26.56.0 (F3) — description 제거. onAssetStart 의 → 라인이 이미 description 표시.\n // result row 는 method + source 만 간결하게 → terminal 120 char 안 wrap 방지.\n // v26.59.0 — plugin / npm-global 에 한해 version 표시 (path 기반 추출).\n const m = asset.method;\n const v = version ? ` ${c.dim(`v${version.replace(/^v/, \"\")}`)}` : \"\";\n switch (m.kind) {\n case \"skill\":\n // v26.63.3 (clarify M1): skill name 이 asset id 와 동일하면 중복 segment 생략.\n // \"skill · pbakaus/impeccable · impeccable\" → \"skill · pbakaus/impeccable\"\n if (m.skill && m.skill !== asset.id) return `skill · ${m.source} · ${m.skill}`;\n return `skill · ${m.source}`;\n case \"plugin\":\n return `plugin · ${m.pluginId}${v}`;\n case \"npm\":\n // A2 (Promise audit) — ADR-020 후 npm 자산 default 는 `--save-dev`(project), `-g` 는 global scope 만.\n // 라벨에 \"-g\" 고정은 scope 거짓 표기 → scope-중립 \"npm\" 으로 정정.\n // v26.80.0 — pinned 버전 표기 (Transparent Defaults: 실행되는 정확한 버전 노출).\n return `npm · ${m.pkg}@${m.version}`;\n case \"npx-run\":\n return `npx · ${m.cmd}@${m.version}`;\n case \"shell-script\":\n return `bash · ${m.script}`;\n case \"internal\":\n // v26.81.0 (ADR-022) — 내부 템플릿 자산 (Phase 1 manifest 가 설치 주체).\n return `internal · templates (${m.key})`;\n }\n}\n\n/**\n * Phase 1 rows 출력. baseline-complete progress event에서 호출 — 외부 자산 설치\n * 시작 전 즉시 화면에 표시되어야 한다 (멈춰 보임 방지).\n */\nfunction renderPhase1Rows(\n log: (msg: string) => void,\n baseline: BaselineReport,\n verbose = false,\n withEcc = false,\n // v26.102.0 (ADR-031) — ecc 힌트의 `--with ecc-plugin` 안내는 claude 도달 시에만 참\n // (plugin 은 claude 전용 — codex 단독에선 그 명령이 no-op, SOD 리뷰 F2).\n claudeSelected = true,\n): void {\n // Update mode rows\n if (baseline.updateMode) {\n if (baseline.backup) {\n log(assetRow(\"success\", \"backup\", shortenPath(baseline.backup)));\n }\n for (const [dir, count] of Object.entries(baseline.updateMode.updated)) {\n if (count > 0) log(assetRow(\"success\", dir, `${count} files updated`));\n }\n // #283 — 릴리즈로 새로 생긴 자산. 갱신 건수에 합치지 않고 따로 낸다: 사용자가 안 만든\n // 파일이 늘어난 것이므로 \"몇 개 갱신\"과는 다른 사실이고, 조용하면 자기 것으로 오인한다.\n // 한 줄에 이어 붙이지 않는다 — 레거시 설치본에서는 십수 개가 한 번에 나온다.\n for (const path of baseline.updateMode.installedNew) {\n log(assetRow(\"success\", path, \"added by this release\"));\n }\n // 원인이 다르면 문구도 달라야 한다. 이쪽은 전에 깔아 준 적이 있는 파일이라 사용자가\n // 지웠을 수 있다 — \"이번 릴리즈에 추가됨\"이라고 적으면 그 사용자에게는 거짓말이고,\n // 자기가 지운 파일이 왜 돌아왔는지 추적할 단서가 사라진다.\n for (const path of baseline.updateMode.restored) {\n log(assetRow(\"success\", path, \"was missing — reinstalled (delete it again if intentional)\"));\n }\n // 깔지 **못한** 것은 더 크게 말해야 한다. 훅은 배선이 있어야 발화하는데 update 는\n // settings.json 을 동기화하지 않는다 — 조용하면 사용자는 최신 상태라고 믿는다.\n if (baseline.updateMode.needsReinstall.length > 0) {\n log(\n assetRow(\n \"skip\",\n \"needs reinstall\",\n `${baseline.updateMode.needsReinstall.join(\", \")} · new in this release but update cannot wire them — run \\`agent-harness install\\` to get them`,\n ),\n );\n }\n for (const [dir, removed] of Object.entries(baseline.updateMode.pruned)) {\n if (removed.length > 0) {\n log(assetRow(\"skip\", `${dir} orphan prune`, `${removed.length} removed`));\n }\n }\n // #480 — 고르지 않은 묶음은 건드리지 않았음을 말한다. 조용하면 \"update 를 돌렸는데 룰이\n // 그대로다\"가 결함으로 읽힌다.\n if (baseline.updateMode.skippedGroups.length > 0) {\n log(\n assetRow(\n \"skip\",\n \"not selected — untouched\",\n `${baseline.updateMode.skippedGroups.join(\", \")} · run again with --only or the wizard to include`,\n ),\n );\n }\n if (baseline.updateMode.claudeMdUpdated) {\n log(assetRow(\"success\", HARNESS_ANCHOR_FILE, \"refreshed from template\"));\n }\n // #480 — 편집분을 백업했다는 사실은 반드시 화면에 남긴다(룰 `edited policy files` 행과 같은 이유).\n if (baseline.updateMode.anchorBackedUp) {\n log(\n assetRow(\n \"skip\",\n `${HARNESS_ANCHOR_FILE} edited`,\n \"your edits backed up as *.backup-<time> — latest template is now active\",\n ),\n );\n }\n // P5 이행 (ADR-060) — v26.140.0 이전 설치본은 앵커가 `.claude/CLAUDE.md` 라 루트에 없다.\n // 이번 update 가 만든 앵커와 사용자 `CLAUDE.md` 에 얹은 import 줄을 **화면에 남긴다**:\n // 조용히 하면 앵커 계약이 바뀐 사실도, 자기 CLAUDE.md 가 한 줄 늘었다는 사실도 모른다.\n if (baseline.updateMode.anchorCreated) {\n log(assetRow(\"success\", HARNESS_ANCHOR_FILE, \"created from template (anchor migration)\"));\n }\n if (baseline.updateMode.rootImportAdded) {\n log(assetRow(\"success\", \"CLAUDE.md\", `${HARNESS_IMPORT_LINE} import added`));\n }\n // ADR-085 — 관리 블록 안(상시 스킬 안내)이 깔린 스킬을 따라 바뀌었으면 말한다. 사용자\n // 파일의 한 구간이 바뀐 것이라 조용히 넘기지 않는다.\n if (baseline.updateMode.rootBlockRefreshed) {\n log(\n assetRow(\n \"success\",\n \"CLAUDE.md\",\n \"harness block refreshed (skills that apply continuously)\",\n ),\n );\n }\n // 구 앵커는 지우지 않는다(사용자 편집 여부 판정 불가) — 대신 죽은 사본이라는 사실을 알린다.\n if (baseline.updateMode.legacyAnchor) {\n log(\n assetRow(\n \"skip\",\n baseline.updateMode.legacyAnchor,\n `legacy anchor · no longer updated — content now in ${HARNESS_ANCHOR_FILE}; delete it when you no longer need it`,\n ),\n );\n }\n // v26.126.0 (R-3a) — 편집분을 백업했다는 사실은 **반드시 화면에 남긴다**. 갱신 건수만 보이면\n // 사용자는 자기가 고친 내용이 어디로 갔는지 알 수 없고, 그게 R-3a 를 만든 침묵과 같은 실패다.\n if (baseline.updateMode.skillsBackedUp.length > 0) {\n log(\n assetRow(\n \"skip\",\n \".claude/skills edited files\",\n `${baseline.updateMode.skillsBackedUp.length} backed up as *.backup-<time>`,\n ),\n );\n }\n // #477 — 번들에서 사라진 파일을 지웠다는 사실도 화면에 남긴다. 조용히 지우면 사용자는\n // 파일이 왜 없어졌는지 모르고, 안 지우면 옛 서식이 디스크에 남는다 — 둘 다 침묵이 문제다.\n if (baseline.updateMode.skillsPruned.length > 0) {\n log(\n assetRow(\n \"success\",\n \".claude/skills not in bundle\",\n `${baseline.updateMode.skillsPruned.length} files deleted · ${baseline.updateMode.skillsPruned.join(\", \")}`,\n ),\n );\n }\n // 2026-08-02 (ADR-062) — 다른 도구(`npx skills add`)가 소유한 자리는 건너뛴다. 그 사실을\n // 안 보이면 사용자는 \"이 스킬만 왜 안 갱신되지\"를 추적할 방법이 없고, 반대로 조용히\n // 덮어썼다면 자기 저장소가 바뀐 줄도 모른다. 둘 다 침묵이 문제라 건수가 아니라 이름을 낸다.\n // #343 — **종류를 단정하지 않는다.** 판정이 `isSymbolicLink()` 에서 \"디렉터리가 아닌 것\n // 전부\"로 넓어져 일반 파일·FIFO·깨진 링크도 이 행에 들어온다. 아래 install 행과 같은 어휘다.\n if (baseline.updateMode.skillsSkippedLinks.length > 0) {\n log(\n assetRow(\n \"skip\",\n \".claude/skills owned by another tool\",\n `${baseline.updateMode.skillsSkippedLinks.join(\", \")} · 그 자리가 우리 디렉터리가 아니라 갱신하지 않았다`,\n ),\n );\n }\n // #343 — 외부 CLI 산출물(`.agents/skills/<id>` 등)에서 같은 이유로 건너뛴 자리.\n // `.claude/skills linked` 와 나눠 내는 이유는 자리가 달라서다 — 옮겨야 할 경로를 그대로 낸다.\n if (baseline.updateMode.foreignOwned.length > 0) {\n log(\n assetRow(\n \"skip\",\n \"owned by another tool\",\n `${baseline.updateMode.foreignOwned.join(\", \")} · 그 자리가 우리 것이 아니라 건드리지 않았다`,\n ),\n );\n }\n // 2026-08-16 (ADR-072) — `.mcp-allowlist` 은퇴. 이 줄이 없으면 사용자는 자기 저장소에서\n // 파일 하나가 사라진 것만 보고 이유를 못 찾는다. 백업 경로를 함께 내는 이유는 그게\n // \"되돌릴 수 있다\"의 유일한 증거이기 때문이다 — 경로 없이 \"백업했다\"는 안내는 검증 불가다.\n if (baseline.updateMode.mcpAllowlistRetired) {\n log(\n assetRow(\n \"skip\",\n \".mcp-allowlist\",\n `retired · the hook that read it is gone — backed up as ${shortenPath(baseline.updateMode.mcpAllowlistRetired)}`,\n ),\n );\n }\n // ADR-089 (#445) — 은퇴한 에이전트. 스킬 은퇴 행(아래)과 같은 규율이다: 지우지 않고\n // **지워도 된다는 사실과 대신 쓸 것**을 말한다. 대안을 함께 적는 이유는 은퇴가 곧 기능\n // 상실로 읽히면 사용자가 파일을 붙들기 때문이다 — 여기서는 벤더 기본 기능이 대안이다.\n for (const id of baseline.updateMode.retiredAgents) {\n const instead = RETIRED_AGENTS.find((a) => a.id === id)?.instead;\n log(\n assetRow(\n \"skip\",\n \"agents\",\n `${id} · 이 릴리즈에서 은퇴 — .claude/agents/${id}.md 를 지워도 된다` +\n (instead === undefined ? \"\" : ` · ${instead}`),\n ),\n );\n }\n // ADR-090 (#458) — 트랙에서 **강등된** 에이전트. 위 은퇴 행과 사용자가 할 일은 같고(지워도\n // 된다) 사실은 다르다 — 이건 **다른 트랙에는 여전히 가는** 자산이다. 그래서 \"은퇴\"라 하지\n // 않고 어느 트랙 전용인지를 말한다. 목록을 여기 적지 않으려고 배선 SSOT(`TRACK_AGENTS`)를\n // 돌면서 보고된 id 만 찍는다 — 트랙명도 그 패턴에서 그대로 derive 한다.\n for (const [id, pattern] of TRACK_AGENTS) {\n if (!baseline.updateMode.demotedAgents.includes(id)) continue;\n log(\n assetRow(\n \"skip\",\n \"agents\",\n `${id} · 이 트랙에서는 더 이상 설치하지 않는다 — ${pattern.split(\"|\").join(\" · \")} 트랙 전용 · .claude/agents/${id}.md 를 지워도 된다`,\n ),\n );\n }\n // v26.132.0 (ADR-047) — 룰·훅 편집분도 같은 이유로 노출. 자산 종류에 따라 보이고 안 보이면\n // 사용자는 \"룰은 백업 안 되나 보다\"로 학습한다.\n if (baseline.updateMode.policyBackedUp.length > 0) {\n log(\n assetRow(\n \"skip\",\n \"edited policy files\",\n `${baseline.updateMode.policyBackedUp.length} backed up as *.backup-<time>`,\n ),\n );\n }\n // M-1 (표면 대칭) — fresh 분기(`renderFinalSummary`)와 **같은 정보량**으로 어느 파일이\n // 지워졌는지 나열한다. 논거는 update 쪽이 더 강하다: install 은 settings.json 을 템플릿으로\n // 덮어쓰지만 update 는 사용자가 손댄 settings.json 을 **제자리에서** 고치는 유일한 경로라,\n // 사용자 자신이 적어 넣은 훅이 실제로 사라질 수 있는 쪽이다. 건수만 찍으면 그 사용자는\n // 자기 훅이 왜 없어졌는지 추적할 방법이 없다.\n const staleRefs = baseline.updateMode.staleHookRefs;\n if (staleRefs.length > 0) {\n log(\n assetRow(\n \"skip\",\n \"settings.json stale hook refs\",\n `${staleRefs.length} removed (${staleRefs.join(\", \")})`,\n ),\n );\n }\n // v26.134.0 (R-3j-A · ADR-049) — 외부 CLI 산출물도 갱신 대상이 됐다. 화면에 안 보이면\n // codex/opencode 사용자는 update 가 자기 CLI 를 건드렸는지 알 수 없고, 그 침묵이 곧\n // \"update 는 .claude/ 만 한다\"는 오해를 유지시킨다.\n if (baseline.updateMode.externalUpdated > 0) {\n log(\n assetRow(\n \"success\",\n \"external CLI artifacts\",\n `${baseline.updateMode.externalUpdated} files updated`,\n ),\n );\n }\n if (baseline.updateMode.externalBackedUp.length > 0) {\n log(\n assetRow(\n \"skip\",\n \"edited external CLI files\",\n `${baseline.updateMode.externalBackedUp.length} backed up as *.backup-<time>`,\n ),\n );\n }\n // #374 — 외부 스킬(`npx skills add` 로 깐 것)은 위 행과 **다른 자산**이다. 이 행이 없던\n // 동안 사용자는 `external CLI artifacts` 를 보고 스킬도 갱신된 줄 알았고, 실제로는 첫 설치\n // 판본이 영영 남았다. 그래서 성공·실패·판정불가가 **각자 한 줄**을 갖는다 — 셋을 같은\n // 침묵으로 합치는 것이 이 결함의 정체였다.\n if (baseline.updateMode.externalSkillsRefreshed > 0) {\n log(\n assetRow(\n \"success\",\n \"external skills\",\n `${baseline.updateMode.externalSkillsRefreshed} refreshed from upstream`,\n ),\n );\n }\n if (baseline.updateMode.externalSkillsFailed.length > 0) {\n // 실패가 여럿이면 이름만 낸다 — 사유를 전부 이어 붙이면 한 줄이 1,000자를 넘어 표\n // 정렬이 깨진다(리뷰 NIT-1). 사유는 첫 건만 대표로 싣는다: 대개 같은 원인이다.\n const failed = baseline.updateMode.externalSkillsFailed;\n const head = failed[0] as { id: string; message: string };\n const meta =\n failed.length === 1\n ? `${head.id}: ${head.message}`\n : `${failed.map((f) => f.id).join(\", \")} (${failed.length}건) · 예: ${head.message}`;\n log(assetRow(\"skip\", \"external skills\", `${meta} · 다음 update 에서 다시 시도한다`));\n }\n // 기록에는 있는데 카탈로그에서 사라진 자산 — \"갱신했다\"에 섞이면 사용자는 일부만 갱신된\n // 것을 모른다.\n //\n // ADR-088 (#426) — 한 문구로 뭉치지 않는다. 사용자가 할 일이 셋으로 갈린다: 개명된 것은\n // **새 이름으로 다시 받아야** 하고, 은퇴한 것은 **지워도 되고**, 나머지는 우리가 모른다.\n // 같은 줄로 말하면 은퇴한 스킬의 새 판을 찾아 헤맨다. 매핑은 카탈로그가 소유한다.\n if (baseline.updateMode.externalSkillsNotInCatalog.length > 0) {\n const ids = baseline.updateMode.externalSkillsNotInCatalog;\n for (const id of ids) {\n const renamedTo = RENAMED_SKILL_IDS[id];\n if (renamedTo) {\n log(\n assetRow(\n \"skip\",\n \"skills\",\n `${id} 는 ${renamedTo} 가 됐다 · ${renamedTo} 는 new-skills 묶음이 깐다 · .claude/skills/${id} 는 지워도 된다`,\n ),\n );\n } else if (RETIRED_SKILL_IDS.includes(id)) {\n log(\n assetRow(\n \"skip\",\n \"skills\",\n `${id} · 이 릴리즈에서 은퇴 — .claude/skills/${id} 를 지워도 된다`,\n ),\n );\n }\n }\n const unknown = ids.filter((id) => !RENAMED_SKILL_IDS[id] && !RETIRED_SKILL_IDS.includes(id));\n if (unknown.length > 0) {\n log(\n assetRow(\n \"skip\",\n \"external skills\",\n `${unknown.join(\", \")} · 카탈로그에 없어 갱신 대상이 아니다`,\n ),\n );\n }\n }\n // 레거시 설치본 — \"갱신할 게 없다\"와 \"무엇을 갱신할지 모른다\"는 다른 사실이다.\n if (baseline.updateMode.externalSkillsUnknown) {\n log(\n assetRow(\n \"skip\",\n \"external skills\",\n \"설치 기록이 없어 갱신 대상을 판정할 수 없다 · `agent-harness install` 로 다시 깔면 기록된다\",\n ),\n );\n }\n return;\n }\n\n // Fresh / add / reinstall — Phase 1 rows\n // audit SEC-1/CODE-2 — 기존 settings.json·CLAUDE.md 를 덮어쓰기 전 백업했으면 fail-loud 노출.\n if (baseline.backups) {\n for (const b of baseline.backups) {\n log(assetRow(\"success\", \"backup\", shortenPath(b)));\n }\n }\n // v26.57.1 (F2) — multi-line 구조 (header + use + files). visual hierarchy + width-safe.\n // 사용자 image 검증 (2026-05-17): 단일 라인 description 이 width 좁을 때 wrap → 들여쓰기 깨짐.\n const cats = baseline.categories;\n if (cats) {\n // v26.63.0 — files 라인은 verbose 옵션 시만. 기본은 카운트 + use 1 줄.\n // v26.63.2 — polish: label + count 칼럼 fixed-width 정렬 (28 char). spacing scale 일관.\n const phase1Row = (label: string, count: number, useText: string, files?: string[]) => {\n const labelCol = `${c.bold(label)} ${c.dim(`(${count})`)}`;\n const padded = padDisplay(labelCol, 28);\n log(` ${c.green(\"✓\")} ${padded} ${c.dim(useText)}`);\n if (verbose && files && files.length > 0) {\n log(` ${c.dim(\"└ files:\")} ${c.dim(files.join(\", \"))}`);\n }\n };\n\n if (cats.rules.length > 0) {\n phase1Row(\n \"rules\",\n cats.rules.length,\n \"coding · git/PR · tests · ship checklist · MCP policy\",\n cats.rules,\n );\n }\n if (cats.agents.length > 0) {\n // ADR-090 (#452) — 라벨을 자산 중립으로. 은퇴·강등으로 목록이 트랙마다 달라졌고, 이름을\n // 부르면 화면이 없는 자산을 계속 부른다(ADR-073 의 `/ecc:*` 와 같은 형태). 실제 이름은\n // `--verbose` 의 files 줄이 낸다.\n phase1Row(\n \"agents\",\n cats.agents.length,\n \"independent verifier · implementation lane · domain lanes (track)\",\n cats.agents,\n );\n }\n if (cats.hooks.length > 0) {\n phase1Row(\"hooks\", cats.hooks.length, \"session-start · protect-files\", cats.hooks);\n }\n // 2026-08-16 (ADR-073) — 라벨에서 `/ecc:*` 를 뺐다. 명령 템플릿이 하나도 남지 않아 이 행은\n // 지금 뜨지 않지만, 분류기(`.claude/commands/` 접두)는 일반형이라 명령이 다시 생기면 그대로\n // 센다. 그때 화면이 없어진 자산의 이름을 부르지 않도록 라벨을 자산 중립으로 둔다.\n if (cats.commands > 0) {\n phase1Row(\"commands\", cats.commands, \"slash commands\");\n }\n if (cats.skills.length > 0) {\n phase1Row(\n \"skills\",\n cats.skills.length,\n \"bundled methodology skills (track · opt-in)\",\n cats.skills,\n );\n }\n } else {\n // v0.6.0 backwards compat — categories 없는 fakeReport 등\n log(assetRow(\"success\", \"rules + hooks + commands + agents\", `${baseline.filesCopied} files`));\n log(assetRow(\"success\", \"skeleton\", `${baseline.dirsCopied} dirs`));\n }\n // 2026-08-16 — 사용자가 3단계에서 **체크를 푼** 트랙 자산. 위 행들은 \"무엇이 깔렸는가\"만\n // 말하므로, 이 줄이 없으면 해제가 실제로 먹혔는지 화면에서 확인할 길이 없다. 0건이면 안 뜬다.\n // 이름을 전부 낸다 — 건수만 찍으면 어느 것이 빠졌는지 추적할 수 없다.\n if (baseline.baselineExcluded.length > 0) {\n // 디스크에 남은 것은 이름 옆에 표시한다. `add`·`reinstall` 은 이전 설치본을 지우지 않으므로\n // 표시가 없으면 화면이 \"빠졌다\"고 말하는 동안 그 룰은 계속 상주한다 (체크 해제 ≠ 제거).\n const onDisk = new Set(baseline.baselineExcludedOnDisk);\n const names = baseline.baselineExcluded.map((t) => {\n const name = t.replace(/^\\.claude\\//, \"\");\n return onDisk.has(t) ? `${name} (still on disk)` : name;\n });\n log(\n assetRow(\n \"skip\",\n \"excluded by you\",\n onDisk.size > 0\n ? `${names.length} — ${names.join(\", \")} · \"still on disk\" = 이전 설치본이라 지우지 않는다 (제거: agent-harness uninstall)`\n : `${names.length} — ${names.join(\", \")}`,\n ),\n );\n }\n // #343 — 자리가 남의 것이라 건너뛴 자산. 이 줄이 없으면 사용자는 자기가 3단계에서 고른\n // 스킬이 왜 없는지 알 방법이 없다 (설치는 성공으로 끝났으니 실패 메시지도 없다).\n // 어떻게 해야 받을 수 있는지까지 적는다 — 원인만 알려주는 안내는 다음 행동을 못 만든다.\n if (baseline.baselineForeignOwned.length > 0) {\n // 경로를 **자르지 않는다**. 목록에 `.claude/skills/<id>` 와 `.agents/skills/<id>` 가 섞이고\n // (전자는 Claude Code, 후자는 codex·antigravity 자리), 접두를 지우면 둘이 같은 것처럼 보인다.\n // 종류를 열거하지도 않는다 — 링크 말고 파일·FIFO·하드링크도 실제로 도달 가능하다.\n log(\n assetRow(\n \"skip\",\n \"owned by another tool\",\n `${baseline.baselineForeignOwned.length} — ${baseline.baselineForeignOwned.join(\", \")} · 그 자리가 우리 것이 아니라 건드리지 않았다 · 하네스 판본을 받으려면 그 자리를 옮기고 재설치`,\n ),\n );\n }\n // v26.63.4 (P3): Templates section 의 assetRow 호출 labelWidth=28 명시 → phase1Row 와 column 정렬.\n // default 40 은 External assets 의 긴 asset id (python-performance-optimization 등) 용 — 별개.\n const TEMPLATES_COL = 28;\n if (baseline.rootClaudeMd) {\n const n = baseline.rootClaudeMd.tracks.length;\n // 기존 사용자 파일에는 스캐폴드를 쓰지 않는다 — 두 경우를 같은 문구로 보고하면 그게 곧\n // 거짓 보고다 (P5 · ADR-060: 루트 CLAUDE.md 는 더 이상 덮어쓰지 않는다).\n log(\n assetRow(\n \"success\",\n \"CLAUDE.md (root)\",\n baseline.rootClaudeMd.created\n ? `fill-in scaffold + @import · ${n} track${n > 1 ? \"s\" : \"\"} noted`\n : `@import ${HARNESS_ANCHOR_FILE} (body preserved)`,\n TEMPLATES_COL,\n ),\n );\n }\n // v26.108.0 (ADR-037) — CI 스캐폴드 (opt-in). no-clobber: 기존 파일 보존은 skip 행으로\n // 정직 보고 (숨기면 \"설치됨\" 오인 — no-false-ship).\n if (baseline.ciScaffold) {\n for (const f of baseline.ciScaffold.written) {\n log(assetRow(\"success\", f, \"CI scaffold (fill-in template)\", TEMPLATES_COL));\n }\n for (const f of baseline.ciScaffold.skippedExisting) {\n log(assetRow(\"skip\", f, \"exists — preserved (no overwrite)\", TEMPLATES_COL));\n }\n }\n if (baseline.skipped > 0) {\n log(\n assetRow(\n \"skip\",\n \"manifest entries (applies → false)\",\n `${baseline.skipped} skipped`,\n TEMPLATES_COL,\n ),\n );\n }\n if (baseline.backup) {\n log(assetRow(\"success\", \"backup\", shortenPath(baseline.backup), TEMPLATES_COL));\n }\n const mcpList = baseline.mcpServers.join(\", \") || \"(none)\";\n log(assetRow(\"success\", \".mcp.json\", mcpList, TEMPLATES_COL));\n // v26.63.3 (distill H2): ECC fallback hint — Templates section 마지막에 통합 표시.\n // withEcc=true (ECC plugin opt-in) 사용자에게는 hint 미표시.\n // 2026-08-18 (#338 리뷰) — 괄호절에서 대상 경로를 뺐다. 이 hint 는 claude 미선택 설치에서도\n // 뜨는데(`withEcc` 는 claude 를 안 고르면 항상 false, `baseline.categories` 는 CLI 중립\n // 경로에서도 truthy), 그 설치에는 `.claude/` 가 만들어지지 않는다. 옛 문구의 숫자도 틀렸다\n // (실측 4 agents + 6 skills + 0 commands) — 숫자를 고쳐 적는 대신 세지 않아도 참인 문장으로.\n if (!withEcc && baseline.categories) {\n log(\"\");\n log(` ${c.dim(\"·\")} ${c.dim(\"ECC plugin not selected — cherry-pick fallback active\")}`);\n if (claudeSelected) {\n log(` ${c.dim(\"·\")} ${c.dim(\"Use --with ecc-plugin to install ECC plugin instead\")}`);\n }\n }\n if (baseline.envFiles.envExampleCreated) {\n log(assetRow(\"success\", \".env.example\", \"Supabase token guide\"));\n }\n if (baseline.envFiles.gitignoreEnvAdded) {\n log(assetRow(\"success\", \".gitignore\", \"+ .env\"));\n }\n if (baseline.envFiles.gitignoreNpxSkillsAdded.length > 0) {\n log(\n assetRow(\n \"success\",\n \".gitignore\",\n `+ ${baseline.envFiles.gitignoreNpxSkillsAdded.join(\" \")} (agent CLI / harness artifacts)`,\n ),\n );\n }\n log(\"\");\n}\n\nfunction formatOptions(spec: InstallSpec): string {\n // v26.81.0 (ADR-022) — 자산 플래그 13종 삭제 후 동작 옵션만. 키 순회로 enumeration drift 차단.\n const flags = (Object.keys(spec.options) as Array<keyof OptionFlags>)\n .filter((k) => spec.options[k])\n .map((k) =>\n k\n .replace(/^with/, \"\")\n .replace(/([a-z])([A-Z])/g, \"$1-$2\")\n .toLowerCase(),\n );\n // v26.63.3 (clarify H1): \"(defaults only)\" 모호 → \"(none added)\" 명료.\n return flags.length > 0 ? flags.join(\", \") : c.dim(\"(none added)\");\n}\n\n/**\n * Shorten an absolute path for display:\n * /Users/foo/bar → ~/bar (HOME relative)\n * /private/tmp/x.X → /tmp/x.X\n * /a/very/long/path → …/long/path (≥3 segs from end if > 50 chars)\n *\n * v26.48.0 — export for direct unit test (branch coverage 복구).\n */\nexport function shortenPath(p: string): string {\n if (p.length <= 50) return p;\n const home = process.env.HOME ?? \"\";\n if (home && p.startsWith(home)) {\n const rel = p.slice(home.length);\n return `~${rel.startsWith(\"/\") ? \"\" : \"/\"}${rel}`;\n }\n // private/tmp prefix on macOS — drop /private\n if (p.startsWith(\"/private/tmp/\")) {\n return p.slice(\"/private\".length);\n }\n // Last 3 segments\n const segs = p.split(\"/\").filter(Boolean);\n if (segs.length > 3) {\n return `…/${segs.slice(-3).join(\"/\")}`;\n }\n return p;\n}\n\n/**\n * v0.7.0 — CliTargets에서 codex/opencode 포함 여부에 따라 title 결정.\n * Phase 3는 codex 또는 opencode 1개 이상 포함 시 호출됨.\n * v26.48.0 — export for direct unit test (branch coverage 복구).\n */\nexport function formatCliPhaseTitle(targets: CliTargets): string {\n // v26.78.1 (R2) — antigravity 추가. 누락 시 `--cli antigravity` 산출물 헤더가\n // \"CLI artifacts\" generic 으로만 떠 antigravity 가 invisible 했음.\n const labels: string[] = [];\n if (targets.includes(\"codex\")) labels.push(\"Codex\");\n if (targets.includes(\"opencode\")) labels.push(\"OpenCode\");\n if (targets.includes(\"antigravity\")) labels.push(\"Antigravity\");\n return labels.length > 0 ? `${labels.join(\" + \")} artifacts` : \"CLI artifacts\";\n}\n","/**\n * Preset → Step 2 의 추천 ✓ 자산 매핑 (v26.44.0).\n *\n * Step 1 에서 선택된 preset 들의 condition 을 만족하는 외부 자산(plugin/skill/npm/npx)\n * 의 id 를 반환한다. Step 2 multiselect 의 초기 체크 상태에 사용.\n *\n * Option-gated 자산은 추천에 포함 X — 사용자가 의식적으로 토글해야 함\n * (superpowers, addy-agent-skills, ECC suite 등).\n */\n\nimport { assetTrustTier, EXTERNAL_ASSETS, filterApplicableAssets } from \"./external-assets.js\";\nimport { DEFAULT_OPTIONS, type InstallSpec, type Track } from \"./types.js\";\n\n/**\n * preset 1개 또는 N개 선택 시 추천 ✓ 자산 id 의 안정 정렬 배열.\n * - DEFAULT_OPTIONS 로 호출 → option-gated 자산은 모두 false → 추천에서 제외.\n * - v26.71.0 (PRD v26-71 R6) — experimental(T3) 자산은 pre-check 제외 (opt-in). official/vetted 만 추천.\n * - 결과는 자산 id 알파벳 정렬 (deterministic).\n */\nexport function recommendedExternalAssets(presets: ReadonlyArray<Track>): ReadonlyArray<string> {\n if (presets.length === 0) {\n return [];\n }\n const apps = filterApplicableAssets(EXTERNAL_ASSETS, {\n tracks: presets,\n options: DEFAULT_OPTIONS,\n });\n return apps\n .filter((a) => assetTrustTier(a.id) !== \"experimental\")\n .map((a) => a.id)\n .sort();\n}\n\n/**\n * v26.82.0 (Phase R, S6) — preset recommended + userOverride 적용 후 최종 선택 자산 id (정렬).\n * 이전엔 install.ts `computeFinalAssets` ↔ interactive.ts `formatSummary` 에 동일 merge 가\n * 중복 구현 (v26.62.4 주석이 본 위치를 통합 지점으로 지목). 우선순위: forceExclude > forceInclude.\n */\nexport function finalSelectedAssets(\n tracks: ReadonlyArray<Track>,\n userOverride?: InstallSpec[\"userOverride\"],\n): string[] {\n const selected = new Set(recommendedExternalAssets(tracks));\n if (userOverride) {\n for (const id of userOverride.forceExclude) selected.delete(id);\n for (const id of userOverride.forceInclude) selected.add(id);\n }\n return [...selected].sort();\n}\n\n/**\n * v26.82.0 (Phase R, S6) — 자산 id 를 카테고리별로 묶어 정렬된 entries 반환 (출력 hierarchy 용).\n * install header ASSETS row 와 wizard confirm summary 가 공유.\n */\nexport function groupAssetsByCategory(assetIds: ReadonlyArray<string>): Array<[string, string[]]> {\n const map = new Map<string, string[]>();\n for (const id of assetIds) {\n const asset = EXTERNAL_ASSETS.find((a) => a.id === id);\n const cat = asset?.category ?? \"other\";\n const list = map.get(cat) ?? [];\n list.push(id);\n map.set(cat, list);\n }\n return [...map.entries()];\n}\n","/**\n * List command — v26.123.0 (F-1b).\n *\n * `.uzys-agent-harness/.harness-install.json` 을 사람이 읽는 표로 출력한다. 기록은 v26.64.0(ADR-020)부터\n * 있었지만 **사용자가 볼 수단이 없었다** — 무엇이 깔렸는지 알 수 없으면 항목별 제거(`--only`)의\n * 입력값도 알 수 없다. 본 커맨드가 그 입력값(자산 id)을 보여주는 곳이다.\n *\n * 읽기 전용 — 어떤 파일도 쓰지 않는다.\n */\n\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { join, resolve } from \"node:path\";\nimport { c, padDisplay, status } from \"../design.js\";\nimport {\n hashContent,\n type InstallLog,\n type InstallLogAsset,\n type InstallLogRootFile,\n installLogPath,\n readInstallLog,\n} from \"../install-log.js\";\n\nexport interface ListOptions {\n projectDir?: string;\n}\n\nexport interface ListActionDeps {\n log?: (msg: string) => void;\n err?: (msg: string) => void;\n exit?: (code: number) => never;\n}\n\nexport function listAction(options: ListOptions = {}, deps: ListActionDeps = {}): void {\n const log = deps.log ?? console.log;\n const err = deps.err ?? console.error;\n const exit = deps.exit ?? ((code: number) => process.exit(code) as never);\n\n const projectDir = resolve(options.projectDir ?? process.cwd());\n const installLog = readInstallLog(projectDir);\n if (!installLog) {\n err(status.failure(c.red(`ERROR: install log not found at ${installLogPath(projectDir)}`)));\n err(c.dim(\" Nothing installed here by agent-harness.\"));\n exit(1);\n return;\n }\n\n log(\"\");\n log(c.bold(\"uzys-agent-harness · installed\"));\n log(\"\");\n log(c.dim(` installed: ${installLog.installedAt}`));\n log(c.dim(` scope: ${installLog.scope}`));\n log(c.dim(` tracks: ${installLog.spec.tracks.join(\", \") || \"(none)\"}`));\n log(c.dim(` cli: ${installLog.spec.cli.join(\", \") || \"(none)\"}`));\n log(\"\");\n\n log(c.bold(` Assets (${installLog.assets.length})`));\n if (installLog.assets.length === 0) {\n log(c.dim(\" (none — 내부 템플릿만 설치됨)\"));\n }\n for (const line of formatAssetRows(installLog.assets)) {\n log(line);\n }\n\n log(\"\");\n log(c.bold(\" Templates\"));\n for (const line of formatTemplateRows(installLog, projectDir)) {\n log(line);\n }\n\n const rootRows = formatRootFileRows(installLog.rootFiles ?? [], projectDir);\n if (rootRows.length > 0) {\n log(\"\");\n log(c.bold(\" Root files\"));\n log(c.dim(\" (uninstall 이 지우지 않는다 — 사용자 내용이 섞인다)\"));\n for (const line of rootRows) log(line);\n }\n\n log(\"\");\n log(c.dim(\" remove one: agent-harness uninstall --only <id>\"));\n log(c.dim(\" remove all: agent-harness uninstall\"));\n log(\"\");\n exit(0);\n}\n\n/** 자산 행 — id / method / scope / version. global 은 uninstall 이 자동 삭제하지 않으므로 표시한다. */\nexport function formatAssetRows(assets: ReadonlyArray<InstallLogAsset>): string[] {\n const idWidth = Math.max(0, ...assets.map((a) => a.id.length));\n const methodWidth = Math.max(0, ...assets.map((a) => a.method.length));\n return assets.map((a) => {\n const marker = a.scope === \"global\" ? c.yellow(\"!\") : c.green(\"✓\");\n const version = a.version ? c.dim(` v${a.version}`) : \"\";\n const note = a.scope === \"global\" ? c.yellow(\" (manual removal — D16)\") : \"\";\n return ` ${marker} ${padDisplay(a.id, idWidth)} ${c.dim(padDisplay(a.method, methodWidth))} ${c.dim(a.scope)}${version}${note}`;\n });\n}\n\n/** templates 행 — 디렉토리 + root CLAUDE.md 수정 여부(uninstall 이 보존할지 여부와 같은 판정). */\nfunction formatTemplateRows(log: InstallLog, projectDir: string): string[] {\n const dirs = [log.templates.claudeDir, log.templates.codexDir, log.templates.opencodeDir].filter(\n (d): d is string => Boolean(d),\n );\n const rows = [` ${c.dim(dirs.join(\" \"))}`];\n const rootMd = log.templates.rootClaudeMd;\n if (rootMd) {\n const path = join(projectDir, rootMd.path);\n const modified = existsSync(path) && hashContent(readFileSync(path, \"utf8\")) !== rootMd.sha256;\n rows.push(\n modified\n ? ` ${c.dim(rootMd.path)} ${c.yellow(\"(수정됨 — uninstall 시 보존)\")}`\n : ` ${c.dim(rootMd.path)}`,\n );\n }\n return rows;\n}\n\n/**\n * v26.124.0 (F-1f) — `.claude/` 밖 루트 파일 행. uninstall 안내와 같은 규율로\n * **실재하는 것만** 낸다 (사용자가 이미 지운 파일을 인벤토리에 남기면 그게 거짓 기록이다).\n */\nfunction formatRootFileRows(\n rootFiles: ReadonlyArray<InstallLogRootFile>,\n projectDir: string,\n): string[] {\n const present = rootFiles.filter((f) => existsSync(join(projectDir, f.path)));\n const width = Math.max(0, ...present.map((f) => f.path.length));\n return present.map(\n (f) =>\n ` ${c.dim(padDisplay(f.path, width))} ${c.dim(f.change === \"created\" ? \"생성\" : \"병합\")} ${c.dim(f.notes.join(\" / \"))}`,\n );\n}\n\nexport function registerListCommand(cli: import(\"../cli.js\").Cli): void {\n cli\n .command(\"list\", \"Show what agent-harness installed in this project\")\n .option(\"--project-dir <path>\", \"[Project] Target project directory\", {\n default: process.cwd(),\n })\n /* v8 ignore next 3 — cac action callback. listAction 자체는 별도 tests 로 검증. */\n .action((options: ListOptions) => {\n listAction(options);\n });\n}\n","/**\n * Uninstall command — v26.64.0 (ADR-020).\n *\n * 동작:\n * 1. `.uzys-agent-harness/.harness-install.json` 읽기 (구 위치 `.claude/` 폴백).\n * 2. assets[] 별 reverse:\n * - scope=project: 실제 reverse (`claude plugin uninstall --scope project`, `npm uninstall`, fs rm).\n * - scope=global: 안내만 (D16 — 글로벌 영역 자동 삭제 금지). 사용자가 직접 명령 실행.\n * 3. templates 폴더 rm (`.claude/`, `.codex/`, `.opencode/`) — `--keep-templates` 시 보존.\n * 4. install log 자체도 함께 제거.\n *\n * 옵션:\n * --dry-run 실제 변경 없이 reverse list 만 출력.\n * --keep-templates `.claude/`, `.codex/`, `.opencode/` 보존.\n * --only <ids> v26.123.0 (F-1c) — 항목별 제거. templates 미변경 + 로그는 남은 자산으로 재기록.\n *\n * 안전:\n * - log 없으면 명확 에러 + early exit.\n * - scope=global 자산은 절대 자동 삭제 X (D16).\n * - 되돌리기에 성공한 것만 로그에서 뺀다. 자동 경로가 없거나 실패한 자산은 기록에 남기고,\n * 아무것도 못 되돌렸으면 성공으로 보고하지 않는다 (no-false-ship).\n */\n\nimport { type SpawnSyncReturns, spawnSync } from \"node:child_process\";\nimport {\n existsSync,\n lstatSync,\n readdirSync,\n readFileSync,\n rmdirSync,\n rmSync,\n writeFileSync,\n} from \"node:fs\";\nimport { dirname, join, relative, resolve, sep } from \"node:path\";\nimport { c, status } from \"../design.js\";\nimport { skillsCliSpec } from \"../external-installer.js\";\nimport {\n hashContent,\n INSTALL_LOG_DIR,\n type InstallLog,\n type InstallLogAsset,\n type InstallLogRootFile,\n installLogPath,\n legacyInstallLogPath,\n readInstallLog,\n writeInstallLog,\n} from \"../install-log.js\";\nimport { stripHarnessImport } from \"../project-claude-merge.js\";\nimport { runInteractiveUninstall } from \"../uninstall-interactive.js\";\n\nexport interface UninstallOptions {\n projectDir?: string;\n dryRun?: boolean;\n keepTemplates?: boolean;\n /**\n * v26.123.0 (F-1c) — 항목별 제거. 쉼표 구분 자산 id.\n * 지정 시 templates(`.claude/` 등)는 건드리지 않고, 로그도 지우지 않고 **남은 자산으로 다시 쓴다**.\n */\n only?: string;\n /** v26.125.0 — 대화형 선택 화면을 건너뛰고 전량 제거 (비대화형 스크립트용). */\n yes?: boolean;\n}\n\nexport interface UninstallActionDeps {\n log?: (msg: string) => void;\n err?: (msg: string) => void;\n exit?: (code: number) => never;\n spawn?: (cmd: string, args: ReadonlyArray<string>) => SpawnSyncReturns<string>;\n rm?: (path: string) => void;\n /** `--only` 후 로그 재기록. 실패 경로를 테스트에서 재현하기 위해 주입 가능. */\n writeLog?: (projectDir: string, log: InstallLog) => void;\n}\n\ninterface ReverseStep {\n /** 어느 자산의 reverse 인지 — `--only` 성공분만 로그에서 빼기 위해 필요. */\n assetId: string;\n /** 사람이 읽는 라벨 (한 줄) */\n label: string;\n /** 실제 동작 — dry-run 일 때는 호출 안 함. */\n execute: () => { ok: boolean; message?: string };\n}\n\ninterface GlobalAdvisory {\n asset: InstallLogAsset;\n /** 사용자에게 안내할 reverse 명령 */\n command: string;\n}\n\n/**\n * 크기 예외 사유 (code-style 50줄 상한): 본 함수의 내용은 **단계의 순서와 전제조건 분기**이고,\n * 각 단계는 이미 이름 있는 함수로 빠져 있다 (`planReverse` → `headerLines` → `dryRunLines` |\n * `executeReverse` → `removeTemplates` → `settleLog` → `advisoryLines` → `summarize`).\n * 남은 것은 early return 3개(로그 없음 / 모르는 id / dry-run)와 호출 순서뿐 — 더 쪼개면\n * 그 순서가 흩어진다.\n */\nexport function uninstallAction(options: UninstallOptions, deps: UninstallActionDeps = {}): void {\n const log = deps.log ?? console.log;\n const err = deps.err ?? console.error;\n const exit = deps.exit ?? ((code: number) => process.exit(code) as never);\n const spawn = deps.spawn ?? defaultSpawn;\n const rm = deps.rm ?? defaultRm;\n const writeLog = deps.writeLog ?? writeInstallLog;\n\n const projectDir = resolve(options.projectDir ?? process.cwd());\n const installLog = readInstallLog(projectDir);\n if (!installLog) {\n err(status.failure(c.red(`ERROR: install log not found at ${installLogPath(projectDir)}`)));\n err(c.dim(\" Was this project installed by agent-harness? Nothing to uninstall.\"));\n exit(1);\n return;\n }\n\n const selectedIds = parseOnly(options.only);\n // `--only ,` 처럼 값이 있는데 id 가 하나도 안 나오면 **전량 제거로 흘려보내지 않는다** —\n // 하나만 빼려던 사용자가 templates 까지 잃는다. 무응답보다 명시적 거절이 안전하다.\n if (options.only !== undefined && selectedIds === null) {\n err(status.failure(c.red(\"ERROR: --only 에 자산 id 가 없다\")));\n err(c.dim(\" 예: --only code-review (id 는 `agent-harness list` 에서 확인)\"));\n exit(1);\n return;\n }\n const unknown = selectedIds ? unknownIds(installLog, selectedIds) : [];\n if (unknown.length > 0) {\n err(status.failure(c.red(`ERROR: not in install log: ${unknown.join(\", \")}`)));\n err(c.dim(` installed: ${installLog.assets.map((a) => a.id).join(\", \") || \"(none)\"}`));\n exit(1);\n return;\n }\n const targetAssets = selectedIds\n ? installLog.assets.filter((a) => selectedIds.includes(a.id))\n : installLog.assets;\n // `--only` 는 자산만 건드린다 — templates 를 지우면 \"하나만 빼기\"가 아니게 된다.\n const keepTemplates = options.keepTemplates || selectedIds !== null;\n\n // v26.124.0 (F-1f) — `.claude/` 밖 루트 파일 안내. `--only` 는 특정 자산만 건드리는 작업이라\n // 설치 전반이 만든 루트 파일은 대상이 아니다. 구 로그(rootFiles 부재)는 빈 배열 = 안내 없음.\n const rootFiles = selectedIds ? [] : (installLog.rootFiles ?? []);\n\n const plan = planReverse(targetAssets, spawn);\n for (const line of headerLines(installLog, selectedIds, targetAssets.length)) log(line);\n\n if (options.dryRun) {\n for (const line of dryRunLines(plan, installLog, projectDir, keepTemplates, rootFiles)) {\n log(line);\n }\n exit(0);\n return;\n }\n\n // 두 사실을 분리해서 쓴다 — 셋을 하나로 묶다 리뷰 3라운드 내리 회귀가 났다.\n // `.claude/` 가 남는가 = keepTemplates (수기 안내 대상 여부)\n // install log 가 남는가 = `--only` 인가 (settleLog: --only 만 재기록, 나머지는 삭제)\n const logSurvives = selectedIds !== null;\n\n const { succeeded, failed, removedIds } = executeReverse(plan, log, logSurvives);\n\n if (!keepTemplates) {\n const { rootClaudeMdKept, importStripped, external } = removeTemplates(\n installLog,\n projectDir,\n rm,\n );\n log(` ${status.success(`templates removed: ${formatTemplateList(installLog)}`)}`);\n for (const line of externalRemovalLines(external)) log(line);\n if (importStripped) {\n log(` ${status.success(\"CLAUDE.md — harness @import removed (본문 보존)\")}`);\n }\n if (rootClaudeMdKept) {\n const kept = installLog.templates.rootClaudeMd?.path ?? \"CLAUDE.md\";\n log(` ${c.yellow(\"⊘\")} ${kept} kept — modified since install. Remove manually if intended.`);\n }\n }\n\n const logWriteFailed = settleLog(\n { installLog, projectDir, selectedIds, removedIds },\n { log, err, rm, writeLog },\n );\n\n // dry-run 과 같은 인자를 넘겨야 미리보기가 실행 결과와 맞는다.\n for (const line of advisoryLines(plan, projectDir, rootFiles)) {\n log(line);\n }\n\n const outcome = summarize({\n succeeded,\n failed,\n logWriteFailed,\n // 제거된 것도 없고 templates 도 안 지웠는데, 사용자에게 줄 방법조차 없을 때만 실패다.\n // ① `--only` = \"이걸 빼라\"는 특정 요청 — 하나도 못 뺐으면 요청 불이행 (C1).\n // ② 자동 경로가 없는 자산이 남았으면 — 안내할 명령조차 없다 (R1).\n // ③ 반면 global 자산만 남은 경우는 **정확한 수기 명령을 출력했으므로** 성공이다.\n // D16 설계대로의 정상이고, 여기까지 묶으면 global scope 사용자는 exit 0 을 영원히\n // 못 받는다 (로그가 지워져 재시도하면 더 나빠진다).\n nothingDone: succeeded === 0 && keepTemplates && (logSurvives || plan.noReversePath.length > 0),\n });\n log(\"\");\n log(outcome.line);\n exit(outcome.code);\n}\n\n/**\n * 종료 보고 — **한 일이 없으면 성공이라 하지 않는다.** 이전 판본은 reverse step 이 0개일 때\n * `0 === 0` 이 성공 판정을 통과해 `✓ uninstall complete` + exit 0 을 찍었다 (SOD CRITICAL).\n */\nfunction summarize(r: {\n succeeded: number;\n failed: number;\n nothingDone: boolean;\n logWriteFailed: boolean;\n}): { line: string; code: number } {\n if (r.failed > 0)\n return {\n line: c.yellow(`uninstall finished with ${r.failed} skip(s) (${r.succeeded} ok)`),\n code: 1,\n };\n if (r.nothingDone)\n return {\n line: c.yellow(\"아무것도 자동 제거되지 않았다 — 위 안내를 따라 직접 처리해야 한다\"),\n code: 1,\n };\n if (r.logWriteFailed)\n return {\n line: c.yellow(\"자산은 제거됐으나 install log 를 갱신하지 못했다 (기록이 실제와 다르다)\"),\n code: 1,\n };\n return { line: status.success(c.green(`uninstall complete (${r.succeeded} asset(s))`)), code: 0 };\n}\n\nfunction headerLines(\n installLog: InstallLog,\n selectedIds: string[] | null,\n targetCount: number,\n): string[] {\n return [\n \"\",\n c.bold(\"uzys-agent-harness · uninstall\"),\n \"\",\n c.dim(` installed: ${installLog.installedAt}`),\n c.dim(` scope: ${installLog.scope}`),\n c.dim(\n selectedIds\n ? ` assets: ${targetCount} selected of ${installLog.assets.length} (--only)`\n : ` assets: ${installLog.assets.length}`,\n ),\n \"\",\n ];\n}\n\nfunction executeReverse(\n plan: ReversePlan,\n log: (msg: string) => void,\n logSurvives: boolean,\n): { succeeded: number; failed: number; removedIds: string[] } {\n let succeeded = 0;\n let failed = 0;\n const removedIds: string[] = [];\n for (const step of plan.reverseSteps) {\n const result = step.execute();\n if (result.ok) {\n log(` ${status.success(step.label)}`);\n removedIds.push(step.assetId);\n succeeded++;\n } else {\n log(` ${c.yellow(\"⊘\")} ${step.label} (${result.message ?? \"failed\"})`);\n failed++;\n }\n }\n // 자동 되돌리기 경로가 없는 자산은 **말한다.** 조용히 넘기면 `uninstall complete` 가\n // 아무것도 안 한 실행에 붙어 거짓 보고가 된다 (no-false-ship).\n // \"기록 유지\"는 **로그가 남을 때만** 참이다. `--keep-templates` 는 `.claude/` 를 남기면서도\n // 로그는 지우므로(settleLog), templates 보존 여부로 판단하면 지워질 기록을 유지한다고 말한다.\n const tail = logSurvives ? \"자동 되돌리기 경로 없음, 기록 유지\" : \"자동 되돌리기 경로 없음\";\n for (const asset of plan.noReversePath) {\n log(` ${c.yellow(\"⊘\")} ${asset.id} (${asset.method}) — ${tail}`);\n }\n return { succeeded, failed, removedIds };\n}\n\n/** 제거 후 로그 처리. @returns 재기록에 실패했는가 (실패해도 uninstall 을 죽이지 않는다). */\nfunction settleLog(\n ctx: {\n installLog: InstallLog;\n projectDir: string;\n selectedIds: string[] | null;\n removedIds: string[];\n },\n io: {\n log: (msg: string) => void;\n err: (msg: string) => void;\n rm: (path: string) => void;\n writeLog: (projectDir: string, log: InstallLog) => void;\n },\n): boolean {\n const { installLog, projectDir, selectedIds, removedIds } = ctx;\n if (selectedIds) {\n // v26.123.0 (F-1c) — 로그를 지우는 게 아니라 **되돌린 것만 빼고 다시 쓴다**.\n // 실패한 항목은 남긴다 — 실제로 안 지워진 걸 기록에서 지우면 그게 곧 거짓 기록이다.\n const remaining = installLog.assets.filter((a) => !removedIds.includes(a.id));\n try {\n io.writeLog(projectDir, { ...installLog, assets: remaining });\n io.log(` ${status.success(`install log updated (${remaining.length} asset(s) remain)`)}`);\n } catch (e) {\n // 되돌리기는 이미 끝난 뒤다 — 스택트레이스로 죽으면 무엇이 지워졌는지도 사라진다.\n io.err(status.failure(c.red(`ERROR: install log 갱신 실패 — ${installLogPath(projectDir)}`)));\n io.err(c.dim(` ${e instanceof Error ? e.message : String(e)}`));\n io.err(c.dim(` 실제로 제거된 자산: ${removedIds.join(\", \") || \"(없음)\"}`));\n return true;\n }\n } else {\n // v26.135.0 (#253) — 로그가 `.claude/` 밖으로 나왔다. 예전엔 templates 제거가 `.claude/` 를\n // 통째로 지우며 로그도 딸려 갔지만 이제 그 경로가 없다 — **templates 보존 여부와 무관하게**\n // 여기서 지운다. 디렉터리째 지우는 이유: `.uzys-agent-harness/` 는 하네스 전용이라\n // 파일만 지우면 빈 디렉터리가 남고, 그러면 \"전부 지웠다\"가 거짓이 된다.\n io.rm(join(projectDir, INSTALL_LOG_DIR));\n // v26.134.1 이하로 설치한 프로젝트의 잔재. keepTemplates 면 `.claude/` 가 살아남으므로\n // 여기서 안 지우면 다음 실행이 그 구 파일을 읽어 **이미 지운 자산을 다시 보고**한다.\n io.rm(legacyInstallLogPath(projectDir));\n io.log(` ${status.success(\"install log removed\")}`);\n }\n return false;\n}\n\n/** dry-run 미리보기 — 실행 경로와 같은 판정을 쓴다 (미리보기가 실제와 어긋나면 미리보기가 아니다). */\nfunction dryRunLines(\n plan: ReversePlan,\n installLog: InstallLog,\n projectDir: string,\n keepTemplates: boolean,\n rootFiles: ReadonlyArray<InstallLogRootFile>,\n): string[] {\n const lines = [c.yellow(\"[DRY RUN] reverse list (실제 변경 없음):\"), \"\"];\n if (plan.reverseSteps.length === 0) {\n lines.push(c.dim(\" (no project-scope assets to reverse)\"));\n }\n lines.push(...plan.reverseSteps.map((s) => ` ○ ${s.label}`));\n lines.push(\n ...plan.noReversePath.map((a) =>\n c.dim(` ⊘ ${a.id} (${a.method}) — 자동 되돌리기 경로 없음, 기록 유지`),\n ),\n );\n if (!keepTemplates) {\n lines.push(` ○ remove templates: ${formatTemplateList(installLog)}`);\n const rootMd = installLog.templates.rootClaudeMd;\n if (rootMd) {\n lines.push(\n rootClaudeMdModified(installLog, projectDir)\n ? ` ○ keep ${rootMd.path} (modified since install — preserved)`\n : ` ○ remove ${rootMd.path}`,\n );\n }\n if (hasRootImport(projectDir)) {\n lines.push(\" ○ strip harness @import from CLAUDE.md (본문 보존)\");\n }\n lines.push(...previewExternalLines(installLog, projectDir));\n }\n lines.push(...advisoryLines(plan, projectDir, rootFiles), \"\");\n return lines;\n}\n\n/** global(D16) + 루트 파일 안내. dry-run 과 실행 경로가 같은 함수를 쓴다. */\nfunction advisoryLines(\n plan: ReversePlan,\n projectDir: string,\n rootFiles: ReadonlyArray<InstallLogRootFile>,\n): string[] {\n const lines: string[] = [];\n if (plan.globalAdvisories.length > 0) {\n lines.push(\n \"\",\n c.yellow(\n `[GLOBAL] ${plan.globalAdvisories.length} asset(s) at scope=global — manual removal required (D16):`,\n ),\n );\n for (const adv of plan.globalAdvisories) {\n lines.push(c.dim(` · ${adv.asset.id} (${adv.asset.method})`), c.dim(` ${adv.command}`));\n }\n }\n // templatesKept 와 무관하다 — `.claude/` 를 통째로 지우는 경로야말로 밖에 남는 것을\n // 사용자가 존재조차 모르게 되는 경우다. 그게 F-1f 가 잡는 구멍이다.\n lines.push(...rootFileAdvisoryLines(rootFiles, projectDir));\n return lines;\n}\n\n/**\n * v26.124.0 (F-1f) — `.claude/` 밖에 남는 것 안내. **지우지 않는다**:\n * `.mcp.json`/`.gitignore` 는 사용자 내용이 섞이고, `.github/workflows/` 는 설치 후 사용자\n * 소유물이다 (ci-scaffold.ts 안전 계약 2). 기계적 되돌리기는 손실 위험이라 안내로 넘긴다.\n *\n * 규율: **예측이 아니라 현재 파일 상태를 읽어** 실재하는 것만 낸다.\n */\nfunction rootFileAdvisoryLines(\n rootFiles: ReadonlyArray<InstallLogRootFile>,\n projectDir: string,\n): string[] {\n const present = rootFiles.filter((f) => existsSync(join(projectDir, f.path)));\n if (present.length === 0) return [];\n return [\n \"\",\n c.yellow(\"[ROOT] `.claude/` 밖에 남는 것 (자동으로 지우지 않는다):\"),\n ...present.flatMap((f) => [\n c.dim(\n ` · ${f.path} — ${\n f.change === \"created\"\n ? \"하네스가 생성 (수정한 적 없으면 삭제해도 안전)\"\n : \"기존 사용자 파일에 병합 (직접 확인 필요)\"\n }`,\n ),\n c.dim(` ${f.notes.join(\" / \")}`),\n ]),\n ];\n}\n\ninterface ReversePlan {\n reverseSteps: ReverseStep[];\n globalAdvisories: GlobalAdvisory[];\n /**\n * 자동 되돌리기 경로가 없는 자산 (npx-run / shell-script / internal). 전량 uninstall 에선\n * `.claude/` 통째 제거가 덮지만, `--only` 에선 **아무 일도 안 일어난다** — 그래서 따로 센다.\n */\n noReversePath: InstallLogAsset[];\n}\n\nfunction planReverse(\n assets: ReadonlyArray<InstallLogAsset>,\n spawn: (cmd: string, args: ReadonlyArray<string>) => SpawnSyncReturns<string>,\n): ReversePlan {\n const reverseSteps: ReverseStep[] = [];\n const globalAdvisories: GlobalAdvisory[] = [];\n const noReversePath: InstallLogAsset[] = [];\n\n for (const asset of assets) {\n if (asset.scope === \"global\") {\n globalAdvisories.push({ asset, command: buildGlobalAdvisoryCmd(asset) });\n continue;\n }\n const step = buildProjectReverseStep(asset, spawn);\n if (step) reverseSteps.push(step);\n else noReversePath.push(asset);\n }\n\n return { reverseSteps, globalAdvisories, noReversePath };\n}\n\nfunction buildProjectReverseStep(\n asset: InstallLogAsset,\n spawn: (cmd: string, args: ReadonlyArray<string>) => SpawnSyncReturns<string>,\n): ReverseStep | null {\n switch (asset.method) {\n case \"plugin\": {\n const pluginId = asset.detail.pluginId ?? asset.id;\n return {\n assetId: asset.id,\n label: `claude plugin uninstall --scope project ${pluginId}`,\n execute: () => {\n const r = spawn(\"claude\", [\"plugin\", \"uninstall\", \"--scope\", \"project\", pluginId]);\n return r.status === 0 ? { ok: true } : { ok: false, message: (r.stderr || \"\").trim() };\n },\n };\n }\n case \"skill\": {\n // skills CLI default 가 project — `skills remove <source>` (no -g).\n // 일부 source 는 폴더 경로/직접 id — npx skills remove 가 처리.\n const source = asset.detail.source ?? asset.id;\n return {\n assetId: asset.id,\n label: `npx skills remove ${source}`,\n execute: () => {\n const r = spawn(\"npx\", [skillsCliSpec(), \"remove\", source, \"--yes\"]);\n return r.status === 0 ? { ok: true } : { ok: false, message: (r.stderr || \"\").trim() };\n },\n };\n }\n case \"npm\": {\n const pkg = asset.detail.pkg ?? asset.id;\n return {\n assetId: asset.id,\n label: `npm uninstall --save-dev ${pkg}`,\n execute: () => {\n const r = spawn(\"npm\", [\"uninstall\", \"--save-dev\", pkg]);\n return r.status === 0 ? { ok: true } : { ok: false, message: (r.stderr || \"\").trim() };\n },\n };\n }\n case \"npx-run\":\n // fire-and-forget — reverse 없음 (예: GSD orchestrator).\n return null;\n case \"shell-script\":\n // 로컬 script 호출 — 일반 reverse 없음 (script 별 별도 cleanup 필요).\n return null;\n case \"internal\":\n // v26.81.0 (ADR-022) — 내부 템플릿 — removeTemplates 가 .claude/ 전체로 처리.\n return null;\n }\n}\n\n/**\n * 로그에 없는 `--only` id — 오타로 엉뚱한 자산이 남지 않도록 **아무것도 실행하기 전에** 본다\n * (Pre-flight: 전제조건 미충족 시 차단, 부분 작업 없음).\n */\nfunction unknownIds(installLog: InstallLog, selectedIds: ReadonlyArray<string>): string[] {\n const known = new Set(installLog.assets.map((a) => a.id));\n return selectedIds.filter((id) => !known.has(id));\n}\n\n/** `--only <a,b>` → [\"a\",\"b\"]. 미지정이면 null (= 전량 제거, 기존 동작). */\nfunction parseOnly(only: string | undefined): string[] | null {\n if (!only) return null;\n const ids = only\n .split(\",\")\n .map((s) => s.trim())\n .filter((s) => s.length > 0);\n return ids.length > 0 ? ids : null;\n}\n\n// 2026-08-02 정비 (ADR-060) — [MANUAL] 안내(`manualAdvisoryLines`)는 karpathy-coder 훅\n// 하나만을 위한 것이었고 그 자산과 함께 삭제됐다. `.claude/` 밖에 남는 것은 아래\n// `rootFileAdvisoryLines` 가 계속 안내한다.\n\nfunction buildGlobalAdvisoryCmd(asset: InstallLogAsset): string {\n switch (asset.method) {\n case \"plugin\": {\n const pid = asset.detail.pluginId ?? asset.id;\n return `claude plugin uninstall --scope user ${pid}`;\n }\n case \"skill\": {\n const s = asset.detail.source ?? asset.id;\n return `npx skills remove -g ${s}`;\n }\n case \"npm\": {\n const pkg = asset.detail.pkg ?? asset.id;\n return `npm uninstall -g ${pkg}`;\n }\n case \"npx-run\":\n case \"shell-script\":\n case \"internal\":\n return \"(no standard reverse — manual)\";\n }\n}\n\nfunction removeTemplates(\n log: InstallLog,\n projectDir: string,\n rm: (path: string) => void,\n): { rootClaudeMdKept: boolean; importStripped: boolean; external: ExternalRemoval } {\n rm(join(projectDir, log.templates.claudeDir));\n if (log.templates.codexDir) rm(join(projectDir, log.templates.codexDir));\n if (log.templates.opencodeDir) rm(join(projectDir, log.templates.opencodeDir));\n const external = removeExternalFiles(log, projectDir, rm);\n // 루트 `CLAUDE.md` 는 **사용자 소유**다 (P5 · ADR-060) — 지우지 않고 하네스가 넣은 마커\n // import 블록만 도로 걷어낸다. 안 걷으면 앵커 파일을 지운 뒤 없는 파일을 가리키는 import 가\n // 남아 매 세션 끊긴 참조가 로드된다.\n const importStripped = stripRootImport(projectDir);\n // 하네스 앵커 파일 — install 원본 그대로일 때만 삭제. 사용자가 수정했으면 보존.\n const rootMd = log.templates.rootClaudeMd;\n if (rootMd) {\n if (rootClaudeMdModified(log, projectDir))\n return { rootClaudeMdKept: true, importStripped, external };\n rm(join(projectDir, rootMd.path));\n }\n return { rootClaudeMdKept: false, importStripped, external };\n}\n\n/** `removeExternalFiles` 의 결과 — 화면이 지운 것과 남긴 것을 나눠 말할 수 있어야 한다. */\ninterface ExternalRemoval {\n removed: string[];\n /** 설치 이후 내용이 바뀌어 남긴 것 — 사용자 편집분이라 우리가 소유를 주장하지 않는다. */\n kept: string[];\n}\n\n/**\n * 외부 CLI transform 산출물 회수 (#350).\n *\n * **디렉터리를 통째로 지우지 않는다.** `templates` 는 `.claude/`·`.codex/`·`.opencode/` 세\n * 디렉터리만 알고 있어서, codex·opencode·antigravity 가 함께 쓰는 `.agents/` 는 uninstall\n * 이후에도 통째로 남았다(실측 2026-08-29 · v26.148.1: codex 는 `.agents/skills`, antigravity 는\n * `.agents/rules` + `.agents/skills` 가 잔존하고 화면에는 한 줄도 안 뜬다).\n *\n * 그렇다고 `.agents/` 를 회수 목록에 더하면 안 된다 — **그 자리는 `npx skills` 와 공유**하고,\n * 그쪽은 `.claude/skills/<id>` 를 `.agents/` 로의 심링크로 만들어 **본문을 거기 둔다**\n * (`external-installer.ts` 의 실측 주석). 통째 삭제는 남의 도구가 깐 스킬 본문을 지운다.\n * `.claude/` 통짜 삭제가 안전한 것은 그 안의 링크를 지워도 본문이 `.agents/` 에 남기 때문이고,\n * 반대 방향은 성립하지 않는다.\n *\n * 그래서 **우리가 쓴 파일만** 지운다. 무엇을 썼는지는 이미 정확히 기록돼 있다 —\n * `externalFiles`(ADR-048)는 transform 이 쓴 파일의 projectDir 상대경로와 sha256 을 담는다.\n * 열거 사본을 새로 만들지 않아도 되고, 자산이 늘어도 기록이 따라온다.\n *\n * 설치 이후 내용이 바뀐 파일은 **남긴다** — 루트 앵커에 이미 쓰는 규칙과 같다(사용자 편집분).\n */\nfunction removeExternalFiles(\n log: InstallLog,\n projectDir: string,\n rm: (path: string) => void,\n): ExternalRemoval {\n const removed: string[] = [];\n const kept: string[] = [];\n for (const { path, sha256 } of log.externalFiles ?? []) {\n const abs = join(projectDir, path);\n // `.claude/`·`.codex/`·`.opencode/` 아래 것은 위에서 이미 사라졌다 — 부재는 정상이다.\n if (!existsSync(abs)) continue;\n // 일반 파일만 회수한다. `.agents/skills/<id>` 는 `npx skills add` 가 **심링크로** 깔아 두는\n // 자리이고(#343 실사용자 신고로 관측), 그 링크는 우리가 만든 것이 아니다. 안 걸러 두면\n // 내용이 우연히 같을 때 남의 설치 포인터를 지우고, 다를 때는 \"네가 고쳤다\"고 잘못 말한다.\n if (!lstatSync(abs).isFile()) continue;\n if (hashContent(readFileSync(abs, \"utf8\")) !== sha256) {\n kept.push(path);\n continue;\n }\n rm(abs);\n removed.push(path);\n }\n for (const path of removed) pruneEmptyDirsUpward(projectDir, dirname(join(projectDir, path)));\n return { removed, kept };\n}\n\n/**\n * 파일을 지운 뒤 빈 껍데기로 남은 상위 디렉터리를 `projectDir` 직전까지 걷어낸다.\n *\n * 이게 없으면 `.agents/skills/<id>/` 같은 빈 디렉터리 트리가 남아 \"지웠다\"는 보고와 디스크가\n * 다른 말을 한다. 비어 있지 않으면 즉시 멈춘다 — 남의 파일이 하나라도 있으면 그 자리는\n * 우리 것이 아니다.\n */\nfunction pruneEmptyDirsUpward(projectDir: string, startDir: string): void {\n let cursor = startDir;\n while (true) {\n const rel = relative(projectDir, cursor);\n // projectDir 자신이거나 그 밖으로 나갔으면 멈춘다.\n if (rel === \"\" || rel.startsWith(`..${sep}`) || rel === \"..\") return;\n try {\n if (readdirSync(cursor).length > 0) return;\n rmdirSync(cursor);\n } catch {\n // 이미 없거나 지울 수 없다 — uninstall 을 죽일 이유가 아니다.\n return;\n }\n cursor = dirname(cursor);\n }\n}\n\n/**\n * 루트 `CLAUDE.md` 에서 하네스 import 블록만 제거. 마커가 없으면 아무것도 쓰지 않는다.\n *\n * 실패해도 uninstall 을 죽이지 않는다 — 여기서 throw 하면 자산은 이미 다 지운 상태에서\n * 명령이 실패로 끝나고, 사용자는 무엇이 남았는지 알 수 없게 된다 (D16 과 같은 방침).\n */\nfunction stripRootImport(projectDir: string): boolean {\n const current = readRootClaudeMd(projectDir);\n const stripped = current === null ? null : stripHarnessImport(current);\n if (stripped === null) return false;\n try {\n writeFileSync(join(projectDir, \"CLAUDE.md\"), stripped, \"utf8\");\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * dry-run 이 실행과 **같은 판정**으로 미리 보여 준다. 실행 경로가 회수를 판정하는 술어\n * (기록에 있고 · 디스크에 있고 · sha256 이 그대로)를 그대로 다시 쓴다.\n */\nfunction previewExternalLines(installLog: InstallLog, projectDir: string): string[] {\n const lines: string[] = [];\n let removable = 0;\n for (const { path, sha256 } of installLog.externalFiles ?? []) {\n const abs = join(projectDir, path);\n if (!existsSync(abs)) continue;\n if (hashContent(readFileSync(abs, \"utf8\")) !== sha256) {\n lines.push(` ○ keep ${path} (modified since install — preserved)`);\n continue;\n }\n removable += 1;\n }\n if (removable > 0) lines.unshift(` ○ remove ${removable} CLI output file(s)`);\n return lines;\n}\n\n/**\n * `.claude/`·`.codex/`·`.opencode/` 밖(주로 `.agents/`)에서 회수한 산출물 보고.\n *\n * 지운 게 없으면 아무 줄도 내지 않는다 — codex·opencode·antigravity 를 안 고른 설치가\n * 대부분이고, 거기서 \"0 files\" 를 찍으면 화면만 길어진다.\n */\nfunction externalRemovalLines(external: ExternalRemoval): string[] {\n const lines: string[] = [];\n if (external.removed.length > 0) {\n lines.push(` ${status.success(`CLI outputs removed: ${external.removed.length} file(s)`)}`);\n }\n for (const path of external.kept) {\n lines.push(\n ` ${c.yellow(\"⊘\")} ${path} kept — modified since install. Remove manually if intended.`,\n );\n }\n return lines;\n}\n\n/** 미리보기용 — 실행 경로와 **같은 술어**를 쓴다 (미리보기가 실제와 어긋나면 미리보기가 아니다). */\nfunction hasRootImport(projectDir: string): boolean {\n const current = readRootClaudeMd(projectDir);\n return current !== null && stripHarnessImport(current) !== null;\n}\n\nfunction readRootClaudeMd(projectDir: string): string | null {\n const path = join(projectDir, \"CLAUDE.md\");\n if (!existsSync(path)) return null;\n try {\n return readFileSync(path, \"utf8\");\n } catch {\n return null;\n }\n}\n\n/** 하네스 앵커 파일이 install 이후 수정됐는지. log 에 없거나 파일 부재 시 false (= 삭제 대상). */\nfunction rootClaudeMdModified(log: InstallLog, projectDir: string): boolean {\n const rootMd = log.templates.rootClaudeMd;\n if (!rootMd) return false;\n const path = join(projectDir, rootMd.path);\n if (!existsSync(path)) return false;\n return hashContent(readFileSync(path, \"utf8\")) !== rootMd.sha256;\n}\n\nfunction formatTemplateList(log: InstallLog): string {\n const items: string[] = [log.templates.claudeDir];\n if (log.templates.codexDir) items.push(log.templates.codexDir);\n if (log.templates.opencodeDir) items.push(log.templates.opencodeDir);\n return items.join(\", \");\n}\n\n/* v8 ignore start — thin dep-inject defaults. tests 는 항상 mock 주입. */\nfunction defaultSpawn(cmd: string, args: ReadonlyArray<string>): SpawnSyncReturns<string> {\n return spawnSync(cmd, [...args], { encoding: \"utf8\", stdio: \"pipe\", timeout: 120_000 });\n}\n\nfunction defaultRm(path: string): void {\n if (existsSync(path)) {\n rmSync(path, { recursive: true, force: true });\n }\n}\n/* v8 ignore stop */\n\nexport function registerUninstallCommand(cli: import(\"../cli.js\").Cli): void {\n cli\n .command(\"uninstall\", \"Uninstall harness assets (log-based reverse)\")\n .option(\"--project-dir <path>\", \"[Project] Target project directory\", {\n default: process.cwd(),\n })\n .option(\"--dry-run\", \"[Mode] List reverse steps without executing\")\n .option(\n \"--keep-templates\",\n \"[Mode] Keep `.claude/`, `.codex/`, `.opencode/` templates (remove only external assets)\",\n )\n .option(\n \"--only <ids>\",\n \"[Scope] Remove only these assets (comma-separated ids from `agent-harness list`). Templates untouched\",\n )\n .option(\"--yes\", \"[Mode] Skip the interactive picker and remove everything (non-interactive)\")\n /* v8 ignore next 3 — cac action callback. 분기 판정은 shouldRunInteractive 가 갖고 tests 로 검증. */\n .action(async (options: UninstallOptions) => {\n await dispatchUninstall(options);\n });\n}\n\n/**\n * v26.125.0 — 대화형 선택 화면으로 들어갈 것인가.\n *\n * 들어가지 **않는** 조건은 전부 \"사용자가 이미 무엇을 원하는지 말한 경우\"다:\n * `--only` = 뺄 대상을 지정함 · `--dry-run` = 미리보기 · `--yes` = 묻지 말라는 명시.\n * 그 외 TTY 라면 화면으로 들어간다 — 플래그 없는 `uninstall` 이 즉시 전량 삭제하던 것이\n * 이 명령에서 가장 위험한 기본값이었다. TTY 가 아니면(CI·파이프) 기존 동작 그대로다.\n */\nexport function shouldRunInteractive(options: UninstallOptions, isTty: boolean): boolean {\n if (!isTty) return false;\n if (options.yes || options.dryRun) return false;\n return options.only === undefined;\n}\n\n/* v8 ignore start — 얇은 배선. 판정은 shouldRunInteractive, 선택은 uninstall-interactive, 실행은 uninstallAction 이 각각 tests 로 검증. */\nasync function dispatchUninstall(options: UninstallOptions): Promise<void> {\n if (!shouldRunInteractive(options, Boolean(process.stdin.isTTY))) {\n uninstallAction(options);\n return;\n }\n const projectDir = resolve(options.projectDir ?? process.cwd());\n const picked = await runInteractiveUninstall(projectDir);\n if (!picked.ok || !picked.options) {\n if (picked.reason === \"no-log\") {\n console.error(\n status.failure(c.red(`ERROR: install log not found at ${installLogPath(projectDir)}`)),\n );\n console.error(c.dim(\" Nothing installed here by agent-harness.\"));\n process.exit(1);\n }\n // no-tty 는 위 분기에서 이미 걸러졌고, 나머지(cancelled/nothing-selected)는 정상 종료다.\n return;\n }\n uninstallAction(picked.options);\n}\n/* v8 ignore stop */\n","/**\n * Interactive uninstall — v26.125.0 (사용자 요청 2026-07-19).\n *\n * `agent-harness uninstall` 을 TTY 에서 실행하면 **무엇을 뺄지 고르는 화면**으로 들어간다.\n *\n * 왜 install 위저드가 아니라 별도 명령인가 (사용자 결정): install 화면의 체크 해제는\n * \"이번에 설치하지 않음\"이지 제거가 아니다. 설치 화면 안에서 삭제가 일어나면 실수 한 번이\n * 되돌릴 수 없는 삭제가 되고, \"install 은 지우지 않는다\"는 불변식도 깨진다. 그래서 제거는\n * 이 명령으로만 들어온다.\n *\n * 본 모듈은 **선택만** 한다 — 실제 되돌리기는 기존 `uninstallAction` 이 그대로 수행한다.\n * 두 모드가 각각 기존 경로 하나에 1:1 로 대응하므로 새로운 파괴적 조합이 생기지 않는다:\n * 선택 제거 → `--only <ids>` (templates 유지, 로그는 남은 자산으로 재기록)\n * 전량 제거 → 플래그 없음 (templates 포함)\n */\n\nimport { cancel, confirm, intro, isCancel, multiselect, outro, select } from \"@clack/prompts\";\nimport { type InstallLog, type InstallLogAsset, readInstallLog } from \"./install-log.js\";\n\nexport interface RemovableRow {\n value: string;\n label: string;\n hint: string;\n}\n\nexport type UninstallMode = \"selected\" | \"all\";\n\nexport interface UninstallPrompts {\n intro: (msg: string) => void;\n outro: (msg: string) => void;\n cancel: (msg: string) => void;\n /** null = ESC/취소 */\n selectMode: (rowCount: number) => Promise<UninstallMode | null>;\n /** null = ESC/취소. 빈 배열 = 아무것도 안 고름 */\n selectAssets: (rows: ReadonlyArray<RemovableRow>) => Promise<ReadonlyArray<string> | null>;\n confirm: (summary: string) => Promise<boolean | null>;\n}\n\nexport interface InteractiveUninstallDeps {\n prompts?: UninstallPrompts;\n isTty?: () => boolean;\n readLog?: (projectDir: string) => InstallLog | null;\n}\n\nexport interface InteractiveUninstallResult {\n ok: boolean;\n /** ok=true 일 때 `uninstallAction` 에 그대로 넘길 옵션. */\n options?: { projectDir: string; only?: string };\n reason?: \"no-tty\" | \"no-log\" | \"cancelled\" | \"nothing-selected\";\n message?: string;\n}\n\n/**\n * 로그의 자산 → 선택 화면 행.\n *\n * hint 에 **고르면 실제로 무슨 일이 일어나는지**를 적는다. global(D16) 과 자동 되돌리기 경로가\n * 없는 method 는 골라도 자동 삭제되지 않으므로, 고르기 전에 말해야 한다 — 안 그러면 사용자가\n * 체크하고 Enter 를 눌렀는데 아무 일도 안 일어나는 것을 결과 화면에서야 알게 된다.\n */\nexport function buildRemovableRows(\n assets: ReadonlyArray<InstallLogAsset>,\n): ReadonlyArray<RemovableRow> {\n return assets.map((a) => ({\n value: a.id,\n label: `${a.id} [${a.method}]${a.version ? ` v${a.version}` : \"\"}`,\n hint: hintFor(a),\n }));\n}\n\nfunction hintFor(asset: InstallLogAsset): string {\n if (asset.scope === \"global\") return \"global scope — 자동 삭제 안 함, 수기 제거 명령을 출력한다\";\n switch (asset.method) {\n case \"plugin\":\n return \"claude plugin uninstall --scope project\";\n case \"skill\":\n return \"npx skills remove\";\n case \"npm\":\n return \"npm uninstall --save-dev\";\n case \"npx-run\":\n case \"shell-script\":\n case \"internal\":\n return \"자동 되돌리기 경로 없음 — 전량 제거(`.claude/` 삭제)로만 사라진다\";\n }\n}\n\nexport async function runInteractiveUninstall(\n projectDir: string,\n deps: InteractiveUninstallDeps = {},\n): Promise<InteractiveUninstallResult> {\n const prompts = deps.prompts ?? defaultUninstallPrompts();\n const isTty = deps.isTty ?? (() => Boolean(process.stdin.isTTY));\n const readLog = deps.readLog ?? readInstallLog;\n\n // CI/파이프에서 프롬프트가 뜨면 그대로 멈춘다 — install 위저드와 같은 게이트.\n if (!isTty()) return { ok: false, reason: \"no-tty\" };\n\n const log = readLog(projectDir);\n if (!log) return { ok: false, reason: \"no-log\" };\n\n prompts.intro(\"uzys-agent-harness · uninstall\");\n const rows = buildRemovableRows(log.assets);\n\n const mode = await prompts.selectMode(rows.length);\n if (mode === null) {\n prompts.cancel(\"Cancelled.\");\n return { ok: false, reason: \"cancelled\" };\n }\n\n if (mode === \"all\") {\n const ok = await prompts.confirm(\n [\n \"전량 제거 — 되돌릴 수 없다:\",\n ` · 자산 ${log.assets.length}개 (자동 경로가 있는 것만 실제 제거)`,\n \" · templates 삭제: `.claude/` 등 (설치 기록도 함께 사라진다)\",\n \" · `.claude/` 밖 파일(`.mcp.json` 등)은 삭제하지 않고 안내만 한다\",\n ].join(\"\\n\"),\n );\n if (!ok) {\n prompts.cancel(\"Cancelled.\");\n return { ok: false, reason: \"cancelled\" };\n }\n return { ok: true, options: { projectDir } };\n }\n\n const picked = await prompts.selectAssets(rows);\n if (picked === null) {\n prompts.cancel(\"Cancelled.\");\n return { ok: false, reason: \"cancelled\" };\n }\n // 빈 선택을 그대로 흘리면 `--only` 가 비어 **전량 제거로 떨어진다**. 하나만 빼려던 사용자가\n // templates 까지 잃는 경로라 여기서 끊는다 (uninstall.ts 의 `--only ,` 방어와 같은 이유).\n if (picked.length === 0) {\n prompts.outro(\"아무것도 선택하지 않았다 — 변경 없음.\");\n return { ok: false, reason: \"nothing-selected\" };\n }\n\n const ok = await prompts.confirm(\n [\n `선택 제거 (${picked.length}개):`,\n ...picked.map((id) => ` · ${id}`),\n \"\",\n \"templates 는 그대로 둔다.\",\n ].join(\"\\n\"),\n );\n if (!ok) {\n prompts.cancel(\"Cancelled.\");\n return { ok: false, reason: \"cancelled\" };\n }\n return { ok: true, options: { projectDir, only: picked.join(\",\") } };\n}\n\n/* v8 ignore start — @clack/prompts 어댑터. 선택 로직은 위 순수 함수들이 갖고 tests 로 검증. */\nfunction defaultUninstallPrompts(): UninstallPrompts {\n return {\n intro: (m) => intro(m),\n outro: (m) => outro(m),\n cancel: (m) => cancel(m),\n selectMode: async (rowCount) => {\n const r = await select({\n message: `무엇을 제거할까? (설치된 자산 ${rowCount}개)`,\n options: [\n {\n value: \"selected\",\n label: \"항목 선택해서 제거\",\n hint: \"templates(`.claude/` 등)는 그대로 둔다\",\n },\n {\n value: \"all\",\n label: \"전부 제거\",\n hint: \"자산 + templates. 설치 기록도 사라진다\",\n },\n ],\n });\n return isCancel(r) ? null : (r as UninstallMode);\n },\n selectAssets: async (rows) => {\n if (rows.length === 0) return [];\n const r = await multiselect({\n message: \"제거할 항목 (Space 토글 · Enter 확정 · ESC 취소)\",\n options: rows.map((x) => ({ value: x.value, label: x.label, hint: x.hint })),\n required: false,\n });\n return isCancel(r) ? null : (r as string[]);\n },\n confirm: async (summary) => {\n const r = await confirm({ message: `${summary}\\n\\n진행할까?`, initialValue: false });\n return isCancel(r) ? null : r;\n },\n };\n}\n/* v8 ignore stop */\n",null,"/* IMPORT */\nimport fastStringTruncatedWidth from 'fast-string-truncated-width';\n/* HELPERS */\nconst NO_TRUNCATION = {\n limit: Infinity,\n ellipsis: '',\n ellipsisWidth: 0,\n};\n/* MAIN */\nconst fastStringWidth = (input, options = {}) => {\n return fastStringTruncatedWidth(input, NO_TRUNCATION, options).width;\n};\n/* EXPORT */\nexport default fastStringWidth;\n","/* IMPORT */\nimport { getCodePointsLength, isFullWidth, isWideNotCJKTNotEmoji } from './utils.js';\n/* HELPERS */\nconst ANSI_RE = /[\\u001b\\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]|\\u001b\\]8;[^;]*;.*?(?:\\u0007|\\u001b\\u005c)/y;\nconst CONTROL_RE = /[\\x00-\\x08\\x0A-\\x1F\\x7F-\\x9F]{1,1000}/y;\nconst CJKT_WIDE_RE = /(?:(?![\\uFF61-\\uFF9F\\uFF00-\\uFFEF])[\\p{Script=Han}\\p{Script=Hiragana}\\p{Script=Katakana}\\p{Script=Hangul}\\p{Script=Tangut}]){1,1000}/yu;\nconst TAB_RE = /\\t{1,1000}/y;\nconst EMOJI_RE = /[\\u{1F1E6}-\\u{1F1FF}]{2}|\\u{1F3F4}[\\u{E0061}-\\u{E007A}]{2}[\\u{E0030}-\\u{E0039}\\u{E0061}-\\u{E007A}]{1,3}\\u{E007F}|(?:\\p{Emoji}\\uFE0F\\u20E3?|\\p{Emoji_Modifier_Base}\\p{Emoji_Modifier}?|\\p{Emoji_Presentation})(?:\\u200D(?:\\p{Emoji_Modifier_Base}\\p{Emoji_Modifier}?|\\p{Emoji_Presentation}|\\p{Emoji}\\uFE0F\\u20E3?))*/yu;\nconst LATIN_RE = /(?:[\\x20-\\x7E\\xA0-\\xFF](?!\\uFE0F)){1,1000}/y;\nconst MODIFIER_RE = /\\p{M}+/gu;\nconst NO_TRUNCATION = { limit: Infinity, ellipsis: '' };\n/* MAIN */\nconst getStringTruncatedWidth = (input, truncationOptions = {}, widthOptions = {}) => {\n /* CONSTANTS */\n const LIMIT = truncationOptions.limit ?? Infinity;\n const ELLIPSIS = truncationOptions.ellipsis ?? '';\n const ELLIPSIS_WIDTH = truncationOptions?.ellipsisWidth ?? (ELLIPSIS ? getStringTruncatedWidth(ELLIPSIS, NO_TRUNCATION, widthOptions).width : 0);\n const ANSI_WIDTH = 0;\n const CONTROL_WIDTH = widthOptions.controlWidth ?? 0;\n const TAB_WIDTH = widthOptions.tabWidth ?? 8;\n const EMOJI_WIDTH = widthOptions.emojiWidth ?? 2;\n const FULL_WIDTH_WIDTH = 2;\n const REGULAR_WIDTH = widthOptions.regularWidth ?? 1;\n const WIDE_WIDTH = widthOptions.wideWidth ?? FULL_WIDTH_WIDTH;\n const PARSE_BLOCKS = [\n [LATIN_RE, REGULAR_WIDTH],\n [ANSI_RE, ANSI_WIDTH],\n [CONTROL_RE, CONTROL_WIDTH],\n [TAB_RE, TAB_WIDTH],\n [EMOJI_RE, EMOJI_WIDTH],\n [CJKT_WIDE_RE, WIDE_WIDTH],\n ];\n /* STATE */\n let indexPrev = 0;\n let index = 0;\n let length = input.length;\n let lengthExtra = 0;\n let truncationEnabled = false;\n let truncationIndex = length;\n let truncationLimit = Math.max(0, LIMIT - ELLIPSIS_WIDTH);\n let unmatchedStart = 0;\n let unmatchedEnd = 0;\n let width = 0;\n let widthExtra = 0;\n /* PARSE LOOP */\n outer: while (true) {\n /* UNMATCHED */\n if ((unmatchedEnd > unmatchedStart) || (index >= length && index > indexPrev)) {\n const unmatched = input.slice(unmatchedStart, unmatchedEnd) || input.slice(indexPrev, index);\n lengthExtra = 0;\n for (const char of unmatched.replaceAll(MODIFIER_RE, '')) {\n const codePoint = char.codePointAt(0) || 0;\n if (isFullWidth(codePoint)) {\n widthExtra = FULL_WIDTH_WIDTH;\n }\n else if (isWideNotCJKTNotEmoji(codePoint)) {\n widthExtra = WIDE_WIDTH;\n }\n else {\n widthExtra = REGULAR_WIDTH;\n }\n if ((width + widthExtra) > truncationLimit) {\n truncationIndex = Math.min(truncationIndex, Math.max(unmatchedStart, indexPrev) + lengthExtra);\n }\n if ((width + widthExtra) > LIMIT) {\n truncationEnabled = true;\n break outer;\n }\n lengthExtra += char.length;\n width += widthExtra;\n }\n unmatchedStart = unmatchedEnd = 0;\n }\n /* EXITING */\n if (index >= length) {\n break outer;\n }\n /* PARSE BLOCKS */\n for (let i = 0, l = PARSE_BLOCKS.length; i < l; i++) {\n const [BLOCK_RE, BLOCK_WIDTH] = PARSE_BLOCKS[i];\n BLOCK_RE.lastIndex = index;\n if (BLOCK_RE.test(input)) {\n lengthExtra = BLOCK_RE === CJKT_WIDE_RE ? getCodePointsLength(input.slice(index, BLOCK_RE.lastIndex)) : BLOCK_RE === EMOJI_RE ? 1 : BLOCK_RE.lastIndex - index;\n widthExtra = lengthExtra * BLOCK_WIDTH;\n if ((width + widthExtra) > truncationLimit) {\n truncationIndex = Math.min(truncationIndex, index + Math.floor((truncationLimit - width) / BLOCK_WIDTH));\n }\n if ((width + widthExtra) > LIMIT) {\n truncationEnabled = true;\n break outer;\n }\n width += widthExtra;\n unmatchedStart = indexPrev;\n unmatchedEnd = index;\n index = indexPrev = BLOCK_RE.lastIndex;\n continue outer;\n }\n }\n /* UNMATCHED INDEX */\n index += 1;\n }\n /* RETURN */\n return {\n width: truncationEnabled ? truncationLimit : width,\n index: truncationEnabled ? truncationIndex : length,\n truncated: truncationEnabled,\n ellipsed: truncationEnabled && LIMIT >= ELLIPSIS_WIDTH\n };\n};\n/* EXPORT */\nexport default getStringTruncatedWidth;\n","/* MAIN */\nconst getCodePointsLength = (() => {\n const SURROGATE_PAIR_RE = /[\\uD800-\\uDBFF][\\uDC00-\\uDFFF]/g;\n return (input) => {\n let surrogatePairsNr = 0;\n SURROGATE_PAIR_RE.lastIndex = 0;\n while (SURROGATE_PAIR_RE.test(input)) {\n surrogatePairsNr += 1;\n }\n return input.length - surrogatePairsNr;\n };\n})();\nconst isFullWidth = (x) => {\n return x === 0x3000 || x >= 0xFF01 && x <= 0xFF60 || x >= 0xFFE0 && x <= 0xFFE6;\n};\nconst isWideNotCJKTNotEmoji = (x) => {\n return x === 0x231B || x === 0x2329 || x >= 0x2FF0 && x <= 0x2FFF || x >= 0x3001 && x <= 0x303E || x >= 0x3099 && x <= 0x30FF || x >= 0x3105 && x <= 0x312F || x >= 0x3131 && x <= 0x318E || x >= 0x3190 && x <= 0x31E3 || x >= 0x31EF && x <= 0x321E || x >= 0x3220 && x <= 0x3247 || x >= 0x3250 && x <= 0x4DBF || x >= 0xFE10 && x <= 0xFE19 || x >= 0xFE30 && x <= 0xFE52 || x >= 0xFE54 && x <= 0xFE66 || x >= 0xFE68 && x <= 0xFE6B || x >= 0x1F200 && x <= 0x1F202 || x >= 0x1F210 && x <= 0x1F23B || x >= 0x1F240 && x <= 0x1F248 || x >= 0x20000 && x <= 0x2FFFD || x >= 0x30000 && x <= 0x3FFFD;\n};\n/* EXPORT */\nexport { getCodePointsLength, isFullWidth, isWideNotCJKTNotEmoji };\n","export function findCursor<T extends { disabled?: boolean }>(\n\tcursor: number,\n\tdelta: number,\n\toptions: T[]\n) {\n\tconst hasEnabledOptions = options.some((opt) => !opt.disabled);\n\tif (!hasEnabledOptions) {\n\t\treturn cursor;\n\t}\n\tconst newCursor = cursor + delta;\n\tconst maxCursor = Math.max(options.length - 1, 0);\n\tconst clampedCursor = newCursor < 0 ? maxCursor : newCursor > maxCursor ? 0 : newCursor;\n\tconst newOption = options[clampedCursor];\n\tif (newOption.disabled) {\n\t\treturn findCursor(clampedCursor, delta < 0 ? -1 : 1, options);\n\t}\n\treturn clampedCursor;\n}\n\nexport function findTextCursor(\n\tcursor: number,\n\tdeltaX: number,\n\tdeltaY: number,\n\tvalue: string\n): number {\n\tconst lines = value.split('\\n');\n\tlet cursorY = 0;\n\tlet cursorX = cursor;\n\n\tfor (const line of lines) {\n\t\tif (cursorX <= line.length) {\n\t\t\tbreak;\n\t\t}\n\t\tcursorX -= line.length + 1;\n\t\tcursorY++;\n\t}\n\n\tcursorY = Math.max(0, Math.min(lines.length - 1, cursorY + deltaY));\n\n\tcursorX = Math.min(cursorX, lines[cursorY].length) + deltaX;\n\twhile (cursorX < 0 && cursorY > 0) {\n\t\tcursorY--;\n\t\tcursorX += lines[cursorY].length + 1;\n\t}\n\twhile (cursorX > lines[cursorY].length && cursorY < lines.length - 1) {\n\t\tcursorX -= lines[cursorY].length + 1;\n\t\tcursorY++;\n\t}\n\tcursorX = Math.max(0, Math.min(lines[cursorY].length, cursorX));\n\n\tlet newCursor = 0;\n\tfor (let i = 0; i < cursorY; i++) {\n\t\tnewCursor += lines[i].length + 1;\n\t}\n\treturn newCursor + cursorX;\n}\n","const actions = ['up', 'down', 'left', 'right', 'space', 'enter', 'cancel'] as const;\nexport type Action = (typeof actions)[number];\n\nconst DEFAULT_MONTH_NAMES = [\n\t'January',\n\t'February',\n\t'March',\n\t'April',\n\t'May',\n\t'June',\n\t'July',\n\t'August',\n\t'September',\n\t'October',\n\t'November',\n\t'December',\n];\n\n/** Global settings for Clack programs, stored in memory */\ninterface InternalClackSettings {\n\tactions: Set<Action>;\n\taliases: Map<string, Action>;\n\tmessages: {\n\t\tcancel: string;\n\t\terror: string;\n\t};\n\twithGuide: boolean;\n\tdate: {\n\t\tmonthNames: string[];\n\t\tmessages: {\n\t\t\tinvalidMonth: string;\n\t\t\trequired: string;\n\t\t\tinvalidDay: (days: number, month: string) => string;\n\t\t\tafterMin: (min: Date) => string;\n\t\t\tbeforeMax: (max: Date) => string;\n\t\t};\n\t};\n}\n\nexport const settings: InternalClackSettings = {\n\tactions: new Set(actions),\n\taliases: new Map<string, Action>([\n\t\t// vim support\n\t\t['k', 'up'],\n\t\t['j', 'down'],\n\t\t['h', 'left'],\n\t\t['l', 'right'],\n\t\t['\\x03', 'cancel'],\n\t\t// opinionated defaults!\n\t\t['escape', 'cancel'],\n\t]),\n\tmessages: {\n\t\tcancel: 'Canceled',\n\t\terror: 'Something went wrong',\n\t},\n\twithGuide: true,\n\tdate: {\n\t\tmonthNames: [...DEFAULT_MONTH_NAMES],\n\t\tmessages: {\n\t\t\trequired: 'Please enter a valid date',\n\t\t\tinvalidMonth: 'There are only 12 months in a year',\n\t\t\tinvalidDay: (days, month) => `There are only ${days} days in ${month}`,\n\t\t\tafterMin: (min) => `Date must be on or after ${min.toISOString().slice(0, 10)}`,\n\t\t\tbeforeMax: (max) => `Date must be on or before ${max.toISOString().slice(0, 10)}`,\n\t\t},\n\t},\n};\n\nexport interface ClackSettings {\n\t/**\n\t * Set custom global aliases for the default actions.\n\t * This will not overwrite existing aliases, it will only add new ones!\n\t *\n\t * @param aliases - An object that maps aliases to actions\n\t * @default { k: 'up', j: 'down', h: 'left', l: 'right', '\\x03': 'cancel', 'escape': 'cancel' }\n\t */\n\taliases?: Record<string, Action>;\n\n\t/**\n\t * Custom messages for prompts\n\t */\n\tmessages?: {\n\t\t/**\n\t\t * Custom message to display when a spinner is cancelled\n\t\t * @default \"Canceled\"\n\t\t */\n\t\tcancel?: string;\n\t\t/**\n\t\t * Custom message to display when a spinner encounters an error\n\t\t * @default \"Something went wrong\"\n\t\t */\n\t\terror?: string;\n\t};\n\n\twithGuide?: boolean;\n\n\t/**\n\t * Date prompt localization\n\t */\n\tdate?: {\n\t\t/** Month names for validation messages (January, February, ...) */\n\t\tmonthNames?: string[];\n\t\tmessages?: {\n\t\t\t/** Shown when date is missing */\n\t\t\trequired?: string;\n\t\t\t/** Shown when month > 12 */\n\t\t\tinvalidMonth?: string;\n\t\t\t/** (days, monthName) => message for invalid day */\n\t\t\tinvalidDay?: (days: number, month: string) => string;\n\t\t\t/** (min) => message when date is before minDate */\n\t\t\tafterMin?: (min: Date) => string;\n\t\t\t/** (max) => message when date is after maxDate */\n\t\t\tbeforeMax?: (max: Date) => string;\n\t\t};\n\t};\n}\n\nexport function updateSettings(updates: ClackSettings) {\n\t// Handle each property in the updates\n\tif (updates.aliases !== undefined) {\n\t\tconst aliases = updates.aliases;\n\t\tfor (const alias in aliases) {\n\t\t\tif (!Object.hasOwn(aliases, alias)) continue;\n\n\t\t\tconst action = aliases[alias];\n\t\t\tif (!settings.actions.has(action)) continue;\n\n\t\t\tif (!settings.aliases.has(alias)) {\n\t\t\t\tsettings.aliases.set(alias, action);\n\t\t\t}\n\t\t}\n\t}\n\n\tif (updates.messages !== undefined) {\n\t\tconst messages = updates.messages;\n\t\tif (messages.cancel !== undefined) {\n\t\t\tsettings.messages.cancel = messages.cancel;\n\t\t}\n\t\tif (messages.error !== undefined) {\n\t\t\tsettings.messages.error = messages.error;\n\t\t}\n\t}\n\n\tif (updates.withGuide !== undefined) {\n\t\tsettings.withGuide = updates.withGuide !== false;\n\t}\n\n\tif (updates.date !== undefined) {\n\t\tconst date = updates.date;\n\t\tif (date.monthNames !== undefined) {\n\t\t\tsettings.date.monthNames = [...date.monthNames];\n\t\t}\n\t\tif (date.messages !== undefined) {\n\t\t\tif (date.messages.required !== undefined) {\n\t\t\t\tsettings.date.messages.required = date.messages.required;\n\t\t\t}\n\t\t\tif (date.messages.invalidMonth !== undefined) {\n\t\t\t\tsettings.date.messages.invalidMonth = date.messages.invalidMonth;\n\t\t\t}\n\t\t\tif (date.messages.invalidDay !== undefined) {\n\t\t\t\tsettings.date.messages.invalidDay = date.messages.invalidDay;\n\t\t\t}\n\t\t\tif (date.messages.afterMin !== undefined) {\n\t\t\t\tsettings.date.messages.afterMin = date.messages.afterMin;\n\t\t\t}\n\t\t\tif (date.messages.beforeMax !== undefined) {\n\t\t\t\tsettings.date.messages.beforeMax = date.messages.beforeMax;\n\t\t\t}\n\t\t}\n\t}\n}\n\n/**\n * Check if a key is an alias for a default action\n * @param key - The raw key which might match to an action\n * @param action - The action to match\n * @returns boolean\n */\nexport function isActionKey(key: string | Array<string | undefined>, action: Action) {\n\tif (typeof key === 'string') {\n\t\treturn settings.aliases.get(key) === action;\n\t}\n\n\tfor (const value of key) {\n\t\tif (value === undefined) continue;\n\t\tif (isActionKey(value, action)) {\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n","export function diffLines(a: string, b: string) {\n\tif (a === b) return;\n\n\tconst aLines = a.split('\\n');\n\tconst bLines = b.split('\\n');\n\tconst numLines = Math.max(aLines.length, bLines.length);\n\tconst diff: number[] = [];\n\n\tfor (let i = 0; i < numLines; i++) {\n\t\tif (aLines[i] !== bLines[i]) diff.push(i);\n\t}\n\n\treturn {\n\t\tlines: diff,\n\t\tnumLinesBefore: aLines.length,\n\t\tnumLinesAfter: bLines.length,\n\t\tnumLines,\n\t};\n}\n","import { stdin, stdout } from 'node:process';\nimport type { Key } from 'node:readline';\nimport * as readline from 'node:readline';\nimport type { Readable, Writable } from 'node:stream';\nimport { ReadStream } from 'node:tty';\nimport { wrapAnsi } from 'fast-wrap-ansi';\nimport { cursor } from 'sisteransi';\nimport { isActionKey } from './settings.js';\n\nexport * from './settings.js';\nexport * from './string.js';\n\nconst isWindows = globalThis.process.platform.startsWith('win');\n\nexport const CANCEL_SYMBOL = Symbol('clack:cancel');\n\nexport function isCancel(value: unknown): value is symbol {\n\treturn value === CANCEL_SYMBOL;\n}\n\nexport function setRawMode(input: Readable, value: boolean) {\n\tconst i = input as typeof stdin;\n\n\tif (i.isTTY) i.setRawMode(value);\n}\n\ninterface BlockOptions {\n\tinput?: Readable;\n\toutput?: Writable;\n\toverwrite?: boolean;\n\thideCursor?: boolean;\n}\n\nexport function block({\n\tinput = stdin,\n\toutput = stdout,\n\toverwrite = true,\n\thideCursor = true,\n}: BlockOptions = {}) {\n\tconst rl = readline.createInterface({\n\t\tinput,\n\t\toutput,\n\t\tprompt: '',\n\t\ttabSize: 1,\n\t});\n\treadline.emitKeypressEvents(input, rl);\n\n\tif (input instanceof ReadStream && input.isTTY) {\n\t\tinput.setRawMode(true);\n\t}\n\n\tconst clear = (data: Buffer, { name, sequence }: Key) => {\n\t\tconst str = String(data);\n\t\tif (isActionKey([str, name, sequence], 'cancel')) {\n\t\t\tif (hideCursor) output.write(cursor.show);\n\t\t\tprocess.exit(0);\n\t\t\treturn;\n\t\t}\n\t\tif (!overwrite) return;\n\t\tconst dx = name === 'return' ? 0 : -1;\n\t\tconst dy = name === 'return' ? -1 : 0;\n\n\t\treadline.moveCursor(output, dx, dy, () => {\n\t\t\treadline.clearLine(output, 1, () => {\n\t\t\t\tinput.once('keypress', clear);\n\t\t\t});\n\t\t});\n\t};\n\tif (hideCursor) output.write(cursor.hide);\n\tinput.once('keypress', clear);\n\n\treturn () => {\n\t\tinput.off('keypress', clear);\n\t\tif (hideCursor) output.write(cursor.show);\n\n\t\t// Prevent Windows specific issues: https://github.com/bombshell-dev/clack/issues/176\n\t\tif (input instanceof ReadStream && input.isTTY && !isWindows) {\n\t\t\tinput.setRawMode(false);\n\t\t}\n\n\t\t// @ts-expect-error fix for https://github.com/nodejs/node/issues/31762#issuecomment-1441223907\n\t\trl.terminal = false;\n\t\trl.close();\n\t};\n}\n\nexport const getColumns = (output: Writable): number => {\n\tif ('columns' in output && typeof output.columns === 'number') {\n\t\treturn output.columns;\n\t}\n\treturn 80;\n};\n\nexport const getRows = (output: Writable): number => {\n\tif ('rows' in output && typeof output.rows === 'number') {\n\t\treturn output.rows;\n\t}\n\treturn 20;\n};\n\nexport function wrapTextWithPrefix(\n\toutput: Writable | undefined,\n\ttext: string,\n\tprefix: string,\n\tstartPrefix: string = prefix,\n\tlineFormatter?: (line: string, index: number) => string\n): string {\n\tconst columns = getColumns(output ?? stdout);\n\tconst wrapped = wrapAnsi(text, columns - prefix.length, {\n\t\thard: true,\n\t\ttrim: false,\n\t});\n\tconst lines = wrapped\n\t\t.split('\\n')\n\t\t.map((line, index) => {\n\t\t\tconst lineString = lineFormatter ? lineFormatter(line, index) : line;\n\t\t\treturn `${index === 0 ? startPrefix : prefix}${lineString}`;\n\t\t})\n\t\t.join('\\n');\n\treturn lines;\n}\n","import { stdin, stdout } from 'node:process';\nimport readline, { type Key, type ReadLine } from 'node:readline';\nimport type { Readable, Writable } from 'node:stream';\nimport { wrapAnsi } from 'fast-wrap-ansi';\nimport { cursor, erase } from 'sisteransi';\nimport type { ClackEvents, ClackState } from '../types.js';\nimport type { Action } from '../utils/index.js';\nimport {\n\tCANCEL_SYMBOL,\n\tdiffLines,\n\tgetRows,\n\tisActionKey,\n\tsetRawMode,\n\tsettings,\n} from '../utils/index.js';\n\nexport interface PromptOptions<TValue, Self extends Prompt<TValue>> {\n\trender(this: Omit<Self, 'prompt'>): string | undefined;\n\tinitialValue?: any;\n\tinitialUserInput?: string;\n\tvalidate?: ((value: TValue | undefined) => string | Error | undefined) | undefined;\n\tinput?: Readable;\n\toutput?: Writable;\n\tsignal?: AbortSignal;\n}\n\nexport default class Prompt<TValue> {\n\tprotected input: Readable;\n\tprotected output: Writable;\n\tprivate _abortSignal?: AbortSignal;\n\n\tprivate rl: ReadLine | undefined;\n\tprivate opts: Omit<PromptOptions<TValue, Prompt<TValue>>, 'render' | 'input' | 'output'>;\n\tprivate _render: (context: Omit<Prompt<TValue>, 'prompt'>) => string | undefined;\n\tprivate _track = false;\n\tprivate _prevFrame = '';\n\tprivate _subscribers = new Map<string, { cb: (...args: any) => any; once?: boolean }[]>();\n\tprotected _cursor = 0;\n\n\tpublic state: ClackState = 'initial';\n\tpublic error = '';\n\tpublic value: TValue | undefined;\n\tpublic userInput = '';\n\n\tconstructor(options: PromptOptions<TValue, Prompt<TValue>>, trackValue = true) {\n\t\tconst { input = stdin, output = stdout, render, signal, ...opts } = options;\n\n\t\tthis.opts = opts;\n\t\tthis.onKeypress = this.onKeypress.bind(this);\n\t\tthis.close = this.close.bind(this);\n\t\tthis.render = this.render.bind(this);\n\t\tthis._render = render.bind(this);\n\t\tthis._track = trackValue;\n\t\tthis._abortSignal = signal;\n\n\t\tthis.input = input;\n\t\tthis.output = output;\n\t}\n\n\t/**\n\t * Unsubscribe all listeners\n\t */\n\tprotected unsubscribe() {\n\t\tthis._subscribers.clear();\n\t}\n\n\t/**\n\t * Set a subscriber with opts\n\t * @param event - The event name\n\t */\n\tprivate setSubscriber<T extends keyof ClackEvents<TValue>>(\n\t\tevent: T,\n\t\topts: { cb: ClackEvents<TValue>[T]; once?: boolean }\n\t) {\n\t\tconst params = this._subscribers.get(event) ?? [];\n\t\tparams.push(opts);\n\t\tthis._subscribers.set(event, params);\n\t}\n\n\t/**\n\t * Subscribe to an event\n\t * @param event - The event name\n\t * @param cb - The callback\n\t */\n\tpublic on<T extends keyof ClackEvents<TValue>>(event: T, cb: ClackEvents<TValue>[T]) {\n\t\tthis.setSubscriber(event, { cb });\n\t}\n\n\t/**\n\t * Subscribe to an event once\n\t * @param event - The event name\n\t * @param cb - The callback\n\t */\n\tpublic once<T extends keyof ClackEvents<TValue>>(event: T, cb: ClackEvents<TValue>[T]) {\n\t\tthis.setSubscriber(event, { cb, once: true });\n\t}\n\n\t/**\n\t * Emit an event with data\n\t * @param event - The event name\n\t * @param data - The data to pass to the callback\n\t */\n\tpublic emit<T extends keyof ClackEvents<TValue>>(\n\t\tevent: T,\n\t\t...data: Parameters<ClackEvents<TValue>[T]>\n\t) {\n\t\tconst cbs = this._subscribers.get(event) ?? [];\n\t\tconst cleanup: (() => void)[] = [];\n\n\t\tfor (const subscriber of cbs) {\n\t\t\tsubscriber.cb(...data);\n\n\t\t\tif (subscriber.once) {\n\t\t\t\tcleanup.push(() => cbs.splice(cbs.indexOf(subscriber), 1));\n\t\t\t}\n\t\t}\n\n\t\tfor (const cb of cleanup) {\n\t\t\tcb();\n\t\t}\n\t}\n\n\tpublic prompt() {\n\t\treturn new Promise<TValue | symbol | undefined>((resolve) => {\n\t\t\tif (this._abortSignal) {\n\t\t\t\tif (this._abortSignal.aborted) {\n\t\t\t\t\tthis.state = 'cancel';\n\n\t\t\t\t\tthis.close();\n\t\t\t\t\treturn resolve(CANCEL_SYMBOL);\n\t\t\t\t}\n\n\t\t\t\tthis._abortSignal.addEventListener(\n\t\t\t\t\t'abort',\n\t\t\t\t\t() => {\n\t\t\t\t\t\tthis.state = 'cancel';\n\t\t\t\t\t\tthis.close();\n\t\t\t\t\t},\n\t\t\t\t\t{ once: true }\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tthis.rl = readline.createInterface({\n\t\t\t\tinput: this.input,\n\t\t\t\ttabSize: 2,\n\t\t\t\tprompt: '',\n\t\t\t\tescapeCodeTimeout: 50,\n\t\t\t\tterminal: true,\n\t\t\t});\n\t\t\tthis.rl.prompt();\n\n\t\t\tif (this.opts.initialUserInput !== undefined) {\n\t\t\t\tthis._setUserInput(this.opts.initialUserInput, true);\n\t\t\t}\n\n\t\t\tthis.input.on('keypress', this.onKeypress);\n\t\t\tsetRawMode(this.input, true);\n\t\t\tthis.output.on('resize', this.render);\n\n\t\t\tthis.render();\n\n\t\t\tthis.once('submit', () => {\n\t\t\t\tthis.output.write(cursor.show);\n\t\t\t\tthis.output.off('resize', this.render);\n\t\t\t\tsetRawMode(this.input, false);\n\t\t\t\tresolve(this.value);\n\t\t\t});\n\t\t\tthis.once('cancel', () => {\n\t\t\t\tthis.output.write(cursor.show);\n\t\t\t\tthis.output.off('resize', this.render);\n\t\t\t\tsetRawMode(this.input, false);\n\t\t\t\tresolve(CANCEL_SYMBOL);\n\t\t\t});\n\t\t});\n\t}\n\n\tprotected _isActionKey(char: string | undefined, _key: Key): boolean {\n\t\treturn char === '\\t';\n\t}\n\n\tprotected _shouldSubmit(_char: string | undefined, _key: Key): boolean {\n\t\treturn true;\n\t}\n\n\tprotected _setValue(value: TValue | undefined): void {\n\t\tthis.value = value;\n\t\tthis.emit('value', this.value);\n\t}\n\n\tprotected _setUserInput(value: string | undefined, write?: boolean): void {\n\t\tthis.userInput = value ?? '';\n\t\tthis.emit('userInput', this.userInput);\n\t\tif (write && this._track && this.rl) {\n\t\t\tthis.rl.write(this.userInput);\n\t\t\tthis._cursor = this.rl.cursor;\n\t\t}\n\t}\n\n\tprotected _clearUserInput(): void {\n\t\tthis.rl?.write(null, { ctrl: true, name: 'u' });\n\t\tthis._setUserInput('');\n\t}\n\n\tprivate onKeypress(char: string | undefined, key: Key) {\n\t\tif (this._track && key.name !== 'return') {\n\t\t\tif (key.name && this._isActionKey(char, key)) {\n\t\t\t\tthis.rl?.write(null, { ctrl: true, name: 'h' });\n\t\t\t}\n\t\t\tthis._cursor = this.rl?.cursor ?? 0;\n\t\t\tthis._setUserInput(this.rl?.line);\n\t\t}\n\n\t\tif (this.state === 'error') {\n\t\t\tthis.state = 'active';\n\t\t}\n\t\tif (key?.name) {\n\t\t\tif (!this._track && settings.aliases.has(key.name)) {\n\t\t\t\tthis.emit('cursor', settings.aliases.get(key.name));\n\t\t\t}\n\t\t\tif (settings.actions.has(key.name as Action)) {\n\t\t\t\tthis.emit('cursor', key.name as Action);\n\t\t\t}\n\t\t}\n\t\tif (char && (char.toLowerCase() === 'y' || char.toLowerCase() === 'n')) {\n\t\t\tthis.emit('confirm', char.toLowerCase() === 'y');\n\t\t}\n\n\t\t// Call the key event handler and emit the key event\n\t\tthis.emit('key', char?.toLowerCase(), key);\n\n\t\tif (key?.name === 'return' && this._shouldSubmit(char, key)) {\n\t\t\tif (this.opts.validate) {\n\t\t\t\tconst problem = this.opts.validate(this.value);\n\t\t\t\tif (problem) {\n\t\t\t\t\tthis.error = problem instanceof Error ? problem.message : problem;\n\t\t\t\t\tthis.state = 'error';\n\t\t\t\t\tthis.rl?.write(this.userInput);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (this.state !== 'error') {\n\t\t\t\tthis.state = 'submit';\n\t\t\t}\n\t\t}\n\n\t\tif (isActionKey([char, key?.name, key?.sequence], 'cancel')) {\n\t\t\tthis.state = 'cancel';\n\t\t}\n\n\t\tif (this.state === 'submit' || this.state === 'cancel') {\n\t\t\tthis.emit('finalize');\n\t\t}\n\t\tthis.render();\n\t\tif (this.state === 'submit' || this.state === 'cancel') {\n\t\t\tthis.close();\n\t\t}\n\t}\n\n\tprotected close() {\n\t\tthis.input.unpipe();\n\t\tthis.input.removeListener('keypress', this.onKeypress);\n\t\tthis.output.write('\\n');\n\t\tsetRawMode(this.input, false);\n\t\tthis.rl?.close();\n\t\tthis.rl = undefined;\n\t\tthis.emit(`${this.state}`, this.value);\n\t\tthis.unsubscribe();\n\t}\n\n\tprivate restoreCursor() {\n\t\tconst lines =\n\t\t\twrapAnsi(this._prevFrame, process.stdout.columns, { hard: true, trim: false }).split('\\n')\n\t\t\t\t.length - 1;\n\t\tthis.output.write(cursor.move(-999, lines * -1));\n\t}\n\n\tprivate render() {\n\t\tconst frame = wrapAnsi(this._render(this) ?? '', process.stdout.columns, {\n\t\t\thard: true,\n\t\t\ttrim: false,\n\t\t});\n\t\tif (frame === this._prevFrame) return;\n\n\t\tif (this.state === 'initial') {\n\t\t\tthis.output.write(cursor.hide);\n\t\t} else {\n\t\t\tconst diff = diffLines(this._prevFrame, frame);\n\t\t\tconst rows = getRows(this.output);\n\t\t\tthis.restoreCursor();\n\t\t\tif (diff) {\n\t\t\t\tconst diffOffsetAfter = Math.max(0, diff.numLinesAfter - rows);\n\t\t\t\tconst diffOffsetBefore = Math.max(0, diff.numLinesBefore - rows);\n\t\t\t\tlet diffLine = diff.lines.find((line) => line >= diffOffsetAfter);\n\n\t\t\t\tif (diffLine === undefined) {\n\t\t\t\t\tthis._prevFrame = frame;\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// If a single line has changed, only update that line\n\t\t\t\tif (diff.lines.length === 1) {\n\t\t\t\t\tthis.output.write(cursor.move(0, diffLine - diffOffsetBefore));\n\t\t\t\t\tthis.output.write(erase.lines(1));\n\t\t\t\t\tconst lines = frame.split('\\n');\n\t\t\t\t\tthis.output.write(lines[diffLine]);\n\t\t\t\t\tthis._prevFrame = frame;\n\t\t\t\t\tthis.output.write(cursor.move(0, lines.length - diffLine - 1));\n\t\t\t\t\treturn;\n\t\t\t\t\t// If many lines have changed, rerender everything past the first line\n\t\t\t\t} else if (diff.lines.length > 1) {\n\t\t\t\t\tif (diffOffsetAfter < diffOffsetBefore) {\n\t\t\t\t\t\tdiffLine = diffOffsetAfter;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tconst adjustedDiffLine = diffLine - diffOffsetBefore;\n\t\t\t\t\t\tif (adjustedDiffLine > 0) {\n\t\t\t\t\t\t\tthis.output.write(cursor.move(0, adjustedDiffLine));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tthis.output.write(erase.down());\n\t\t\t\t\tconst lines = frame.split('\\n');\n\t\t\t\t\tconst newLines = lines.slice(diffLine);\n\t\t\t\t\tthis.output.write(newLines.join('\\n'));\n\t\t\t\t\tthis._prevFrame = frame;\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis.output.write(erase.down());\n\t\t}\n\n\t\tthis.output.write(frame);\n\t\tif (this.state === 'initial') {\n\t\t\tthis.state = 'active';\n\t\t}\n\t\tthis._prevFrame = frame;\n\t}\n}\n","import type { Key } from 'node:readline';\nimport { styleText } from 'node:util';\nimport { findCursor } from '../utils/cursor.js';\nimport Prompt, { type PromptOptions } from './prompt.js';\n\ninterface OptionLike {\n\tvalue: unknown;\n\tlabel?: string;\n\tdisabled?: boolean;\n}\n\ntype FilterFunction<T extends OptionLike> = (search: string, opt: T) => boolean;\n\nfunction getCursorForValue<T extends OptionLike>(\n\tselected: T['value'] | undefined,\n\titems: T[]\n): number {\n\tif (selected === undefined) {\n\t\treturn 0;\n\t}\n\n\tconst currLength = items.length;\n\n\t// If filtering changed the available options, update cursor\n\tif (currLength === 0) {\n\t\treturn 0;\n\t}\n\n\t// Try to maintain the same selected item\n\tconst index = items.findIndex((item) => item.value === selected);\n\treturn index !== -1 ? index : 0;\n}\n\nfunction defaultFilter<T extends OptionLike>(input: string, option: T): boolean {\n\tconst label = option.label ?? String(option.value);\n\treturn label.toLowerCase().includes(input.toLowerCase());\n}\n\nfunction normalisedValue<T>(multiple: boolean, values: T[] | undefined): T | T[] | undefined {\n\tif (!values) {\n\t\treturn undefined;\n\t}\n\tif (multiple) {\n\t\treturn values;\n\t}\n\treturn values[0];\n}\n\nexport interface AutocompleteOptions<T extends OptionLike>\n\textends PromptOptions<T['value'] | T['value'][], AutocompletePrompt<T>> {\n\toptions: T[] | ((this: AutocompletePrompt<T>) => T[]);\n\tfilter?: FilterFunction<T>;\n\tmultiple?: boolean;\n\t/**\n\t * When set (non-empty), pressing Tab with no input fills the field with this value\n\t * and runs the normal filter/selection logic so the user can confirm with Enter.\n\t * Tab only fills the input when the placeholder matches at least one option under\n\t * the prompt's filter (so the value remains selectable).\n\t */\n\tplaceholder?: string;\n}\n\nexport default class AutocompletePrompt<T extends OptionLike> extends Prompt<\n\tT['value'] | T['value'][]\n> {\n\tfilteredOptions: T[];\n\tmultiple: boolean;\n\tisNavigating = false;\n\tselectedValues: Array<T['value']> = [];\n\n\tfocusedValue: T['value'] | undefined;\n\t#cursor = 0;\n\t#lastUserInput = '';\n\t#filterFn: FilterFunction<T> | undefined;\n\t#options: T[] | (() => T[]);\n\t#placeholder: string | undefined;\n\n\tget cursor(): number {\n\t\treturn this.#cursor;\n\t}\n\n\tget userInputWithCursor() {\n\t\tif (!this.userInput) {\n\t\t\treturn styleText(['inverse', 'hidden'], '_');\n\t\t}\n\t\tif (this._cursor >= this.userInput.length) {\n\t\t\treturn `${this.userInput}█`;\n\t\t}\n\t\tconst s1 = this.userInput.slice(0, this._cursor);\n\t\tconst [s2, ...s3] = this.userInput.slice(this._cursor);\n\t\treturn `${s1}${styleText('inverse', s2)}${s3.join('')}`;\n\t}\n\n\tget options(): T[] {\n\t\tif (typeof this.#options === 'function') {\n\t\t\treturn this.#options();\n\t\t}\n\t\treturn this.#options;\n\t}\n\n\tconstructor(opts: AutocompleteOptions<T>) {\n\t\tsuper(opts);\n\n\t\tthis.#options = opts.options;\n\t\tthis.#placeholder = opts.placeholder;\n\t\tconst options = this.options;\n\t\tthis.filteredOptions = [...options];\n\t\tthis.multiple = opts.multiple === true;\n\t\tthis.#filterFn =\n\t\t\ttypeof opts.options === 'function' ? opts.filter : (opts.filter ?? defaultFilter);\n\t\tlet initialValues: unknown[] | undefined;\n\t\tif (opts.initialValue && Array.isArray(opts.initialValue)) {\n\t\t\tif (this.multiple) {\n\t\t\t\tinitialValues = opts.initialValue;\n\t\t\t} else {\n\t\t\t\tinitialValues = opts.initialValue.slice(0, 1);\n\t\t\t}\n\t\t} else {\n\t\t\tif (!this.multiple && this.options.length > 0) {\n\t\t\t\tinitialValues = [this.options[0].value];\n\t\t\t}\n\t\t}\n\n\t\tif (initialValues) {\n\t\t\tfor (const selectedValue of initialValues) {\n\t\t\t\tconst selectedIndex = options.findIndex((opt) => opt.value === selectedValue);\n\t\t\t\tif (selectedIndex !== -1) {\n\t\t\t\t\tthis.toggleSelected(selectedValue);\n\t\t\t\t\tthis.#cursor = selectedIndex;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tthis.focusedValue = this.options[this.#cursor]?.value;\n\n\t\tthis.on('key', (char, key) => this.#onKey(char, key));\n\t\tthis.on('userInput', (value) => this.#onUserInputChanged(value));\n\t}\n\n\tprotected override _isActionKey(char: string | undefined, key: Key): boolean {\n\t\treturn (\n\t\t\tchar === '\\t' ||\n\t\t\t(this.multiple &&\n\t\t\t\tthis.isNavigating &&\n\t\t\t\tkey.name === 'space' &&\n\t\t\t\tchar !== undefined &&\n\t\t\t\tchar !== '')\n\t\t);\n\t}\n\n\t#onKey(_char: string | undefined, key: Key): void {\n\t\tconst isUpKey = key.name === 'up';\n\t\tconst isDownKey = key.name === 'down';\n\t\tconst isReturnKey = key.name === 'return';\n\n\t\t// Tab with empty input and placeholder: fill input with placeholder to trigger autocomplete\n\t\t// Only when the placeholder matches at least one (non-disabled) option so the value remains selectable\n\t\tconst isEmptyOrOnlyTab = this.userInput === '' || this.userInput === '\\t';\n\t\tconst placeholder = this.#placeholder;\n\t\tconst options = this.options;\n\t\tconst placeholderMatchesOption =\n\t\t\tplaceholder !== undefined &&\n\t\t\tplaceholder !== '' &&\n\t\t\toptions.some(\n\t\t\t\t(opt) => !opt.disabled && (this.#filterFn ? this.#filterFn(placeholder, opt) : true)\n\t\t\t);\n\t\tif (key.name === 'tab' && isEmptyOrOnlyTab && placeholderMatchesOption) {\n\t\t\tif (this.userInput === '\\t') {\n\t\t\t\tthis._clearUserInput();\n\t\t\t}\n\t\t\tthis._setUserInput(placeholder, true);\n\t\t\tthis.isNavigating = false;\n\t\t\treturn;\n\t\t}\n\n\t\t// Start navigation mode with up/down arrows\n\t\tif (isUpKey || isDownKey) {\n\t\t\tthis.#cursor = findCursor(this.#cursor, isUpKey ? -1 : 1, this.filteredOptions);\n\t\t\tthis.focusedValue = this.filteredOptions[this.#cursor]?.value;\n\t\t\tif (!this.multiple) {\n\t\t\t\tthis.selectedValues = [this.focusedValue];\n\t\t\t}\n\t\t\tthis.isNavigating = true;\n\t\t} else if (isReturnKey) {\n\t\t\tthis.value = normalisedValue(this.multiple, this.selectedValues);\n\t\t} else {\n\t\t\tif (this.multiple) {\n\t\t\t\tif (\n\t\t\t\t\tthis.focusedValue !== undefined &&\n\t\t\t\t\t(key.name === 'tab' || (this.isNavigating && key.name === 'space'))\n\t\t\t\t) {\n\t\t\t\t\tthis.toggleSelected(this.focusedValue);\n\t\t\t\t} else {\n\t\t\t\t\tthis.isNavigating = false;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif (this.focusedValue) {\n\t\t\t\t\tthis.selectedValues = [this.focusedValue];\n\t\t\t\t}\n\t\t\t\tthis.isNavigating = false;\n\t\t\t}\n\t\t}\n\t}\n\n\tdeselectAll() {\n\t\tthis.selectedValues = [];\n\t}\n\n\ttoggleSelected(value: T['value']) {\n\t\tif (this.filteredOptions.length === 0) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (this.multiple) {\n\t\t\tif (this.selectedValues.includes(value)) {\n\t\t\t\tthis.selectedValues = this.selectedValues.filter((v) => v !== value);\n\t\t\t} else {\n\t\t\t\tthis.selectedValues = [...this.selectedValues, value];\n\t\t\t}\n\t\t} else {\n\t\t\tthis.selectedValues = [value];\n\t\t}\n\t}\n\n\t#onUserInputChanged(value: string): void {\n\t\tif (value !== this.#lastUserInput) {\n\t\t\tthis.#lastUserInput = value;\n\n\t\t\tconst options = this.options;\n\n\t\t\tif (value && this.#filterFn) {\n\t\t\t\tthis.filteredOptions = options.filter((opt) => this.#filterFn?.(value, opt));\n\t\t\t} else {\n\t\t\t\tthis.filteredOptions = [...options];\n\t\t\t}\n\t\t\tconst valueCursor = getCursorForValue(this.focusedValue, this.filteredOptions);\n\t\t\tthis.#cursor = findCursor(valueCursor, 0, this.filteredOptions);\n\t\t\tconst focusedOption = this.filteredOptions[this.#cursor];\n\t\t\tif (focusedOption && !focusedOption.disabled) {\n\t\t\t\tthis.focusedValue = focusedOption.value;\n\t\t\t} else {\n\t\t\t\tthis.focusedValue = undefined;\n\t\t\t}\n\t\t\tif (!this.multiple) {\n\t\t\t\tif (this.focusedValue !== undefined) {\n\t\t\t\t\tthis.toggleSelected(this.focusedValue);\n\t\t\t\t} else {\n\t\t\t\t\tthis.deselectAll();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n","import { cursor } from 'sisteransi';\nimport Prompt, { type PromptOptions } from './prompt.js';\n\nexport interface ConfirmOptions extends PromptOptions<boolean, ConfirmPrompt> {\n\tactive: string;\n\tinactive: string;\n\tinitialValue?: boolean;\n}\n\nexport default class ConfirmPrompt extends Prompt<boolean> {\n\tget cursor() {\n\t\treturn this.value ? 0 : 1;\n\t}\n\n\tprivate get _value() {\n\t\treturn this.cursor === 0;\n\t}\n\n\tconstructor(opts: ConfirmOptions) {\n\t\tsuper(opts, false);\n\t\tthis.value = !!opts.initialValue;\n\n\t\tthis.on('userInput', () => {\n\t\t\tthis.value = this._value;\n\t\t});\n\n\t\tthis.on('confirm', (confirm) => {\n\t\t\tthis.output.write(cursor.move(0, -1));\n\t\t\tthis.value = confirm;\n\t\t\tthis.state = 'submit';\n\t\t\tthis.close();\n\t\t});\n\n\t\tthis.on('cursor', () => {\n\t\t\tthis.value = !this.value;\n\t\t});\n\t}\n}\n","import type { Key } from 'node:readline';\nimport { settings } from '../utils/settings.js';\nimport Prompt, { type PromptOptions } from './prompt.js';\n\ninterface SegmentConfig {\n\ttype: 'year' | 'month' | 'day';\n\tlen: number;\n}\n\nexport interface DateParts {\n\tyear: string;\n\tmonth: string;\n\tday: string;\n}\n\nexport type DateFormat = 'YMD' | 'MDY' | 'DMY';\n\nconst SEGMENTS: Record<string, SegmentConfig> = {\n\tY: { type: 'year', len: 4 },\n\tM: { type: 'month', len: 2 },\n\tD: { type: 'day', len: 2 },\n} as const;\n\nfunction segmentsFor(fmt: DateFormat): SegmentConfig[] {\n\treturn [...fmt].map((c) => SEGMENTS[c as keyof typeof SEGMENTS]);\n}\n\nfunction detectLocaleFormat(locale?: string): { segments: SegmentConfig[]; separator: string } {\n\tconst fmt = new Intl.DateTimeFormat(locale, {\n\t\tyear: 'numeric',\n\t\tmonth: '2-digit',\n\t\tday: '2-digit',\n\t});\n\tconst parts = fmt.formatToParts(new Date(2000, 0, 15));\n\tconst segments: SegmentConfig[] = [];\n\tlet separator = '/';\n\tfor (const p of parts) {\n\t\tif (p.type === 'literal') {\n\t\t\tseparator = p.value.trim() || p.value;\n\t\t} else if (p.type === 'year' || p.type === 'month' || p.type === 'day') {\n\t\t\tsegments.push({ type: p.type, len: p.type === 'year' ? 4 : 2 });\n\t\t}\n\t}\n\treturn { segments, separator };\n}\n\n/** Parse string segment values to numbers, treating blanks as 0 */\nfunction parseSegmentToNum(s: string): number {\n\treturn Number.parseInt((s || '0').replace(/_/g, '0'), 10) || 0;\n}\n\nfunction parse(parts: DateParts): { year: number; month: number; day: number } {\n\treturn {\n\t\tyear: parseSegmentToNum(parts.year),\n\t\tmonth: parseSegmentToNum(parts.month),\n\t\tday: parseSegmentToNum(parts.day),\n\t};\n}\n\nfunction daysInMonth(year: number, month: number): number {\n\treturn new Date(year || 2001, month || 1, 0).getDate();\n}\n\n/** Validate and return calendar parts, or undefined if invalid */\nfunction validParts(parts: DateParts): { year: number; month: number; day: number } | undefined {\n\tconst { year, month, day } = parse(parts);\n\tif (!year || year < 0 || year > 9999) return undefined;\n\tif (!month || month < 1 || month > 12) return undefined;\n\tif (!day || day < 1) return undefined;\n\tconst d = new Date(Date.UTC(year, month - 1, day));\n\tif (d.getUTCFullYear() !== year || d.getUTCMonth() !== month - 1 || d.getUTCDate() !== day)\n\t\treturn undefined;\n\treturn { year, month, day };\n}\n\nfunction toDate(parts: DateParts): Date | undefined {\n\tconst p = validParts(parts);\n\treturn p ? new Date(Date.UTC(p.year, p.month - 1, p.day)) : undefined;\n}\n\nfunction segmentBounds(\n\ttype: 'year' | 'month' | 'day',\n\tctx: { year: number; month: number },\n\tminDate: Date | undefined,\n\tmaxDate: Date | undefined\n): { min: number; max: number } {\n\tconst minP = minDate\n\t\t? {\n\t\t\t\tyear: minDate.getUTCFullYear(),\n\t\t\t\tmonth: minDate.getUTCMonth() + 1,\n\t\t\t\tday: minDate.getUTCDate(),\n\t\t\t}\n\t\t: null;\n\tconst maxP = maxDate\n\t\t? {\n\t\t\t\tyear: maxDate.getUTCFullYear(),\n\t\t\t\tmonth: maxDate.getUTCMonth() + 1,\n\t\t\t\tday: maxDate.getUTCDate(),\n\t\t\t}\n\t\t: null;\n\n\tif (type === 'year') {\n\t\treturn { min: minP?.year ?? 1, max: maxP?.year ?? 9999 };\n\t}\n\tif (type === 'month') {\n\t\treturn {\n\t\t\tmin: minP && ctx.year === minP.year ? minP.month : 1,\n\t\t\tmax: maxP && ctx.year === maxP.year ? maxP.month : 12,\n\t\t};\n\t}\n\treturn {\n\t\tmin: minP && ctx.year === minP.year && ctx.month === minP.month ? minP.day : 1,\n\t\tmax:\n\t\t\tmaxP && ctx.year === maxP.year && ctx.month === maxP.month\n\t\t\t\t? maxP.day\n\t\t\t\t: daysInMonth(ctx.year, ctx.month),\n\t};\n}\n\nexport interface DateOptions extends PromptOptions<Date, DatePrompt> {\n\tformat?: DateFormat;\n\tlocale?: string;\n\tseparator?: string;\n\tdefaultValue?: Date;\n\tinitialValue?: Date;\n\tminDate?: Date;\n\tmaxDate?: Date;\n}\n\nexport default class DatePrompt extends Prompt<Date> {\n\t#segments: SegmentConfig[];\n\t#separator: string;\n\t#segmentValues: DateParts;\n\t#minDate: Date | undefined;\n\t#maxDate: Date | undefined;\n\t#cursor = { segmentIndex: 0, positionInSegment: 0 };\n\t#segmentSelected = true;\n\t#pendingTensDigit: string | null = null;\n\n\tinlineError = '';\n\n\tget segmentCursor() {\n\t\treturn { ...this.#cursor };\n\t}\n\n\tget segmentValues(): DateParts {\n\t\treturn { ...this.#segmentValues };\n\t}\n\n\tget segments(): readonly SegmentConfig[] {\n\t\treturn this.#segments;\n\t}\n\n\tget separator(): string {\n\t\treturn this.#separator;\n\t}\n\n\tget formattedValue(): string {\n\t\treturn this.#format(this.#segmentValues);\n\t}\n\n\t#format(parts: DateParts): string {\n\t\treturn this.#segments.map((s) => parts[s.type]).join(this.#separator);\n\t}\n\n\t#refresh() {\n\t\tthis._setUserInput(this.#format(this.#segmentValues));\n\t\tthis._setValue(toDate(this.#segmentValues) ?? undefined);\n\t}\n\n\tconstructor(opts: DateOptions) {\n\t\tconst detected = opts.format\n\t\t\t? { segments: segmentsFor(opts.format), separator: opts.separator ?? '/' }\n\t\t\t: detectLocaleFormat(opts.locale);\n\t\tconst sep = opts.separator ?? detected.separator;\n\t\tconst segments = opts.format ? segmentsFor(opts.format) : detected.segments;\n\n\t\tconst initialDate = opts.initialValue ?? opts.defaultValue;\n\t\tconst segmentValues: DateParts = initialDate\n\t\t\t? {\n\t\t\t\t\tyear: String(initialDate.getUTCFullYear()).padStart(4, '0'),\n\t\t\t\t\tmonth: String(initialDate.getUTCMonth() + 1).padStart(2, '0'),\n\t\t\t\t\tday: String(initialDate.getUTCDate()).padStart(2, '0'),\n\t\t\t\t}\n\t\t\t: { year: '____', month: '__', day: '__' };\n\n\t\tconst initialDisplay = segments.map((s) => segmentValues[s.type]).join(sep);\n\n\t\tsuper({ ...opts, initialUserInput: initialDisplay }, false);\n\t\tthis.#segments = segments;\n\t\tthis.#separator = sep;\n\t\tthis.#segmentValues = segmentValues;\n\t\tthis.#minDate = opts.minDate;\n\t\tthis.#maxDate = opts.maxDate;\n\t\tthis.#refresh();\n\n\t\tthis.on('cursor', (key) => this.#onCursor(key));\n\t\tthis.on('key', (char, key) => this.#onKey(char, key));\n\t\tthis.on('finalize', () => this.#onFinalize(opts));\n\t}\n\n\t#seg(): { segment: SegmentConfig; index: number } | undefined {\n\t\tconst index = Math.max(0, Math.min(this.#cursor.segmentIndex, this.#segments.length - 1));\n\t\tconst segment = this.#segments[index];\n\t\tif (!segment) return undefined;\n\t\tthis.#cursor.positionInSegment = Math.max(\n\t\t\t0,\n\t\t\tMath.min(this.#cursor.positionInSegment, segment.len - 1)\n\t\t);\n\t\treturn { segment, index };\n\t}\n\n\t#navigate(direction: 1 | -1) {\n\t\tthis.inlineError = '';\n\t\tthis.#pendingTensDigit = null;\n\t\tconst ctx = this.#seg();\n\t\tif (!ctx) return;\n\t\tthis.#cursor.segmentIndex = Math.max(\n\t\t\t0,\n\t\t\tMath.min(this.#segments.length - 1, ctx.index + direction)\n\t\t);\n\t\tthis.#cursor.positionInSegment = 0;\n\t\tthis.#segmentSelected = true;\n\t}\n\n\t#adjust(direction: 1 | -1) {\n\t\tconst ctx = this.#seg();\n\t\tif (!ctx) return;\n\t\tconst { segment } = ctx;\n\t\tconst raw = this.#segmentValues[segment.type];\n\t\tconst isBlank = !raw || raw.replace(/_/g, '') === '';\n\t\tconst num = Number.parseInt((raw || '0').replace(/_/g, '0'), 10) || 0;\n\t\tconst bounds = segmentBounds(\n\t\t\tsegment.type,\n\t\t\tparse(this.#segmentValues),\n\t\t\tthis.#minDate,\n\t\t\tthis.#maxDate\n\t\t);\n\n\t\tlet next: number;\n\t\tif (isBlank) {\n\t\t\tnext = direction === 1 ? bounds.min : bounds.max;\n\t\t} else {\n\t\t\tnext = Math.max(Math.min(bounds.max, num + direction), bounds.min);\n\t\t}\n\n\t\tthis.#segmentValues = {\n\t\t\t...this.#segmentValues,\n\t\t\t[segment.type]: next.toString().padStart(segment.len, '0'),\n\t\t};\n\t\tthis.#segmentSelected = true;\n\t\tthis.#pendingTensDigit = null;\n\t\tthis.#refresh();\n\t}\n\n\t#onCursor(key?: string) {\n\t\tif (!key) return;\n\t\tswitch (key) {\n\t\t\tcase 'right':\n\t\t\t\treturn this.#navigate(1);\n\t\t\tcase 'left':\n\t\t\t\treturn this.#navigate(-1);\n\t\t\tcase 'up':\n\t\t\t\treturn this.#adjust(1);\n\t\t\tcase 'down':\n\t\t\t\treturn this.#adjust(-1);\n\t\t}\n\t}\n\n\t#onKey(char: string | undefined, key: Key) {\n\t\t// Backspace\n\t\tconst isBackspace =\n\t\t\tkey?.name === 'backspace' ||\n\t\t\tkey?.sequence === '\\x7f' ||\n\t\t\tkey?.sequence === '\\b' ||\n\t\t\tchar === '\\x7f' ||\n\t\t\tchar === '\\b';\n\t\tif (isBackspace) {\n\t\t\tthis.inlineError = '';\n\t\t\tconst ctx = this.#seg();\n\t\t\tif (!ctx) return;\n\t\t\tif (!this.#segmentValues[ctx.segment.type].replace(/_/g, '')) {\n\t\t\t\tthis.#navigate(-1);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthis.#segmentValues[ctx.segment.type] = '_'.repeat(ctx.segment.len);\n\t\t\tthis.#segmentSelected = true;\n\t\t\tthis.#cursor.positionInSegment = 0;\n\t\t\tthis.#refresh();\n\t\t\treturn;\n\t\t}\n\n\t\t// Tab navigation\n\t\tif (key?.name === 'tab') {\n\t\t\tthis.inlineError = '';\n\t\t\tconst ctx = this.#seg();\n\t\t\tif (!ctx) return;\n\t\t\tconst dir = key.shift ? -1 : 1;\n\t\t\tconst next = ctx.index + dir;\n\t\t\tif (next >= 0 && next < this.#segments.length) {\n\t\t\t\tthis.#cursor.segmentIndex = next;\n\t\t\t\tthis.#cursor.positionInSegment = 0;\n\t\t\t\tthis.#segmentSelected = true;\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Digit input\n\t\tif (char && /^[0-9]$/.test(char)) {\n\t\t\tconst ctx = this.#seg();\n\t\t\tif (!ctx) return;\n\t\t\tconst { segment } = ctx;\n\t\t\tconst isBlank = !this.#segmentValues[segment.type].replace(/_/g, '');\n\n\t\t\t// Pending tens digit: complete the two-digit entry\n\t\t\tif (this.#segmentSelected && this.#pendingTensDigit !== null && !isBlank) {\n\t\t\t\tconst newVal = this.#pendingTensDigit + char;\n\t\t\t\tconst newParts = { ...this.#segmentValues, [segment.type]: newVal };\n\t\t\t\tconst err = this.#validateSegment(newParts, segment);\n\t\t\t\tif (err) {\n\t\t\t\t\tthis.inlineError = err;\n\t\t\t\t\tthis.#pendingTensDigit = null;\n\t\t\t\t\tthis.#segmentSelected = false;\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tthis.inlineError = '';\n\t\t\t\tthis.#segmentValues[segment.type] = newVal;\n\t\t\t\tthis.#pendingTensDigit = null;\n\t\t\t\tthis.#segmentSelected = false;\n\t\t\t\tthis.#refresh();\n\t\t\t\tif (ctx.index < this.#segments.length - 1) {\n\t\t\t\t\tthis.#cursor.segmentIndex = ctx.index + 1;\n\t\t\t\t\tthis.#cursor.positionInSegment = 0;\n\t\t\t\t\tthis.#segmentSelected = true;\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Clear-on-type: typing into a selected filled segment clears it first\n\t\t\tif (this.#segmentSelected && !isBlank) {\n\t\t\t\tthis.#segmentValues[segment.type] = '_'.repeat(segment.len);\n\t\t\t\tthis.#cursor.positionInSegment = 0;\n\t\t\t}\n\t\t\tthis.#segmentSelected = false;\n\t\t\tthis.#pendingTensDigit = null;\n\n\t\t\tconst display = this.#segmentValues[segment.type];\n\t\t\tconst firstBlank = display.indexOf('_');\n\t\t\tconst pos =\n\t\t\t\tfirstBlank >= 0 ? firstBlank : Math.min(this.#cursor.positionInSegment, segment.len - 1);\n\t\t\tif (pos < 0 || pos >= segment.len) return;\n\n\t\t\tlet newVal = display.slice(0, pos) + char + display.slice(pos + 1);\n\n\t\t\t// Smart digit placement\n\t\t\tlet shouldStaySelected = false;\n\t\t\tif (pos === 0 && display === '__' && (segment.type === 'month' || segment.type === 'day')) {\n\t\t\t\tconst digit = Number.parseInt(char, 10);\n\t\t\t\tnewVal = `0${char}`;\n\t\t\t\tshouldStaySelected = digit <= (segment.type === 'month' ? 1 : 2);\n\t\t\t}\n\t\t\tif (segment.type === 'year') {\n\t\t\t\tconst digits = display.replace(/_/g, '');\n\t\t\t\tnewVal = (digits + char).padStart(segment.len, '_');\n\t\t\t}\n\n\t\t\tif (!newVal.includes('_')) {\n\t\t\t\tconst newParts = { ...this.#segmentValues, [segment.type]: newVal };\n\t\t\t\tconst err = this.#validateSegment(newParts, segment);\n\t\t\t\tif (err) {\n\t\t\t\t\tthis.inlineError = err;\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.inlineError = '';\n\n\t\t\tthis.#segmentValues[segment.type] = newVal;\n\n\t\t\t// Clamp only when the current segment is fully entered\n\t\t\tconst parsed = !newVal.includes('_') ? validParts(this.#segmentValues) : undefined;\n\t\t\tif (parsed) {\n\t\t\t\tconst { year, month } = parsed;\n\t\t\t\tconst maxDay = daysInMonth(year, month);\n\t\t\t\tthis.#segmentValues = {\n\t\t\t\t\tyear: String(Math.max(0, Math.min(9999, year))).padStart(4, '0'),\n\t\t\t\t\tmonth: String(Math.max(1, Math.min(12, month))).padStart(2, '0'),\n\t\t\t\t\tday: String(Math.max(1, Math.min(maxDay, parsed.day))).padStart(2, '0'),\n\t\t\t\t};\n\t\t\t}\n\t\t\tthis.#refresh();\n\n\t\t\t// Advance cursor\n\t\t\tconst nextBlank = newVal.indexOf('_');\n\t\t\tif (shouldStaySelected) {\n\t\t\t\tthis.#segmentSelected = true;\n\t\t\t\tthis.#pendingTensDigit = char;\n\t\t\t} else if (nextBlank >= 0) {\n\t\t\t\tthis.#cursor.positionInSegment = nextBlank;\n\t\t\t} else if (firstBlank >= 0 && ctx.index < this.#segments.length - 1) {\n\t\t\t\tthis.#cursor.segmentIndex = ctx.index + 1;\n\t\t\t\tthis.#cursor.positionInSegment = 0;\n\t\t\t\tthis.#segmentSelected = true;\n\t\t\t} else {\n\t\t\t\tthis.#cursor.positionInSegment = Math.min(pos + 1, segment.len - 1);\n\t\t\t}\n\t\t}\n\t}\n\n\t#validateSegment(parts: DateParts, seg: SegmentConfig): string | undefined {\n\t\tconst { month, day } = parse(parts);\n\t\tif (seg.type === 'month' && (month < 0 || month > 12)) {\n\t\t\treturn settings.date.messages.invalidMonth;\n\t\t}\n\t\tif (seg.type === 'day' && (day < 0 || day > 31)) {\n\t\t\treturn settings.date.messages.invalidDay(31, 'any month');\n\t\t}\n\t\treturn undefined;\n\t}\n\n\t#onFinalize(opts: DateOptions) {\n\t\tconst { year, month, day } = parse(this.#segmentValues);\n\t\tif (year && month && day) {\n\t\t\tconst maxDay = daysInMonth(year, month);\n\t\t\tthis.#segmentValues = {\n\t\t\t\t...this.#segmentValues,\n\t\t\t\tday: String(Math.min(day, maxDay)).padStart(2, '0'),\n\t\t\t};\n\t\t}\n\t\tthis.value = toDate(this.#segmentValues) ?? opts.defaultValue ?? undefined;\n\t}\n}\n","import Prompt, { type PromptOptions } from './prompt.js';\n\nexport interface GroupMultiSelectOptions<T extends { value: any }>\n\textends PromptOptions<T['value'][], GroupMultiSelectPrompt<T>> {\n\toptions: Record<string, T[]>;\n\tinitialValues?: T['value'][];\n\trequired?: boolean;\n\tcursorAt?: T['value'];\n\tselectableGroups?: boolean;\n}\nexport default class GroupMultiSelectPrompt<T extends { value: any }> extends Prompt<T['value'][]> {\n\toptions: (T & { group: string | boolean })[];\n\tcursor = 0;\n\t#selectableGroups: boolean;\n\n\tgetGroupItems(group: string): T[] {\n\t\treturn this.options.filter((o) => o.group === group);\n\t}\n\n\tisGroupSelected(group: string) {\n\t\tconst items = this.getGroupItems(group);\n\t\tconst value = this.value;\n\t\tif (value === undefined) {\n\t\t\treturn false;\n\t\t}\n\t\treturn items.every((i) => value.includes(i.value));\n\t}\n\n\tprivate toggleValue() {\n\t\tconst item = this.options[this.cursor];\n\t\tif (this.value === undefined) {\n\t\t\tthis.value = [];\n\t\t}\n\t\tif (item.group === true) {\n\t\t\tconst group = item.value;\n\t\t\tconst groupedItems = this.getGroupItems(group);\n\t\t\tif (this.isGroupSelected(group)) {\n\t\t\t\tthis.value = this.value.filter(\n\t\t\t\t\t(v: string) => groupedItems.findIndex((i) => i.value === v) === -1\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tthis.value = [...this.value, ...groupedItems.map((i) => i.value)];\n\t\t\t}\n\t\t\tthis.value = Array.from(new Set(this.value));\n\t\t} else {\n\t\t\tconst selected = this.value.includes(item.value);\n\t\t\tthis.value = selected\n\t\t\t\t? this.value.filter((v: T['value']) => v !== item.value)\n\t\t\t\t: [...this.value, item.value];\n\t\t}\n\t}\n\n\tconstructor(opts: GroupMultiSelectOptions<T>) {\n\t\tsuper(opts, false);\n\t\tconst { options } = opts;\n\t\tthis.#selectableGroups = opts.selectableGroups !== false;\n\t\tthis.options = Object.entries(options).flatMap(([key, option]) => [\n\t\t\t{ value: key, group: true, label: key },\n\t\t\t...option.map((opt) => ({ ...opt, group: key })),\n\t\t]) as any;\n\t\tthis.value = [...(opts.initialValues ?? [])];\n\t\tthis.cursor = Math.max(\n\t\t\tthis.options.findIndex(({ value }) => value === opts.cursorAt),\n\t\t\tthis.#selectableGroups ? 0 : 1\n\t\t);\n\n\t\tthis.on('cursor', (key) => {\n\t\t\tswitch (key) {\n\t\t\t\tcase 'left':\n\t\t\t\tcase 'up': {\n\t\t\t\t\tthis.cursor = this.cursor === 0 ? this.options.length - 1 : this.cursor - 1;\n\t\t\t\t\tconst currentIsGroup = this.options[this.cursor]?.group === true;\n\t\t\t\t\tif (!this.#selectableGroups && currentIsGroup) {\n\t\t\t\t\t\tthis.cursor = this.cursor === 0 ? this.options.length - 1 : this.cursor - 1;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase 'down':\n\t\t\t\tcase 'right': {\n\t\t\t\t\tthis.cursor = this.cursor === this.options.length - 1 ? 0 : this.cursor + 1;\n\t\t\t\t\tconst currentIsGroup = this.options[this.cursor]?.group === true;\n\t\t\t\t\tif (!this.#selectableGroups && currentIsGroup) {\n\t\t\t\t\t\tthis.cursor = this.cursor === this.options.length - 1 ? 0 : this.cursor + 1;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase 'space':\n\t\t\t\t\tthis.toggleValue();\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t});\n\t}\n}\n","import type { Key } from 'node:readline';\nimport { styleText } from 'node:util';\nimport { findTextCursor } from '../utils/cursor.js';\nimport { type Action, settings } from '../utils/index.js';\nimport Prompt, { type PromptOptions } from './prompt.js';\n\nexport interface MultiLineOptions extends PromptOptions<string, MultiLinePrompt> {\n\tplaceholder?: string;\n\tdefaultValue?: string;\n\tshowSubmit?: boolean;\n}\n\nexport default class MultiLinePrompt extends Prompt<string> {\n\t#lastKeyWasReturn = false;\n\t#showSubmit: boolean;\n\tpublic focused: 'editor' | 'submit' = 'editor';\n\n\tget userInputWithCursor() {\n\t\tif (this.state === 'submit') {\n\t\t\treturn this.userInput;\n\t\t}\n\t\tconst userInput = this.userInput;\n\t\tif (this.cursor >= userInput.length) {\n\t\t\treturn `${userInput}█`;\n\t\t}\n\t\tconst s1 = userInput.slice(0, this.cursor);\n\t\tconst s2 = userInput[this.cursor];\n\t\tconst s3 = userInput.slice(this.cursor + 1);\n\t\tif (s2 === '\\n') return `${s1}█\\n${s3}`;\n\t\treturn `${s1}${styleText('inverse', s2)}${s3}`;\n\t}\n\tget cursor() {\n\t\treturn this._cursor;\n\t}\n\t#insertAtCursor(char: string) {\n\t\tif (this.userInput.length === 0) {\n\t\t\tthis._setUserInput(char);\n\t\t\treturn;\n\t\t}\n\t\tthis._setUserInput(\n\t\t\tthis.userInput.slice(0, this.cursor) + char + this.userInput.slice(this.cursor)\n\t\t);\n\t}\n\t#handleCursor(key?: Action) {\n\t\tconst text = this.value ?? '';\n\t\tswitch (key) {\n\t\t\tcase 'up':\n\t\t\t\tthis._cursor = findTextCursor(this._cursor, 0, -1, text);\n\t\t\t\treturn;\n\t\t\tcase 'down':\n\t\t\t\tthis._cursor = findTextCursor(this._cursor, 0, 1, text);\n\t\t\t\treturn;\n\t\t\tcase 'left':\n\t\t\t\tthis._cursor = findTextCursor(this._cursor, -1, 0, text);\n\t\t\t\treturn;\n\t\t\tcase 'right':\n\t\t\t\tthis._cursor = findTextCursor(this._cursor, 1, 0, text);\n\t\t\t\treturn;\n\t\t}\n\t}\n\n\tprotected override _shouldSubmit(_char: string | undefined, _key: Key): boolean {\n\t\tif (this.#showSubmit) {\n\t\t\tif (this.focused === 'submit') {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tthis.#insertAtCursor('\\n');\n\t\t\tthis._cursor++;\n\t\t\treturn false;\n\t\t}\n\t\tconst wasReturn = this.#lastKeyWasReturn;\n\t\tthis.#lastKeyWasReturn = true;\n\t\tif (wasReturn) {\n\t\t\tif (this.userInput[this.cursor - 1] === '\\n') {\n\t\t\t\tthis._setUserInput(\n\t\t\t\t\tthis.userInput.slice(0, this.cursor - 1) + this.userInput.slice(this.cursor)\n\t\t\t\t);\n\t\t\t\tthis._cursor--;\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\tthis.#insertAtCursor('\\n');\n\t\tthis._cursor++;\n\t\treturn false;\n\t}\n\n\tconstructor(opts: MultiLineOptions) {\n\t\tsuper(opts, false);\n\t\tthis.#showSubmit = opts.showSubmit ?? false;\n\n\t\tthis.on('key', (char, key) => {\n\t\t\tif (key?.name && settings.actions.has(key.name as Action)) {\n\t\t\t\tthis.#handleCursor(key.name as Action);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (char === '\\t' && this.#showSubmit) {\n\t\t\t\tthis.focused = this.focused === 'editor' ? 'submit' : 'editor';\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (key?.name === 'return') {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthis.#lastKeyWasReturn = false;\n\t\t\tif (key?.name === 'backspace' && this.cursor > 0) {\n\t\t\t\tthis._setUserInput(\n\t\t\t\t\tthis.userInput.slice(0, this.cursor - 1) + this.userInput.slice(this.cursor)\n\t\t\t\t);\n\t\t\t\tthis._cursor--;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (key?.name === 'delete' && this.cursor < this.userInput.length) {\n\t\t\t\tthis._setUserInput(\n\t\t\t\t\tthis.userInput.slice(0, this.cursor) + this.userInput.slice(this.cursor + 1)\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (char) {\n\t\t\t\tif (this.#showSubmit && this.focused === 'submit') {\n\t\t\t\t\tthis.focused = 'editor';\n\t\t\t\t}\n\t\t\t\tthis.#insertAtCursor(char ?? '');\n\t\t\t\tthis._cursor++;\n\t\t\t}\n\t\t});\n\n\t\tthis.on('userInput', (input) => {\n\t\t\tthis._setValue(input);\n\t\t});\n\t\tthis.on('finalize', () => {\n\t\t\tif (!this.value) {\n\t\t\t\tthis.value = opts.defaultValue;\n\t\t\t}\n\t\t\tif (this.value === undefined) {\n\t\t\t\tthis.value = '';\n\t\t\t}\n\t\t});\n\t}\n}\n","import { findCursor } from '../utils/cursor.js';\nimport Prompt, { type PromptOptions } from './prompt.js';\n\ninterface OptionLike {\n\tvalue: any;\n\tdisabled?: boolean;\n}\n\nexport interface MultiSelectOptions<T extends OptionLike>\n\textends PromptOptions<T['value'][], MultiSelectPrompt<T>> {\n\toptions: T[];\n\tinitialValues?: T['value'][];\n\trequired?: boolean;\n\tcursorAt?: T['value'];\n}\nexport default class MultiSelectPrompt<T extends OptionLike> extends Prompt<T['value'][]> {\n\toptions: T[];\n\tcursor = 0;\n\n\tprivate get _value(): T['value'] {\n\t\treturn this.options[this.cursor].value;\n\t}\n\n\tprivate get _enabledOptions(): T[] {\n\t\treturn this.options.filter((option) => option.disabled !== true);\n\t}\n\n\tprivate toggleAll() {\n\t\tconst enabledOptions = this._enabledOptions;\n\t\tconst allSelected = this.value !== undefined && this.value.length === enabledOptions.length;\n\t\tthis.value = allSelected ? [] : enabledOptions.map((v) => v.value);\n\t}\n\n\tprivate toggleInvert() {\n\t\tconst value = this.value;\n\t\tif (!value) {\n\t\t\treturn;\n\t\t}\n\t\tconst notSelected = this._enabledOptions.filter((v) => !value.includes(v.value));\n\t\tthis.value = notSelected.map((v) => v.value);\n\t}\n\n\tprivate toggleValue() {\n\t\tif (this.value === undefined) {\n\t\t\tthis.value = [];\n\t\t}\n\t\tconst selected = this.value.includes(this._value);\n\t\tthis.value = selected\n\t\t\t? this.value.filter((value) => value !== this._value)\n\t\t\t: [...this.value, this._value];\n\t}\n\n\tconstructor(opts: MultiSelectOptions<T>) {\n\t\tsuper(opts, false);\n\n\t\tthis.options = opts.options;\n\t\tthis.value = [...(opts.initialValues ?? [])];\n\t\tconst cursor = Math.max(\n\t\t\tthis.options.findIndex(({ value }) => value === opts.cursorAt),\n\t\t\t0\n\t\t);\n\t\tthis.cursor = this.options[cursor].disabled ? findCursor<T>(cursor, 1, this.options) : cursor;\n\t\tthis.on('key', (char) => {\n\t\t\tif (char === 'a') {\n\t\t\t\tthis.toggleAll();\n\t\t\t}\n\t\t\tif (char === 'i') {\n\t\t\t\tthis.toggleInvert();\n\t\t\t}\n\t\t});\n\n\t\tthis.on('cursor', (key) => {\n\t\t\tswitch (key) {\n\t\t\t\tcase 'left':\n\t\t\t\tcase 'up':\n\t\t\t\t\tthis.cursor = findCursor<T>(this.cursor, -1, this.options);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'down':\n\t\t\t\tcase 'right':\n\t\t\t\t\tthis.cursor = findCursor<T>(this.cursor, 1, this.options);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'space':\n\t\t\t\t\tthis.toggleValue();\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t});\n\t}\n}\n","import { styleText } from 'node:util';\nimport Prompt, { type PromptOptions } from './prompt.js';\n\nexport interface PasswordOptions extends PromptOptions<string, PasswordPrompt> {\n\tmask?: string;\n}\nexport default class PasswordPrompt extends Prompt<string> {\n\tprivate _mask = '•';\n\tget cursor() {\n\t\treturn this._cursor;\n\t}\n\tget masked() {\n\t\treturn this.userInput.replaceAll(/./g, this._mask);\n\t}\n\tget userInputWithCursor() {\n\t\tif (this.state === 'submit' || this.state === 'cancel') {\n\t\t\treturn this.masked;\n\t\t}\n\t\tconst userInput = this.userInput;\n\t\tif (this.cursor >= userInput.length) {\n\t\t\treturn `${this.masked}${styleText(['inverse', 'hidden'], '_')}`;\n\t\t}\n\t\tconst masked = this.masked;\n\t\tconst s1 = masked.slice(0, this.cursor);\n\t\tconst s2 = masked.slice(this.cursor);\n\t\treturn `${s1}${styleText('inverse', s2[0])}${s2.slice(1)}`;\n\t}\n\tclear() {\n\t\tthis._clearUserInput();\n\t}\n\tconstructor({ mask, ...opts }: PasswordOptions) {\n\t\tsuper(opts);\n\t\tthis._mask = mask ?? '•';\n\t\tthis.on('userInput', (input) => {\n\t\t\tthis._setValue(input);\n\t\t});\n\t}\n}\n","import { findCursor } from '../utils/cursor.js';\nimport Prompt, { type PromptOptions } from './prompt.js';\n\nexport interface SelectOptions<T extends { value: any; disabled?: boolean }>\n\textends PromptOptions<T['value'], SelectPrompt<T>> {\n\toptions: T[];\n\tinitialValue?: T['value'];\n}\nexport default class SelectPrompt<T extends { value: any; disabled?: boolean }> extends Prompt<\n\tT['value']\n> {\n\toptions: T[];\n\tcursor = 0;\n\n\tprivate get _selectedValue() {\n\t\treturn this.options[this.cursor];\n\t}\n\n\tprivate changeValue() {\n\t\tthis.value = this._selectedValue.value;\n\t}\n\n\tconstructor(opts: SelectOptions<T>) {\n\t\tsuper(opts, false);\n\n\t\tthis.options = opts.options;\n\n\t\tconst initialCursor = this.options.findIndex(({ value }) => value === opts.initialValue);\n\t\tconst cursor = initialCursor === -1 ? 0 : initialCursor;\n\t\tthis.cursor = this.options[cursor].disabled ? findCursor<T>(cursor, 1, this.options) : cursor;\n\t\tthis.changeValue();\n\n\t\tthis.on('cursor', (key) => {\n\t\t\tswitch (key) {\n\t\t\t\tcase 'left':\n\t\t\t\tcase 'up':\n\t\t\t\t\tthis.cursor = findCursor<T>(this.cursor, -1, this.options);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'down':\n\t\t\t\tcase 'right':\n\t\t\t\t\tthis.cursor = findCursor<T>(this.cursor, 1, this.options);\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tthis.changeValue();\n\t\t});\n\t}\n}\n","import Prompt, { type PromptOptions } from './prompt.js';\n\nexport interface SelectKeyOptions<T extends { value: string }>\n\textends PromptOptions<T['value'], SelectKeyPrompt<T>> {\n\toptions: T[];\n\tcaseSensitive?: boolean;\n}\nexport default class SelectKeyPrompt<T extends { value: string }> extends Prompt<T['value']> {\n\toptions: T[];\n\tcursor = 0;\n\n\tconstructor(opts: SelectKeyOptions<T>) {\n\t\tsuper(opts, false);\n\n\t\tthis.options = opts.options;\n\t\tconst caseSensitive = opts.caseSensitive === true;\n\t\tconst keys = this.options.map(({ value: [initial] }) => {\n\t\t\treturn caseSensitive ? initial : initial?.toLowerCase();\n\t\t});\n\t\tthis.cursor = Math.max(keys.indexOf(opts.initialValue), 0);\n\n\t\tthis.on('key', (key, keyInfo) => {\n\t\t\tif (!key) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst casedKey = caseSensitive && keyInfo.shift ? key.toUpperCase() : key;\n\t\t\tif (!keys.includes(casedKey)) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst value = this.options.find(({ value: [initial] }) => {\n\t\t\t\treturn caseSensitive ? initial === casedKey : initial?.toLowerCase() === key;\n\t\t\t});\n\t\t\tif (value) {\n\t\t\t\tthis.value = value.value;\n\t\t\t\tthis.state = 'submit';\n\t\t\t\tthis.emit('submit');\n\t\t\t}\n\t\t});\n\t}\n}\n","import { styleText } from 'node:util';\nimport Prompt, { type PromptOptions } from './prompt.js';\n\nexport interface TextOptions extends PromptOptions<string, TextPrompt> {\n\tplaceholder?: string;\n\tdefaultValue?: string;\n}\n\nexport default class TextPrompt extends Prompt<string> {\n\tget userInputWithCursor() {\n\t\tif (this.state === 'submit') {\n\t\t\treturn this.userInput;\n\t\t}\n\t\tconst userInput = this.userInput;\n\t\tif (this.cursor >= userInput.length) {\n\t\t\treturn `${this.userInput}█`;\n\t\t}\n\t\tconst s1 = userInput.slice(0, this.cursor);\n\t\tconst [s2, ...s3] = userInput.slice(this.cursor);\n\t\treturn `${s1}${styleText('inverse', s2)}${s3.join('')}`;\n\t}\n\tget cursor() {\n\t\treturn this._cursor;\n\t}\n\tconstructor(opts: TextOptions) {\n\t\tsuper({\n\t\t\t...opts,\n\t\t\tinitialUserInput: opts.initialUserInput ?? opts.initialValue,\n\t\t});\n\n\t\tthis.on('userInput', (input) => {\n\t\t\tthis._setValue(input);\n\t\t});\n\t\tthis.on('finalize', () => {\n\t\t\tif (!this.value) {\n\t\t\t\tthis.value = opts.defaultValue;\n\t\t\t}\n\t\t\tif (this.value === undefined) {\n\t\t\t\tthis.value = '';\n\t\t\t}\n\t\t});\n\t}\n}\n","import process from 'node:process';\n\nexport default function isUnicodeSupported() {\n\tif (process.platform !== 'win32') {\n\t\treturn process.env.TERM !== 'linux'; // Linux console (kernel)\n\t}\n\n\treturn Boolean(process.env.CI)\n\t\t|| Boolean(process.env.WT_SESSION) // Windows Terminal\n\t\t|| Boolean(process.env.TERMINUS_SUBLIME) // Terminus (<0.2.27)\n\t\t|| process.env.ConEmuTask === '{cmd::Cmder}' // ConEmu and cmder\n\t\t|| process.env.TERM_PROGRAM === 'Terminus-Sublime'\n\t\t|| process.env.TERM_PROGRAM === 'vscode'\n\t\t|| process.env.TERM === 'xterm-256color'\n\t\t|| process.env.TERM === 'alacritty'\n\t\t|| process.env.TERMINAL_EMULATOR === 'JetBrains-JediTerm';\n}\n","import type { Readable, Writable } from 'node:stream';\nimport { styleText } from 'node:util';\nimport type { State } from '@clack/core';\nimport isUnicodeSupported from 'is-unicode-supported';\n\nexport const unicode = isUnicodeSupported();\nexport const isCI = (): boolean => process.env.CI === 'true';\nexport const isTTY = (output: Writable): boolean => {\n\treturn (output as Writable & { isTTY?: boolean }).isTTY === true;\n};\nexport const unicodeOr = (c: string, fallback: string) => (unicode ? c : fallback);\nexport const S_STEP_ACTIVE = unicodeOr('◆', '*');\nexport const S_STEP_CANCEL = unicodeOr('■', 'x');\nexport const S_STEP_ERROR = unicodeOr('▲', 'x');\nexport const S_STEP_SUBMIT = unicodeOr('◇', 'o');\n\nexport const S_BAR_START = unicodeOr('┌', 'T');\nexport const S_BAR = unicodeOr('│', '|');\nexport const S_BAR_END = unicodeOr('└', '—');\nexport const S_BAR_START_RIGHT = unicodeOr('┐', 'T');\nexport const S_BAR_END_RIGHT = unicodeOr('┘', '—');\n\nexport const S_RADIO_ACTIVE = unicodeOr('●', '>');\nexport const S_RADIO_INACTIVE = unicodeOr('○', ' ');\nexport const S_CHECKBOX_ACTIVE = unicodeOr('◻', '[•]');\nexport const S_CHECKBOX_SELECTED = unicodeOr('◼', '[+]');\nexport const S_CHECKBOX_INACTIVE = unicodeOr('◻', '[ ]');\nexport const S_PASSWORD_MASK = unicodeOr('▪', '•');\n\nexport const S_BAR_H = unicodeOr('─', '-');\nexport const S_CORNER_TOP_RIGHT = unicodeOr('╮', '+');\nexport const S_CONNECT_LEFT = unicodeOr('├', '+');\nexport const S_CORNER_BOTTOM_RIGHT = unicodeOr('╯', '+');\nexport const S_CORNER_BOTTOM_LEFT = unicodeOr('╰', '+');\nexport const S_CORNER_TOP_LEFT = unicodeOr('╭', '+');\n\nexport const S_INFO = unicodeOr('●', '•');\nexport const S_SUCCESS = unicodeOr('◆', '*');\nexport const S_WARN = unicodeOr('▲', '!');\nexport const S_ERROR = unicodeOr('■', 'x');\n\nexport const symbol = (state: State) => {\n\tswitch (state) {\n\t\tcase 'initial':\n\t\tcase 'active':\n\t\t\treturn styleText('cyan', S_STEP_ACTIVE);\n\t\tcase 'cancel':\n\t\t\treturn styleText('red', S_STEP_CANCEL);\n\t\tcase 'error':\n\t\t\treturn styleText('yellow', S_STEP_ERROR);\n\t\tcase 'submit':\n\t\t\treturn styleText('green', S_STEP_SUBMIT);\n\t}\n};\n\nexport const symbolBar = (state: State) => {\n\tswitch (state) {\n\t\tcase 'initial':\n\t\tcase 'active':\n\t\t\treturn styleText('cyan', S_BAR);\n\t\tcase 'cancel':\n\t\t\treturn styleText('red', S_BAR);\n\t\tcase 'error':\n\t\t\treturn styleText('yellow', S_BAR);\n\t\tcase 'submit':\n\t\t\treturn styleText('green', S_BAR);\n\t}\n};\n\nexport interface CommonOptions {\n\tinput?: Readable;\n\toutput?: Writable;\n\tsignal?: AbortSignal;\n\twithGuide?: boolean;\n}\n","import { styleText } from 'node:util';\nimport { getColumns, getRows } from '@clack/core';\nimport { wrapAnsi } from 'fast-wrap-ansi';\nimport type { CommonOptions } from './common.js';\n\nexport interface LimitOptionsParams<TOption> extends CommonOptions {\n\toptions: TOption[];\n\tcursor: number;\n\tstyle: (option: TOption, active: boolean) => string;\n\tmaxItems?: number;\n\tcolumnPadding?: number;\n\trowPadding?: number;\n}\n\nconst trimLines = (\n\tgroups: Array<string[]>,\n\tinitialLineCount: number,\n\tstartIndex: number,\n\tendIndex: number,\n\tmaxLines: number\n) => {\n\tlet lineCount = initialLineCount;\n\tlet removals = 0;\n\tfor (let i = startIndex; i < endIndex; i++) {\n\t\tconst group = groups[i];\n\t\tlineCount = lineCount - group.length;\n\t\tremovals++;\n\t\tif (lineCount <= maxLines) {\n\t\t\tbreak;\n\t\t}\n\t}\n\treturn { lineCount, removals };\n};\n\nexport const limitOptions = <TOption>({\n\tcursor,\n\toptions,\n\tstyle,\n\toutput = process.stdout,\n\tmaxItems = Number.POSITIVE_INFINITY,\n\tcolumnPadding = 0,\n\trowPadding = 4,\n}: LimitOptionsParams<TOption>): string[] => {\n\tconst columns = getColumns(output);\n\tconst maxWidth = columns - columnPadding;\n\tconst rows = getRows(output);\n\tconst overflowFormat = styleText('dim', '...');\n\n\tconst outputMaxItems = Math.max(rows - rowPadding, 0);\n\t// We clamp to minimum 5 because anything less doesn't make sense UX wise\n\tconst computedMaxItems = Math.max(Math.min(maxItems, outputMaxItems), 5);\n\tlet slidingWindowLocation = 0;\n\n\tif (cursor >= computedMaxItems - 3) {\n\t\tslidingWindowLocation = Math.max(\n\t\t\tMath.min(cursor - computedMaxItems + 3, options.length - computedMaxItems),\n\t\t\t0\n\t\t);\n\t}\n\n\tlet shouldRenderTopEllipsis = computedMaxItems < options.length && slidingWindowLocation > 0;\n\tlet shouldRenderBottomEllipsis =\n\t\tcomputedMaxItems < options.length && slidingWindowLocation + computedMaxItems < options.length;\n\n\tconst slidingWindowLocationEnd = Math.min(\n\t\tslidingWindowLocation + computedMaxItems,\n\t\toptions.length\n\t);\n\tconst lineGroups: Array<string[]> = [];\n\tlet lineCount = 0;\n\tif (shouldRenderTopEllipsis) {\n\t\tlineCount++;\n\t}\n\tif (shouldRenderBottomEllipsis) {\n\t\tlineCount++;\n\t}\n\n\tconst slidingWindowLocationWithEllipsis =\n\t\tslidingWindowLocation + (shouldRenderTopEllipsis ? 1 : 0);\n\tconst slidingWindowLocationEndWithEllipsis =\n\t\tslidingWindowLocationEnd - (shouldRenderBottomEllipsis ? 1 : 0);\n\n\tfor (let i = slidingWindowLocationWithEllipsis; i < slidingWindowLocationEndWithEllipsis; i++) {\n\t\tconst wrappedLines = wrapAnsi(style(options[i], i === cursor), maxWidth, {\n\t\t\thard: true,\n\t\t\ttrim: false,\n\t\t}).split('\\n');\n\t\tlineGroups.push(wrappedLines);\n\t\tlineCount += wrappedLines.length;\n\t}\n\n\tif (lineCount > outputMaxItems) {\n\t\tlet precedingRemovals = 0;\n\t\tlet followingRemovals = 0;\n\t\tlet newLineCount = lineCount;\n\t\tconst cursorGroupIndex = cursor - slidingWindowLocationWithEllipsis;\n\t\tconst trimLinesLocal = (startIndex: number, endIndex: number) =>\n\t\t\ttrimLines(lineGroups, newLineCount, startIndex, endIndex, outputMaxItems);\n\n\t\tif (shouldRenderTopEllipsis) {\n\t\t\t({ lineCount: newLineCount, removals: precedingRemovals } = trimLinesLocal(\n\t\t\t\t0,\n\t\t\t\tcursorGroupIndex\n\t\t\t));\n\t\t\tif (newLineCount > outputMaxItems) {\n\t\t\t\t({ lineCount: newLineCount, removals: followingRemovals } = trimLinesLocal(\n\t\t\t\t\tcursorGroupIndex + 1,\n\t\t\t\t\tlineGroups.length\n\t\t\t\t));\n\t\t\t}\n\t\t} else {\n\t\t\t({ lineCount: newLineCount, removals: followingRemovals } = trimLinesLocal(\n\t\t\t\tcursorGroupIndex + 1,\n\t\t\t\tlineGroups.length\n\t\t\t));\n\t\t\tif (newLineCount > outputMaxItems) {\n\t\t\t\t({ lineCount: newLineCount, removals: precedingRemovals } = trimLinesLocal(\n\t\t\t\t\t0,\n\t\t\t\t\tcursorGroupIndex\n\t\t\t\t));\n\t\t\t}\n\t\t}\n\n\t\tif (precedingRemovals > 0) {\n\t\t\tshouldRenderTopEllipsis = true;\n\t\t\tlineGroups.splice(0, precedingRemovals);\n\t\t}\n\t\tif (followingRemovals > 0) {\n\t\t\tshouldRenderBottomEllipsis = true;\n\t\t\tlineGroups.splice(lineGroups.length - followingRemovals, followingRemovals);\n\t\t}\n\t}\n\n\tconst result: string[] = [];\n\tif (shouldRenderTopEllipsis) {\n\t\tresult.push(overflowFormat);\n\t}\n\tfor (const lineGroup of lineGroups) {\n\t\tfor (const line of lineGroup) {\n\t\t\tresult.push(line);\n\t\t}\n\t}\n\tif (shouldRenderBottomEllipsis) {\n\t\tresult.push(overflowFormat);\n\t}\n\n\treturn result;\n};\n","import { styleText } from 'node:util';\nimport { AutocompletePrompt, settings } from '@clack/core';\nimport {\n\ttype CommonOptions,\n\tS_BAR,\n\tS_BAR_END,\n\tS_CHECKBOX_INACTIVE,\n\tS_CHECKBOX_SELECTED,\n\tS_RADIO_ACTIVE,\n\tS_RADIO_INACTIVE,\n\tsymbol,\n} from './common.js';\nimport { limitOptions } from './limit-options.js';\nimport type { Option } from './select.js';\n\nfunction getLabel<T>(option: Option<T>) {\n\treturn option.label ?? String(option.value ?? '');\n}\n\nfunction getFilteredOption<T>(searchText: string, option: Option<T>): boolean {\n\tif (!searchText) {\n\t\treturn true;\n\t}\n\tconst label = (option.label ?? String(option.value ?? '')).toLowerCase();\n\tconst hint = (option.hint ?? '').toLowerCase();\n\tconst value = String(option.value).toLowerCase();\n\tconst term = searchText.toLowerCase();\n\n\treturn label.includes(term) || hint.includes(term) || value.includes(term);\n}\n\nfunction getSelectedOptions<T>(values: T[], options: Option<T>[]): Option<T>[] {\n\tconst results: Option<T>[] = [];\n\n\tfor (const option of options) {\n\t\tif (values.includes(option.value)) {\n\t\t\tresults.push(option);\n\t\t}\n\t}\n\n\treturn results;\n}\n\ninterface AutocompleteSharedOptions<Value> extends CommonOptions {\n\t/**\n\t * The message to display to the user.\n\t */\n\tmessage: string;\n\t/**\n\t * Available options for the autocomplete prompt.\n\t */\n\toptions: Option<Value>[] | ((this: AutocompletePrompt<Option<Value>>) => Option<Value>[]);\n\t/**\n\t * Maximum number of items to display at once.\n\t */\n\tmaxItems?: number;\n\t/**\n\t * Placeholder text to display when no input is provided.\n\t */\n\tplaceholder?: string;\n\t/**\n\t * Validates the value\n\t */\n\tvalidate?: (value: Value | Value[] | undefined) => string | Error | undefined;\n\t/**\n\t * Custom filter function to match options against search input.\n\t * If not provided, a default filter that matches label, hint, and value is used.\n\t */\n\tfilter?: (search: string, option: Option<Value>) => boolean;\n}\n\nexport interface AutocompleteOptions<Value> extends AutocompleteSharedOptions<Value> {\n\t/**\n\t * The initial selected value.\n\t */\n\tinitialValue?: Value;\n\t/**\n\t * The initial user input\n\t */\n\tinitialUserInput?: string;\n}\n\nexport const autocomplete = <Value>(opts: AutocompleteOptions<Value>) => {\n\tconst prompt = new AutocompletePrompt({\n\t\toptions: opts.options,\n\t\tinitialValue: opts.initialValue ? [opts.initialValue] : undefined,\n\t\tinitialUserInput: opts.initialUserInput,\n\t\tplaceholder: opts.placeholder,\n\t\tfilter:\n\t\t\topts.filter ??\n\t\t\t((search: string, opt: Option<Value>) => {\n\t\t\t\treturn getFilteredOption(search, opt);\n\t\t\t}),\n\t\tsignal: opts.signal,\n\t\tinput: opts.input,\n\t\toutput: opts.output,\n\t\tvalidate: opts.validate,\n\t\trender() {\n\t\t\tconst hasGuide = opts.withGuide ?? settings.withGuide;\n\t\t\t// Title and message display\n\t\t\tconst headings = hasGuide\n\t\t\t\t? [`${styleText('gray', S_BAR)}`, `${symbol(this.state)} ${opts.message}`]\n\t\t\t\t: [`${symbol(this.state)} ${opts.message}`];\n\t\t\tconst userInput = this.userInput;\n\t\t\tconst options = this.options;\n\t\t\tconst placeholder = opts.placeholder;\n\t\t\tconst showPlaceholder = userInput === '' && placeholder !== undefined;\n\t\t\tconst opt = (option: Option<Value>, state: 'inactive' | 'active' | 'disabled') => {\n\t\t\t\tconst label = getLabel(option);\n\t\t\t\tconst hint =\n\t\t\t\t\toption.hint && option.value === this.focusedValue\n\t\t\t\t\t\t? styleText('dim', ` (${option.hint})`)\n\t\t\t\t\t\t: '';\n\t\t\t\tswitch (state) {\n\t\t\t\t\tcase 'active':\n\t\t\t\t\t\treturn `${styleText('green', S_RADIO_ACTIVE)} ${label}${hint}`;\n\t\t\t\t\tcase 'inactive':\n\t\t\t\t\t\treturn `${styleText('dim', S_RADIO_INACTIVE)} ${styleText('dim', label)}`;\n\t\t\t\t\tcase 'disabled':\n\t\t\t\t\t\treturn `${styleText('gray', S_RADIO_INACTIVE)} ${styleText(['strikethrough', 'gray'], label)}`;\n\t\t\t\t}\n\t\t\t};\n\n\t\t\t// Handle different states\n\t\t\tswitch (this.state) {\n\t\t\t\tcase 'submit': {\n\t\t\t\t\t// Show selected value\n\t\t\t\t\tconst selected = getSelectedOptions(this.selectedValues, options);\n\t\t\t\t\tconst label =\n\t\t\t\t\t\tselected.length > 0 ? ` ${styleText('dim', selected.map(getLabel).join(', '))}` : '';\n\t\t\t\t\tconst submitPrefix = hasGuide ? styleText('gray', S_BAR) : '';\n\t\t\t\t\treturn `${headings.join('\\n')}\\n${submitPrefix}${label}`;\n\t\t\t\t}\n\n\t\t\t\tcase 'cancel': {\n\t\t\t\t\tconst userInputText = userInput\n\t\t\t\t\t\t? ` ${styleText(['strikethrough', 'dim'], userInput)}`\n\t\t\t\t\t\t: '';\n\t\t\t\t\tconst cancelPrefix = hasGuide ? styleText('gray', S_BAR) : '';\n\t\t\t\t\treturn `${headings.join('\\n')}\\n${cancelPrefix}${userInputText}`;\n\t\t\t\t}\n\n\t\t\t\tdefault: {\n\t\t\t\t\tconst barStyle = this.state === 'error' ? 'yellow' : 'cyan';\n\t\t\t\t\tconst guidePrefix = hasGuide ? `${styleText(barStyle, S_BAR)} ` : '';\n\t\t\t\t\tconst guidePrefixEnd = hasGuide ? styleText(barStyle, S_BAR_END) : '';\n\t\t\t\t\t// Display cursor position - show plain text in navigation mode\n\t\t\t\t\tlet searchText = '';\n\t\t\t\t\tif (this.isNavigating || showPlaceholder) {\n\t\t\t\t\t\tconst searchTextValue = showPlaceholder ? placeholder : userInput;\n\t\t\t\t\t\tsearchText = searchTextValue !== '' ? ` ${styleText('dim', searchTextValue)}` : '';\n\t\t\t\t\t} else {\n\t\t\t\t\t\tsearchText = ` ${this.userInputWithCursor}`;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Show match count if filtered\n\t\t\t\t\tconst matches =\n\t\t\t\t\t\tthis.filteredOptions.length !== options.length\n\t\t\t\t\t\t\t? styleText(\n\t\t\t\t\t\t\t\t\t'dim',\n\t\t\t\t\t\t\t\t\t` (${this.filteredOptions.length} match${this.filteredOptions.length === 1 ? '' : 'es'})`\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t: '';\n\n\t\t\t\t\t// No matches message\n\t\t\t\t\tconst noResults =\n\t\t\t\t\t\tthis.filteredOptions.length === 0 && userInput\n\t\t\t\t\t\t\t? [`${guidePrefix}${styleText('yellow', 'No matches found')}`]\n\t\t\t\t\t\t\t: [];\n\n\t\t\t\t\tconst validationError =\n\t\t\t\t\t\tthis.state === 'error' ? [`${guidePrefix}${styleText('yellow', this.error)}`] : [];\n\n\t\t\t\t\tif (hasGuide) {\n\t\t\t\t\t\theadings.push(`${guidePrefix.trimEnd()}`);\n\t\t\t\t\t}\n\t\t\t\t\theadings.push(\n\t\t\t\t\t\t`${guidePrefix}${styleText('dim', 'Search:')}${searchText}${matches}`,\n\t\t\t\t\t\t...noResults,\n\t\t\t\t\t\t...validationError\n\t\t\t\t\t);\n\n\t\t\t\t\t// Show instructions\n\t\t\t\t\tconst instructions = [\n\t\t\t\t\t\t`${styleText('dim', '↑/↓')} to select`,\n\t\t\t\t\t\t`${styleText('dim', 'Enter:')} confirm`,\n\t\t\t\t\t\t`${styleText('dim', 'Type:')} to search`,\n\t\t\t\t\t];\n\n\t\t\t\t\tconst footers = [`${guidePrefix}${instructions.join(' • ')}`, guidePrefixEnd];\n\n\t\t\t\t\t// Render options with selection\n\t\t\t\t\tconst displayOptions =\n\t\t\t\t\t\tthis.filteredOptions.length === 0\n\t\t\t\t\t\t\t? []\n\t\t\t\t\t\t\t: limitOptions({\n\t\t\t\t\t\t\t\t\tcursor: this.cursor,\n\t\t\t\t\t\t\t\t\toptions: this.filteredOptions,\n\t\t\t\t\t\t\t\t\tcolumnPadding: hasGuide ? 3 : 0, // for `| ` when guide is shown\n\t\t\t\t\t\t\t\t\trowPadding: headings.length + footers.length,\n\t\t\t\t\t\t\t\t\tstyle: (option, active) => {\n\t\t\t\t\t\t\t\t\t\treturn opt(\n\t\t\t\t\t\t\t\t\t\t\toption,\n\t\t\t\t\t\t\t\t\t\t\toption.disabled ? 'disabled' : active ? 'active' : 'inactive'\n\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\tmaxItems: opts.maxItems,\n\t\t\t\t\t\t\t\t\toutput: opts.output,\n\t\t\t\t\t\t\t\t});\n\n\t\t\t\t\t// Return the formatted prompt\n\t\t\t\t\treturn [\n\t\t\t\t\t\t...headings,\n\t\t\t\t\t\t...displayOptions.map((option) => `${guidePrefix}${option}`),\n\t\t\t\t\t\t...footers,\n\t\t\t\t\t].join('\\n');\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t});\n\n\t// Return the result or cancel symbol\n\treturn prompt.prompt() as Promise<Value | symbol>;\n};\n\n// Type definition for the autocompleteMultiselect component\nexport interface AutocompleteMultiSelectOptions<Value> extends AutocompleteSharedOptions<Value> {\n\t/**\n\t * The initial selected values\n\t */\n\tinitialValues?: Value[];\n\t/**\n\t * If true, at least one option must be selected\n\t */\n\trequired?: boolean;\n}\n\n/**\n * Integrated autocomplete multiselect - combines type-ahead filtering with multiselect in one UI\n */\nexport const autocompleteMultiselect = <Value>(opts: AutocompleteMultiSelectOptions<Value>) => {\n\tconst formatOption = (\n\t\toption: Option<Value>,\n\t\tactive: boolean,\n\t\tselectedValues: Value[],\n\t\tfocusedValue: Value | undefined\n\t) => {\n\t\tconst isSelected = selectedValues.includes(option.value);\n\t\tconst label = option.label ?? String(option.value ?? '');\n\t\tconst hint =\n\t\t\toption.hint && focusedValue !== undefined && option.value === focusedValue\n\t\t\t\t? styleText('dim', ` (${option.hint})`)\n\t\t\t\t: '';\n\t\tconst checkbox = isSelected\n\t\t\t? styleText('green', S_CHECKBOX_SELECTED)\n\t\t\t: styleText('dim', S_CHECKBOX_INACTIVE);\n\n\t\tif (option.disabled) {\n\t\t\treturn `${styleText('gray', S_CHECKBOX_INACTIVE)} ${styleText(['strikethrough', 'gray'], label)}`;\n\t\t}\n\t\tif (active) {\n\t\t\treturn `${checkbox} ${label}${hint}`;\n\t\t}\n\t\treturn `${checkbox} ${styleText('dim', label)}`;\n\t};\n\n\t// Create text prompt which we'll use as foundation\n\tconst prompt = new AutocompletePrompt<Option<Value>>({\n\t\toptions: opts.options,\n\t\tmultiple: true,\n\t\tplaceholder: opts.placeholder,\n\t\tfilter:\n\t\t\topts.filter ??\n\t\t\t((search, opt) => {\n\t\t\t\treturn getFilteredOption(search, opt);\n\t\t\t}),\n\t\tvalidate: () => {\n\t\t\tif (opts.required && prompt.selectedValues.length === 0) {\n\t\t\t\treturn 'Please select at least one item';\n\t\t\t}\n\t\t\treturn undefined;\n\t\t},\n\t\tinitialValue: opts.initialValues,\n\t\tsignal: opts.signal,\n\t\tinput: opts.input,\n\t\toutput: opts.output,\n\t\trender() {\n\t\t\tconst hasGuide = opts.withGuide ?? settings.withGuide;\n\t\t\t// Title and symbol\n\t\t\tconst title = `${hasGuide ? `${styleText('gray', S_BAR)}\\n` : ''}${symbol(this.state)} ${\n\t\t\t\topts.message\n\t\t\t}\\n`;\n\n\t\t\t// Selection counter\n\t\t\tconst userInput = this.userInput;\n\t\t\tconst placeholder = opts.placeholder;\n\t\t\tconst showPlaceholder = userInput === '' && placeholder !== undefined;\n\n\t\t\t// Search input display\n\t\t\tconst searchText =\n\t\t\t\tthis.isNavigating || showPlaceholder\n\t\t\t\t\t? styleText('dim', showPlaceholder ? placeholder : userInput) // Just show plain text when in navigation mode\n\t\t\t\t\t: this.userInputWithCursor;\n\n\t\t\tconst options = this.options;\n\n\t\t\tconst matches =\n\t\t\t\tthis.filteredOptions.length !== options.length\n\t\t\t\t\t? styleText(\n\t\t\t\t\t\t\t'dim',\n\t\t\t\t\t\t\t` (${this.filteredOptions.length} match${this.filteredOptions.length === 1 ? '' : 'es'})`\n\t\t\t\t\t\t)\n\t\t\t\t\t: '';\n\n\t\t\t// Render prompt state\n\t\t\tswitch (this.state) {\n\t\t\t\tcase 'submit': {\n\t\t\t\t\treturn `${title}${hasGuide ? `${styleText('gray', S_BAR)} ` : ''}${styleText(\n\t\t\t\t\t\t'dim',\n\t\t\t\t\t\t`${this.selectedValues.length} items selected`\n\t\t\t\t\t)}`;\n\t\t\t\t}\n\t\t\t\tcase 'cancel': {\n\t\t\t\t\treturn `${title}${hasGuide ? `${styleText('gray', S_BAR)} ` : ''}${styleText(\n\t\t\t\t\t\t['strikethrough', 'dim'],\n\t\t\t\t\t\tuserInput\n\t\t\t\t\t)}`;\n\t\t\t\t}\n\t\t\t\tdefault: {\n\t\t\t\t\tconst barStyle = this.state === 'error' ? 'yellow' : 'cyan';\n\t\t\t\t\tconst guidePrefix = hasGuide ? `${styleText(barStyle, S_BAR)} ` : '';\n\t\t\t\t\tconst guidePrefixEnd = hasGuide ? styleText(barStyle, S_BAR_END) : '';\n\t\t\t\t\t// Instructions\n\t\t\t\t\tconst instructions = [\n\t\t\t\t\t\t`${styleText('dim', '↑/↓')} to navigate`,\n\t\t\t\t\t\t`${styleText('dim', this.isNavigating ? 'Space/Tab:' : 'Tab:')} select`,\n\t\t\t\t\t\t`${styleText('dim', 'Enter:')} confirm`,\n\t\t\t\t\t\t`${styleText('dim', 'Type:')} to search`,\n\t\t\t\t\t];\n\n\t\t\t\t\t// No results message\n\t\t\t\t\tconst noResults =\n\t\t\t\t\t\tthis.filteredOptions.length === 0 && userInput\n\t\t\t\t\t\t\t? [`${guidePrefix}${styleText('yellow', 'No matches found')}`]\n\t\t\t\t\t\t\t: [];\n\n\t\t\t\t\tconst errorMessage =\n\t\t\t\t\t\tthis.state === 'error' ? [`${guidePrefix}${styleText('yellow', this.error)}`] : [];\n\n\t\t\t\t\t// Calculate header and footer line counts for rowPadding\n\t\t\t\t\tconst headerLines = [\n\t\t\t\t\t\t...`${title}${hasGuide ? styleText(barStyle, S_BAR) : ''}`.split('\\n'),\n\t\t\t\t\t\t`${guidePrefix}${styleText('dim', 'Search:')} ${searchText}${matches}`,\n\t\t\t\t\t\t...noResults,\n\t\t\t\t\t\t...errorMessage,\n\t\t\t\t\t];\n\t\t\t\t\tconst footerLines = [`${guidePrefix}${instructions.join(' • ')}`, guidePrefixEnd];\n\n\t\t\t\t\t// Get limited options for display\n\t\t\t\t\tconst displayOptions = limitOptions({\n\t\t\t\t\t\tcursor: this.cursor,\n\t\t\t\t\t\toptions: this.filteredOptions,\n\t\t\t\t\t\tstyle: (option, active) =>\n\t\t\t\t\t\t\tformatOption(option, active, this.selectedValues, this.focusedValue),\n\t\t\t\t\t\tmaxItems: opts.maxItems,\n\t\t\t\t\t\toutput: opts.output,\n\t\t\t\t\t\trowPadding: headerLines.length + footerLines.length,\n\t\t\t\t\t});\n\n\t\t\t\t\t// Build the prompt display\n\t\t\t\t\treturn [\n\t\t\t\t\t\t...headerLines,\n\t\t\t\t\t\t...displayOptions.map((option) => `${guidePrefix}${option}`),\n\t\t\t\t\t\t...footerLines,\n\t\t\t\t\t].join('\\n');\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t});\n\n\t// Return the result or cancel symbol\n\treturn prompt.prompt() as Promise<Value[] | symbol>;\n};\n","import type { Writable } from 'node:stream';\nimport { getColumns, settings } from '@clack/core';\nimport stringWidth from 'fast-string-width';\nimport { wrapAnsi } from 'fast-wrap-ansi';\nimport {\n\ttype CommonOptions,\n\tS_BAR,\n\tS_BAR_END,\n\tS_BAR_END_RIGHT,\n\tS_BAR_H,\n\tS_BAR_START,\n\tS_BAR_START_RIGHT,\n\tS_CORNER_BOTTOM_LEFT,\n\tS_CORNER_BOTTOM_RIGHT,\n\tS_CORNER_TOP_LEFT,\n\tS_CORNER_TOP_RIGHT,\n} from './common.js';\n\nexport type BoxAlignment = 'left' | 'center' | 'right';\n\ntype BoxSymbols = [topLeft: string, topRight: string, bottomLeft: string, bottomRight: string];\n\nconst roundedSymbols: BoxSymbols = [\n\tS_CORNER_TOP_LEFT,\n\tS_CORNER_TOP_RIGHT,\n\tS_CORNER_BOTTOM_LEFT,\n\tS_CORNER_BOTTOM_RIGHT,\n];\nconst squareSymbols: BoxSymbols = [S_BAR_START, S_BAR_START_RIGHT, S_BAR_END, S_BAR_END_RIGHT];\n\nexport interface BoxOptions extends CommonOptions {\n\tcontentAlign?: BoxAlignment;\n\ttitleAlign?: BoxAlignment;\n\twidth?: number | 'auto';\n\ttitlePadding?: number;\n\tcontentPadding?: number;\n\trounded?: boolean;\n\tformatBorder?: (text: string) => string;\n}\n\nfunction getPaddingForLine(\n\tlineLength: number,\n\tinnerWidth: number,\n\tpadding: number,\n\tcontentAlign: BoxAlignment | undefined\n): [number, number] {\n\tlet leftPadding = padding;\n\tlet rightPadding = padding;\n\tif (contentAlign === 'center') {\n\t\tleftPadding = Math.floor((innerWidth - lineLength) / 2);\n\t} else if (contentAlign === 'right') {\n\t\tleftPadding = innerWidth - lineLength - padding;\n\t}\n\n\trightPadding = innerWidth - leftPadding - lineLength;\n\n\treturn [leftPadding, rightPadding];\n}\n\nconst defaultFormatBorder = (text: string) => text;\n\nexport const box = (message = '', title = '', opts?: BoxOptions) => {\n\tconst output: Writable = opts?.output ?? process.stdout;\n\tconst columns = getColumns(output);\n\tconst borderWidth = 1;\n\tconst borderTotalWidth = borderWidth * 2;\n\tconst titlePadding = opts?.titlePadding ?? 1;\n\tconst contentPadding = opts?.contentPadding ?? 2;\n\tconst width = opts?.width === undefined || opts.width === 'auto' ? 1 : Math.min(1, opts.width);\n\tconst hasGuide = opts?.withGuide ?? settings.withGuide;\n\tconst linePrefix = !hasGuide ? '' : `${S_BAR} `;\n\tconst formatBorder = opts?.formatBorder ?? defaultFormatBorder;\n\tconst symbols = (opts?.rounded ? roundedSymbols : squareSymbols).map(formatBorder);\n\tconst hSymbol = formatBorder(S_BAR_H);\n\tconst vSymbol = formatBorder(S_BAR);\n\tconst linePrefixWidth = stringWidth(linePrefix);\n\tconst titleWidth = stringWidth(title);\n\tconst maxBoxWidth = columns - linePrefixWidth;\n\tlet boxWidth = Math.floor(columns * width) - linePrefixWidth;\n\tif (opts?.width === 'auto') {\n\t\tconst lines = message.split('\\n');\n\t\tlet longestLine = titleWidth + titlePadding * 2;\n\t\tfor (const line of lines) {\n\t\t\tconst lineWithPadding = stringWidth(line) + contentPadding * 2;\n\t\t\tif (lineWithPadding > longestLine) {\n\t\t\t\tlongestLine = lineWithPadding;\n\t\t\t}\n\t\t}\n\t\tconst longestLineWidth = longestLine + borderTotalWidth;\n\t\tif (longestLineWidth < boxWidth) {\n\t\t\tboxWidth = longestLineWidth;\n\t\t}\n\t}\n\tif (boxWidth % 2 !== 0) {\n\t\tif (boxWidth < maxBoxWidth) {\n\t\t\tboxWidth++;\n\t\t} else {\n\t\t\tboxWidth--;\n\t\t}\n\t}\n\tconst innerWidth = boxWidth - borderTotalWidth;\n\tconst maxTitleLength = innerWidth - titlePadding * 2;\n\tconst truncatedTitle =\n\t\ttitleWidth > maxTitleLength ? `${title.slice(0, maxTitleLength - 3)}...` : title;\n\tconst [titlePaddingLeft, titlePaddingRight] = getPaddingForLine(\n\t\tstringWidth(truncatedTitle),\n\t\tinnerWidth,\n\t\ttitlePadding,\n\t\topts?.titleAlign\n\t);\n\tconst wrappedMessage = wrapAnsi(message, innerWidth - contentPadding * 2, {\n\t\thard: true,\n\t\ttrim: false,\n\t});\n\toutput.write(\n\t\t`${linePrefix}${symbols[0]}${hSymbol.repeat(titlePaddingLeft)}${truncatedTitle}${hSymbol.repeat(titlePaddingRight)}${symbols[1]}\\n`\n\t);\n\tconst wrappedLines = wrappedMessage.split('\\n');\n\tfor (const line of wrappedLines) {\n\t\tconst [leftLinePadding, rightLinePadding] = getPaddingForLine(\n\t\t\tstringWidth(line),\n\t\t\tinnerWidth,\n\t\t\tcontentPadding,\n\t\t\topts?.contentAlign\n\t\t);\n\t\toutput.write(\n\t\t\t`${linePrefix}${vSymbol}${' '.repeat(leftLinePadding)}${line}${' '.repeat(rightLinePadding)}${vSymbol}\\n`\n\t\t);\n\t}\n\toutput.write(`${linePrefix}${symbols[2]}${hSymbol.repeat(innerWidth)}${symbols[3]}\\n`);\n};\n","import { styleText } from 'node:util';\nimport { ConfirmPrompt, settings, wrapTextWithPrefix } from '@clack/core';\nimport {\n\ttype CommonOptions,\n\tS_BAR,\n\tS_BAR_END,\n\tS_RADIO_ACTIVE,\n\tS_RADIO_INACTIVE,\n\tsymbol,\n} from './common.js';\n\nexport interface ConfirmOptions extends CommonOptions {\n\tmessage: string;\n\tactive?: string;\n\tinactive?: string;\n\tinitialValue?: boolean;\n\tvertical?: boolean;\n}\nexport const confirm = (opts: ConfirmOptions) => {\n\tconst active = opts.active ?? 'Yes';\n\tconst inactive = opts.inactive ?? 'No';\n\treturn new ConfirmPrompt({\n\t\tactive,\n\t\tinactive,\n\t\tsignal: opts.signal,\n\t\tinput: opts.input,\n\t\toutput: opts.output,\n\t\tinitialValue: opts.initialValue ?? true,\n\t\trender() {\n\t\t\tconst hasGuide = opts.withGuide ?? settings.withGuide;\n\t\t\tconst titlePrefix = `${symbol(this.state)} `;\n\t\t\tconst titlePrefixBar = hasGuide ? `${styleText('gray', S_BAR)} ` : '';\n\t\t\tconst messageLines = wrapTextWithPrefix(\n\t\t\t\topts.output,\n\t\t\t\topts.message,\n\t\t\t\ttitlePrefixBar,\n\t\t\t\ttitlePrefix\n\t\t\t);\n\t\t\tconst title = `${hasGuide ? `${styleText('gray', S_BAR)}\\n` : ''}${messageLines}\\n`;\n\t\t\tconst value = this.value ? active : inactive;\n\n\t\t\tswitch (this.state) {\n\t\t\t\tcase 'submit': {\n\t\t\t\t\tconst submitPrefix = hasGuide ? `${styleText('gray', S_BAR)} ` : '';\n\t\t\t\t\treturn `${title}${submitPrefix}${styleText('dim', value)}`;\n\t\t\t\t}\n\t\t\t\tcase 'cancel': {\n\t\t\t\t\tconst cancelPrefix = hasGuide ? `${styleText('gray', S_BAR)} ` : '';\n\t\t\t\t\treturn `${title}${cancelPrefix}${styleText(['strikethrough', 'dim'], value)}${\n\t\t\t\t\t\thasGuide ? `\\n${styleText('gray', S_BAR)}` : ''\n\t\t\t\t\t}`;\n\t\t\t\t}\n\t\t\t\tdefault: {\n\t\t\t\t\tconst defaultPrefix = hasGuide ? `${styleText('cyan', S_BAR)} ` : '';\n\t\t\t\t\tconst defaultPrefixEnd = hasGuide ? styleText('cyan', S_BAR_END) : '';\n\t\t\t\t\treturn `${title}${defaultPrefix}${\n\t\t\t\t\t\tthis.value\n\t\t\t\t\t\t\t? `${styleText('green', S_RADIO_ACTIVE)} ${active}`\n\t\t\t\t\t\t\t: `${styleText('dim', S_RADIO_INACTIVE)} ${styleText('dim', active)}`\n\t\t\t\t\t}${opts.vertical ? (hasGuide ? `\\n${styleText('cyan', S_BAR)} ` : '\\n') : ` ${styleText('dim', '/')} `}${\n\t\t\t\t\t\t!this.value\n\t\t\t\t\t\t\t? `${styleText('green', S_RADIO_ACTIVE)} ${inactive}`\n\t\t\t\t\t\t\t: `${styleText('dim', S_RADIO_INACTIVE)} ${styleText('dim', inactive)}`\n\t\t\t\t\t}\\n${defaultPrefixEnd}\\n`;\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t}).prompt() as Promise<boolean | symbol>;\n};\n","import { styleText } from 'node:util';\nimport type { DateFormat, State } from '@clack/core';\nimport { DatePrompt, settings } from '@clack/core';\nimport { type CommonOptions, S_BAR, S_BAR_END, symbol } from './common.js';\n\nexport type { DateFormat };\n\nexport interface DateOptions extends CommonOptions {\n\tmessage: string;\n\tformat?: DateFormat;\n\tlocale?: string;\n\tdefaultValue?: Date;\n\tinitialValue?: Date;\n\tminDate?: Date;\n\tmaxDate?: Date;\n\tvalidate?: (value: Date | undefined) => string | Error | undefined;\n}\n\nexport const date = (opts: DateOptions) => {\n\tconst validate = opts.validate;\n\treturn new DatePrompt({\n\t\t...opts,\n\t\tvalidate(value: Date | undefined) {\n\t\t\tif (value === undefined) {\n\t\t\t\tif (opts.defaultValue !== undefined) return undefined;\n\t\t\t\tif (validate) return validate(value);\n\t\t\t\treturn settings.date.messages.required;\n\t\t\t}\n\t\t\tconst iso = (d: Date) => d.toISOString().slice(0, 10);\n\t\t\tif (opts.minDate && iso(value) < iso(opts.minDate)) {\n\t\t\t\treturn settings.date.messages.afterMin(opts.minDate);\n\t\t\t}\n\t\t\tif (opts.maxDate && iso(value) > iso(opts.maxDate)) {\n\t\t\t\treturn settings.date.messages.beforeMax(opts.maxDate);\n\t\t\t}\n\t\t\tif (validate) return validate(value);\n\t\t\treturn undefined;\n\t\t},\n\t\trender() {\n\t\t\tconst hasGuide = (opts?.withGuide ?? settings.withGuide) !== false;\n\t\t\tconst titlePrefix = `${hasGuide ? `${styleText('gray', S_BAR)}\\n` : ''}${symbol(this.state)} `;\n\t\t\tconst title = `${titlePrefix}${opts.message}\\n`;\n\n\t\t\tconst state = this.state !== 'initial' ? this.state : 'active';\n\n\t\t\tconst userInput = renderDate(this, state);\n\t\t\tconst value = this.value instanceof Date ? this.formattedValue : '';\n\n\t\t\tswitch (this.state) {\n\t\t\t\tcase 'error': {\n\t\t\t\t\tconst errorText = this.error ? ` ${styleText('yellow', this.error)}` : '';\n\t\t\t\t\tconst bar = hasGuide ? `${styleText('yellow', S_BAR)} ` : '';\n\t\t\t\t\tconst barEnd = hasGuide ? styleText('yellow', S_BAR_END) : '';\n\t\t\t\t\treturn `${title.trim()}\\n${bar}${userInput}\\n${barEnd}${errorText}\\n`;\n\t\t\t\t}\n\t\t\t\tcase 'submit': {\n\t\t\t\t\tconst valueText = value ? ` ${styleText('dim', value)}` : '';\n\t\t\t\t\tconst bar = hasGuide ? styleText('gray', S_BAR) : '';\n\t\t\t\t\treturn `${title}${bar}${valueText}`;\n\t\t\t\t}\n\t\t\t\tcase 'cancel': {\n\t\t\t\t\tconst valueText = value ? ` ${styleText(['strikethrough', 'dim'], value)}` : '';\n\t\t\t\t\tconst bar = hasGuide ? styleText('gray', S_BAR) : '';\n\t\t\t\t\treturn `${title}${bar}${valueText}${value.trim() ? `\\n${bar}` : ''}`;\n\t\t\t\t}\n\t\t\t\tdefault: {\n\t\t\t\t\tconst bar = hasGuide ? `${styleText('cyan', S_BAR)} ` : '';\n\t\t\t\t\tconst barEnd = hasGuide ? styleText('cyan', S_BAR_END) : '';\n\t\t\t\t\tconst inlineBar = hasGuide ? `${styleText('cyan', S_BAR)} ` : '';\n\t\t\t\t\tconst inlineError = this.inlineError\n\t\t\t\t\t\t? `\\n${inlineBar}${styleText('yellow', this.inlineError)}`\n\t\t\t\t\t\t: '';\n\t\t\t\t\treturn `${title}${bar}${userInput}${inlineError}\\n${barEnd}\\n`;\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t}).prompt() as Promise<Date | symbol>;\n};\n\nfunction renderDate(prompt: Omit<InstanceType<typeof DatePrompt>, 'prompt'>, state: State): string {\n\tconst parts = prompt.segmentValues;\n\tconst cursor = prompt.segmentCursor;\n\n\tif (state === 'submit' || state === 'cancel') {\n\t\treturn prompt.formattedValue;\n\t}\n\n\tconst sep = styleText('gray', prompt.separator);\n\treturn prompt.segments\n\t\t.map((seg, i) => {\n\t\t\tconst isActive = i === cursor.segmentIndex && !['submit', 'cancel'].includes(state);\n\t\t\tconst label = DEFAULT_LABELS[seg.type];\n\t\t\treturn renderSegment(parts[seg.type], { isActive, label });\n\t\t})\n\t\t.join(sep);\n}\n\ninterface SegmentOptions {\n\tisActive: boolean;\n\tlabel: string;\n}\nfunction renderSegment(value: string, opts: SegmentOptions): string {\n\tconst isBlank = !value || value.replace(/_/g, '') === '';\n\tif (opts.isActive) return styleText('inverse', isBlank ? opts.label : value.replace(/_/g, ' '));\n\tif (isBlank) return styleText('dim', opts.label);\n\treturn value.replace(/_/g, styleText('dim', ' '));\n}\n\nconst DEFAULT_LABELS: Record<'year' | 'month' | 'day', string> = {\n\tyear: 'yyyy',\n\tmonth: 'mm',\n\tday: 'dd',\n};\n","import { isCancel } from '@clack/core';\n\ntype Prettify<T> = {\n\t[P in keyof T]: T[P];\n} & {};\n\nexport type PromptGroupAwaitedReturn<T> = {\n\t[P in keyof T]: Exclude<Awaited<T[P]>, symbol>;\n};\n\nexport interface PromptGroupOptions<T> {\n\t/**\n\t * Control how the group can be canceled\n\t * if one of the prompts is canceled.\n\t */\n\tonCancel?: (opts: { results: Prettify<Partial<PromptGroupAwaitedReturn<T>>> }) => void;\n}\n\nexport type PromptGroup<T> = {\n\t[P in keyof T]: (opts: {\n\t\tresults: Prettify<Partial<PromptGroupAwaitedReturn<Omit<T, P>>>>;\n\t}) => undefined | Promise<T[P] | undefined>;\n};\n\n/**\n * Define a group of prompts to be displayed\n * and return a results of objects within the group\n */\nexport const group = async <T>(\n\tprompts: PromptGroup<T>,\n\topts?: PromptGroupOptions<T>\n): Promise<Prettify<PromptGroupAwaitedReturn<T>>> => {\n\tconst results = {} as any;\n\tconst promptNames = Object.keys(prompts);\n\n\tfor (const name of promptNames) {\n\t\tconst prompt = prompts[name as keyof T];\n\t\tconst result = await prompt({ results })?.catch((e) => {\n\t\t\tthrow e;\n\t\t});\n\n\t\t// Pass the results to the onCancel function\n\t\t// so the user can decide what to do with the results\n\t\t// TODO: Switch to callback within core to avoid isCancel Fn\n\t\tif (typeof opts?.onCancel === 'function' && isCancel(result)) {\n\t\t\tresults[name] = 'canceled';\n\t\t\topts.onCancel({ results });\n\t\t\tcontinue;\n\t\t}\n\n\t\tresults[name] = result;\n\t}\n\n\treturn results;\n};\n","import { styleText } from 'node:util';\nimport { GroupMultiSelectPrompt, settings } from '@clack/core';\nimport {\n\ttype CommonOptions,\n\tS_BAR,\n\tS_BAR_END,\n\tS_CHECKBOX_ACTIVE,\n\tS_CHECKBOX_INACTIVE,\n\tS_CHECKBOX_SELECTED,\n\tsymbol,\n} from './common.js';\nimport type { Option } from './select.js';\n\nexport interface GroupMultiSelectOptions<Value> extends CommonOptions {\n\tmessage: string;\n\toptions: Record<string, Option<Value>[]>;\n\tinitialValues?: Value[];\n\trequired?: boolean;\n\tcursorAt?: Value;\n\tselectableGroups?: boolean;\n\tgroupSpacing?: number;\n}\nexport const groupMultiselect = <Value>(opts: GroupMultiSelectOptions<Value>) => {\n\tconst { selectableGroups = true, groupSpacing = 0 } = opts;\n\tconst opt = (\n\t\toption: Option<Value> & { group: string | boolean },\n\t\tstate:\n\t\t\t| 'inactive'\n\t\t\t| 'active'\n\t\t\t| 'selected'\n\t\t\t| 'active-selected'\n\t\t\t| 'group-active'\n\t\t\t| 'group-active-selected'\n\t\t\t| 'submitted'\n\t\t\t| 'cancelled',\n\t\toptions: (Option<Value> & { group: string | boolean })[] = []\n\t) => {\n\t\tconst label = option.label ?? String(option.value);\n\t\tconst isItem = typeof option.group === 'string';\n\t\tconst next = isItem && (options[options.indexOf(option) + 1] ?? { group: true });\n\t\tconst isLast = isItem && next && next.group === true;\n\t\tconst prefix = isItem ? (selectableGroups ? `${isLast ? S_BAR_END : S_BAR} ` : ' ') : '';\n\t\tlet spacingPrefix = '';\n\t\tif (groupSpacing > 0 && !isItem) {\n\t\t\tconst spacingPrefixText = `\\n${styleText('cyan', S_BAR)}`;\n\t\t\tspacingPrefix = `${spacingPrefixText.repeat(groupSpacing - 1)}${spacingPrefixText} `;\n\t\t}\n\n\t\tif (state === 'active') {\n\t\t\treturn `${spacingPrefix}${styleText('dim', prefix)}${styleText('cyan', S_CHECKBOX_ACTIVE)} ${label}${\n\t\t\t\toption.hint ? ` ${styleText('dim', `(${option.hint})`)}` : ''\n\t\t\t}`;\n\t\t}\n\t\tif (state === 'group-active') {\n\t\t\treturn `${spacingPrefix}${prefix}${styleText('cyan', S_CHECKBOX_ACTIVE)} ${styleText('dim', label)}`;\n\t\t}\n\t\tif (state === 'group-active-selected') {\n\t\t\treturn `${spacingPrefix}${prefix}${styleText('green', S_CHECKBOX_SELECTED)} ${styleText('dim', label)}`;\n\t\t}\n\t\tif (state === 'selected') {\n\t\t\tconst selectedCheckbox =\n\t\t\t\tisItem || selectableGroups ? styleText('green', S_CHECKBOX_SELECTED) : '';\n\t\t\treturn `${spacingPrefix}${styleText('dim', prefix)}${selectedCheckbox} ${styleText('dim', label)}${\n\t\t\t\toption.hint ? ` ${styleText('dim', `(${option.hint})`)}` : ''\n\t\t\t}`;\n\t\t}\n\t\tif (state === 'cancelled') {\n\t\t\treturn `${styleText(['strikethrough', 'dim'], label)}`;\n\t\t}\n\t\tif (state === 'active-selected') {\n\t\t\treturn `${spacingPrefix}${styleText('dim', prefix)}${styleText('green', S_CHECKBOX_SELECTED)} ${label}${\n\t\t\t\toption.hint ? ` ${styleText('dim', `(${option.hint})`)}` : ''\n\t\t\t}`;\n\t\t}\n\t\tif (state === 'submitted') {\n\t\t\treturn `${styleText('dim', label)}`;\n\t\t}\n\t\tconst unselectedCheckbox =\n\t\t\tisItem || selectableGroups ? styleText('dim', S_CHECKBOX_INACTIVE) : '';\n\t\treturn `${spacingPrefix}${styleText('dim', prefix)}${unselectedCheckbox} ${styleText('dim', label)}`;\n\t};\n\tconst required = opts.required ?? true;\n\n\treturn new GroupMultiSelectPrompt({\n\t\toptions: opts.options,\n\t\tsignal: opts.signal,\n\t\tinput: opts.input,\n\t\toutput: opts.output,\n\t\tinitialValues: opts.initialValues,\n\t\trequired,\n\t\tcursorAt: opts.cursorAt,\n\t\tselectableGroups,\n\t\tvalidate(selected: Value[] | undefined) {\n\t\t\tif (required && (selected === undefined || selected.length === 0))\n\t\t\t\treturn `Please select at least one option.\\n${styleText(\n\t\t\t\t\t'reset',\n\t\t\t\t\tstyleText(\n\t\t\t\t\t\t'dim',\n\t\t\t\t\t\t`Press ${styleText(['gray', 'bgWhite', 'inverse'], ' space ')} to select, ${styleText(\n\t\t\t\t\t\t\t'gray',\n\t\t\t\t\t\t\tstyleText(['bgWhite', 'inverse'], ' enter ')\n\t\t\t\t\t\t)} to submit`\n\t\t\t\t\t)\n\t\t\t\t)}`;\n\t\t},\n\t\trender() {\n\t\t\tconst hasGuide = opts.withGuide ?? settings.withGuide;\n\t\t\tconst title = `${hasGuide ? `${styleText('gray', S_BAR)}\\n` : ''}${symbol(this.state)} ${opts.message}\\n`;\n\t\t\tconst value = this.value ?? [];\n\n\t\t\tswitch (this.state) {\n\t\t\t\tcase 'submit': {\n\t\t\t\t\tconst selectedOptions = this.options\n\t\t\t\t\t\t.filter(({ value: optionValue }) => value.includes(optionValue))\n\t\t\t\t\t\t.map((option) => opt(option, 'submitted'));\n\t\t\t\t\tconst optionsText =\n\t\t\t\t\t\tselectedOptions.length === 0 ? '' : ` ${selectedOptions.join(styleText('dim', ', '))}`;\n\t\t\t\t\treturn `${title}${hasGuide ? styleText('gray', S_BAR) : ''}${optionsText}`;\n\t\t\t\t}\n\t\t\t\tcase 'cancel': {\n\t\t\t\t\tconst label = this.options\n\t\t\t\t\t\t.filter(({ value: optionValue }) => value.includes(optionValue))\n\t\t\t\t\t\t.map((option) => opt(option, 'cancelled'))\n\t\t\t\t\t\t.join(styleText('dim', ', '));\n\t\t\t\t\treturn `${title}${hasGuide ? `${styleText('gray', S_BAR)} ` : ''}${\n\t\t\t\t\t\tlabel.trim() ? `${label}${hasGuide ? `\\n${styleText('gray', S_BAR)}` : ''}` : ''\n\t\t\t\t\t}`;\n\t\t\t\t}\n\t\t\t\tcase 'error': {\n\t\t\t\t\tconst footer = this.error\n\t\t\t\t\t\t.split('\\n')\n\t\t\t\t\t\t.map((ln, i) =>\n\t\t\t\t\t\t\ti === 0\n\t\t\t\t\t\t\t\t? `${hasGuide ? `${styleText('yellow', S_BAR_END)} ` : ''}${styleText('yellow', ln)}`\n\t\t\t\t\t\t\t\t: ` ${ln}`\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.join('\\n');\n\t\t\t\t\treturn `${title}${hasGuide ? `${styleText('yellow', S_BAR)} ` : ''}${this.options\n\t\t\t\t\t\t.map((option, i, options) => {\n\t\t\t\t\t\t\tconst selected =\n\t\t\t\t\t\t\t\tvalue.includes(option.value) ||\n\t\t\t\t\t\t\t\t(option.group === true && this.isGroupSelected(`${option.value}`));\n\t\t\t\t\t\t\tconst active = i === this.cursor;\n\t\t\t\t\t\t\tconst groupActive =\n\t\t\t\t\t\t\t\t!active &&\n\t\t\t\t\t\t\t\ttypeof option.group === 'string' &&\n\t\t\t\t\t\t\t\tthis.options[this.cursor].value === option.group;\n\t\t\t\t\t\t\tif (groupActive) {\n\t\t\t\t\t\t\t\treturn opt(option, selected ? 'group-active-selected' : 'group-active', options);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (active && selected) {\n\t\t\t\t\t\t\t\treturn opt(option, 'active-selected', options);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (selected) {\n\t\t\t\t\t\t\t\treturn opt(option, 'selected', options);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn opt(option, active ? 'active' : 'inactive', options);\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.join(`\\n${hasGuide ? `${styleText('yellow', S_BAR)} ` : ''}`)}\\n${footer}\\n`;\n\t\t\t\t}\n\t\t\t\tdefault: {\n\t\t\t\t\tconst optionsText = this.options\n\t\t\t\t\t\t.map((option, i, options) => {\n\t\t\t\t\t\t\tconst selected =\n\t\t\t\t\t\t\t\tvalue.includes(option.value) ||\n\t\t\t\t\t\t\t\t(option.group === true && this.isGroupSelected(`${option.value}`));\n\t\t\t\t\t\t\tconst active = i === this.cursor;\n\t\t\t\t\t\t\tconst groupActive =\n\t\t\t\t\t\t\t\t!active &&\n\t\t\t\t\t\t\t\ttypeof option.group === 'string' &&\n\t\t\t\t\t\t\t\tthis.options[this.cursor].value === option.group;\n\t\t\t\t\t\t\tlet optionText = '';\n\t\t\t\t\t\t\tif (groupActive) {\n\t\t\t\t\t\t\t\toptionText = opt(\n\t\t\t\t\t\t\t\t\toption,\n\t\t\t\t\t\t\t\t\tselected ? 'group-active-selected' : 'group-active',\n\t\t\t\t\t\t\t\t\toptions\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t} else if (active && selected) {\n\t\t\t\t\t\t\t\toptionText = opt(option, 'active-selected', options);\n\t\t\t\t\t\t\t} else if (selected) {\n\t\t\t\t\t\t\t\toptionText = opt(option, 'selected', options);\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\toptionText = opt(option, active ? 'active' : 'inactive', options);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tconst prefix = i !== 0 && !optionText.startsWith('\\n') ? ' ' : '';\n\t\t\t\t\t\t\treturn `${prefix}${optionText}`;\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.join(`\\n${hasGuide ? styleText('cyan', S_BAR) : ''}`);\n\t\t\t\t\tconst optionsPrefix = optionsText.startsWith('\\n') ? '' : ' ';\n\t\t\t\t\treturn `${title}${hasGuide ? styleText('cyan', S_BAR) : ''}${optionsPrefix}${optionsText}\\n${\n\t\t\t\t\t\thasGuide ? styleText('cyan', S_BAR_END) : ''\n\t\t\t\t\t}\\n`;\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t}).prompt() as Promise<Value[] | symbol>;\n};\n","import { styleText } from 'node:util';\nimport { settings } from '@clack/core';\nimport {\n\ttype CommonOptions,\n\tS_BAR,\n\tS_ERROR,\n\tS_INFO,\n\tS_STEP_SUBMIT,\n\tS_SUCCESS,\n\tS_WARN,\n} from './common.js';\n\nexport interface LogMessageOptions extends CommonOptions {\n\tsymbol?: string;\n\tspacing?: number;\n\tsecondarySymbol?: string;\n}\n\nexport const log = {\n\tmessage: (\n\t\tmessage: string | string[] = [],\n\t\t{\n\t\t\tsymbol = styleText('gray', S_BAR),\n\t\t\tsecondarySymbol = styleText('gray', S_BAR),\n\t\t\toutput = process.stdout,\n\t\t\tspacing = 1,\n\t\t\twithGuide,\n\t\t}: LogMessageOptions = {}\n\t) => {\n\t\tconst parts: string[] = [];\n\t\tconst hasGuide = withGuide ?? settings.withGuide;\n\t\tconst spacingString = !hasGuide ? '' : secondarySymbol;\n\t\tconst prefix = !hasGuide ? '' : `${symbol} `;\n\t\tconst secondaryPrefix = !hasGuide ? '' : `${secondarySymbol} `;\n\n\t\tfor (let i = 0; i < spacing; i++) {\n\t\t\tparts.push(spacingString);\n\t\t}\n\n\t\tconst messageParts = Array.isArray(message) ? message : message.split('\\n');\n\t\tif (messageParts.length > 0) {\n\t\t\tconst [firstLine, ...lines] = messageParts;\n\t\t\tif (firstLine.length > 0) {\n\t\t\t\tparts.push(`${prefix}${firstLine}`);\n\t\t\t} else {\n\t\t\t\tparts.push(hasGuide ? symbol : '');\n\t\t\t}\n\t\t\tfor (const ln of lines) {\n\t\t\t\tif (ln.length > 0) {\n\t\t\t\t\tparts.push(`${secondaryPrefix}${ln}`);\n\t\t\t\t} else {\n\t\t\t\t\tparts.push(hasGuide ? secondarySymbol : '');\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\toutput.write(`${parts.join('\\n')}\\n`);\n\t},\n\tinfo: (message: string, opts?: LogMessageOptions) => {\n\t\tlog.message(message, { ...opts, symbol: styleText('blue', S_INFO) });\n\t},\n\tsuccess: (message: string, opts?: LogMessageOptions) => {\n\t\tlog.message(message, { ...opts, symbol: styleText('green', S_SUCCESS) });\n\t},\n\tstep: (message: string, opts?: LogMessageOptions) => {\n\t\tlog.message(message, { ...opts, symbol: styleText('green', S_STEP_SUBMIT) });\n\t},\n\twarn: (message: string, opts?: LogMessageOptions) => {\n\t\tlog.message(message, { ...opts, symbol: styleText('yellow', S_WARN) });\n\t},\n\t/** alias for `log.warn()`. */\n\twarning: (message: string, opts?: LogMessageOptions) => {\n\t\tlog.warn(message, opts);\n\t},\n\terror: (message: string, opts?: LogMessageOptions) => {\n\t\tlog.message(message, { ...opts, symbol: styleText('red', S_ERROR) });\n\t},\n};\n","import type { Writable } from 'node:stream';\nimport { styleText } from 'node:util';\nimport { settings } from '@clack/core';\nimport { type CommonOptions, S_BAR, S_BAR_END, S_BAR_START } from './common.js';\n\nexport const cancel = (message = '', opts?: CommonOptions) => {\n\tconst output: Writable = opts?.output ?? process.stdout;\n\tconst hasGuide = opts?.withGuide ?? settings.withGuide;\n\tconst prefix = hasGuide ? `${styleText('gray', S_BAR_END)} ` : '';\n\toutput.write(`${prefix}${styleText('red', message)}\\n\\n`);\n};\n\nexport const intro = (title = '', opts?: CommonOptions) => {\n\tconst output: Writable = opts?.output ?? process.stdout;\n\tconst hasGuide = opts?.withGuide ?? settings.withGuide;\n\tconst prefix = hasGuide ? `${styleText('gray', S_BAR_START)} ` : '';\n\toutput.write(`${prefix}${title}\\n`);\n};\n\nexport const outro = (message = '', opts?: CommonOptions) => {\n\tconst output: Writable = opts?.output ?? process.stdout;\n\tconst hasGuide = opts?.withGuide ?? settings.withGuide;\n\tconst prefix = hasGuide ? `${styleText('gray', S_BAR)}\\n${styleText('gray', S_BAR_END)} ` : '';\n\toutput.write(`${prefix}${message}\\n\\n`);\n};\n","import { styleText } from 'node:util';\nimport { MultiLinePrompt, settings, wrapTextWithPrefix } from '@clack/core';\nimport { S_BAR, S_BAR_END, symbol } from './common.js';\nimport type { TextOptions } from './text.js';\n\nexport interface MultiLineOptions extends TextOptions {\n\tshowSubmit?: boolean;\n}\n\nexport const multiline = (opts: MultiLineOptions) => {\n\treturn new MultiLinePrompt({\n\t\tvalidate: opts.validate,\n\t\tplaceholder: opts.placeholder,\n\t\tdefaultValue: opts.defaultValue,\n\t\tinitialValue: opts.initialValue,\n\t\tshowSubmit: opts.showSubmit,\n\t\toutput: opts.output,\n\t\tsignal: opts.signal,\n\t\tinput: opts.input,\n\t\trender() {\n\t\t\tconst hasGuide = opts?.withGuide ?? settings.withGuide;\n\t\t\tconst titlePrefix = `${hasGuide ? `${styleText('gray', S_BAR)}\\n` : ''}${symbol(this.state)} `;\n\t\t\tconst title = `${titlePrefix}${opts.message}\\n`;\n\t\t\tconst placeholder = opts.placeholder\n\t\t\t\t? styleText('inverse', opts.placeholder[0]) + styleText('dim', opts.placeholder.slice(1))\n\t\t\t\t: styleText(['inverse', 'hidden'], '_');\n\t\t\tconst userInput = !this.userInput ? placeholder : this.userInputWithCursor;\n\t\t\tconst value = this.value ?? '';\n\t\t\tconst submitButton = opts.showSubmit\n\t\t\t\t? `\\n ${styleText(this.focused === 'submit' ? 'cyan' : 'dim', '[ submit ]')}`\n\t\t\t\t: '';\n\t\t\tswitch (this.state) {\n\t\t\t\tcase 'error': {\n\t\t\t\t\tconst errorPrefix = `${styleText('yellow', S_BAR)} `;\n\t\t\t\t\tconst lines = hasGuide\n\t\t\t\t\t\t? wrapTextWithPrefix(opts.output, userInput, errorPrefix, undefined)\n\t\t\t\t\t\t: userInput;\n\t\t\t\t\tconst errorPrefixEnd = styleText('yellow', S_BAR_END);\n\t\t\t\t\treturn `${title}${lines}\\n${errorPrefixEnd} ${styleText('yellow', this.error)}${submitButton}\\n`;\n\t\t\t\t}\n\t\t\t\tcase 'submit': {\n\t\t\t\t\tconst submitPrefix = `${styleText('gray', S_BAR)} `;\n\t\t\t\t\tconst lines = hasGuide\n\t\t\t\t\t\t? wrapTextWithPrefix(opts.output, value, submitPrefix, undefined, (str) =>\n\t\t\t\t\t\t\t\tstyleText('dim', str)\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t: value\n\t\t\t\t\t\t\t? styleText('dim', value)\n\t\t\t\t\t\t\t: '';\n\t\t\t\t\treturn `${title}${lines}`;\n\t\t\t\t}\n\t\t\t\tcase 'cancel': {\n\t\t\t\t\tconst cancelPrefix = `${styleText('gray', S_BAR)} `;\n\t\t\t\t\tconst lines = hasGuide\n\t\t\t\t\t\t? wrapTextWithPrefix(opts.output, value, cancelPrefix, undefined, (str) =>\n\t\t\t\t\t\t\t\tstyleText(['strikethrough', 'dim'], str)\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t: value\n\t\t\t\t\t\t\t? styleText(['strikethrough', 'dim'], value)\n\t\t\t\t\t\t\t: '';\n\t\t\t\t\treturn `${title}${lines}`;\n\t\t\t\t}\n\t\t\t\tdefault: {\n\t\t\t\t\tconst defaultPrefix = hasGuide ? `${styleText('cyan', S_BAR)} ` : '';\n\t\t\t\t\tconst defaultPrefixEnd = hasGuide ? styleText('cyan', S_BAR_END) : '';\n\t\t\t\t\tconst lines = hasGuide\n\t\t\t\t\t\t? wrapTextWithPrefix(opts.output, userInput, defaultPrefix)\n\t\t\t\t\t\t: userInput;\n\t\t\t\t\treturn `${title}${lines}\\n${defaultPrefixEnd}${submitButton}\\n`;\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t}).prompt() as Promise<string | symbol>;\n};\n","import { styleText } from 'node:util';\nimport { MultiSelectPrompt, settings, wrapTextWithPrefix } from '@clack/core';\nimport {\n\ttype CommonOptions,\n\tS_BAR,\n\tS_BAR_END,\n\tS_CHECKBOX_ACTIVE,\n\tS_CHECKBOX_INACTIVE,\n\tS_CHECKBOX_SELECTED,\n\tsymbol,\n\tsymbolBar,\n} from './common.js';\nimport { limitOptions } from './limit-options.js';\nimport type { Option } from './select.js';\n\nexport interface MultiSelectOptions<Value> extends CommonOptions {\n\tmessage: string;\n\toptions: Option<Value>[];\n\tinitialValues?: Value[];\n\tmaxItems?: number;\n\trequired?: boolean;\n\tcursorAt?: Value;\n}\nconst computeLabel = (label: string, format: (text: string) => string) => {\n\treturn label\n\t\t.split('\\n')\n\t\t.map((line) => format(line))\n\t\t.join('\\n');\n};\n\nexport const multiselect = <Value>(opts: MultiSelectOptions<Value>) => {\n\tconst opt = (\n\t\toption: Option<Value>,\n\t\tstate:\n\t\t\t| 'inactive'\n\t\t\t| 'active'\n\t\t\t| 'selected'\n\t\t\t| 'active-selected'\n\t\t\t| 'submitted'\n\t\t\t| 'cancelled'\n\t\t\t| 'disabled'\n\t) => {\n\t\tconst label = option.label ?? String(option.value);\n\t\tif (state === 'disabled') {\n\t\t\treturn `${styleText('gray', S_CHECKBOX_INACTIVE)} ${computeLabel(label, (str) => styleText(['strikethrough', 'gray'], str))}${\n\t\t\t\toption.hint ? ` ${styleText('dim', `(${option.hint ?? 'disabled'})`)}` : ''\n\t\t\t}`;\n\t\t}\n\t\tif (state === 'active') {\n\t\t\treturn `${styleText('cyan', S_CHECKBOX_ACTIVE)} ${label}${\n\t\t\t\toption.hint ? ` ${styleText('dim', `(${option.hint})`)}` : ''\n\t\t\t}`;\n\t\t}\n\t\tif (state === 'selected') {\n\t\t\treturn `${styleText('green', S_CHECKBOX_SELECTED)} ${computeLabel(label, (text) => styleText('dim', text))}${\n\t\t\t\toption.hint ? ` ${styleText('dim', `(${option.hint})`)}` : ''\n\t\t\t}`;\n\t\t}\n\t\tif (state === 'cancelled') {\n\t\t\treturn `${computeLabel(label, (text) => styleText(['strikethrough', 'dim'], text))}`;\n\t\t}\n\t\tif (state === 'active-selected') {\n\t\t\treturn `${styleText('green', S_CHECKBOX_SELECTED)} ${label}${\n\t\t\t\toption.hint ? ` ${styleText('dim', `(${option.hint})`)}` : ''\n\t\t\t}`;\n\t\t}\n\t\tif (state === 'submitted') {\n\t\t\treturn `${computeLabel(label, (text) => styleText('dim', text))}`;\n\t\t}\n\t\treturn `${styleText('dim', S_CHECKBOX_INACTIVE)} ${computeLabel(label, (text) => styleText('dim', text))}`;\n\t};\n\tconst required = opts.required ?? true;\n\n\treturn new MultiSelectPrompt({\n\t\toptions: opts.options,\n\t\tsignal: opts.signal,\n\t\tinput: opts.input,\n\t\toutput: opts.output,\n\t\tinitialValues: opts.initialValues,\n\t\trequired,\n\t\tcursorAt: opts.cursorAt,\n\t\tvalidate(selected: Value[] | undefined) {\n\t\t\tif (required && (selected === undefined || selected.length === 0))\n\t\t\t\treturn `Please select at least one option.\\n${styleText(\n\t\t\t\t\t'reset',\n\t\t\t\t\tstyleText(\n\t\t\t\t\t\t'dim',\n\t\t\t\t\t\t`Press ${styleText(['gray', 'bgWhite', 'inverse'], ' space ')} to select, ${styleText(\n\t\t\t\t\t\t\t'gray',\n\t\t\t\t\t\t\tstyleText('bgWhite', styleText('inverse', ' enter '))\n\t\t\t\t\t\t)} to submit`\n\t\t\t\t\t)\n\t\t\t\t)}`;\n\t\t},\n\t\trender() {\n\t\t\tconst hasGuide = opts.withGuide ?? settings.withGuide;\n\t\t\tconst wrappedMessage = wrapTextWithPrefix(\n\t\t\t\topts.output,\n\t\t\t\topts.message,\n\t\t\t\thasGuide ? `${symbolBar(this.state)} ` : '',\n\t\t\t\t`${symbol(this.state)} `\n\t\t\t);\n\t\t\tconst title = `${hasGuide ? `${styleText('gray', S_BAR)}\\n` : ''}${wrappedMessage}\\n`;\n\t\t\tconst value = this.value ?? [];\n\n\t\t\tconst styleOption = (option: Option<Value>, active: boolean) => {\n\t\t\t\tif (option.disabled) {\n\t\t\t\t\treturn opt(option, 'disabled');\n\t\t\t\t}\n\t\t\t\tconst selected = value.includes(option.value);\n\t\t\t\tif (active && selected) {\n\t\t\t\t\treturn opt(option, 'active-selected');\n\t\t\t\t}\n\t\t\t\tif (selected) {\n\t\t\t\t\treturn opt(option, 'selected');\n\t\t\t\t}\n\t\t\t\treturn opt(option, active ? 'active' : 'inactive');\n\t\t\t};\n\n\t\t\tswitch (this.state) {\n\t\t\t\tcase 'submit': {\n\t\t\t\t\tconst submitText =\n\t\t\t\t\t\tthis.options\n\t\t\t\t\t\t\t.filter(({ value: optionValue }) => value.includes(optionValue))\n\t\t\t\t\t\t\t.map((option) => opt(option, 'submitted'))\n\t\t\t\t\t\t\t.join(styleText('dim', ', ')) || styleText('dim', 'none');\n\t\t\t\t\tconst wrappedSubmitText = wrapTextWithPrefix(\n\t\t\t\t\t\topts.output,\n\t\t\t\t\t\tsubmitText,\n\t\t\t\t\t\thasGuide ? `${styleText('gray', S_BAR)} ` : ''\n\t\t\t\t\t);\n\t\t\t\t\treturn `${title}${wrappedSubmitText}`;\n\t\t\t\t}\n\t\t\t\tcase 'cancel': {\n\t\t\t\t\tconst label = this.options\n\t\t\t\t\t\t.filter(({ value: optionValue }) => value.includes(optionValue))\n\t\t\t\t\t\t.map((option) => opt(option, 'cancelled'))\n\t\t\t\t\t\t.join(styleText('dim', ', '));\n\t\t\t\t\tif (label.trim() === '') {\n\t\t\t\t\t\treturn `${title}${styleText('gray', S_BAR)}`;\n\t\t\t\t\t}\n\t\t\t\t\tconst wrappedLabel = wrapTextWithPrefix(\n\t\t\t\t\t\topts.output,\n\t\t\t\t\t\tlabel,\n\t\t\t\t\t\thasGuide ? `${styleText('gray', S_BAR)} ` : ''\n\t\t\t\t\t);\n\t\t\t\t\treturn `${title}${wrappedLabel}${hasGuide ? `\\n${styleText('gray', S_BAR)}` : ''}`;\n\t\t\t\t}\n\t\t\t\tcase 'error': {\n\t\t\t\t\tconst prefix = hasGuide ? `${styleText('yellow', S_BAR)} ` : '';\n\t\t\t\t\tconst footer = this.error\n\t\t\t\t\t\t.split('\\n')\n\t\t\t\t\t\t.map((ln, i) =>\n\t\t\t\t\t\t\ti === 0\n\t\t\t\t\t\t\t\t? `${hasGuide ? `${styleText('yellow', S_BAR_END)} ` : ''}${styleText('yellow', ln)}`\n\t\t\t\t\t\t\t\t: ` ${ln}`\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.join('\\n');\n\t\t\t\t\t// Calculate rowPadding: title lines + footer lines (error message + trailing newline)\n\t\t\t\t\tconst titleLineCount = title.split('\\n').length;\n\t\t\t\t\tconst footerLineCount = footer.split('\\n').length + 1; // footer + trailing newline\n\t\t\t\t\treturn `${title}${prefix}${limitOptions({\n\t\t\t\t\t\toutput: opts.output,\n\t\t\t\t\t\toptions: this.options,\n\t\t\t\t\t\tcursor: this.cursor,\n\t\t\t\t\t\tmaxItems: opts.maxItems,\n\t\t\t\t\t\tcolumnPadding: prefix.length,\n\t\t\t\t\t\trowPadding: titleLineCount + footerLineCount,\n\t\t\t\t\t\tstyle: styleOption,\n\t\t\t\t\t}).join(`\\n${prefix}`)}\\n${footer}\\n`;\n\t\t\t\t}\n\t\t\t\tdefault: {\n\t\t\t\t\tconst prefix = hasGuide ? `${styleText('cyan', S_BAR)} ` : '';\n\t\t\t\t\t// Calculate rowPadding: title lines + footer lines (S_BAR_END + trailing newline)\n\t\t\t\t\tconst titleLineCount = title.split('\\n').length;\n\t\t\t\t\tconst footerLineCount = hasGuide ? 2 : 1; // S_BAR_END + trailing newline\n\t\t\t\t\treturn `${title}${prefix}${limitOptions({\n\t\t\t\t\t\toutput: opts.output,\n\t\t\t\t\t\toptions: this.options,\n\t\t\t\t\t\tcursor: this.cursor,\n\t\t\t\t\t\tmaxItems: opts.maxItems,\n\t\t\t\t\t\tcolumnPadding: prefix.length,\n\t\t\t\t\t\trowPadding: titleLineCount + footerLineCount,\n\t\t\t\t\t\tstyle: styleOption,\n\t\t\t\t\t}).join(`\\n${prefix}`)}\\n${hasGuide ? styleText('cyan', S_BAR_END) : ''}\\n`;\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t}).prompt() as Promise<Value[] | symbol>;\n};\n","import process from 'node:process';\nimport type { Writable } from 'node:stream';\nimport { styleText } from 'node:util';\nimport { getColumns, settings } from '@clack/core';\nimport stringWidth from 'fast-string-width';\nimport { type Options as WrapAnsiOptions, wrapAnsi } from 'fast-wrap-ansi';\nimport {\n\ttype CommonOptions,\n\tS_BAR,\n\tS_BAR_H,\n\tS_CONNECT_LEFT,\n\tS_CORNER_BOTTOM_LEFT,\n\tS_CORNER_BOTTOM_RIGHT,\n\tS_CORNER_TOP_RIGHT,\n\tS_STEP_SUBMIT,\n} from './common.js';\n\ntype FormatFn = (line: string) => string;\nexport interface NoteOptions extends CommonOptions {\n\tformat?: FormatFn;\n}\n\nconst defaultNoteFormatter = (line: string): string => styleText('dim', line);\n\nconst wrapWithFormat = (message: string, width: number, format: FormatFn): string => {\n\tconst opts: WrapAnsiOptions = {\n\t\thard: true,\n\t\ttrim: false,\n\t};\n\tconst wrapMsg = wrapAnsi(message, width, opts).split('\\n');\n\tconst maxWidthNormal = wrapMsg.reduce((sum, ln) => Math.max(stringWidth(ln), sum), 0);\n\tconst maxWidthFormat = wrapMsg.map(format).reduce((sum, ln) => Math.max(stringWidth(ln), sum), 0);\n\tconst wrapWidth = width - (maxWidthFormat - maxWidthNormal);\n\treturn wrapAnsi(message, wrapWidth, opts);\n};\n\nexport const note = (message = '', title = '', opts?: NoteOptions) => {\n\tconst output: Writable = opts?.output ?? process.stdout;\n\tconst hasGuide = opts?.withGuide ?? settings.withGuide;\n\tconst format = opts?.format ?? defaultNoteFormatter;\n\tconst wrapMsg = wrapWithFormat(message, getColumns(output) - 6, format);\n\tconst lines = ['', ...wrapMsg.split('\\n').map(format), ''];\n\tconst titleLen = stringWidth(title);\n\tconst len =\n\t\tMath.max(\n\t\t\tlines.reduce((sum, ln) => {\n\t\t\t\tconst width = stringWidth(ln);\n\t\t\t\treturn width > sum ? width : sum;\n\t\t\t}, 0),\n\t\t\ttitleLen\n\t\t) + 2;\n\tconst msg = lines\n\t\t.map(\n\t\t\t(ln) =>\n\t\t\t\t`${styleText('gray', S_BAR)} ${ln}${' '.repeat(len - stringWidth(ln))}${styleText('gray', S_BAR)}`\n\t\t)\n\t\t.join('\\n');\n\tconst leadingBorder = hasGuide ? `${styleText('gray', S_BAR)}\\n` : '';\n\tconst bottomLeft = hasGuide ? S_CONNECT_LEFT : S_CORNER_BOTTOM_LEFT;\n\toutput.write(\n\t\t`${leadingBorder}${styleText('green', S_STEP_SUBMIT)} ${styleText('reset', title)} ${styleText(\n\t\t\t'gray',\n\t\t\tS_BAR_H.repeat(Math.max(len - titleLen - 1, 1)) + S_CORNER_TOP_RIGHT\n\t\t)}\\n${msg}\\n${styleText('gray', bottomLeft + S_BAR_H.repeat(len + 2) + S_CORNER_BOTTOM_RIGHT)}\\n`\n\t);\n};\n","import { styleText } from 'node:util';\nimport { PasswordPrompt, settings } from '@clack/core';\nimport { type CommonOptions, S_BAR, S_BAR_END, S_PASSWORD_MASK, symbol } from './common.js';\n\nexport interface PasswordOptions extends CommonOptions {\n\tmessage: string;\n\tmask?: string;\n\tvalidate?: (value: string | undefined) => string | Error | undefined;\n\tclearOnError?: boolean;\n}\nexport const password = (opts: PasswordOptions) => {\n\treturn new PasswordPrompt({\n\t\tvalidate: opts.validate,\n\t\tmask: opts.mask ?? S_PASSWORD_MASK,\n\t\tsignal: opts.signal,\n\t\tinput: opts.input,\n\t\toutput: opts.output,\n\t\trender() {\n\t\t\tconst hasGuide = opts.withGuide ?? settings.withGuide;\n\t\t\tconst title = `${hasGuide ? `${styleText('gray', S_BAR)}\\n` : ''}${symbol(this.state)} ${opts.message}\\n`;\n\t\t\tconst userInput = this.userInputWithCursor;\n\t\t\tconst masked = this.masked;\n\n\t\t\tswitch (this.state) {\n\t\t\t\tcase 'error': {\n\t\t\t\t\tconst errorPrefix = hasGuide ? `${styleText('yellow', S_BAR)} ` : '';\n\t\t\t\t\tconst errorPrefixEnd = hasGuide ? `${styleText('yellow', S_BAR_END)} ` : '';\n\t\t\t\t\tconst maskedText = masked ?? '';\n\t\t\t\t\tif (opts.clearOnError) {\n\t\t\t\t\t\tthis.clear();\n\t\t\t\t\t}\n\t\t\t\t\treturn `${title.trim()}\\n${errorPrefix}${maskedText}\\n${errorPrefixEnd}${styleText('yellow', this.error)}\\n`;\n\t\t\t\t}\n\t\t\t\tcase 'submit': {\n\t\t\t\t\tconst submitPrefix = hasGuide ? `${styleText('gray', S_BAR)} ` : '';\n\t\t\t\t\tconst maskedText = masked ? styleText('dim', masked) : '';\n\t\t\t\t\treturn `${title}${submitPrefix}${maskedText}`;\n\t\t\t\t}\n\t\t\t\tcase 'cancel': {\n\t\t\t\t\tconst cancelPrefix = hasGuide ? `${styleText('gray', S_BAR)} ` : '';\n\t\t\t\t\tconst maskedText = masked ? styleText(['strikethrough', 'dim'], masked) : '';\n\t\t\t\t\treturn `${title}${cancelPrefix}${maskedText}${\n\t\t\t\t\t\tmasked && hasGuide ? `\\n${styleText('gray', S_BAR)}` : ''\n\t\t\t\t\t}`;\n\t\t\t\t}\n\t\t\t\tdefault: {\n\t\t\t\t\tconst defaultPrefix = hasGuide ? `${styleText('cyan', S_BAR)} ` : '';\n\t\t\t\t\tconst defaultPrefixEnd = hasGuide ? styleText('cyan', S_BAR_END) : '';\n\t\t\t\t\treturn `${title}${defaultPrefix}${userInput}\\n${defaultPrefixEnd}\\n`;\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t}).prompt() as Promise<string | symbol>;\n};\n","import { existsSync, lstatSync, readdirSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { autocomplete } from './autocomplete.js';\nimport type { CommonOptions } from './common.js';\n\nexport interface PathOptions extends CommonOptions {\n\troot?: string;\n\tdirectory?: boolean;\n\tinitialValue?: string;\n\tmessage: string;\n\tvalidate?: (value: string | undefined) => string | Error | undefined;\n}\n\nexport const path = (opts: PathOptions) => {\n\tconst validate = opts.validate;\n\n\treturn autocomplete({\n\t\t...opts,\n\t\tinitialUserInput: opts.initialValue ?? opts.root ?? process.cwd(),\n\t\tmaxItems: 5,\n\t\tvalidate(value) {\n\t\t\tif (Array.isArray(value)) {\n\t\t\t\t// Shouldn't ever happen since we don't enable `multiple: true`\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t\tif (!value) {\n\t\t\t\treturn 'Please select a path';\n\t\t\t}\n\t\t\tif (validate) {\n\t\t\t\treturn validate(value);\n\t\t\t}\n\t\t\treturn undefined;\n\t\t},\n\t\toptions() {\n\t\t\tconst userInput = this.userInput;\n\t\t\tif (userInput === '') {\n\t\t\t\treturn [];\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tlet searchPath: string;\n\n\t\t\t\tif (!existsSync(userInput)) {\n\t\t\t\t\tsearchPath = dirname(userInput);\n\t\t\t\t} else {\n\t\t\t\t\tconst stat = lstatSync(userInput);\n\t\t\t\t\tif (stat.isDirectory() && (!opts.directory || userInput.endsWith('/'))) {\n\t\t\t\t\t\tsearchPath = userInput;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tsearchPath = dirname(userInput);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Strip trailing slash so startsWith matches the directory itself among its siblings\n\t\t\t\tconst prefix =\n\t\t\t\t\tuserInput.length > 1 && userInput.endsWith('/') ? userInput.slice(0, -1) : userInput;\n\n\t\t\t\tconst items = readdirSync(searchPath)\n\t\t\t\t\t.map((item) => {\n\t\t\t\t\t\tconst path = join(searchPath, item);\n\t\t\t\t\t\tconst stats = lstatSync(path);\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tname: item,\n\t\t\t\t\t\t\tpath,\n\t\t\t\t\t\t\tisDirectory: stats.isDirectory(),\n\t\t\t\t\t\t};\n\t\t\t\t\t})\n\t\t\t\t\t.filter(\n\t\t\t\t\t\t({ path, isDirectory }) => path.startsWith(prefix) && (isDirectory || !opts.directory)\n\t\t\t\t\t);\n\n\t\t\t\treturn items.map((item) => ({\n\t\t\t\t\tvalue: item.path,\n\t\t\t\t}));\n\t\t\t} catch (_e) {\n\t\t\t\treturn [];\n\t\t\t}\n\t\t},\n\t});\n};\n","import { styleText } from 'node:util';\nimport { block, getColumns, settings } from '@clack/core';\nimport { wrapAnsi } from 'fast-wrap-ansi';\nimport { cursor, erase } from 'sisteransi';\nimport {\n\ttype CommonOptions,\n\tisCI as isCIFn,\n\tS_BAR,\n\tS_STEP_CANCEL,\n\tS_STEP_ERROR,\n\tS_STEP_SUBMIT,\n\tunicode,\n} from './common.js';\n\nexport interface SpinnerOptions extends CommonOptions {\n\tindicator?: 'dots' | 'timer';\n\tonCancel?: () => void;\n\tcancelMessage?: string;\n\terrorMessage?: string;\n\tframes?: string[];\n\tdelay?: number;\n\tstyleFrame?: (frame: string) => string;\n}\n\nexport interface SpinnerResult {\n\tstart(msg?: string): void;\n\tstop(msg?: string): void;\n\tcancel(msg?: string): void;\n\terror(msg?: string): void;\n\tmessage(msg?: string): void;\n\tclear(): void;\n\treadonly isCancelled: boolean;\n}\n\nconst defaultStyleFn: SpinnerOptions['styleFrame'] = (frame) => styleText('magenta', frame);\n\nexport const spinner = ({\n\tindicator = 'dots',\n\tonCancel,\n\toutput = process.stdout,\n\tcancelMessage,\n\terrorMessage,\n\tframes = unicode ? ['◒', '◐', '◓', '◑'] : ['•', 'o', 'O', '0'],\n\tdelay = unicode ? 80 : 120,\n\tsignal,\n\t...opts\n}: SpinnerOptions = {}): SpinnerResult => {\n\tconst isCI = isCIFn();\n\n\tlet unblock: () => void;\n\tlet loop: NodeJS.Timeout;\n\tlet isSpinnerActive = false;\n\tlet isCancelled = false;\n\tlet _message = '';\n\tlet _prevMessage: string | undefined;\n\tlet _origin: number = performance.now();\n\tconst columns = getColumns(output);\n\tconst styleFn = opts?.styleFrame ?? defaultStyleFn;\n\n\tconst handleExit = (code: number) => {\n\t\tconst msg =\n\t\t\tcode > 1\n\t\t\t\t? (errorMessage ?? settings.messages.error)\n\t\t\t\t: (cancelMessage ?? settings.messages.cancel);\n\t\tisCancelled = code === 1;\n\t\tif (isSpinnerActive) {\n\t\t\t_stop(msg, code);\n\t\t\tif (isCancelled && typeof onCancel === 'function') {\n\t\t\t\tonCancel();\n\t\t\t}\n\t\t}\n\t};\n\n\tconst errorEventHandler = () => handleExit(2);\n\tconst signalEventHandler = () => handleExit(1);\n\n\tconst registerHooks = () => {\n\t\t// Reference: https://nodejs.org/api/process.html#event-uncaughtexception\n\t\tprocess.on('uncaughtExceptionMonitor', errorEventHandler);\n\t\t// Reference: https://nodejs.org/api/process.html#event-unhandledrejection\n\t\tprocess.on('unhandledRejection', errorEventHandler);\n\t\t// Reference Signal Events: https://nodejs.org/api/process.html#signal-events\n\t\tprocess.on('SIGINT', signalEventHandler);\n\t\tprocess.on('SIGTERM', signalEventHandler);\n\t\tprocess.on('exit', handleExit);\n\n\t\tif (signal) {\n\t\t\tsignal.addEventListener('abort', signalEventHandler);\n\t\t}\n\t};\n\n\tconst clearHooks = () => {\n\t\tprocess.removeListener('uncaughtExceptionMonitor', errorEventHandler);\n\t\tprocess.removeListener('unhandledRejection', errorEventHandler);\n\t\tprocess.removeListener('SIGINT', signalEventHandler);\n\t\tprocess.removeListener('SIGTERM', signalEventHandler);\n\t\tprocess.removeListener('exit', handleExit);\n\n\t\tif (signal) {\n\t\t\tsignal.removeEventListener('abort', signalEventHandler);\n\t\t}\n\t};\n\n\tconst clearPrevMessage = () => {\n\t\tif (_prevMessage === undefined) return;\n\t\tif (isCI) output.write('\\n');\n\t\tconst wrapped = wrapAnsi(_prevMessage, columns, {\n\t\t\thard: true,\n\t\t\ttrim: false,\n\t\t});\n\t\tconst prevLines = wrapped.split('\\n');\n\t\tif (prevLines.length > 1) {\n\t\t\toutput.write(cursor.up(prevLines.length - 1));\n\t\t}\n\t\toutput.write(cursor.to(0));\n\t\toutput.write(erase.down());\n\t};\n\n\tconst removeTrailingDots = (msg: string): string => {\n\t\treturn msg.replace(/\\.+$/, '');\n\t};\n\n\tconst formatTimer = (origin: number): string => {\n\t\tconst duration = (performance.now() - origin) / 1000;\n\t\tconst min = Math.floor(duration / 60);\n\t\tconst secs = Math.floor(duration % 60);\n\t\treturn min > 0 ? `[${min}m ${secs}s]` : `[${secs}s]`;\n\t};\n\n\tconst hasGuide = opts.withGuide ?? settings.withGuide;\n\n\tconst start = (msg = ''): void => {\n\t\tisSpinnerActive = true;\n\t\tunblock = block({ output });\n\t\t_message = removeTrailingDots(msg);\n\t\t_origin = performance.now();\n\t\tif (hasGuide) {\n\t\t\toutput.write(`${styleText('gray', S_BAR)}\\n`);\n\t\t}\n\t\tlet frameIndex = 0;\n\t\tlet indicatorTimer = 0;\n\t\tregisterHooks();\n\t\tloop = setInterval(() => {\n\t\t\tif (isCI && _message === _prevMessage) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tclearPrevMessage();\n\t\t\t_prevMessage = _message;\n\t\t\tconst frame = styleFn(frames[frameIndex]);\n\t\t\tlet outputMessage: string;\n\n\t\t\tif (isCI) {\n\t\t\t\toutputMessage = `${frame} ${_message}...`;\n\t\t\t} else if (indicator === 'timer') {\n\t\t\t\toutputMessage = `${frame} ${_message} ${formatTimer(_origin)}`;\n\t\t\t} else {\n\t\t\t\tconst loadingDots = '.'.repeat(Math.floor(indicatorTimer)).slice(0, 3);\n\t\t\t\toutputMessage = `${frame} ${_message}${loadingDots}`;\n\t\t\t}\n\n\t\t\tconst wrapped = wrapAnsi(outputMessage, columns, {\n\t\t\t\thard: true,\n\t\t\t\ttrim: false,\n\t\t\t});\n\t\t\toutput.write(wrapped);\n\n\t\t\tframeIndex = frameIndex + 1 < frames.length ? frameIndex + 1 : 0;\n\t\t\t// indicator increase by 1 every 8 frames\n\t\t\tindicatorTimer = indicatorTimer < 4 ? indicatorTimer + 0.125 : 0;\n\t\t}, delay);\n\t};\n\n\tconst _stop = (msg = '', code = 0, silent: boolean = false): void => {\n\t\tif (!isSpinnerActive) return;\n\t\tisSpinnerActive = false;\n\t\tclearInterval(loop);\n\t\tclearPrevMessage();\n\t\tconst step =\n\t\t\tcode === 0\n\t\t\t\t? styleText('green', S_STEP_SUBMIT)\n\t\t\t\t: code === 1\n\t\t\t\t\t? styleText('red', S_STEP_CANCEL)\n\t\t\t\t\t: styleText('red', S_STEP_ERROR);\n\t\t_message = msg ?? _message;\n\t\tif (!silent) {\n\t\t\tif (indicator === 'timer') {\n\t\t\t\toutput.write(`${step} ${_message} ${formatTimer(_origin)}\\n`);\n\t\t\t} else {\n\t\t\t\toutput.write(`${step} ${_message}\\n`);\n\t\t\t}\n\t\t}\n\t\tclearHooks();\n\t\tunblock();\n\t};\n\n\tconst stop = (msg = ''): void => _stop(msg, 0);\n\tconst cancel = (msg = ''): void => _stop(msg, 1);\n\tconst error = (msg = ''): void => _stop(msg, 2);\n\t// TODO (43081j): this will leave the initial S_BAR since we purposely\n\t// don't erase that in `clearPrevMessage`. In future, we may want to treat\n\t// `clear` as a special case and remove the bar too.\n\tconst clear = (): void => _stop('', 0, true);\n\n\tconst message = (msg = ''): void => {\n\t\t_message = removeTrailingDots(msg ?? _message);\n\t};\n\n\treturn {\n\t\tstart,\n\t\tstop,\n\t\tmessage,\n\t\tcancel,\n\t\terror,\n\t\tclear,\n\t\tget isCancelled() {\n\t\t\treturn isCancelled;\n\t\t},\n\t};\n};\n","import { styleText } from 'node:util';\nimport type { State } from '@clack/core';\nimport { unicodeOr } from './common.js';\nimport { type SpinnerOptions, type SpinnerResult, spinner } from './spinner.js';\n\nconst S_PROGRESS_CHAR: Record<NonNullable<ProgressOptions['style']>, string> = {\n\tlight: unicodeOr('─', '-'),\n\theavy: unicodeOr('━', '='),\n\tblock: unicodeOr('█', '#'),\n};\n\nexport interface ProgressOptions extends SpinnerOptions {\n\tstyle?: 'light' | 'heavy' | 'block';\n\tmax?: number;\n\tsize?: number;\n}\n\nexport interface ProgressResult extends SpinnerResult {\n\tadvance(step?: number, msg?: string): void;\n}\n\nexport function progress({\n\tstyle = 'heavy',\n\tmax: userMax = 100,\n\tsize: userSize = 40,\n\t...spinnerOptions\n}: ProgressOptions = {}): ProgressResult {\n\tconst spin = spinner(spinnerOptions);\n\tlet value = 0;\n\tlet previousMessage = '';\n\n\tconst max = Math.max(1, userMax);\n\tconst size = Math.max(1, userSize);\n\n\tconst activeStyle = (state: State) => {\n\t\tswitch (state) {\n\t\t\tcase 'initial':\n\t\t\tcase 'active':\n\t\t\t\treturn (text: string) => styleText('magenta', text);\n\t\t\tcase 'error':\n\t\t\tcase 'cancel':\n\t\t\t\treturn (text: string) => styleText('red', text);\n\t\t\tcase 'submit':\n\t\t\t\treturn (text: string) => styleText('green', text);\n\t\t\tdefault:\n\t\t\t\treturn (text: string) => styleText('magenta', text);\n\t\t}\n\t};\n\tconst drawProgress = (state: State, msg: string) => {\n\t\tconst active = Math.floor((value / max) * size);\n\t\treturn `${activeStyle(state)(S_PROGRESS_CHAR[style].repeat(active))}${styleText('dim', S_PROGRESS_CHAR[style].repeat(size - active))} ${msg}`;\n\t};\n\n\tconst start = (msg = '') => {\n\t\tpreviousMessage = msg;\n\t\tspin.start(drawProgress('initial', msg));\n\t};\n\tconst advance = (step = 1, msg?: string): void => {\n\t\tvalue = Math.min(max, step + value);\n\t\tspin.message(drawProgress('active', msg ?? previousMessage));\n\t\tpreviousMessage = msg ?? previousMessage;\n\t};\n\treturn {\n\t\tstart,\n\t\tstop: spin.stop,\n\t\tcancel: spin.cancel,\n\t\terror: spin.error,\n\t\tclear: spin.clear,\n\t\tadvance,\n\t\tisCancelled: spin.isCancelled,\n\t\tmessage: (msg: string) => advance(0, msg),\n\t};\n}\n","import { styleText } from 'node:util';\nimport { SelectPrompt, settings, wrapTextWithPrefix } from '@clack/core';\nimport {\n\ttype CommonOptions,\n\tS_BAR,\n\tS_BAR_END,\n\tS_RADIO_ACTIVE,\n\tS_RADIO_INACTIVE,\n\tsymbol,\n\tsymbolBar,\n} from './common.js';\nimport { limitOptions } from './limit-options.js';\n\ntype Primitive = Readonly<string | boolean | number>;\n\nexport type Option<Value> = Value extends Primitive\n\t? {\n\t\t\t/**\n\t\t\t * Internal data for this option.\n\t\t\t */\n\t\t\tvalue: Value;\n\t\t\t/**\n\t\t\t * The optional, user-facing text for this option.\n\t\t\t *\n\t\t\t * By default, the `value` is converted to a string.\n\t\t\t */\n\t\t\tlabel?: string;\n\t\t\t/**\n\t\t\t * An optional hint to display to the user when\n\t\t\t * this option might be selected.\n\t\t\t *\n\t\t\t * By default, no `hint` is displayed.\n\t\t\t */\n\t\t\thint?: string;\n\t\t\t/**\n\t\t\t * Whether this option is disabled.\n\t\t\t * Disabled options are visible but cannot be selected.\n\t\t\t *\n\t\t\t * By default, options are not disabled.\n\t\t\t */\n\t\t\tdisabled?: boolean;\n\t\t}\n\t: {\n\t\t\t/**\n\t\t\t * Internal data for this option.\n\t\t\t */\n\t\t\tvalue: Value;\n\t\t\t/**\n\t\t\t * Required. The user-facing text for this option.\n\t\t\t */\n\t\t\tlabel: string;\n\t\t\t/**\n\t\t\t * An optional hint to display to the user when\n\t\t\t * this option might be selected.\n\t\t\t *\n\t\t\t * By default, no `hint` is displayed.\n\t\t\t */\n\t\t\thint?: string;\n\t\t\t/**\n\t\t\t * Whether this option is disabled.\n\t\t\t * Disabled options are visible but cannot be selected.\n\t\t\t *\n\t\t\t * By default, options are not disabled.\n\t\t\t */\n\t\t\tdisabled?: boolean;\n\t\t};\n\nexport interface SelectOptions<Value> extends CommonOptions {\n\tmessage: string;\n\toptions: Option<Value>[];\n\tinitialValue?: Value;\n\tmaxItems?: number;\n}\n\nconst computeLabel = (label: string, format: (text: string) => string) => {\n\tif (!label.includes('\\n')) {\n\t\treturn format(label);\n\t}\n\treturn label\n\t\t.split('\\n')\n\t\t.map((line) => format(line))\n\t\t.join('\\n');\n};\n\nexport const select = <Value>(opts: SelectOptions<Value>) => {\n\tconst opt = (\n\t\toption: Option<Value>,\n\t\tstate: 'inactive' | 'active' | 'selected' | 'cancelled' | 'disabled'\n\t) => {\n\t\tconst label = option.label ?? String(option.value);\n\t\tswitch (state) {\n\t\t\tcase 'disabled':\n\t\t\t\treturn `${styleText('gray', S_RADIO_INACTIVE)} ${computeLabel(label, (text) => styleText('gray', text))}${\n\t\t\t\t\toption.hint ? ` ${styleText('dim', `(${option.hint ?? 'disabled'})`)}` : ''\n\t\t\t\t}`;\n\t\t\tcase 'selected':\n\t\t\t\treturn `${computeLabel(label, (text) => styleText('dim', text))}`;\n\t\t\tcase 'active':\n\t\t\t\treturn `${styleText('green', S_RADIO_ACTIVE)} ${label}${\n\t\t\t\t\toption.hint ? ` ${styleText('dim', `(${option.hint})`)}` : ''\n\t\t\t\t}`;\n\t\t\tcase 'cancelled':\n\t\t\t\treturn `${computeLabel(label, (str) => styleText(['strikethrough', 'dim'], str))}`;\n\t\t\tdefault:\n\t\t\t\treturn `${styleText('dim', S_RADIO_INACTIVE)} ${computeLabel(label, (text) => styleText('dim', text))}`;\n\t\t}\n\t};\n\n\treturn new SelectPrompt({\n\t\toptions: opts.options,\n\t\tsignal: opts.signal,\n\t\tinput: opts.input,\n\t\toutput: opts.output,\n\t\tinitialValue: opts.initialValue,\n\t\trender() {\n\t\t\tconst hasGuide = opts.withGuide ?? settings.withGuide;\n\t\t\tconst titlePrefix = `${symbol(this.state)} `;\n\t\t\tconst titlePrefixBar = `${symbolBar(this.state)} `;\n\t\t\tconst messageLines = wrapTextWithPrefix(\n\t\t\t\topts.output,\n\t\t\t\topts.message,\n\t\t\t\ttitlePrefixBar,\n\t\t\t\ttitlePrefix\n\t\t\t);\n\t\t\tconst title = `${hasGuide ? `${styleText('gray', S_BAR)}\\n` : ''}${messageLines}\\n`;\n\n\t\t\tswitch (this.state) {\n\t\t\t\tcase 'submit': {\n\t\t\t\t\tconst submitPrefix = hasGuide ? `${styleText('gray', S_BAR)} ` : '';\n\t\t\t\t\tconst wrappedLines = wrapTextWithPrefix(\n\t\t\t\t\t\topts.output,\n\t\t\t\t\t\topt(this.options[this.cursor], 'selected'),\n\t\t\t\t\t\tsubmitPrefix\n\t\t\t\t\t);\n\t\t\t\t\treturn `${title}${wrappedLines}`;\n\t\t\t\t}\n\t\t\t\tcase 'cancel': {\n\t\t\t\t\tconst cancelPrefix = hasGuide ? `${styleText('gray', S_BAR)} ` : '';\n\t\t\t\t\tconst wrappedLines = wrapTextWithPrefix(\n\t\t\t\t\t\topts.output,\n\t\t\t\t\t\topt(this.options[this.cursor], 'cancelled'),\n\t\t\t\t\t\tcancelPrefix\n\t\t\t\t\t);\n\t\t\t\t\treturn `${title}${wrappedLines}${hasGuide ? `\\n${styleText('gray', S_BAR)}` : ''}`;\n\t\t\t\t}\n\t\t\t\tdefault: {\n\t\t\t\t\tconst prefix = hasGuide ? `${styleText('cyan', S_BAR)} ` : '';\n\t\t\t\t\tconst prefixEnd = hasGuide ? styleText('cyan', S_BAR_END) : '';\n\t\t\t\t\t// Calculate rowPadding: title lines + footer lines (S_BAR_END + trailing newline)\n\t\t\t\t\tconst titleLineCount = title.split('\\n').length;\n\t\t\t\t\tconst footerLineCount = hasGuide ? 2 : 1; // S_BAR_END + trailing newline (or just trailing newline)\n\t\t\t\t\treturn `${title}${prefix}${limitOptions({\n\t\t\t\t\t\toutput: opts.output,\n\t\t\t\t\t\tcursor: this.cursor,\n\t\t\t\t\t\toptions: this.options,\n\t\t\t\t\t\tmaxItems: opts.maxItems,\n\t\t\t\t\t\tcolumnPadding: prefix.length,\n\t\t\t\t\t\trowPadding: titleLineCount + footerLineCount,\n\t\t\t\t\t\tstyle: (item, active) =>\n\t\t\t\t\t\t\topt(item, item.disabled ? 'disabled' : active ? 'active' : 'inactive'),\n\t\t\t\t\t}).join(`\\n${prefix}`)}\\n${prefixEnd}\\n`;\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t}).prompt() as Promise<Value | symbol>;\n};\n","import { styleText } from 'node:util';\nimport { SelectKeyPrompt, settings, wrapTextWithPrefix } from '@clack/core';\nimport { type CommonOptions, S_BAR, S_BAR_END, symbol } from './common.js';\nimport type { Option } from './select.js';\n\nexport interface SelectKeyOptions<Value extends string> extends CommonOptions {\n\tmessage: string;\n\toptions: Option<Value>[];\n\tinitialValue?: Value;\n\tcaseSensitive?: boolean;\n}\n\nexport const selectKey = <Value extends string>(opts: SelectKeyOptions<Value>) => {\n\tconst opt = (\n\t\toption: Option<Value>,\n\t\tstate: 'inactive' | 'active' | 'selected' | 'cancelled' = 'inactive'\n\t) => {\n\t\tconst label = option.label ?? String(option.value);\n\t\tif (state === 'selected') {\n\t\t\treturn `${styleText('dim', label)}`;\n\t\t}\n\t\tif (state === 'cancelled') {\n\t\t\treturn `${styleText(['strikethrough', 'dim'], label)}`;\n\t\t}\n\t\tif (state === 'active') {\n\t\t\treturn `${styleText(['bgCyan', 'gray'], ` ${option.value} `)} ${label}${\n\t\t\t\toption.hint ? ` ${styleText('dim', `(${option.hint})`)}` : ''\n\t\t\t}`;\n\t\t}\n\t\treturn `${styleText(['gray', 'bgWhite', 'inverse'], ` ${option.value} `)} ${label}${\n\t\t\toption.hint ? ` ${styleText('dim', `(${option.hint})`)}` : ''\n\t\t}`;\n\t};\n\n\treturn new SelectKeyPrompt({\n\t\toptions: opts.options,\n\t\tsignal: opts.signal,\n\t\tinput: opts.input,\n\t\toutput: opts.output,\n\t\tinitialValue: opts.initialValue,\n\t\tcaseSensitive: opts.caseSensitive,\n\t\trender() {\n\t\t\tconst hasGuide = opts.withGuide ?? settings.withGuide;\n\t\t\tconst title = `${hasGuide ? `${styleText('gray', S_BAR)}\\n` : ''}${symbol(this.state)} ${opts.message}\\n`;\n\n\t\t\tswitch (this.state) {\n\t\t\t\tcase 'submit': {\n\t\t\t\t\tconst submitPrefix = hasGuide ? `${styleText('gray', S_BAR)} ` : '';\n\t\t\t\t\tconst selectedOption =\n\t\t\t\t\t\tthis.options.find((opt) => opt.value === this.value) ?? opts.options[0];\n\t\t\t\t\tconst wrapped = wrapTextWithPrefix(\n\t\t\t\t\t\topts.output,\n\t\t\t\t\t\topt(selectedOption, 'selected'),\n\t\t\t\t\t\tsubmitPrefix\n\t\t\t\t\t);\n\t\t\t\t\treturn `${title}${wrapped}`;\n\t\t\t\t}\n\t\t\t\tcase 'cancel': {\n\t\t\t\t\tconst cancelPrefix = hasGuide ? `${styleText('gray', S_BAR)} ` : '';\n\t\t\t\t\tconst wrapped = wrapTextWithPrefix(\n\t\t\t\t\t\topts.output,\n\t\t\t\t\t\topt(this.options[0], 'cancelled'),\n\t\t\t\t\t\tcancelPrefix\n\t\t\t\t\t);\n\t\t\t\t\treturn `${title}${wrapped}${hasGuide ? `\\n${styleText('gray', S_BAR)}` : ''}`;\n\t\t\t\t}\n\t\t\t\tdefault: {\n\t\t\t\t\tconst defaultPrefix = hasGuide ? `${styleText('cyan', S_BAR)} ` : '';\n\t\t\t\t\tconst defaultPrefixEnd = hasGuide ? styleText('cyan', S_BAR_END) : '';\n\t\t\t\t\tconst wrapped = this.options\n\t\t\t\t\t\t.map((option, i) =>\n\t\t\t\t\t\t\twrapTextWithPrefix(\n\t\t\t\t\t\t\t\topts.output,\n\t\t\t\t\t\t\t\topt(option, i === this.cursor ? 'active' : 'inactive'),\n\t\t\t\t\t\t\t\tdefaultPrefix\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.join('\\n');\n\t\t\t\t\treturn `${title}${wrapped}\\n${defaultPrefixEnd}\\n`;\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t}).prompt() as Promise<Value | symbol>;\n};\n","import { stripVTControlCharacters as strip, styleText } from 'node:util';\nimport { S_BAR, S_ERROR, S_INFO, S_STEP_SUBMIT, S_SUCCESS, S_WARN } from './common.js';\nimport type { LogMessageOptions } from './log.js';\n\nconst prefix = `${styleText('gray', S_BAR)} `;\n\n// TODO (43081j): this currently doesn't support custom `output` writables\n// because we rely on `columns` existing (i.e. `process.stdout.columns).\n//\n// If we want to support `output` being passed in, we will need to use\n// a condition like `if (output insance Writable)` to check if it has columns\nexport const stream = {\n\tmessage: async (\n\t\titerable: Iterable<string> | AsyncIterable<string>,\n\t\t{ symbol = styleText('gray', S_BAR) }: LogMessageOptions = {}\n\t) => {\n\t\tprocess.stdout.write(`${styleText('gray', S_BAR)}\\n${symbol} `);\n\t\tlet lineWidth = 3;\n\t\tfor await (let chunk of iterable) {\n\t\t\tchunk = chunk.replace(/\\n/g, `\\n${prefix}`);\n\t\t\tif (chunk.includes('\\n')) {\n\t\t\t\tlineWidth = 3 + strip(chunk.slice(chunk.lastIndexOf('\\n'))).length;\n\t\t\t}\n\t\t\tconst chunkLen = strip(chunk).length;\n\t\t\tif (lineWidth + chunkLen < process.stdout.columns) {\n\t\t\t\tlineWidth += chunkLen;\n\t\t\t\tprocess.stdout.write(chunk);\n\t\t\t} else {\n\t\t\t\tprocess.stdout.write(`\\n${prefix}${chunk.trimStart()}`);\n\t\t\t\tlineWidth = 3 + strip(chunk.trimStart()).length;\n\t\t\t}\n\t\t}\n\t\tprocess.stdout.write('\\n');\n\t},\n\tinfo: (iterable: Iterable<string> | AsyncIterable<string>) => {\n\t\treturn stream.message(iterable, { symbol: styleText('blue', S_INFO) });\n\t},\n\tsuccess: (iterable: Iterable<string> | AsyncIterable<string>) => {\n\t\treturn stream.message(iterable, { symbol: styleText('green', S_SUCCESS) });\n\t},\n\tstep: (iterable: Iterable<string> | AsyncIterable<string>) => {\n\t\treturn stream.message(iterable, { symbol: styleText('green', S_STEP_SUBMIT) });\n\t},\n\twarn: (iterable: Iterable<string> | AsyncIterable<string>) => {\n\t\treturn stream.message(iterable, { symbol: styleText('yellow', S_WARN) });\n\t},\n\t/** alias for `log.warn()`. */\n\twarning: (iterable: Iterable<string> | AsyncIterable<string>) => {\n\t\treturn stream.warn(iterable);\n\t},\n\terror: (iterable: Iterable<string> | AsyncIterable<string>) => {\n\t\treturn stream.message(iterable, { symbol: styleText('red', S_ERROR) });\n\t},\n};\n","import type { CommonOptions } from './common.js';\nimport { spinner } from './spinner.js';\n\nexport type Task = {\n\t/**\n\t * Task title\n\t */\n\ttitle: string;\n\t/**\n\t * Task function\n\t */\n\ttask: (message: (string: string) => void) => string | Promise<string> | void | Promise<void>;\n\n\t/**\n\t * If enabled === false the task will be skipped\n\t */\n\tenabled?: boolean;\n};\n\n/**\n * Define a group of tasks to be executed\n */\nexport const tasks = async (tasks: Task[], opts?: CommonOptions) => {\n\tfor (const task of tasks) {\n\t\tif (task.enabled === false) continue;\n\n\t\tconst s = spinner(opts);\n\t\ts.start(task.title);\n\t\tconst result = await task.task(s.message);\n\t\ts.stop(result || task.title);\n\t}\n};\n","import type { Writable } from 'node:stream';\nimport { styleText } from 'node:util';\nimport { getColumns } from '@clack/core';\nimport { erase } from 'sisteransi';\nimport {\n\ttype CommonOptions,\n\tisCI as isCIFn,\n\tisTTY as isTTYFn,\n\tS_BAR,\n\tS_STEP_SUBMIT,\n} from './common.js';\nimport { log } from './log.js';\n\nexport interface TaskLogOptions extends CommonOptions {\n\ttitle: string;\n\tlimit?: number;\n\tspacing?: number;\n\tretainLog?: boolean;\n}\n\nexport interface TaskLogMessageOptions {\n\traw?: boolean;\n}\n\nexport interface TaskLogCompletionOptions {\n\tshowLog?: boolean;\n}\n\ninterface BufferEntry {\n\theader?: string;\n\tvalue: string;\n\tfull: string;\n\tresult?: {\n\t\tstatus: 'success' | 'error';\n\t\tmessage: string;\n\t};\n}\n\nconst stripDestructiveANSI = (input: string): string => {\n\t// biome-ignore lint/suspicious/noControlCharactersInRegex: intentional\n\treturn input.replace(/\\x1b\\[(?:\\d+;)*\\d*[ABCDEFGHfJKSTsu]|\\x1b\\[(s|u)/g, '');\n};\n\n/**\n * Renders a log which clears on success and remains on failure\n */\nexport const taskLog = (opts: TaskLogOptions) => {\n\tconst output: Writable = opts.output ?? process.stdout;\n\tconst columns = getColumns(output);\n\tconst secondarySymbol = styleText('gray', S_BAR);\n\tconst spacing = opts.spacing ?? 1;\n\tconst barSize = 3;\n\tconst retainLog = opts.retainLog === true;\n\tconst isTTY = !isCIFn() && isTTYFn(output);\n\n\toutput.write(`${secondarySymbol}\\n`);\n\toutput.write(`${styleText('green', S_STEP_SUBMIT)} ${opts.title}\\n`);\n\tfor (let i = 0; i < spacing; i++) {\n\t\toutput.write(`${secondarySymbol}\\n`);\n\t}\n\n\tconst buffers: BufferEntry[] = [\n\t\t{\n\t\t\tvalue: '',\n\t\t\tfull: '',\n\t\t},\n\t];\n\tlet lastMessageWasRaw = false;\n\n\tconst clear = (clearTitle: boolean): void => {\n\t\tif (buffers.length === 0) {\n\t\t\treturn;\n\t\t}\n\n\t\tlet lines = 0;\n\n\t\tif (clearTitle) {\n\t\t\tlines += spacing + 2;\n\t\t}\n\n\t\tfor (const buffer of buffers) {\n\t\t\tconst { value, result } = buffer;\n\t\t\tlet text = result?.message ?? value;\n\n\t\t\tif (text.length === 0) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (result === undefined && buffer.header !== undefined && buffer.header !== '') {\n\t\t\t\ttext += `\\n${buffer.header}`;\n\t\t\t}\n\n\t\t\tconst bufferHeight = text.split('\\n').reduce((count, line) => {\n\t\t\t\tif (line === '') {\n\t\t\t\t\treturn count + 1;\n\t\t\t\t}\n\t\t\t\treturn count + Math.ceil((line.length + barSize) / columns);\n\t\t\t}, 0);\n\n\t\t\tlines += bufferHeight;\n\t\t}\n\n\t\tif (lines > 0) {\n\t\t\tlines += 1;\n\t\t\toutput.write(erase.lines(lines));\n\t\t}\n\t};\n\tconst printBuffer = (buffer: BufferEntry, messageSpacing?: number, full?: boolean): void => {\n\t\tconst messages = full ? `${buffer.full}\\n${buffer.value}` : buffer.value;\n\t\tif (buffer.header !== undefined && buffer.header !== '') {\n\t\t\tlog.message(\n\t\t\t\tbuffer.header.split('\\n').map((line) => styleText('bold', line)),\n\t\t\t\t{\n\t\t\t\t\toutput,\n\t\t\t\t\tsecondarySymbol,\n\t\t\t\t\tsymbol: secondarySymbol,\n\t\t\t\t\tspacing: 0,\n\t\t\t\t}\n\t\t\t);\n\t\t}\n\t\tlog.message(\n\t\t\tmessages.split('\\n').map((line) => styleText('dim', line)),\n\t\t\t{\n\t\t\t\toutput,\n\t\t\t\tsecondarySymbol,\n\t\t\t\tsymbol: secondarySymbol,\n\t\t\t\tspacing: messageSpacing ?? spacing,\n\t\t\t}\n\t\t);\n\t};\n\tconst renderBuffer = (): void => {\n\t\tfor (const buffer of buffers) {\n\t\t\tconst { header, value, full } = buffer;\n\t\t\tif ((header === undefined || header.length === 0) && value.length === 0) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tprintBuffer(buffer, undefined, retainLog === true && full.length > 0);\n\t\t}\n\t};\n\tconst message = (buffer: BufferEntry, msg: string, mopts?: TaskLogMessageOptions) => {\n\t\tclear(false);\n\t\tif ((mopts?.raw !== true || !lastMessageWasRaw) && buffer.value !== '') {\n\t\t\tbuffer.value += '\\n';\n\t\t}\n\t\tbuffer.value += stripDestructiveANSI(msg);\n\t\tlastMessageWasRaw = mopts?.raw === true;\n\t\tif (opts.limit !== undefined) {\n\t\t\tconst lines = buffer.value.split('\\n');\n\t\t\tconst linesToRemove = lines.length - opts.limit;\n\t\t\tif (linesToRemove > 0) {\n\t\t\t\tconst removedLines = lines.splice(0, linesToRemove);\n\t\t\t\tif (retainLog) {\n\t\t\t\t\tbuffer.full += (buffer.full === '' ? '' : '\\n') + removedLines.join('\\n');\n\t\t\t\t}\n\t\t\t}\n\t\t\tbuffer.value = lines.join('\\n');\n\t\t}\n\t\tif (isTTY) {\n\t\t\tprintBuffers();\n\t\t}\n\t};\n\tconst printBuffers = (): void => {\n\t\tfor (const buffer of buffers) {\n\t\t\tif (buffer.result) {\n\t\t\t\tif (buffer.result.status === 'error') {\n\t\t\t\t\tlog.error(buffer.result.message, { output, secondarySymbol, spacing: 0 });\n\t\t\t\t} else {\n\t\t\t\t\tlog.success(buffer.result.message, { output, secondarySymbol, spacing: 0 });\n\t\t\t\t}\n\t\t\t} else if (buffer.value !== '') {\n\t\t\t\tprintBuffer(buffer, 0);\n\t\t\t}\n\t\t}\n\t};\n\tconst completeBuffer = (buffer: BufferEntry, result: BufferEntry['result']): void => {\n\t\tclear(false);\n\n\t\tbuffer.result = result;\n\n\t\tif (isTTY) {\n\t\t\tprintBuffers();\n\t\t}\n\t};\n\n\treturn {\n\t\tmessage(msg: string, mopts?: TaskLogMessageOptions) {\n\t\t\tmessage(buffers[0], msg, mopts);\n\t\t},\n\t\tgroup(name: string) {\n\t\t\tconst buffer: BufferEntry = {\n\t\t\t\theader: name,\n\t\t\t\tvalue: '',\n\t\t\t\tfull: '',\n\t\t\t};\n\t\t\tbuffers.push(buffer);\n\t\t\treturn {\n\t\t\t\tmessage(msg: string, mopts?: TaskLogMessageOptions) {\n\t\t\t\t\tmessage(buffer, msg, mopts);\n\t\t\t\t},\n\t\t\t\terror(message: string) {\n\t\t\t\t\tcompleteBuffer(buffer, {\n\t\t\t\t\t\tstatus: 'error',\n\t\t\t\t\t\tmessage,\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t\tsuccess(message: string) {\n\t\t\t\t\tcompleteBuffer(buffer, {\n\t\t\t\t\t\tstatus: 'success',\n\t\t\t\t\t\tmessage,\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t};\n\t\t},\n\t\terror(message: string, opts?: TaskLogCompletionOptions): void {\n\t\t\tclear(true);\n\t\t\tlog.error(message, { output, secondarySymbol, spacing: 1 });\n\t\t\tif (opts?.showLog !== false) {\n\t\t\t\trenderBuffer();\n\t\t\t}\n\t\t\t// clear buffer since error is an end state\n\t\t\tbuffers.splice(1, buffers.length - 1);\n\t\t\tbuffers[0].value = '';\n\t\t\tbuffers[0].full = '';\n\t\t},\n\t\tsuccess(message: string, opts?: TaskLogCompletionOptions): void {\n\t\t\tclear(true);\n\t\t\tlog.success(message, { output, secondarySymbol, spacing: 1 });\n\t\t\tif (opts?.showLog === true) {\n\t\t\t\trenderBuffer();\n\t\t\t}\n\t\t\t// clear buffer since success is an end state\n\t\t\tbuffers.splice(1, buffers.length - 1);\n\t\t\tbuffers[0].value = '';\n\t\t\tbuffers[0].full = '';\n\t\t},\n\t};\n};\n","import { styleText } from 'node:util';\nimport { settings, TextPrompt } from '@clack/core';\nimport { type CommonOptions, S_BAR, S_BAR_END, symbol } from './common.js';\n\nexport interface TextOptions extends CommonOptions {\n\tmessage: string;\n\tplaceholder?: string;\n\tdefaultValue?: string;\n\tinitialValue?: string;\n\tvalidate?: (value: string | undefined) => string | Error | undefined;\n}\n\nexport const text = (opts: TextOptions) => {\n\treturn new TextPrompt({\n\t\tvalidate: opts.validate,\n\t\tplaceholder: opts.placeholder,\n\t\tdefaultValue: opts.defaultValue,\n\t\tinitialValue: opts.initialValue,\n\t\toutput: opts.output,\n\t\tsignal: opts.signal,\n\t\tinput: opts.input,\n\t\trender() {\n\t\t\tconst hasGuide = opts?.withGuide ?? settings.withGuide;\n\t\t\tconst titlePrefix = `${hasGuide ? `${styleText('gray', S_BAR)}\\n` : ''}${symbol(this.state)} `;\n\t\t\tconst title = `${titlePrefix}${opts.message}\\n`;\n\t\t\tconst placeholder = opts.placeholder\n\t\t\t\t? styleText('inverse', opts.placeholder[0]) + styleText('dim', opts.placeholder.slice(1))\n\t\t\t\t: styleText(['inverse', 'hidden'], '_');\n\t\t\tconst userInput = !this.userInput ? placeholder : this.userInputWithCursor;\n\t\t\tconst value = this.value ?? '';\n\n\t\t\tswitch (this.state) {\n\t\t\t\tcase 'error': {\n\t\t\t\t\tconst errorText = this.error ? ` ${styleText('yellow', this.error)}` : '';\n\t\t\t\t\tconst errorPrefix = hasGuide ? `${styleText('yellow', S_BAR)} ` : '';\n\t\t\t\t\tconst errorPrefixEnd = hasGuide ? styleText('yellow', S_BAR_END) : '';\n\t\t\t\t\treturn `${title.trim()}\\n${errorPrefix}${userInput}\\n${errorPrefixEnd}${errorText}\\n`;\n\t\t\t\t}\n\t\t\t\tcase 'submit': {\n\t\t\t\t\tconst valueText = value ? ` ${styleText('dim', value)}` : '';\n\t\t\t\t\tconst submitPrefix = hasGuide ? styleText('gray', S_BAR) : '';\n\t\t\t\t\treturn `${title}${submitPrefix}${valueText}`;\n\t\t\t\t}\n\t\t\t\tcase 'cancel': {\n\t\t\t\t\tconst valueText = value ? ` ${styleText(['strikethrough', 'dim'], value)}` : '';\n\t\t\t\t\tconst cancelPrefix = hasGuide ? styleText('gray', S_BAR) : '';\n\t\t\t\t\treturn `${title}${cancelPrefix}${valueText}${value.trim() ? `\\n${cancelPrefix}` : ''}`;\n\t\t\t\t}\n\t\t\t\tdefault: {\n\t\t\t\t\tconst defaultPrefix = hasGuide ? `${styleText('cyan', S_BAR)} ` : '';\n\t\t\t\t\tconst defaultPrefixEnd = hasGuide ? styleText('cyan', S_BAR_END) : '';\n\t\t\t\t\treturn `${title}${defaultPrefix}${userInput}\\n${defaultPrefixEnd}\\n`;\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t}).prompt() as Promise<string | symbol>;\n};\n","/**\n * Update command — 비대화형 정책 파일 갱신.\n *\n * 왜 있나: `install`·`list`·`uninstall` 은 전부 플래그로 비대화형 실행이 되는데 `update` 만\n * 위저드 전용이었다. CI 로 하네스를 **깔 수는 있는데 갱신할 수는 없다**는 건 수요의 문제가\n * 아니라 명령 계열의 비대칭이다 — 빠진 쪽이 사유를 대야 한다 (사용자 지시 2026-07-20).\n *\n * 동작: 기존 설치를 감지해 위저드의 update 액션과 **동일한 spec**(`buildUpdateSpec`)으로\n * `mode: \"update\"` 파이프라인을 돈다. 백업은 update mode 가 자동으로 뜬다 (`.claude.backup-*`).\n *\n * 하지 않는 것: track 추가(=`install`), 자산 재설치, 대화형 확인. update 는 이미 깔린\n * 정책 파일만 최신판으로 맞춘다.\n */\n\nimport { resolve } from \"node:path\";\nimport { c, status } from \"../design.js\";\nimport { type DetectedInstall, detectInstallState } from \"../state.js\";\nimport { type InstallSpec, UPDATE_GROUPS } from \"../types.js\";\nimport { buildUpdateSpec, parseUpdateOnly } from \"../update-mode.js\";\nimport { type ExecuteSpecDeps, executeSpec } from \"./install.js\";\n\nexport interface UpdateOptions {\n projectDir?: string;\n /** #480 — `--only <group>` (반복). cac 는 한 번이면 string, 여러 번이면 string[] 을 준다. */\n only?: string | string[];\n}\n\nexport interface UpdateActionDeps {\n log?: (msg: string) => void;\n err?: (msg: string) => void;\n exit?: (code: number) => never;\n detect?: (projectDir: string) => DetectedInstall;\n execute?: (spec: InstallSpec, deps: ExecuteSpecDeps) => void;\n}\n\nexport function updateAction(options: UpdateOptions = {}, deps: UpdateActionDeps = {}): void {\n const log = deps.log ?? console.log;\n const err = deps.err ?? console.error;\n const exit = deps.exit ?? ((code: number) => process.exit(code) as never);\n const detect = deps.detect ?? detectInstallState;\n const execute = deps.execute ?? executeSpec;\n\n const projectDir = resolve(options.projectDir ?? process.cwd());\n const state = detect(projectDir);\n\n // Pre-flight: 갱신할 대상이 없으면 조용히 성공하지 않는다. 파이프라인도 같은 조건에서\n // throw 하지만, 그건 \"install failed\" 로 렌더돼 원인이 안 보인다.\n // v26.135.0 (#253) — 판정 기준이 `.claude/` 존재에서 **설치 여부**로 바뀌었다. update 는\n // v26.134.0(ADR-049)부터 외부 CLI 산출물도 갱신하므로, `.claude/` 없는 opencode/codex 단독\n // 설치도 정당한 update 대상이다. `.claude/` 로 막으면 갱신할 게 있는데 거절한다.\n if (state.state === \"new\") {\n err(status.failure(c.red(`No harness install found at ${projectDir}`)));\n err(c.dim(\" Run `agent-harness install --track <name>` first.\"));\n exit(1);\n return;\n }\n\n // cac 는 `--only` 를 값 없이 주면 boolean true 를 준다 — \"true\" 가 모르는 값으로 거절되게 문자열로.\n const rawOnly =\n options.only === undefined\n ? []\n : [options.only].flat().map((v) => (typeof v === \"string\" ? v : \"(missing value)\"));\n const parsed = parseUpdateOnly(rawOnly);\n if (!parsed.ok) {\n err(status.failure(c.red(`Unknown --only value: ${parsed.invalid.join(\", \")}`)));\n err(c.dim(` Valid groups: ${UPDATE_GROUPS.join(\" | \")}`));\n exit(1);\n return;\n }\n log(c.dim(`Updating installed harness files in ${projectDir}`));\n execute(buildUpdateSpec(projectDir, state.tracks, parsed.groups), {\n log,\n err,\n exit,\n mode: \"update\",\n });\n}\n\nexport function registerUpdateCommand(cli: import(\"../cli.js\").Cli): void {\n cli\n .command(\n \"update\",\n \"Refresh installed policy files (rules / agents / commands / hooks / skills)\",\n )\n .option(\"--project-dir <path>\", \"[Project] Target project directory\", {\n default: process.cwd(),\n })\n .option(\n \"--only <group>\",\n `[Scope] Update only this group (repeatable): ${UPDATE_GROUPS.join(\" | \")}`,\n )\n /* v8 ignore next 3 — cac action callback. updateAction 자체는 별도 tests 로 검증. */\n .action((options: UpdateOptions) => {\n updateAction(options);\n });\n}\n","import { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { readInstallLog } from \"./install-log.js\";\nimport { isTrack, type Track } from \"./types.js\";\n\nexport type InstallState = \"new\" | \"existing\";\n\nexport interface DetectedInstall {\n state: InstallState;\n tracks: Track[];\n /**\n * Source of the detected tracks: \"metafile\" (v27.17+), \"legacy\" (rules/*.md heuristic),\n * \"install-log\" (v26.135.0 — `.claude/` 없는 설치), or \"none\".\n */\n source: \"metafile\" | \"legacy\" | \"install-log\" | \"none\";\n /**\n * `.claude/` 가 실제로 있는가. **\"설치됐는가\"가 아니다** — opencode/codex 단독 설치는\n * `.claude/` 없이도 설치 상태다 (v26.135.0 · #253). 설치 여부는 `state` 를 봐라.\n */\n hasClaudeDir: boolean;\n}\n\nconst META_FILE = \".claude/.installed-tracks\";\n\ninterface LegacySignature {\n rule: string;\n track: Track;\n}\n\nconst LEGACY_SIGNATURES: readonly LegacySignature[] = [\n { rule: \"htmx.md\", track: \"ssr-htmx\" },\n { rule: \"nextjs.md\", track: \"ssr-nextjs\" },\n { rule: \"data-analysis.md\", track: \"data\" },\n { rule: \"pyside6.md\", track: \"data\" },\n { rule: \"cli-development.md\", track: \"tooling\" },\n];\n\n/**\n * Detect what was previously installed in the given project directory.\n * Mirrors the v27.17 detect_install_state shell function (setup-harness.sh:191).\n */\nexport function detectInstallState(projectDir: string): DetectedInstall {\n const claudeDir = join(projectDir, \".claude\");\n const hasClaudeDir = existsSync(claudeDir);\n\n if (hasClaudeDir) {\n const metaPath = join(projectDir, META_FILE);\n if (existsSync(metaPath)) {\n const tracks = readMetafile(metaPath);\n return { state: \"existing\", tracks, source: \"metafile\", hasClaudeDir: true };\n }\n const tracks = inferFromLegacySignatures(projectDir);\n return { state: \"existing\", tracks, source: \"legacy\", hasClaudeDir: true };\n }\n\n // v26.135.0 (#253) — `.claude/` 부재 ≠ 미설치. opencode/codex 단독 설치는 `.claude/` 를\n // 만들지 않는다(만들던 것이 그 이슈다). 설치 로그가 있으면 설치된 것이고, 이걸 안 보면\n // 위저드가 기설치 프로젝트에 \"new install\" 흐름을 태운다.\n const log = readInstallLog(projectDir);\n if (log) {\n return {\n state: \"existing\",\n tracks: tracksFromLog(log.spec.tracks),\n source: \"install-log\",\n hasClaudeDir: false,\n };\n }\n\n return { state: \"new\", tracks: [], source: \"none\", hasClaudeDir: false };\n}\n\n/** 로그의 tracks 는 문자열이라 현재 Track 어휘로 좁힌다 (구 버전 로그에 폐기된 track 이 있을 수 있다). */\nfunction tracksFromLog(tracks: ReadonlyArray<string>): Track[] {\n return [...new Set(tracks.filter(isTrack))].sort();\n}\n\nfunction readMetafile(path: string): Track[] {\n const raw = readFileSync(path, \"utf8\");\n const seen = new Set<Track>();\n for (const line of raw.split(/\\s+/)) {\n const trimmed = line.trim();\n if (isTrack(trimmed)) {\n seen.add(trimmed);\n }\n }\n return [...seen].sort();\n}\n\nfunction inferFromLegacySignatures(projectDir: string): Track[] {\n const rulesDir = join(projectDir, \".claude/rules\");\n if (!existsSync(rulesDir)) {\n return [];\n }\n const found = new Set<Track>();\n for (const sig of LEGACY_SIGNATURES) {\n if (existsSync(join(rulesDir, sig.rule))) {\n found.add(sig.track);\n }\n }\n return [...found].sort();\n}\n","import { BASELINE_PREFIX, isBaselineExcluded, listBaselineTargets } from \"./baseline-targets.js\";\nimport {\n formatResidentCostLine,\n landsOnDisk,\n residentCost,\n summarizeContextCost,\n} from \"./context-cost.js\";\nimport { assetReachesCli, EXTERNAL_ASSETS } from \"./external-assets.js\";\nimport { readInstallLog } from \"./install-log.js\";\nimport { buildManifestSpec, type InstallMode } from \"./installer.js\";\nimport { buildManifest } from \"./manifest.js\";\nimport {\n finalSelectedAssets,\n groupAssetsByCategory,\n recommendedExternalAssets,\n} from \"./preset-recommend.js\";\nimport {\n defaultPrompts,\n type InstallTargetId,\n type Prompts,\n VISIBLE_OPTION_DEFS,\n} from \"./prompts.js\";\nimport { type DetectedInstall, detectInstallState } from \"./state.js\";\nimport { type InstallSpec, type OptionFlags, type Track, UPDATE_GROUPS } from \"./types.js\";\nimport { buildUpdateSpec } from \"./update-mode.js\";\nimport { stepLabel, WIZARD } from \"./wizard-steps.js\";\n\n/**\n * v26.54.0 — All-in-one 결과 → option keys + asset id list 분리.\n * `option:<key>` → OptionFlags key\n * `asset:<id>` → EXTERNAL_ASSETS id\n */\nexport function splitInstallTargets(targets: ReadonlyArray<InstallTargetId>): {\n optionKeys: Array<keyof OptionFlags>;\n assetIds: Array<string>;\n /** 2026-08-16 — 사용자가 **체크한 채로 둔** baseline id. 제외는 이것의 여집합으로 낸다. */\n baselineIds: Array<string>;\n} {\n const optionKeys: Array<keyof OptionFlags> = [];\n const assetIds: Array<string> = [];\n const baselineIds: Array<string> = [];\n for (const t of targets) {\n if (t.startsWith(\"option:\")) {\n optionKeys.push(t.slice(\"option:\".length) as keyof OptionFlags);\n } else if (t.startsWith(\"asset:\")) {\n assetIds.push(t.slice(\"asset:\".length));\n } else if (t.startsWith(BASELINE_PREFIX)) {\n baselineIds.push(t);\n }\n }\n return { optionKeys, assetIds, baselineIds };\n}\n\n/**\n * 체크 결과 → **제외 목록**. 화면은 \"설치할 것\"을 체크로 보여주는데 spec 이 받는 것은\n * \"빼는 것\"이라, 이 뒤집기를 한 곳에 둔다.\n *\n * 전부 체크된 기본 상태에서는 빈 배열이 나온다 — 즉 아무것도 안 고른 사용자의 설치 결과는\n * 이 기능이 생기기 전과 **바이트 단위로 같다**.\n */\nexport function baselineExcludeFrom(\n offered: ReadonlyArray<{ id: string }>,\n checked: ReadonlyArray<string>,\n): string[] {\n const keep = new Set(checked);\n return offered.filter((t) => !keep.has(t.id)).map((t) => t.id);\n}\n\n/**\n * v26.81.0 (ADR-022) — 자산 1:1 boolean 13종 삭제 후 잔존 동작 옵션만 매핑.\n * wizard 의 자산 선택은 전부 `asset:<id>` → userOverride.forceInclude 경로.\n */\nexport function toOptionFlags(keys: ReadonlyArray<keyof OptionFlags>): OptionFlags {\n const picked = new Set<keyof OptionFlags>(keys);\n return {\n withPrune: picked.has(\"withPrune\"),\n withCodexTrust: picked.has(\"withCodexTrust\"),\n };\n}\n\nexport interface InteractiveDeps {\n prompts?: Prompts;\n detect?: (projectDir: string) => DetectedInstall;\n isTty?: () => boolean;\n /** v26.125.0 — 설치 상태 주입 (테스트용). 미주입 시 install log 를 읽는다. */\n readInstalled?: (projectDir: string) => InstalledTargetState;\n}\n\n/**\n * v26.125.0 — wizard 가 참조하는 설치 상태.\n *\n * `installed` 와 `projectScoped` 를 나누는 이유: global scope 자산까지 사전 체크하면\n * step 4 에서 project 를 고른 순간 **같은 자산이 project 에 한 벌 더 깔린다**. 그래서\n * 표시(마커)는 전부 하고, 사전 체크는 project scope 만 한다.\n */\nexport interface InstalledTargetState {\n /** 마커를 붙일 자산 id — scope 무관 (설치돼 있다는 사실 자체는 참) */\n installed: ReadonlyArray<string>;\n /** 사전 체크할 자산 id — project scope 만 */\n projectScoped: ReadonlyArray<string>;\n}\n\n/** install log → wizard 표시용 설치 상태. 로그가 없으면(최초 설치) 빈 상태. */\nexport function installedTargetState(projectDir: string): InstalledTargetState {\n const log = readInstallLog(projectDir);\n if (!log) return { installed: [], projectScoped: [] };\n return {\n installed: log.assets.map((a) => a.id),\n projectScoped: log.assets.filter((a) => a.scope !== \"global\").map((a) => a.id),\n };\n}\n\n/**\n * step 3 의 초기 체크 = **트랙 추천 ∪ 이미 설치된 project 자산**.\n *\n * 추천만 쓰면 추천 밖의 설치된 자산이 빈칸으로 보여, 사용자가 \"안 깔렸다\"고 읽는다.\n * 합집합이므로 중복은 생기지 않는다 (헤더의 `n/m ✓` 카운트가 거짓이 되지 않아야 한다).\n */\nexport function initialTargetSelection(\n tracks: ReadonlyArray<Track>,\n installedProjectAssetIds: ReadonlyArray<string>,\n): InstallTargetId[] {\n const ids = new Set<string>(recommendedExternalAssets(tracks));\n for (const id of installedProjectAssetIds) ids.add(id);\n const assets = [...ids].map((id) => `asset:${id}` as InstallTargetId);\n // 트랙이 고르는 자산은 **전부 체크된 채로** 시작한다. 기본값을 바꾸는 것이 아니라 기본값을\n // 보이게 하는 것이 목적이다 — 아무것도 안 건드리면 설치 결과는 이전과 같다.\n const baseline = listBaselineTargets({ tracks }).map((t) => t.id as InstallTargetId);\n return [...assets, ...baseline];\n}\n\nexport interface InteractiveResult {\n ok: boolean;\n spec?: InstallSpec;\n mode?: InstallMode;\n reason?: \"no-tty\" | \"cancelled\" | \"disabled-action\" | \"exit\";\n message?: string;\n}\n\n/**\n * v26.54.0 — 3-step wizard. SPEC: docs/specs/v26-54-all-in-one-installer.md\n *\n * Step 1: tracks (ESC = exit + cancel msg)\n * Step 2: cli (ESC = silent back to tracks)\n * Step 3: install-targets all-in-one (ESC = silent back to cli)\n * confirm prompt (ESC = silent back to targets)\n *\n * 이전 5-step 의 options + 2-tier asset navigator 를 step 3 1 화면 group multiselect 로 흡수.\n */\nexport async function runInteractive(\n projectDir: string,\n deps: InteractiveDeps = {},\n): Promise<InteractiveResult> {\n const prompts = deps.prompts ?? defaultPrompts;\n const detect = deps.detect ?? detectInstallState;\n const isTty = deps.isTty ?? (() => Boolean(process.stdin.isTTY));\n\n if (!isTty()) {\n return {\n ok: false,\n reason: \"no-tty\",\n message:\n \"Interactive mode requires a TTY. Use `agent-harness install --track <name>` for non-interactive use.\",\n };\n }\n\n prompts.intro(\"uzys-agent-harness installer\");\n const state = detect(projectDir);\n // v26.125.0 — 위저드가 설치 상태를 읽는다. 이전에는 step 3 의 체크가 트랙 추천에서만 나와\n // **이미 깔린 자산이 빈칸으로 보였다** — 사용자가 그 체크박스를 설치 상태로 읽으므로 화면이\n // 거짓을 말한 셈이다. 마커는 표시 전용이고 체크를 풀어도 제거되지 않는다 (제거 = `uninstall`).\n const installed = (deps.readInstalled ?? installedTargetState)(projectDir);\n\n let initialTracks: Track[] | undefined;\n let mode: InstallMode = \"fresh\";\n if (state.state === \"existing\") {\n const action = await prompts.selectAction(state);\n if (action === null) {\n prompts.cancel(\"Cancelled.\");\n return { ok: false, reason: \"cancelled\" };\n }\n if (action === \"exit\") {\n prompts.outro(\"Exiting without changes.\");\n return { ok: false, reason: \"exit\" };\n }\n if (action === \"remove\") {\n prompts.cancel(\"Track removal is not automated — manually edit `.claude/`. Aborting.\");\n return { ok: false, reason: \"disabled-action\" };\n }\n if (action === \"update\") {\n mode = \"update\";\n // spec 은 `buildUpdateSpec` 단일 출처 — 비대화형 `update` 명령과 같은 것을 쓴다.\n // #480 — 무엇을 갱신할지 고른다. 프롬프트가 없는 구현(테스트 픽스처)은 전부.\n const groups = prompts.selectUpdateGroups ? await prompts.selectUpdateGroups() : null;\n if (groups === null && prompts.selectUpdateGroups) {\n prompts.cancel(\"Cancelled.\");\n return { ok: false, reason: \"cancelled\" };\n }\n const spec = buildUpdateSpec(projectDir, state.tracks, groups ?? undefined);\n const scopeLine = spec.updateOnly\n ? `UPDATE ${spec.updateOnly.join(\" · \")} only (untouched: ${UPDATE_GROUPS.filter((g) => !spec.updateOnly?.includes(g)).join(\", \")}):`\n : \"UPDATE installed harness files:\";\n const confirmed = await prompts.confirmInstall(`${scopeLine}\\n${formatSummary(spec)}`);\n if (!confirmed) {\n prompts.outro(\"Cancelled.\");\n return { ok: false, reason: \"cancelled\" };\n }\n prompts.outro(\"Running update mode...\");\n return { ok: true, mode: \"update\", spec };\n }\n if (action === \"add\") {\n mode = \"add\";\n initialTracks = state.tracks;\n } else if (action === \"reinstall\") {\n mode = \"reinstall\";\n }\n }\n\n // v26.64.0 (ADR-020) — scope step 추가. Default \"project\". Step 3.5 (targets 직후, confirm 직전).\n type Step = \"tracks\" | \"cli\" | \"targets\" | \"scope\" | \"confirm\";\n let step: Step = \"tracks\";\n let tracks: Track[] | null = null;\n let cli: import(\"./types.js\").CliTargets | null = null;\n let targetSelections: ReadonlyArray<InstallTargetId> | null = null;\n let scope: import(\"./types.js\").InstallScope = \"project\";\n\n while (true) {\n if (step === \"tracks\") {\n const result = await prompts.selectTracks(tracks ?? initialTracks, WIZARD.TRACKS);\n if (result === null) {\n // Step 1 ESC = exit with cancel message (only step where ESC is \"cancel\")\n prompts.cancel(\"Cancelled.\");\n return { ok: false, reason: \"cancelled\" };\n }\n // preset 변경 감지 → install-targets reset (v26.50 정책 유지)\n if (tracks !== null && !tracksEqual(tracks, result)) {\n targetSelections = null;\n }\n tracks = result;\n step = \"cli\";\n } else if (step === \"cli\") {\n const result = await prompts.selectCli(cli ?? [\"claude\"], WIZARD.CLI);\n if (result === null) {\n step = \"tracks\"; // silent back\n continue;\n }\n cli = result;\n step = \"targets\";\n } else if (step === \"targets\") {\n const initial: InstallTargetId[] =\n targetSelections !== null\n ? [...targetSelections]\n : initialTargetSelection(tracks ?? [], installed.projectScoped);\n // v26.65.0 — step indicator SSOT (wizard-steps.ts). Phase: 3 targets → 4 scope → 5 confirm → 6 install.\n const result = await prompts.selectInstallTargets(initial, WIZARD.TARGETS, {\n tracks: tracks ?? [],\n cli: cli ?? [\"claude\"],\n installed: installed.installed.map((id) => `asset:${id}`),\n });\n if (result === null) {\n step = \"cli\"; // silent back\n continue;\n }\n targetSelections = result;\n step = \"scope\";\n } else if (step === \"scope\") {\n // v26.64.0 (ADR-020) — Installation scope select. Default \"project\" (D16).\n const result = await prompts.selectScope(scope, WIZARD.SCOPE);\n if (result === null) {\n step = \"targets\"; // silent back\n continue;\n }\n scope = result;\n step = \"confirm\";\n } else {\n // confirm\n // biome-ignore lint/style/noNonNullAssertion: confirm step 도달 = 모든 이전 step 완료 보장\n const finalTracks = tracks!;\n // biome-ignore lint/style/noNonNullAssertion: same as above\n const finalCli = cli!;\n const { optionKeys, assetIds, baselineIds } = splitInstallTargets(targetSelections ?? []);\n const options = toOptionFlags(optionKeys);\n const userOverride =\n targetSelections === null ? undefined : computeUserOverride(finalTracks, assetIds);\n // 제외는 \"화면에 낸 것\" 대비로 낸다. 화면에 안 낸 자산(`settings.json` 등)은 후보가\n // 아니므로 어떤 조작으로도 빠지지 않는다.\n const baselineExclude =\n targetSelections === null\n ? []\n : baselineExcludeFrom(listBaselineTargets({ tracks: finalTracks }), baselineIds);\n // v26.64.0 (ADR-020) — Confirm summary 에 SCOPE 명시 (사용자 인지 + D16).\n const scopeLabel =\n scope === \"global\"\n ? \"Global (writes to ~/.claude/, ~/.codex/, npm -g)\"\n : \"Project (current directory only)\";\n const summary = `${formatSummary({\n tracks: finalTracks,\n options,\n cli: finalCli,\n projectDir,\n ...(userOverride ? { userOverride } : {}),\n // 해제 목록을 안 넘기면 이 화면이 **제외 유무와 문자열이 완전히 같아진다** — 같은 화면이\n // 외부 자산 제거는 `-User removed:` 로 이미 보고하므로, 없음은 \"아무것도 안 빠졌다\"로\n // 읽힌다. 상주 비용을 줄이려고 20개를 푼 사용자가 그대로인 숫자를 보게 된다.\n ...(baselineExclude.length > 0 ? { baselineExclude } : {}),\n })}\\n SCOPE ${scopeLabel}`;\n const confirmed = await prompts.confirmInstall(\n `${stepLabel(WIZARD.CONFIRM, \"Confirm\")}\\n${summary}`,\n );\n if (confirmed === null) {\n step = \"scope\"; // silent back\n continue;\n }\n if (!confirmed) {\n prompts.outro(\"Cancelled by user.\");\n return { ok: false, reason: \"cancelled\" };\n }\n\n const spec: InstallSpec = {\n tracks: finalTracks,\n options,\n cli: finalCli,\n projectDir,\n scope,\n ...(userOverride ? { userOverride } : {}),\n ...(baselineExclude.length > 0 ? { baselineExclude } : {}),\n };\n\n prompts.outro(stepLabel(WIZARD.INSTALL, \"Installing...\"));\n return { ok: true, mode, spec };\n }\n }\n}\n\n/**\n * Track 배열 동등 비교 (순서 무관). Preset 변경 감지에 사용.\n */\nfunction tracksEqual(a: ReadonlyArray<Track>, b: ReadonlyArray<Track>): boolean {\n if (a.length !== b.length) return false;\n const sortedA = [...a].sort();\n const sortedB = [...b].sort();\n return sortedA.every((t, i) => t === sortedB[i]);\n}\n\n/**\n * v26.54.0 — Asset 선택 결과 (id 만) 와 preset 추천 비교 → forceInclude / forceExclude.\n * - `recommended - selected` → forceExclude (사용자가 unchecked)\n * - `selected - recommended` → forceInclude (사용자가 추가 선택)\n * 둘 다 비어있으면 undefined (no override).\n */\nexport function computeUserOverride(\n tracks: ReadonlyArray<Track>,\n assetIds: ReadonlyArray<string>,\n): { forceInclude: ReadonlyArray<string>; forceExclude: ReadonlyArray<string> } | undefined {\n const recommended = new Set(recommendedExternalAssets(tracks));\n const selected = new Set(assetIds);\n const forceExclude = [...recommended].filter((id) => !selected.has(id)).sort();\n const forceInclude = [...selected].filter((id) => !recommended.has(id)).sort();\n if (forceInclude.length === 0 && forceExclude.length === 0) return undefined;\n return { forceInclude, forceExclude };\n}\n\nexport function formatSummary(spec: InstallSpec): string {\n const baselineExcluded = new Set(spec.baselineExclude ?? []);\n const opts = (Object.keys(spec.options) as Array<keyof OptionFlags>)\n .filter((k) => spec.options[k])\n .map((k) => k.replace(/^with/, \"\").toLowerCase());\n // v26.63.3 (clarify H1): \"(defaults only)\" 모호 → \"(none added)\" 명료.\n const optsLabel = opts.length > 0 ? opts.join(\", \") : \"(none added)\";\n const lines = [\n `Tracks: ${spec.tracks.join(\", \")}`,\n `Options: ${optsLabel}`,\n `CLI: ${spec.cli.join(\" · \")}`,\n `Target: ${spec.projectDir}`,\n ];\n\n // v26.62.3 — 실제 install 될 자산 list 명시. defaults 만으로는 사용자가\n // Step 3 에서 무엇을 confirm 했는지 알 수 없음. preset recommended +\n // userOverride 적용 후 최종 selected assets list 표시.\n // v26.82.0 (Phase R, S6) — merge/그룹화는 preset-recommend.ts 단일 구현 사용 (중복 제거).\n const finalAssets = finalSelectedAssets(spec.tracks, spec.userOverride);\n if (finalAssets.length > 0) {\n // v26.102.0 (ADR-031) — confirm 화면의 약속 숫자에 CLI 도달 분해 병기 (SOD 리뷰 F3:\n // \"4 selected\" 확정 후 0 설치이던 불일치 — 숨김 없이 고지만).\n const unreachable = finalAssets.filter((id) => {\n const asset = EXTERNAL_ASSETS.find((a) => a.id === id);\n return asset ? !assetReachesCli(asset, spec.cli) : false;\n });\n lines.push(\n unreachable.length > 0\n ? `Assets: ${finalAssets.length} selected (${unreachable.length} outside [${spec.cli.join(\", \")}] reach — not installed)`\n : `Assets: ${finalAssets.length} selected`,\n );\n for (const [cat, ids] of groupAssetsByCategory(finalAssets)) {\n lines.push(` · ${cat}: ${ids.join(\", \")}`);\n }\n // v26.103.0 (ADR-032) — header 와 동일 문구 (표면별 상이 문구 금지, v26.88.0 교훈).\n // 해제분을 빼고 센다 (ADR-074). 이 숫자는 이 저장소의 1차 지표(Context Cost per Install)이고,\n // 상주 비용을 줄이려고 항목을 푼 사용자에게 안 줄어든 숫자를 보이면 그 자체가 거짓 보고다.\n // #320 H1 — 헤더와 같은 이유로 **설치기와 같은 spec**(`buildManifestSpec`)으로 센다.\n const assetSpec = buildManifestSpec(spec);\n const cost = formatResidentCostLine(\n residentCost(\n buildManifest(assetSpec).filter(\n (e) =>\n e.applies(assetSpec) &&\n !isBaselineExcluded(e.target, baselineExcluded) &&\n landsOnDisk(e.target, spec.cli),\n ),\n ),\n summarizeContextCost(finalAssets).unmeasuredCount,\n );\n if (cost) lines.push(` · ${cost}`);\n }\n\n if (spec.userOverride) {\n if (spec.userOverride.forceInclude.length > 0) {\n lines.push(` +User added: ${spec.userOverride.forceInclude.join(\", \")}`);\n }\n if (spec.userOverride.forceExclude.length > 0) {\n lines.push(` -User removed: ${spec.userOverride.forceExclude.join(\", \")}`);\n }\n }\n // 트랙 baseline 해제분. 설치 화면과 **같은 문구**를 쓴다 (표면별 상이 문구 금지) — 다르면\n // 사용자가 확인 화면과 설치 결과를 대조할 수 없다.\n if (baselineExcluded.size > 0) {\n lines.push(\n ` -Excluded by you: ${baselineExcluded.size} — ${[...baselineExcluded]\n .map((id) => id.replace(BASELINE_PREFIX, \"\"))\n .join(\", \")}`,\n );\n }\n return lines.join(\"\\n\");\n}\n\n// v26.54.0 — Re-exports to keep test imports stable (test의 mock 구조 변경 없음)\nexport { EXTERNAL_ASSETS, VISIBLE_OPTION_DEFS };\n","// prompts.ts is a thin adapter over @clack/prompts. It deliberately contains\n// no transformation logic — interactive.ts owns the business rules. This file\n// is excluded from coverage in vitest.config.ts (justification at exclude line).\n\nimport {\n cancel,\n confirm,\n groupMultiselect,\n intro,\n isCancel,\n multiselect,\n outro,\n select,\n} from \"@clack/prompts\";\nimport {\n type BaselineKind,\n type BaselineTarget,\n listBaselineTargets,\n withBaselineHints,\n} from \"./baseline-targets.js\";\nimport { CATEGORIES, CATEGORY_TITLES, type Category } from \"./categories.js\";\nimport { CLI_BASE_SORT_ORDER } from \"./cli-targets.js\";\nimport { assetTrustTier, EXTERNAL_ASSETS } from \"./external-assets.js\";\nimport { buildRouterChoices, type RouterAction, summarizeState } from \"./router.js\";\nimport type { DetectedInstall } from \"./state.js\";\nimport {\n type CliBase,\n type CliTargets,\n type InstallScope,\n type OptionFlags,\n TRACKS,\n type Track,\n UPDATE_GROUPS,\n type UpdateGroup,\n} from \"./types.js\";\nimport { stepLabel, type WizardStep } from \"./wizard-steps.js\";\n\n/**\n * v26.54.0 — All-in-one install-targets value scheme.\n * groupMultiselect 의 단일 string value 에 두 source 통합:\n * - `option:<key>` → OPTION_DEFS 의 manifest-영향 build option (현재 빈 배열 — 아래 OPTION_DEFS 참조)\n * - `asset:<id>` → EXTERNAL_ASSETS 의 외부 자산\n * 다른 OptionFlags 항목 (withGsd, withEcc, withPrune, withTob,\n * withAddyAgentSkills, withSuperpowers, withWshobsonAgents, withOpenspec, withBmad) 은\n * EXTERNAL_ASSETS 에 1:1 자산이 있어\n * 자산 체크 → userOverride.forceInclude 로 처리. UI 중복 표시 없음.\n */\nexport type InstallTargetId = `option:${keyof OptionFlags}` | `asset:${string}`;\n\nexport interface Prompts {\n intro: (msg: string) => void;\n outro: (msg: string) => void;\n cancel: (msg: string) => void;\n\n /**\n * v26.65.0 — step optional 두 번째 인자. 호출자가 `WIZARD.TRACKS` 전달 시 message 에\n * \"Step N/M — Select Track(s)\" 형식 indicator 자동 삽입. 미전달 시 prefix 없이 raw label.\n */\n selectTracks: (initial?: Track[], step?: WizardStep) => Promise<Track[] | null>;\n /** v0.7.0 — single select → multiselect (3 base 체크박스). default `[\"claude\"]`. */\n selectCli: (initial?: CliTargets, step?: WizardStep) => Promise<CliTargets | null>;\n selectAction: (state: DetectedInstall) => Promise<RouterAction | null>;\n /**\n * v26.64.0 (ADR-020) — Installation scope 선택. Default = \"project\" (pre-selected).\n * Global 은 사용자 명시 opt-in. null = silent back.\n */\n selectScope: (initial?: InstallScope, step?: WizardStep) => Promise<InstallScope | null>;\n confirmInstall: (summary: string) => Promise<boolean | null>;\n /**\n * #480 — update 위저드의 \"무엇을 갱신할까\" 체크박스. 옵셔널: 없는 구현(테스트 픽스처)은 전부\n * 갱신으로 읽는다. null = 취소.\n */\n selectUpdateGroups?: () => Promise<UpdateGroup[] | null>;\n\n /**\n * v26.54.0 — Step 3 (all-in-one). EXTERNAL_ASSETS + 표시-대상 OPTION_DEFS 를\n * 카테고리 그룹화. 추천 ✓ pre-check. ESC → null (silent back).\n * v26.61.0 — recap (tracks/cli) 추가. alt screen 안에서 동작 — terminal scrollback\n * 본질 차단 → wizard scroll 시 cursor highlight 항상 visible.\n */\n selectInstallTargets: (\n initialChecked: ReadonlyArray<InstallTargetId>,\n step: { current: number; total: number },\n recap?: {\n tracks: ReadonlyArray<Track>;\n cli: CliTargets;\n /**\n * v26.125.0 — 이미 설치된 target id (표시 전용 마커). 체크를 풀어도 제거되지 않는다 —\n * 제거는 `agent-harness uninstall` 을 따로 실행한다 (사용자 결정 2026-07-19).\n */\n installed?: ReadonlyArray<string>;\n },\n ) => Promise<ReadonlyArray<InstallTargetId> | null>;\n}\n\nconst TRACK_LABELS: Record<Track, string> = {\n base: \"base — principles · methodology · tests only (no stack)\",\n tooling: \"tooling — Bash + Markdown meta-project\",\n \"csr-supabase\": \"csr-supabase — Vite + React + Supabase\",\n \"csr-fastify\": \"csr-fastify — Vite + React + Fastify\",\n \"csr-fastapi\": \"csr-fastapi — Vite + React + FastAPI\",\n \"ssr-htmx\": \"ssr-htmx — htmx + FastAPI\",\n \"ssr-nextjs\": \"ssr-nextjs — Next.js (App Router)\",\n data: \"data — Python data / DuckDB / PySide6\",\n executive: \"executive — proposals / DD / pitch (no agent-skills)\",\n full: \"full — all dev capabilities\",\n \"project-management\": \"project-management — PM / Scrum / Jira / Confluence\",\n \"growth-marketing\": \"growth-marketing — Growth / Marketing / Content\",\n};\n\ninterface OptionDef {\n key: keyof OptionFlags;\n label: string;\n hint: string;\n category: Category;\n source: string;\n}\n\n/**\n * v26.54.0 — 표시 대상 OPTION_DEFS.\n * v26.81.0 (ADR-022) — **빈 배열로 소멸**. 마지막 항목(withTauri)이 내부\n * 자산(tauri-desktop — EXTERNAL_ASSETS `kind:\"internal\"`)으로 흡수돼 wizard 의\n * `option:` 특례가 사라짐. 자산 선택은 전부 `asset:<id>` 경로. 잔존 동작 옵션(D16 글로벌\n * 4종/prune)은 wizard 미노출 (CLI `--with-*` 동작 플래그 전용 — 기존과 동일).\n */\nexport const VISIBLE_OPTION_DEFS: ReadonlyArray<OptionDef> = [];\n\nconst CLI_BASE_LABELS: Record<CliBase, string> = {\n claude: \"Claude Code\",\n codex: \"Codex (OpenAI)\",\n opencode: \"OpenCode (anomalyco)\",\n antigravity: \"Antigravity (Google)\",\n};\n\n/**\n * v26.78.1 — Step 3 wizard page layout (SSOT). 카테고리를 페이지로 묶어 clack\n * groupMultiselect 의 maxItems 한계(페이지당 옵션 ≤ ~30)를 우회.\n *\n * ⚠️ drift 가드 (no-false-ship): 모든 Category 는 정확히 한 페이지에 등장해야 한다.\n * 누락 시 해당 카테고리 자산이 wizard 에서 선택 불가가 되어 \"출하 거짓 광고\"가 된다\n * (v26.78.0 understanding 누락 회귀 — pages 가 selectInstallTargets 안에 하드코딩되어\n * CATEGORIES 추가와 drift). 아래 assertPagesCoverAllCategories 가 모듈 로드 시점에\n * 강제 — 신규 카테고리 미배치 시 즉시 throw. (tests/wizard-page-parity.test.ts 가 이중 가드)\n *\n * v26.99.0 — Dev 단일 페이지가 37행(옵션 32 + 헤더 5)으로 위 \"≤ ~30\" 제약을 스스로 위반하고\n * 있었다(실측). 터미널을 넘겨 스크롤이 생기는 것이 사용자가 보고한 \"선택 row 가 너무 많다\"의\n * 실제 메커니즘. → Dev 를 도메인(Core)과 도구(Tools)로 분할. 번들링(아래 DEV_METHOD_BUNDLE)만으론\n * 33행이라 부족했다 — Dev 32항목 중 dev-method 는 4개뿐이고, 번들 row 는 workflow 에 렌더되어\n * Dev 페이지에 남지 않으므로 37-4=33. ADR-028.\n *\n * 페이지 묶음 (전 페이지 ≤ 30 행):\n * Page 1: Dev Core — frontend + backend + data (20행)\n * Page 2: Dev Tools — dev-tools + understanding (13행)\n * Page 3: Business — business (documents) ( 9행)\n * Page 4: Visual&Media — visual-media (slides·diagrams·motion·video) (10행)\n * Page 5: Workflow/ECC — workflow(+ 방법론 번들 1행) + ecc-suite (10행)\n */\nexport interface InstallTargetPage {\n label: string;\n cats: ReadonlyArray<Category>;\n /**\n * 2026-08-16 — 이 페이지가 렌더하는 **트랙 baseline** 종류. 외부 자산 카테고리와 별도 축이다:\n * `cats` 는 `EXTERNAL_ASSETS` 를 보고, 이쪽은 `manifest.ts` 가 트랙에서 유도하는 설치 대상을\n * 본다. 한 축에 섞으면 `assertPagesCoverAllCategories` 의 전수 가드가 baseline 을 미배치\n * 카테고리로 오인한다.\n */\n baseline?: ReadonlyArray<BaselineKind>;\n}\n\nexport const INSTALL_TARGET_PAGES: ReadonlyArray<InstallTargetPage> = [\n // 2026-08-16 — 트랙이 고르는 자산을 **맨 앞에서** 전부 보여준다. 그전까지 이 항목들은 화면에\n // 한 번도 안 나온 채 깔렸고, 사용자는 설치가 끝난 뒤 요약에서 처음 봤다.\n //\n // 2페이지로 나눈 근거는 실측이다: 최대 트랙(`full`)이 32항목(당시 실측 — rules 6 · agents 9 · hooks 3 ·\n // skills 14)이라 clack groupMultiselect 의 \"페이지당 ≤ ~30행\" 제약을 한 페이지로는 못 지킨다.\n // 가르는 축은 역할이다 — 정책·가드가 한쪽, 실행 능력이 다른 쪽.\n { label: \"Track baseline — Rules & Hooks (정책·가드)\", cats: [], baseline: [\"rules\", \"hooks\"] },\n {\n label: \"Track baseline — Agents & Skills (에이전트·스킬)\",\n cats: [],\n baseline: [\"agents\", \"skills\"],\n },\n { label: \"Dev Core (Frontend · Backend · Data)\", cats: [\"frontend\", \"backend\", \"data\"] },\n { label: \"Dev Tools (Security · Quality · Understanding)\", cats: [\"dev-tools\", \"understanding\"] },\n { label: \"Business (PM · Executive · Documents)\", cats: [\"business\"] },\n // v26.85.0 — 코드-퍼스트 비주얼/미디어 제작 (용도별 섹션, 전부 opt-in).\n { label: \"Visual & Media (Slides · Diagrams · Motion · Video)\", cats: [\"visual-media\"] },\n { label: \"Workflow & ECC Suite\", cats: [\"workflow\", \"ecc-suite\"] },\n];\n\n/**\n * #421 ② (사용자 결정 2026-09-20, ADR-092 — ADR-028 ⓐ 대체) — 방법론 스킬은 **개별 행**으로 렌더한다.\n * 전에는 5종을 \"uzys 하네스 방법론 N종\" 한 행으로 접었는데(ADR-028), 하나만 빼고 싶어도 못 빼고\n * 각각의 쓸모가 다른데 설명이 한 줄뿐이었다. 다섯 전부 기본 체크(has-dev-track)는 그대로다.\n */\nexport interface PageItem {\n value: string;\n label: string;\n hint?: string;\n}\n\n/**\n * v26.99.0 (ADR-028) — Step 3 한 페이지의 group/item 구성. clack 의존 0 인 순수 함수.\n *\n * `selectInstallTargets` 안의 클로저였으나 export 로 승격했다 (SOD 리뷰 Important #2):\n * **이 함수가 dev-method 스킬 전원을 wizard 에서 도달 가능하게 만드는 유일한 코드**인데, 테스트가\n * 하나도 닿지 않았다. `wizard-page-parity` 는 \"자산의 **카테고리**가 페이지에 있다\"만 보는데,\n * 구성원은 자기 카테고리에서 필터링되고 번들 row 로만 대표되므로 그 단언은 이제 구성원에 대해\n * 비논리(non sequitur)다 — 번들 row 블록을 지워도 parity 는 통과하고 CI 는 green 인 채\n * 구성원이 **어디서도 선택 불가**가 된다. v26.78.0 거짓출하의 정확한 재현이다.\n * (`prompts.ts` 는 coverage 제외 대상이라 커버리지 수치도 이 코드에 대해 아무 보장을 못 준다.)\n */\nconst BASELINE_TITLES: Record<BaselineKind, string> = {\n rules: \"Rules — 상시 상주하는 대원칙 (4 CLI 전부에 설치)\",\n hooks: \"Hooks — 결정론적 가드 (해제하면 settings.json 배선도 함께 빠진다)\",\n agents: \"Agents — 독립 레인 (리뷰·검증)\",\n skills: \"Skills — 필요할 때 열리는 작업 절차\",\n};\n\nexport function buildPageGroups(\n cats: ReadonlyArray<Category>,\n initialSet: ReadonlySet<string>,\n /**\n * v26.125.0 — 이미 설치된 target id. 라벨에 `● installed` 마커만 붙인다.\n * **체크 상태와 별개다** — 체크는 \"이번에 설치할 것\", 마커는 \"이미 있는 것\"이고,\n * 체크를 풀어도 제거되지 않는다(제거는 `uninstall` 을 따로 실행).\n */\n installedSet: ReadonlySet<string> = new Set(),\n /**\n * 2026-08-16 — 이 페이지에 렌더할 트랙 baseline. 호출자가 트랙에서 유도해 넘긴다 —\n * 여기서 다시 고르면 화면과 설치가 서로 다른 목록을 보게 된다.\n */\n baselineKinds: ReadonlyArray<BaselineKind> = [],\n baselineTargets: ReadonlyArray<BaselineTarget> = [],\n): { groups: Record<string, PageItem[]>; flatItems: PageItem[] } {\n const installedMark = (value: string): string => (installedSet.has(value) ? \" ● installed\" : \"\");\n const groups: Record<string, PageItem[]> = {};\n const flatItems: PageItem[] = [];\n for (const kind of baselineKinds) {\n const items: PageItem[] = baselineTargets\n .filter((t) => t.kind === kind)\n .map((t) => ({\n value: t.id,\n label: ` ${t.name}${installedMark(t.id)}`,\n ...(t.hint ? { hint: t.hint } : {}),\n }));\n if (items.length === 0) continue;\n groups[BASELINE_TITLES[kind]] = items;\n flatItems.push(...items);\n }\n for (const cat of cats) {\n const items: PageItem[] = [];\n for (const o of VISIBLE_OPTION_DEFS.filter((d) => d.category === cat)) {\n items.push({\n value: `option:${o.key}`,\n // v26.62.3 — group header 와 옵션 사이 시각 hierarchy 강화. label prefix 4 space.\n label: ` ${o.label} [${o.source}]${installedMark(`option:${o.key}`)}`,\n hint: o.hint,\n });\n }\n // v26.71.0 (PRD v26-71) — tier 우선 정렬 (official → vetted → experimental) + 배지.\n const tierOrder = { official: 0, vetted: 1, experimental: 2 } as const;\n // #421 ② — 방법론 스킬도 개별 행(ADR-092). 카탈로그 description 이 그대로 hint 가 된다.\n const catAssets = [...EXTERNAL_ASSETS.filter((x) => x.category === cat)].sort(\n (a, b) => tierOrder[assetTrustTier(a.id)] - tierOrder[assetTrustTier(b.id)],\n );\n for (const a of catAssets) {\n const tier = assetTrustTier(a.id);\n const badge =\n tier === \"official\"\n ? \" ★ official\"\n : tier === \"experimental\"\n ? \" ⚠ experimental (opt-in)\"\n : \"\";\n items.push({\n value: `asset:${a.id}`,\n label: ` ${a.id} [${a.source}]${badge}${installedMark(`asset:${a.id}`)}`,\n hint: a.description,\n });\n }\n if (items.length === 0) continue;\n const selectedInCat = items.filter((it) => initialSet.has(it.value)).length;\n const header = `${CATEGORY_TITLES[cat]} [${selectedInCat}/${items.length} ✓ default]`;\n groups[header] = items;\n flatItems.push(...items);\n }\n return { groups, flatItems };\n}\n\n/**\n * 모든 Category 가 정확히 한 페이지에 등장하는지 모듈 로드 시 검증.\n * 누락(미배치) 또는 중복(2개 페이지)이면 throw — 값싼 fail-loud pre-flight.\n */\nfunction assertPagesCoverAllCategories(pages: ReadonlyArray<InstallTargetPage>): void {\n const counts = new Map<Category, number>();\n for (const page of pages) {\n for (const cat of page.cats) counts.set(cat, (counts.get(cat) ?? 0) + 1);\n }\n const missing = CATEGORIES.filter((c) => !counts.has(c));\n const duplicated = CATEGORIES.filter((c) => (counts.get(c) ?? 0) > 1);\n if (missing.length > 0 || duplicated.length > 0) {\n throw new Error(\n `INSTALL_TARGET_PAGES drift vs CATEGORIES — missing=[${missing.join(\", \")}] ` +\n `duplicated=[${duplicated.join(\", \")}]. 모든 카테고리는 정확히 한 페이지에 배치해야 한다 ` +\n `(no-false-ship: wizard 미노출 = 거짓 광고).`,\n );\n }\n}\n\nassertPagesCoverAllCategories(INSTALL_TARGET_PAGES);\n\n/**\n * v26.58.1 — Wizard viewport size. clack 의 limitOptions 가 maxItems 안에서만\n * cursor follow + ↕ ... indicator. 미지정 시 Infinity → terminal height 초과 시\n * 위 항목이 scrollback 으로 밀려 selected indicator 안 보임 (사용자 보고된 문제).\n *\n * rowPadding 10 = message line + status footer + safety margin.\n * floor 8 = 너무 작아도 최소한의 viewport 보장.\n */\nfunction viewportItems(itemCount: number): number {\n const rows = process.stdout.rows ?? 24;\n return Math.max(8, Math.min(itemCount, rows - 10));\n}\n\nexport const defaultPrompts: Prompts = {\n intro: (msg) => intro(msg),\n outro: (msg) => outro(msg),\n cancel: (msg) => cancel(msg),\n\n selectTracks: async (initial, step) => {\n // v26.65.0 — step indicator SSOT (wizard-steps.ts). 6-step 통합 (1 tracks · 2 cli · 3 targets · 4 scope · 5 confirm · 6 installing).\n const result = await multiselect({\n message: stepLabel(step, \"Select Track(s)\"),\n options: TRACKS.map((t) => ({ value: t, label: TRACK_LABELS[t] })),\n ...(initial ? { initialValues: initial } : {}),\n maxItems: viewportItems(11),\n required: true,\n });\n return isCancel(result) ? null : (result as Track[]);\n },\n\n selectCli: async (initial, step) => {\n const initialValues: CliBase[] = initial && initial.length > 0 ? [...initial] : [\"claude\"];\n const result = await multiselect({\n message: stepLabel(step, \"Target CLI(s)\"),\n options: [\n { value: \"claude\" as const, label: CLI_BASE_LABELS.claude },\n { value: \"codex\" as const, label: CLI_BASE_LABELS.codex },\n { value: \"opencode\" as const, label: CLI_BASE_LABELS.opencode },\n { value: \"antigravity\" as const, label: CLI_BASE_LABELS.antigravity },\n ],\n initialValues,\n required: true,\n });\n if (isCancel(result)) return null;\n return [...(result as CliBase[])].sort(\n (a, b) => CLI_BASE_SORT_ORDER[a] - CLI_BASE_SORT_ORDER[b],\n );\n },\n\n selectAction: async (state) => {\n const result = await select({\n message: summarizeState(state),\n options: buildRouterChoices(state).map((c) => {\n const label = c.enabled ? c.label : `${c.label} [disabled]`;\n // disabled:true → clack 이 cursor skip + strikethrough (선택 자체 차단).\n return {\n value: c.value,\n label,\n ...(c.hint ? { hint: c.hint } : {}),\n ...(c.enabled ? {} : { disabled: true }),\n };\n }),\n });\n return isCancel(result) ? null : (result as RouterAction);\n },\n\n /**\n * v26.64.0 (ADR-020) — Installation scope select. Default Project (D16 — no global write).\n * Global 은 사용자 명시 opt-in 시에만.\n */\n selectScope: async (initial = \"project\", step) => {\n const result = await select({\n message: stepLabel(step, \"Installation scope\"),\n initialValue: initial,\n options: [\n {\n value: \"project\",\n label: \"Project\",\n hint: \"Install in current directory (committed with your project)\",\n },\n {\n value: \"global\",\n label: \"Global\",\n hint: \"Write to ~/.claude/, ~/.codex/, npm -g (shared across all projects)\",\n },\n ],\n });\n return isCancel(result) ? null : (result as InstallScope);\n },\n\n selectUpdateGroups: async () => {\n const result = await multiselect({\n message: \"What to update (space = toggle · your edits are always backed up)\",\n options: [\n { value: \"skills\" as const, label: \"Skills\", hint: \"installed bundled skills → latest\" },\n {\n value: \"new-skills\" as const,\n label: \"New skills\",\n hint: \"bundled skills added since your install\",\n },\n {\n value: \"rules\" as const,\n label: \"Rules · agents · commands\",\n hint: \"your edited files → *.backup-<time>\",\n },\n {\n value: \"anchor\" as const,\n label: \"CLAUDE-uzys-harness.md anchor\",\n hint: \"your edited anchor → *.backup-<time>\",\n },\n {\n value: \"hooks\" as const,\n label: \"Hooks · settings.json\",\n hint: \"stale hook refs cleaned\",\n },\n {\n value: \"external\" as const,\n label: \"Codex / OpenCode / Antigravity + external skills\",\n hint: \"rendered artifacts + npx skills\",\n },\n ],\n initialValues: [...UPDATE_GROUPS],\n required: true,\n });\n return isCancel(result) ? null : (result as UpdateGroup[]);\n },\n\n confirmInstall: async (summary) => {\n const result = await confirm({\n message: `${summary}\\n\\nProceed?`,\n initialValue: true,\n });\n return isCancel(result) ? null : result;\n },\n\n selectInstallTargets: async (initialChecked, step, recap) => {\n // v26.62.2 — groupMultiselect 복귀 + page paginate.\n // v26.62.1 에서 multiselect + disabled separator 시도 → clack 가 disabled option 에\n // 체크박스 강제 + dim/strikethrough 효과 → 카테고리 헤더가 \"옵션 같지만 선택 불가\"\n // 처럼 보여 사용자 보고. groupMultiselect 의 group header 는 라이브러리에서 본래\n // 설명 라인 형태로 자연 표시 → 시각 명료.\n //\n // maxItems 한계 (GroupMultiSelectPrompt 미지원) 는 page 묶음으로 cover:\n // 한 page 안 옵션 ≤ ~30 → 사용자 iTerm2 (30+ rows) 환경에서 fit. 매우 작은 terminal\n // (< 25 rows) 한계는 follow-up.\n //\n // 페이지 정의 = 모듈 스코프 INSTALL_TARGET_PAGES (SSOT, 카테고리 전수 가드됨).\n const pages = INSTALL_TARGET_PAGES;\n // 트랙에서 유도한다 — 화면과 설치가 같은 목록을 보게 하는 유일한 방법이다.\n // #421 — 이름만으로는 체크를 풀지 말지 판단할 수 없다. 파일에서 뽑은 한 줄을 붙인다.\n const baselineTargets = withBaselineHints(listBaselineTargets({ tracks: recap?.tracks ?? [] }));\n const initialSet = new Set<string>(initialChecked);\n const collected = new Set<string>(initialChecked);\n\n const recapLine = recap\n ? `Tracks: ${recap.tracks.join(\", \")} · CLIs: ${recap.cli.join(\", \")}`\n : \"\";\n // v26.125.0 — 마커 전용. 체크 상태와 별개이며, 체크를 풀어도 제거되지 않는다.\n const installedSet = new Set<string>((recap?.installed ?? []) as ReadonlyArray<string>);\n\n // alt screen for the whole Step 3 loop. page 전환 시 buffer 안에서 redraw.\n process.stdout.write(\"\\x1b[?1049h\");\n let resultIds: ReadonlyArray<InstallTargetId> | null = null;\n let aborted = false;\n try {\n let pageIdx = 0;\n // 이동 방향. 빈 페이지 건너뛰기가 이걸 따라간다 — 아래 주석 참조.\n let dir: 1 | -1 = 1;\n while (pageIdx < pages.length) {\n const page = pages[pageIdx];\n if (!page) break;\n const { groups, flatItems } = buildPageGroups(\n page.cats,\n initialSet,\n installedSet,\n page.baseline ?? [],\n baselineTargets,\n );\n // 항목이 0개인 페이지는 건너뛴다 — 빈 화면에서 Enter 를 치게 만들 이유가 없다.\n //\n // **오늘 이 분기는 도달하지 않는다**(독립 리뷰 실측). `listBaselineTargets` 는 `tracks`\n // 만 보고 `spec.cli` 를 아예 안 받으므로 \"CLI 조합 때문에 비는 페이지\"는 없다 — codex\n // 단독 설치도 24행을 다 렌더한다. 실제 하한은 구조적이다: `COMMON_RULES`(3)·\n // `ALWAYS_HOOKS`(3)가 `applies: all` 이라 페이지 0 은 어떤 트랙에서도 6항목 아래로\n // 안 내려가고, 페이지 1 도 전 트랙 최소 9항목이다. 트랙이 자산을 다 잃으면 살아난다.\n //\n // **이동 방향을 따라간다.** 앞으로만 건너뛰면 빈 페이지가 일방통행 문이 된다 — 뒤에서\n // ESC 로 돌아온 사용자를 다시 앞으로 밀어내 Step 2 로 나갈 길이 사라진다.\n if (flatItems.length === 0) {\n pageIdx += dir;\n if (pageIdx < 0) {\n aborted = true; // 뒤로 건너뛰다 첫 페이지를 지났다 = Step 2 로\n break;\n }\n continue;\n }\n const selectedNow = flatItems.filter((it) => collected.has(it.value)).map((it) => it.value);\n const pageDefault = flatItems.filter((it) => initialSet.has(it.value)).length;\n const totalSelected = collected.size;\n const message = [\n `Step ${step.current}/${step.total} · Page ${pageIdx + 1}/${pages.length} · ${page.label}`,\n recapLine ? ` ${recapLine}` : \"\",\n ` Selected so far: ${totalSelected} items · This page default ✓ ${pageDefault}/${flatItems.length}`,\n // 마커의 뜻을 화면에서 바로 알려준다 — 체크 해제를 제거로 오해하는 것이 이 화면의\n // 원래 문제였으므로, 마커가 보일 때는 제거 경로를 같은 자리에서 말한다.\n installedSet.size > 0\n ? \" ● installed = 이미 설치됨 · 체크 해제해도 제거되지 않는다 (제거: agent-harness uninstall)\"\n : \"\",\n \" Space toggle · Enter → next · ESC → prev\",\n ]\n .filter(Boolean)\n .join(\"\\n\");\n\n const groupOpts = {\n message,\n options: groups,\n initialValues: selectedNow,\n required: false,\n selectableGroups: false,\n } as Parameters<typeof groupMultiselect>[0];\n const result = await groupMultiselect(groupOpts);\n if (isCancel(result)) {\n if (pageIdx === 0) {\n aborted = true; // first page ESC → Step 2 back\n break;\n }\n dir = -1;\n pageIdx--; // prev page\n continue;\n }\n // update collected: remove page items + add the new selection\n for (const it of flatItems) collected.delete(it.value);\n for (const v of result as ReadonlyArray<string>) collected.add(v);\n dir = 1;\n pageIdx++;\n }\n if (!aborted) {\n resultIds = [...collected] as ReadonlyArray<InstallTargetId>;\n }\n } finally {\n process.stdout.write(\"\\x1b[?1049l\");\n }\n // v26.63.1 — alt screen exit 후 main buffer 에 Step 3 완료 라인 출력. alt buffer\n // 안 동작은 main 에 흔적 0 → Step 1·2·4 사이 Step 3 missing → 사용자 보고.\n // clack `◇` marker + `│` line 으로 다른 step 과 시각 일관성 유지.\n if (resultIds !== null) {\n process.stdout.write(\n `◇ Step ${step.current}/${step.total} — Install targets · ${resultIds.length} selected\\n│\\n`,\n );\n }\n return resultIds;\n },\n};\n","import type { DetectedInstall } from \"./state.js\";\n\nexport type RouterAction = \"add\" | \"update\" | \"remove\" | \"reinstall\" | \"exit\";\n\nexport interface RouterChoice {\n value: RouterAction;\n label: string;\n hint?: string;\n enabled: boolean;\n}\n\n/**\n * 5-action menu for an existing install. Mirrors prompt_action_router (setup-harness.sh:255).\n *\n * \"remove\" is exposed but disabled — no reliable file-ownership mapping yet (would risk data loss).\n */\nexport function buildRouterChoices(state: DetectedInstall): RouterChoice[] {\n const detected = state.tracks.length > 0 ? state.tracks.join(\", \") : \"(none detected)\";\n return [\n {\n value: \"add\",\n label: \"Add a new Track\",\n hint: `Current: ${detected}`,\n enabled: true,\n },\n {\n value: \"update\",\n label: \"Update policy files (auto-backup)\",\n // v26.126.0 (R-3a) — skills 가 목록에 들어왔다. 이 문구가 곧 update 의 광고이고\n // (update 는 위저드로만 도달한다) 실동작과 어긋나면 그 자체로 거짓출하다.\n hint: \"Refresh rules / agents / commands / hooks / skills — your edits are backed up\",\n enabled: true,\n },\n {\n value: \"remove\",\n label: \"Remove a Track (unsupported)\",\n hint: \"Manual edit of .claude/ required — not automated\",\n enabled: false,\n },\n {\n value: \"reinstall\",\n label: \"Reinstall (backs up current .claude/ first)\",\n hint: \"Use when state is corrupted\",\n enabled: true,\n },\n {\n value: \"exit\",\n label: \"Exit\",\n enabled: true,\n },\n ];\n}\n\nexport function summarizeState(state: DetectedInstall): string {\n if (state.state === \"new\") {\n return \"No prior install detected — new install flow.\";\n }\n const trackList = state.tracks.length > 0 ? state.tracks.join(\", \") : \"(no tracks resolved)\";\n const sourceLabel =\n state.source === \"metafile\"\n ? \"via .claude/.installed-tracks\"\n : state.source === \"legacy\"\n ? \"via legacy rules/*.md heuristic\"\n : state.source === \"install-log\"\n ? \"via .uzys-agent-harness/ install log\"\n : \"via no source\";\n return `Existing install detected ${sourceLabel}. Tracks: ${trackList}.`;\n}\n","/**\n * Wizard step single source of truth — v26.65.0.\n *\n * v26.64.0 에서 wizard 가 5→6 step 으로 변경됐는데 prompts.ts 의 message 가 hardcoded\n * (\"Step 1/5\") 였음 → step indicator drift 가 무성. 본 모듈이 SSOT.\n *\n * 추가 step 도입 시 본 파일만 수정 → message 자동 정합.\n */\n\nexport interface WizardStep {\n current: number;\n total: number;\n}\n\nexport const WIZARD_TOTAL = 6;\n\nexport const WIZARD: {\n TRACKS: WizardStep;\n CLI: WizardStep;\n TARGETS: WizardStep;\n SCOPE: WizardStep;\n CONFIRM: WizardStep;\n INSTALL: WizardStep;\n} = {\n TRACKS: { current: 1, total: WIZARD_TOTAL },\n CLI: { current: 2, total: WIZARD_TOTAL },\n TARGETS: { current: 3, total: WIZARD_TOTAL },\n SCOPE: { current: 4, total: WIZARD_TOTAL },\n CONFIRM: { current: 5, total: WIZARD_TOTAL },\n INSTALL: { current: 6, total: WIZARD_TOTAL },\n};\n\n/**\n * Wizard step header — `Step N/M — <suffix>` 형식.\n *\n * step 미지정 시 suffix 만 반환 (backward compat — tests / non-wizard 호출).\n */\nexport function stepLabel(step: WizardStep | undefined, suffix: string): string {\n if (!step) return suffix;\n return `Step ${step.current}/${step.total} — ${suffix}`;\n}\n"],"mappings":"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","names":["ESC","CSI","k","j","v","b","cli","resolve","k","b","b","chmodSync","existsSync","mkdirSync","readdirSync","readFileSync","writeFileSync","basename","dirname","join","existsSync","join","v","join","existsSync","existsSync","readFileSync","join","existsSync","readFileSync","join","join","existsSync","readFileSync","existsSync","readFileSync","join","existsSync","readFileSync","existsSync","readFileSync","join","join","existsSync","readFileSync","join","existsSync","mkdirSync","readFileSync","writeFileSync","dirname","e","join","chmodSync","existsSync","readFileSync","basename","join","k","v","join","basename","existsSync","readFileSync","chmodSync","existsSync","readFileSync","basename","join","renameSlashes","renderAgentsMd","readRequired","join","basename","readOptionalJson","renderAgentsMd","existsSync","readFileSync","cli","existsSync","readFileSync","writeFileSync","join","sep","existsSync","readdirSync","readFileSync","homedir","join","b","cli","v","join","existsSync","c","at","homedir","readdirSync","readFileSync","existsSync","readFileSync","writeFileSync","readFileSync","existsSync","writeFileSync","existsSync","mkdirSync","readdirSync","readFileSync","unlinkSync","writeFileSync","basename","dirname","join","v","join","d","existsSync","readdirSync","e","basename","mkdirSync","dirname","b","unlinkSync","writeFileSync","readFileSync","isHarnessOwned","at","join","existsSync","readFileSync","foreign","e","mkdirSync","dirname","writeFileSync","basename","chmodSync","readdirSync","existsSync","readdirSync","join","e","join","existsSync","readdirSync","b","cli","v","k","p","w","resolve","e","cli","existsSync","readFileSync","join","resolve","resolve","d","join","existsSync","readFileSync","cli","spawnSync","existsSync","lstatSync","readdirSync","readFileSync","rmdirSync","writeFileSync","dirname","join","relative","resolve","sep","NO_TRUNCATION","dist_default","dist_default","findCursor","cursor","delta","options","opt","newCursor","maxCursor","clampedCursor","actions","DEFAULT_MONTH_NAMES","settings","days","month","min","max","isActionKey","key","action","settings","value","diffLines","a","b","aLines","bLines","numLines","diff","i","isWindows","CANCEL_SYMBOL","isCancel","setRawMode","input","getColumns","output","getRows","wrapTextWithPrefix","text","prefix","startPrefix","lineFormatter","columns","stdout","wrapAnsi","line","index","lineString","g$1","options","trackValue","input","stdin","render","signal","opts","event","params","cb","data","cbs","cleanup","subscriber","resolve","CANCEL_SYMBOL","readline","setRawMode","cursor","char","_key","_char","value","write","key","settings","problem","isActionKey","lines","frame","diff","diffLines","rows","diffOffsetAfter","diffOffsetBefore","diffLine","erase","adjustedDiffLine","newLines","ConfirmPrompt","Prompt","opts","confirm","cursor","GroupMultiSelectPrompt","Prompt","#selectableGroups","group","o","items","value","i","item","groupedItems","v","selected","opts","options","key","option","opt","currentIsGroup","Prompt","option","enabledOptions","allSelected","v","value","notSelected","selected","opts","cursor","findCursor","char","key","SelectPrompt","Prompt","opts","initialCursor","value","cursor","findCursor","key","isUnicodeSupported","process","unicode","unicodeOr","c","fallback","unicode","S_STEP_ACTIVE","S_STEP_CANCEL","S_STEP_ERROR","S_STEP_SUBMIT","S_BAR_START","S_BAR","S_BAR_END","S_BAR_START_RIGHT","S_BAR_END_RIGHT","S_RADIO_ACTIVE","S_RADIO_INACTIVE","S_CHECKBOX_ACTIVE","S_CHECKBOX_SELECTED","S_CHECKBOX_INACTIVE","S_PASSWORD_MASK","S_BAR_H","S_CORNER_TOP_RIGHT","S_CONNECT_LEFT","S_CORNER_BOTTOM_RIGHT","S_CORNER_BOTTOM_LEFT","S_CORNER_TOP_LEFT","S_INFO","S_SUCCESS","S_WARN","S_ERROR","symbol","state","styleText","symbolBar","trimLines","groups","initialLineCount","startIndex","endIndex","maxLines","lineCount","removals","i","group","limitOptions","cursor","options","style","output","maxItems","columnPadding","rowPadding","maxWidth","getColumns","rows","getRows","overflowFormat","outputMaxItems","computedMaxItems","slidingWindowLocation","shouldRenderTopEllipsis","shouldRenderBottomEllipsis","slidingWindowLocationEnd","lineGroups","slidingWindowLocationWithEllipsis","slidingWindowLocationEndWithEllipsis","wrappedLines","wrapAnsi","precedingRemovals","followingRemovals","newLineCount","cursorGroupIndex","trimLinesLocal","result","lineGroup","line","confirm","opts","active","inactive","ConfirmPrompt","hasGuide","settings","titlePrefix","symbol","titlePrefixBar","styleText","S_BAR","messageLines","wrapTextWithPrefix","title","value","submitPrefix","cancelPrefix","defaultPrefix","defaultPrefixEnd","S_BAR_END","S_RADIO_ACTIVE","S_RADIO_INACTIVE","groupMultiselect","opts","selectableGroups","groupSpacing","opt","option","state","options","label","isItem","next","isLast","prefix","S_BAR_END","S_BAR","spacingPrefix","spacingPrefixText","styleText","S_CHECKBOX_ACTIVE","S_CHECKBOX_SELECTED","selectedCheckbox","unselectedCheckbox","S_CHECKBOX_INACTIVE","required","GroupMultiSelectPrompt","selected","hasGuide","settings","title","symbol","value","selectedOptions","optionValue","optionsText","footer","ln","i","active","groupActive","optionText","optionsPrefix","cancel","message","opts","output","prefix","settings","styleText","S_BAR_END","intro","title","S_BAR_START","outro","S_BAR","computeLabel","label","format","line","multiselect","opts","opt","option","state","styleText","S_CHECKBOX_INACTIVE","str","S_CHECKBOX_ACTIVE","S_CHECKBOX_SELECTED","text","required","MultiSelectPrompt","selected","hasGuide","settings","wrappedMessage","wrapTextWithPrefix","symbolBar","symbol","title","S_BAR","value","styleOption","active","submitText","optionValue","wrappedSubmitText","wrappedLabel","prefix","footer","ln","i","S_BAR_END","titleLineCount","footerLineCount","limitOptions","S_PROGRESS_CHAR","unicodeOr","computeLabel","label","format","line","select","opts","opt","option","state","styleText","S_RADIO_INACTIVE","text","S_RADIO_ACTIVE","str","SelectPrompt","hasGuide","settings","titlePrefix","symbol","titlePrefixBar","symbolBar","messageLines","wrapTextWithPrefix","title","S_BAR","submitPrefix","wrappedLines","cancelPrefix","prefix","prefixEnd","S_BAR_END","titleLineCount","footerLineCount","limitOptions","item","active","prefix","styleText","S_BAR","ok","defaultSpawn","resolve","e","join","existsSync","lstatSync","readFileSync","dirname","relative","sep","readdirSync","rmdirSync","writeFileSync","spawnSync","cli","resolve","existsSync","readFileSync","join","join","existsSync","tracks","readFileSync","resolve","v","cli","d","b","it","c","v","cli","b","k","e","cli"]}
1
+ {"version":3,"sources":["../node_modules/sisteransi/src/index.js","../src/index.ts","../src/cli.ts","../node_modules/cac/dist/index.js","../package.json","../src/commands/install.ts","../src/baseline-targets.ts","../src/cli-targets.ts","../src/design.ts","../src/installer.ts","../src/ci-scaffold.ts","../src/cli-transforms.ts","../src/antigravity/transform.ts","../src/codex/agents-md.ts","../src/codex/skills.ts","../src/owned-write.ts","../src/foreign-slot.ts","../src/rules-port.ts","../src/codex/opt-in.ts","../src/codex/trust-entry.ts","../src/codex/transform.ts","../src/codex/config-toml.ts","../src/opencode/transform.ts","../src/opencode/agents-md.ts","../src/opencode/opencode-json.ts","../src/env-files.ts","../src/external-installer.ts","../src/mcp-merge.ts","../src/update-mode.ts","../src/commands/install-render.ts","../src/preset-recommend.ts","../src/commands/list.ts","../src/commands/uninstall.ts","../src/uninstall-interactive.ts","../node_modules/fast-wrap-ansi/src/main.ts","../node_modules/fast-string-width/dist/index.js","../node_modules/fast-string-truncated-width/dist/index.js","../node_modules/fast-string-truncated-width/dist/utils.js","../node_modules/@clack/core/src/utils/cursor.ts","../node_modules/@clack/core/src/utils/settings.ts","../node_modules/@clack/core/src/utils/string.ts","../node_modules/@clack/core/src/utils/index.ts","../node_modules/@clack/core/src/prompts/prompt.ts","../node_modules/@clack/core/src/prompts/autocomplete.ts","../node_modules/@clack/core/src/prompts/confirm.ts","../node_modules/@clack/core/src/prompts/date.ts","../node_modules/@clack/core/src/prompts/group-multiselect.ts","../node_modules/@clack/core/src/prompts/multi-line.ts","../node_modules/@clack/core/src/prompts/multi-select.ts","../node_modules/@clack/core/src/prompts/password.ts","../node_modules/@clack/core/src/prompts/select.ts","../node_modules/@clack/core/src/prompts/select-key.ts","../node_modules/@clack/core/src/prompts/text.ts","../node_modules/node_modules/.pnpm/is-unicode-supported@1.3.0/node_modules/is-unicode-supported/index.js","../node_modules/@clack/prompts/src/common.ts","../node_modules/@clack/prompts/src/limit-options.ts","../node_modules/@clack/prompts/src/autocomplete.ts","../node_modules/@clack/prompts/src/box.ts","../node_modules/@clack/prompts/src/confirm.ts","../node_modules/@clack/prompts/src/date.ts","../node_modules/@clack/prompts/src/group.ts","../node_modules/@clack/prompts/src/group-multi-select.ts","../node_modules/@clack/prompts/src/log.ts","../node_modules/@clack/prompts/src/messages.ts","../node_modules/@clack/prompts/src/multi-line.ts","../node_modules/@clack/prompts/src/multi-select.ts","../node_modules/@clack/prompts/src/note.ts","../node_modules/@clack/prompts/src/password.ts","../node_modules/@clack/prompts/src/path.ts","../node_modules/@clack/prompts/src/spinner.ts","../node_modules/@clack/prompts/src/progress-bar.ts","../node_modules/@clack/prompts/src/select.ts","../node_modules/@clack/prompts/src/select-key.ts","../node_modules/@clack/prompts/src/stream.ts","../node_modules/@clack/prompts/src/task.ts","../node_modules/@clack/prompts/src/task-log.ts","../node_modules/@clack/prompts/src/text.ts","../src/commands/update.ts","../src/state.ts","../src/interactive.ts","../src/prompts.ts","../src/router.ts","../src/wizard-steps.ts"],"sourcesContent":["'use strict';\n\nconst ESC = '\\x1B';\nconst CSI = `${ESC}[`;\nconst beep = '\\u0007';\n\nconst cursor = {\n to(x, y) {\n if (!y) return `${CSI}${x + 1}G`;\n return `${CSI}${y + 1};${x + 1}H`;\n },\n move(x, y) {\n let ret = '';\n\n if (x < 0) ret += `${CSI}${-x}D`;\n else if (x > 0) ret += `${CSI}${x}C`;\n\n if (y < 0) ret += `${CSI}${-y}A`;\n else if (y > 0) ret += `${CSI}${y}B`;\n\n return ret;\n },\n up: (count = 1) => `${CSI}${count}A`,\n down: (count = 1) => `${CSI}${count}B`,\n forward: (count = 1) => `${CSI}${count}C`,\n backward: (count = 1) => `${CSI}${count}D`,\n nextLine: (count = 1) => `${CSI}E`.repeat(count),\n prevLine: (count = 1) => `${CSI}F`.repeat(count),\n left: `${CSI}G`,\n hide: `${CSI}?25l`,\n show: `${CSI}?25h`,\n save: `${ESC}7`,\n restore: `${ESC}8`\n}\n\nconst scroll = {\n up: (count = 1) => `${CSI}S`.repeat(count),\n down: (count = 1) => `${CSI}T`.repeat(count)\n}\n\nconst erase = {\n screen: `${CSI}2J`,\n up: (count = 1) => `${CSI}1J`.repeat(count),\n down: (count = 1) => `${CSI}J`.repeat(count),\n line: `${CSI}2K`,\n lineEnd: `${CSI}K`,\n lineStart: `${CSI}1K`,\n lines(count) {\n let clear = '';\n for (let i = 0; i < count; i++)\n clear += this.line + (i < count - 1 ? cursor.up() : '');\n if (count)\n clear += cursor.left;\n return clear;\n }\n}\n\nmodule.exports = { cursor, scroll, erase, beep };\n","import { buildCli } from \"./cli.js\";\n\nconst cli = buildCli();\ncli.parse(process.argv);\n","import { cac } from \"cac\";\nimport packageJson from \"../package.json\";\nimport { type ExecuteSpecDeps, executeSpec, registerInstallCommand } from \"./commands/install.js\";\nimport { registerListCommand } from \"./commands/list.js\";\nimport { registerUninstallCommand } from \"./commands/uninstall.js\";\nimport { registerUpdateCommand } from \"./commands/update.js\";\nimport { type InteractiveResult, runInteractive } from \"./interactive.js\";\n\n// v26.72.1 — CalVer 정합 (cli --version / package.json / git tag 단일 버전).\n// v26.82.1 — 하드코딩 → package.json derive. v26.82.0 ship 때 package.json 만 bump 되고\n// 본 상수(26.81.0)가 남아 npm 게시 패키지가 --version 을 거짓 보고 (수동 동기화 주석은\n// 못 막는다는 본 repo 3번째 증명) → 빌드 시 esbuild 가 값 인라인, 동기화 자체를 소멸.\nexport const VERSION: string = packageJson.version;\n\nexport type Cli = ReturnType<typeof cac>;\n\nexport interface DefaultActionDeps {\n log?: (msg: string) => void;\n err?: (msg: string) => void;\n exit?: (code: number) => never;\n run?: (cwd: string) => Promise<InteractiveResult>;\n /** Override the install pipeline + report renderer (used by tests). */\n execute?: (spec: NonNullable<InteractiveResult[\"spec\"]>, deps: ExecuteSpecDeps) => void;\n}\n\n/**\n * Default action — runs the interactive flow, then executes the install\n * pipeline with the captured spec. Mirrors the `install` flag-mode command's\n * post-install report.\n */\nexport async function defaultAction(deps: DefaultActionDeps = {}): Promise<void> {\n const log = deps.log ?? console.log;\n const err = deps.err ?? console.error;\n /* v8 ignore next — process.exit default. tests 는 exit 주입. */\n const exit = deps.exit ?? ((code: number) => process.exit(code) as never);\n const run = deps.run ?? ((cwd: string) => runInteractive(cwd));\n const execute = deps.execute ?? executeSpec;\n\n const result = await run(process.cwd());\n if (!result.ok) {\n if (result.message) {\n err(result.message);\n }\n // exit-code mapping: no-tty=2; cancelled/exit/disabled/declined=0\n exit(result.reason === \"no-tty\" ? 2 : 0);\n return;\n }\n if (!result.spec) {\n err(\"Internal error: interactive returned ok=true without a spec.\");\n exit(1);\n return;\n }\n // v26.63.0 — wizard 모드 표시. install header (TARGET 등) 출력 skip → Step 5 sub-section 으로 자연 흐름.\n const execDeps: import(\"./commands/install.js\").ExecuteSpecDeps = {\n log,\n err,\n exit,\n fromWizard: true,\n };\n if (result.mode) execDeps.mode = result.mode;\n execute(result.spec, execDeps);\n}\n\nexport function buildCli(): Cli {\n const cli = cac(\"agent-harness\");\n\n cli.help();\n cli.version(VERSION);\n\n registerInstallCommand(cli);\n registerUpdateCommand(cli);\n registerListCommand(cli);\n registerUninstallCommand(cli);\n\n cli\n .command(\"\", \"Interactive installer (state detection + prompts)\")\n /* v8 ignore next — cac action callback. defaultAction 자체는 별도 tests 로 검증. */\n .action(() => defaultAction());\n\n return cli;\n}\n","//#region node_modules/.pnpm/mri@1.2.0/node_modules/mri/lib/index.mjs\nfunction toArr(any) {\n\treturn any == null ? [] : Array.isArray(any) ? any : [any];\n}\nfunction toVal(out, key, val, opts) {\n\tvar x, old = out[key], nxt = !!~opts.string.indexOf(key) ? val == null || val === true ? \"\" : String(val) : typeof val === \"boolean\" ? val : !!~opts.boolean.indexOf(key) ? val === \"false\" ? false : val === \"true\" || (out._.push((x = +val, x * 0 === 0) ? x : val), !!val) : (x = +val, x * 0 === 0) ? x : val;\n\tout[key] = old == null ? nxt : Array.isArray(old) ? old.concat(nxt) : [old, nxt];\n}\nfunction lib_default(args, opts) {\n\targs = args || [];\n\topts = opts || {};\n\tvar k, arr, arg, name, val, out = { _: [] };\n\tvar i = 0, j = 0, idx = 0, len = args.length;\n\tconst alibi = opts.alias !== void 0;\n\tconst strict = opts.unknown !== void 0;\n\tconst defaults = opts.default !== void 0;\n\topts.alias = opts.alias || {};\n\topts.string = toArr(opts.string);\n\topts.boolean = toArr(opts.boolean);\n\tif (alibi) for (k in opts.alias) {\n\t\tarr = opts.alias[k] = toArr(opts.alias[k]);\n\t\tfor (i = 0; i < arr.length; i++) (opts.alias[arr[i]] = arr.concat(k)).splice(i, 1);\n\t}\n\tfor (i = opts.boolean.length; i-- > 0;) {\n\t\tarr = opts.alias[opts.boolean[i]] || [];\n\t\tfor (j = arr.length; j-- > 0;) opts.boolean.push(arr[j]);\n\t}\n\tfor (i = opts.string.length; i-- > 0;) {\n\t\tarr = opts.alias[opts.string[i]] || [];\n\t\tfor (j = arr.length; j-- > 0;) opts.string.push(arr[j]);\n\t}\n\tif (defaults) for (k in opts.default) {\n\t\tname = typeof opts.default[k];\n\t\tarr = opts.alias[k] = opts.alias[k] || [];\n\t\tif (opts[name] !== void 0) {\n\t\t\topts[name].push(k);\n\t\t\tfor (i = 0; i < arr.length; i++) opts[name].push(arr[i]);\n\t\t}\n\t}\n\tconst keys = strict ? Object.keys(opts.alias) : [];\n\tfor (i = 0; i < len; i++) {\n\t\targ = args[i];\n\t\tif (arg === \"--\") {\n\t\t\tout._ = out._.concat(args.slice(++i));\n\t\t\tbreak;\n\t\t}\n\t\tfor (j = 0; j < arg.length; j++) if (arg.charCodeAt(j) !== 45) break;\n\t\tif (j === 0) out._.push(arg);\n\t\telse if (arg.substring(j, j + 3) === \"no-\") {\n\t\t\tname = arg.substring(j + 3);\n\t\t\tif (strict && !~keys.indexOf(name)) return opts.unknown(arg);\n\t\t\tout[name] = false;\n\t\t} else {\n\t\t\tfor (idx = j + 1; idx < arg.length; idx++) if (arg.charCodeAt(idx) === 61) break;\n\t\t\tname = arg.substring(j, idx);\n\t\t\tval = arg.substring(++idx) || i + 1 === len || (\"\" + args[i + 1]).charCodeAt(0) === 45 || args[++i];\n\t\t\tarr = j === 2 ? [name] : name;\n\t\t\tfor (idx = 0; idx < arr.length; idx++) {\n\t\t\t\tname = arr[idx];\n\t\t\t\tif (strict && !~keys.indexOf(name)) return opts.unknown(\"-\".repeat(j) + name);\n\t\t\t\ttoVal(out, name, idx + 1 < arr.length || val, opts);\n\t\t\t}\n\t\t}\n\t}\n\tif (defaults) {\n\t\tfor (k in opts.default) if (out[k] === void 0) out[k] = opts.default[k];\n\t}\n\tif (alibi) for (k in out) {\n\t\tarr = opts.alias[k] || [];\n\t\twhile (arr.length > 0) out[arr.shift()] = out[k];\n\t}\n\treturn out;\n}\n\n//#endregion\n//#region src/utils.ts\nfunction removeBrackets(v) {\n\treturn v.replace(/[<[].+/, \"\").trim();\n}\nfunction findAllBrackets(v) {\n\tconst ANGLED_BRACKET_RE_GLOBAL = /<([^>]+)>/g;\n\tconst SQUARE_BRACKET_RE_GLOBAL = /\\[([^\\]]+)\\]/g;\n\tconst res = [];\n\tconst parse = (match) => {\n\t\tlet variadic = false;\n\t\tlet value = match[1];\n\t\tif (value.startsWith(\"...\")) {\n\t\t\tvalue = value.slice(3);\n\t\t\tvariadic = true;\n\t\t}\n\t\treturn {\n\t\t\trequired: match[0].startsWith(\"<\"),\n\t\t\tvalue,\n\t\t\tvariadic\n\t\t};\n\t};\n\tlet angledMatch;\n\twhile (angledMatch = ANGLED_BRACKET_RE_GLOBAL.exec(v)) res.push(parse(angledMatch));\n\tlet squareMatch;\n\twhile (squareMatch = SQUARE_BRACKET_RE_GLOBAL.exec(v)) res.push(parse(squareMatch));\n\treturn res;\n}\nfunction getMriOptions(options) {\n\tconst result = {\n\t\talias: {},\n\t\tboolean: []\n\t};\n\tfor (const [index, option] of options.entries()) {\n\t\tif (option.names.length > 1) result.alias[option.names[0]] = option.names.slice(1);\n\t\tif (option.isBoolean) if (option.negated) {\n\t\t\tif (!options.some((o, i) => {\n\t\t\t\treturn i !== index && o.names.some((name) => option.names.includes(name)) && typeof o.required === \"boolean\";\n\t\t\t})) result.boolean.push(option.names[0]);\n\t\t} else result.boolean.push(option.names[0]);\n\t}\n\treturn result;\n}\nfunction findLongest(arr) {\n\treturn arr.sort((a, b) => {\n\t\treturn a.length > b.length ? -1 : 1;\n\t})[0];\n}\nfunction padRight(str, length) {\n\treturn str.length >= length ? str : `${str}${\" \".repeat(length - str.length)}`;\n}\nfunction camelcase(input) {\n\treturn input.replaceAll(/([a-z])-([a-z])/g, (_, p1, p2) => {\n\t\treturn p1 + p2.toUpperCase();\n\t});\n}\nfunction setDotProp(obj, keys, val) {\n\tlet current = obj;\n\tfor (let i = 0; i < keys.length; i++) {\n\t\tconst key = keys[i];\n\t\tif (i === keys.length - 1) {\n\t\t\tcurrent[key] = val;\n\t\t\treturn;\n\t\t}\n\t\tif (current[key] == null) {\n\t\t\tconst nextKeyIsArrayIndex = +keys[i + 1] > -1;\n\t\t\tcurrent[key] = nextKeyIsArrayIndex ? [] : {};\n\t\t}\n\t\tcurrent = current[key];\n\t}\n}\nfunction setByType(obj, transforms) {\n\tfor (const key of Object.keys(transforms)) {\n\t\tconst transform = transforms[key];\n\t\tif (transform.shouldTransform) {\n\t\t\tobj[key] = [obj[key]].flat();\n\t\t\tif (typeof transform.transformFunction === \"function\") obj[key] = obj[key].map(transform.transformFunction);\n\t\t}\n\t}\n}\nfunction getFileName(input) {\n\tconst m = /([^\\\\/]+)$/.exec(input);\n\treturn m ? m[1] : \"\";\n}\nfunction camelcaseOptionName(name) {\n\treturn name.split(\".\").map((v, i) => {\n\t\treturn i === 0 ? camelcase(v) : v;\n\t}).join(\".\");\n}\nvar CACError = class extends Error {\n\tconstructor(message) {\n\t\tsuper(message);\n\t\tthis.name = \"CACError\";\n\t\tif (typeof Error.captureStackTrace !== \"function\") this.stack = new Error(message).stack;\n\t}\n};\n\n//#endregion\n//#region src/option.ts\nvar Option = class {\n\trawName;\n\tdescription;\n\t/** Option name */\n\tname;\n\t/** Option name and aliases */\n\tnames;\n\tisBoolean;\n\trequired;\n\tconfig;\n\tnegated;\n\tconstructor(rawName, description, config) {\n\t\tthis.rawName = rawName;\n\t\tthis.description = description;\n\t\tthis.config = Object.assign({}, config);\n\t\trawName = rawName.replaceAll(\".*\", \"\");\n\t\tthis.negated = false;\n\t\tthis.names = removeBrackets(rawName).split(\",\").map((v) => {\n\t\t\tlet name = v.trim().replace(/^-{1,2}/, \"\");\n\t\t\tif (name.startsWith(\"no-\")) {\n\t\t\t\tthis.negated = true;\n\t\t\t\tname = name.replace(/^no-/, \"\");\n\t\t\t}\n\t\t\treturn camelcaseOptionName(name);\n\t\t}).sort((a, b) => a.length > b.length ? 1 : -1);\n\t\tthis.name = this.names.at(-1);\n\t\tif (this.negated && this.config.default == null) this.config.default = true;\n\t\tif (rawName.includes(\"<\")) this.required = true;\n\t\telse if (rawName.includes(\"[\")) this.required = false;\n\t\telse this.isBoolean = true;\n\t}\n};\n\n//#endregion\n//#region src/runtime.ts\nlet runtimeProcessArgs;\nlet runtimeInfo;\nif (typeof process !== \"undefined\") {\n\tlet runtimeName;\n\tif (typeof Deno !== \"undefined\" && typeof Deno.version?.deno === \"string\") runtimeName = \"deno\";\n\telse if (typeof Bun !== \"undefined\" && typeof Bun.version === \"string\") runtimeName = \"bun\";\n\telse runtimeName = \"node\";\n\truntimeInfo = `${process.platform}-${process.arch} ${runtimeName}-${process.version}`;\n\truntimeProcessArgs = process.argv;\n} else if (typeof navigator === \"undefined\") runtimeInfo = `unknown`;\nelse runtimeInfo = `${navigator.platform} ${navigator.userAgent}`;\n\n//#endregion\n//#region src/command.ts\nvar Command = class {\n\trawName;\n\tdescription;\n\tconfig;\n\tcli;\n\toptions;\n\taliasNames;\n\tname;\n\targs;\n\tcommandAction;\n\tusageText;\n\tversionNumber;\n\texamples;\n\thelpCallback;\n\tglobalCommand;\n\tconstructor(rawName, description, config = {}, cli) {\n\t\tthis.rawName = rawName;\n\t\tthis.description = description;\n\t\tthis.config = config;\n\t\tthis.cli = cli;\n\t\tthis.options = [];\n\t\tthis.aliasNames = [];\n\t\tthis.name = removeBrackets(rawName);\n\t\tthis.args = findAllBrackets(rawName);\n\t\tthis.examples = [];\n\t}\n\tusage(text) {\n\t\tthis.usageText = text;\n\t\treturn this;\n\t}\n\tallowUnknownOptions() {\n\t\tthis.config.allowUnknownOptions = true;\n\t\treturn this;\n\t}\n\tignoreOptionDefaultValue() {\n\t\tthis.config.ignoreOptionDefaultValue = true;\n\t\treturn this;\n\t}\n\tversion(version, customFlags = \"-v, --version\") {\n\t\tthis.versionNumber = version;\n\t\tthis.option(customFlags, \"Display version number\");\n\t\treturn this;\n\t}\n\texample(example) {\n\t\tthis.examples.push(example);\n\t\treturn this;\n\t}\n\t/**\n\t* Add a option for this command\n\t* @param rawName Raw option name(s)\n\t* @param description Option description\n\t* @param config Option config\n\t*/\n\toption(rawName, description, config) {\n\t\tconst option = new Option(rawName, description, config);\n\t\tthis.options.push(option);\n\t\treturn this;\n\t}\n\talias(name) {\n\t\tthis.aliasNames.push(name);\n\t\treturn this;\n\t}\n\taction(callback) {\n\t\tthis.commandAction = callback;\n\t\treturn this;\n\t}\n\t/**\n\t* Check if a command name is matched by this command\n\t* @param name Command name\n\t*/\n\tisMatched(name) {\n\t\treturn this.name === name || this.aliasNames.includes(name);\n\t}\n\tget isDefaultCommand() {\n\t\treturn this.name === \"\" || this.aliasNames.includes(\"!\");\n\t}\n\tget isGlobalCommand() {\n\t\treturn this instanceof GlobalCommand;\n\t}\n\t/**\n\t* Check if an option is registered in this command\n\t* @param name Option name\n\t*/\n\thasOption(name) {\n\t\tname = name.split(\".\")[0];\n\t\treturn this.options.find((option) => {\n\t\t\treturn option.names.includes(name);\n\t\t});\n\t}\n\toutputHelp() {\n\t\tconst { name, commands } = this.cli;\n\t\tconst { versionNumber, options: globalOptions, helpCallback } = this.cli.globalCommand;\n\t\tlet sections = [{ body: `${name}${versionNumber ? `/${versionNumber}` : \"\"}` }];\n\t\tsections.push({\n\t\t\ttitle: \"Usage\",\n\t\t\tbody: ` $ ${name} ${this.usageText || this.rawName}`\n\t\t});\n\t\tif ((this.isGlobalCommand || this.isDefaultCommand) && commands.length > 0) {\n\t\t\tconst longestCommandName = findLongest(commands.map((command) => command.rawName));\n\t\t\tsections.push({\n\t\t\t\ttitle: \"Commands\",\n\t\t\t\tbody: commands.map((command) => {\n\t\t\t\t\treturn ` ${padRight(command.rawName, longestCommandName.length)} ${command.description}`;\n\t\t\t\t}).join(\"\\n\")\n\t\t\t}, {\n\t\t\t\ttitle: `For more info, run any command with the \\`--help\\` flag`,\n\t\t\t\tbody: commands.map((command) => ` $ ${name}${command.name === \"\" ? \"\" : ` ${command.name}`} --help`).join(\"\\n\")\n\t\t\t});\n\t\t}\n\t\tlet options = this.isGlobalCommand ? globalOptions : [...this.options, ...globalOptions || []];\n\t\tif (!this.isGlobalCommand && !this.isDefaultCommand) options = options.filter((option) => option.name !== \"version\");\n\t\tif (options.length > 0) {\n\t\t\tconst longestOptionName = findLongest(options.map((option) => option.rawName));\n\t\t\tsections.push({\n\t\t\t\ttitle: \"Options\",\n\t\t\t\tbody: options.map((option) => {\n\t\t\t\t\treturn ` ${padRight(option.rawName, longestOptionName.length)} ${option.description} ${option.config.default === void 0 ? \"\" : `(default: ${option.config.default})`}`;\n\t\t\t\t}).join(\"\\n\")\n\t\t\t});\n\t\t}\n\t\tif (this.examples.length > 0) sections.push({\n\t\t\ttitle: \"Examples\",\n\t\t\tbody: this.examples.map((example) => {\n\t\t\t\tif (typeof example === \"function\") return example(name);\n\t\t\t\treturn example;\n\t\t\t}).join(\"\\n\")\n\t\t});\n\t\tif (helpCallback) sections = helpCallback(sections) || sections;\n\t\tconsole.info(sections.map((section) => {\n\t\t\treturn section.title ? `${section.title}:\\n${section.body}` : section.body;\n\t\t}).join(\"\\n\\n\"));\n\t}\n\toutputVersion() {\n\t\tconst { name } = this.cli;\n\t\tconst { versionNumber } = this.cli.globalCommand;\n\t\tif (versionNumber) console.info(`${name}/${versionNumber} ${runtimeInfo}`);\n\t}\n\tcheckRequiredArgs() {\n\t\tconst minimalArgsCount = this.args.filter((arg) => arg.required).length;\n\t\tif (this.cli.args.length < minimalArgsCount) throw new CACError(`missing required args for command \\`${this.rawName}\\``);\n\t}\n\t/**\n\t* Check if the parsed options contain any unknown options\n\t*\n\t* Exit and output error when true\n\t*/\n\tcheckUnknownOptions() {\n\t\tconst { options, globalCommand } = this.cli;\n\t\tif (!this.config.allowUnknownOptions) {\n\t\t\tfor (const name of Object.keys(options)) if (name !== \"--\" && !this.hasOption(name) && !globalCommand.hasOption(name)) throw new CACError(`Unknown option \\`${name.length > 1 ? `--${name}` : `-${name}`}\\``);\n\t\t}\n\t}\n\t/**\n\t* Check if the required string-type options exist\n\t*/\n\tcheckOptionValue() {\n\t\tconst { options: parsedOptions, globalCommand } = this.cli;\n\t\tconst options = [...globalCommand.options, ...this.options];\n\t\tfor (const option of options) {\n\t\t\tconst value = parsedOptions[option.name.split(\".\")[0]];\n\t\t\tif (option.required) {\n\t\t\t\tconst hasNegated = options.some((o) => o.negated && o.names.includes(option.name));\n\t\t\t\tif (value === true || value === false && !hasNegated) throw new CACError(`option \\`${option.rawName}\\` value is missing`);\n\t\t\t}\n\t\t}\n\t}\n\t/**\n\t* Check if the number of args is more than expected\n\t*/\n\tcheckUnusedArgs() {\n\t\tconst maximumArgsCount = this.args.some((arg) => arg.variadic) ? Infinity : this.args.length;\n\t\tif (maximumArgsCount < this.cli.args.length) throw new CACError(`Unused args: ${this.cli.args.slice(maximumArgsCount).map((arg) => `\\`${arg}\\``).join(\", \")}`);\n\t}\n};\nvar GlobalCommand = class extends Command {\n\tconstructor(cli) {\n\t\tsuper(\"@@global@@\", \"\", {}, cli);\n\t}\n};\n\n//#endregion\n//#region src/cac.ts\nvar CAC = class extends EventTarget {\n\t/** The program name to display in help and version message */\n\tname;\n\tcommands;\n\tglobalCommand;\n\tmatchedCommand;\n\tmatchedCommandName;\n\t/**\n\t* Raw CLI arguments\n\t*/\n\trawArgs;\n\t/**\n\t* Parsed CLI arguments\n\t*/\n\targs;\n\t/**\n\t* Parsed CLI options, camelCased\n\t*/\n\toptions;\n\tshowHelpOnExit;\n\tshowVersionOnExit;\n\t/**\n\t* @param name The program name to display in help and version message\n\t*/\n\tconstructor(name = \"\") {\n\t\tsuper();\n\t\tthis.name = name;\n\t\tthis.commands = [];\n\t\tthis.rawArgs = [];\n\t\tthis.args = [];\n\t\tthis.options = {};\n\t\tthis.globalCommand = new GlobalCommand(this);\n\t\tthis.globalCommand.usage(\"<command> [options]\");\n\t}\n\t/**\n\t* Add a global usage text.\n\t*\n\t* This is not used by sub-commands.\n\t*/\n\tusage(text) {\n\t\tthis.globalCommand.usage(text);\n\t\treturn this;\n\t}\n\t/**\n\t* Add a sub-command\n\t*/\n\tcommand(rawName, description, config) {\n\t\tconst command = new Command(rawName, description || \"\", config, this);\n\t\tcommand.globalCommand = this.globalCommand;\n\t\tthis.commands.push(command);\n\t\treturn command;\n\t}\n\t/**\n\t* Add a global CLI option.\n\t*\n\t* Which is also applied to sub-commands.\n\t*/\n\toption(rawName, description, config) {\n\t\tthis.globalCommand.option(rawName, description, config);\n\t\treturn this;\n\t}\n\t/**\n\t* Show help message when `-h, --help` flags appear.\n\t*\n\t*/\n\thelp(callback) {\n\t\tthis.globalCommand.option(\"-h, --help\", \"Display this message\");\n\t\tthis.globalCommand.helpCallback = callback;\n\t\tthis.showHelpOnExit = true;\n\t\treturn this;\n\t}\n\t/**\n\t* Show version number when `-v, --version` flags appear.\n\t*\n\t*/\n\tversion(version, customFlags = \"-v, --version\") {\n\t\tthis.globalCommand.version(version, customFlags);\n\t\tthis.showVersionOnExit = true;\n\t\treturn this;\n\t}\n\t/**\n\t* Add a global example.\n\t*\n\t* This example added here will not be used by sub-commands.\n\t*/\n\texample(example) {\n\t\tthis.globalCommand.example(example);\n\t\treturn this;\n\t}\n\t/**\n\t* Output the corresponding help message\n\t* When a sub-command is matched, output the help message for the command\n\t* Otherwise output the global one.\n\t*\n\t*/\n\toutputHelp() {\n\t\tif (this.matchedCommand) this.matchedCommand.outputHelp();\n\t\telse this.globalCommand.outputHelp();\n\t}\n\t/**\n\t* Output the version number.\n\t*\n\t*/\n\toutputVersion() {\n\t\tthis.globalCommand.outputVersion();\n\t}\n\tsetParsedInfo({ args, options }, matchedCommand, matchedCommandName) {\n\t\tthis.args = args;\n\t\tthis.options = options;\n\t\tif (matchedCommand) this.matchedCommand = matchedCommand;\n\t\tif (matchedCommandName) this.matchedCommandName = matchedCommandName;\n\t\treturn this;\n\t}\n\tunsetMatchedCommand() {\n\t\tthis.matchedCommand = void 0;\n\t\tthis.matchedCommandName = void 0;\n\t}\n\t/**\n\t* Parse argv\n\t*/\n\tparse(argv, { run = true } = {}) {\n\t\tif (!argv) {\n\t\t\tif (!runtimeProcessArgs) throw new Error(\"No argv provided and runtime process argv is not available.\");\n\t\t\targv = runtimeProcessArgs;\n\t\t}\n\t\tthis.rawArgs = argv;\n\t\tif (!this.name) this.name = argv[1] ? getFileName(argv[1]) : \"cli\";\n\t\tlet shouldParse = true;\n\t\tfor (const command of this.commands) {\n\t\t\tconst parsed = this.mri(argv.slice(2), command);\n\t\t\tconst commandName = parsed.args[0];\n\t\t\tif (command.isMatched(commandName)) {\n\t\t\t\tshouldParse = false;\n\t\t\t\tconst parsedInfo = {\n\t\t\t\t\t...parsed,\n\t\t\t\t\targs: parsed.args.slice(1)\n\t\t\t\t};\n\t\t\t\tthis.setParsedInfo(parsedInfo, command, commandName);\n\t\t\t\tthis.dispatchEvent(new CustomEvent(`command:${commandName}`, { detail: command }));\n\t\t\t}\n\t\t}\n\t\tif (shouldParse) {\n\t\t\tfor (const command of this.commands) if (command.isDefaultCommand) {\n\t\t\t\tshouldParse = false;\n\t\t\t\tconst parsed = this.mri(argv.slice(2), command);\n\t\t\t\tthis.setParsedInfo(parsed, command);\n\t\t\t\tthis.dispatchEvent(new CustomEvent(\"command:!\", { detail: command }));\n\t\t\t}\n\t\t}\n\t\tif (shouldParse) {\n\t\t\tconst parsed = this.mri(argv.slice(2));\n\t\t\tthis.setParsedInfo(parsed);\n\t\t}\n\t\tif (this.options.help && this.showHelpOnExit) {\n\t\t\tthis.outputHelp();\n\t\t\trun = false;\n\t\t\tthis.unsetMatchedCommand();\n\t\t}\n\t\tif (this.options.version && this.showVersionOnExit && this.matchedCommandName == null) {\n\t\t\tthis.outputVersion();\n\t\t\trun = false;\n\t\t\tthis.unsetMatchedCommand();\n\t\t}\n\t\tconst parsedArgv = {\n\t\t\targs: this.args,\n\t\t\toptions: this.options\n\t\t};\n\t\tif (run) this.runMatchedCommand();\n\t\tif (!this.matchedCommand && this.args[0]) this.dispatchEvent(new CustomEvent(\"command:*\", { detail: this.args[0] }));\n\t\treturn parsedArgv;\n\t}\n\tmri(argv, command) {\n\t\tconst cliOptions = [...this.globalCommand.options, ...command ? command.options : []];\n\t\tconst mriOptions = getMriOptions(cliOptions);\n\t\tlet argsAfterDoubleDashes = [];\n\t\tconst doubleDashesIndex = argv.indexOf(\"--\");\n\t\tif (doubleDashesIndex !== -1) {\n\t\t\targsAfterDoubleDashes = argv.slice(doubleDashesIndex + 1);\n\t\t\targv = argv.slice(0, doubleDashesIndex);\n\t\t}\n\t\tlet parsed = lib_default(argv, mriOptions);\n\t\tparsed = Object.keys(parsed).reduce((res, name) => {\n\t\t\treturn {\n\t\t\t\t...res,\n\t\t\t\t[camelcaseOptionName(name)]: parsed[name]\n\t\t\t};\n\t\t}, { _: [] });\n\t\tconst args = parsed._;\n\t\tconst options = { \"--\": argsAfterDoubleDashes };\n\t\tconst ignoreDefault = command && command.config.ignoreOptionDefaultValue ? command.config.ignoreOptionDefaultValue : this.globalCommand.config.ignoreOptionDefaultValue;\n\t\tconst transforms = Object.create(null);\n\t\tfor (const cliOption of cliOptions) {\n\t\t\tif (!ignoreDefault && cliOption.config.default !== void 0) for (const name of cliOption.names) options[name] = cliOption.config.default;\n\t\t\tif (Array.isArray(cliOption.config.type) && transforms[cliOption.name] === void 0) {\n\t\t\t\ttransforms[cliOption.name] = Object.create(null);\n\t\t\t\ttransforms[cliOption.name].shouldTransform = true;\n\t\t\t\ttransforms[cliOption.name].transformFunction = cliOption.config.type[0];\n\t\t\t}\n\t\t}\n\t\tfor (const key of Object.keys(parsed)) if (key !== \"_\") {\n\t\t\tsetDotProp(options, key.split(\".\"), parsed[key]);\n\t\t\tsetByType(options, transforms);\n\t\t}\n\t\treturn {\n\t\t\targs,\n\t\t\toptions\n\t\t};\n\t}\n\trunMatchedCommand() {\n\t\tconst { args, options, matchedCommand: command } = this;\n\t\tif (!command || !command.commandAction) return;\n\t\tcommand.checkUnknownOptions();\n\t\tcommand.checkOptionValue();\n\t\tcommand.checkRequiredArgs();\n\t\tcommand.checkUnusedArgs();\n\t\tconst actionArgs = [];\n\t\tcommand.args.forEach((arg, index) => {\n\t\t\tif (arg.variadic) actionArgs.push(args.slice(index));\n\t\t\telse actionArgs.push(args[index]);\n\t\t});\n\t\tactionArgs.push(options);\n\t\treturn command.commandAction.apply(this, actionArgs);\n\t}\n};\n\n//#endregion\n//#region src/index.ts\n/**\n* @param name The program name to display in help and version message\n*/\nconst cac = (name = \"\") => new CAC(name);\n\n//#endregion\nexport { CAC, Command, cac, cac as default };","{\n \"name\": \"@uzysjung/agent-harness\",\n \"version\": \"26.158.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.0.0\"\n },\n \"bin\": {\n \"agent-harness\": \"./dist/index.js\"\n },\n \"files\": [\n \"dist\",\n \"templates\",\n \"scripts/prune-ecc.sh\",\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 \"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 },\n \"devDependencies\": {\n \"@biomejs/biome\": \"^2.4.13\",\n \"@types/node\": \"^25.6.0\",\n \"@vitest/coverage-v8\": \"^4.1.10\",\n \"tsup\": \"^8.3.0\",\n \"typescript\": \"^5.6.0\",\n \"vitest\": \"^4.1.10\"\n },\n \"license\": \"MIT\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/uzysjung/uzys-agent-harness.git\"\n }\n}\n","/**\n * `install` subcommand — spec 검증 + 파이프라인 오케스트레이션 (v26.82.0, Phase R).\n * 화면 출력(헤더/Phase rows/산출물/Summary)은 `install-render.ts` 로 분리.\n */\n\nimport { resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { BASELINE_PREFIX, listBaselineTargets } from \"../baseline-targets.js\";\nimport type { Cli } from \"../cli.js\";\nimport { parseCliTargets } from \"../cli-targets.js\";\nimport { c, status, unifiedSection } from \"../design.js\";\nimport { EXTERNAL_ASSETS } from \"../external-assets.js\";\nimport { type InstallReport, runInstall as runInstallPipeline } from \"../installer.js\";\nimport {\n type CliTargets,\n type InstallScope,\n type InstallSpec,\n isInstallScope,\n isTrack,\n type Track,\n} from \"../types.js\";\nimport {\n createInstallRenderer,\n type PipelineCallbacks,\n renderCliArtifacts,\n renderFinalSummary,\n renderInstallHeader,\n renderUpdateSummary,\n} from \"./install-render.js\";\n\nexport interface InstallOptions {\n track?: string[];\n /** v0.7.0 — repeatable. cac type: [String]. v0.8.0 — legacy alias 'both'/'all' 제거됨. */\n cli?: string | string[];\n /** v26.63.0 — Phase 1 templates 의 files 라인 표시 (default: counts only). */\n verbose?: boolean;\n projectDir?: string;\n // v26.81.0 (ADR-022, BREAKING) — 자산 1:1 플래그 13종(withTauri/withGsd/withEcc/withTob/\n // withAddyAgentSkills/withUzysHarness/withSuperpowers/withWshobsonAgents/withOpenspec/\n // withBmad/withClaudeVideo/withUnderstandAnything/withAgentmemory) 완전 삭제.\n // 자산 선택 = generic `--with <id>` / `--without <id>` 만. 아래는 동작 옵션.\n withPrune?: boolean;\n withCodexTrust?: boolean;\n /**\n * v26.47.0 (Phase C full) — External Asset 직접 추가 (preset condition 무관 강제 포함).\n * cac repeatable. 예: `--with railway-skills --with impeccable`.\n * 옵션-키 동작 flag (예: `--with-prune`) 와 별개 — External Asset id 만.\n */\n with?: string | string[];\n /**\n * v26.47.0 (Phase C full) — External Asset 직접 제외 (preset 추천에서 unchecked).\n * cac repeatable. 예: `--without netlify-cli`.\n */\n without?: string | string[];\n /**\n * v26.64.0 (ADR-020) — Installation scope. `project` (default) | `global`.\n * 명시 안 하면 wizard 의 scope prompt → 비대화형은 \"project\".\n */\n scope?: string;\n}\n\nexport interface RunInstallResult {\n ok: boolean;\n cli: CliTargets;\n /** Deprecation warnings (alias 사용 시 emit). caller가 stderr로 출력. */\n warnings: ReadonlyArray<string>;\n message: string;\n report?: InstallReport;\n}\n\n/**\n * Lift raw flag options to a typed InstallSpec.\n * Returns a Result-shaped value so callers can render errors uniformly.\n */\nexport function specFromOptions(options: InstallOptions): RunInstallResult {\n const parsed = parseCliTargets(options.cli);\n if (!parsed.ok) {\n return {\n ok: false,\n cli: [\"claude\"],\n warnings: parsed.warnings,\n message: parsed.error ?? \"Invalid --cli value\",\n };\n }\n const trackInputs = options.track ?? [];\n if (trackInputs.length === 0) {\n return {\n ok: false,\n cli: parsed.targets,\n warnings: parsed.warnings,\n // v26.56.0 (F6) — wizard 진입 안내. `install` subcommand 는 non-interactive.\n message:\n \"At least one --track is required (e.g. --track tooling)\\n Interactive wizard: run without subcommand → `agent-harness` (drop the `install` word)\",\n };\n }\n for (const t of trackInputs) {\n if (!isTrack(t)) {\n return {\n ok: false,\n cli: parsed.targets,\n warnings: parsed.warnings,\n message: `Unknown track: ${t}`,\n };\n }\n }\n return {\n ok: true,\n cli: parsed.targets,\n warnings: parsed.warnings,\n message: \"spec valid\",\n };\n}\n\nexport interface InstallActionDeps {\n log?: (msg: string) => void;\n err?: (msg: string) => void;\n exit?: (code: number) => never;\n /** Override the install pipeline (used by tests to avoid real fs side effects). */\n runPipeline?: (\n spec: InstallSpec,\n harnessRoot: string,\n mode?: import(\"../installer.js\").InstallMode,\n callbacks?: PipelineCallbacks,\n ) => InstallReport;\n /** Override the harness root resolver (defaults to a path relative to this file). */\n resolveHarnessRoot?: () => string;\n}\n\nexport function installAction(options: InstallOptions, deps: InstallActionDeps = {}): void {\n const log = deps.log ?? console.log;\n const err = deps.err ?? console.error;\n const exit = deps.exit ?? ((code: number) => process.exit(code) as never);\n const runPipeline = deps.runPipeline ?? defaultRunPipeline;\n const resolveHarnessRoot = deps.resolveHarnessRoot ?? defaultHarnessRoot;\n\n const validated = specFromOptions(options);\n // Deprecation warnings to stderr (alias 사용 시), regardless of ok/fail.\n for (const w of validated.warnings) {\n err(c.yellow(`[WARN] ${w}`));\n }\n if (!validated.ok) {\n err(status.failure(c.red(`ERROR: ${validated.message}`)));\n exit(1);\n return;\n }\n\n // v26.47.0 — Phase C full: --with/--without repeatable → userOverride.\n const forceInclude = normalizeRepeatable(options.with);\n const forceExclude = normalizeRepeatable(options.without);\n // 2026-08-16 — `--without` 는 두 목록을 받는다: 외부 자산 id 와 트랙 baseline id\n // (`baseline:<kind>/<name>`). 위저드에서 체크를 풀 수 있는 것을 플래그로는 못 뺀다면 같은\n // 기능이 진입점마다 다른 것이고, 이 리포가 세 번 적발당한 표면 비대칭이다.\n const baselineIds = new Set(\n listBaselineTargets({ tracks: (options.track as Track[]) ?? [] }).map((t) => t.id),\n );\n const baselineExclude = forceExclude.filter((id) => baselineIds.has(id));\n\n // v26.49.0 — unknown asset id validation (silent ignore 방지).\n const validIds = new Set(EXTERNAL_ASSETS.map((a) => a.id));\n // `--with` 는 baseline id 를 받지 않는다 — 트랙 baseline 은 이미 기본 설치라 \"추가\"할 것이\n // 없다. 그런데 두 플래그를 한 루프에서 검사하던 탓에 `--with baseline:<id>` 가 경고 없이\n // 통과하고 아무 일도 안 했다: **조용히 no-op 하는 지시**는 ADR-074 가 두 목록을 안 섞은\n // 바로 그 이유다.\n for (const id of forceInclude) {\n if (validIds.has(id)) continue;\n err(\n c.yellow(\n id.startsWith(BASELINE_PREFIX)\n ? `[WARN] '${id}' cannot be used with --with — track baseline assets install by default. Use --without to drop one.`\n : `[WARN] Unknown asset id '${id}' (--with). Skipping. Use one of: ${[...validIds].sort().join(\", \")}`,\n ),\n );\n }\n for (const id of forceExclude) {\n if (validIds.has(id) || baselineIds.has(id)) continue;\n // 갈림은 **id 의 생김새**로 한다(사유가 아니다 — 오타와 트랙 밖은 여기서 구분되지 않는다).\n // `baseline:` 꼴이면 이 트랙의 후보 전체를 함께 보여 주는 편이 카탈로그 전체를 쏟는 것보다\n // 낫다. 반대로 카탈로그 id 오타에 baseline 목록을 보이면 엉뚱한 곳을 뒤지게 된다.\n err(\n c.yellow(\n id.startsWith(BASELINE_PREFIX)\n ? `[WARN] '${id}' is not installed by the selected track(s) — nothing to exclude. Available: ${[...baselineIds].sort().join(\", \")}`\n : `[WARN] Unknown asset id '${id}' (--without). Skipping. Use one of: ${[...validIds].sort().join(\", \")}`,\n ),\n );\n }\n const filteredInclude = forceInclude.filter((id) => validIds.has(id));\n const filteredExclude = forceExclude.filter((id) => validIds.has(id));\n const userOverride =\n filteredInclude.length > 0 || filteredExclude.length > 0\n ? { forceInclude: filteredInclude, forceExclude: filteredExclude }\n : undefined;\n\n const spec: InstallSpec = {\n tracks: (options.track as Track[]) ?? [],\n ...(userOverride ? { userOverride } : {}),\n ...(baselineExclude.length > 0 ? { baselineExclude } : {}),\n // v26.81.0 (ADR-022, BREAKING) — 자산 1:1 boolean 13종 삭제. 자산 선택은 위\n // userOverride(--with <id>)로 일원화. 잔존 = 설치 동작 옵션만.\n options: {\n withPrune: options.withPrune === true,\n withCodexTrust: options.withCodexTrust === true,\n },\n cli: validated.cli,\n projectDir: resolve(options.projectDir ?? process.cwd()),\n scope: resolveScopeOption(options.scope, err),\n };\n\n executeSpec(spec, {\n log,\n err,\n exit,\n runPipeline,\n resolveHarnessRoot,\n verbose: options.verbose === true,\n });\n}\n\nexport interface ExecuteSpecDeps {\n log?: (msg: string) => void;\n err?: (msg: string) => void;\n exit?: (code: number) => never;\n runPipeline?: (\n spec: InstallSpec,\n harnessRoot: string,\n mode?: import(\"../installer.js\").InstallMode,\n callbacks?: PipelineCallbacks,\n ) => InstallReport;\n resolveHarnessRoot?: () => string;\n /** Router action mode (forwarded to runInstall). Default \"fresh\". */\n mode?: import(\"../installer.js\").InstallMode;\n /**\n * v26.63.0 — wizard 모드 (Step 1~4 통과 후 호출) 식별. true 시:\n * - install header (TARGET / TRACKS / CLI / OPTIONS / ASSETS) 출력 skip\n * (Step 3 review + Step 4 confirm 에서 이미 표시)\n * - \"Step 5/5 — Installing\" 흐름에 자연 연결\n */\n fromWizard?: boolean;\n /**\n * v26.63.0 — verbose 출력 (Phase 1 templates 의 files 라인 표시).\n * Default false — 카운트 + use 만 표시 (cognitive load 감소).\n */\n verbose?: boolean;\n}\n\n/**\n * Run the install pipeline for a fully-validated InstallSpec and render the\n * report. Shared by the `install` flag-mode command and the default\n * (interactive) action so both have identical post-install output.\n */\nexport function executeSpec(spec: InstallSpec, deps: ExecuteSpecDeps = {}): void {\n const log = deps.log ?? console.log;\n const err = deps.err ?? console.error;\n const exit = deps.exit ?? ((code: number) => process.exit(code) as never);\n const runPipeline = deps.runPipeline ?? defaultRunPipeline;\n const resolveHarnessRoot = deps.resolveHarnessRoot ?? defaultHarnessRoot;\n\n // v26.63.0 — wizard 모드는 header (TARGET ~ ASSETS) 출력 skip — Step 3/4 에서 이미 표시.\n // non-interactive (--track ...) 모드는 기존 header 유지 — 사용자 spec 확인 cue 필요.\n if (!deps.fromWizard) {\n renderInstallHeader(log, spec, deps.mode);\n }\n\n // v26.63.0 — phaseHeader → unifiedSection. Phase 카운터 (1/2/3) 제거 — 5-step 통합 시\n // wizard step 5/5 안 sub-section 으로 자연 흐름. Update mode 도 동일.\n log(unifiedSection(deps.mode === \"update\" ? \"Update Mode\" : \"Templates\"));\n log(\"\");\n\n // Streaming progress: baseline 완료 시 즉시 Phase 1 rows 출력, external은 per-asset 스트리밍.\n const renderer = createInstallRenderer(log, spec, deps.verbose === true);\n\n let report: InstallReport;\n try {\n report = runPipeline(spec, resolveHarnessRoot(), deps.mode, renderer.callbacks);\n } catch (e: unknown) {\n const detail = e instanceof Error ? e.message : String(e);\n log(\"\");\n err(status.failure(c.red(`install failed — ${detail}`)));\n exit(1);\n return;\n }\n\n // Update mode 단축 출력 — manifest copy / external 모두 skip\n if (report.updateMode) {\n renderUpdateSummary(log, spec, report);\n return;\n }\n\n // Phase 2 trailing newline (if header was printed)\n if (renderer.phase2HeaderPrinted()) {\n log(\"\");\n }\n\n renderCliArtifacts(log, spec, report);\n renderFinalSummary(log, spec, report, deps.fromWizard === true);\n}\n\n/**\n * v26.64.0 (ADR-020) — `--scope` flag 해석. invalid 값은 warn + \"project\" default.\n * 비대화형 (--track 명시) 진입에서만 호출. wizard 는 별도 prompt.\n */\nfunction resolveScopeOption(value: string | undefined, err: (msg: string) => void): InstallScope {\n if (value === undefined) return \"project\";\n if (isInstallScope(value)) return value;\n err(\n c.yellow(`[WARN] Unknown --scope value '${value}' (expected: project, global). Using project.`),\n );\n return \"project\";\n}\n\n/**\n * v26.47.0 — Normalize cac repeatable flag (string | string[] | undefined) → string[].\n * Trim 빈 문자열 + dedup.\n */\nfunction normalizeRepeatable(value: string | string[] | undefined): string[] {\n if (!value) return [];\n const arr = Array.isArray(value) ? value : [value];\n return [...new Set(arr.map((s) => s.trim()).filter((s) => s.length > 0))];\n}\n\n/* v8 ignore start — thin dep-inject defaults. tests 는 항상 runPipeline / resolveHarnessRoot 주입. */\nfunction defaultRunPipeline(\n spec: InstallSpec,\n harnessRoot: string,\n mode?: import(\"../installer.js\").InstallMode,\n callbacks?: PipelineCallbacks,\n): InstallReport {\n const ctx: import(\"../installer.js\").InstallContext = {\n harnessRoot,\n projectDir: spec.projectDir,\n spec,\n };\n if (mode) ctx.mode = mode;\n if (callbacks?.onProgress) ctx.onProgress = callbacks.onProgress;\n if (callbacks?.externalDeps) ctx.externalDeps = callbacks.externalDeps;\n return runInstallPipeline(ctx);\n}\n\nfunction defaultHarnessRoot(): string {\n // The bundled CLI lives at <root>/dist/index.js. import.meta.url + ../ resolves to <root>.\n // fileURLToPath 필수 — `.pathname` 은 공백/비ASCII 경로를 percent-encoded 로 남겨\n // \"Templates dir not found\" 로 install 이 실패한다 (v26.103.0 SOD 리뷰 2기 독립 수렴).\n return resolve(fileURLToPath(new URL(\".\", import.meta.url)), \"..\");\n}\n\n/* v8 ignore stop */\n\nexport { defaultHarnessRoot };\n\nexport function registerInstallCommand(cli: Cli): void {\n cli\n .command(\"install\", \"Install harness assets into a project\")\n // === Track / CLI / Project ===\n .option(\"--track <name>\", \"[Track] Track to install (repeatable)\", { type: [String] })\n .option(\n \"--cli <target>\",\n \"[CLI] Target CLI (repeatable): claude | codex | opencode | antigravity\",\n {\n type: [String],\n default: \"claude\",\n },\n )\n .option(\"--project-dir <path>\", \"[Project] Target project directory\", {\n default: process.cwd(),\n })\n .option(\"--scope <scope>\", \"[Scope] Installation scope: project (default) | global\", {\n default: \"project\",\n })\n // === Asset selection (Phase C full, v26.47.0+) ===\n .option(\n \"--with <asset-id>\",\n \"[Asset] Force-include External Asset id (regardless of preset). Repeatable. v26.47.0+\",\n )\n .option(\n \"--without <asset-id>\",\n \"[Asset] Force-exclude External Asset id (drop from preset recommendation). Repeatable. v26.47.0+\",\n )\n // === Codex global (v26.46.0+) ===\n .option(\n \"--with-codex-trust\",\n \"[Codex] Codex global opt-in: register trust entry in ~/.codex/config.toml\",\n )\n // v26.81.0 (ADR-022, BREAKING) — 자산 1:1 플래그 13종 삭제. 자산 opt-in 은 전부\n // generic `--with <asset-id>` (위) — 자산 id 목록은 docs/COMPATIBILITY.md 표 참조.\n // 아래는 자산이 아닌 설치 동작 옵션만.\n .option(\n \"--with-prune\",\n \"[Behavior] Prune ECC items beyond curated 89 (use with --with ecc-plugin)\",\n )\n // === Misc ===\n .option(\"--verbose\", \"[Misc] Show installed file lists per category (default: counts only)\")\n // === Examples (v26.50.0+) ===\n .example(\"install --track tooling --with marketingskills\")\n .example(\"install --track csr-supabase --cli claude --cli codex\")\n .example(\"install --track csr-supabase --without netlify-cli --with railway-skills\")\n /* v8 ignore next — cac action callback. installAction 자체는 별도 tests 로 검증. */\n .action((options: InstallOptions) => installAction(options));\n}\n","/**\n * baseline-targets.ts — 트랙이 고르는 자산을 **선택 가능한 항목으로** 드러낸다.\n *\n * 왜 있는가: 트랙을 고르면 룰·에이전트·훅·스킬이 화면에 한 번도 안 나온 채 깔렸다. 위저드\n * 3단계는 외부 자산(`EXTERNAL_ASSETS`)만 물었고, `manifest.ts` 가 `applies()` 로 고르는 것들은\n * 설치가 끝난 뒤 요약에서 처음 보였다 — 사용자 지적: *\"기술스택 선택했다고 알아서 설치되는\n * 구조는 개선해. 설치항목에 대해서는 모두 투명하게 다 보여줘야 해.\"*\n *\n * 이 모듈은 그 목록을 **manifest 에서 derive** 한다. 이름을 여기 옮겨 적으면 그게 곧 두 번째\n * 하드코딩 사본이 되고, 자산이 늘어도 화면은 그대로인 상태가 조용히 생긴다 — 이 리포가\n * 반복해서 당한 실패 모드다.\n *\n * **고를 수 있는 것만 낸다.** `settings.json`·앵커·`.mcp.json`·스켈레톤은 빼는 순간 설치가\n * 반쪽이 되므로 목록에 없다. 판별은 대상 경로 접두 하나로 하고, 그 접두는 설치 화면의 분류기\n * (`installer.ts` categorize)와 같은 술어다.\n */\n\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { extractFrontmatter, resolveBundleRoot } from \"./context-cost.js\";\nimport { type AssetSpec, buildManifest } from \"./manifest.js\";\n\n/** 사용자가 해제할 수 있는 baseline 자산 종류. */\nexport type BaselineKind = \"rules\" | \"agents\" | \"hooks\" | \"skills\";\n\nexport const BASELINE_KINDS: ReadonlyArray<BaselineKind> = [\"rules\", \"agents\", \"hooks\", \"skills\"];\n\n/** 선택 항목 id 의 접두. `asset:` / `option:` 과 같은 자리의 세 번째 네임스페이스다. */\nexport const BASELINE_PREFIX = \"baseline:\";\n\nexport interface BaselineTarget {\n /** `baseline:<kind>/<name>` — 위저드 체크 값이자 `--without` 인자. */\n id: string;\n kind: BaselineKind;\n /** 사용자에게 보이는 이름 (`git-policy`, `reviewer`, `protect-files`, `north-star`). */\n name: string;\n /**\n * #421 — 위저드 행 옆의 한 줄 설명. 파일 자체에서 뽑는다(룰 = 제목 + 첫 문장 · 에이전트·스킬 =\n * frontmatter description · 훅 = 머리 주석) — 여기 따로 적으면 파일이 바뀔 때 썩는다.\n * `withBaselineHints` 가 채운다. 없으면 이름만 보인다.\n */\n hint?: string;\n}\n\nconst PREFIX_BY_KIND: ReadonlyArray<[BaselineKind, string]> = [\n [\"rules\", \".claude/rules/\"],\n [\"agents\", \".claude/agents/\"],\n [\"hooks\", \".claude/hooks/\"],\n [\"skills\", \".claude/skills/\"],\n];\n\n/**\n * 설치 대상 경로 → 선택 항목. 고를 수 없는 자산이면 null.\n *\n * 스킬은 **첫 경로 조각**으로 이름을 잡는다. #409 이전에는 manifest 에 디렉터리 엔트리와\n * 파일 엔트리(`.claude/skills/<id>/SKILL.md`)가 섞여 있었고, 마지막 조각을 쓰면 후자가\n * `SKILL.md` 라는 이름의 항목으로 떴다. **지금은 전부 디렉터리 단위라 그 혼재는 없다**\n * (`tests/skill-registration-uniform.test.ts` 가 강제). 첫 조각을 계속 쓰는 이유는 그래도\n * 스킬 하위 파일이 개별 엔트리로 들어올 여지가 남기 때문이고, 그때도 같은 스킬의 여러 형태가\n * **한 체크박스로 합쳐진다** — 해제하면 둘 다 빠지는 것이 맞다.\n */\nexport function classifyBaselineTarget(target: string): BaselineTarget | null {\n for (const [kind, prefix] of PREFIX_BY_KIND) {\n if (!target.startsWith(prefix)) continue;\n const rest = target.slice(prefix.length);\n if (rest === \"\") return null;\n const first = rest.split(\"/\")[0] ?? \"\";\n const name = kind === \"skills\" ? first : first.replace(/\\.(md|sh)$/, \"\");\n if (name === \"\") return null;\n return { id: `${BASELINE_PREFIX}${kind}/${name}`, kind, name };\n }\n return null;\n}\n\n/**\n * 이 spec 에서 실제로 깔릴 baseline 자산 목록 (kind → name 순 정렬, 중복 제거).\n *\n * `applies()` 를 여기서 평가한다 — 안 깔릴 것을 화면에 내면 사용자는 해제할 수 없는 항목을\n * 보게 되고, 그건 투명성이 아니라 소음이다.\n *\n * **알려진 한계: `withEcc` 는 이 시점에 모른다** (독립 리뷰 F5). `ecc-plugin` 은 같은 3단계의\n * **뒤쪽 페이지**에서 고르는데 baseline 페이지가 앞에 있어, 여기서는 `withEcc` 가 undefined 다.\n * 결과는 **한 방향으로만 틀린다**: `!s.withEcc` 폴백 자산이 화면에 뜨는데 `ecc-plugin` 을 고르면\n * 안 깔린다. 그 반대(안 보여 준 것이 깔리는 것)는 일어나지 않으므로 **조용한 설치는 생기지 않고**,\n * 체크를 남겨 둔 항목은 제외 목록에도 안 들어가 오제외도 없다.\n *\n * **대상은 에이전트만이 아니다** — 스킬이 더 많다. 전 트랙을 켜면 14종(에이전트 4 · 스킬 10),\n * `full` 도 같고, 좁은 트랙은 더 적다(`tooling` 7 · `project-management` 4. 실측 2026-08-26).\n * 여기 이름을 옮겨 적지 않는 이유는 `listBaselineTargets` 를 `withEcc` 없이/`true` 로 두 번 불러\n * **diff 하면 그 자리에서 나오기** 때문이다 — 목록 사본은 게이트가 늘 때마다 썩는다. 실제로 옛\n * 문안이 \"에이전트 4종\"으로 열거했다가 #340 이 스킬 4종에 같은 게이트를 붙이면서 틀려졌다.\n *\n * 고치려면 baseline 페이지를 자산 페이지 **뒤로** 옮겨야 한다 — 사용자가 확정한 \"맨 앞에서 전부\n * 보여준다\"를 뒤집는 결정이라 여기서 임의로 하지 않는다 (ADR-074 Consequences).\n *\n * `selectedInternalSkills` 는 한계가 아니다. 그 12종은 전부 `EXTERNAL_ASSETS` 항목이라 자산\n * 페이지에서 이미 개별 선택되고, 여기 또 내면 **한 스킬에 체크박스가 둘** 생긴다.\n */\nexport function listBaselineTargets(spec: AssetSpec): BaselineTarget[] {\n const byId = new Map<string, BaselineTarget>();\n for (const entry of buildManifest(spec)) {\n if (!entry.applies(spec)) continue;\n const t = classifyBaselineTarget(entry.target);\n if (t && !byId.has(t.id)) byId.set(t.id, t);\n }\n const order = new Map(BASELINE_KINDS.map((k, i) => [k, i]));\n return [...byId.values()].sort(\n (a, b) => (order.get(a.kind) ?? 0) - (order.get(b.kind) ?? 0) || a.name.localeCompare(b.name),\n );\n}\n\n/**\n * 제외 판정. 대상 경로가 해제된 항목에 속하면 true.\n *\n * 고를 수 없는 자산(`settings.json` 등)은 `classifyBaselineTarget` 이 null 을 내므로 **어떤\n * 제외 목록으로도 빠지지 않는다** — 잘못된 id 가 들어와도 설치가 반쪽이 되지 않는다.\n */\nexport function isBaselineExcluded(target: string, excluded: ReadonlySet<string>): boolean {\n if (excluded.size === 0) return false;\n const t = classifyBaselineTarget(target);\n return t !== null && excluded.has(t.id);\n}\n\nconst HINT_MAX = 96;\n\nfunction clip(text: string): string {\n const t = text.replace(/\\s+/g, \" \").trim();\n return t.length > HINT_MAX ? `${t.slice(0, HINT_MAX - 1)}…` : t;\n}\n\n/**\n * #421 — 대상 파일에서 한 줄 설명을 뽑는다. 사용자 관측: \"Rule, Agent 에 대한 설명이 불친절\" —\n * 이름(`git-policy`)만으로는 체크를 풀지 말지 판단할 수 없었다.\n */\nexport function describeBaselineTarget(\n t: BaselineTarget,\n templatesDir: string = join(resolveBundleRoot(), \"templates\"),\n): string | undefined {\n const file =\n t.kind === \"skills\"\n ? join(templatesDir, \"skills\", t.name, \"SKILL.md\")\n : join(templatesDir, t.kind, `${t.name}.${t.kind === \"hooks\" ? \"sh\" : \"md\"}`);\n if (!existsSync(file)) return undefined;\n const content = readFileSync(file, \"utf8\");\n if (t.kind === \"agents\" || t.kind === \"skills\") {\n const fm = extractFrontmatter(content) ?? \"\";\n const m = /^description:\\s*(.+)$/m.exec(fm);\n const raw = m?.[1]?.trim().replace(/^[\"']|[\"']$/g, \"\");\n return raw ? clip(raw) : undefined;\n }\n if (t.kind === \"hooks\") {\n // shebang 다음의 주석 줄들 — 첫 빈 줄이나 코드가 나오기 전까지\n const lines = content.split(\"\\n\").slice(1);\n const comment: string[] = [];\n for (const l of lines) {\n if (!l.startsWith(\"#\")) break;\n comment.push(l.replace(/^#\\s?/, \"\"));\n }\n return comment.length > 0 ? clip(comment.join(\" · \")) : undefined;\n }\n // rules — `# 제목` + 첫 본문 줄\n const body = content.split(\"\\n\");\n const heading = body\n .find((l) => l.startsWith(\"# \"))\n ?.slice(2)\n .trim();\n const first = body\n .slice(body.findIndex((l) => l.startsWith(\"# \")) + 1)\n .find((l) => l.trim() !== \"\")\n ?.replace(/^[-*]\\s+/, \"\")\n .replace(/\\*\\*/g, \"\");\n if (!heading) return undefined;\n return clip(first ? `${heading} · ${first}` : heading);\n}\n\n/** 목록 전체에 설명을 붙인다 — 위저드가 부른다. 파일이 없으면 그 행은 이름만 남는다. */\nexport function withBaselineHints(\n targets: ReadonlyArray<BaselineTarget>,\n templatesDir?: string,\n): BaselineTarget[] {\n return targets.map((t) => {\n const hint = describeBaselineTarget(t, templatesDir);\n return hint ? { ...t, hint } : t;\n });\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\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","/**\n * design.ts — CLI visual design tokens (color, symbols, layout helpers).\n *\n * Aesthetic direction: **refined ops-report**. Mission control feel without\n * decoration noise. Phase markers + aligned 2-column rows + structured summary.\n *\n * Goals:\n * 1. Make input-wait points visually obvious (handled by @clack/prompts).\n * 2. Make the install pipeline output legible:\n * - phase-segmented progress (━━━ Phase N · Title ━━━)\n * - aligned per-asset rows (✓/⊘/✗ + id + meta)\n * - explicit skipped/failed reporting (no silent skips)\n * - terminal-width responsive (default 78)\n * 3. Zero runtime dependencies — emit raw ANSI escapes.\n *\n * `NO_COLOR` is honored per https://no-color.org.\n */\n\nconst isColorEnabled = (() => {\n if (process.env.NO_COLOR && process.env.NO_COLOR !== \"\") {\n return false;\n }\n // stdout may be missing in some test contexts\n return Boolean(process.stdout?.isTTY);\n})();\n\nfunction wrap(open: number, close: number) {\n return (s: string): string => {\n if (!isColorEnabled) {\n return s;\n }\n return `\\x1b[${open}m${s}\\x1b[${close}m`;\n };\n}\n\nexport const c = {\n bold: wrap(1, 22),\n dim: wrap(2, 22),\n red: wrap(31, 39),\n green: wrap(32, 39),\n yellow: wrap(33, 39),\n cyan: wrap(36, 39),\n gray: wrap(90, 39),\n};\n\nexport const symbol = {\n success: \"✓\",\n failure: \"✗\",\n skip: \"⊘\",\n arrow: \"›\",\n pointer: \"▸\",\n bullet: \"•\",\n warn: \"⚠\",\n /** Heavy horizontal box-drawing — phase dividers. */\n rule: \"━\",\n /** Middle dot — section separator. */\n mid: \"·\",\n};\n\n/** Default width for phase headers / dividers. Terminal default ≥ 78. */\nexport const DEFAULT_WIDTH = 78;\n\n/** Render a legacy section header. Bold cyan with a leading arrow. (kept for backward compat) */\nexport function header(title: string): string {\n return c.bold(c.cyan(`${symbol.arrow} ${title}`));\n}\n\n/**\n * Render a phase header — `━━━ Phase N · Title ━━━━━━━...` (full-width).\n * v26.63.0 (deprecated): kept for non-interactive mode + backward compat.\n * wizard 모드는 unifiedSection() 사용 (5-step 통합 — phase 카운터 무관).\n */\nexport function phaseHeader(n: number | string, title: string, width = DEFAULT_WIDTH): string {\n const label = `${symbol.rule}${symbol.rule}${symbol.rule} Phase ${n} ${symbol.mid} ${title} `;\n const fill = symbol.rule.repeat(Math.max(0, width - visibleLength(label)));\n return c.bold(c.cyan(`${label}${fill}`));\n}\n\n/**\n * v26.63.0 — Step 5 (Installing) 안의 sub-section 헤더.\n * `━━ Templates ━━` / `━━ External assets (n) ━━` / `━━ Codex artifacts ━━`.\n * phaseHeader (3 rule + Phase N + ·) 대비 단순 — 2 rule + title.\n */\nexport function unifiedSection(title: string, width = DEFAULT_WIDTH): string {\n const label = `${symbol.rule}${symbol.rule} ${title} `;\n const fill = symbol.rule.repeat(Math.max(0, width - visibleLength(label)));\n return c.bold(c.cyan(`${label}${fill}`));\n}\n\n/**\n * Render a section header (non-phase) — `━━━ Title ━━━━━━━...`.\n * Used for TARGET / SUMMARY / NEXT etc.\n */\nexport function sectionHeader(title: string, width = DEFAULT_WIDTH): string {\n const label = `${symbol.rule}${symbol.rule}${symbol.rule} ${title} `;\n const fill = symbol.rule.repeat(Math.max(0, width - visibleLength(label)));\n return c.bold(c.cyan(`${label}${fill}`));\n}\n\n/** Plain horizontal divider — `━━━...━━━` (no label). */\nexport function divider(width = DEFAULT_WIDTH): string {\n return c.dim(symbol.rule.repeat(width));\n}\n\n/**\n * Render a `key: value` row with a fixed-width left column for alignment.\n * Used in pre-flight / summary blocks (▸ TRACKS executive, tooling).\n */\nexport function infoRow(key: string, value: string, width = 12): string {\n const label = `${symbol.pointer} ${key}`.padEnd(width + 2, \" \");\n return ` ${c.dim(label)} ${value}`;\n}\n\n/** Backward-compat — `keyValue` (used by older install report). */\nexport function keyValue(key: string, value: string, width = 16): string {\n const padded = `${key}:`.padEnd(width, \" \");\n return ` ${c.dim(padded)} ${value}`;\n}\n\n/**\n * Render an asset row — ` ✓ asset-id meta`.\n * symbol = success/skip/failure. label = stable id (left-pad). meta = dim right column.\n */\nexport function assetRow(\n kind: \"success\" | \"skip\" | \"failure\",\n label: string,\n meta = \"\",\n labelWidth = 40,\n): string {\n const sym = renderSymbol(kind);\n const labelPadded = label.padEnd(labelWidth, \" \");\n const metaText = meta ? c.dim(meta) : \"\";\n return ` ${sym} ${labelPadded} ${metaText}`.trimEnd();\n}\n\nfunction renderSymbol(kind: \"success\" | \"skip\" | \"failure\"): string {\n switch (kind) {\n case \"success\":\n return c.green(symbol.success);\n case \"skip\":\n return c.yellow(symbol.skip);\n case \"failure\":\n return c.red(symbol.failure);\n }\n}\n\nexport const status = {\n success: (msg: string): string => `${c.green(symbol.success)} ${msg}`,\n failure: (msg: string): string => `${c.red(symbol.failure)} ${msg}`,\n warn: (msg: string): string => `${c.yellow(symbol.warn)} ${msg}`,\n info: (msg: string): string => `${c.cyan(symbol.bullet)} ${msg}`,\n};\n\n/**\n * Strip ANSI escape sequences so visible width can be measured (for header padding).\n */\nfunction visibleLength(s: string): number {\n // biome-ignore lint/suspicious/noControlCharactersInRegex: ANSI escape stripping requires \\x1b\n return s.replace(/\\x1b\\[[0-9;]*m/g, \"\").length;\n}\n\n/**\n * v26.63.2 — Pad to fixed display width (ANSI-aware). spacing scale 정렬 용.\n */\nexport function padDisplay(s: string, width: number): string {\n const visible = visibleLength(s);\n return visible >= width ? s : s + \" \".repeat(width - visible);\n}\n","import {\n chmodSync,\n existsSync,\n mkdirSync,\n readdirSync,\n readFileSync,\n writeFileSync,\n} from \"node:fs\";\nimport { basename, dirname, join, resolve } from \"node:path\";\nimport type { AntigravityTransformReport } from \"./antigravity/transform.js\";\nimport { isBaselineExcluded } from \"./baseline-targets.js\";\nimport { type CiScaffoldReport, installCiScaffold } from \"./ci-scaffold.js\";\nimport { runCliTransforms } from \"./cli-transforms.js\";\nimport type { CodexOptInReport } from \"./codex/opt-in.js\";\nimport type { CodexTransformReport } from \"./codex/transform.js\";\nimport { addGitignoreAgentArtifacts, addGitignoreEnv, writeEnvExample } from \"./env-files.js\";\nimport { EXTERNAL_ASSETS, isAssetSelected } from \"./external-assets.js\";\nimport {\n type ExternalInstallerDeps,\n type ExternalInstallReport,\n runExternalInstall,\n selectExternalTargets,\n} from \"./external-installer.js\";\nimport { foreignOwnedTarget } from \"./foreign-slot.js\";\nimport {\n backupDir,\n backupFile,\n backupFileIfChanged,\n copyBackupDir,\n copyDir,\n copyFile,\n ensureProjectSkeleton,\n} from \"./fs-ops.js\";\nimport {\n buildInstallLog,\n collectPolicyHashes,\n collectSkillHashes,\n hashContent,\n type InstallLog,\n type InstallLogRootFile,\n type InstallLogSkillFile,\n mergeExternalFiles,\n POLICY_DIRS,\n readInstallLog,\n writeInstallLog,\n} from \"./install-log.js\";\nimport {\n type AssetSpec,\n buildAssetSpec,\n buildManifest,\n isCliNeutralTarget,\n resolveRules,\n} from \"./manifest.js\";\nimport { composeMcpJson, writeMcpJson } from \"./mcp-merge.js\";\nimport type { OpencodeTransformReport } from \"./opencode/transform.js\";\nimport { HARNESS_ANCHOR_FILE, upsertHarnessImport } from \"./project-claude-merge.js\";\nimport { type InstallSpec, type OptionFlags, resolveScope, type Track } from \"./types.js\";\nimport { cleanStaleHookRefs, runUpdateMode, type UpdateModeReport } from \"./update-mode.js\";\n\n/**\n * Install mode — Router action 매핑.\n * - \"fresh\" : 첫 설치 (기본값)\n * - \"add\" : 기존 위에 Track union 추가 (backup 없음)\n * - \"update\" : 정책 파일만 templates로 갱신 (backup + orphan prune + stale hook)\n * - \"reinstall\" : 기존 .claude/ backup 후 처음부터 (backup 강제)\n */\nexport type InstallMode = \"fresh\" | \"add\" | \"update\" | \"reinstall\";\n\n/**\n * 각 mode 를 **비대화형으로 도달하는 CLI 명령** (없으면 null = 위저드 전용).\n *\n * 왜 코드로 두나: `install` 은 플래그로 되는데 `update` 는 위저드로만 되던 상태가 오래\n * 방치됐다. \"CI 로 깔 수는 있는데 갱신할 수는 없다\"는 수요 문제가 아니라 계열 비대칭이고,\n * 사람이 매번 계열 전체를 기억해서 대조해야 하면 그 규약은 이미 실패한 것이다.\n *\n * `Record<InstallMode, ...>` 라서 **mode 를 추가하면 여기 분류하기 전에는 컴파일이 안 된다.**\n * null 을 고르는 건 허용하지만 그 순간 \"위저드 전용\"이 명시적 선언이 되고, 아래 테스트가\n * 그 목록을 화면에 내보낸다 — 침묵으로 빠지는 경로가 없다.\n */\nexport const MODE_ENTRY_POINT: Record<InstallMode, string | null> = {\n fresh: \"install\",\n // 기존 설치 위에 `install --track <new>` = add. mode 는 헤더 라벨만 다르고 동작은 fresh 와 같다\n // (backup 없음 · manifest copy 동일) — 별도 명령이 필요 없다.\n add: \"install\",\n update: \"update\",\n // 미제공 — `.claude/` 를 통째로 backup 으로 **옮기는** 파괴적 경로다. 비대화형 진입점을\n // 붙일지는 별도 판단 사항이라 열어둔다 (열어둔 것 자체가 이 표에 보인다).\n reinstall: null,\n};\n\nexport interface InstallContext {\n /** Path to the harness repo (where `templates/` lives). */\n harnessRoot: string;\n /** Target project directory. */\n projectDir: string;\n spec: InstallSpec;\n /**\n * Router action mode. Defaults to \"fresh\".\n * - \"add\"/\"update\"/\"reinstall\" trigger different install paths.\n * - reinstall + update force backup=true.\n */\n mode?: InstallMode;\n /**\n * When true, an existing .claude/ is renamed to a timestamped backup before install.\n * Auto-true when mode ∈ {update, reinstall}.\n */\n backup?: boolean;\n /**\n * External install (claude plugin / npm -g / npx skills) injection point.\n * Default: real `runExternalInstall`. Tests inject mock to avoid real spawn.\n * Pass `null` to disable external install entirely.\n */\n runExternal?:\n | ((\n // v26.77.0 — projectDir: 외부 설치기 spawn cwd (자산 착지 위치). Bug B fix.\n // v26.81.0 (ADR-022) — userOverride: 자산 opt-in(--with <id>) 전파 (flag 13종 대체).\n ctx: {\n tracks: ReadonlyArray<Track>;\n options: OptionFlags;\n projectDir?: string;\n userOverride?: {\n forceInclude: ReadonlyArray<string>;\n forceExclude: ReadonlyArray<string>;\n };\n },\n deps: ExternalInstallerDeps,\n ) => ExternalInstallReport)\n | null;\n /**\n * Progress callback fired between stages so renderers can stream output\n * (avoids \"Phase 1 header → 5 minutes silence\" UX problem).\n */\n onProgress?: (event: ProgressEvent) => void;\n /** External installer streaming hooks (forwarded to runExternalInstall). */\n externalDeps?: Pick<ExternalInstallerDeps, \"onAssetStart\" | \"onAssetResult\">;\n}\n\n/** Progress event types fired during runInstall. */\nexport type ProgressEvent =\n /** Baseline (manifest copy + mcp + envFiles + Codex/OpenCode transforms) finished. External not yet started. */\n | { type: \"baseline-complete\"; baseline: BaselineReport }\n /** External install phase about to begin. */\n | { type: \"external-start\"; assetCount: number }\n /** External install phase finished (with report). */\n | { type: \"external-complete\"; report: ExternalInstallReport }\n /** v26.64.0 — install log write 실패 (non-fatal). */\n | { type: \"install-log-error\"; message: string };\n\n/**\n * v0.6.1 — Phase 1 output 카테고리별 분류. install renderer가 각 카테고리별로 row를 출력한다.\n * Names는 description용 (display only); 빈 배열이면 row 출력 skip.\n */\nexport interface BaselineCategoryCounts {\n /** rule 파일 names (확장자 제외) — git-policy, change-management 등 */\n rules: string[];\n /** agent 파일 names */\n agents: string[];\n /** hook 파일 names (확장자 제외) */\n hooks: string[];\n /** commands 디렉토리 카운트 (uzys + ecc) — names는 디렉토리라 무의미 */\n commands: number;\n /** skill 디렉토리 names */\n skills: string[];\n}\n\n/** Baseline phase result (everything except external assets). */\nexport interface BaselineReport {\n filesCopied: number;\n dirsCopied: number;\n skipped: number;\n backup: string | null;\n installedTracks: string[];\n mcpServers: string[];\n codex: CodexTransformReport | null;\n codexOptIn: CodexOptInReport | null;\n opencode: OpencodeTransformReport | null;\n /** v26.66.0 — Present when spec.cli includes \"antigravity\". */\n antigravity: AntigravityTransformReport | null;\n updateMode: UpdateModeReport | null;\n mode: InstallMode;\n envFiles: {\n envExampleCreated: boolean;\n gitignoreEnvAdded: boolean;\n /**\n * v0.8.0 — `.gitignore`에 추가된 자동 생성물 디렉토리 패턴\n * (`.factory/`, `.goose/`, 2026-08-02부터 `.uzys-agent-harness/`).\n * 필드명은 v0.8.0 당시 범위(npx skills)를 그대로 쓴다 — 개명은 표면 3곳을 함께 건드린다.\n */\n gitignoreNpxSkillsAdded: string[];\n };\n /**\n * v26.108.0 (ADR-037) — CI 스캐폴드 결과. opt-in 미선택 시 null. `.github/workflows/`\n * 는 CLI-agnostic 이라 claude baseline 밖의 전용 단계 (ci-scaffold.ts) 가 설치 주체.\n */\n ciScaffold: CiScaffoldReport | null;\n /** v0.6.1 — Phase 1 카테고리별 카운트 + names. Update mode에서는 빈 객체. */\n categories?: BaselineCategoryCounts;\n /**\n * Root CLAUDE.md 처리 결과. null when claude baseline disabled.\n * `created` = 없던 파일을 fill-in 스캐폴드로 만들었다. false 면 기존 사용자 파일에 앵커\n * import 한 줄만 얹었다는 뜻 — 두 경우의 보고 문구가 달라야 한다 (스캐폴드를 쓰지도 않고\n * \"fill-in scaffold\" 라고 보고하면 그게 거짓 보고다).\n */\n rootClaudeMd: { tracks: ReadonlyArray<Track>; created: boolean } | null;\n /**\n * 2026-08-16 — 사용자가 위저드에서 체크를 푼 트랙 자산의 대상 경로.\n *\n * 화면에 내는 이유는 설치 화면이 **무엇이 깔렸는가**만 말하면 해제가 먹혔는지 확인할 길이\n * 없기 때문이다. 0건이면 아무것도 안 뜬다.\n */\n baselineExcluded: string[];\n /** `baselineExcluded` 중 **디스크에 그대로 남은** 것 (`add`·`reinstall`). 화면이 이걸 표시한다. */\n baselineExcludedOnDisk: string[];\n /**\n * #343 — 깔릴 자리가 디렉터리가 아니라 건너뛴 대상 (`.claude/` 포함 상대경로).\n * 화면에 이름을 내지 않으면 사용자는 **고른 자산이 왜 없는지** 알 방법이 없다.\n */\n baselineForeignOwned: string[];\n /** 덮어쓰기 전 보존한 사용자 파일 백업 경로 (settings.json·CLAUDE.md, fresh/add 모드). audit SEC-1/CODE-2. */\n backups?: string[];\n}\n\nexport interface InstallReport {\n filesCopied: number;\n dirsCopied: number;\n skipped: number;\n backup: string | null;\n installedTracks: string[];\n mcpServers: string[];\n /** Present when spec.cli includes \"codex\". */\n codex: CodexTransformReport | null;\n /** Present when Codex transform ran AND user opted-in to global skills/trust/prompts. null otherwise. */\n codexOptIn: CodexOptInReport | null;\n /** Present when spec.cli includes \"opencode\". */\n opencode: OpencodeTransformReport | null;\n /** v26.66.0 — Present when spec.cli includes \"antigravity\". */\n antigravity: AntigravityTransformReport | null;\n /** v26.108.0 (ADR-037) — CI 스캐폴드 결과 (opt-in 미선택 시 null). */\n ciScaffold: CiScaffoldReport | null;\n /** External install report (claude plugin / npm -g / npx skills). null when disabled or empty. */\n external: ExternalInstallReport | null;\n /** Update-mode report (rules/agents/commands/hooks/skills 갱신 + orphan prune + stale hook). null when not update mode. */\n updateMode: UpdateModeReport | null;\n /**\n * M-1 — settings.json 이 가리키는 없는 스크립트를 지운 결과 (`.claude/` 기준 상대경로).\n * install 은 settings.json 을 매번 템플릿으로 덮어쓰므로 치유도 매번 다시 해야 한다.\n * claude 미선택 시 `.claude/settings.json` 자체가 없어 항상 `[]`.\n */\n staleHookRefs: string[];\n /**\n * 2026-08-16 — 사용자가 위저드 3단계에서 **체크를 푼** 트랙 자산의 대상 경로.\n *\n * `BaselineReport` 와 같은 필드를 여기 다시 선언하는 이유는 두 타입이 별개이기 때문이다\n * (런타임은 `{...baseline}` 로 이미 흐른다). 타입에만 없으면 호출자가 결과를 못 읽고,\n * 그러면 \"해제가 먹혔는지\" 를 프로그램으로 확인할 방법이 사라진다.\n */\n baselineExcluded: string[];\n /** `baselineExcluded` 중 디스크에 남은 것. `BaselineReport` 와 같은 이유로 여기도 선언한다. */\n baselineExcludedOnDisk: string[];\n /** 자리가 디렉터리가 아니라 건너뛴 대상. `BaselineReport` 와 같은 이유로 여기도 선언한다. */\n baselineForeignOwned: string[];\n /** Install mode dispatched (echo of ctx.mode, default \"fresh\"). */\n mode: InstallMode;\n /** Environment file generation results (always present). */\n envFiles: {\n /** true if .env.example was created (csr-supabase/full only). */\n envExampleCreated: boolean;\n /** true if .gitignore got `.env` line appended. */\n gitignoreEnvAdded: boolean;\n /**\n * v0.8.0 — `.gitignore`에 추가된 자동 생성물 디렉토리 패턴\n * (`.factory/`, `.goose/`, 2026-08-02부터 `.uzys-agent-harness/`).\n * 필드명은 v0.8.0 당시 범위(npx skills)를 그대로 쓴다 — 개명은 표면 3곳을 함께 건드린다.\n */\n gitignoreNpxSkillsAdded: string[];\n };\n}\n\n/**\n * Run the installation pipeline. Pure function modulo filesystem side effects.\n * v26.82.0 (Phase R) — 276줄 단일 함수를 단계별 블록 함수로 분해 (동작 변경 0):\n * update 단축 / claude baseline / CLI transforms / external / install log.\n */\nexport function runInstall(ctx: InstallContext): InstallReport {\n const { harnessRoot, projectDir, spec } = ctx;\n const mode: InstallMode = ctx.mode ?? \"fresh\";\n const templatesDir = join(harnessRoot, \"templates\");\n\n if (!existsSync(templatesDir)) {\n throw new Error(`Templates dir not found: ${templatesDir}`);\n }\n\n const claudeDir = join(projectDir, \".claude\");\n\n // v26.123.0 (F-1a) — 추가 설치가 이전 설치 기록을 지우지 않도록 기존 로그를 먼저 읽는다.\n // reinstall 은 아래에서 `.claude/` 를 통째로 backup 으로 옮기므로 그 뒤엔 읽을 수 없다.\n const previousLog = readInstallLog(projectDir);\n\n // Update mode pre-flight — 갱신할 **설치**가 있어야 한다. backup 전에 검증.\n //\n // 판정 기준은 `.claude/` 가 아니다. update 는 v26.134.0(ADR-049)부터 외부 CLI 산출물도\n // 갱신하므로 `.claude/` 가 없는 codex/opencode/antigravity 단독 설치도 정당한 대상이고,\n // `src/commands/update.ts` 의 pre-flight 는 이미 그렇게 판정한다(#253). **파이프라인만\n // `.claude/` 를 요구해 그 사용자를 거절하고 있었다** — 명령은 통과시키고 파이프라인이\n // throw 하니, 비 Claude 단독 사용자는 새 자산을 받을 길이 재설치뿐이었다(독립 검증 C-2c).\n // 설치의 CLI 중립 증거는 install log 다.\n //\n // 단, **claude 를 고른 설치인데 `.claude/` 가 없으면** 그건 정상 상태가 아니라 깨진 설치다 —\n // 그대로 진행하면 룰만 복원되고 `settings.json`·훅이 없는 반쪽 `.claude/` 가 만들어진다\n // (독립 재검증 M-R2). 그 경우는 예전처럼 막고 재설치로 보낸다.\n const claudeWasSelected = previousLog?.spec.cli.includes(\"claude\") ?? false;\n if (mode === \"update\" && !existsSync(claudeDir) && (previousLog === null || claudeWasSelected)) {\n // 두 상황을 같은 문장으로 말하지 않는다 — 하나는 \"깔린 게 없다\", 다른 하나는 \"깔렸는데\n // 일부가 사라졌다\"이고, 사용자가 할 일이 다르다. 후자를 \"설치가 없다\"고 하면 로그를 눈으로\n // 본 사람은 도구가 틀렸다고 생각한다.\n throw new Error(\n claudeWasSelected\n ? `Update mode found a broken install at ${projectDir} — this project installed Claude Code assets but \\`.claude/\\` is gone. Reinstall instead: agent-harness install --track <name>`\n : `Update mode requires an existing install at ${projectDir}`,\n );\n }\n\n const backupPath = resolveBackupPath(ctx, mode, claudeDir);\n\n // Update mode 단축 — 정책 파일만 갱신하고 종료 (manifest copy / external 모두 skip)\n if (mode === \"update\") {\n return runUpdateInstall(ctx, templatesDir, backupPath);\n }\n\n const manifestSpec = buildManifestSpec(spec);\n\n // v0.8.0 — `.claude/` baseline은 spec.cli에 \"claude\" 포함 시에만 생성.\n // Codex/OpenCode 단독 사용자는 dead weight 회피.\n // ADR-047 — 덮어쓰기 전 소유 판정에 쓸 기준선. `.claude/` 를 옮겨낸 뒤(reinstall)엔 대조할\n // 대상이 없으므로 previousLog 를 그대로 쓰되, 그 경우 아래 existsSync 가 자연히 걸러낸다.\n const policyBase = new Map((previousLog?.policyFiles ?? []).map((f) => [f.path, f.sha256]));\n\n // 위저드 3단계에서 사용자가 **해제한** 트랙 자산. 비어 있으면(기본) 아무것도 안 거른다.\n const baselineExcluded = new Set(spec.baselineExclude ?? []);\n\n const base = spec.cli.includes(\"claude\")\n ? installClaudeBaseline(manifestSpec, projectDir, templatesDir, policyBase, baselineExcluded)\n : // claude 미선택이어도 CLI 중립 자산은 깔린다. manifest 전체가 `.claude/` baseline 안에서만\n // 돌던 탓에 이 자산들이 claude 설치에만 도달했는데, **배포 룰 본문이 이 스크립트들을\n // 호출 지점으로 지목한다** — 즉 없는 도구를 있다고 안내하고 있었다(#300 과 같은 형태).\n installCliNeutralAssets(manifestSpec, projectDir, templatesDir, baselineExcluded);\n\n // Compose .mcp.json from template + track-mcp-map.tsv (Codex/OpenCode도 사용 — claude 무관)\n const mcpResult = composeAndWriteMcp(harnessRoot, projectDir, spec);\n\n // v26.108.0 (ADR-037) — CI 스캐폴드 (opt-in 전용). `.github/` 은 CLI-agnostic 이라\n // claude baseline 조건 밖에서 설치. 기존 워크플로 파일은 절대 덮어쓰지 않는다.\n const ciScaffold = isAssetSelected(\"ci-scaffold\", {\n tracks: spec.tracks,\n options: spec.options,\n ...(spec.userOverride ? { userOverride: spec.userOverride } : {}),\n })\n ? installCiScaffold({ harnessRoot, projectDir, tracks: spec.tracks })\n : null;\n\n // v26.133.0 (ADR-048) — 외부 CLI transform 도 소유자 판정을 받는다. 기준선은 `.claude/` 와\n // 별도 필드(`externalFiles`)다: 저기는 templates 복사라 사후에 디스크를 훑어 만들지만\n // (`collectPolicyHashes`), 여기는 **렌더 결과**라 훑어서는 무엇이 하네스 것인지 알 수 없다.\n const {\n externalFiles,\n externalBackups,\n externalUpdated: _externalUpdated,\n externalBackedUp: _externalBackedUp,\n externalForeignOwned,\n ...cliTransforms\n } = runCliTransforms({\n harnessRoot,\n projectDir,\n cli: spec.cli,\n selectedInternalSkills: manifestSpec.selectedInternalSkills,\n // 룰 목록의 SSOT 는 하나다 — `.claude/rules/` 를 채우는 것과 같은 `resolveRules` 결과가\n // 나머지 세 CLI 로도 간다. 여기서 다시 고르면 CLI 마다 다른 룰이 깔린다.\n rules: resolveRules(manifestSpec).filter(\n (r) => !isBaselineExcluded(`.claude/rules/${r}.md`, baselineExcluded),\n ),\n previousExternal: previousLog?.externalFiles ?? [],\n codexTrust: (spec.scope ?? \"project\") === \"global\" && spec.options.withCodexTrust,\n });\n\n const baseline: BaselineReport = {\n filesCopied: base.filesCopied,\n dirsCopied: base.dirsCopied,\n skipped: base.skipped,\n backup: backupPath,\n installedTracks: [...spec.tracks].sort(),\n mcpServers: Object.keys(mcpResult.mcpServers).sort(),\n ...cliTransforms,\n ciScaffold,\n updateMode: null,\n mode,\n envFiles: writeEnvironmentFiles(projectDir, spec.tracks),\n categories: base.categories,\n rootClaudeMd: base.rootClaudeMd,\n // 외부 CLI 백업도 같은 줄에 노출한다 — 백업이 화면에 안 보이면 사용자는 자기 편집분이\n // 어디 갔는지 알 수 없고, 그러면 백업은 있어도 없는 것과 같다 (ADR-046/047 과 같은 이유).\n backups: [...base.backups, ...externalBackups],\n baselineExcluded: base.excluded,\n baselineExcludedOnDisk: base.excludedOnDisk,\n // `.claude/` baseline 과 외부 CLI 산출물의 같은 판정을 **한 목록으로** 낸다.\n baselineForeignOwned: [\n ...base.foreignOwned,\n ...externalForeignOwned.filter((f) => !base.foreignOwned.includes(f)),\n ],\n };\n\n // ━━━ Baseline complete — emit progress event so renderer can show Phase 1 rows ━━━\n ctx.onProgress?.({ type: \"baseline-complete\", baseline });\n\n // ━━━ External assets (claude plugin / npm -g / npx skills) ━━━\n const external = runExternalPhase(ctx);\n\n // ━━━ M-1 — settings.json stale hook ref 치유 (baseline·external 뒤 1회) ━━━\n // 여기서 부르는 이유: 앞 단계들이 settings.json 과 참조 대상(스킬/훅 파일)을 모두 확정한\n // 뒤여야 \"무엇이 없는가\"가 답이 된다. 판정하지 않고 **디스크가 답하게 한다** — 설치자에\n // withEcc 사본이 생기지 않는다 (ADR-049 와 같은 형태).\n const staleHookRefs = healStaleHookRefs(spec, projectDir);\n\n // ━━━ v26.64.0 (ADR-020) — Install log write ━━━\n // backupPath 가 있으면 `.claude/` 를 rename 으로 밀어냈다는 뜻 — 그 안에 살던 이전 자산은\n // 실제로 사라졌으므로 누적에서 빠져야 한다 (fresh/add 는 backupPath=null → 전부 유지).\n writeInstallLogSafe(\n ctx,\n externalFiles,\n external,\n base.rootClaudeMdLog,\n previousLog,\n backupPath !== null,\n collectRootFiles(baseline.envFiles, ciScaffold, mcpResult.created),\n );\n\n return { ...baseline, external, staleHookRefs };\n}\n\n/**\n * M-1 — settings.json 이 참조하는 없는 `.claude/**` 스크립트를 제거한다.\n *\n * update 쪽 치유기(`cleanStaleHookRefs`)를 그대로 재사용한다. install 이 settings.json 을\n * 매번 템플릿으로 덮어쓰므로(`copyFile`) 치유는 설치마다 다시 필요하고, 같은 술어를 두 벌\n * 두면 그 사본이 다음 drift 서식지가 된다.\n */\nfunction healStaleHookRefs(spec: InstallSpec, projectDir: string): string[] {\n // claude 미선택이면 `.claude/settings.json` 자체가 없다.\n if (!spec.cli.includes(\"claude\")) return [];\n const settingsPath = join(projectDir, \".claude/settings.json\");\n if (!existsSync(settingsPath)) return [];\n return cleanStaleHookRefs(settingsPath, join(projectDir, \".claude\"));\n}\n\n/**\n * Backup auto-on for update + reinstall (sourced from router action).\n * Update: copy backup (preserve original .claude/ for in-place update).\n * Reinstall + others: rename backup (move .claude/ aside, then full install).\n */\nfunction resolveBackupPath(\n ctx: InstallContext,\n mode: InstallMode,\n claudeDir: string,\n): string | null {\n const wantBackup = ctx.backup ?? (mode === \"update\" || mode === \"reinstall\");\n if (!wantBackup) return null;\n return mode === \"update\" ? copyBackupDir(claudeDir) : backupDir(claudeDir);\n}\n\n/**\n * Update mode 단축 경로 — manifest copy 는 건너뛴다.\n *\n * 갱신 대상은 ⓐ 정책 파일 ⓑ 외부 CLI 산출물(ADR-049) ⓒ **설치 기록에 적힌 외부 스킬**(#374).\n * 세 번째가 오래 빠져 있었고, 그동안 화면은 다 갱신된 것처럼 보였다.\n */\nfunction runUpdateInstall(\n ctx: InstallContext,\n templatesDir: string,\n backupPath: string | null,\n): InstallReport {\n const updateReport = runUpdateMode(\n ctx.projectDir,\n templatesDir,\n ctx.harnessRoot,\n {},\n ctx.spec.updateOnly,\n );\n const baseline: BaselineReport = {\n filesCopied: 0,\n dirsCopied: 0,\n skipped: 0,\n baselineExcluded: [],\n baselineExcludedOnDisk: [],\n baselineForeignOwned: [],\n backup: backupPath,\n installedTracks: [...ctx.spec.tracks].sort(),\n mcpServers: [],\n codex: null,\n codexOptIn: null,\n opencode: null,\n antigravity: null,\n ciScaffold: null,\n updateMode: updateReport,\n mode: \"update\",\n envFiles: {\n envExampleCreated: false,\n gitignoreEnvAdded: false,\n gitignoreNpxSkillsAdded: [],\n },\n rootClaudeMd: null,\n };\n ctx.onProgress?.({ type: \"baseline-complete\", baseline });\n // update 경로의 치유 결과는 `updateMode.staleHookRefs` 가 이미 싣는다 — 여기서 다시 담으면\n // 같은 사실이 두 필드가 되고 렌더가 중복 보고한다.\n return { ...baseline, external: null, staleHookRefs: [] };\n}\n\n/**\n * v26.81.0 (ADR-022) — manifest 게이팅 입력. 내부 자산 선택 판정 — 이전\n * OptionFlags.withTauri/withUzysHarness/withEcc boolean 자리를 카탈로그 선택\n * (wizard 체크 / --with <id> → forceInclude)으로 대체 (manifest 필드명은 유지).\n */\nexport function buildManifestSpec(spec: InstallSpec): Required<AssetSpec> {\n // derive 본체는 `manifest.ts` 의 `buildAssetSpec` 하나다 (#320) — 계측 경로가 같은 것을 부른다.\n // 여기 다시 조립하면 그 순간 사본이 둘이 되고, 그게 #320 의 원인이었다.\n return buildAssetSpec({\n tracks: spec.tracks,\n options: spec.options,\n ...(spec.userOverride ? { userOverride: spec.userOverride } : {}),\n });\n}\n\n/** `.claude/` baseline (manifest copy) 결과. claude 미선택 시 emptyClaudeBaseline(). */\ninterface ClaudeBaselineResult {\n filesCopied: number;\n dirsCopied: number;\n skipped: number;\n categories: BaselineCategoryCounts;\n rootClaudeMd: { tracks: ReadonlyArray<Track>; created: boolean } | null;\n /** 하네스 앵커 파일 무결성 기록 — uninstall 시 사용자 수정 여부 판별 (install 원본과 sha 비교). */\n rootClaudeMdLog: { path: string; sha256: string } | null;\n /** 덮어쓰기 전 보존한 사용자 파일 백업 경로 (settings.json·CLAUDE.md). audit SEC-1/CODE-2. */\n backups: string[];\n /**\n * 2026-08-16 — 사용자가 위저드에서 **체크를 푼** 자산의 대상 경로.\n *\n * `skipped` 와 나눈다: 저건 \"원본이 없어서 못 깔았다\"는 결함 신호이고 이건 정상 선택이다.\n * 한 숫자에 담으면 설치 화면이 사용자의 선택을 결함으로 보고한다.\n */\n excluded: string[];\n /**\n * 해제했는데 **디스크에 그대로 남아 있는** 대상 (`excluded` 의 부분집합).\n *\n * `add`·`reinstall` 은 이전 설치본을 지우지 않으므로 \"제외됨\"만 찍으면 화면이 디스크와 다른\n * 말을 한다 — 사용자는 파일이 사라진 줄 알고, 실제로는 그 룰이 계속 상주한다. 체크 해제는\n * 제거가 아니라는 기존 규약(v26.125.0 `● installed` 마커)을 baseline 항목에도 적용한다.\n */\n excludedOnDisk: string[];\n /**\n * #343 — 깔릴 자리가 **디렉터리가 아닌 것**으로 이미 차 있어 건너뛴 대상.\n *\n * `excluded`(사용자가 체크를 풂) · `skipped`(원본 부재)와 셋 다 다른 사실이라 따로 센다:\n * 이건 **디스크 쪽 사정**이고, 사용자가 고를 때는 보이지 않던 것이다.\n */\n foreignOwned: string[];\n}\n\nfunction emptyClaudeBaseline(): ClaudeBaselineResult {\n return {\n filesCopied: 0,\n dirsCopied: 0,\n skipped: 0,\n categories: { rules: [], agents: [], hooks: [], commands: 0, skills: [] },\n rootClaudeMd: null,\n rootClaudeMdLog: null,\n backups: [],\n excluded: [],\n excludedOnDisk: [],\n foreignOwned: [],\n };\n}\n\n/**\n * CLI 중립 자산(`.uzys-agent-harness/`)만 설치한다 — claude 를 고르지 않은 설치용.\n *\n * manifest 는 통째로 `.claude/` baseline 안에서만 돌았고, 그래서 `protect-branch.sh` ·\n * `spec-drift-check.sh` 는 두 entry 의 주석이 \"CLI 중립 슬롯\"이라 적어 두었음에도 claude\n * 설치에만 도달했다. 배포 룰 본문이 이 스크립트들을 호출 지점으로 지목하므로, 도달하지 않으면\n * 룰이 **없는 도구를 있다고 안내**하게 된다 (#300 과 같은 형태).\n *\n * `.claude/` 를 만들지 않는 것이 이 함수의 존재 이유다 — 스켈레톤·훅 chmod·루트 CLAUDE.md\n * 병합은 전부 claude 전용이라 여기서 하지 않는다.\n *\n * **해제한 룰은 여기서도 보고한다** (ADR-074). 이 경로에서도 제외는 실제로 작동한다 —\n * `runCliTransforms` 로 가는 `rules` 가 걸러지므로 `AGENTS.md`·`.agents/rules/` 에서 빠진다.\n * 그런데 보고가 없으면 **제외가 가장 안 보이는 곳에서 화면도 침묵한다**: 눈으로 확인할\n * `.claude/rules/` 디렉터리조차 없는 설치다. 룰만 세는 이유는 룰이 `.claude/` 밖으로 나가는\n * 유일한 baseline 종류이기 때문이다(에이전트·훅·트랙 스킬은 비 Claude 표면이 없다).\n */\nfunction installCliNeutralAssets(\n manifestSpec: Required<AssetSpec>,\n projectDir: string,\n templatesDir: string,\n baselineExcluded: ReadonlySet<string>,\n): ClaudeBaselineResult {\n const result = emptyClaudeBaseline();\n for (const entry of buildManifest(manifestSpec)) {\n if (!entry.applies(manifestSpec)) continue;\n if (\n entry.target.startsWith(\".claude/rules/\") &&\n isBaselineExcluded(entry.target, baselineExcluded)\n ) {\n result.excluded.push(entry.target);\n continue;\n }\n if (!isCliNeutralTarget(entry.target)) continue;\n const source = join(templatesDir, entry.source);\n if (!existsSync(source)) {\n result.skipped += 1;\n continue;\n }\n copyFile(source, join(projectDir, entry.target));\n result.filesCopied += 1;\n }\n return result;\n}\n\n/** `.claude/` baseline — manifest copy + hook chmod + .installed-tracks + root CLAUDE.md merge. */\n/**\n * 정책 파일(rules/agents/commands/hooks)을 덮어쓰기 전 사용자 편집분 보호 (v26.132.0 · ADR-047).\n *\n * 판정은 update 와 **같은 기준선**(install log `policyFiles`)을 쓴다 — 두 명령이 서로 다른\n * 기준으로 \"사용자가 고쳤다\"를 판정하면 한쪽이 백업한 걸 다른 쪽이 조용히 밀 수 있다.\n *\n * @returns 백업 경로. 백업이 불필요했으면 null.\n */\nfunction backupEditedPolicyFile(\n entryTarget: string,\n target: string,\n source: string,\n baseline: ReadonlyMap<string, string>,\n): string | null {\n const prefix = \".claude/\";\n if (!entryTarget.startsWith(prefix)) return null;\n const rel = entryTarget.slice(prefix.length);\n if (!POLICY_DIRS.some(({ dir, ext }) => rel.startsWith(`${dir}/`) && rel.endsWith(ext))) {\n return null;\n }\n if (!existsSync(target)) return null;\n const current = readFileSync(target, \"utf-8\");\n if (current === readFileSync(source, \"utf-8\")) return null; // 이미 최신 — 백업 불필요\n const recorded = baseline.get(rel);\n if (recorded !== undefined && recorded === hashContent(current)) return null; // 하네스가 놓아둔 그대로\n return backupFile(target);\n}\n\nfunction installClaudeBaseline(\n manifestSpec: Required<AssetSpec>,\n projectDir: string,\n templatesDir: string,\n policyBase: ReadonlyMap<string, string>,\n baselineExcluded: ReadonlySet<string>,\n): ClaudeBaselineResult {\n ensureProjectSkeleton(projectDir);\n\n const result = emptyClaudeBaseline();\n const manifest = buildManifest(manifestSpec);\n\n for (const entry of manifest) {\n if (!entry.applies(manifestSpec)) {\n continue;\n }\n const target = join(projectDir, entry.target);\n // 사용자가 3단계에서 체크를 푼 자산. `skipped` 로 세지 않는다 — 저 카운터는 \"원본이 없어서\n // 못 깔았다\"는 결함 신호이고, 이쪽은 사용자가 그러라고 한 것이다. 둘을 한 숫자에 담으면\n // 설치 화면이 정상 선택을 결함으로 보고한다.\n if (isBaselineExcluded(entry.target, baselineExcluded)) {\n result.excluded.push(entry.target);\n // 이미 있던 파일은 지우지 않는다(체크 해제 ≠ 제거). 그 사실을 여기서 잡아 두지 않으면\n // `add`·`reinstall` 화면이 \"제외됨\"이라 적고 파일은 그대로 남는다 — 화면이 디스크와\n // 다른 말을 하는 것이고, 그게 이 PR 이 없애려던 상태다.\n if (existsSync(target)) result.excludedOnDisk.push(entry.target);\n continue;\n }\n const source = join(templatesDir, entry.source);\n if (!existsSync(source)) {\n result.skipped += 1;\n continue;\n }\n // #343 — 남의 도구가 소유한 스킬 슬롯에는 쓰지 않는다 (판정 SSOT = foreign-slot.ts).\n // 슬롯이 링크인 경우와 슬롯 안 파일이 링크인 경우를 한 술어가 함께 본다.\n const foreign = foreignOwnedTarget(projectDir, entry.target);\n if (foreign !== null) {\n // 슬롯 단위로 한 번만 보고한다 — 사용자가 옮겨야 할 대상은 파일이 아니라 그 자리다.\n if (!result.foreignOwned.includes(foreign)) result.foreignOwned.push(foreign);\n continue;\n }\n if (entry.type === \"file\") {\n // 사용자 편집 가능 파일은 덮어쓰기 전 백업 (audit SEC-1 — settings.json hook/statusLine 소실 방지).\n if (entry.target === \".claude/settings.json\") {\n const backup = backupFileIfChanged(target, readFileSync(source, \"utf-8\"));\n if (backup) {\n result.backups.push(backup);\n }\n } else {\n // v26.132.0 (ADR-047) — 룰·훅·에이전트도 같은 보호를 받는다. 그 전까지 install 이\n // 백업한 건 settings.json 하나뿐이라, 기존 설치 위 `install` 이 사용자가 고친 룰을\n // 흔적 없이 밀었다 (add 모드는 `.claude/` 통짜 백업도 없다 — resolveBackupPath).\n const backup = backupEditedPolicyFile(entry.target, target, source, policyBase);\n if (backup) {\n result.backups.push(backup);\n }\n }\n copyFile(source, target);\n result.filesCopied += 1;\n } else {\n // #343 — 디렉터리 자산은 **파일 단위로** 판정한다. 슬롯이 우리 것이어도 그 **안의 파일**이\n // 링크일 수 있고, 통짜 복사는 그것을 그대로 따라가 남의 파일을 덮었다. 스킬 14종 중\n // 13종이 이 경로(dir 엔트리)라 슬롯 판정만으로는 대부분이 안 막혔다.\n for (const foreign of copyDir(source, target, (relFile) =>\n foreignOwnedTarget(projectDir, `${entry.target}/${relFile}`),\n )) {\n if (!result.foreignOwned.includes(foreign)) result.foreignOwned.push(foreign);\n }\n result.dirsCopied += 1;\n }\n accumulateCategory(result.categories, entry);\n }\n\n // chmod +x on hook scripts (cp does not preserve exec bit when source is non-exec)\n const hookDir = join(projectDir, \".claude/hooks\");\n if (existsSync(hookDir)) {\n chmodHooksSync(hookDir);\n }\n\n // Write metadata file used by detect_install_state on next run (.claude/.installed-tracks)\n writeInstalledTracks(projectDir, manifestSpec.tracks);\n\n // Project root CLAUDE.md — 없으면 fill-in 스캐폴드로 만들고, 있으면 앵커 import 한 줄만 얹는다.\n const rootClaudeMd = writeRootClaudeMd(\n projectDir,\n manifestSpec.tracks,\n manifestSpec.selectedInternalSkills ?? [],\n );\n result.rootClaudeMd = { tracks: manifestSpec.tracks, created: rootClaudeMd.created };\n // 무결성 기록의 대상은 **하네스 앵커 파일**이다 (루트 CLAUDE.md 가 아니다) — uninstall 이\n // 회수하는 것도, update 가 갱신하는 것도 그 파일뿐이라 소유를 주장할 수 있는 것도 그것뿐이다.\n // 방금 manifest copy 가 놓아둔 디스크 내용을 읽는다: 렌더를 다시 하면 기준선이 두 벌이 된다.\n result.rootClaudeMdLog = harnessAnchorLog(projectDir);\n return result;\n}\n\n/** 앵커 파일의 설치 시점 sha. manifest 에서 빠졌거나 source 부재로 skip 됐으면 null (정직 기록). */\nfunction harnessAnchorLog(projectDir: string): { path: string; sha256: string } | null {\n const anchor = join(projectDir, HARNESS_ANCHOR_FILE);\n if (!existsSync(anchor)) return null;\n return { path: HARNESS_ANCHOR_FILE, sha256: hashContent(readFileSync(anchor, \"utf-8\")) };\n}\n\n/** Environment files (F7/F8 — bash setup-harness.sh L880~890 + L954~996 등가). */\nfunction writeEnvironmentFiles(\n projectDir: string,\n tracks: ReadonlyArray<Track>,\n): BaselineReport[\"envFiles\"] {\n return {\n envExampleCreated: writeEnvExample(projectDir, tracks),\n gitignoreEnvAdded: addGitignoreEnv(projectDir),\n // v0.8.0 — `.factory/`, `.goose/` ignore (npx skills universal install 사용자 #3).\n // 2026-08-02 — `.uzys-agent-harness/` 합류 (설치 로그 + 훅 차단 로그).\n gitignoreNpxSkillsAdded: addGitignoreAgentArtifacts(projectDir),\n };\n}\n\n/**\n * External assets (claude plugin / npm -g / npx skills) 설치 단계.\n * Default = real runExternalInstall. Tests inject mock or `null` to skip.\n * log/warn은 silent (renderer가 onAssetStart/Result로 스트리밍).\n */\nfunction runExternalPhase(ctx: InstallContext): ExternalInstallReport | null {\n if (ctx.runExternal === null) {\n return null;\n }\n const { harnessRoot, projectDir, spec } = ctx;\n const runExt = ctx.runExternal ?? runExternalInstall;\n const externalDeps: ExternalInstallerDeps = {\n harnessRoot,\n log: () => {},\n warn: () => {},\n };\n if (ctx.externalDeps?.onAssetStart) {\n externalDeps.onAssetStart = ctx.externalDeps.onAssetStart;\n }\n if (ctx.externalDeps?.onAssetResult) {\n externalDeps.onAssetResult = ctx.externalDeps.onAssetResult;\n }\n const filterCtx = {\n tracks: spec.tracks,\n options: spec.options,\n ...(spec.userOverride ? { userOverride: spec.userOverride } : {}),\n };\n // v26.102.0 (ADR-031) — 헤더 카운트 = 실제 시도될 자산 수. runExternalInstall 과 **같은\n // selector** 를 호출해 \"External assets (N)\" 의 N 이 시도 목록과 구조적으로 일치\n // (이전엔 internal 8종을 포함해 dev 트랙 전부에서 헤더가 과대였다 — SOD 리뷰 F1 실측).\n const applicableCount = selectExternalTargets(EXTERNAL_ASSETS, {\n ...filterCtx,\n cli: spec.cli,\n }).targets.length;\n ctx.onProgress?.({ type: \"external-start\", assetCount: applicableCount });\n const external = runExt(\n { ...filterCtx, cli: spec.cli, projectDir, ...(spec.scope ? { scope: spec.scope } : {}) },\n externalDeps,\n );\n ctx.onProgress?.({ type: \"external-complete\", report: external });\n return external;\n}\n\n/**\n * Install log write — `.uzys-agent-harness/.harness-install.json` (자산 list + scope + timestamp,\n * uninstall command 의 source). 실패는 install 자체를 fail 시키지 않음 (D16 — install 성공 우선).\n */\nfunction writeInstallLogSafe(\n ctx: InstallContext,\n externalFiles: ReadonlyArray<InstallLogSkillFile>,\n external: ExternalInstallReport | null,\n rootClaudeMdLog: { path: string; sha256: string } | null,\n previousLog: InstallLog | null,\n claudeDirMovedAside: boolean,\n rootFiles: ReadonlyArray<InstallLogRootFile>,\n): void {\n try {\n const log = buildInstallLog(\n ctx.spec,\n external,\n resolveScope(ctx.spec.scope),\n rootClaudeMdLog,\n previousLog,\n claudeDirMovedAside,\n rootFiles,\n );\n // v26.126.0 (ADR-046) — 스킬 기준선은 **이력이 아니라 스냅샷**이라 buildInstallLog 의 누적\n // 경로를 타지 않는다. manifest copy 가 끝난 뒤 디스크를 읽어야 값이 맞다.\n const skillFiles = collectSkillHashes(ctx.projectDir, join(ctx.harnessRoot, \"templates\"));\n // v26.132.0 (ADR-047) — 정책 파일 기준선도 같은 이유로 여기서 찍는다. 이게 없으면\n // 다음 update 가 소유를 판정하지 못해 ⓐ 멀쩡한 파일을 전부 백업하고 ⓑ 폐기 룰을 회수 못 한다.\n const policyFiles = collectPolicyHashes(ctx.projectDir, join(ctx.harnessRoot, \"templates\"));\n // v26.133.0 (ADR-048) — 외부 CLI 기준선은 transform 이 **쓰면서 만든 값**이라 여기서 다시\n // 훑지 않는다. 이번에 안 건드린 산출물의 기록은 유지하고(다음 실행이 판정 불가로 떨어지지\n // 않게), 디스크에서 사라진 항목만 뺀다.\n const merged = mergeExternalFiles(ctx.projectDir, previousLog?.externalFiles, externalFiles);\n writeInstallLog(ctx.projectDir, {\n ...log,\n ...(skillFiles.length > 0 ? { skillFiles } : {}),\n ...(policyFiles.length > 0 ? { policyFiles } : {}),\n ...(merged.length > 0 ? { externalFiles: merged } : {}),\n });\n } catch (e) {\n ctx.onProgress?.({\n type: \"install-log-error\",\n message: e instanceof Error ? e.message : String(e),\n });\n }\n}\n\nfunction composeAndWriteMcp(\n harnessRoot: string,\n projectDir: string,\n spec: InstallSpec,\n): { mcpServers: Record<string, unknown>; created: boolean } {\n const mcpPath = join(projectDir, \".mcp.json\");\n // 쓰기 전에 본다 — 쓰고 나면 \"우리가 만든 것\"과 \"사용자 것에 병합한 것\"을 구분할 수 없다.\n const created = !existsSync(mcpPath);\n const composed = composeMcpJson({\n templateMcpPath: join(harnessRoot, \"templates/mcp.json\"),\n trackMapPath: join(harnessRoot, \"templates/track-mcp-map.tsv\"),\n existingPath: mcpPath,\n tracks: spec.tracks,\n });\n writeMcpJson(mcpPath, composed);\n return { ...composed, created };\n}\n\n/**\n * v26.124.0 (F-1f) — 이번 설치가 `.claude/` **밖**에 만들거나 고친 루트 파일 목록.\n *\n * uninstall 은 이걸 지우지 않고 **안내만** 한다 (사용자 내용이 섞임). 그러려면 무엇을 건드렸는지\n * 기록이 있어야 하는데 v26.123.0 까지 아무 기록이 없어서 안내조차 못 했다.\n *\n * **이번 설치가 실제로 바꾼 것만 넣는다** — idempotent skip(이미 있어서 안 건드림)은 넣지 않는다.\n * 이전 설치분은 install-log 의 누적(mergeRootFiles)이 살려 준다.\n */\nfunction collectRootFiles(\n envFiles: BaselineReport[\"envFiles\"],\n ciScaffold: CiScaffoldReport | null,\n mcpCreated: boolean,\n): InstallLogRootFile[] {\n const files: InstallLogRootFile[] = [\n {\n path: \".mcp.json\",\n change: mcpCreated ? \"created\" : \"modified\",\n notes: [mcpCreated ? \"MCP 서버 정의 생성\" : \"MCP 서버 정의 병합 (기존 항목 보존)\"],\n },\n ];\n if (envFiles.envExampleCreated) {\n files.push({ path: \".env.example\", change: \"created\", notes: [\"Supabase 토큰 가이드\"] });\n }\n const gitignoreAdded = [\n ...(envFiles.gitignoreEnvAdded ? [\".env\"] : []),\n ...envFiles.gitignoreNpxSkillsAdded,\n ];\n if (gitignoreAdded.length > 0) {\n files.push({\n path: \".gitignore\",\n change: \"modified\",\n notes: [`추가된 줄: ${gitignoreAdded.join(\", \")}`],\n });\n }\n for (const workflow of ciScaffold?.written ?? []) {\n files.push({ path: workflow, change: \"created\", notes: [\"CI 워크플로 스캐폴드\"] });\n }\n return files;\n}\n\n/**\n * v0.6.1 — manifest entry를 카테고리별로 누적. install renderer Phase 1 row 출력에 사용.\n * `entry.target` prefix로 분류. file은 basename(.확장자 제거), dir은 dir name.\n */\nfunction accumulateCategory(\n cats: BaselineCategoryCounts,\n entry: import(\"./manifest.js\").AssetEntry,\n): void {\n const target = entry.target;\n if (target.startsWith(\".claude/rules/\") && target.endsWith(\".md\")) {\n const name = target.replace(/^\\.claude\\/rules\\//, \"\").replace(/\\.md$/, \"\");\n cats.rules.push(name);\n } else if (target.startsWith(\".claude/agents/\") && target.endsWith(\".md\")) {\n const name = target.replace(/^\\.claude\\/agents\\//, \"\").replace(/\\.md$/, \"\");\n cats.agents.push(name);\n } else if (target.startsWith(\".claude/hooks/\") && target.endsWith(\".sh\")) {\n const name = target.replace(/^\\.claude\\/hooks\\//, \"\").replace(/\\.sh$/, \"\");\n cats.hooks.push(name);\n } else if (target.startsWith(\".claude/commands/\")) {\n cats.commands += 1;\n } else if (target.startsWith(\".claude/skills/\") && entry.type === \"dir\") {\n const name = target.replace(/^\\.claude\\/skills\\//, \"\").replace(/\\/?$/, \"\");\n cats.skills.push(name);\n }\n}\n\nfunction writeInstalledTracks(projectDir: string, tracks: ReadonlyArray<string>): void {\n const path = join(projectDir, \".claude/.installed-tracks\");\n mkdirSync(dirname(path), { recursive: true });\n const sorted = [...new Set(tracks)].sort().join(\"\\n\");\n writeFileSync(path, `${sorted}\\n`);\n}\n\n/**\n * 루트 `CLAUDE.md` — **덮어쓰지 않는다** (P5 · ADR-060). 하네스 내용은 앵커 파일\n * (`HARNESS_ANCHOR_FILE`)로 따로 나가고, 여기엔 그것을 끌어오는 마커 import 한 줄만 얹는다.\n *\n * 그래서 백업도 사라졌다 — 백업은 \"덮어쓰기 전 원본 보존\"의 대응물인데 이제 덮어쓰기가 없다.\n * 사용자 본문은 그대로 남고 우리 블록만 추가되며, uninstall 이 그 블록만 도로 걷어간다.\n * (`.mcp.json`·`.gitignore` 처럼 사용자 파일에 병합하는 다른 산출물과 같은 방침이다.)\n */\nfunction writeRootClaudeMd(\n projectDir: string,\n tracks: ReadonlyArray<Track>,\n continuousSkills: ReadonlyArray<string>,\n): { created: boolean } {\n const target = join(projectDir, \"CLAUDE.md\");\n const existing = existsSync(target) ? readFileSync(target, \"utf-8\") : null;\n const content = upsertHarnessImport(existing, {\n projectName: basename(projectDir),\n tracks,\n continuousSkills,\n });\n // 이미 import 가 있으면 upsert 가 입력을 그대로 돌려준다 — 그때는 파일을 만지지 않는다.\n if (content !== existing) {\n writeFileSync(target, content);\n }\n return { created: existing === null };\n}\n\nfunction chmodHooksSync(hookDir: string): void {\n for (const file of listHookFiles(hookDir)) {\n try {\n chmodSync(file, 0o755);\n } catch {\n // Best-effort; many platforms (Windows in particular) ignore mode bits.\n }\n }\n}\n\nfunction listHookFiles(hookDir: string): string[] {\n // Hooks are flat shell scripts — avoid pulling glob deps.\n return readdirSync(hookDir, { withFileTypes: true })\n .filter((e) => e.isFile() && e.name.endsWith(\".sh\"))\n .map((e) => resolve(hookDir, e.name));\n}\n","/**\n * CI scaffold (v26.108.0, 라이프사이클 자산화 ② — ADR-037) — `.github/workflows/` fill-in\n * 워크플로 템플릿 설치. 실무 CI 패턴(실DB 서비스 컨테이너 · tag-only 트리거 +\n * 로컬 검증 1차 게이트 · Playwright E2E · coverage 게이트)의 도메인 중립 일반화\n * (docs/plans/lifecycle-codification-2026-07-18.md ② — CI 0 인 실프로젝트가 갭의 존재 증명).\n *\n * 본 하네스가 `.claude/` 밖에 쓰는 첫 자산 — 안전 계약 2가지:\n * 1. opt-in 전용 (`--with ci-scaffold` / wizard 체크) — 무인지 설치 없음.\n * 2. **기존 파일 절대 덮어쓰지 않음** — 이미 존재하는 워크플로 파일은 skip 하고\n * `skippedExisting` 으로 정직 보고 (manifest copyFile 의 무조건 overwrite 와 다른 이유).\n * uninstall 도 `.github/` 를 건드리지 않는다 (install-log 미기록 — 설치 후 사용자 소유물).\n */\nimport { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { copyFile } from \"./fs-ops.js\";\nimport { anyTrack, hasUiTrack } from \"./track-match.js\";\nimport type { Track } from \"./types.js\";\n\nexport interface CiScaffoldReport {\n /** 새로 쓴 워크플로 파일 (project-relative). */\n written: string[];\n /** 이미 존재해 보존한 파일 (project-relative) — no-clobber 정직 보고용. */\n skippedExisting: string[];\n}\n\ninterface WorkflowVariant {\n /** `templates/github-workflows/` 안의 소스 파일명. */\n source: string;\n /** `.github/workflows/` 안의 타깃 파일명. */\n target: string;\n applies: (tracks: ReadonlyArray<Track>) => boolean;\n}\n\nconst PYTHON_TRACKS = \"data|csr-fastapi|full\";\n\n/**\n * Track → 변형 매핑 (결정론 — 코드로 되는 건 코드로. 사용자가 받는 파일을 사전에 알 수 있어야 한다):\n * - node CI: node 계열 트랙, 또는 python 계열이 전혀 없을 때의 fallback — 명시 opt-in 이\n * 트랙 매핑 밖(예: PM 단독)이라고 무설치가 되면 silent no-op (no-false-ship 위반).\n * - python CI: python 계열 트랙. csr-fastapi·full 은 polyglot — node 와 양쪽 설치.\n * - e2e(Playwright): UI 트랙만 (`hasUiTrack` = csr-*|ssr-*|full).\n */\nconst VARIANTS: ReadonlyArray<WorkflowVariant> = [\n {\n source: \"ci-node.yml\",\n target: \"ci.yml\",\n applies: (tracks) =>\n anyTrack(tracks, \"csr-*|ssr-*|tooling|full\") || !anyTrack(tracks, PYTHON_TRACKS),\n },\n {\n source: \"ci-python.yml\",\n target: \"ci-python.yml\",\n applies: (tracks) => anyTrack(tracks, PYTHON_TRACKS),\n },\n {\n source: \"e2e.yml\",\n target: \"e2e.yml\",\n applies: (tracks) => hasUiTrack(tracks),\n },\n];\n\nexport function installCiScaffold(ctx: {\n harnessRoot: string;\n projectDir: string;\n tracks: ReadonlyArray<Track>;\n}): CiScaffoldReport {\n const report: CiScaffoldReport = { written: [], skippedExisting: [] };\n for (const v of VARIANTS) {\n if (!v.applies(ctx.tracks)) continue;\n const relTarget = `.github/workflows/${v.target}`;\n const target = join(ctx.projectDir, relTarget);\n if (existsSync(target)) {\n report.skippedExisting.push(relTarget);\n continue;\n }\n copyFile(join(ctx.harnessRoot, \"templates/github-workflows\", v.source), target);\n report.written.push(relTarget);\n }\n return report;\n}\n","/**\n * cli-transforms.ts — 외부 CLI(codex · opencode · antigravity) transform 실행 (v26.134.0 · ADR-049).\n *\n * `installer.ts` 안에 있던 것을 옮겼다. `update` 도 **같은 함수**를 써야 하기 때문이다 —\n * 소유자 판정이 세 자산 종류에 세 번 나눠 붙어 같은 버그가 세 번 난 게 ADR-046~048 의 역사이고\n * (`feedback_surface_symmetry`), 그 교훈은 \"공통 규칙은 자산별 코드가 아니라 한 도구에\" 였다.\n * install 과 update 가 각자 transform 을 부르면 기준선을 잇는 규칙이 두 벌이 되고, 그건 곧\n * 네 번째 재발이다. 겸사로 `update-mode.ts` → `installer.ts` 순환 import 도 생기지 않는다.\n */\n\nimport {\n type AntigravityTransformReport,\n runAntigravityTransform,\n} from \"./antigravity/transform.js\";\nimport { type CodexOptInReport, runCodexOptIn } from \"./codex/opt-in.js\";\nimport { type CodexTransformReport, runCodexTransform } from \"./codex/transform.js\";\nimport type { InstallLogSkillFile } from \"./install-log.js\";\nimport { type OpencodeTransformReport, runOpencodeTransform } from \"./opencode/transform.js\";\nimport type { OwnedWriteResult } from \"./owned-write.js\";\nimport { CLI_BASES, type CliBase } from \"./types.js\";\n\n/** Codex / OpenCode / Antigravity per-CLI transforms (+ scope=global opt-in) 결과. */\nexport interface CliTransformResults {\n codex: CodexTransformReport | null;\n codexOptIn: CodexOptInReport | null;\n opencode: OpencodeTransformReport | null;\n antigravity: AntigravityTransformReport | null;\n /** v26.133.0 (ADR-048) — 세 transform 이 쓴 산출물의 기준선 (install log `externalFiles`). */\n externalFiles: InstallLogSkillFile[];\n /** v26.133.0 (ADR-048) — 사용자 편집분이라 만든 백업 파일 경로. 설치 화면에 노출한다. */\n externalBackups: string[];\n /** v26.134.0 (ADR-049) — 실제로 디스크가 바뀐 파일 수. update 화면의 카운트. */\n externalUpdated: number;\n /** v26.134.0 (ADR-049) — 백업된 사용자 편집분의 project-relative 경로. */\n externalBackedUp: string[];\n /**\n * #343 — 다른 도구가 소유한 스킬 슬롯이라 쓰지 않은 자리 (`.agents/skills/<id>` 등).\n * `.claude/` baseline 의 같은 판정과 **한 줄로 합쳐** 화면에 낸다 — 사용자에게는 어느\n * 단계가 건너뛰었는지가 아니라 \"어느 자리를 옮겨야 하는지\"가 필요하다.\n */\n externalForeignOwned: string[];\n}\n\nexport interface CliTransformParams {\n harnessRoot: string;\n projectDir: string;\n /** 대상 CLI. install 은 `spec.cli`, update 는 전부(`CLI_BASES`) — 아래 refreshOnly 주석 참조. */\n cli: ReadonlyArray<CliBase>;\n selectedInternalSkills: ReadonlyArray<string>;\n /**\n * 2026-08-12 — 이 설치에 깔릴 룰 이름들(`resolveRules`). 룰이 `.claude/rules/` 하나로만\n * 나가던 탓에 비 Claude 단독 설치는 룰을 한 종도 못 받았다. `selectedInternalSkills` 와 같은\n * 형태다 — 무엇을 깔지는 installer 가 정하고, 어디에 놓을지는 각 transform 이 정한다.\n */\n rules: ReadonlyArray<string>;\n /** install log 의 `externalFiles`. 없으면 빈 배열 = 판정 불가 → 보수적 백업. */\n previousExternal: ReadonlyArray<InstallLogSkillFile>;\n /**\n * v26.134.0 (ADR-049) — 이미 있는 산출물만 갱신하고 없는 것은 만들지 않는다 (`update` 경로).\n *\n * **이 플래그가 \"어느 CLI 가 설치돼 있나\"라는 질문 자체를 없앤다.** 안 깐 CLI 는 대상 파일이\n * 하나도 없어 자연히 아무것도 안 쓰므로, update 는 `CLI_BASES` 전부를 그냥 넘기면 된다 —\n * CLI 목록이나 스킬 목록의 **열거 사본을 만들 필요가 없다** (`no-false-ship` §게이트는\n * 열거하지 말고 훑어라와 같은 취지: 사람이 기억해야 하는 목록은 다음 drift 의 서식지다).\n */\n refreshOnly?: boolean;\n /** codex global trust opt-in — `scope=global` + `withCodexTrust` 일 때만. update 는 안 쓴다. */\n codexTrust?: boolean;\n}\n\n/**\n * update 가 넘기는 CLI 목록 — 전부. 판정은 `refreshOnly` 가 디스크로 대신한다.\n * `CLI_BASES` 에서 파생하므로 CLI 가 늘어도 여기를 고칠 필요가 없다.\n */\nexport const ALL_CLI_TARGETS: ReadonlyArray<CliBase> = CLI_BASES;\n\nexport function runCliTransforms(params: CliTransformParams): CliTransformResults {\n const {\n harnessRoot,\n projectDir,\n cli,\n selectedInternalSkills,\n rules,\n previousExternal,\n refreshOnly = false,\n codexTrust = false,\n } = params;\n\n // v26.133.0 (ADR-048) — 기준선을 transform 사이로 **이어준다**. codex 와 opencode 는 같은\n // `AGENTS.md` 를, codex 와 antigravity 는 같은 `.agents/skills/<id>/SKILL.md` 를 쓴다.\n // 안 이어주면 뒤 단계가 앞 단계의 산출물을 '사용자 편집'으로 오판해 **실행할 때마다**\n // 백업이 생긴다 — 백업 노이즈는 진짜 백업을 눈에 안 띄게 만들어 보호 자체를 무력화한다.\n const baseline = new Map(previousExternal.map((f) => [f.path, f.sha256]));\n const externalFiles: InstallLogSkillFile[] = [];\n const externalBackups: string[] = [];\n const externalBackedUp: string[] = [];\n const externalForeignOwned: string[] = [];\n let externalUpdated = 0;\n const absorb = (report: { ownership: OwnedWriteResult }): void => {\n for (const f of report.ownership.files) {\n baseline.set(f.path, f.sha256);\n externalFiles.push(f);\n }\n externalBackups.push(...report.ownership.backupPaths);\n externalBackedUp.push(...report.ownership.backedUp);\n for (const f of report.ownership.foreignOwned) {\n // 세 transform 이 같은 `.agents/skills/` 를 쓰므로 중복이 실제로 난다.\n if (!externalForeignOwned.includes(f)) externalForeignOwned.push(f);\n }\n externalUpdated += report.ownership.updated;\n };\n\n let codex: CodexTransformReport | null = null;\n let codexOptIn: CodexOptInReport | null = null;\n if (cli.includes(\"codex\")) {\n // v26.87.0 — dev-method skills 는 selectedInternalSkills 로 게이팅.\n codex = runCodexTransform({\n harnessRoot,\n projectDir,\n selectedInternalSkills,\n rules,\n baseline,\n refreshOnly,\n });\n absorb(codex);\n // v26.64.0 (ADR-020) — Codex global trust opt-in 은 scope=global 일 때만 의미.\n // scope=project (default) 시 ~/.codex/ write skip (config.toml trust entry 만).\n if (codexTrust) {\n codexOptIn = runCodexOptIn({ projectDir });\n }\n }\n\n let opencode: OpencodeTransformReport | null = null;\n if (cli.includes(\"opencode\")) {\n opencode = runOpencodeTransform({\n harnessRoot,\n projectDir,\n selectedInternalSkills,\n rules,\n baseline,\n refreshOnly,\n });\n absorb(opencode);\n }\n\n // v26.66.0 — Antigravity transform: `.agents/rules/uzys-harness.md` + dev-method skills.\n let antigravity: AntigravityTransformReport | null = null;\n if (cli.includes(\"antigravity\")) {\n antigravity = runAntigravityTransform({\n harnessRoot,\n projectDir,\n selectedInternalSkills,\n rules,\n baseline,\n refreshOnly,\n });\n absorb(antigravity);\n }\n\n return {\n codex,\n codexOptIn,\n opencode,\n antigravity,\n externalFiles,\n externalBackups,\n externalUpdated,\n externalBackedUp,\n externalForeignOwned,\n };\n}\n","/**\n * Antigravity transform — v26.66.0 (skills/workflows) + v26.69.0 (rules).\n *\n * Google Antigravity 2.0 (I/O 2026-05-19) 공식 spec (codelabs):\n * - Workspace skills: .agents/skills/<name>/SKILL.md (Anthropic skill format — codex 와 공유)\n * - Workspace workflows: .agents/workflows/<name>.md (`/<name>` 슬래시로 호출)\n * - Workspace rules: .agents/rules/<name>.md (디렉토리, plain markdown)\n * - Global rules: ~/.gemini/GEMINI.md (사용자 글로벌 — harness 미터치)\n * - Global skills: ~/.gemini/antigravity/skills/ (Phase C opt-in — antigravity/opt-in.ts)\n *\n * 본 transform 의 책임 (모두 project-scope):\n * 1. `.agents/rules/uzys-harness.md` — project context (CLAUDE.md → Antigravity rule).\n * foundational context (CLAUDE.md/AGENTS.md 처럼 항상 작성).\n * cli=antigravity 단독 선택 시 이게 없으면 Antigravity 가 프로젝트 컨벤션을 모름.\n * 2. `.agents/skills/<id>/**` — dev-method skills 디렉터리 전체 (frontmatter 보존, codex 와 공유).\n *\n * SAFETY: `~/.gemini/` 글로벌 write 없음.\n */\n\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { basename, join } from \"node:path\";\nimport { renderAgentsMd } from \"../codex/agents-md.js\";\nimport { writeBundledSkillDirs } from \"../codex/skills.js\";\nimport { createOwnedWriter, type OwnedWriteResult, type OwnedWriter } from \"../owned-write.js\";\nimport { renderFillScaffold, withContinuousSkillsNote } from \"../project-claude-merge.js\";\nimport { portRules } from \"../rules-port.js\";\n\nexport interface AntigravityTransformParams {\n /** harness root (templates/CLAUDE.md source 위치). */\n harnessRoot: string;\n /** 사용자 프로젝트 root. `.agents/` 가 만들어질 위치. */\n projectDir: string;\n /**\n * v26.87.0 — dev-method skill ids 선택 목록. 각 id 의 `templates/skills/<id>/` 를\n * Antigravity native `.agents/skills/<id>/` 로 (SKILL.md frontmatter 보존) 출력.\n */\n selectedInternalSkills?: ReadonlyArray<string>;\n /** 2026-08-12 — 이 설치의 배포 룰 이름들. `.agents/rules/<name>.md` 로 나간다. */\n rules?: ReadonlyArray<string>;\n /**\n * v26.133.0 (ADR-048) — 설치 시점 기준선 (install log `externalFiles`).\n * codex/opencode 와 같은 이유로 **required**. 레거시는 빈 Map 을 명시적으로 넘긴다.\n */\n baseline: ReadonlyMap<string, string>;\n /**\n * v26.134.0 (ADR-049) — `update` 경로. 이미 있는 산출물만 갱신하고 없는 것은 만들지 않는다.\n * antigravity 를 안 깐 프로젝트에서 돌려도 `.agents/rules/` 가 생기지 않는다.\n */\n refreshOnly?: boolean;\n}\n\nexport interface AntigravityTransformReport {\n /** v26.69.0 — 작성된 **앵커** 파일 경로 (.agents/rules/uzys-harness.md). null = template 부재. */\n rulesFile: string | null;\n /** 2026-08-12 — 작성된 배포 룰 경로 (.agents/rules/<name>.md). 앵커와 형제. */\n harnessRuleFiles: string[];\n /**\n * `.agents/skills/<id>/` 에 쓴 **모든** 파일 경로 — `SKILL.md` + 형제(references/scripts 등).\n * #431 이전에는 `SKILL.md` 하나뿐이었다.\n */\n skillFiles: ReadonlyArray<string>;\n /** v26.133.0 (ADR-048) — 소유권 결과 (기준선 · 백업된 사용자 편집분). */\n ownership: OwnedWriteResult;\n}\n\n/**\n * Antigravity 용 project-scope 자산 생성 (rules + dev-method skills).\n */\nexport function runAntigravityTransform(\n params: AntigravityTransformParams,\n): AntigravityTransformReport {\n const {\n harnessRoot,\n projectDir,\n selectedInternalSkills = [],\n rules = [],\n baseline,\n refreshOnly,\n } = params;\n const writer = createOwnedWriter(projectDir, baseline, { refreshOnly: refreshOnly ?? false });\n\n // 1. .agents/rules/uzys-harness.md — project context (CLAUDE.md → Antigravity rule, 항상).\n const rulesFile = writeRules(harnessRoot, projectDir, writer, selectedInternalSkills);\n\n // 1a. 2026-08-12 — 배포 룰을 같은 워크스페이스 룰 디렉터리에 형제 파일로 놓는다.\n // Antigravity 는 `.agents/rules/*.md` 를 네이티브로 읽으므로 변환이 필요 없다(파일당 12,000자\n // 상한 — 배포 룰은 전부 그 아래다). 위 `uzys-harness.md` 는 이름과 달리 **앵커**라서,\n // 그것만으로는 룰이 도달하지 않았다.\n //\n // `createInRefresh` 를 켜는 근거는 **바로 위 줄의 반환값**이다. 앵커를 담당했다는 것은\n // 이 프로젝트에 Antigravity 가 설치돼 있다는 뜻이고(refresh 모드에서 앵커가 없으면\n // `rulesFile === null`), 그때만 새 룰 파일을 만든다. 이 예외가 없으면 기존 설치자는\n // update 를 아무리 돌려도 룰을 못 받는다 — 없는 파일은 refresh 가 건너뛰기 때문이다.\n const antigravityInstalled = rulesFile !== null;\n const harnessRuleFiles: string[] = [];\n for (const rule of portRules(harnessRoot, rules)) {\n const target = join(projectDir, \".agents\", \"rules\", `${rule.name}.md`);\n if (!writer.write(target, `${rule.body}\\n`, { createInRefresh: antigravityInstalled })) {\n continue;\n }\n harnessRuleFiles.push(target);\n }\n\n // 1b. v26.87.0 — dev-method skills → .agents/skills/<id>/ (frontmatter 보존).\n // renderBundledSkill 이 source frontmatter(name: <id>)를 보존.\n // 2026-09-13 (#431) — `SKILL.md` 한 파일이 아니라 디렉터리 전체다. 루프는 세 transform\n // 공용 helper 가 소유한다(codex·opencode 와 같은 산출물).\n const skillFiles = writeBundledSkillDirs({\n harnessRoot,\n projectDir,\n skillIds: selectedInternalSkills,\n writer,\n });\n\n return {\n rulesFile,\n harnessRuleFiles,\n skillFiles,\n ownership: writer.result(),\n };\n}\n\n/**\n * v26.69.0 — `.agents/rules/uzys-harness.md` 작성. CLAUDE.md → Antigravity workspace rule.\n *\n * Source: templates/CLAUDE.md (전문) + templates/antigravity/AGENTS.md.template.\n * v26.70.0 — renderAgentsMd 재사용 (codex/opencode 와 동일 전문 embed). `{PROJECT_RULES}` 에\n * CLAUDE.md 본문 전체 삽입 + `/uzys:` → `/uzys-` rename.\n *\n * template 또는 CLAUDE.md 부재 시 null (graceful — install 진행).\n */\nfunction writeRules(\n harnessRoot: string,\n projectDir: string,\n writer: OwnedWriter,\n selectedInternalSkills: ReadonlyArray<string>,\n): string | null {\n const claudeMdPath = join(harnessRoot, \"templates/CLAUDE.md\");\n const templatePath = join(harnessRoot, \"templates/antigravity/AGENTS.md.template\");\n if (!existsSync(claudeMdPath) || !existsSync(templatePath)) {\n return null;\n }\n const claudeMd = readFileSync(claudeMdPath, \"utf8\");\n const template = readFileSync(templatePath, \"utf8\");\n const target = join(projectDir, \".agents\", \"rules\", \"uzys-harness.md\");\n const rulesOut = renderAgentsMd({\n template,\n claudeMd,\n projectName: basename(projectDir),\n // ADR-085 — 상시 스킬 안내는 앵커가 아니라 여기(프로젝트 맥락)에, 깔린 것만.\n projectContext: withContinuousSkillsNote(\n renderFillScaffold(\"antigravity-rule\"),\n selectedInternalSkills,\n ),\n });\n // 사용자가 채운 rules 파일을 재설치(add 모드) 덮어쓰기 전 보존 — 루트 CLAUDE.md 와 대칭.\n // v26.133.0 (ADR-048) — 내용 비교에서 소유자 판정으로 (릴리즈마다 백업 쌓임 방지).\n // refresh 모드가 건너뛰면 null — 안 만든 파일을 만들었다고 보고하지 않는다.\n return writer.write(target, rulesOut) ? target : null;\n}\n","/**\n * AGENTS.md transform — CLAUDE.md → AGENTS.md.\n *\n * v26.70.0 — section 추출(Identity/Direction/Principles) → CLAUDE.md **전문 embed**.\n * 실 `templates/CLAUDE.md` 에 Identity/Direction/Principles 헤딩이 없어\n * extractSection 이 빈 결과 → AGENTS.md 가 빈 섹션으로 shipping 되던 버그 fix.\n * `{PROJECT_RULES}` placeholder 에 CLAUDE.md 본문 전체를 삽입 (heading 구조 의존 0).\n */\n\n/** Rename Claude slash conventions (`/uzys:foo`) to Codex (`/uzys-foo`). */\nexport function renameSlashes(text: string): string {\n return text.replaceAll(\"/uzys:\", \"/uzys-\");\n}\n\nexport interface AgentsMdParams {\n template: string;\n claudeMd: string;\n projectName: string;\n /** Project-context fill scaffold — the same body shipped to the Claude Code CLAUDE.md. */\n projectContext: string;\n /**\n * 2026-08-12 — 배포 룰 본문(이어 붙인 한 덩어리). **Codex 전용**이다: Codex 는 룰 디렉터리가\n * 없고 `AGENTS.md` 계층만 읽으므로 룰이 본문에 들어가야 도달한다. OpenCode · Antigravity 는\n * 각자 룰 디렉터리를 쓰므로 이 값을 넘기지 않고, 그 템플릿에는 placeholder 도 없다.\n */\n harnessRules?: string;\n}\n\n/**\n * Render AGENTS.md by embedding the full CLAUDE.md body into the template.\n *\n * Placeholders:\n * - {PROJECT_NAME} — basename of project dir\n * - {PROJECT_RULES} — full CLAUDE.md body (first h1 stripped; template provides its own h1)\n * - {PROJECT_CONTEXT} — project-specific fill scaffold (renderFillScaffold())\n *\n * 마지막에 `/uzys:` → `/uzys-` rename (Codex/Antigravity 는 slash namespace 미지원).\n */\nexport function renderAgentsMd(params: AgentsMdParams): string {\n // CLAUDE.md 의 첫 h1 (# title) 제거 — 템플릿이 자체 h1 보유.\n const body = params.claudeMd.replace(/^#\\s+.*\\r?\\n/, \"\").trim();\n const replaced = params.template\n .replaceAll(\"{PROJECT_NAME}\", params.projectName)\n .replaceAll(\"{PROJECT_RULES}\", body)\n .replaceAll(\"{PROJECT_CONTEXT}\", params.projectContext)\n .replaceAll(\"{HARNESS_RULES}\", params.harnessRules ?? \"\");\n return renameSlashes(replaced);\n}\n","/**\n * Bundled SKILL.md → non-Claude CLI native skill transform (dev-method skills).\n */\nimport { chmodSync, existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { listFilesRecursive } from \"../fs-ops.js\";\nimport type { OwnedWriter } from \"../owned-write.js\";\nimport { renameSlashes } from \"./agents-md.js\";\n\n/**\n * v26.87.0 — render a bundled, already-complete SKILL.md (dev-method skills) for a\n * non-Claude CLI (Codex / Antigravity native `.agents/skills/<id>/SKILL.md`).\n *\n * These sources are full Anthropic skills with their OWN frontmatter (`name: <id>`,\n * full description). We MUST preserve that frontmatter verbatim — only the BODY is\n * ported: `/uzys:` → `/uzys-` slash rename + `CLAUDE_PROJECT_DIR` → `CODEX_PROJECT_DIR`\n * env-var rename (Codex/Antigravity share the `.agents/` format + `CODEX_PROJECT_DIR`).\n */\nexport function renderBundledSkill(source: string): string {\n const trimmed = source.trimEnd();\n const lines = trimmed.split(/\\r?\\n/);\n // No frontmatter → emit body as-is (port slashes/env only). Defensive: bundled\n // dev-method skills always have frontmatter, but never silently drop content.\n if (lines[0] !== \"---\") {\n return `${portBody(trimmed)}\\n`;\n }\n let secondDelimAt = -1;\n for (let i = 1; i < lines.length; i++) {\n if (lines[i] === \"---\") {\n secondDelimAt = i;\n break;\n }\n }\n if (secondDelimAt < 0) {\n // Malformed frontmatter (no closing ---) → port whole thing as body.\n return `${portBody(trimmed)}\\n`;\n }\n const frontmatter = lines.slice(0, secondDelimAt + 1).join(\"\\n\");\n const body = lines.slice(secondDelimAt + 1).join(\"\\n\");\n return `${frontmatter}\\n${portBody(body)}\\n`;\n}\n\n/** Port a skill body for Codex/Antigravity: slash + project-dir env-var rename. */\nfunction portBody(body: string): string {\n return renameSlashes(body)\n .replace(/CLAUDE_PROJECT_DIR/g, \"CODEX_PROJECT_DIR\")\n .trimEnd();\n}\n\nexport interface BundledSkillDirParams {\n /** harness root — 원본은 `templates/skills/<id>/`. */\n harnessRoot: string;\n /** 대상 프로젝트 root — 산출물은 `.agents/skills/<id>/`. */\n projectDir: string;\n /** 이 설치에서 고른 번들 스킬 id (installer 가 채운 `selectedInternalSkills`). */\n skillIds: ReadonlyArray<string>;\n /** 소유자 판정을 쥔 writer — 쓴 파일이 전부 `ownership.files` 에 실린다. */\n writer: OwnedWriter;\n}\n\n/**\n * 2026-09-13 (#431) — 번들 스킬을 **디렉터리 전체**로 `.agents/skills/<id>/` 에 쓴다.\n *\n * 이전에는 세 transform(codex·opencode·antigravity)이 `SKILL.md` **한 파일**만 보내는 같은\n * 루프를 각자 갖고 있었다. 번들 스킬 다수가 본문을 `references/`·`scripts/`·`agents/` 로\n * 나눠 들고 `SKILL.md` 가 \"그 파일을 읽어라\"로 라우팅하는데, 비-Claude CLI 설치자에게는 그\n * 자리가 **비어 있었다** — 안내판만 가고 본문이 안 갔다. Claude Code 쪽(`manifest.ts`)은\n * 처음부터 디렉터리 단위 복사라 이 비대칭은 한쪽 계열에만 있었다.\n *\n * 루프를 여기 하나로 모은 이유: 같은 루프가 셋이면 다음 수정이 또 하나를 빠뜨린다(#340·\n * `feedback_surface_symmetry` 와 같은 형태).\n *\n * 파일 종류별 처리 —\n * - `SKILL.md`: `renderBundledSkill` 그대로. 이전 판본과 **바이트 동일**해야 한다.\n * - 형제 `.md`·`.sh`: `SKILL.md` 본문과 같은 포팅(`/uzys:` → `/uzys-` ·\n * `CLAUDE_PROJECT_DIR` → `CODEX_PROJECT_DIR`). 참조 문서가 Claude 전용 슬래시를 들고\n * 가면 그 CLI 에서 존재하지 않는 커맨드를 불러 보게 된다.\n * - 그 밖(`.py`·`.yaml`·`.json` 등): 그대로. 코드·데이터를 문자열 치환하면 깨진다.\n * - 이름이 `.` 로 시작하는 파일·디렉터리: 건너뛴다(`.DS_Store` 같은 OS 부산물).\n *\n * @returns 이번에 쓴 **모든** 파일의 절대 경로 (`SKILL.md` + 형제). 호출부가 그대로\n * `skillFiles` 로 보고한다 — 안 쓴 경로는 들어가지 않는다(건너뛴 것을 싣는 순간 거짓 보고).\n */\nexport function writeBundledSkillDirs(params: BundledSkillDirParams): string[] {\n const { harnessRoot, projectDir, skillIds, writer } = params;\n const written: string[] = [];\n for (const id of skillIds) {\n const srcDir = join(harnessRoot, \"templates/skills\", id);\n const srcSkill = join(srcDir, \"SKILL.md\");\n if (!existsSync(srcSkill)) {\n continue;\n }\n const targetDir = join(projectDir, \".agents\", \"skills\", id);\n const targetSkill = join(targetDir, \"SKILL.md\");\n // 건너뛴 경로를 report 에 실으면 \"깔았다\"는 거짓 보고가 된다. 형제도 함께 건너뛴다 —\n // refresh 모드에서 `SKILL.md` 가 없다는 것은 이 스킬이 이 프로젝트에 없다는 뜻이고,\n // 남의 도구가 소유한 슬롯이면 그 트리에 우리 파일을 흘리지 않아야 한다(#343).\n if (!writer.write(targetSkill, renderBundledSkill(readFileSync(srcSkill, \"utf8\")))) {\n continue;\n }\n written.push(targetSkill);\n for (const rel of listFilesRecursive(srcDir)) {\n if (rel === \"SKILL.md\") continue;\n if (rel.split(\"/\").some((segment) => segment.startsWith(\".\"))) continue;\n const segments = rel.split(\"/\");\n const raw = readFileSync(join(srcDir, ...segments), \"utf8\");\n const ported = rel.endsWith(\".md\") || rel.endsWith(\".sh\") ? `${portBody(raw)}\\n` : raw;\n const target = join(targetDir, ...segments);\n // `createInRefresh`: 바로 위에서 `SKILL.md` 를 담당했다는 것이 **이 CLI 가 설치돼 있고\n // 이 스킬이 선택됐다**는 증거다(antigravity 룰이 앵커 반환값을 쓰는 것과 같은 논거).\n // 그 증거 없이 켜면 안 깐 CLI 에 `.agents/` 가 생겨 refreshOnly 가 무의미해진다.\n if (!writer.write(target, ported, { createInRefresh: true })) continue;\n // refresh 가 건너뛴 경로에는 파일이 없다 — chmod 를 무조건 걸면 ENOENT 로 터진다.\n if (rel.endsWith(\".sh\")) chmodSync(target, 0o755);\n written.push(target);\n }\n }\n return written;\n}\n","/**\n * owned-write.ts — 소유자 판정을 붙인 파일 쓰기 (v26.133.0 · ADR-048).\n *\n * ADR-046(스킬) → ADR-047(`.claude/` 정책 파일)이 정한 규칙을 **하네스가 렌더해서 내보내는\n * 모든 산출물**에 쓰기 위한 공통 도구. 그 두 ADR 의 구현이 각각 자기 자산 종류에만 걸려서\n * 같은 버그가 두 번 났다 — 세 번째를 막으려면 규칙이 자산별 코드가 아니라 **한 도구**에 있어야\n * 한다 (`feedback_surface_symmetry` \"결정의 범위 ≠ 구현의 범위\").\n *\n * `.claude/` 쪽과 다른 점 하나: 저기는 templates 파일을 **복사**하므로 기준선을 나중에\n * 디스크에서 다시 훑지만(`collectPolicyHashes`), 외부 CLI 산출물은 템플릿을 **렌더**한\n * 결과라 디스크를 훑어도 \"무엇이 하네스 것인지\" 알 수 없다. 대신 쓰는 쪽이 방금 쓴 내용을\n * 알고 있으므로 **그 자리에서 기준선을 만든다** — 경로 목록을 따로 하드코딩할 필요가 없고,\n * 그래서 `POLICY_DIRS` 같은 열거 사본이 여기엔 생기지 않는다.\n */\n\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname, relative, sep } from \"node:path\";\nimport { foreignOwnedTarget } from \"./foreign-slot.js\";\nimport { backupFile } from \"./fs-ops.js\";\nimport { hashContent, type InstallLogSkillFile, isHarnessOwned } from \"./install-log.js\";\n\n/**\n * 한 transform 실행의 소유권 결과. **세 transform 이 같은 타입을 반환한다** — 필드가 늘 때\n * 세 곳을 따로 고쳐야 하는 구조를 만들지 않기 위해서다 (이 파일이 존재하는 이유와 같다).\n */\nexport interface OwnedWriteResult {\n /** 이번 실행이 쓴 산출물의 기준선 (projectDir 상대경로 → sha256). install log 에 실린다. */\n files: InstallLogSkillFile[];\n /** 사용자 편집분이라 백업한 **원본**의 project-relative 경로 — 사용자가 알아보는 이름. */\n backedUp: string[];\n /**\n * #343 — 다른 도구가 소유한 스킬 슬롯이라 **쓰지 않은** 자리 (project-relative).\n *\n * `.agents/skills/<id>` 는 codex·antigravity 산출물의 자리이자 `npx skills add --agent` 의\n * 설치처다. 그동안 이 writer 는 그 자리가 링크여도 따라 써서 사용자의 다른 저장소를 덮고,\n * 백업까지 **그 저장소 안에** 떨궜다 — 사용자가 찾을 자리가 아니다 (ADR-062 와 같은 논거).\n */\n foreignOwned: string[];\n /** 생성된 백업 파일의 절대경로 — 설치 화면의 `backup` 행이 이걸 쓴다. */\n backupPaths: string[];\n /**\n * 실제로 디스크가 바뀐 파일 수 (신규 + 내용이 달라 덮어쓴 것). 이미 최신이라 아무것도 안 한\n * 파일은 세지 않는다 — update 화면이 \"N files updated\" 로 쓰므로, 안 한 일을 셌다가는\n * 매번 같은 숫자가 떠서 갱신이 일어난 것처럼 보인다.\n */\n updated: number;\n}\n\nexport interface OwnedWriter {\n /**\n * 소유자 판정을 거쳐 쓴다.\n *\n * | 디스크 vs 기준선 | 뜻 | 처리 |\n * |---|---|---|\n * | 파일 없음 | 신규 | 그냥 쓴다 (refresh 모드에서는 **건너뛴다**) |\n * | 내용 동일 | 이미 최신 | 아무것도 안 한다 (백업도 쓰기도) |\n * | 기준선과 같다 | 사용자가 안 고쳤다 | 조용히 덮어쓴다 |\n * | 기준선과 다르다 | 사용자가 고쳤다 | `.backup-<stamp>` 남기고 최신판을 자리에 |\n * | 기록 없음 | 판정 불가 | 보수적으로 백업 |\n *\n * @returns 이 경로를 이번에 담당했는가. `false` 는 **안 썼다**는 뜻이고 경우는 둘이다 —\n * refresh 모드에서 없는 파일을 건너뛴 경우, 그리고 #343 이후 **남의 도구가 소유한 슬롯**인 경우.\n * 호출자가 쓰기 성공을 전제로 하는 후속 동작(예: `chmod`)을 그때 건너뛰게 하려는 것이고,\n * 반환을 무시해도 되는 호출부는 무시해도 안전하다. `void` 로 두면 건너뛴 경로에\n * `chmodSync` 가 걸려 ENOENT 로 터진다.\n */\n write(absPath: string, content: string, opts?: WriteOptions): boolean;\n /** 이번 실행의 소유권 결과 — transform 이 그대로 report 에 실어 반환한다. */\n result(): OwnedWriteResult;\n}\n\n/**\n * @param now 백업 파일명 stamp — 테스트가 고정값을 주입한다.\n * @param refreshOnly 이미 있는 파일만 갱신하고 **없는 파일은 만들지 않는다** (v26.134.0 · ADR-049).\n *\n * `update` 가 쓰는 모드다. update 는 사용자가 고르지 않은 것을 새로 깔면 안 된다 —\n * `.claude/` 쪽 `updateDir` 이 \"target 에 이미 있는 파일만\"으로 오래 지켜 온 규율(Track 혼입\n * 방지)과 같은 것이고, 외부 CLI 에 그대로 적용하면 **\"어느 CLI 가 설치돼 있나\"를 판정할 필요\n * 자체가 없어진다** — 안 깔린 CLI 는 대상 파일이 하나도 없어 자연히 아무것도 안 쓴다.\n * 판정을 안 하니 CLI 목록·스킬 목록의 열거 사본도 생기지 않는다.\n */\n/** 개별 write 의 refresh 정책 예외. */\nexport interface WriteOptions {\n /**\n * refresh 모드에서도 **없으면 만든다**.\n *\n * `refreshOnly` 의 규율은 \"사용자가 안 고른 CLI 의 자산을 새로 깔지 않는다\"이고, 그 판정을\n * 파일 존재로 대신해 왔다. 그런데 **이미 설치된 CLI 에 하네스가 새 자산을 추가한 경우**엔\n * 그 판정이 반대로 작동한다 — 파일이 없는 이유가 \"안 골라서\"가 아니라 \"그때는 없던\n * 자산이라서\"인데, update 가 영원히 건너뛴다(독립 검증 C-2 실측: 기존 설치자는 새 룰을\n * 재설치 전에는 못 받는다).\n *\n * 그래서 호출부가 **그 CLI 가 설치돼 있다는 별도 증거**를 잡았을 때만 이 예외를 쓴다.\n * 증거 없이 쓰면 안 깐 CLI 에 디렉터리가 생겨 `refreshOnly` 자체가 무의미해진다.\n */\n createInRefresh?: boolean;\n}\n\nexport interface OwnedWriterOptions {\n now?: Date;\n refreshOnly?: boolean;\n}\n\n/**\n * @param projectDir 기준 디렉터리 — 기록되는 경로는 전부 여기 기준 상대경로다.\n * 절대경로를 기록하면 프로젝트를 옮긴 순간 기준선이 통째로 무효가 된다.\n * @param baseline 설치 시점 기준선 (install log `externalFiles`). 빈 Map = 판정 불가.\n *\n * writer 하나는 **transform 하나**를 담당한다. codex 와 opencode 가 같은 `AGENTS.md` 를 쓰는\n * 것 같은 교차 누적은 호출자(installer)가 다음 writer 를 만들기 전에 기준선을 갱신해서\n * 처리한다 — 누적을 여기와 거기 양쪽에 두면 한쪽이 죽은 코드가 되고, 죽은 줄은 테스트가\n * 잡지 못한다 (이 파일의 첫 판이 실제로 그랬고 음성 대조에서 드러났다).\n */\nexport function createOwnedWriter(\n projectDir: string,\n baseline: ReadonlyMap<string, string>,\n options: OwnedWriterOptions = {},\n): OwnedWriter {\n const { now = new Date(), refreshOnly = false } = options;\n const written = new Map<string, string>();\n const backedUp: string[] = [];\n const backupPaths: string[] = [];\n const foreignOwned: string[] = [];\n let updated = 0;\n\n return {\n write(absPath, content, opts) {\n const rel = relative(projectDir, absPath).split(sep).join(\"/\");\n // #343 — 남의 도구가 소유한 스킬 슬롯이면 쓰지 않는다 (판정 SSOT = foreign-slot.ts).\n // `false` 반환은 기존 계약 그대로다 — 호출부는 이미 \"안 썼으면 report 에 안 싣는다\".\n const foreign = foreignOwnedTarget(projectDir, rel);\n if (foreign !== null) {\n if (!foreignOwned.includes(foreign)) foreignOwned.push(foreign);\n return false;\n }\n const digest = hashContent(content);\n\n if (!existsSync(absPath)) {\n // refresh 모드: 없는 파일은 사용자가 안 고른 것 → 새로 깔지 않는다.\n // 기준선에도 넣지 않는다 — 디스크에 없는 파일의 해시를 기록하면 그게 곧 거짓 기록이다.\n // 예외 = `createInRefresh` (호출부가 그 CLI 의 설치 증거를 이미 잡은 경우).\n if (refreshOnly && !opts?.createInRefresh) return false;\n mkdirSync(dirname(absPath), { recursive: true });\n writeFileSync(absPath, content);\n updated++;\n } else {\n const current = readFileSync(absPath, \"utf8\");\n if (current !== content) {\n if (!isHarnessOwned(baseline, rel, current)) {\n backupPaths.push(backupFile(absPath, now));\n backedUp.push(rel);\n }\n writeFileSync(absPath, content);\n updated++;\n }\n }\n\n written.set(rel, digest);\n return true;\n },\n result() {\n return {\n foreignOwned: [...foreignOwned],\n files: [...written].map(([path, sha256]) => ({ path, sha256 })),\n backedUp: [...backedUp],\n backupPaths: [...backupPaths],\n updated,\n };\n },\n };\n}\n","import { lstatSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\n/**\n * foreign-slot.ts — \"그 자리는 우리 것이 아니다\" 판정 (#343 · ADR-062 적용 표면 확장).\n *\n * 규칙을 **한 도구**에 둔다. `owned-write.ts` 서문이 같은 이유를 이미 적었다 — 소유자 판정이\n * 자산 종류별 코드에 흩어지면 같은 버그가 종류마다 다시 난다. 실제로 그렇게 났다:\n * `update` 는 ADR-062 로 링크를 건너뛰었는데 `install` 은 같은 자리에서 죽었고(#343),\n * `.agents/skills/` 는 두 명령 어느 쪽에서도 판정을 안 받아 조용히 덮고 있었다.\n *\n * **적용 범위는 스킬 슬롯뿐이다.** `.claude/settings.json` · `.claude/rules/*.md` 처럼 슬롯 밖\n * 파일을 사용자가 공유 dotfiles 로 링크해 두는 것은 지원 케이스이고(`fs-ops.ts` copyBackupDir\n * 주석), 거기까지 넓히면 정상 설치가 통째로 건너뛰어진다.\n */\n\n/**\n * 다른 도구(`npx skills add`)가 소유할 수 있는 스킬 슬롯. **두 자리를 함께 본다** —\n * `.claude/skills/<id>` (Claude Code) 와 `.agents/skills/<id>` (codex·antigravity 공용,\n * `external-installer.ts` 의 SKILLS_CLI_AGENT_MAP 이 그 자리로 보낸다).\n * 슬롯 자체(`…/<id>`)와 그 안의 파일(`…/<id>/SKILL.md`) 둘 다 매치한다.\n */\nconst SKILL_SLOT = /^((?:\\.claude|\\.agents)\\/skills\\/[^/]+)(?:\\/|$)/;\n\n/**\n * 그 자리가 **디렉터리가 아닌 것**으로 이미 차 있는가 (비어 있으면 false).\n *\n * `existsSync` 로는 판정할 수 없다 — 링크를 따라가므로 \"디렉터리가 있다\"와 \"디렉터리를 가리키는\n * 링크가 있다\"에 같은 답을 내고, 깨진 링크는 아예 없는 것으로 보인다. `lstatSync` 는 링크를\n * 따라가지 않아 셋을 구분한다. 심링크뿐 아니라 파일·FIFO 등 **디렉터리가 아닌 모든 것**을 잡는다:\n * 우리가 디렉터리를 만들 자리를 차지한 것은 무엇이든 우리 것이 아니다.\n */\nexport function occupiedByNonDirectory(path: string): boolean {\n // throwIfNoEntry:false → 없으면 undefined. EACCES 같은 진짜 오류는 계속 던지게 둔다.\n const stat = lstatSync(path, { throwIfNoEntry: false });\n return stat !== undefined && !stat.isDirectory();\n}\n\n/**\n * 이 대상에 쓰면 **남의 것을 건드리는가**. 건드리면 사용자에게 보여 줄 경로를, 아니면 null.\n *\n * 두 가지를 본다:\n * ⓐ 슬롯 자체가 디렉터리가 아니다 → 그 자리 전체가 남의 것 (`npx skills add` 의 링크가 이 모양)\n * ⓑ 슬롯 안의 **중간 디렉터리**가 디렉터리가 아니다 → 그 중간 자리가 남의 것\n * ⓒ 최종 경로가 **일반 파일이 아니다**(심링크·FIFO·소켓·디렉터리) → 그 자리만 남의 것\n * ⓑⓒ 가 따로 필요한 이유: `copyFileSync`·`writeFileSync` 는 링크를 따라가므로 죽지 않고\n * 조용히 링크 대상을 덮는다 — 크래시가 없어 아무도 신고하지 않는 형태다. 심링크만 보지 않는\n * 이유는 나머지가 더 나쁘기 때문이다: FIFO 면 읽기·쓰기가 **영영 블록**되고(설치가 멈춘다),\n * 디렉터리면 EISDIR 로 죽는다.\n *\n * @param rel `projectDir` 기준 상대경로 (슬래시 구분)\n */\nexport function foreignOwnedTarget(projectDir: string, rel: string): string | null {\n const slot = SKILL_SLOT.exec(rel)?.[1];\n if (slot === undefined) return null;\n if (occupiedByNonDirectory(join(projectDir, slot))) return slot;\n if (rel !== slot) {\n // 슬롯과 최종 경로 **사이의 디렉터리**부터 본다. 중간 성분이 링크면 마지막 것만 보는\n // 판정은 그 링크를 따라간 자리를 \"평범한 파일\"로 읽고 통과시킨다.\n const parts = rel.slice(slot.length + 1).split(\"/\");\n let cursor = slot;\n for (const part of parts.slice(0, -1)) {\n cursor = `${cursor}/${part}`;\n if (occupiedByNonDirectory(join(projectDir, cursor))) return cursor;\n }\n const leaf = lstatSync(join(projectDir, rel), { throwIfNoEntry: false });\n if (leaf !== undefined && !leaf.isFile()) return rel;\n }\n return null;\n}\n","/**\n * 배포 룰(`templates/rules/*.md`)을 **Claude Code 가 아닌 CLI 로** 옮기는 공용 층.\n *\n * WHY (실측 2026-08-12): 룰은 `.claude/rules/` 하나로만 나갔고, 그 디렉터리는 `spec.cli` 에\n * claude 가 있을 때만 만들어진다. 그래서 Codex · OpenCode · Antigravity 단독 설치자는 룰을\n * **한 종도 못 받았다** — 네 CLI 에 룰·훅·스킬을 설치한다는 것이 이 저장소가 파는 것인데.\n * 세 CLI 모두 룰을 받을 자리가 있으므로(각 CLI 문서 확인) 공백은 능력 한계가 아니라 배선이었다.\n *\n * 여기 있는 것은 **한 벌의 변환 규칙**이다. CLI 별 목적지는 각 transform 이 정한다:\n * - Antigravity → `.agents/rules/<name>.md` (워크스페이스 룰, 네이티브)\n * - OpenCode → `.opencode/rules/<name>.md` + `opencode.json` `instructions` 글롭\n * - Codex → `AGENTS.md` 본문 embed (Codex 는 룰 디렉터리가 없다 — AGENTS.md 계층뿐)\n */\n\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\n/** 룰 하나의 이식 결과. `name` 은 확장자 없는 파일명 그대로다. */\nexport interface PortedRule {\n name: string;\n body: string;\n}\n\n/**\n * Claude Code 전용 frontmatter 를 벗긴다.\n *\n * `paths:` 는 Claude Code 의 조건부 로드 키다(`cli-development` 이 유일한 사용처). 다른 CLI 는\n * 그 키를 모르므로 본문 맨 앞에 남으면 **그냥 이상한 텍스트**가 되고, Codex 처럼 AGENTS.md\n * 한복판에 끼워 넣는 경우엔 문서를 깨뜨린다. 벗겨서 상시 룰로 보낸다 — 그 대가로 세 CLI 에서\n * 그 룰이 항상 상주하지만, 문서에 확인되지 않은 활성 문법을 추측해 쓰는 것보다는 낫다.\n * (Antigravity 는 glob 활성 모드가 있다고 문서가 말하지만 frontmatter 문법을 공개하지 않는다.)\n */\nexport function stripClaudeFrontmatter(source: string): string {\n if (!source.startsWith(\"---\\n\")) return source.trim();\n const end = source.indexOf(\"\\n---\", 4);\n if (end === -1) return source.trim();\n return source.slice(end + 4).trim();\n}\n\n/**\n * 선택된 룰들을 읽어 이식형으로 돌려준다. 원본이 없는 이름은 **조용히 건너뛴다** —\n * 설치를 세우는 것보다 낫고, 없는 파일을 깔았다고 보고하지 않는 쪽이 정직하다.\n */\nexport function portRules(harnessRoot: string, ruleNames: ReadonlyArray<string>): PortedRule[] {\n const ported: PortedRule[] = [];\n for (const name of ruleNames) {\n const src = join(harnessRoot, \"templates/rules\", `${name}.md`);\n if (!existsSync(src)) continue;\n ported.push({ name, body: stripClaudeFrontmatter(readFileSync(src, \"utf8\")) });\n }\n return ported;\n}\n\n/**\n * Codex 용 — 룰 전체를 AGENTS.md 에 끼울 한 덩어리로 잇는다.\n *\n * Codex 는 룰 디렉터리가 없고 `AGENTS.md` 계층만 읽는다. 파일 경계가 사라지므로 각 룰의 h1 을\n * h2 로 한 단 낮춰 문서 구조를 유지한다 — 그러지 않으면 h1 이 여러 개인 문서가 되고, 어디까지가\n * 한 룰인지 읽는 쪽이 알 수 없다.\n */\nexport function renderRulesBlock(rules: ReadonlyArray<PortedRule>): string {\n if (rules.length === 0) return \"\";\n return rules.map((r) => r.body.replace(/^#\\s+/, \"## \")).join(\"\\n\\n---\\n\\n\");\n}\n","/**\n * Codex global opt-in — ~/.codex/config.toml trust entry 등록.\n *\n * SPEC: docs/specs/cli-rewrite-completeness.md F11 (Reviewer HIGH-4)\n * Source: bash setup-harness.sh@911c246~1 L1389~1429\n *\n * SAFETY: 사용자 명시 opt-in 없이 ~/.codex/ 글로벌 수정 금지 (D16 / ADR-002 v2 D4).\n * 호출자(installer)는 OptionFlags.withCodexTrust 가 true 일 때만 호출.\n */\n\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { registerTrustEntry } from \"./trust-entry.js\";\n\nexport interface CodexOptInReport {\n /** ~/.codex/config.toml trust entry 등록 결과 */\n trustEntry: {\n enabled: boolean;\n status: \"registered\" | \"already-present\" | \"error\" | \"skipped\";\n message?: string;\n };\n}\n\nexport interface CodexOptInContext {\n /** 사용자 프로젝트 root (trust entry 경로). */\n projectDir: string;\n /** 글로벌 ~/.codex/ 경로 (테스트 override 가능). */\n codexHome?: string;\n}\n\n/** ~/.codex/config.toml 에 프로젝트 trust entry 등록. */\nexport function runCodexOptIn(ctx: CodexOptInContext): CodexOptInReport {\n const codexHome = ctx.codexHome ?? join(homedir(), \".codex\");\n const configPath = join(codexHome, \"config.toml\");\n const result = registerTrustEntry({ configPath, projectDir: ctx.projectDir });\n return {\n trustEntry: {\n enabled: true,\n status: result.status,\n ...(result.message ? { message: result.message } : {}),\n },\n };\n}\n","/**\n * Codex trust entry — `~/.codex/config.toml [projects.\"<dir>\"]` (parity with\n * setup-harness.sh L1404-1422).\n *\n * SAFETY: never modify the global `~/.codex/config.toml` without an explicit\n * opt-in (D16 / ADR-002 v2 D4). Callers must verify user consent first.\n */\n\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\n\nexport interface RegisterTrustResult {\n status: \"registered\" | \"already-present\" | \"error\";\n message?: string;\n}\n\nconst TRUST_BLOCK_REGEX = /\\[projects\\.\"([^\"]+)\"\\]/g;\n\n/** Append a `[projects.\"<projectDir>\"]` block to the user `config.toml` (idempotent). */\nexport function registerTrustEntry(opts: {\n configPath: string;\n projectDir: string;\n}): RegisterTrustResult {\n const { configPath, projectDir } = opts;\n try {\n mkdirSync(dirname(configPath), { recursive: true });\n const existing = existsSync(configPath) ? readFileSync(configPath, \"utf8\") : \"\";\n if (hasTrustEntry(existing, projectDir)) {\n return { status: \"already-present\" };\n }\n const block = `\\n[projects.\"${projectDir}\"]\\ntrust_level = \"trusted\"\\n`;\n writeFileSync(configPath, existing + block);\n return { status: \"registered\" };\n } catch (e: unknown) {\n return {\n status: \"error\",\n message: e instanceof Error ? e.message : String(e),\n };\n }\n}\n\nexport function hasTrustEntry(configContent: string, projectDir: string): boolean {\n const matches = [...configContent.matchAll(TRUST_BLOCK_REGEX)].map((m) => m[1]);\n return matches.includes(projectDir);\n}\n","/**\n * Codex transform orchestrator — wraps the 5-step pipeline.\n *\n * Replaces `scripts/claude-to-codex.sh` (Phase D, OQ4 = TS port).\n *\n * Inputs:\n * - harnessRoot: repository root (templates/ + .mcp.json)\n * - projectDir: target project to receive AGENTS.md + .codex/ + .agents/skills/\n *\n * Outputs (under projectDir):\n * - AGENTS.md\n * - .codex/config.toml\n * - .codex/hooks/*.sh (hooks ported from templates/hooks/)\n * - .agents/skills/<id>/** (dev-method skills — SKILL.md frontmatter 보존 + 형제 파일, #431)\n *\n * v0.6.4 — skill 출력 경로는 Codex 공식 표준 `.agents/skills/<name>/SKILL.md` (repo-level scope).\n * 참조: https://developers.openai.com/codex/skills\n */\n\nimport { chmodSync, existsSync, readFileSync } from \"node:fs\";\nimport { basename, join } from \"node:path\";\nimport { ensureDir } from \"../fs-ops.js\";\nimport type { McpJson } from \"../mcp-merge.js\";\nimport { createOwnedWriter, type OwnedWriteResult } from \"../owned-write.js\";\nimport { renderFillScaffold, withContinuousSkillsNote } from \"../project-claude-merge.js\";\nimport { portRules, renderRulesBlock } from \"../rules-port.js\";\nimport { renderAgentsMd } from \"./agents-md.js\";\nimport { renderConfigToml } from \"./config-toml.js\";\nimport { writeBundledSkillDirs } from \"./skills.js\";\n\nexport interface CodexTransformParams {\n harnessRoot: string;\n projectDir: string;\n /**\n * v26.87.0 — dev-method skill ids 선택 목록 (installer 가 `DEV_METHOD_SKILL_IDS` 를\n * `isAssetSelected` 로 필터). 각 id 의 `templates/skills/<id>/` 를 Codex native\n * `.agents/skills/<id>/` 로 (SKILL.md frontmatter 보존) 출력 — #431 이후 디렉터리 전체다.\n */\n selectedInternalSkills?: ReadonlyArray<string>;\n /** 2026-08-12 — 이 설치의 배포 룰 이름들. Codex 는 룰 디렉터리가 없어 AGENTS.md 본문에 embed 한다. */\n rules?: ReadonlyArray<string>;\n /**\n * v26.133.0 (ADR-048) — 설치 시점 기준선 (install log `externalFiles`).\n *\n * **required 로 둔다.** 옵셔널이면 호출부 하나가 안 넘겨도 컴파일이 통과하고, 그 경로만\n * 조용히 판정 불가로 떨어져 사용자에게 매 설치마다 백업이 쌓인다. 기준선이 없는 상황\n * (레거시 설치)은 빈 Map 을 **명시적으로** 넘겨서 표현한다.\n */\n baseline: ReadonlyMap<string, string>;\n /**\n * v26.134.0 (ADR-049) — `update` 경로. 이미 있는 산출물만 갱신하고 없는 것은 만들지 않는다.\n * 그래서 codex 를 안 깐 프로젝트에서 이 transform 을 돌려도 `.codex/` 가 생기지 않는다.\n */\n refreshOnly?: boolean;\n}\n\nexport interface CodexTransformReport {\n agentsMdPath: string;\n configTomlPath: string;\n hookFiles: string[];\n /** `.agents/skills/<id>/` 에 쓴 **모든** 파일 — `SKILL.md` + 형제(references/scripts 등, #431). */\n skillFiles: string[];\n /** v26.133.0 (ADR-048) — 소유권 결과 (기준선 · 백업된 사용자 편집분). */\n ownership: OwnedWriteResult;\n}\n\nconst HOOK_NAMES = [\"session-start\"];\n\nconst ENV_VAR_RENAME = /CLAUDE_PROJECT_DIR/g;\n\nexport function runCodexTransform(params: CodexTransformParams): CodexTransformReport {\n const {\n harnessRoot,\n projectDir,\n selectedInternalSkills = [],\n rules = [],\n baseline,\n refreshOnly,\n } = params;\n const writer = createOwnedWriter(projectDir, baseline, { refreshOnly: refreshOnly ?? false });\n\n const claudeMd = readRequired(join(harnessRoot, \"templates/CLAUDE.md\"));\n const agentsTemplate = readRequired(join(harnessRoot, \"templates/codex/AGENTS.md.template\"));\n const configTemplate = readRequired(join(harnessRoot, \"templates/codex/config.toml.template\"));\n const projectName = basename(projectDir);\n const mcp = readOptionalJson(join(harnessRoot, \".mcp.json\"));\n\n // 1. AGENTS.md\n const agentsMdPath = join(projectDir, \"AGENTS.md\");\n ensureDir(projectDir);\n const agentsMdOut = renderAgentsMd({\n template: agentsTemplate,\n claudeMd,\n projectName,\n // ADR-085 — 상시 스킬 안내는 앵커가 아니라 여기(프로젝트 맥락)에, 깔린 것만.\n projectContext: withContinuousSkillsNote(\n renderFillScaffold(\"agents-md\"),\n selectedInternalSkills,\n ),\n // Codex 는 룰 디렉터리가 없다 — 룰이 AGENTS.md 본문에 들어가야 도달한다(§Harness Rules).\n harnessRules: renderRulesBlock(portRules(harnessRoot, rules)),\n });\n // 사용자가 채운 AGENTS.md 를 재설치(add 모드) 덮어쓰기 전 보존 — 루트 CLAUDE.md 와 대칭.\n // v26.133.0 (ADR-048) — 내용 비교(backupFileIfChanged)에서 소유자 판정으로 바꿨다. 내용\n // 비교는 하네스가 템플릿을 고친 릴리즈마다 전 사용자에게 백업을 쌓는다 (ADR-047 기각 사유).\n writer.write(agentsMdPath, agentsMdOut);\n\n // 2. .codex/config.toml\n const configTomlPath = join(projectDir, \".codex/config.toml\");\n writer.write(\n configTomlPath,\n renderConfigToml({\n template: configTemplate,\n projectName,\n projectDir,\n mcp,\n }),\n );\n\n // 3. .codex/hooks/session-start.sh\n const hookDir = join(projectDir, \".codex/hooks\");\n const hookFiles: string[] = [];\n for (const hook of HOOK_NAMES) {\n const src = join(harnessRoot, \"templates/hooks\", `${hook}.sh`);\n if (!existsSync(src)) {\n continue;\n }\n const ported = readFileSync(src, \"utf8\").replace(ENV_VAR_RENAME, \"CODEX_PROJECT_DIR\");\n const target = join(hookDir, `${hook}.sh`);\n // refresh 모드가 건너뛴 경로에는 파일이 없다 — chmod 를 무조건 걸면 ENOENT 로 터진다.\n if (!writer.write(target, ported)) continue;\n chmodSync(target, 0o755);\n hookFiles.push(target);\n }\n\n // 4. v26.87.0 — dev-method skills → .agents/skills/<id>/ (frontmatter 보존).\n // renderBundledSkill 이 source frontmatter(name: <id>)를 그대로 보존하고 body 만 포팅.\n // 2026-09-13 (#431) — `SKILL.md` 한 파일이 아니라 **디렉터리 전체**다. 루프는 세 transform\n // 공용 helper 가 소유한다(사본 셋이면 다음 수정이 또 하나를 빠뜨린다).\n const skillFiles = writeBundledSkillDirs({\n harnessRoot,\n projectDir,\n skillIds: selectedInternalSkills,\n writer,\n });\n\n return {\n agentsMdPath,\n configTomlPath,\n hookFiles,\n skillFiles,\n ownership: writer.result(),\n };\n}\n\nfunction readRequired(path: string): string {\n if (!existsSync(path)) {\n throw new Error(`Codex transform: required source missing: ${path}`);\n }\n return readFileSync(path, \"utf8\");\n}\n\nfunction readOptionalJson(path: string): McpJson | null {\n if (!existsSync(path)) {\n return null;\n }\n try {\n return JSON.parse(readFileSync(path, \"utf8\")) as McpJson;\n } catch {\n return null;\n }\n}\n","/**\n * config.toml transform — fill placeholders + append [mcp_servers.X] from .mcp.json.\n * Mirrors `claude-to-codex.sh` steps 2 + 5.\n */\n\nimport type { McpJson } from \"../mcp-merge.js\";\n\nexport interface RenderConfigTomlParams {\n template: string;\n projectName: string;\n projectDir: string;\n /** Source `.mcp.json` (parsed). When provided, [mcp_servers.X] blocks replace defaults. */\n mcp?: McpJson | null;\n}\n\nconst DEFAULT_MCP_BLOCK_RE = /\\n# =+\\n# MCP Servers — .*?\\n# =+[\\s\\S]*$/;\n\n/**\n * Substitute placeholders + replace the MCP servers section with blocks\n * derived from the supplied `.mcp.json` (or leave the template default).\n */\nexport function renderConfigToml(params: RenderConfigTomlParams): string {\n const substituted = params.template\n .replaceAll(\"{PROJECT_NAME}\", params.projectName)\n .replaceAll(\"{PROJECT_DIR}\", params.projectDir)\n .replaceAll(\"{GITHUB_TOKEN}\", \"${GITHUB_TOKEN}\");\n\n if (!params.mcp) {\n return substituted;\n }\n\n const stripped = stripExistingMcpSection(substituted);\n const fresh = renderMcpServers(params.mcp);\n return `${stripped.trimEnd()}\\n${fresh}\\n`;\n}\n\nfunction stripExistingMcpSection(toml: string): string {\n // Drop default [mcp_servers.X] blocks shipped in the template (we replace from .mcp.json)\n const lines = toml.split(/\\r?\\n/);\n const out: string[] = [];\n let skipping = false;\n for (const line of lines) {\n if (line.startsWith(\"[mcp_servers.\")) {\n skipping = true;\n continue;\n }\n if (skipping && line.startsWith(\"[\") && !line.startsWith(\"[mcp_servers.\")) {\n skipping = false;\n }\n if (skipping) {\n continue;\n }\n if (\n /^# .*MCP Servers/.test(line) ||\n /^# Railway MCP/.test(line) ||\n /^# github MCP/.test(line)\n ) {\n continue;\n }\n out.push(line);\n }\n return out.join(\"\\n\").replace(DEFAULT_MCP_BLOCK_RE, \"\");\n}\n\nfunction renderMcpServers(mcp: McpJson): string {\n const stamp = new Date().toISOString().slice(0, 10);\n const header = [\n \"# ============================================================\",\n `# MCP Servers — generated from .mcp.json (${stamp})`,\n \"# ============================================================\",\n ].join(\"\\n\");\n\n const blocks = Object.entries(mcp.mcpServers).map(([name, cfg]) => {\n const lines = [`[mcp_servers.${quoteIfNeeded(name)}]`];\n lines.push(`command = ${jsonString(cfg.command)}`);\n lines.push(`args = ${JSON.stringify(cfg.args)}`);\n if (cfg.env && Object.keys(cfg.env).length > 0) {\n const envBody = Object.entries(cfg.env)\n .map(([k, v]) => `${k} = ${jsonString(v)}`)\n .join(\", \");\n lines.push(`env = { ${envBody} }`);\n }\n return lines.join(\"\\n\");\n });\n\n return [header, \"\", ...blocks].join(\"\\n\");\n}\n\nfunction jsonString(s: string): string {\n return JSON.stringify(s);\n}\n\nfunction quoteIfNeeded(name: string): string {\n return /^[A-Za-z0-9_-]+$/.test(name) ? name : `\"${name}\"`;\n}\n","/**\n * OpenCode transform orchestrator — SSOT (templates/CLAUDE.md, .mcp.json) →\n * OpenCode 자산.\n *\n * Inputs:\n * - harnessRoot: repository root (templates/ + .mcp.json)\n * - projectDir: target project to receive AGENTS.md + opencode.json + .opencode/\n *\n * Outputs (under projectDir):\n * - AGENTS.md\n * - opencode.json\n * - .agents/skills/<id>/** (dev-method skills 디렉터리 전체 — codex·antigravity 와 같은 자리)\n *\n * SPEC: docs/specs/opencode-compat.md\n * Phase: C1 (transform orchestrator)\n */\n\nimport { existsSync, readdirSync, readFileSync, unlinkSync } from \"node:fs\";\nimport { basename, join } from \"node:path\";\nimport { writeBundledSkillDirs } from \"../codex/skills.js\";\nimport { backupFile, ensureDir } from \"../fs-ops.js\";\nimport type { McpJson } from \"../mcp-merge.js\";\nimport { createOwnedWriter, type OwnedWriteResult } from \"../owned-write.js\";\nimport { renderFillScaffold, withContinuousSkillsNote } from \"../project-claude-merge.js\";\nimport { portRules, renderRulesBlock } from \"../rules-port.js\";\nimport { renderAgentsMd } from \"./agents-md.js\";\nimport { renderOpencodeJson } from \"./opencode-json.js\";\n\nexport interface OpencodeTransformParams {\n harnessRoot: string;\n projectDir: string;\n /**\n * dev-method skill ids 선택 목록. installer 가 `DEV_METHOD_SKILL_IDS` 필터로 채움.\n *\n * 2026-08-29 (ADR-081) — `.agents/skills/<id>/SKILL.md` 로 보낸다. v26.87.0 이 커맨드로\n * 변환하던 근거(\"OpenCode 는 native skill 개념이 없다\")가 더는 사실이 아니다.\n */\n selectedInternalSkills?: ReadonlyArray<string>;\n /** 2026-08-12 — 이 설치의 배포 룰 이름들. codex 와 공유하는 `AGENTS.md` 본문에 embed 된다. */\n rules?: ReadonlyArray<string>;\n /**\n * v26.133.0 (ADR-048) — 설치 시점 기준선 (install log `externalFiles`).\n * codex 쪽과 같은 이유로 **required** 다 — 안 넘긴 호출부가 조용히 판정 불가로 떨어지면\n * 그 경로만 매 설치마다 백업이 쌓인다. 레거시는 빈 Map 을 명시적으로 넘긴다.\n */\n baseline: ReadonlyMap<string, string>;\n /**\n * v26.134.0 (ADR-049) — `update` 경로. 이미 있는 산출물만 갱신하고 없는 것은 만들지 않는다.\n * opencode 를 안 깐 프로젝트에서 돌려도 `opencode.json`/`.opencode/` 가 생기지 않는다.\n */\n refreshOnly?: boolean;\n}\n\nexport interface OpencodeTransformReport {\n agentsMdPath: string;\n opencodeJsonPath: string;\n /**\n * `.agents/skills/<id>/` 에 쓴 **모든** 파일 — codex·antigravity 와 같은 자리(같은 파일)다.\n * #431 이후 `SKILL.md` 의 형제(references/scripts 등)도 함께 들어온다.\n */\n skillFiles: string[];\n /** 옛 `.opencode/commands/<id>.md` 를 백업하고 지운 경로들 (ADR-081 전환 뒷정리). */\n retiredCommands: string[];\n /** v26.133.0 (ADR-048) — 소유권 결과 (기준선 · 백업된 사용자 편집분). */\n ownership: OwnedWriteResult;\n}\n\nexport function runOpencodeTransform(params: OpencodeTransformParams): OpencodeTransformReport {\n const {\n harnessRoot,\n projectDir,\n selectedInternalSkills = [],\n rules = [],\n baseline,\n refreshOnly,\n } = params;\n const writer = createOwnedWriter(projectDir, baseline, { refreshOnly: refreshOnly ?? false });\n\n const claudeMd = readRequired(join(harnessRoot, \"templates/CLAUDE.md\"));\n const agentsTemplate = readRequired(join(harnessRoot, \"templates/opencode/AGENTS.md.template\"));\n const opencodeTemplate = readRequired(\n join(harnessRoot, \"templates/opencode/opencode.json.template\"),\n );\n const projectName = basename(projectDir);\n const mcp = readOptionalJson(join(harnessRoot, \".mcp.json\"));\n\n // 1. AGENTS.md\n ensureDir(projectDir);\n const agentsMdPath = join(projectDir, \"AGENTS.md\");\n const agentsMdOut = renderAgentsMd({\n template: agentsTemplate,\n claudeMd,\n projectName,\n // ADR-085 — 상시 스킬 안내는 앵커가 아니라 여기(프로젝트 맥락)에, 깔린 것만.\n projectContext: withContinuousSkillsNote(\n renderFillScaffold(\"agents-md\"),\n selectedInternalSkills,\n ),\n // codex 와 **같은 파일**(프로젝트 루트 `AGENTS.md`)이다. 두 transform 이 서로 다른 본문을\n // 쓰면 나중에 도는 쪽이 앞선 쪽을 덮어써, codex+opencode 조합에서 룰이 통째로 사라진다\n // (독립 검증 C-1 실측). 같은 내용을 쓰면 순서가 결과를 바꾸지 않는다.\n harnessRules: renderRulesBlock(portRules(harnessRoot, rules)),\n });\n // 사용자가 채운 AGENTS.md 를 재설치(add 모드) 덮어쓰기 전 보존 — 루트 CLAUDE.md 와 대칭.\n // v26.133.0 (ADR-048) — 내용 비교에서 소유자 판정으로. codex 가 같은 install 안에서 이미\n // 쓴 AGENTS.md 를 여기서 '사용자 편집'으로 오판하면 매 설치마다 백업이 생긴다.\n writer.write(agentsMdPath, agentsMdOut);\n\n // 2. opencode.json\n const opencodeJsonPath = join(projectDir, \"opencode.json\");\n writer.write(opencodeJsonPath, renderOpencodeJson({ template: opencodeTemplate, mcp }));\n\n // 3. dev-method skills → `.agents/skills/<id>/SKILL.md` (ADR-081).\n //\n // 실측 2026-08-29 (`opencode 1.18.23`, 컨테이너, 대조군 포함): OpenCode 는 프로젝트\n // 스코프 `.agents/skills/<id>/SKILL.md` 를 **자동 로드**하고, 그렇게 실린 스킬이\n // 커맨드 목록에도 `source: \"skill\"` 로 함께 뜬다. 즉 슬래시 호출을 잃지 않으면서\n // 모델이 description 을 보고 스스로 부를 수 있게 된다.\n //\n // codex·antigravity 와 **같은 파일**이다. 셋이 한 벌을 공유하므로 조합 설치에서\n // 같은 스킬이 두 판본으로 깔리는 일이 없다 — 그것이 #340 의 형태였다.\n //\n // 2026-09-13 (#431) — `SKILL.md` 한 파일이 아니라 디렉터리 전체를 보낸다. 루프는 세\n // transform 공용 helper 가 소유한다.\n const skillFiles = writeBundledSkillDirs({\n harnessRoot,\n projectDir,\n skillIds: selectedInternalSkills,\n writer,\n });\n\n // 4. 옛 커맨드 사본 은퇴. 안 지우면 OpenCode 커맨드 목록에 같은 이름이 **두 줄**로 뜬다\n // (옛 `source: \"command\"` + 새 `source: \"skill\"`). 대상은 번들 스킬 이름인 것만 —\n // 목록을 적지 않고 `templates/skills/<이름>/SKILL.md` 존재로 유도한다. 사용자가 직접\n // 쓴 커맨드는 그 조건에 안 걸린다.\n //\n // 판정 대신 **백업하고 지운다**: 우리가 렌더한 파일이지만 사용자가 고쳤을 수 있고,\n // 그 편집분을 되살릴 방법이 없다(`retireMcpAllowlist` 와 같은 이유). 파일이 다시\n // 생기지 않으므로 이 백업은 프로젝트당 한 번뿐이다.\n const retiredCommands: string[] = [];\n const cmdDir = join(projectDir, \".opencode/commands\");\n if (existsSync(cmdDir)) {\n for (const entry of readdirSync(cmdDir)) {\n if (!entry.endsWith(\".md\")) continue;\n const id = entry.slice(0, -3);\n if (!existsSync(join(harnessRoot, \"templates/skills\", id, \"SKILL.md\"))) continue;\n const victim = join(cmdDir, entry);\n try {\n backupFile(victim);\n unlinkSync(victim);\n retiredCommands.push(victim);\n } catch {\n // 뒷정리라 실패해도 설치를 세우지 않는다 (read-only 디렉터리 등).\n }\n }\n }\n\n return {\n agentsMdPath,\n opencodeJsonPath,\n skillFiles,\n retiredCommands,\n ownership: writer.result(),\n };\n}\n\nfunction readRequired(path: string): string {\n if (!existsSync(path)) {\n throw new Error(`OpenCode transform: required source missing: ${path}`);\n }\n return readFileSync(path, \"utf8\");\n}\n\nfunction readOptionalJson(path: string): McpJson | null {\n if (!existsSync(path)) {\n return null;\n }\n try {\n return JSON.parse(readFileSync(path, \"utf8\")) as McpJson;\n } catch {\n return null;\n }\n}\n","/**\n * AGENTS.md transform — CLAUDE.md → AGENTS.md (OpenCode flavor).\n *\n * Mirrors `src/codex/agents-md.ts` logic (Codex와 OpenCode 둘 다 콜론 namespace\n * 미사용으로 slash rename 동일). 별도 파일로 유지 — 모듈 독립성.\n *\n * v26.70.0 — section 추출 → CLAUDE.md 전문 embed (`{PROJECT_RULES}`). codex/agents-md 와 동일 fix.\n */\n\n/** Rename Claude slash conventions (`/uzys:foo`) to OpenCode (`/uzys-foo`). */\nexport function renameSlashes(text: string): string {\n return text.replaceAll(\"/uzys:\", \"/uzys-\");\n}\n\nexport interface AgentsMdParams {\n template: string;\n claudeMd: string;\n projectName: string;\n /** Project-context fill scaffold — the same body shipped to the Claude Code CLAUDE.md. */\n projectContext: string;\n /**\n * 2026-08-12 — 배포 룰 본문. **codex 와 같은 파일**(프로젝트 루트 `AGENTS.md`)을 쓰므로 두\n * 렌더러가 같은 값을 넣어야 한다 — 한쪽만 넣으면 나중에 도는 transform 이 룰을 지운다.\n */\n harnessRules?: string;\n}\n\n/**\n * Render the OpenCode AGENTS.md output by embedding the full CLAUDE.md body.\n *\n * Placeholders (matches templates/opencode/AGENTS.md.template):\n * - {PROJECT_NAME} — basename of project dir\n * - {PROJECT_RULES} — full CLAUDE.md body (first h1 stripped)\n * - {PROJECT_CONTEXT} — project-specific fill scaffold (renderFillScaffold())\n */\nexport function renderAgentsMd(params: AgentsMdParams): string {\n const body = params.claudeMd.replace(/^#\\s+.*\\r?\\n/, \"\").trim();\n const replaced = params.template\n .replaceAll(\"{PROJECT_NAME}\", params.projectName)\n .replaceAll(\"{PROJECT_RULES}\", body)\n .replaceAll(\"{PROJECT_CONTEXT}\", params.projectContext)\n .replaceAll(\"{HARNESS_RULES}\", params.harnessRules ?? \"\");\n return renameSlashes(replaced);\n}\n","/**\n * opencode.json transform — fill template + merge mcp.<name> from .mcp.json.\n *\n * Output: opencode.json (top-level `mcp.<name>` map; OpenCode 1:1 매핑).\n *\n * Codex `[mcp_servers.X]` TOML과 다르게 OpenCode는 JSON이라 Object spread\n * 만으로 충분 (TOML 직렬화 없음).\n */\n\nimport type { McpJson, McpServerConfig } from \"../mcp-merge.js\";\n\nexport interface RenderOpencodeJsonParams {\n /** Template content (templates/opencode/opencode.json.template). */\n template: string;\n /** Source `.mcp.json` (parsed). When provided, top-level `mcp.<name>` is replaced. */\n mcp?: McpJson | null;\n}\n\ninterface OpencodeConfig {\n $schema?: string;\n instructions?: string[];\n mcp?: Record<string, McpServerConfig>;\n command?: Record<string, unknown>;\n agent?: Record<string, unknown>;\n plugin?: string[];\n permission?: Record<string, unknown>;\n [key: string]: unknown;\n}\n\n/**\n * Substitute `mcp` in the template with entries from `.mcp.json`.\n * Other keys (`agent`, `command`, `plugin`, `permission`, `instructions`,\n * `$schema`) are preserved from the template.\n */\nexport function renderOpencodeJson(params: RenderOpencodeJsonParams): string {\n const config = parseTemplate(params.template);\n\n if (params.mcp) {\n config.mcp = { ...params.mcp.mcpServers };\n }\n\n return `${JSON.stringify(config, null, 2)}\\n`;\n}\n\nfunction parseTemplate(template: string): OpencodeConfig {\n try {\n return JSON.parse(template) as OpencodeConfig;\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n throw new Error(`opencode.json template invalid JSON: ${message}`);\n }\n}\n","/**\n * env-files.ts — 환경 파일 자동 생성.\n *\n * SPEC: docs/specs/cli-rewrite-completeness.md F7, F8\n * Source: bash setup-harness.sh@911c246~1 L880~890 + L954~996.\n *\n * 2 종 산출:\n * 1. .env.example (csr-supabase / full Track) — Supabase 토큰 가이드\n * 2. .gitignore .env 라인 추가 (없을 때만)\n *\n * 모두 idempotent — 이미 있으면 skip.\n *\n * 2026-08-16 (ADR-072) — `.mcp-allowlist` 생성기 제거. 그 파일을 읽던 `mcp-pre-exec.sh` 훅이\n * 목적 부적합으로 빠지면서(루트 `CLAUDE.md` §판정은 목적에서 시작한다) 읽는 쪽이 없어졌다.\n * 생성기만 남기면 아무도 안 보는 파일을 남의 저장소에 계속 만든다.\n */\n\nimport { appendFileSync, existsSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { Track } from \"./types.js\";\n\nconst ENV_EXAMPLE_BODY = `# .env.example — csr-supabase Track\n# Copy to .env (gitignored) and fill in values: cp .env.example .env\n\n# ===== Supabase Management API (MCP server용) =====\n# Personal Access Token — @supabase/mcp-server가 프로젝트 생성/마이그레이션/Edge Functions 배포에 사용\n# 발급: https://supabase.com/dashboard/account/tokens\nSUPABASE_ACCESS_TOKEN=\n\n# 프로젝트 참조 ID (예: \"abcdefghijklmnop\")\n# 위치: Supabase Dashboard → Project Settings → General\nSUPABASE_PROJECT_REF=\n\n# DB 패스워드 (supabase db push 등 직접 DB 접근용)\n# 위치: Supabase Dashboard → Project Settings → Database\nSUPABASE_DB_PASSWORD=\n\n# ===== Frontend (public, 클라이언트 노출 OK) =====\n# 위치: Supabase Dashboard → Project Settings → API\nNEXT_PUBLIC_SUPABASE_URL=\nNEXT_PUBLIC_SUPABASE_ANON_KEY=\n\n# ===== Optional — 앱 측 AI 기능용 =====\n# OPENAI_API_KEY=\n# ANTHROPIC_API_KEY=\n\n# ===== Note =====\n# - Vercel/Netlify는 별도 CLI login 사용 (env 불필요): vercel login / netlify login\n# - Supabase CLI(supabase login)는 OAuth로 ~/.config/supabase/에 토큰 저장 — env 별개\n# - .env는 .gitignore됨 (자동 추가). 절대 commit 금지.\n`;\n\nconst ENV_EXAMPLE_TRACKS: ReadonlyArray<Track> = [\"csr-supabase\", \"full\"];\n\nconst GITIGNORE_ENV_PATTERN = /^\\.env$|^\\.env\\s/m;\n\n/**\n * .env.example 생성 (csr-supabase/full Track 한정, idempotent).\n * @returns true if created, false if skipped (already exists or non-applicable track)\n */\nexport function writeEnvExample(projectDir: string, tracks: ReadonlyArray<Track>): boolean {\n if (!tracks.some((t) => ENV_EXAMPLE_TRACKS.includes(t))) {\n return false;\n }\n const path = join(projectDir, \".env.example\");\n if (existsSync(path)) {\n return false;\n }\n writeFileSync(path, ENV_EXAMPLE_BODY);\n return true;\n}\n\n/**\n * .gitignore에 `.env` 라인 추가. 이미 있으면 skip.\n * @returns true if appended, false if skipped (no .gitignore or .env already listed)\n */\nexport function addGitignoreEnv(projectDir: string): boolean {\n const path = join(projectDir, \".gitignore\");\n if (!existsSync(path)) {\n return false;\n }\n const content = readFileSync(path, \"utf8\");\n if (GITIGNORE_ENV_PATTERN.test(content)) {\n return false;\n }\n // append with separator (avoid double newlines)\n const sep = content.endsWith(\"\\n\") ? \"\" : \"\\n\";\n appendFileSync(path, `${sep}\\n# Secret env (auto-added by agent-harness install)\\n.env\\n`);\n return true;\n}\n\nconst AGENT_ARTIFACT_DIRS = [\".factory/\", \".goose/\", \".uzys-agent-harness/\"];\nconst GITIGNORE_AGENT_ARTIFACT_HEADER =\n \"# agent CLI / harness 자동 생성물 (auto-added by agent-harness)\";\n\n/**\n * v0.8.0 — `.gitignore`에 `.factory/`, `.goose/` 패턴 추가 (사용자 보고 #3).\n *\n * `npx skills add`가 multi-CLI universal install 동작 — Codex 사용자 환경에서\n * `.factory/skills/`, `.goose/skills/` 자동 생성. 사용자 git noise 회피용 ignore.\n *\n * 2026-08-02 — `.uzys-agent-harness/` 추가. 설치 로그가 살던 자리에 훅 **차단 로그**\n * (`hook-blocks.log`)가 합류하면서, 차단이 일어날 때마다 사용자 리포에 추적되는 파일이\n * 늘어나게 됐다. 계측이 남의 저장소를 더럽히면 안 된다.\n *\n * idempotent — 이미 있으면 skip.\n * @returns added pattern list (empty if all already present or no .gitignore)\n */\nexport function addGitignoreAgentArtifacts(projectDir: string): string[] {\n const path = join(projectDir, \".gitignore\");\n if (!existsSync(path)) {\n return [];\n }\n const content = readFileSync(path, \"utf8\");\n const missing = AGENT_ARTIFACT_DIRS.filter((pattern) => {\n // exact line match (이스케이프 후 줄 단위 비교 — 단순화: 문자열 포함)\n const lineRegex = new RegExp(`^${pattern.replace(/\\./g, \"\\\\.\").replace(/\\//g, \"/\")}\\\\s*$`, \"m\");\n return !lineRegex.test(content);\n });\n if (missing.length === 0) {\n return [];\n }\n const sep = content.endsWith(\"\\n\") ? \"\" : \"\\n\";\n const block = [GITIGNORE_AGENT_ARTIFACT_HEADER, ...missing].join(\"\\n\");\n appendFileSync(path, `${sep}\\n${block}\\n`);\n return [...missing];\n}\n","/**\n * External installer — `EXTERNAL_ASSETS` 매트릭스를 실제 호출로 변환.\n *\n * SPEC: docs/specs/cli-rewrite-completeness.md F1\n *\n * Decision (OQ1): 실패는 warn-skip. 종료 시 누락 자산 목록 보고.\n * abort는 첫 실행 신뢰성을 깨뜨리므로 채택 안 함 (vibe killer).\n *\n * Spawning은 `child_process.spawnSync` 사용. command/args 분리로 shell injection 차단.\n * stdout/stderr는 captured — 사용자에게 한 줄 요약만 노출 (verbose-log는 별도 옵션 후속).\n */\n\nimport { type SpawnSyncReturns, spawnSync } from \"node:child_process\";\nimport { existsSync, readdirSync, readFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { CATEGORIES as CATEGORY_ORDER } from \"./categories.js\";\nimport {\n assetReachesCli,\n EXTERNAL_ASSETS,\n type ExternalAsset,\n type ExternalAssetMethod,\n filterApplicableAssets,\n} from \"./external-assets.js\";\nimport { type InstallLog, readInstallLog } from \"./install-log.js\";\nimport {\n type CliTargets,\n DEFAULT_OPTIONS,\n type InstallScope,\n type OptionFlags,\n resolveScope,\n type Track,\n} from \"./types.js\";\n\nexport interface ExternalInstallerDeps {\n /** Override `spawnSync` for tests (mock으로 호출 횟수 + args 검증). */\n spawn?: (cmd: string, args: ReadonlyArray<string>, opts: SpawnOpts) => SpawnSyncReturns<string>;\n /** harness root (prune-ecc.sh script 위치 resolve용). */\n harnessRoot?: string;\n /** asset 매트릭스 override (테스트용, 기본 EXTERNAL_ASSETS 전체). */\n assets?: ReadonlyArray<ExternalAsset>;\n /** 진행 상황 로그 stream (기본 console.log). 일반 로그용. */\n log?: (msg: string) => void;\n /** 경고 메시지 stream (기본 console.error). */\n warn?: (msg: string) => void;\n /**\n * 자산 설치 시작 직전 호출 (streaming UI용).\n * Renderer가 \"→ asset (installing...)\" 라인 출력에 사용.\n */\n onAssetStart?: (asset: ExternalAsset) => void;\n /**\n * 자산 설치 완료 후 호출 (streaming UI용).\n * Renderer가 \"✓/⊘ asset meta\" 라인 출력에 사용.\n */\n onAssetResult?: (result: AssetInstallResult) => void;\n}\n\ninterface SpawnOpts {\n encoding: \"utf8\";\n stdio: (\"ignore\" | \"pipe\")[] | \"ignore\" | \"pipe\";\n timeout?: number;\n /** v26.77.0 — 작업 디렉토리. projectDir 로 고정해 자산이 올바른 프로젝트에 착지. */\n cwd?: string;\n}\n\nexport interface AssetInstallResult {\n asset: ExternalAsset;\n ok: boolean;\n /** ok=false 시 user-facing 메시지 */\n message?: string;\n /**\n * v26.59.0 — 설치된 자산 version. install 후 detectVersion 으로 path 기반 추출.\n * plugin: ~/.claude/plugins/cache/<marketplace>/<plugin>/<VERSION>/ 디렉토리명\n * npm-global: <npm root -g>/<pkg>/package.json 의 version\n * 그 외 method (skill, npx-run, shell-script): 표준 metadata 없음 → undefined.\n */\n version?: string;\n}\n\nexport interface ExternalInstallReport {\n /** 적용 시도된 자산 (조건 통과한 것만) */\n attempted: ReadonlyArray<AssetInstallResult>;\n /** 성공 갯수 */\n succeeded: number;\n /** warn-skip 된 갯수 */\n skipped: number;\n /**\n * v26.102.0 (ADR-031) — 조건은 통과했으나 선택 CLI 와 도달 범위(assetCliSupport)의\n * 교집합이 없어 **시도조차 하지 않은** 자산 (예: codex 단독 설치의 claude 전용 plugin).\n * 침묵 제외 금지 — render 가 이 목록을 사용자에게 고지한다 (no-false-ship).\n */\n excludedByCli: ReadonlyArray<ExternalAsset>;\n}\n\nconst DEFAULT_SPAWN_TIMEOUT_MS = 120_000;\n/**\n * #422 — `npm install` 계열은 큰 CLI(netlify-cli 26.x 는 413 MB · 컨테이너 실측 86초)를 받는다.\n * 120초는 느린 망·기존 lockfile 해석에서 넘기고, 그동안 화면은 침묵이라 설치자에게는 \"멈춤\"이다.\n * 진행 표시는 렌더러(`onAssetStart`)가, 시간 상한은 여기서 — 넘기면 직접 돌릴 명령을 알려 준다.\n */\nconst NPM_SPAWN_TIMEOUT_MS = 600_000;\n\n/** method.kind 별 spawn 시간 상한 — 패키지 설치는 길고, 그 밖은 기본값. */\nfunction spawnTimeoutFor(kind: ExternalAsset[\"method\"][\"kind\"]): number {\n return kind === \"npm\" || kind === \"npx-run\" ? NPM_SPAWN_TIMEOUT_MS : DEFAULT_SPAWN_TIMEOUT_MS;\n}\n\n/**\n * v26.102.0 (ADR-031) — external 단계의 대상/배제 판정 **단일 지점**. 규칙 = 조건 통과 ∧\n * non-internal(Phase 1 담당) ∧ 선택 CLI 도달. runExternalInstall(시도 목록)과\n * runExternalPhase(헤더 카운트)가 이 함수만 호출한다 — 같은 규칙을 두 파일에 각각 기술하면\n * 3번째 조건이 생길 때 카운트만 조용히 어긋난다 (SOD 리뷰 Important-6, no-false-ship\n * \"동일 목록 2곳 하드코딩 금지\").\n */\nexport function selectExternalTargets(\n assets: ReadonlyArray<ExternalAsset>,\n ctx: {\n tracks: ReadonlyArray<Track>;\n options: OptionFlags;\n cli: CliTargets;\n userOverride?: { forceInclude: ReadonlyArray<string>; forceExclude: ReadonlyArray<string> };\n },\n): { targets: ExternalAsset[]; excludedByCli: ExternalAsset[] } {\n const conditionPassed = filterApplicableAssets(assets, ctx).filter(\n (a) => a.method.kind !== \"internal\",\n );\n return {\n targets: conditionPassed.filter((a) => assetReachesCli(a, ctx.cli)),\n excludedByCli: conditionPassed.filter((a) => !assetReachesCli(a, ctx.cli)),\n };\n}\n\n/**\n * spec에 적용 가능한 자산을 모두 시도. 실패는 warn-skip (기본).\n */\nexport function runExternalInstall(\n ctx: {\n tracks: ReadonlyArray<Track>;\n options: OptionFlags;\n cli: CliTargets;\n /** v26.47.0 — Phase C full user override (forceInclude/forceExclude). */\n userOverride?: { forceInclude: ReadonlyArray<string>; forceExclude: ReadonlyArray<string> };\n /** v26.64.0 (ADR-020) — Install scope. undefined → default \"project\". */\n scope?: InstallScope;\n /**\n * v26.77.0 — 외부 설치기 spawn 의 작업 디렉토리.\n * 미지정 시 process.cwd(). 핵심: npm(--save-dev)·npx-run(bmad --directory .)·\n * plugin(--scope project, claude 의 cwd 기반 project 탐지)·skill 이 모두 cwd 기준으로\n * 착지하므로, --project-dir 가 cwd 와 다르면 cwd 를 projectDir 로 맞춰야 자산이 올바른\n * 프로젝트에 떨어진다 (이 누락 = 2026-06-07 probe 가 repo 를 오염시킨 근본 원인).\n */\n projectDir?: string;\n },\n deps: ExternalInstallerDeps = {},\n): ExternalInstallReport {\n const log = deps.log ?? console.log;\n const warn = deps.warn ?? console.error;\n const spawn = deps.spawn ?? defaultSpawn;\n const assets = deps.assets ?? EXTERNAL_ASSETS;\n const harnessRoot = deps.harnessRoot ?? process.cwd();\n const projectDir = ctx.projectDir ?? process.cwd();\n\n // v26.81.0 (ADR-022) — internal 자산(tauri-desktop)은 Phase 1 의\n // manifest/transform 이 설치 주체 — external(spawn) 단계에서 제외. Phase 1 의\n // templates 행으로 사용자에게 이미 가시화됨 (중복 보고 방지).\n // v26.102.0 (ADR-031, Batch3) — CLI 도달 범위 필터. 선택 CLI 와 교집합 없는 자산은\n // spawn 자체를 배제한다: codex 단독 설치가 claude 전용 plugin 을 `claude plugin ...` 으로\n // spawn 해 ~/.claude/plugins 를 오염시키던 P0 [4cli-asymmetry-cluster] 의 구조적 fix.\n // 제외분은 excludedByCli 로 보고 — 침묵 제외 금지 (no-false-ship).\n const { targets: applicable, excludedByCli } = selectExternalTargets(assets, ctx);\n // v26.55.0 — Phase 2 grouped progress UX. 카테고리 순서로 정렬 → install.ts 의 onAssetStart\n // callback 이 category 변경 감지로 헤더 출력 가능. ADR-016.\n const sorted = [...applicable].sort((a, b) => {\n const ai = CATEGORY_ORDER.indexOf(a.category);\n const bi = CATEGORY_ORDER.indexOf(b.category);\n return ai - bi;\n });\n const attempted: AssetInstallResult[] = [];\n const cli = ctx.cli;\n const scope = resolveScope(ctx.scope);\n\n for (const asset of sorted) {\n deps.onAssetStart?.(asset);\n log(` → ${asset.description}`);\n const baseResult = installOne(asset, { spawn, harnessRoot, cli, scope, projectDir });\n let result: AssetInstallResult = baseResult;\n if (baseResult.ok) {\n const v = detectVersion(asset.method, spawn);\n if (v) result = { ...baseResult, version: v };\n }\n deps.onAssetResult?.(result);\n\n if (!result.ok) {\n // v26.79.0 — 모든 실패는 warn-skip (abort 는 vibe killer 라 미채택). 죽은\n // failureMode/aborted 메커니즘 제거 (사용 자산 0 + 렌더러 미참조).\n warn(` [warn-skip] ${asset.id}: ${result.message ?? \"failed\"}`);\n }\n\n attempted.push(result);\n }\n\n return {\n attempted,\n succeeded: attempted.filter((r) => r.ok).length,\n skipped: attempted.filter((r) => !r.ok).length,\n excludedByCli,\n };\n}\n\n/**\n * 자산 1개 설치. method.kind 별 적절한 명령 실행.\n */\nfunction installOne(\n asset: ExternalAsset,\n ctx: {\n spawn: NonNullable<ExternalInstallerDeps[\"spawn\"]>;\n harnessRoot: string;\n cli: CliTargets;\n scope: InstallScope;\n /** v26.77.0 — spawn cwd. 자산이 올바른 프로젝트에 착지하도록 projectDir 로 고정. */\n projectDir: string;\n },\n): AssetInstallResult {\n const { method } = asset;\n const cwd = ctx.projectDir;\n switch (method.kind) {\n case \"skill\":\n return runSpawn(asset, ctx.spawn, \"npx\", buildSkillArgs(method, ctx.cli, ctx.scope), cwd);\n case \"plugin\":\n return installPlugin(asset, ctx.spawn, method, ctx.scope, cwd);\n case \"npm\": {\n // v26.64.0 (ADR-020) — scope=project 시 devDep, scope=global 시 -g.\n // v26.68.0 — method.kind \"npm-global\" → \"npm\" rename (scope 분기와 무관 의미).\n // v26.80.0 — pinned 버전 설치 (`pkg@version`). vetting 시점의 코드만 실행 (보안 wedge).\n const pinned = `${method.pkg}@${method.version}`;\n return runSpawn(\n asset,\n ctx.spawn,\n \"npm\",\n ctx.scope === \"global\" ? [\"install\", \"-g\", pinned] : [\"install\", \"--save-dev\", pinned],\n cwd,\n );\n }\n case \"npx-run\":\n // v26.80.0 — pinned 버전 실행 (`cmd@version`). 이전 `cmd` 에 \"@latest\" 인라인이던 것을\n // 구조 필드로 분리 (cmd 는 bare 이름 — drift override/라벨이 이름 그대로 사용).\n return runSpawn(\n asset,\n ctx.spawn,\n \"npx\",\n [`${method.cmd}@${method.version}`, ...(method.args ?? [])],\n cwd,\n );\n case \"shell-script\": {\n const scriptPath = join(ctx.harnessRoot, method.script);\n if (!existsSync(scriptPath)) {\n return {\n asset,\n ok: false,\n message: `script not found: ${scriptPath}`,\n };\n }\n return runSpawn(asset, ctx.spawn, \"bash\", [scriptPath, ...method.args], cwd);\n }\n case \"internal\":\n // v26.81.0 (ADR-022) — 도달 불가 (runExternalInstall 이 사전 필터). exhaustive switch\n // + 방어: 도달해도 spawn 없이 ok (Phase 1 manifest 가 실 설치 주체).\n return { asset, ok: true, message: \"internal template (installed by Phase 1 manifest)\" };\n }\n}\n\n/**\n * v26.39.5 fix — `--agent <list>` 명시 추가 (사용자 보고 #3 진짜 fix).\n *\n * `npx skills add` default 동작은 `*` (all installed agents) → universal install →\n * `.factory/skills/`, `.goose/skills/` 자동 생성. v0.8.0 의 `.gitignore` 패턴 추가만으론\n * git noise 만 차단하고 disk 디렉토리 생성은 막지 못함.\n *\n * 본 fix: `spec.cli` 의 base CLI 만 콤마 구분 명시 → 의도된 agent 만 install.\n *\n * v26.39.6 fix — skills CLI agent name 매핑.\n * skills CLI 1.5.5 valid agent 이름은 `claude-code` 인데 우리 CliBase 는 `claude`.\n * 매핑 누락 시 `Invalid agents: claude` 로 exit 1 → 외부 사용자 (실사용 리포\n * reproduce 2026-05-06) 환경에서 7건 skill 자산 100% skip.\n */\nconst SKILLS_CLI_AGENT_MAP: Record<CliTargets[number], string> = {\n claude: \"claude-code\",\n codex: \"codex\",\n opencode: \"opencode\",\n // v26.66.0 — Antigravity (Google) skills agent. `.agents/skills/` 표준 공유 (codex transform 산출과 동일).\n antigravity: \"antigravity\",\n};\n\n/**\n * `npx skills` CLI 고정 버전 — unpinned 면 upstream breaking 이 설치·검증을 동시에 깬다\n * (1.5.5→1.5.7 multi-agent `--agent` 플래그 파손 전례). bump 시 Docker 시나리오 재검증 필수.\n * audit CODE-4/D-1. scripts/verify-catalog.mjs 와 동일 값 유지 (drift 테스트 가드).\n */\nexport const SKILLS_CLI_VERSION = \"1.5.11\";\n\n/** `npx skills <subcommand>` 의 첫 인자 — 항상 버전 고정. */\nexport function skillsCliSpec(): string {\n return `skills@${SKILLS_CLI_VERSION}`;\n}\n\n/** `update` 가 외부 스킬을 갱신했는지에 대한 보고 (#374). */\nexport interface ExternalSkillRefresh {\n /** 갱신을 시도한 자산 수. 0 = 설치 기록에 외부 스킬이 없다. */\n attempted: number;\n /** 그중 성공한 수. */\n refreshed: number;\n /** 실패한 자산 — 화면에 이름을 낸다. 실패해도 update 는 계속한다. */\n failed: ReadonlyArray<{ id: string; message: string }>;\n /**\n * 설치 기록에는 있는데 **지금 카탈로그에 없는** 스킬 자산 id — 갱신 대상이 아니다.\n *\n * 침묵하면 사용자는 깔린 3종 중 2종만 갱신됐다는 사실을 알 수 없다. 이 저장소는 실제로\n * 자산을 지운 적이 있다(`north-star-skill`) — 그 릴리즈 뒤 옛 설치본이 도는 자리다.\n */\n notInCatalog: ReadonlyArray<string>;\n /**\n * **설치 기록이 없어 무엇을 갱신해야 하는지 판정할 수 없다** (레거시 설치본).\n *\n * `attempted: 0` 과 구분한다 — 그쪽은 \"갱신할 게 없다\"이고 이쪽은 \"모른다\"다. 둘을 같은\n * 침묵으로 합치면 이 이슈가 고치려던 실패 형태(조용한 무동작)가 그대로 남는다.\n */\n unknown: boolean;\n}\n\n/**\n * 이미 깔린 외부 스킬을 상류 최신판으로 다시 받는다 (#374).\n *\n * **왜 있나**: `update` 는 우리가 놓아둔 정책 파일과 우리가 렌더한 CLI 산출물만 새로 썼다.\n * `npx skills add` 로 깐 스킬 본문은 그 경로에 없어서 **한 번도 갱신되지 않았다**(실측\n * 2026-08-27, npx 호출 추적: install 2회 · update 0회). 그런데 화면에는\n * `✓ external CLI artifacts` 가 떠서 다 된 것처럼 보였다.\n *\n * **`skills update` 를 쓰지 않는 이유** (독립 리뷰가 잡은 CRITICAL, 실측 2026-08-27):\n * 그 서브명령은 **`--copy` 도 `--agent` 도 받지 않는다**(도움말의 *Update Options* = `-g`·`-p`·`-y`\n * 뿐). 그래서 자기 기본 배치로 스킬 자리를 다시 만들고, 결과가 이렇다:\n *\n * claude 단독 설치 · install 직후 → `.claude/skills/<id>` = 실제 디렉터리 · `.agents/` 없음\n * 같은 프로젝트에 `skills update` → `.claude/skills/<id>` = **`.agents/` 로의 심링크**,\n * 고른 적 없는 `.agents/` 트리가 **생성**됨\n *\n * 이건 #372 가 세운 \"두 자리에 각각 실제 사본\" 계약을 되돌린다. 측정된 해악은 셋이다:\n * ⓐ 고른 적 없는 CLI 의 자산 트리가 생긴다(ADR-031 이 P0 로 닫은 형태) ⓑ 사용자가 정체 모를\n * `.agents/` 를 지우면 `.claude/skills/*` 는 **끊긴 링크**가 되어 본문이 사라진다\n * ⓒ 슬롯 이름이 번들 스킬과 겹치면 `foreign-slot.ts` 가 디렉터리 아닌 자리를 \"남의 것\"으로\n * 판정해(#343) 하네스 writer 가 건너뛴다.\n *\n * *(초판은 여기에 \"그 뒤로 우리 최신본이 영영 그 자리에 안 들어간다\"고 적었는데 **틀렸다** —\n * 외부 스킬 id 는 `templates/skills/` 에 없어 우리 writer 가 그 자리를 겨냥하지 않고, 실측에서\n * 이 구현은 이미 심링크가 된 슬롯을 오히려 디렉터리로 **복구한다**. 독립 리뷰가 반증했다.)*\n *\n * **그래서 install 과 같은 호출**(`add … --agent … --copy --yes`)을 다시 돌린다. 실측으로\n * 재실행은 상류 최신판을 받아오고(변이 1 → 0) 두 자리가 실제 디렉터리로 유지된다.\n * 같은 함수(`runExternalInstall`)를 쓰는 이유는 ADR-049 와 같다 — 인자 조립을 두 벌 두면\n * 한쪽만 고쳐지는 순간 조용히 갈린다.\n *\n * **대상은 설치 기록에 적힌 스킬 자산뿐이다.** 트랙·옵션에서 다시 유도하지 않는다 — 그러면\n * 사용자가 고른 적 없는 자산이 update 로 새로 깔릴 수 있고, 반대로 opt-in 으로 깐 자산이\n * 조건 불일치로 조용히 빠진다. `forceInclude` 로 조건 판정을 우회해 **깐 것을 깐 그대로**\n * 다시 받는다.\n *\n * **그 우회는 신뢰 티어 게이트도 지난다** — `shouldInstallAsset` 이 `forceInclude` 를 tier\n * 검사보다 앞에서 본다. 즉 자산이 나중에 experimental(T3)로 강등돼도 이미 깐 사람은 갱신을\n * 계속 받는다. 이건 부작용이 아니라 **결정**이다(ADR-078 Consequences): update 는 무엇을\n * 깔지 다시 묻는 자리가 아니고, 티어 강등을 이유로 조용히 갱신을 끊으면 사용자는 낡은 본문을\n * 쥔 채 그 사실을 모른다. 강등 자체는 `trust-tier-drift` 가 따로 감시한다.\n */\nexport function refreshExternalSkills(\n projectDir: string,\n deps: Pick<ExternalInstallerDeps, \"spawn\" | \"assets\" | \"log\" | \"warn\"> & {\n /** 설치 기록 읽기 주입점 (테스트용). 기본 `readInstallLog`. */\n readLog?: (projectDir: string) => InstallLog | null;\n /** 실 설치 주입점 (테스트용). 기본 `runExternalInstall`. */\n run?: typeof runExternalInstall;\n } = {},\n): ExternalSkillRefresh {\n const none = { attempted: 0, refreshed: 0, failed: [] as const, notInCatalog: [] as const };\n const log = (deps.readLog ?? readInstallLog)(projectDir);\n if (!log) {\n return { ...none, unknown: true };\n }\n const installedIds = new Set(log.assets.filter((a) => a.method === \"skill\").map((a) => a.id));\n const catalog = deps.assets ?? EXTERNAL_ASSETS;\n const targets = catalog.filter((a) => a.method.kind === \"skill\" && installedIds.has(a.id));\n // 기록에는 있는데 카탈로그에서 사라진 자산 — 갱신할 방법이 없다. 화면에 이름을 낸다.\n const inCatalog = new Set(targets.map((a) => a.id));\n const notInCatalog = [...installedIds].filter((id) => !inCatalog.has(id));\n if (targets.length === 0) {\n return { ...none, notInCatalog, unknown: false };\n }\n const report = (deps.run ?? runExternalInstall)(\n {\n tracks: log.spec.tracks as ReadonlyArray<Track>,\n // `forceInclude` 가 조건 판정을 앞지르므로 옵션 값은 결과에 영향을 주지 않는다.\n options: DEFAULT_OPTIONS,\n cli: log.spec.cli as CliTargets,\n userOverride: { forceInclude: [...installedIds], forceExclude: [] },\n scope: log.scope,\n projectDir,\n },\n {\n assets: targets,\n ...(deps.spawn ? { spawn: deps.spawn } : {}),\n ...(deps.log ? { log: deps.log } : {}),\n ...(deps.warn ? { warn: deps.warn } : {}),\n },\n );\n return {\n attempted: report.attempted.length,\n refreshed: report.succeeded,\n failed: report.attempted\n .filter((r) => !r.ok)\n .map((r) => ({ id: r.asset.id, message: r.message ?? \"failed\" })),\n notInCatalog,\n unknown: false,\n };\n}\n\n/**\n * `npx skills add` 인자.\n *\n * **`--copy` 가 핵심이다** (#372, 실측 2026-08-27 `skills@1.5.11` 컨테이너). 기본 모드에서\n * 에이전트를 여럿 넘기면 skill 이 `.agents/skills/`(codex·opencode·antigravity 공용)에만 깔리고\n * **Claude Code 몫의 `.claude/skills/` 가 조용히 빠진다** — exit 0 이라 설치 화면에는 ✓ 로 뜬다.\n * `--copy`(help: \"Copy files instead of symlinking to agent directories\")를 붙이면 한 호출로\n * 두 자리가 다 생긴다.\n *\n * 재 본 것들:\n * `--agent a --agent b` (반복) → `.agents/` 만 ❌\n * `--agent a b` (variadic, 문서 형태) → `.agents/` 만 ❌ (1.5.23 도 동일)\n * `--agent \"a,b\"` (콤마) → `Invalid agents:` **exit 1**, 아무것도 안 깔림\n * 위 어느 형태든 **+ `--copy`** → 두 자리 다 생김 ✅ 1회 5초\n * 콤마는 1.5.5 에서만 되던 형태다(1.5.7 폐지, v26.55.1 회귀). 버전 bump 때 이 표를 다시 잰다.\n *\n * **`--copy` 는 에이전트를 명시할 때만 붙인다.** 미지정(`cli: []`, 레거시 \"전체\")에 붙이면\n * 설치 대상이 `.agents/` 한 곳에서 **약 50개 도구 디렉터리로 폭발**한다(실측). 그 경로는 종전\n * 그대로 둔다.\n */\nfunction buildSkillArgs(\n method: { kind: \"skill\"; source: string; skill?: string },\n cli: CliTargets,\n scope: InstallScope,\n): string[] {\n const args = [skillsCliSpec(), \"add\", method.source];\n if (method.skill) {\n args.push(\"--skill\", method.skill);\n }\n if (cli.length > 0) {\n for (const c of cli) {\n args.push(\"--agent\", SKILLS_CLI_AGENT_MAP[c] ?? c);\n }\n // 에이전트를 고른 경우에만. 위 주석의 폭발 사유.\n args.push(\"--copy\");\n }\n // v26.64.0 (ADR-020) — global scope 시 -g. project 는 skills CLI default (project) 따름.\n if (scope === \"global\") {\n args.push(\"-g\");\n }\n args.push(\"--yes\");\n return args;\n}\n\n/**\n * Plugin 은 marketplace add → install 두 단계. marketplace add 실패는 무시 (이미 등록 케이스).\n *\n * v26.64.0 (ADR-020) — `--scope <project|user>` 분기. claude CLI native:\n * - project: --scope project (현재 projectPath 격리, installed_plugins.json 메타 매칭)\n * - global: --scope user (모든 projectPath 에서 활성)\n * fs 적으로는 양쪽 모두 ~/.claude/plugins/cache/ + ~/.claude/plugins/marketplaces/ 에 write\n * (claude CLI 자체 디자인). 격리는 메타데이터.\n */\nfunction installPlugin(\n asset: ExternalAsset,\n spawn: NonNullable<ExternalInstallerDeps[\"spawn\"]>,\n method: { kind: \"plugin\"; marketplace: string; pluginId: string },\n scope: InstallScope,\n cwd: string,\n): AssetInstallResult {\n const claudeScope = scope === \"global\" ? \"user\" : \"project\";\n // v26.77.0 — cwd=projectDir: --scope project 시 claude 가 cwd 기준으로 프로젝트를 탐지하므로\n // installed_plugins.json 의 projectPath 가 올바른 프로젝트로 기록된다.\n spawn(\n \"claude\",\n [\"plugin\", \"marketplace\", \"add\", \"--scope\", claudeScope, method.marketplace],\n spawnOpts(cwd),\n );\n return runSpawn(\n asset,\n spawn,\n \"claude\",\n [\"plugin\", \"install\", \"--scope\", claudeScope, method.pluginId],\n cwd,\n );\n}\n\nfunction runSpawn(\n asset: ExternalAsset,\n spawn: NonNullable<ExternalInstallerDeps[\"spawn\"]>,\n cmd: string,\n args: ReadonlyArray<string>,\n cwd?: string,\n): AssetInstallResult {\n const timeout = spawnTimeoutFor(asset.method.kind);\n const result = spawn(cmd, args, spawnOpts(cwd, timeout));\n if (result.error) {\n // #422 — 시간 초과는 \"실패\"가 아니라 \"덜 끝남\"이다. 설치자가 같은 명령을 직접 이어 돌릴 수\n // 있게 명령을 그대로 낸다 — 메시지에 원인(ETIMEDOUT)만 있으면 무엇을 해야 하는지 모른다.\n const code = (result.error as NodeJS.ErrnoException).code;\n if (code === \"ETIMEDOUT\") {\n return {\n asset,\n ok: false,\n message: `timed out after ${Math.round(timeout / 1000)}s — finish it yourself: ${cmd} ${args.join(\" \")}`,\n };\n }\n return { asset, ok: false, message: result.error.message };\n }\n if ((result.status ?? 1) !== 0) {\n const stderr = (result.stderr ?? \"\").trim();\n const tail = stderr.length > 200 ? `${stderr.slice(0, 200)}…` : stderr;\n return {\n asset,\n ok: false,\n message: `${cmd} exited ${result.status}${tail ? `: ${tail}` : \"\"}`,\n };\n }\n return { asset, ok: true };\n}\n\nfunction spawnOpts(cwd?: string, timeout: number = DEFAULT_SPAWN_TIMEOUT_MS): SpawnOpts {\n return {\n encoding: \"utf8\",\n stdio: \"pipe\",\n timeout,\n ...(cwd ? { cwd } : {}),\n };\n}\n\n/* v8 ignore next 7 — thin dep-inject default. tests 는 항상 spawn 주입. */\nfunction defaultSpawn(\n cmd: string,\n args: ReadonlyArray<string>,\n opts: SpawnOpts,\n): SpawnSyncReturns<string> {\n return spawnSync(cmd, [...args], opts);\n}\n\n/**\n * v26.59.0 — install 후 path 기반 version 추출.\n *\n * 안전 원칙: 실패 시 undefined 반환 (silent). install 성공 자체는 이미 검증됨.\n *\n * - plugin: ~/.claude/plugins/cache/<marketplace>/<plugin>/<VERSION>/ 디렉토리명 (semver-like 만)\n * - npm-global: <npm root -g>/<pkg>/package.json 의 version\n * - skill / npx-run / shell-script: 표준 metadata 위치 없음 → undefined\n */\nfunction detectVersion(\n method: ExternalAssetMethod,\n spawn: NonNullable<ExternalInstallerDeps[\"spawn\"]>,\n): string | undefined {\n try {\n switch (method.kind) {\n case \"plugin\": {\n // pluginId = \"<plugin>@<marketplace-short>\". cache path:\n // ~/.claude/plugins/cache/<marketplace-short>/<plugin>/<VERSION>/\n // method.marketplace 는 GH `<user>/<repo>` (다른 값) 이라 path 에 사용 X.\n const at = method.pluginId.lastIndexOf(\"@\");\n if (at <= 0) return undefined;\n const plugin = method.pluginId.slice(0, at);\n const marketplaceShort = method.pluginId.slice(at + 1);\n const cacheBase = join(homedir(), \".claude/plugins/cache\", marketplaceShort, plugin);\n if (!existsSync(cacheBase)) return undefined;\n const versions = readdirSync(cacheBase)\n .filter((v) => /^\\d/.test(v))\n .sort();\n return versions.at(-1);\n }\n case \"npm\": {\n const npmRoot = getNpmGlobalRoot(spawn);\n if (!npmRoot) return undefined;\n const pkgJson = join(npmRoot, method.pkg, \"package.json\");\n if (!existsSync(pkgJson)) return undefined;\n const parsed = JSON.parse(readFileSync(pkgJson, \"utf8\")) as { version?: string };\n return parsed.version;\n }\n default:\n return undefined;\n }\n } catch {\n return undefined;\n }\n}\n\nlet npmGlobalRootCache: string | undefined;\n\n/* v8 ignore start — npm CLI 실행 + cache. 실 시스템 의존. detectVersion (plugin 외 method) 가 본 함수 호출. */\nfunction getNpmGlobalRoot(spawn: NonNullable<ExternalInstallerDeps[\"spawn\"]>): string | undefined {\n if (npmGlobalRootCache !== undefined) return npmGlobalRootCache || undefined;\n try {\n const r = spawn(\"npm\", [\"root\", \"-g\"], spawnOpts());\n if ((r.status ?? 1) === 0) {\n npmGlobalRootCache = (r.stdout ?? \"\").trim();\n return npmGlobalRootCache || undefined;\n }\n } catch {\n // fallthrough\n }\n npmGlobalRootCache = \"\";\n return undefined;\n}\n/* v8 ignore stop */\n\n/**\n * 누락(skip) 자산 목록을 사용자 보고용 텍스트로 포맷.\n */\nexport function formatSkippedReport(report: ExternalInstallReport): string {\n const failed = report.attempted.filter((r) => !r.ok);\n if (failed.length === 0) return \"\";\n const lines = failed.map((r) => ` • ${r.asset.id} — ${r.message ?? \"failed\"}`);\n return [\n `${failed.length}개 외부 자산이 설치되지 않았습니다 (warn-skip):`,\n ...lines,\n \"\",\n \"Manual install or retry needed. See docs/REFERENCE.md or README.md for details.\",\n ].join(\"\\n\");\n}\n","import { existsSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { anyTrack } from \"./track-match.js\";\nimport type { Track } from \"./types.js\";\n\nexport interface McpServerConfig {\n type?: \"stdio\" | \"http\";\n command: string;\n args: string[];\n env?: Record<string, string>;\n}\n\nexport interface McpJson {\n mcpServers: Record<string, McpServerConfig>;\n _comment?: string;\n}\n\nexport interface TrackMcpRow {\n name: string;\n pattern: string;\n command: string;\n args: string[];\n}\n\n/** Parse `templates/track-mcp-map.tsv` (tab-separated, comment-aware). */\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 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, command, args: args as string[] });\n }\n return rows;\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): McpJson {\n const out: McpJson = {\n ...base,\n mcpServers: { ...base.mcpServers },\n };\n for (const row of rows) {\n if (!anyTrack(tracks, row.pattern)) {\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] = {\n type: \"stdio\",\n command: row.command,\n args: row.args,\n };\n }\n // Strip _comment marker (parity with the bash `jq 'del(._comment)'`).\n delete out._comment;\n return out;\n}\n\n/**\n * Compose the final `.mcp.json` for a project install.\n * Read base template + track map, merge with optional existing user file (additive).\n */\nexport function composeMcpJson(opts: {\n templateMcpPath: string;\n trackMapPath: string;\n existingPath?: string;\n tracks: ReadonlyArray<Track>;\n}): McpJson {\n const base = JSON.parse(readFileSync(opts.templateMcpPath, \"utf8\")) as McpJson;\n const merged =\n opts.existingPath && existsSync(opts.existingPath)\n ? mergeUserBase(base, opts.existingPath)\n : base;\n const mapRaw = existsSync(opts.trackMapPath) ? readFileSync(opts.trackMapPath, \"utf8\") : \"\";\n const rows = parseTrackMcpMap(mapRaw);\n return mergeMcpServers(merged, rows, opts.tracks);\n}\n\nfunction mergeUserBase(base: McpJson, existingPath: string): McpJson {\n try {\n const existing = JSON.parse(readFileSync(existingPath, \"utf8\")) as McpJson;\n return {\n ...base,\n mcpServers: { ...base.mcpServers, ...existing.mcpServers },\n };\n } catch {\n return base;\n }\n}\n\n/** Write the composed `.mcp.json` to disk (2-space pretty). */\nexport function writeMcpJson(path: string, mcp: McpJson): void {\n writeFileSync(path, `${JSON.stringify(mcp, null, 2)}\\n`);\n}\n","/**\n * update-mode.ts — Update / Add / Reinstall router 액션 처리.\n *\n * SPEC: docs/specs/cli-rewrite-completeness.md F5, F6\n * Source: bash setup-harness.sh@911c246~1 L460~573 (update mode 113 LOC)\n *\n * Update 모드 동작:\n * 1. backup: .claude/ → .claude.backup-<timestamp>/\n * 2. update_dir: target에 이미 존재하는 파일만 templates로 덮어쓰기 (Track 혼입 방지)\n * 3. prune_orphans: templates에 없는데 target에 있는 파일 제거 (예: 폐기된 rule)\n * 4. clean_stale_hook_refs: settings.json hook 참조 중 실존 파일 없는 것 제거\n *\n * 보존: .mcp.json (사용자 추가 항목), docs/SPEC.md, settings.local.json\n */\n\nimport {\n copyFileSync,\n existsSync,\n mkdirSync,\n readdirSync,\n readFileSync,\n rmdirSync,\n statSync,\n unlinkSync,\n writeFileSync,\n} from \"node:fs\";\nimport { basename, dirname, join } from \"node:path\";\nimport { isBaselineExcluded } from \"./baseline-targets.js\";\nimport { ALL_CLI_TARGETS, runCliTransforms } from \"./cli-transforms.js\";\nimport {\n CONTINUOUS_SKILLS,\n INTERNAL_BUNDLED_SKILL_IDS,\n RENAMED_SKILL_IDS,\n RETIRED_SKILL_IDS,\n} from \"./external-assets.js\";\nimport { type ExternalSkillRefresh, refreshExternalSkills } from \"./external-installer.js\";\nimport { foreignOwnedTarget, occupiedByNonDirectory } from \"./foreign-slot.js\";\nimport { backupFile, copyDir, listFilesRecursive } from \"./fs-ops.js\";\nimport {\n collectPolicyHashes,\n collectSkillHashes,\n hashContent,\n type InstallLog,\n isHarnessOwned as isOwnedByBaseline,\n mergeExternalFiles,\n POLICY_DIRS,\n readInstallLog,\n writeInstallLog,\n} from \"./install-log.js\";\nimport {\n ALL_RULES,\n type AssetEntry,\n type AssetSpec,\n buildAssetSpec,\n buildManifest,\n RETIRED_AGENT_IDS,\n TRACK_AGENTS,\n} from \"./manifest.js\";\nimport { HARNESS_ANCHOR_FILE, upsertHarnessImport } from \"./project-claude-merge.js\";\nimport { anyTrack } from \"./track-match.js\";\nimport {\n DEFAULT_OPTIONS,\n type InstallSpec,\n TRACKS,\n type Track,\n UPDATE_GROUPS,\n type UpdateGroup,\n} from \"./types.js\";\n\n/**\n * v26.140.0 **이전** 설치본의 앵커 위치 (P5 · ADR-060 이행 대상).\n *\n * 이행 안내에만 쓴다 — 하네스는 더 이상 이 경로에 아무것도 쓰지 않는다.\n */\nconst LEGACY_ANCHOR_FILE = \".claude/CLAUDE.md\";\n\nexport interface UpdateModeReport {\n /** 덮어쓰기된 파일 갯수 (디렉토리별). */\n updated: Record<string, number>;\n /** 제거된 orphan 파일명 목록 (디렉토리별). */\n pruned: Record<string, string[]>;\n /** 제거된 stale hook ref 파일명 목록. */\n staleHookRefs: string[];\n /** 갱신된 CLAUDE.md (true if updated). */\n claudeMdUpdated: boolean;\n /**\n * #480 ③ — 이번 update 가 남긴 백업 쌍(원본 ↔ `*.backup-<time>`, projectDir 상대경로). 화면\n * 마지막 줄과 `.uzys-agent-harness/update-backups.json` 에 낸다 — 설치자가 편집분을 새 판에\n * 다시 얹을 때(audit-harness-fit 에 요청) 그 파일이 증거다. 범용 스킬에 이 하네스의 백업\n * 규약을 심지 않는다(사용자 정정 2026-09-20) — 증거를 내는 쪽이 하네스다.\n */\n backups: Array<{ path: string; backup: string }>;\n /**\n * #480 — 설치자가 고르지 않아 **건드리지 않은** 묶음 (`update --only` · 위저드 체크박스).\n * 화면에 낸다 — 안 보이면 \"update 를 돌렸는데 룰이 그대로다\"가 결함으로 읽힌다.\n */\n skippedGroups: UpdateGroup[];\n /**\n * #480 — 앵커(`CLAUDE-uzys-harness.md`)를 설치자가 고친 상태에서 갱신해 `*.backup-<time>` 을\n * 남겼다. 전에는 기준선 대조 없이 덮어써 편집이 **백업 없이 사라졌다**(컨테이너 실측\n * 2026-09-20) — 룰·스킬은 ADR-046 대로 백업하는데 앵커만 예외였다. 화면에 노출한다.\n */\n anchorBackedUp: boolean;\n /**\n * P5 · ADR-060 이행 — 루트 앵커가 **없어서 이번에 만들었다** (v26.140.0 이전 설치본).\n *\n * `claudeMdUpdated` 와 배타적이다: 갱신은 이미 있던 파일, 생성은 이번에 생긴 파일.\n */\n anchorCreated: boolean;\n /** 이행 중 루트 `CLAUDE.md` 에 앵커 import 줄을 새로 얹었나 (이미 있었으면 false). */\n rootImportAdded: boolean;\n /**\n * ADR-085 — 이미 있던 관리 블록의 **안**이 바뀌었나(상시 스킬 안내가 깔린 스킬을 따라 현행화됐다,\n * 또는 지워진 import 줄이 돌아왔다). 블록이 처음 생긴 경우는 `rootImportAdded` 다.\n */\n rootBlockRefreshed: boolean;\n /**\n * 디스크에 아직 남아 있는 구 앵커 경로(`.claude/CLAUDE.md`). 없으면 null.\n *\n * **지우지 않는다** — 사용자가 그 파일을 고쳤는지 update 시점엔 판정할 수 없다. 대신 화면에\n * \"이제 갱신되지 않는다\"를 알린다. 침묵하면 옛 하네스 내용이 담긴 사본이 계속 상주하는데\n * 사용자는 그게 죽은 사본인 줄 모른다.\n */\n legacyAnchor: string | null;\n /**\n * v26.126.0 (R-3a) — 사용자가 고쳐서 백업본을 남긴 스킬 파일 (`.claude/skills/` 상대경로).\n * 화면에 그대로 노출한다. 안 보이면 사용자는 자기 편집분이 어디 갔는지 알 수 없다.\n */\n skillsBackedUp: string[];\n /**\n * 2026-08-02 (ADR-062) — `.claude/skills/<id>` 가 **디렉터리가 아니라** 건너뛴 스킬 id\n * (심볼릭 링크가 대표적이지만 일반 파일·FIFO 도 들어온다 — #343 에서 술어를 install 과 통일).\n * 필드명의 `Links` 는 도입 당시 범위를 그대로 쓴다 — 개명은 화면·픽스처 표면을 함께 건드린다.\n * 그 자리는 `npx skills add` 등 **다른 도구가 소유**하므로 하네스가 쓰지 않는다.\n * 화면에 남기는 이유는 위와 같다 — 안 보이면 사용자는 \"왜 이 스킬만 안 갱신되지\"를 알 수 없다.\n */\n skillsSkippedLinks: string[];\n /**\n * #477 — 번들에서 사라져 설치본에서 지운 스킬 파일 (`.claude/skills/` 상대경로). 번들 스킬\n * 디렉터리의 파일 목록이 \"최신본이 가져야 할 파일\"의 정의다 — 그 목록에 없는 파일이 남으면\n * 갱신된 SKILL.md 가 가리키지 않는 옛 서식이 디스크에 살아 있다(실측: 26.152→26.153 에서\n * `clear-korean-communication/references/` 3파일). 지우기 전에 기준선과 다르거나 기록이 없는\n * 파일은 `*.backup-<time>` 으로 남긴다(`skillsBackedUp` 에도 든다) — 덮어쓸 때와 같은 잣대다.\n */\n skillsPruned: string[];\n /**\n * v26.132.0 (ADR-047) — 사용자가 고쳐서 백업본을 남긴 정책 파일 (`.claude/` 상대경로).\n * `skillsBackedUp` 과 같은 이유로 화면에 노출한다 — 안 보이면 사용자는 자기 편집분이\n * 어디 갔는지 알 수 없고, 그러면 백업은 있어도 없는 것과 같다.\n */\n policyBackedUp: string[];\n /**\n * v26.134.0 (R-3j-A · ADR-049) — 갱신된 외부 CLI 산출물 수\n * (`.codex/` · `.opencode/` · `.agents/` · `AGENTS.md` · `opencode.json`).\n *\n * 그전까지 update 는 `.claude/` 만 갱신했다. codex/opencode 사용자는 install 이 개선한 룰을\n * `update` 로는 영영 못 받았고, 그 비대칭이 **문서 어디에도 안 적혀 있었다**.\n */\n externalUpdated: number;\n /** 사용자가 고쳐서 백업본을 남긴 외부 CLI 산출물 (projectDir 상대경로). */\n externalBackedUp: string[];\n /**\n * #374 — `npx skills add` 로 깐 **외부 스킬** 중 이번 update 가 상류 최신판으로 다시 받은 수.\n *\n * `externalUpdated` 와 다른 것이다. 위 필드는 **우리가 렌더한** CLI 산출물이고 이쪽은\n * **남의 저장소에서 받아온 스킬 본문**이다. 둘을 한 행으로 합치지 않는 이유는 이 결함이\n * 정확히 그 혼동이었기 때문이다 — 화면의 `external CLI artifacts` 를 보고 스킬도 갱신된 줄\n * 알았지만 그쪽은 한 번도 안 돌았다.\n */\n externalSkillsRefreshed: number;\n /**\n * 갱신에 실패한 외부 스킬. **실패해도 update 는 계속한다** — 네트워크 하나 때문에 정책 파일\n * 갱신까지 잃는 것은 사용자에게 더 나쁘다. 대신 침묵하지 않는다(화면에 이름을 낸다).\n */\n externalSkillsFailed: ReadonlyArray<{ id: string; message: string }>;\n /**\n * 설치 기록에는 있는데 **지금 카탈로그에 없는** 스킬 자산 id. 갱신 대상이 아니라는 사실을\n * 화면에 낸다 — 침묵하면 사용자는 깔린 것 중 일부만 갱신됐다는 사실을 알 수 없다.\n */\n externalSkillsNotInCatalog: ReadonlyArray<string>;\n /**\n * 설치 기록이 없어 **무엇을 갱신해야 하는지 판정할 수 없었다** (레거시 설치본).\n *\n * `externalSkillsRefreshed: 0` 과 구분한다 — 그쪽은 \"갱신할 게 없다\"이고 이쪽은 \"모른다\"다.\n * 둘을 같은 침묵으로 합치면 이 이슈가 고치려던 실패 형태가 그대로 남는다(독립 리뷰 MEDIUM-1).\n */\n externalSkillsUnknown: boolean;\n /**\n * #343 — 다른 도구가 소유한 자리라 **쓰지 않은** 경로. 출처를 나누지 않는다 —\n * 외부 CLI 산출물(`.agents/skills/<id>`)이든 이번 릴리즈 신규 자산이든, 사용자에게\n * 필요한 것은 \"어느 자리를 옮겨야 하는가\" 하나다. `skillsSkippedLinks` 는 스킬 id 를\n * 내는 기존 행이라 그대로 두고, 이쪽은 경로를 낸다.\n */\n foreignOwned: string[];\n /**\n * 이번 update 가 **새로 깔아 준** 자산 (projectDir 상대경로) — #283.\n *\n * 갱신(`updated`)과 배타적이다: 갱신은 이미 있던 파일, 이쪽은 릴리즈로 새로 생겨서\n * 이 설치본에 아직 없던 파일이다. 화면에 남기는 이유는 `anchorCreated` 와 같다 — 조용히\n * 늘어난 파일은 사용자가 자기 것으로 오인한다.\n */\n installedNew: string[];\n /**\n * **전에 깔아 줬는데 디스크에 없어서 다시 깐** 자산 (projectDir 상대경로).\n *\n * `installedNew` 와 가르는 이유는 원인이 다르기 때문이다 — 이쪽은 사용자가 지웠거나 유실된\n * 것이다. 둘을 한 문구(\"이번 릴리즈에 추가됨\")로 보고하면 한쪽에는 거짓이 되고, 자기가 지운\n * 파일이 왜 돌아왔는지 추적할 수 없다. 판별 신호는 install log 의 `policyFiles` 기준선이다.\n */\n restored: string[];\n /**\n * 이 릴리즈에 새로 생겼지만 **update 가 깔 수 없는** 자산 (projectDir 상대경로).\n *\n * 훅과 `settings.json` 이 그쪽이다 — 훅은 `settings.json` 의 배선이 있어야 발화하는데 update 는\n * 그 파일을 동기화하지 않는다(죽은 참조를 지울 뿐이다). 파일만 놓고 \"추가됨\"이라 보고하면\n * 안 도는 기능을 받았다고 읽히므로(거짓출하), 깔지 않고 **재설치가 필요하다는 사실을 낸다.**\n */\n needsReinstall: string[];\n /**\n * ADR-089 (#445) — `.claude/agents/` 에 남아 있는 **은퇴한** 에이전트 id.\n *\n * **지우지 않는다** — 사용자가 그 파일을 고쳤는지 update 시점엔 판정할 수 없다(`legacyAnchor`\n * 와 같은 규율). 대신 화면이 \"이 릴리즈에서 은퇴했고 벤더 기본 기능이 같은 일을 한다\"를 낸다.\n * 침묵하면 옛 릴리즈로 깐 설치본은 죽은 에이전트 descriptor 를 매 세션 상주시키면서 그 사실을\n * 영영 모른다.\n */\n retiredAgents: string[];\n /**\n * ADR-090 (#452 · #458) — 이 설치본의 트랙에서 **빠진**(강등된) 에이전트 id.\n *\n * 은퇴(`retiredAgents`)와 가르는 이유는 사실이 다르기 때문이다: 은퇴한 것은 아무에게도 안 가고,\n * 강등된 것은 **다른 트랙에는 여전히 간다**. 한 문구로 뭉치면 data 트랙 설치자가 자기 자산을\n * 없어진 것으로 읽는다. 그래서 화면이 \"어느 트랙 전용인지\"를 함께 말한다.\n *\n * 은퇴와 같은 것: **지우지 않는다**(ADR-046 · 사용자 결정 2026-09-14). 그 파일을 사용자가\n * 고쳤는지 update 시점엔 판정할 수 없다. `pruneOrphans` 도 못 지운다 — 템플릿에 원본이\n * 그대로 있어 orphan 이 아니다. 말하고 손은 사용자가.\n */\n demotedAgents: string[];\n /**\n * 2026-08-16 (ADR-072) — 이번 update 가 물러낸 `.mcp-allowlist` 의 **백업 경로**. 없었으면 null.\n *\n * 이 파일은 `.claude/` 밖(프로젝트 루트)이라 `pruneOrphans` 의 사정거리에 없다. 읽던 훅\n * (`mcp-pre-exec.sh`)은 `hooks` 가 `POLICY_DIRS` 에 있어 자동으로 물러나는데, 이쪽만 남으면\n * **아무도 안 읽는 파일이 사용자 저장소에 영구히 남는다.** 지우기 전에 백업하는 이유는\n * 자동 생성물이라도 사용자가 서버를 주석 처리해 자기 정책을 적어 넣는 파일이었기 때문이다\n * — 그건 사용자 것이다.\n */\n mcpAllowlistRetired: string | null;\n}\n\n/**\n * Update 진입점이 쓰는 InstallSpec — **위저드와 `update` 명령이 공유한다.**\n *\n * `cli`/`options` 는 타입을 채우기 위한 값이라 어느 진입점이든 같아야 하고, 두 곳에서 각자\n * 리터럴로 쓰면 한쪽만 바뀌었을 때 조용히 갈린다 — 이 repo 가 반복해서 당한 실패 모드라\n * 처음부터 한 곳에 둔다.\n *\n * **`scope` 는 설치 기록에서 읽는다** (#374, 독립 리뷰 HIGH-1). update 는 이제 `.claude/` 만\n * 건드리지 않는다 — ADR-049 이후 `.agents/`·`.codex/` 를, #374 이후 **글로벌 설치본에서는\n * `~/.claude/` 까지** 갱신한다. 그런데 여기서 스코프를 안 실으면 화면 헤더가 렌더러 기본값인\n * *\"Project — current directory only (no global write)\"* 를 찍는다. 홈에 쓰면서 안 쓴다고\n * 적는 것은 이 저장소가 반복해서 당한 거짓출하 그 형태다.\n */\nexport function buildUpdateSpec(\n projectDir: string,\n tracks: ReadonlyArray<Track>,\n only?: ReadonlyArray<UpdateGroup>,\n): InstallSpec {\n return {\n tracks: [...tracks],\n options: DEFAULT_OPTIONS,\n cli: [\"claude\"],\n projectDir,\n scope: readInstallLog(projectDir)?.scope ?? \"project\",\n // 전부 골랐으면 \"제한 없음\"과 같다 — 화면·기록에 제한이 있었던 것처럼 남기지 않는다.\n ...(only !== undefined && only.length > 0 && only.length < UPDATE_GROUPS.length\n ? { updateOnly: [...only] }\n : {}),\n };\n}\n\n/**\n * `--only <group>` 값 검증 (#480). 모르는 이름은 조용히 무시하지 않는다 — 오타 하나로 \"전부\n * 갱신\"이 되면 설치자가 룰을 안 건드리려고 쓴 플래그가 정반대로 동작한다.\n */\nexport function parseUpdateOnly(\n raw: ReadonlyArray<string>,\n): { ok: true; groups: UpdateGroup[] } | { ok: false; invalid: string[] } {\n const invalid = raw.filter((v) => !(UPDATE_GROUPS as ReadonlyArray<string>).includes(v));\n if (invalid.length > 0) return { ok: false, invalid };\n return { ok: true, groups: [...new Set(raw as UpdateGroup[])] };\n}\n\n/**\n * `runUpdateMode` 주입점. 기본은 실 구현 — 테스트만 바꿔 끼운다.\n *\n * 인터페이스를 함수 JSDoc **앞**에 두는 이유: 사이에 끼우면 함수 문서 블록이 함수에 안 붙는다.\n */\nexport interface UpdateModeDeps {\n /**\n * #374 — 외부 스킬 갱신. 기본은 `refreshExternalSkills`.\n * 테스트가 네트워크를 타지 않게 하는 유일한 지점이다.\n */\n refreshSkills?: (projectDir: string) => ExternalSkillRefresh;\n}\n\n/**\n * Update mode 메인 — backup은 caller가 별도 처리.\n *\n * @param projectDir 대상 프로젝트 root\n * @param templatesDir templates/ 디렉토리 (sync source)\n * @param harnessRoot harness repo root — 외부 CLI transform 의 렌더 소스.\n *\n * `templatesDir` 에서 파생시킬 수도 있지만 **required 인자로 받는다**: 옵셔널이면 안 넘긴\n * 호출부만 조용히 외부 CLI 갱신을 건너뛰고, 그건 지금 고치고 있는 바로 그 버그다\n * (ADR-048 의 `baseline` 을 required 로 둔 것과 같은 이유).\n */\nexport function runUpdateMode(\n projectDir: string,\n templatesDir: string,\n harnessRoot: string,\n deps: UpdateModeDeps = {},\n only?: ReadonlyArray<UpdateGroup>,\n): UpdateModeReport {\n const claudeDir = join(projectDir, \".claude\");\n const startedAt = Date.now();\n // #480 — 고른 묶음만 돈다. 안 고르면(undefined) 전부 — 기존 호출부의 동작 그대로.\n const wants = (g: UpdateGroup): boolean => only === undefined || only.includes(g);\n const report: UpdateModeReport = {\n updated: {},\n pruned: {},\n staleHookRefs: [],\n claudeMdUpdated: false,\n backups: [],\n skippedGroups: only === undefined ? [] : UPDATE_GROUPS.filter((g) => !only.includes(g)),\n anchorBackedUp: false,\n anchorCreated: false,\n rootImportAdded: false,\n rootBlockRefreshed: false,\n legacyAnchor: null,\n skillsBackedUp: [],\n skillsSkippedLinks: [],\n skillsPruned: [],\n policyBackedUp: [],\n externalUpdated: 0,\n externalBackedUp: [],\n externalSkillsRefreshed: 0,\n externalSkillsFailed: [],\n externalSkillsNotInCatalog: [],\n externalSkillsUnknown: false,\n foreignOwned: [],\n installedNew: [],\n restored: [],\n needsReinstall: [],\n retiredAgents: [],\n demotedAgents: [],\n mcpAllowlistRetired: null,\n };\n\n // 0) 릴리즈로 **새로 생긴** 자산 설치 (#283). 정책 동기화보다 먼저 도는 이유는\n // `refreshPolicyBaseline` 이 아래에서 기준선을 다시 찍기 때문이다 — 순서를 뒤집으면 방금 깐\n // 파일이 기준선에 없어 다음 update 가 \"사용자가 만든 파일\"로 오판한다.\n const fresh = installNewAssets(projectDir, templatesDir, wants);\n report.installedNew = fresh.installed;\n report.restored = fresh.restored;\n report.needsReinstall = fresh.needsReinstall;\n\n // 1) 정책 디렉터리 동기화 — 대상 목록은 POLICY_DIRS 가 SSOT (install-log.ts).\n // v26.132.0 (ADR-047) — 사용자 편집분 판정이 붙었다. 기준선은 install log 의 policyFiles.\n const policyBase = policyBaseline(projectDir);\n for (const { dir, ext } of POLICY_DIRS) {\n if (!wants(dir === \"hooks\" ? \"hooks\" : \"rules\")) continue;\n const target = join(claudeDir, dir);\n const source = join(templatesDir, dir);\n const label = `.claude/${dir}`;\n const ctx = { prefix: dir, baseline: policyBase };\n const synced = updateDir(target, source, ext, ctx);\n report.updated[label] = synced.updated;\n report.policyBackedUp.push(...synced.backedUp);\n report.pruned[label] = pruneOrphans(target, source, ext, ctx);\n }\n // 기준선은 **동기화한 디렉터리만** 다시 찍는다 — 건너뛴 디렉터리의 사용자 편집을 지금 디스크\n // 그대로 \"하네스가 놓아둔 것\"으로 기록하면 다음 update 가 백업 없이 덮어쓴다(리뷰 ⓒ). 반대로\n // 동기화한 디렉터리를 안 찍으면 다음 전체 update 가 방금 놓은 최신판을 \"편집분\"으로 백업한다\n // (리뷰 N-1 실측). 그래서 전부-아니면-전무가 아니라 디렉터리 단위다.\n const syncedDirs = POLICY_DIRS.filter((d) => wants(d.dir === \"hooks\" ? \"hooks\" : \"rules\")).map(\n (d) => d.dir,\n );\n if (syncedDirs.length > 0) refreshPolicyBaseline(projectDir, templatesDir, syncedDirs);\n\n // 1.5) `.claude/skills/` — v26.126.0 (R-3a · ADR-046).\n // 위 4개와 달리 스킬은 디렉터리 단위라 재귀가 필요하고, 사용자 편집분 판정이 붙는다.\n const skillSync = wants(\"skills\")\n ? syncSkills(\n join(claudeDir, \"skills\"),\n join(templatesDir, \"skills\"),\n skillBaseline(projectDir),\n new Date(),\n (relInSkills) => foreignOwnedTarget(projectDir, `.claude/skills/${relInSkills}`),\n )\n : { updated: 0, backedUp: [], skippedLinks: [], foreignOwned: [], pruned: [] };\n if (wants(\"skills\")) report.updated[\".claude/skills\"] = skillSync.updated;\n report.skillsBackedUp = skillSync.backedUp;\n report.skillsSkippedLinks = skillSync.skippedLinks;\n report.skillsPruned = skillSync.pruned;\n // #480 ① — 릴리즈로 새로 생긴 번들 스킬을 깐다. `syncSkills` 는 이미 깔린 것만 다루고\n // `installNewAssets` 는 파일 자산만 다뤄, 기존 설치본은 새 스킬을 영영 못 받았다.\n const newSkills = wants(\"new-skills\")\n ? installNewSkillDirs(projectDir, templatesDir, installedTracks(projectDir))\n : { installed: [], foreignOwned: [] };\n report.installedNew.push(...newSkills.installed);\n if (wants(\"skills\")) refreshSkillBaseline(projectDir, templatesDir);\n else if (newSkills.installed.length > 0)\n recordNewSkillBaseline(projectDir, templatesDir, newSkills.installed);\n\n // 2) 하네스 앵커 (프로젝트 루트 `CLAUDE-uzys-harness.md` — P5 · ADR-060).\n //\n // **claude 를 고른 설치에서만** 돈다. 앵커 파일과 루트 `CLAUDE.md` 의 `@import` 한 줄은\n // Claude Code 전용 로딩 경로이고, 다른 CLI 사용자는 같은 내용을 `AGENTS.md` ·\n // `.agents/rules/` 로 이미 받는다 — 만들면 중복이고, 루트 `CLAUDE.md` 는 **사용자 소유\n // 이름**이라 고른 적 없는 CLI 때문에 사용자 파일이 수정된다. install 은 이 조건을 이미\n // 지키는데(`.claude/` baseline 게이트) update 만 무조건 돌고 있었다 — 비 Claude 단독\n // 설치가 update 에 도달할 수 있게 되면서 드러난 비대칭이다(독립 재검증 HIGH-1).\n //\n // 증거는 **둘을 함께** 본다. `.claude/` 존재만으로 판정하면 비 Claude 설치에 어떤 이유로든\n // `.claude/` 가 있을 때 같은 증상이 돌아오고(사용자가 만든 디렉터리 · 이전 설치의 잔재),\n // install log 만으로 판정하면 **로그가 없는 레거시 설치본**이 claude 미설치로 오판돼 앵커\n // 이행 자체가 죽는다(그 이행이 이 함수의 원래 목적이다 — 로그 단독 판정은 update-mode\n // 테스트 6건이 red 로 잡았다). 그래서 디렉터리가 있고, **로그가 claude 를 말하거나 로그가\n // 아예 없을 때**만 돈다. 로그가 있는데 claude 가 없다 = 명시적으로 안 고른 것이다.\n const installLog = readInstallLog(projectDir);\n if (existsSync(claudeDir) && (installLog === null || installLog.spec.cli.includes(\"claude\"))) {\n if (wants(\"anchor\")) syncHarnessAnchor(projectDir, templatesDir, report);\n }\n\n // 3) settings.json stale hook ref cleanup\n const settingsPath = join(claudeDir, \"settings.json\");\n if (wants(\"hooks\") && existsSync(settingsPath)) {\n report.staleHookRefs = cleanStaleHookRefs(settingsPath, claudeDir);\n }\n\n // 3.5) `.mcp-allowlist` 회수 (ADR-072). 3) 바로 뒤인 이유는 같은 은퇴의 나머지 절반이기\n // 때문이다 — 위가 배선을 지우고 여기가 그 배선이 읽던 데이터를 지운다.\n if (wants(\"hooks\")) report.mcpAllowlistRetired = retireMcpAllowlist(projectDir);\n\n // 3.6) 은퇴한 에이전트 (ADR-089 · #445). 바로 위 두 절과 같은 축이다 — **더는 갱신되지 않는\n // 것이 디스크에 남아 있다**. 다만 지우는 것은 위 둘과 다르다: `.mcp-allowlist` 는 우리가\n // 만든 자동 생성물이라 회수했지만, 에이전트 파일은 사용자가 고쳤을 수 있고 update 시점에\n // 그것을 판정할 기준선이 없다. 그래서 말만 한다.\n report.retiredAgents = staleAgentFiles(claudeDir);\n // 3.7) 트랙에서 **강등된** 에이전트 (ADR-090 · #458). 같은 자리에 두는 이유는 사용자가 할 일이\n // 같아서다(지워도 된다). 판정 근거만 다르다 — 은퇴는 목록, 강등은 **이 설치본의 트랙**이다.\n report.demotedAgents = demotedAgentFiles(claudeDir, installedTracks(projectDir));\n\n // 4) 외부 CLI 산출물 — v26.134.0 (R-3j-A · ADR-049).\n // install 과 **같은 함수**를 refresh 모드로 부른다. 여기서 transform 을 따로 부르면\n // 기준선을 잇는 규칙이 두 벌이 되고, 그게 ADR-046~048 을 세 번 반복하게 만든 구조다.\n const external = wants(\"external\")\n ? refreshExternalCli(projectDir, harnessRoot)\n : { externalUpdated: 0, externalBackedUp: [], externalForeignOwned: [] };\n report.externalUpdated = external.externalUpdated;\n report.externalBackedUp = external.externalBackedUp;\n // 한 자리를 두 행이 말하지 않게 한다 — 위 `skillsSkippedLinks` 행이 이미 낸 슬롯은 뺀다.\n // 사용자는 두 줄을 서로 다른 두 사건으로 읽는다(리뷰 3라운드 지적).\n const saidBySlotRow = new Set(report.skillsSkippedLinks.map((id) => `.claude/skills/${id}`));\n const merged: string[] = [];\n for (const f of [\n ...skillSync.foreignOwned,\n ...fresh.foreignOwned,\n ...newSkills.foreignOwned,\n ...external.externalForeignOwned,\n ]) {\n if (!saidBySlotRow.has(f) && !merged.includes(f)) merged.push(f);\n }\n report.foreignOwned = merged;\n\n // 5) 외부 스킬 (#374). 4) 는 **우리가 렌더한** 산출물만 새로 쓴다 — `npx skills add` 로 깐\n // 스킬 본문은 그 경로에 없어서 update 를 몇 번 돌려도 첫 설치 판본 그대로였다.\n // 실패해도 여기서 멈추지 않는다(위 필드 주석의 사유). 화면 행은 install-render 가 낸다.\n const skillRefresh = wants(\"external\")\n ? (deps.refreshSkills ?? refreshExternalSkills)(projectDir)\n : { attempted: 0, refreshed: 0, failed: [], notInCatalog: [], unknown: false };\n report.externalSkillsRefreshed = skillRefresh.refreshed;\n report.externalSkillsFailed = skillRefresh.failed;\n // 개명·은퇴한 스킬은 **디스크에서** 찾는다. 위 refresh 는 `method: \"skill\"`(npx 외부 스킬)만\n // 훑으므로 번들 스킬(`method: \"internal\"`)은 기록에 그 부류로 남지 않는다 — 그쪽만 보면\n // 옛 이름의 디렉터리를 그대로 들고 있는 설치자에게 화면이 한 마디도 안 한다.\n // 지우지는 않는다(ADR-046 — 스킬 디렉터리 안에는 사용자 파일이 섞인다). 말해 주고 손은 사용자가.\n report.externalSkillsNotInCatalog = [\n ...skillRefresh.notInCatalog,\n ...staleSkillDirs(claudeDir).filter((id) => !skillRefresh.notInCatalog.includes(id)),\n ];\n report.externalSkillsUnknown = skillRefresh.unknown;\n\n report.backups = collectRunBackups(projectDir, startedAt);\n writeBackupList(projectDir, report.backups);\n return report;\n}\n\n/**\n * `.claude/skills/` 에 남아 있는 **개명·은퇴한** 스킬 id. 화면 문구는 `install-render` 가 가른다.\n *\n * 열거하지 않는다 — 카탈로그의 `RENAMED_SKILL_IDS`·`RETIRED_SKILL_IDS` 를 읽는다. 여기에 이름을\n * 적으면 다음 개명에서 이 파일이 조용히 뒤처진다.\n */\nfunction staleSkillDirs(claudeDir: string): string[] {\n const dir = join(claudeDir, \"skills\");\n if (!existsSync(dir)) return [];\n const stale = (id: string): boolean =>\n RENAMED_SKILL_IDS[id] !== undefined || RETIRED_SKILL_IDS.includes(id);\n return readdirSync(dir, { withFileTypes: true })\n .filter((e) => e.isDirectory() && stale(e.name))\n .map((e) => e.name);\n}\n\n/**\n * `.claude/agents/` 에 남아 있는 **은퇴한** 에이전트 id (ADR-089 · #445). 문구는 `install-render`.\n *\n * `staleSkillDirs` 와 같은 형태로 **디스크를 본다** — manifest 에서 사라진 자산은 설치 기록이나\n * 카탈로그를 훑어서는 안 보인다. 열거하지 않는 것도 같다: 배선 SSOT 의 `RETIRED_AGENT_IDS` 를\n * 읽으므로 다음 은퇴에서 이 함수가 뒤처지지 않는다.\n */\nfunction staleAgentFiles(claudeDir: string): string[] {\n const dir = join(claudeDir, \"agents\");\n if (!existsSync(dir)) return [];\n return readdirSync(dir, { withFileTypes: true })\n .filter((e) => e.isFile() && e.name.endsWith(\".md\"))\n .map((e) => basename(e.name, \".md\"))\n .filter((id) => RETIRED_AGENT_IDS.includes(id));\n}\n\n/**\n * `.claude/agents/` 에 남아 있는 **이 트랙에서 강등된** 에이전트 id (ADR-090 · #458).\n *\n * `staleAgentFiles` 와 같은 형태로 디스크를 보되, 판정은 **배선 SSOT 의 `TRACK_AGENTS` 패턴을\n * 이 설치본의 트랙으로 `anyTrack` 한 결과**다. id 를 여기 적지 않는 이유는 은퇴 쪽과 같다 —\n * 다음 강등에서 이 함수가 조용히 뒤처진다.\n *\n * **트랙을 모르면 아무 말도 하지 않는다.** install log 가 없는 레거시 설치본이 그쪽이고, 거기서\n * 추측으로 \"지워도 된다\"고 말하면 실제로 그 트랙의 자산인 파일을 지우게 만든다.\n */\nfunction demotedAgentFiles(claudeDir: string, tracks: ReadonlyArray<Track>): string[] {\n if (tracks.length === 0) return [];\n const dir = join(claudeDir, \"agents\");\n if (!existsSync(dir)) return [];\n const demoted = TRACK_AGENTS.filter(([, pattern]) => !anyTrack(tracks, pattern)).map(\n ([id]) => id,\n );\n return readdirSync(dir, { withFileTypes: true })\n .filter((e) => e.isFile() && e.name.endsWith(\".md\"))\n .map((e) => basename(e.name, \".md\"))\n .filter((id) => demoted.includes(id));\n}\n\n/**\n * 릴리즈로 **새로 생긴** 자산을 설치한다 (#283).\n *\n * update 의 기본 규율은 \"이미 있는 것만 갱신\"이고(`updateDir` Track 혼입 방지), 그 규율에는\n * 반대편 구멍이 있었다: 릴리즈가 자산을 **추가하면** 기존 설치본은 update 를 몇 번 돌려도 그\n * 파일을 영영 못 받는다. 실제로 `.uzys-agent-harness/` 의 두 스크립트가 그 자리였고, 배포판\n * 룰(`ship-checklist`·`doc-governance`)이 그 경로를 호출하라고 적는 동안 파일은 없었다 —\n * 훅·에이전트·룰도 같은 경로로 새로 추가되므로 증상은 스크립트에만 있는 것이 아니다.\n *\n * **무엇을 깔지는 manifest 가 SSOT** 다. 여기에 파일 목록을 적으면 그게 두 번째 사본이 되어\n * 다음에 추가되는 자산이 똑같이 빠진다 (이 repo 가 반복해서 당한 열거-사본 실패 모드).\n *\n * **이미 있는 파일은 건드리지 않는다** — 갱신은 `updateDir`·`syncSkills` 의 몫이고 그쪽은\n * 사용자 편집분 판정과 백업을 한다. 여기서 덮어쓰면 그 판정을 우회하게 된다.\n *\n * **`.claude/` 는 claude 를 고른 설치본에만 넣는다.** install 은 `spec.cli` 에 claude 가 있을 때만\n * 그 baseline 을 만든다(`installer.ts` — codex/opencode 단독 사용자의 dead weight 회피). update 에\n * 그 술어가 없으면 codex 전용 설치본에 `.claude/` 하네스가 통째로 들어가고, 그중 `settings.json`\n * 은 훅 배선과 외부 실행까지 딸려 온다 — 고른 적 없는 CLI 의 설정이다.\n *\n * **훅과 settings.json 은 대상이 아니다.** 훅은 파일만으로는 안 돈다: `settings.json` 의 배선이\n * 있어야 발화하는데 update 는 그 파일을 동기화하지 않는다(`cleanStaleHookRefs` 로 죽은 참조를\n * 지울 뿐이다). 깔아 놓고 \"추가됨\"이라 보고하면 사용자는 안 도는 기능을 받았다고 읽는다 —\n * `hook-wiring-parity` 가 templates 쪽에서 막는 바로 그 상태를 update 가 만드는 셈이다.\n * 대신 **재설치가 필요하다고 알린다**(`needsReinstall`).\n *\n * **사용자가 해제한 자산은 다시 깔지 않는다** (ADR-074). 이 목록을 안 보면 이 함수가 곧\n * 해제의 취소 장치가 된다 — 트랙에서 manifest 를 다시 유도할 뿐이라 뺀 룰·에이전트가 돌아오고,\n * 화면은 그것을 *\"added by this release\"* 로 보고한다(같은 릴리즈에서 방금 설치한 파일인데도).\n * 게다가 되살아나는 종류가 룰·에이전트뿐이라(스킬은 `dir` 엔트리, 훅은 `needsReinstall`)\n * 사용자는 \"해제\"가 무슨 뜻인지 모델을 세울 수조차 없다.\n */\nfunction installNewAssets(\n projectDir: string,\n templatesDir: string,\n wants: (g: UpdateGroup) => boolean = () => true,\n): {\n installed: string[];\n restored: string[];\n needsReinstall: string[];\n foreignOwned: string[];\n} {\n const installed: string[] = [];\n const restored: string[] = [];\n const needsReinstall: string[] = [];\n const foreignOwned: string[] = [];\n const log = readInstallLog(projectDir);\n // 기록이 없으면 `.claude/` 를 건드리지 않는다 — 고르지 않은 CLI 의 자산을 들이는 쪽이\n // 안 깔아 주는 쪽보다 비싸다. CLI 중립 자산(`.uzys-agent-harness/`)은 그대로 대상이다.\n const claudeSelected = log?.spec.cli.includes(\"claude\") ?? false;\n const baselineExcluded = new Set(log?.spec.baselineExclude ?? []);\n // 전에 깔아 준 적이 있는가 — \"이번 릴리즈 신규\"와 \"사용자가 지운 것\"을 가르는 유일한 신호다.\n // 디스크만 보면 둘이 같아 보이고, 그 둘을 한 문구로 보고하면 한쪽에는 거짓말이 된다.\n const priorBaseline = policyBaseline(projectDir);\n\n for (const entry of trackOnlyFileAssets(installedTracks(projectDir))) {\n // #480 — 파일 자산의 묶음: 훅·settings 는 \"hooks\", 나머지(룰·에이전트·명령·스크립트)는 \"rules\".\n const group: UpdateGroup =\n entry.target.startsWith(\".claude/hooks/\") || entry.target === \".claude/settings.json\"\n ? \"hooks\"\n : \"rules\";\n if (!wants(group)) continue;\n // 하네스 앵커는 제외 — `syncHarnessAnchor` 가 **이행 로직과 함께** 소유한다. 여기서 먼저\n // 만들면 그쪽이 \"이미 있었다\"로 보고 루트 `CLAUDE.md` 의 import 줄을 얹지 않아, ADR-060\n // 이행이 조용히 반쪽이 된다.\n if (entry.target === HARNESS_ANCHOR_FILE) continue;\n // 설치 때 해제한 자산 — 없는 것이 사용자가 원한 상태다. 여기서 깔면 해제가 취소된다.\n if (isBaselineExcluded(entry.target, baselineExcluded)) continue;\n\n const target = join(projectDir, entry.target);\n // #343 — **existsSync 보다 먼저** 본다. 슬롯이 남의 저장소로의 링크면 그 저장소에 그 파일이\n // 없을 때 existsSync 가 false 를 내고, 아래 복사가 **남의 저장소 안에 우리 파일을 새로\n // 만든다**. 그 상태에서 화면은 같은 자리를 \"추가했다\"(이 함수)와 \"안 건드렸다\"(syncSkills)로\n // 동시에 말한다. 슬롯이 깨진 링크면 `mkdirSync` 가 ENOENT 로 죽어 update 자체가 끝난다.\n const foreign = foreignOwnedTarget(projectDir, entry.target);\n if (foreign !== null) {\n if (!foreignOwned.includes(foreign)) foreignOwned.push(foreign);\n continue;\n }\n if (existsSync(target)) continue;\n const source = join(templatesDir, entry.source);\n if (!existsSync(source)) continue;\n\n if (entry.target.startsWith(\".claude/\")) {\n if (!claudeSelected) continue;\n // 배선이 있어야 사는 자산 — 파일만 놓으면 침묵하는 거짓 설치가 된다.\n if (entry.target.startsWith(\".claude/hooks/\") || entry.target === \".claude/settings.json\") {\n needsReinstall.push(entry.target);\n continue;\n }\n }\n\n mkdirSync(dirname(target), { recursive: true });\n copyFileSync(source, target);\n // `policyFiles` 키는 `.claude/` 상대다 (`rules/git-policy.md`).\n const recordedAs = entry.target.startsWith(\".claude/\") ? entry.target.slice(8) : entry.target;\n if (priorBaseline.has(recordedAs)) restored.push(entry.target);\n else installed.push(entry.target);\n }\n return { installed, restored, needsReinstall, foreignOwned };\n}\n\n/** `backupFile` 이 만드는 이름 — `<원본>.backup-<YYYYMMDDTHHMMSS>[-n]` (`fs-ops.ts` claimBackupPath). */\nconst BACKUP_SUFFIX = /\\.backup-\\d{8}T\\d{6}(-\\d+)?$/;\n\n/** 백업이 생길 수 있는 자리 — 하네스가 쓰는 디렉터리 + 루트의 앵커·AGENTS.md. */\nconst BACKUP_SCAN_DIRS = [\".claude\", \".codex\", \".opencode\", \".agents\"] as const;\n\n/**\n * #480 ③ — 이번 실행이 남긴 백업 쌍. 만든 자리마다 경로를 모아 올리지 않고 **디스크를 본다**:\n * 백업을 만드는 함수가 넷(룰·스킬·앵커·외부 산출물)이라 하나가 빠지면 목록이 조용히 거짓이 된다.\n * 판정은 이름 규칙 + 시작 시각 이후의 mtime.\n */\nexport function collectRunBackups(\n projectDir: string,\n startedAt: number,\n): Array<{ path: string; backup: string }> {\n const out: Array<{ path: string; backup: string }> = [];\n const consider = (rel: string): void => {\n if (!BACKUP_SUFFIX.test(rel)) return;\n const abs = join(projectDir, rel);\n if (statSync(abs).mtimeMs < startedAt - 1000) return;\n out.push({ path: rel.replace(BACKUP_SUFFIX, \"\"), backup: rel });\n };\n for (const e of readdirSync(projectDir, { withFileTypes: true }))\n if (e.isFile()) consider(e.name);\n for (const dir of BACKUP_SCAN_DIRS) {\n for (const rel of listFilesRecursive(join(projectDir, dir))) consider(`${dir}/${rel}`);\n }\n return out.sort((a, b) => a.path.localeCompare(b.path));\n}\n\n/**\n * `.uzys-agent-harness/update-backups.json` — 이번 실행의 백업 목록. 백업이 없으면 지운다\n * (옛 목록이 남으면 \"지금도 백업이 있다\"로 읽힌다). 설치 기록 디렉터리가 없으면 만들지 않는다.\n */\nfunction writeBackupList(\n projectDir: string,\n backups: ReadonlyArray<{ path: string; backup: string }>,\n): void {\n const dir = join(projectDir, \".uzys-agent-harness\");\n const file = join(dir, \"update-backups.json\");\n try {\n if (backups.length === 0) {\n if (existsSync(file)) unlinkSync(file);\n return;\n }\n if (!existsSync(dir)) return;\n writeFileSync(\n file,\n `${JSON.stringify({ updatedAt: new Date().toISOString(), note: \"update 가 남긴 백업 쌍. 원본(path)은 최신 번들판, 백업(backup)은 당신의 편집분. 편집을 새 판에 다시 얹으려면 두 파일의 diff 를 보라.\", backups }, null, 2)}\\n`,\n );\n } catch {\n // 목록 기록 실패가 update 자체를 실패시키지는 않는다.\n }\n}\n\n/**\n * #480 ① — 릴리즈로 새로 생긴 **번들 스킬 디렉터리**를 깐다. 대상 = 이 트랙 구성이 기본\n * 옵션으로 설치했을 스킬(`buildAssetSpec` 의 기본 선택 — opt-in 스킬은 안 들인다) 중 디스크에\n * 없는 것. 설치 때 해제한 것(ADR-074)·남의 자리(#343)·이미 있는 것은 건너뛴다.\n */\nfunction installNewSkillDirs(\n projectDir: string,\n templatesDir: string,\n tracks: ReadonlyArray<Track>,\n): { installed: string[]; foreignOwned: string[] } {\n const installed: string[] = [];\n const foreignOwned: string[] = [];\n const log = readInstallLog(projectDir);\n const excluded = new Set(log?.spec.baselineExclude ?? []);\n if (!(log?.spec.cli ?? [\"claude\"]).includes(\"claude\")) return { installed, foreignOwned };\n const spec = buildAssetSpec({ tracks, options: DEFAULT_OPTIONS });\n for (const entry of buildManifest(spec)) {\n if (entry.type !== \"dir\" || !entry.target.startsWith(\".claude/skills/\")) continue;\n // ADR-074 — 설치 때 해제한 스킬은 다시 들이지 않는다. 판정은 파일 자산과 같은 헬퍼로\n // (`tests/baseline-targets.test.ts` F1 이 이 줄이 빠졌던 것을 잡았다 — python-patterns 부활).\n if (!entry.applies(spec) || isBaselineExcluded(entry.target, excluded)) continue;\n const target = join(projectDir, entry.target);\n const foreign = foreignOwnedTarget(projectDir, entry.target);\n if (foreign !== null) {\n if (!foreignOwned.includes(foreign)) foreignOwned.push(foreign);\n continue;\n }\n if (existsSync(target)) continue;\n const source = join(templatesDir, entry.source);\n if (!existsSync(source)) continue;\n copyDir(source, target, (rel) => foreignOwnedTarget(projectDir, `${entry.target}/${rel}`));\n installed.push(entry.target);\n }\n return { installed, foreignOwned };\n}\n\n/**\n * 새로 깐 스킬만 기준선에 **더한다** — `refreshSkillBaseline` 처럼 통째로 다시 찍으면 이번에\n * 건너뛴(`--only new-skills`) 기존 스킬의 사용자 편집이 \"하네스가 놓아둔 것\"으로 기록된다.\n */\nfunction recordNewSkillBaseline(\n projectDir: string,\n templatesDir: string,\n targets: ReadonlyArray<string>,\n): void {\n const log = readInstallLog(projectDir);\n if (!log) return;\n const ids = new Set(targets.map((t) => t.slice(\".claude/skills/\".length)));\n const added = collectSkillHashes(projectDir, templatesDir).filter((f) =>\n ids.has(f.path.split(\"/\")[0] ?? \"\"),\n );\n const kept = (log.skillFiles ?? []).filter((f) => !ids.has(f.path.split(\"/\")[0] ?? \"\"));\n try {\n writeInstallLog(projectDir, { ...log, skillFiles: [...kept, ...added] });\n } catch {\n // 기록 실패가 update 자체를 실패시키지는 않는다 (다른 기준선 갱신과 같은 방침).\n }\n}\n\n/**\n * opt-in 선택과 무관하게 이 트랙 구성이 받아야 하는 **파일** 자산.\n *\n * update 는 사용자가 어떤 opt-in 을 골랐는지 모른다 — install log 의 spec 은 tracks·cli 뿐이다.\n * 그래서 opt-in 축을 **양극단으로 평가해 교집합**을 취한다: 어느 쪽에서도 설치 대상이면 선택과\n * 무관한 자산이고, 한쪽에서만 대상이면 선택에 달린 자산이라 update 가 임의로 들일 수 없다\n * (plugin 을 켠 사용자에게 ECC fallback 에이전트를 깔면 그게 곧 원치 않는 자산이다).\n * 축 이름을 열거하지 않는 이유는 늘 같다 — 열거는 곧 두 번째 사본이고, 다음에 늘어나는 축이\n * 여기서 빠진다.\n *\n * `type: \"dir\"` 은 제외한다. 그쪽은 스킬 디렉터리이고 설치 여부를 `selectedInternalSkills` 가\n * 가르는데 update 는 그 선택을 복원할 수 없다 (`syncSkills` 가 \"이미 깔린 것만\" 다루는 것과\n * 같은 이유).\n *\n * 트랙 기록이 없으면 빈 배열이 들어와 `applies: all` 자산만 남는다 — 모르는 트랙의 자산을\n * 들이지 않으면서 전 트랙 공통분은 복구된다.\n */\nfunction trackOnlyFileAssets(tracks: ReadonlyArray<Track>): AssetEntry[] {\n const base = { tracks, selectedInternalSkills: [] as ReadonlyArray<string> };\n const optedOut: AssetSpec = { ...base, withEcc: false, withTauri: false };\n const optedIn: AssetSpec = { ...base, withEcc: true, withTauri: true };\n // `buildManifest` 는 목록만 만들고 게이팅은 하지 않는다 — 거르는 것은 각 엔트리의 `applies`\n // 술어이고, 그건 호출자가 평가한다 (`installer.ts` 의 manifest 루프와 같은 계약).\n return buildManifest(optedOut).filter(\n (e) => e.type === \"file\" && e.applies(optedOut) && e.applies(optedIn),\n );\n}\n\n/**\n * 하네스 앵커 동기화 + 레거시 설치본 이행 (P5 · ADR-060).\n *\n * 루트 `CLAUDE.md` 는 **덮어쓰지 않는다** — 그건 사용자 소유이고 우리 몫은 그 안의 import 한\n * 줄뿐이다. 갱신 대상은 하네스가 통째로 소유한 앵커 파일이다.\n *\n * | 루트 앵커 | 처리 |\n * |---|---|\n * | 있다 | 템플릿으로 갱신 (`claudeMdUpdated`) |\n * | 없다 | **만들고** 루트 CLAUDE.md 에 import 를 얹는다 (`anchorCreated` · `rootImportAdded`) |\n *\n * 아랫줄이 이번에 생겼다. v26.140.0 이전 설치본의 앵커는 `.claude/CLAUDE.md` 라 루트엔 아무것도\n * 없고, \"있을 때만 갱신\"으로 두면 그 사용자는 update 를 몇 번을 돌려도 ⓐ 새 앵커가 안 생기고\n * ⓑ import 줄도 안 붙고 ⓒ 구 앵커가 옛 내용 그대로 굳는데 **화면엔 아무 말도 안 뜬다**.\n * ADR-060 「적용 범위」가 단언한 이행이 코드에 없던 자리다.\n */\nfunction syncHarnessAnchor(\n projectDir: string,\n templatesDir: string,\n report: UpdateModeReport,\n now: Date = new Date(),\n): void {\n // 구 앵커는 **지우지 않고 알린다** — 사용자가 고쳤는지 판정할 근거가 update 에는 없다.\n // 이행이 끝난 뒤에도 파일이 남아 있는 한 계속 알린다 (지워야 없어지는 안내).\n if (existsSync(join(projectDir, LEGACY_ANCHOR_FILE))) {\n report.legacyAnchor = LEGACY_ANCHOR_FILE;\n }\n\n const templateMd = join(templatesDir, \"CLAUDE.md\");\n if (!existsSync(templateMd)) return;\n\n const anchor = join(projectDir, HARNESS_ANCHOR_FILE);\n const existed = existsSync(anchor);\n if (existed) {\n // #480 — 룰·스킬과 같은 잣대(ADR-046): 설치 시점 sha 와 다르면 설치자가 고친 것이니 먼저\n // 백업한다. 기준선이 없으면(레거시) 보수적으로 백업 — 증명 없이 편집분을 지우지 않는다.\n // 내용이 이미 같으면 아무것도 안 한다(재실행이 백업 노이즈를 쌓으면 안 된다).\n const current = readFileSync(anchor, \"utf8\");\n if (current !== readFileSync(templateMd, \"utf8\")) {\n const recorded = readInstallLog(projectDir)?.templates.rootClaudeMd;\n const owned =\n recorded?.path === HARNESS_ANCHOR_FILE && hashContent(current) === recorded.sha256;\n if (!owned) {\n backupFile(anchor, now);\n report.anchorBackedUp = true;\n }\n copyFileSync(templateMd, anchor);\n }\n report.claudeMdUpdated = true;\n // 갱신 뒤 기준선도 다시 찍는다 — 안 찍으면 다음 update 가 방금 놓은 최신판을 \"사용자가\n // 고쳤다\"로 읽어 매번 백업한다(전에는 생성 때 한 번만 기록했다).\n recordAnchorBaseline(projectDir, anchor);\n // ADR-085 — 앵커가 있는 정상 설치본에서도 루트 CLAUDE.md 의 관리 블록은 매 update 현행화한다\n // (상시 스킬 안내 = 지금 깔린 스킬). upsert 는 내용이 같으면 입력을 그대로 돌려주므로 파일을\n // 만지지 않는다. 독립 리뷰(#433 B-1)가 이 분기가 빠져 있던 것을 실측으로 잡았다 — 그 전에는\n // 이행 분기에서만 불려 \"현행화한다\"는 단언이 정상 설치본에서 거짓이었다.\n report.rootBlockRefreshed = upsertRootImport(projectDir);\n return;\n }\n\n copyFileSync(templateMd, anchor);\n report.anchorCreated = true;\n report.rootImportAdded = upsertRootImport(projectDir);\n // 이번에 만든 앵커는 install log 에 남긴다 — uninstall 이 회수를 주장하는 근거가 그 기록\n // 하나뿐이라(`commands/uninstall.ts` templates.rootClaudeMd), 빼면 아무도 못 지우는 파일을\n // 새로 만들어 놓는 셈이 된다. 로그가 없으면 만들지 않는다 (설치 기록 날조 금지 — 다른\n // 기준선 갱신과 같은 방침).\n recordAnchorBaseline(projectDir, anchor);\n}\n\n/**\n * 루트 `CLAUDE.md` 에 앵커 import 를 얹는다 — install 과 **같은 함수·같은 계약**\n * (`installer.ts` writeRootClaudeMd). 파일이 없으면 fill-in 스캐폴드로 만든다.\n *\n * @returns 실제로 파일을 고쳤으면 true. 이미 import 가 살아 있으면 upsert 가 입력을 그대로\n * 돌려주고, 그때는 파일을 만지지 않는다.\n */\nfunction upsertRootImport(projectDir: string): boolean {\n const target = join(projectDir, \"CLAUDE.md\");\n const existing = existsSync(target) ? readFileSync(target, \"utf8\") : null;\n const next = upsertHarnessImport(existing, {\n projectName: basename(projectDir),\n tracks: installedTracks(projectDir),\n // ADR-085 — update 는 선택 목록을 인자로 받지 않는다. `syncSkills` 와 같은 판정을 쓴다:\n // `.claude/skills/<id>` 가 있으면 그 스킬은 이 설치가 고른 것이다.\n continuousSkills: CONTINUOUS_SKILLS.map((s) => s.id).filter((id) =>\n existsSync(join(projectDir, \".claude/skills\", id)),\n ),\n });\n if (next === existing) return false;\n writeFileSync(target, next);\n return true;\n}\n\n/**\n * 스캐폴드가 적는 \"Active track(s)\" — **설치 기록이 SSOT** 다. update 는 트랙을 인자로 받지\n * 않으므로 여기서 따로 추론하면 그 추론이 두 번째 사본이 된다. 기록이 없거나 지금 어휘에 없는\n * 트랙이면 빼고 쓴다 — 모르는 것을 지어내지 않는다.\n */\nfunction installedTracks(projectDir: string): ReadonlyArray<Track> {\n const recorded = readInstallLog(projectDir)?.spec.tracks ?? [];\n return recorded.filter((t): t is Track => (TRACKS as ReadonlyArray<string>).includes(t));\n}\n\n/** 이행으로 만든 앵커의 설치 시점 sha 를 install log 에 기록 (uninstall 회수 근거). */\nfunction recordAnchorBaseline(projectDir: string, anchor: string): void {\n const log = readInstallLog(projectDir);\n if (!log) return;\n const next: InstallLog = {\n ...log,\n templates: {\n ...log.templates,\n rootClaudeMd: {\n path: HARNESS_ANCHOR_FILE,\n sha256: hashContent(readFileSync(anchor, \"utf8\")),\n },\n },\n };\n try {\n writeInstallLog(projectDir, next);\n } catch {\n // 기록 실패가 update 자체를 실패시키지는 않는다 (D16 과 같은 방침).\n }\n}\n\n/**\n * 외부 CLI 산출물 갱신 + 기준선 재기록 (v26.134.0 · ADR-049).\n *\n * **어느 CLI 가 설치돼 있는지 판정하지 않는다.** `refreshOnly` 가 \"디스크에 이미 있는 파일만\"\n * 으로 걸러 주므로, 안 깐 CLI 는 대상 파일이 없어 자연히 아무것도 안 쓴다. 선택 스킬도 같다 —\n * 전체 목록을 넘겨도 안 깔린 스킬은 파일이 없어 건너뛴다. 그래서 update 쪽에 CLI 목록이나\n * 스킬 선택 상태의 **사본이 생기지 않는다** (이 repo 가 반복해서 당한 열거-사본 실패 모드).\n *\n * **룰만 예외로 거른다** (ADR-074). `AGENTS.md` 는 룰을 파일 하나에 **합쳐 렌더**하므로\n * refreshOnly 의 \"디스크에 있는 것만\" 규칙이 룰 단위로는 작동하지 않는다 — 파일이 있으니\n * 통째로 다시 써지고, 그 안에 사용자가 뺀 룰이 되돌아온다. 여기가 `.claude/` 밖으로 룰이\n * 나가는 유일한 경로다.\n */\nfunction refreshExternalCli(\n projectDir: string,\n harnessRoot: string,\n): { externalUpdated: number; externalBackedUp: string[]; externalForeignOwned: string[] } {\n const log = readInstallLog(projectDir);\n const baselineExcluded = new Set(log?.spec.baselineExclude ?? []);\n const result = runCliTransforms({\n harnessRoot,\n projectDir,\n cli: ALL_CLI_TARGETS,\n selectedInternalSkills: INTERNAL_BUNDLED_SKILL_IDS,\n // 스킬은 **전부** 넘긴다 — 안 깔린 스킬은 대상 파일이 없어 refreshOnly 가 건너뛴다.\n rules: ALL_RULES.filter((r) => !isBaselineExcluded(`.claude/rules/${r}.md`, baselineExcluded)),\n previousExternal: log?.externalFiles ?? [],\n refreshOnly: true,\n });\n\n // 기준선 재기록 — `refreshPolicyBaseline` 과 같은 이유로 필수다. 빼면 다음 update 가 방금\n // 자기가 덮어쓴 파일을 \"사용자가 고쳤다\"로 오판해 백업을 매번 쌓는다.\n // 이번에 안 건드린 산출물의 기록은 `mergeExternalFiles` 가 유지한다 (지우면 그 파일들이\n // 다음 실행에서 판정 불가로 떨어진다). 로그가 없으면 만들지 않는다 — 설치 기록 날조 금지.\n if (log) {\n const merged = mergeExternalFiles(projectDir, log.externalFiles, result.externalFiles);\n const next: InstallLog = { ...log };\n if (merged.length > 0) next.externalFiles = merged;\n else delete next.externalFiles;\n try {\n writeInstallLog(projectDir, next);\n } catch {\n // 기록 실패가 update 자체를 실패시키지는 않는다 (D16 과 같은 방침).\n }\n }\n\n return {\n externalUpdated: result.externalUpdated,\n externalBackedUp: result.externalBackedUp,\n externalForeignOwned: result.externalForeignOwned,\n };\n}\n\n/**\n * 정책 디렉터리 하나에 대한 소유 판정 컨텍스트 (v26.132.0 · ADR-047).\n *\n * `prefix` 는 `.claude/` 기준 디렉터리명(`rules` 등) — install log 의 `policyFiles` 키가\n * 그 형식이라 baseline 조회에 필요하다.\n */\nexport interface PolicySyncCtx {\n prefix: string;\n /** 설치 시점 기준선 (`.claude/` 상대경로 → sha256). 빈 Map = 판정 불가. */\n baseline: ReadonlyMap<string, string>;\n}\n\n/**\n * 디스크 내용이 하네스가 놓아둔 것 그대로인가.\n *\n * v26.134.0 — 판정식 자체는 `install-log.ts` 의 `isHarnessOwned` **하나뿐**이고 여기서는\n * 키 조립(`<prefix>/<rel>`)만 한다. ADR-048 이 \"술어는 한 곳에\"라고 적고도 이 파일에 사본이\n * 남아 있었다 — 소유 판정은 사용자 파일 삭제 여부를 가르므로 두 벌이 갈리면 피해가 크다.\n */\nfunction isHarnessOwned(ctx: PolicySyncCtx, rel: string, current: string): boolean {\n return isOwnedByBaseline(ctx.baseline, `${ctx.prefix}/${rel}`, current);\n}\n\n/**\n * target 에 이미 있는 파일만 templates 기준으로 갱신한다 (Track 혼입 방지).\n *\n * v26.132.0 (ADR-047) — 사용자 편집분 판정이 붙었다. 그 전까지는 `copyFileSync` 로 무조건\n * 덮어써서, 같은 update 실행 안에서 스킬은 백업을 받고 룰·훅은 조용히 밀렸다.\n *\n * | 디스크 vs 기준선 | 뜻 | 처리 |\n * |---|---|---|\n * | 같다 | 사용자가 안 고쳤다 | 조용히 덮어쓴다 |\n * | 다르다 | 사용자가 고쳤다 | `.backup-<stamp>` 남기고 최신판을 자리에 |\n * | 기록 없음 | 판정 불가 | 보수적으로 백업 (레거시 설치의 첫 update 1회) |\n *\n * 내용 비교로 대신할 수 없다: 하네스가 개선해서 달라진 파일도 \"다르다\"로 잡혀 릴리즈마다\n * 전 사용자에게 백업본이 쌓인다 (`fs-ops.ts` backupFile 주석과 같은 이유).\n */\nexport function updateDir(\n target: string,\n source: string,\n ext: string,\n ctx: PolicySyncCtx,\n now: Date = new Date(),\n): { updated: number; backedUp: string[] } {\n if (!existsSync(target) || !existsSync(source)) return { updated: 0, backedUp: [] };\n let updated = 0;\n const backedUp: string[] = [];\n\n for (const file of readdirSync(target)) {\n if (!file.endsWith(ext)) continue;\n const targetFile = join(target, file);\n const sourceFile = join(source, file);\n if (!existsSync(sourceFile)) continue;\n\n const next = readFileSync(sourceFile, \"utf8\");\n const current = readFileSync(targetFile, \"utf8\");\n if (current === next) continue; // 이미 최신 — 백업도 쓰기도 불필요\n\n if (!isHarnessOwned(ctx, file, current)) {\n backupFile(targetFile, now);\n backedUp.push(`${ctx.prefix}/${file}`);\n }\n writeFileSync(targetFile, next);\n updated++;\n }\n return { updated, backedUp };\n}\n\n/**\n * `.claude/skills/` 를 templates 기준으로 갱신한다 (v26.126.0 · R-3a · ADR-046).\n *\n * **설치된 스킬만 손댄다** — templates 에 있어도 target 에 그 스킬 디렉터리가 없으면 건너뛴다.\n * 스킬은 트랙/opt-in 으로 게이팅돼 설치되므로(`installer.ts` selectedInternalSkills), 전부\n * 복사하면 사용자가 고르지 않은 스킬이 딸려 들어간다 (`updateDir` 의 \"Track 혼입 방지\"와 같은 취지).\n *\n * 파일 단위 판정:\n * | 디스크 vs 기준선 | 뜻 | 처리 |\n * |---|---|---|\n * | 같다 | 사용자가 안 고쳤다 | 조용히 덮어쓴다 |\n * | 다르다 | 사용자가 고쳤다 | `.backup-<stamp>` 남기고 덮어쓴다 |\n * | 기준선 기록 없음 | 판정 불가 | 보수적으로 백업 (레거시 설치의 첫 update 1회) |\n *\n * **번들에 없는 파일은 지운다** (#477, 사용자 결정 2026-09-20 — \"있으면 백업하고 다시 깐다\").\n * 번들 스킬 디렉터리의 파일 목록이 최신본의 정의이고, 사용자 것(기준선 없음·다름)은 지우기 전에\n * 덮어쓸 때와 같은 잣대로 `.backup-<stamp>` 을 남긴다. 기준선은 번들에 있는 파일만 담는다\n * (`collectSkillHashes`) — 그래야 사용자 파일이 \"하네스가 깔았던 것\"으로 오판되지 않는다.\n *\n * **심볼릭 링크는 건너뛴다** (2026-08-02 · ADR-062). `existsSync` 는 링크를 따라가므로 그것만\n * 보면 \"설치돼 있다\"와 \"다른 저장소를 가리키는 링크가 있다\"가 구분되지 않는다. `npx skills add`\n * 로 받은 스킬의 프로젝트 스코프 설치처가 바로 `.claude/skills/<id>` 이고 그 실체는 skills\n * 저장소로의 링크다 — 그대로 쓰면 하네스가 **사용자의 다른 저장소 본문을 우리 판본으로\n * 덮어쓴다**. 쓰기 대상이 `.claude/` 밖이라 백업이 남아도 사용자가 찾을 자리가 아니다.\n * 소유자가 우리가 아닌 것은 손대지 않고 건너뛴 사실을 보고한다(침묵 금지).\n */\nexport function syncSkills(\n targetDir: string,\n sourceDir: string,\n baseline: ReadonlyMap<string, string>,\n now: Date = new Date(),\n /**\n * #343 — `<id>/<rel>` 를 받아 그 파일을 쓰면 남의 것을 건드리는 경우 **그 자리**를 돌려준다.\n * `copyDir` 과 같은 모양이다: 판정(정책)은 `foreign-slot.ts` 가, 경로 조립은 호출자가 갖는다.\n * 슬롯만 보는 위 판정으로는 **슬롯 안쪽**(파일 링크·중간 디렉터리 링크·FIFO)이 안 걸린다.\n * **필수**다 — 옵셔널이면 다음 호출자가 조용히 빠뜨린다(`copyDir` 과 같은 이유).\n */\n foreignOf: (relInSkills: string) => string | null,\n): {\n updated: number;\n backedUp: string[];\n skippedLinks: string[];\n foreignOwned: string[];\n pruned: string[];\n} {\n if (!existsSync(targetDir) || !existsSync(sourceDir))\n return { updated: 0, backedUp: [], skippedLinks: [], foreignOwned: [], pruned: [] };\n let updated = 0;\n const backedUp: string[] = [];\n const skippedLinks: string[] = [];\n const foreignOwned: string[] = [];\n const pruned: string[] = [];\n\n for (const skill of readdirSync(sourceDir, { withFileTypes: true })) {\n if (!skill.isDirectory()) continue;\n const targetSkill = join(targetDir, skill.name);\n // #343 — 판정은 `install` 과 **같은 술어**를 쓴다 (SSOT = foreign-slot.ts). 전에는 여기만\n // `isSymbolicLink()` 라 심링크는 건너뛰면서 **일반 파일·FIFO 는 그대로 통과**했고, 아래\n // `mkdirSync` 가 EEXIST 로 죽어 update 전체가 끝났다. install 이 그 상태를 화면으로\n // 승인하게 된 지금은 그 크래시가 곧 \"install 은 되는데 update 만 죽는다\"가 된다.\n // existsSync 보다 **먼저** 본다 — 깨진 링크는 existsSync 가 false 라 조용히 빠져나갔다.\n if (occupiedByNonDirectory(targetSkill)) {\n skippedLinks.push(skill.name);\n continue;\n }\n if (!existsSync(targetSkill)) continue; // 사용자가 선택하지 않은 스킬 — 새로 깔지 않는다\n\n for (const rel of listFilesRecursive(join(sourceDir, skill.name))) {\n const targetFile = join(targetSkill, rel);\n // 슬롯이 우리 디렉터리여도 **그 안**이 남의 것일 수 있다. 판정 없이 쓰면 링크를 따라\n // 남의 파일을 덮고(보고 0줄), FIFO 면 `readFileSync` 가 영영 블록된다.\n const foreign = foreignOf(`${skill.name}/${rel}`);\n if (foreign !== null) {\n if (!foreignOwned.includes(foreign)) foreignOwned.push(foreign);\n continue;\n }\n const next = readFileSync(join(sourceDir, skill.name, rel), \"utf8\");\n\n if (!existsSync(targetFile)) {\n // 스킬 안에 새로 생긴 파일 (예: references/ 추가) — 스킬 자체는 이미 설치돼 있다.\n mkdirSync(dirname(targetFile), { recursive: true });\n writeFileSync(targetFile, next);\n updated++;\n continue;\n }\n\n const current = readFileSync(targetFile, \"utf8\");\n if (current === next) continue; // 이미 최신 — 백업도 갱신도 불필요\n\n const recorded = baseline.get(`${skill.name}/${rel}`);\n if (recorded === undefined || hashContent(current) !== recorded) {\n backupFile(targetFile, now);\n backedUp.push(`${skill.name}/${rel}`);\n }\n writeFileSync(targetFile, next);\n updated++;\n }\n\n // #477 — 번들 목록이 곧 \"최신본이 가져야 할 파일\" 이다. 그 밖의 파일은 지운다 — 결과는\n // \"디렉터리를 지우고 다시 까는 것\"과 같고, 사용자 편집분·출처 미상 파일은 덮어쓸 때와 같은\n // 잣대(기준선 sha)로 먼저 백업한다. 우리가 남긴 `*.backup-*` 은 목록 밖이지만 지우지\n // 않는다 — 지우면 매 update 가 직전 백업을 먹는다. 남의 자리(링크·FIFO)도 건드리지 않는다.\n const bundled = new Set(listFilesRecursive(join(sourceDir, skill.name)));\n for (const rel of listFilesRecursive(targetSkill)) {\n if (bundled.has(rel) || basename(rel).includes(\".backup-\")) continue;\n if (foreignOf(`${skill.name}/${rel}`) !== null) continue;\n const targetFile = join(targetSkill, rel);\n const recorded = baseline.get(`${skill.name}/${rel}`);\n if (recorded === undefined || hashContent(readFileSync(targetFile, \"utf8\")) !== recorded) {\n backupFile(targetFile, now);\n backedUp.push(`${skill.name}/${rel}`);\n }\n unlinkSync(targetFile);\n pruned.push(`${skill.name}/${rel}`);\n // 비게 된 중간 디렉터리(예: references/)도 걷는다 — 빈 디렉터리는 \"파일이 있었다\"는 흔적만 남긴다.\n for (\n let d = dirname(targetFile);\n d !== targetSkill && readdirSync(d).length === 0;\n d = dirname(d)\n )\n rmdirSync(d);\n }\n }\n return { updated, backedUp, skippedLinks, foreignOwned, pruned };\n}\n\n/** 설치 시점 기준선을 Map 으로. 기록이 없으면 빈 Map — 그때는 보수적 백업으로 폴백한다. */\nfunction skillBaseline(projectDir: string): ReadonlyMap<string, string> {\n const log = readInstallLog(projectDir);\n return new Map((log?.skillFiles ?? []).map((f) => [f.path, f.sha256]));\n}\n\n/** 정책 파일 기준선 (v26.132.0 · ADR-047). 빈 Map = 덮어쓰기는 보수적 백업, prune 은 전면 중단. */\nfunction policyBaseline(projectDir: string): ReadonlyMap<string, string> {\n const log = readInstallLog(projectDir);\n return new Map((log?.policyFiles ?? []).map((f) => [f.path, f.sha256]));\n}\n\n/**\n * 정책 파일 기준선을 갱신한다 — `refreshSkillBaseline` 과 같은 이유로 필수다.\n * 이걸 빼면 다음 update 가 방금 자기가 덮어쓴 파일을 \"사용자가 고쳤다\"로 오판해 백업을 매번 쌓는다.\n *\n * 로그가 없으면 만들지 않는다 (설치 기록 날조 금지 — uninstall 이 그걸 믿는다).\n */\nfunction refreshPolicyBaseline(\n projectDir: string,\n templatesDir: string,\n /** #480 — 이번에 동기화한 디렉터리(`POLICY_DIRS` 의 `dir`). 안 주면 전부. */\n dirs?: ReadonlyArray<string>,\n): void {\n const log = readInstallLog(projectDir);\n if (!log) return;\n const inDirs = (path: string): boolean =>\n dirs === undefined || dirs.some((d) => path.startsWith(`${d}/`));\n const policyFiles = [\n ...(log.policyFiles ?? []).filter((f) => !inDirs(f.path)),\n ...collectPolicyHashes(projectDir, templatesDir).filter((f) => inDirs(f.path)),\n ];\n const next: InstallLog = { ...log };\n if (policyFiles.length > 0) next.policyFiles = policyFiles;\n else delete next.policyFiles;\n try {\n writeInstallLog(projectDir, next);\n } catch {\n // 기록 실패가 update 자체를 실패시키지는 않는다 (D16 과 같은 방침).\n }\n}\n\n/**\n * 갱신 직후 기준선을 다시 찍는다.\n *\n * 이걸 빼면 다음 update 가 **방금 자기가 덮어쓴 파일**을 전부 \"사용자가 고쳤다\"로 오판해\n * 백업본을 매번 새로 쌓는다. update 는 install log 를 안 쓰는 단축 경로라\n * (`installer.ts` runUpdateInstall) 여기서 안 하면 아무도 안 한다.\n *\n * 로그가 없으면 **만들지 않는다** — update 가 설치 기록을 날조하면 uninstall 이 그걸 믿는다.\n */\nfunction refreshSkillBaseline(projectDir: string, templatesDir: string): void {\n const log = readInstallLog(projectDir);\n if (!log) return;\n const skillFiles = collectSkillHashes(projectDir, templatesDir);\n const next: InstallLog = { ...log };\n if (skillFiles.length > 0) next.skillFiles = skillFiles;\n else delete next.skillFiles;\n try {\n writeInstallLog(projectDir, next);\n } catch {\n // 기록 실패가 update 자체를 실패시키지는 않는다 (D16 — 설치/갱신 성공 우선과 같은 방침).\n }\n}\n\n/**\n * Templates 에 없는데 target 에 있는 파일 제거 — **하네스가 깔았던 것만** (v26.132.0 · ADR-047).\n *\n * 그 전까지는 \"templates 에 없다\"만으로 지웠다. 그러면 사용자가 직접 만든 커스텀 룰·훅이\n * 백업도 없이 사라진다 — templates 에 없는 건 폐기된 하네스 룰도, 사용자가 쓴 팀 규칙도\n * 똑같이 \"없음\"이기 때문이다. 소유는 install log 기준선으로만 주장할 수 있다.\n *\n * 기록이 없으면(레거시 로그·로그 부재) **아무것도 지우지 않는다.** 폐기 룰이 남는 비용보다\n * 사용자 파일을 지우는 비용이 크다. 그쪽은 다음 install 이 기준선을 채우면 자연히 회수된다.\n *\n * 지우기 전 사용자가 고친 흔적이 있으면 백업을 남긴다 — 하네스가 깔았더라도 그 위에 쓴 내용은\n * 사용자 것이다.\n */\nexport function pruneOrphans(\n target: string,\n source: string,\n ext: string,\n ctx: PolicySyncCtx,\n now: Date = new Date(),\n): string[] {\n if (!existsSync(target) || !existsSync(source)) return [];\n const removed: string[] = [];\n for (const file of readdirSync(target)) {\n if (!file.endsWith(ext)) continue;\n const sourceFile = join(source, file);\n if (!existsSync(sourceFile)) {\n const targetFile = join(target, file);\n // 소유를 주장할 수 있는 것만 지운다. 기준선에 없으면 사용자가 만든 파일이다.\n if (!ctx.baseline.has(`${ctx.prefix}/${file}`)) continue;\n try {\n const current = readFileSync(targetFile, \"utf8\");\n if (!isHarnessOwned(ctx, file, current)) backupFile(targetFile, now);\n unlinkSync(targetFile);\n removed.push(file);\n } catch {\n // best-effort — read-only? 다음 update 시 재시도\n }\n }\n }\n return removed;\n}\n\n/**\n * 프로젝트 루트의 `.mcp-allowlist` 를 백업 후 제거한다 (ADR-072). 없었으면 null.\n *\n * `pruneOrphans` 로 못 덮는 이유는 대상이 `.claude/` 밖이고 디렉터리 단위 동기화의 짝이 없기\n * 때문이다. 그래서 소유 판정도 다르게 한다 — 저쪽은 install log 기준선으로 \"우리가 깔았나\"를\n * 묻지만, 이 파일은 **경로 하나가 곧 소유 증거다**(하네스 말고 이 이름을 쓰는 것이 없다).\n *\n * 백업은 무조건 남긴다. 자동 생성물이지만 사용자가 서버 줄을 주석 처리해 정책을 적어 넣는\n * 파일이었고, 그 편집분을 우리가 판정할 수단이 없다 — 판정할 수 없으면 지우기 전에 남긴다.\n *\n * 실패는 삼킨다(read-only 디렉터리 등). 이 정리는 update 의 목적이 아니라 뒷정리라, 여기서\n * 던지면 갱신 전체가 실패한다.\n */\nexport function retireMcpAllowlist(projectDir: string, now: Date = new Date()): string | null {\n const target = join(projectDir, \".mcp-allowlist\");\n if (!existsSync(target)) return null;\n try {\n const backup = backupFile(target, now);\n unlinkSync(target);\n return backup;\n } catch {\n return null;\n }\n}\n\n/**\n * settings.json의 PreToolUse/PostToolUse hooks 중 실존 파일 없는 hook script 참조 제거.\n * bash clean_stale_hook_refs 등가 (jq 의존 없이 JSON 직접 파싱).\n *\n * @param claudeDir `.claude/` 자신. 이전엔 `.claude/hooks/` 였다 — M-1 으로 한 층 넓혔다.\n * @returns 제거된 hook script 의 `.claude/` 기준 상대경로 목록\n */\nexport function cleanStaleHookRefs(settingsPath: string, claudeDir: string): string[] {\n let settings: SettingsJson;\n try {\n settings = JSON.parse(readFileSync(settingsPath, \"utf8\")) as SettingsJson;\n } catch {\n return [];\n }\n const hookEvents = settings.hooks ?? {};\n const removed: string[] = [];\n const cleanedHooks: Record<string, HookEntry[]> = {};\n\n for (const [eventName, eventEntries] of Object.entries(hookEvents)) {\n if (!Array.isArray(eventEntries)) {\n cleanedHooks[eventName] = eventEntries; // non-array event — 그대로 보존\n continue;\n }\n cleanedHooks[eventName] = eventEntries\n .filter((entry) => Array.isArray(entry?.hooks))\n .map((entry) => ({\n ...entry,\n hooks: entry.hooks.filter((hook) => keepHookRef(hook, claudeDir, removed)),\n }))\n .filter((entry) => entry.hooks.length > 0); // stale 제거 후 hooks 빈 entry 제거\n }\n\n if (removed.length > 0) {\n const next: SettingsJson = { ...settings, hooks: cleanedHooks };\n writeFileSync(settingsPath, `${JSON.stringify(next, null, 2)}\\n`);\n }\n return removed;\n}\n\n/**\n * hook command 가 **이 프로젝트에 앵커된** 실존 `.sh` 참조면 true.\n * 앵커돼 있는데 파일이 없으면 removed 에 상대경로를 수집하고 false (= 제거).\n * 앵커가 없으면 파일 부재와 무관하게 true (= 보존, `removed` 수집도 안 한다).\n *\n * M-1 — 탐지 범위가 `.claude/hooks/` 한 층에서 **`.claude/` 이하 임의 깊이**로 넓어졌다.\n * `templates/settings.json` 은 `applies: all` 인데 거기 배선된 훅이 **스킬 디렉터리 안의 사이드카\n * 스크립트**(`.claude/skills/<id>/*.sh`)를 참조하고 그 스킬은 좁게 깔릴 수 있다 — plugin 을 켠\n * 설치자는 Write/Edit 마다 없는 파일을 bash 로 부른다(exit 127). 치유기는 이미 있었지만 이\n * 부류를 regex 가 못 물었을 뿐이다. (그 훅은 ADR-088 에서 스킬과 함께 은퇴했다 — 부류는 남는다.)\n *\n * H-2 — 그 확장이 경로 세그먼트 `/.claude/` 만 봐서 **홈 `~/.claude/`** 까지 사정권에 넣었다.\n * 앵커 판정(`projectAnchoredRef`)이 그 경계를 되돌린다.\n *\n * 캡처와 기준 디렉터리는 **원자적으로 같이** 간다 — 기준만 `claudeDir` 로 옮기고 캡처가\n * 파일명이면 `.claude/alive.sh` 를 찾다 못 찾아 멀쩡한 훅을 지운다.\n *\n * 기존 한계 유지: 한 command 안에서 **앵커 배열 순서상 먼저 걸린 앵커**가 뽑아낸 참조 하나만\n * 본다 — 문자열상 먼저 나오는 참조가 아니다. 앵커가 여럿 섞인 command 에서는 뒤쪽 참조가\n * 판정 대상이 될 수 있고, 나머지는 검사 없이 보존된다(안전한 쪽).\n *\n * export 사유: `tests/settings-reference-parity.test.ts` 가 \"이 참조는 치유기가 실제로 무는가\"를\n * 직접 호출해 면제 판정에 쓴다 — 면제는 말이 아니라 기계적 계약으로 증명한다.\n */\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 * `.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 * @returns `.claude/` 기준 상대경로. 앵커가 없거나 `.sh` 참조가 아니면 undefined.\n */\nfunction 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}/`,\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\ninterface HookCommand {\n type?: string;\n command?: string;\n}\n\ninterface HookEntry {\n matcher?: string;\n hooks: HookCommand[];\n}\n\ninterface SettingsJson {\n hooks?: Record<string, HookEntry[]>;\n [key: string]: unknown;\n}\n","/**\n * Install 출력 렌더 레이어 (v26.82.0, Phase R).\n *\n * `commands/install.ts` 가 979줄(cap 800 초과 — repo 최대 위반)로 비대해진 원인이\n * 렌더 함수 누적이었음 → 본 파일로 추출. install.ts 는 spec 검증 + 파이프라인\n * 오케스트레이션만, 여기는 화면 출력만. 동작 변경 0 (순수 이동).\n */\n\nimport { existsSync, readdirSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { CATEGORY_TITLES, type Category } from \"../categories.js\";\nimport { targetsInclude } from \"../cli-targets.js\";\nimport {\n formatResidentCostLine,\n landsOnDisk,\n residentCost,\n summarizeContextCost,\n} from \"../context-cost.js\";\nimport { assetRow, c, infoRow, padDisplay, sectionHeader, unifiedSection } from \"../design.js\";\nimport {\n assetCliSupport,\n assetReachesCli,\n EXTERNAL_ASSETS,\n type ExternalAsset,\n experimentalOptInCandidates,\n isAssetSelected,\n RENAMED_SKILL_IDS,\n RETIRED_SKILL_IDS,\n} from \"../external-assets.js\";\nimport type { AssetInstallResult } from \"../external-installer.js\";\nimport {\n type BaselineReport,\n buildManifestSpec,\n type InstallMode,\n type InstallReport,\n type ProgressEvent,\n} from \"../installer.js\";\nimport { buildManifest, RETIRED_AGENTS, TRACK_AGENTS } from \"../manifest.js\";\nimport { finalSelectedAssets, groupAssetsByCategory } from \"../preset-recommend.js\";\nimport { HARNESS_ANCHOR_FILE, HARNESS_IMPORT_LINE } from \"../project-claude-merge.js\";\nimport type { CliBase, CliTargets, InstallSpec, OptionFlags } from \"../types.js\";\n\n/**\n * v26.78.1 — Summary `CLI` 행 라벨 (SSOT). spec.cli 에서 derive → 헤더와 일관.\n * 이전 pairwise if-chain 은 codex/opencode 만 열거해 `--cli antigravity` 가 \"Claude\" 로\n * 잘못 출력 (R2). 4 base 전부 매핑.\n */\nconst CLI_SUMMARY_LABELS: Record<CliBase, string> = {\n claude: \"Claude\",\n codex: \"Codex\",\n opencode: \"OpenCode\",\n antigravity: \"Antigravity\",\n};\n\n/** Callbacks for progressive rendering during runInstall (avoids \"Phase 1 silence\" UX). */\nexport interface PipelineCallbacks {\n onProgress?: (event: ProgressEvent) => void;\n externalDeps?: {\n onAssetStart?: (asset: ExternalAsset) => void;\n onAssetResult?: (result: AssetInstallResult) => void;\n };\n}\n\n/** createInstallRenderer 반환 — 스트리밍 콜백 + 렌더 상태 조회. */\nexport interface InstallRenderer {\n callbacks: PipelineCallbacks;\n /** External assets 헤더 출력 여부 — Summary 직전 trailing newline 판단용. */\n phase2HeaderPrinted(): boolean;\n}\n\n/**\n * install header (TARGET / TRACKS / CLI / SCOPE / OPTIONS / ASSETS) 렌더.\n * wizard 모드는 Step 3 review + Step 4 confirm 에서 이미 표시하므로 호출 안 함.\n */\nexport function renderInstallHeader(\n log: (msg: string) => void,\n spec: InstallSpec,\n mode?: InstallMode,\n): void {\n const headerLabel =\n mode === \"update\"\n ? \"uzys-agent-harness · update\"\n : mode === \"add\"\n ? \"uzys-agent-harness · add\"\n : mode === \"reinstall\"\n ? \"uzys-agent-harness · reinstall\"\n : \"uzys-agent-harness · install\";\n log(\"\");\n log(sectionHeader(headerLabel));\n log(\"\");\n log(infoRow(\"TARGET\", shortenPath(spec.projectDir)));\n log(infoRow(\"TRACKS\", spec.tracks.join(\", \")));\n log(infoRow(\"CLI\", spec.cli.join(\" · \")));\n // v26.64.0 (ADR-020) — SCOPE row. 사용자가 매 install 시 어디에 write 되는지 인지 (D16).\n {\n const effectiveScope = spec.scope ?? \"project\";\n const scopeMsg =\n effectiveScope === \"global\"\n ? \"Global — writes to ~/.claude/, ~/.codex/, npm -g\"\n : \"Project — current directory only (no global write)\";\n log(infoRow(\"SCOPE\", scopeMsg));\n }\n log(infoRow(\"OPTIONS\", formatOptions(spec)));\n // v26.82.0 (Phase R, S6) — merge 는 preset-recommend.ts 단일 구현 (이전 computeFinalAssets 중복).\n const finalAssets = finalSelectedAssets(spec.tracks, spec.userOverride);\n if (finalAssets.length > 0) {\n // v26.102.0 (ADR-031) — 선택 수와 실제 설치 수의 어긋남을 약속 시점에 고지 (SOD 리뷰 F3:\n // executive/codex 가 \"4 selected\" 약속 후 0 설치이던 불일치). 숨김 없이 분해만 병기.\n // 미지 id(검증은 install.ts 담당)는 도달 가능으로 취급 — 여기서 이중 판정하지 않는다.\n const unreachable = finalAssets.filter((id) => {\n const asset = EXTERNAL_ASSETS.find((a) => a.id === id);\n return asset ? !assetReachesCli(asset, spec.cli) : false;\n });\n const label =\n unreachable.length > 0\n ? `${finalAssets.length} selected (${unreachable.length} outside [${spec.cli.join(\", \")}] reach — not installed)`\n : `${finalAssets.length} selected`;\n log(infoRow(\"ASSETS\", label));\n for (const [cat, ids] of groupAssetsByCategory(finalAssets)) {\n log(` ${c.dim(`· ${cat}:`)} ${ids.join(\", \")}`);\n }\n // v26.103.0 (ADR-032) — Session-Start Context Cost NSM. 번들 스킬 = frontmatter 실측(~),\n // 외부 자산 = unmeasured 명시 (추정치를 실측처럼 표기 금지).\n // #320 H1 — **설치기와 같은 spec 으로 센다.** `InstallSpec` 을 그대로 넘기면\n // `selectedInternalSkills` 가 없어 번들 스킬이 전부 미설치로 계산되고, 설치자에게\n // 실제보다 작은 숫자가 나간다(track=tooling 에서 23 vs 실제 34). 계측·문서만 고치고\n // 이 줄을 두면 화면과 내부 수치가 어긋난다 — 일관되게 틀린 것보다 나쁘다.\n // #458 — **update 모드는 여기서 안 찍는다.** 이 줄은 manifest 계획, 즉 \"지금 깔면 이렇게\n // 된다\"이고 update 화면에서는 사실이 될 수 없다: 트랙에서 강등·은퇴한 에이전트 파일이\n // 디스크에 남아 매 세션 상주하는데 계획에는 없어서, 설치자는 실제보다 작은 숫자를 본다\n // (실측: 화면 agents 2 · 디스크 4). 갱신 **후 디스크**로 잰 줄을 `renderUpdateSummary` 가 낸다.\n if (mode !== \"update\") {\n const assetSpec = buildManifestSpec(spec);\n const cost = formatResidentCostLine(\n residentCost(\n buildManifest(assetSpec).filter(\n (e) => e.applies(assetSpec) && landsOnDisk(e.target, spec.cli),\n ),\n ),\n summarizeContextCost(finalAssets).unmeasuredCount,\n );\n if (cost) log(` ${c.dim(`· ${cost}`)}`);\n }\n }\n log(\"\");\n}\n\n/**\n * runInstall 스트리밍 렌더 콜백 생성 — baseline 완료 시 즉시 Phase 1 rows 출력,\n * external 은 per-asset 스트리밍 + 카테고리 헤더 (ADR-016 grouped progress UX).\n */\nexport function createInstallRenderer(\n log: (msg: string) => void,\n spec: InstallSpec,\n verbose: boolean,\n): InstallRenderer {\n let phase2HeaderPrinted = false;\n // v26.55.0 — Phase 2 grouped progress UX (ADR-016). category 변경 시 ━━ <Title> ━━ 헤더 출력.\n // external-installer 가 카테고리 순서로 정렬해 호출 → 첫 번째 호출이 category 1 의 첫 자산.\n let currentCategory: Category | null = null;\n const callbacks: PipelineCallbacks = {\n onProgress: (event) => {\n if (event.type === \"baseline-complete\") {\n // v26.81.0 (ADR-022) — withEcc boolean 삭제 → ecc-plugin 자산 선택으로 판정 (hint 게이팅).\n // v26.102.0 (ADR-031) — \"선택 = 설치됨\" 은 claude 도달 시에만 성립: codex 단독에선\n // ecc-plugin 이 배제되므로 fallback 힌트가 계속 진실이어야 한다 (SOD 리뷰 F2).\n const claudeSelected = targetsInclude(spec.cli, \"claude\");\n const eccWillInstall =\n claudeSelected &&\n (isAssetSelected(\"ecc-plugin\", spec) || spec.options.withPrune === true);\n renderPhase1Rows(log, event.baseline, verbose, eccWillInstall, claudeSelected);\n } else if (event.type === \"external-start\" && event.assetCount > 0) {\n // v26.63.0 — phaseHeader → unifiedSection. count 헤더에 inline 표시.\n log(unifiedSection(`External assets (${event.assetCount})`));\n log(\"\");\n phase2HeaderPrinted = true;\n } else if (event.type === \"external-complete\") {\n // v26.102.0 (ADR-031, Batch3) — CLI 도달 불가로 시도조차 안 한 자산 고지.\n // 침묵 제외는 \"4-CLI 지원\" 광고와 실동작의 어긋남을 숨긴다 (no-false-ship).\n // 어휘 주의: \"skipped\"(설치 실패)와 구분해 \"not installed\" 사용, 사유는 각 자산의\n // 실 도달 범위에서 derive — \"claude-only\" 하드코딩 금지 (SOD 리뷰 F4/F7/Nit-4).\n const excluded = event.report.excludedByCli;\n if (excluded.length > 0) {\n if (!phase2HeaderPrinted) {\n // attempted=0 인 트랙(executive 등)에서 고지가 헤더 없이 떠도는 것 방지 (F8).\n log(unifiedSection(\"External assets (0)\"));\n phase2HeaderPrinted = true;\n }\n const bySupport = new Map<string, string[]>();\n for (const a of excluded) {\n const key = assetCliSupport(a).join(\"/\");\n bySupport.set(key, [...(bySupport.get(key) ?? []), a.id]);\n }\n log(\"\");\n for (const [support, ids] of bySupport) {\n log(\n ` ${c.dim(`· ${ids.length} asset(s) not installed — requires ${support}, selected [${spec.cli.join(\", \")}]: ${ids.join(\", \")}`)}`,\n );\n }\n }\n }\n },\n externalDeps: {\n onAssetStart: (asset) => {\n // v26.57.0 (F2) — 카테고리 헤더만 출력. 자산 시작 라인 (→) 제거 — ✓ 결과 한 라인으로 1 단위 명확화.\n if (asset.category !== currentCategory) {\n if (currentCategory !== null) log(\"\");\n log(` ${c.bold(`━━ ${CATEGORY_TITLES[asset.category]} ━━`)}`);\n currentCategory = asset.category;\n }\n // #422 — 패키지 설치(npm · npx)는 수 분 걸리는데 결과 행이 뜰 때까지 화면이 침묵해\n // 설치자에게 \"멈춤\"으로 보였다(netlify-cli 413 MB). 시작을 한 줄로 알린다 — 다른 종류는\n // 초 단위라 F2 결정(자산당 결과 1행) 그대로 둔다.\n if (asset.method.kind === \"npm\" || asset.method.kind === \"npx-run\") {\n const what =\n asset.method.kind === \"npm\"\n ? `npm install ${asset.method.pkg}@${asset.method.version}`\n : `npx ${asset.method.cmd}@${asset.method.version}`;\n log(` ${c.dim(`… ${asset.id} ${what} — running, may take a few minutes`)}`);\n }\n },\n onAssetResult: (result) => {\n const meta = result.ok\n ? formatAssetMeta(result.asset, result.version)\n : (result.message ?? \"failed\");\n log(` ${assetRow(result.ok ? \"success\" : \"skip\", result.asset.id, meta)}`);\n },\n },\n };\n return { callbacks, phase2HeaderPrinted: () => phase2HeaderPrinted };\n}\n\n/** Update mode 단축 Summary — manifest copy / external 모두 skip 된 경로. */\nexport function renderUpdateSummary(\n log: (msg: string) => void,\n spec: InstallSpec,\n report: InstallReport,\n): void {\n log(\"\");\n // v26.63.2 — Summary 도 unifiedSection 으로 통일 (━━ marker). Step 5 안 sub-section 들과 일관.\n log(unifiedSection(\"Summary\"));\n log(\"\");\n log(infoRow(\"STATUS\", c.green(\"Update complete\")));\n log(infoRow(\"MODE\", \"update\"));\n if (report.backup) {\n log(infoRow(\"BACKUP\", shortenPath(report.backup)));\n log(infoRow(\"ROLLBACK\", `rm -rf .claude && mv ${shortenPath(report.backup)} .claude`));\n }\n // #458 — 상주 계측은 **갱신이 끝난 뒤** 낸다. 헤더 자리(계획)에서 옮겨온 이유는 위 주석에.\n // 문구는 헤더·wizard 와 같은 `formatResidentCostLine` 하나에서 온다 (표면별 조립 금지).\n const cost = formatResidentCostLine(\n residentCost(residentEntriesOnDisk(spec)),\n summarizeContextCost(finalSelectedAssets(spec.tracks, spec.userOverride)).unmeasuredCount,\n );\n if (cost) log(infoRow(\"CONTEXT\", cost));\n // #480 ③ — 백업이 있으면 **다음 행동**을 지목한다. 백업 사실만 알리면 설치자는 파일을 열어\n // 손으로 옮긴다 — 그게 이 이슈가 말한 스트레스다.\n const backups = report.updateMode?.backups ?? [];\n if (backups.length > 0) {\n log(\n infoRow(\n \"BACKUPS\",\n `${backups.length} edited file(s) kept as *.backup-<time> · list: .uzys-agent-harness/update-backups.json`,\n ),\n );\n log(\n infoRow(\n \"NEXT\",\n 'to re-apply your edits on the new version, ask audit-harness-fit: \"update 백업본의 내 편집을 새 판에 다시 얹어줘\"',\n ),\n );\n }\n log(\"\");\n}\n\n/**\n * update 가 재는 상주 엔트리 — **에이전트만 디스크에서** 읽는다 (#458).\n *\n * 나머지(룰·스킬·CLAUDE.md)는 update 가 방금 배포판으로 동기화한 것이라 계획 = 디스크다.\n * 에이전트만 갈리는 이유는 **트랙 조건화(ADR-090)와 은퇴(ADR-089)가 파일을 안 지우기 때문**이다:\n * manifest 에는 없는데 파일은 남아 descriptor 가 매 세션 상주한다. 그 차이를 안 반영하면 화면이\n * 실제보다 작은 숫자를 내고, 바로 그 숫자를 근거로 \"지워도 된다\" 안내가 붙는 화면이다.\n */\nfunction residentEntriesOnDisk(\n spec: InstallSpec,\n): Array<{ source: string; target: string; file?: string }> {\n const assetSpec = buildManifestSpec(spec);\n const planned = buildManifest(assetSpec).filter((e) => e.applies(assetSpec));\n const agentsDir = join(spec.projectDir, \".claude\", \"agents\");\n const onDisk = existsSync(agentsDir)\n ? readdirSync(agentsDir, { withFileTypes: true })\n .filter((e) => e.isFile() && e.name.endsWith(\".md\"))\n .map((e) => ({\n source: `agents/${e.name}`,\n target: `.claude/agents/${e.name}`,\n // 배포판이 아니라 **이 프로젝트의 파일**을 잰다 — 사용자가 고친 descriptor 도,\n // 배포판에 더는 없는 에이전트도 실제로 상주하는 것은 이쪽이다.\n file: join(agentsDir, e.name),\n }))\n : [];\n return [...planned.filter((e) => !e.target.startsWith(\".claude/agents/\")), ...onDisk];\n}\n\n/**\n * Codex / OpenCode / Antigravity 산출물 sub-section.\n * v26.78.1 (R2): antigravity 추가 — `--cli antigravity` 시 산출물 invisible 이던 버그 fix.\n * 산출물 report 가 없거나 해당 CLI 미선택 시 출력 없음 (이전 executeSpec 의 게이트 if 이동).\n */\nexport function renderCliArtifacts(\n log: (msg: string) => void,\n spec: InstallSpec,\n report: InstallReport,\n): void {\n const hasArtifacts = Boolean(report.codex || report.opencode || report.antigravity);\n const cliSelected =\n targetsInclude(spec.cli, \"codex\") ||\n targetsInclude(spec.cli, \"opencode\") ||\n targetsInclude(spec.cli, \"antigravity\");\n if (!hasArtifacts || !cliSelected) {\n return;\n }\n log(unifiedSection(formatCliPhaseTitle(spec.cli)));\n log(\"\");\n // AGENTS.md is shared across Codex/OpenCode — render once with shared note\n if (report.codex && report.opencode) {\n log(assetRow(\"success\", \"AGENTS.md\", \"shared (Codex + OpenCode)\"));\n } else if (report.codex || report.opencode) {\n log(assetRow(\"success\", \"AGENTS.md\", `from ${HARNESS_ANCHOR_FILE}`));\n }\n if (report.codex) {\n log(assetRow(\"success\", \".codex/config.toml\", \"settings + [mcp_servers.*]\"));\n log(assetRow(\"success\", \".codex/hooks/\", `${report.codex.hookFiles.length} files`));\n if (report.codex.skillFiles.length > 0) {\n log(\n assetRow(\n \"success\",\n \".agents/skills/<id>/\",\n `${countSkillDirs(report.codex.skillFiles)} skills`,\n ),\n );\n }\n // Codex global opt-in (D16) — config.toml trust entry, only when explicitly enabled.\n if (report.codexOptIn?.trustEntry.enabled) {\n const trust = report.codexOptIn.trustEntry;\n const kind = trust.status === \"error\" ? \"skip\" : \"success\";\n const meta =\n trust.status === \"registered\"\n ? '[projects.\"<dir>\"] trust_level=\"trusted\"'\n : trust.status === \"already-present\"\n ? \"already present\"\n : (trust.message ?? \"error\");\n log(assetRow(kind, \"~/.codex/config.toml trust entry\", meta));\n }\n }\n if (report.opencode) {\n log(assetRow(\"success\", \"opencode.json\", \"$schema + 5 keys\"));\n if (report.opencode.skillFiles.length > 0) {\n log(\n assetRow(\n \"success\",\n \".agents/skills/\",\n `${countSkillDirs(report.opencode.skillFiles)} dev-method skills (codex·antigravity 와 같은 자리)`,\n ),\n );\n }\n // ADR-081 — 옛 커맨드 사본을 지웠으면 말한다. 조용히 지우면 사용자는 자기가 쓰던\n // 슬래시 커맨드가 왜 사라졌는지 알 방법이 없다 (스킬로 옮겨져 이름은 그대로 뜬다).\n if (report.opencode.retiredCommands.length > 0) {\n log(\n assetRow(\n \"skip\",\n \".opencode/commands/\",\n `${report.opencode.retiredCommands.length} retired · 같은 스킬이 .agents/skills/ 로 옮겨졌다 (백업 남김)`,\n ),\n );\n }\n }\n // v26.78.1 (R2) — Antigravity 산출물: rules (항상) + dev-method skills.\n if (report.antigravity) {\n if (report.antigravity.rulesFile) {\n log(assetRow(\"success\", \".agents/rules/uzys-harness.md\", `from ${HARNESS_ANCHOR_FILE}`));\n }\n if (report.antigravity.skillFiles.length > 0) {\n log(\n assetRow(\n \"success\",\n \".agents/skills/<id>/\",\n `${countSkillDirs(report.antigravity.skillFiles)} skills`,\n ),\n );\n }\n }\n log(\"\");\n}\n\n/** 최종 Summary (STATUS / TRACKS / CLI / HOOK / WARN / OPT-IN / NEXT). */\nexport function renderFinalSummary(\n log: (msg: string) => void,\n spec: InstallSpec,\n report: InstallReport,\n fromWizard: boolean,\n): void {\n // v26.63.2 — Summary 도 unifiedSection 으로 통일 (━━ marker).\n log(unifiedSection(\"Summary\"));\n log(\"\");\n log(infoRow(\"STATUS\", c.green(\"Install complete\")));\n log(infoRow(\"TRACKS\", report.installedTracks.join(\", \")));\n // v26.63.4 (P3): install header `CLI` 와 Summary `CLIs` 라벨 불일치 → `CLI` 로 통일.\n // v26.78.1 (R2): pairwise if-chain → spec.cli derive. antigravity 누락 + claude 무조건\n // prepend(claude 미선택 시에도 \"Claude\" 표기) 버그 fix. 헤더와 동일 SSOT.\n log(infoRow(\"CLI\", spec.cli.map((b) => CLI_SUMMARY_LABELS[b]).join(\" · \")));\n // M-1 — settings.json 이 가리키던 없는 스크립트를 지웠으면 **소리를 낸다.** 무음 no-op 은\n // 이 처방을 채택할 때 명시적 기각 사유였다: 지금 유일한 파손 신호(bash exit 127)를 지우면서\n // 아무 말도 안 하면, 다음에 참조가 깨져도 아무도 모른다 (`no-false-ship` 원칙 5).\n // update 분기(아래 renderPhase1Rows — renderUpdateSummary 는 STATUS/BACKUP 만 찍는다)와\n // **같은 라벨·같은 정보량**을 쓰고, 어느 파일이 지워졌는지\n // `.claude/` 기준 상대경로로 함께 보여준다 — 파일명만으로는 사용자가 못 찾는다.\n if (report.staleHookRefs.length > 0) {\n log(\n infoRow(\n \"HOOK\",\n c.yellow(\n `settings.json stale hook refs · ${report.staleHookRefs.length} removed ` +\n `(${report.staleHookRefs.join(\", \")})`,\n ),\n ),\n );\n }\n if (report.external && report.external.skipped > 0) {\n log(\"\");\n log(\n infoRow(\n \"WARN\",\n c.yellow(\n `${report.external.skipped} external asset${report.external.skipped > 1 ? \"s\" : \"\"} skipped (see Phase 2 above)`,\n ),\n ),\n );\n }\n // v26.102.0 (ADR-031) — v26.88.0 의 NOTE(plugin-kind 만 자체 재계산)를 대체: SSOT =\n // report.external.excludedByCli. 구 NOTE 는 ⊘ 고지와 다른 계산식(shell-script 누락)이라\n // 같은 화면에서 숫자가 어긋났고, claude 를 함께 골라 실제 설치된 경우에도 \"not installed\"\n // 를 찍었다 (SOD 리뷰 F4 — no-false-ship \"동일 목록 2곳 하드코딩 금지\").\n if (report.external && report.external.excludedByCli.length > 0) {\n const excluded = report.external.excludedByCli;\n log(\"\");\n log(\n infoRow(\n \"EXCLUDED\",\n c.dim(\n `${excluded.length} asset${excluded.length > 1 ? \"s\" : \"\"} not installed — outside [${spec.cli.join(\", \")}] reach: ${excluded.map((a) => a.id).join(\", \")}`,\n ),\n ),\n );\n }\n // v26.71.1 — experimental(T3) opt-in discoverability (Transparent Defaults — 숨김 0건).\n // 비대화형(--track) 에서 condition 은 맞지만 T3 라 default 제외된 자산을 --with 안내.\n // wizard 모드는 이미 ⚠ 배지로 노출하므로 skip.\n if (!fromWizard) {\n const optIn = experimentalOptInCandidates(spec);\n if (optIn.length > 0) {\n log(\"\");\n log(\n infoRow(\n \"OPT-IN\",\n c.dim(\n `${optIn.length} experimental available — add with --with <id>: ${optIn.map((a) => a.id).join(\", \")}`,\n ),\n ),\n );\n }\n }\n log(\"\");\n const primary = (spec.cli.includes(\"claude\") ? \"claude\" : spec.cli[0]) ?? \"claude\";\n const label = CLI_SUMMARY_LABELS[primary];\n log(infoRow(\"NEXT\", `Open ${c.bold(label)} — installed rules & skills are now active`));\n const scaffoldFiles = scaffoldFilesForCli(spec.cli);\n if (scaffoldFiles.length > 0) {\n // ADR-084 — `audit-harness-fit` 의 populate 모드가 같은 스캐폴드를 리포 근거로 채운다.\n // **실제로 깔린 경우에만** 말한다: 안 깔린 스킬을 부르라는 안내는 \"advertised ≠ real\" 이다.\n // 설치 여부는 설치기와 같은 spec 으로 센다(위 ASSETS 줄과 같은 이유).\n const auditInstalled =\n buildManifestSpec(spec).selectedInternalSkills?.includes(\"audit-harness-fit\");\n const populateHint = auditInstalled\n ? `, or ask the ${c.bold(\"audit-harness-fit\")} skill to fill it from repository evidence`\n : \"\";\n log(\n infoRow(\n \"FILL\",\n `${scaffoldFiles.map((f) => c.bold(f)).join(\" · \")} — a fill-in scaffold. Open and paste each ${c.bold(\"<!-- FILL: … -->\")} prompt to your agent to tailor it to this project${populateHint}`,\n ),\n );\n }\n log(\"\");\n}\n\n/**\n * ADR-086 — `skillFiles` 는 이제 스킬 **디렉터리 전체**의 파일이다(SKILL.md + references/ …).\n * 화면의 \"N skills\" 는 파일 수가 아니라 `<id>` 디렉터리 수여야 한다 — 8종을 골랐는데 \"13 skills\"\n * 로 뜨면 설치자가 자기가 안 고른 것이 깔렸다고 읽는다.\n */\nexport function countSkillDirs(skillFiles: ReadonlyArray<string>): number {\n const ids = new Set<string>();\n for (const f of skillFiles) {\n const m = /[\\\\/]\\.agents[\\\\/]skills[\\\\/]([^\\\\/]+)[\\\\/]/.exec(f);\n if (m?.[1] !== undefined) ids.add(m[1]);\n }\n return ids.size;\n}\n\n/**\n * Which project-context scaffold files a given CLI selection actually writes:\n * `CLAUDE.md` only for a claude install, `AGENTS.md` only for a non-claude CLI.\n * The FILL hint must name only files that were written — advertising a file that\n * a given `--cli` never produced is the no-false-ship \"advertised ≠ real\" trap.\n */\nexport function scaffoldFilesForCli(cli: ReadonlyArray<CliBase>): string[] {\n const files: string[] = [];\n if (cli.includes(\"claude\")) {\n files.push(\"CLAUDE.md\");\n }\n if (cli.some((target) => target !== \"claude\")) {\n files.push(\"AGENTS.md\");\n }\n return files;\n}\n\nfunction formatAssetMeta(asset: ExternalAsset, version?: string): string {\n // v26.56.0 (F3) — description 제거. onAssetStart 의 → 라인이 이미 description 표시.\n // result row 는 method + source 만 간결하게 → terminal 120 char 안 wrap 방지.\n // v26.59.0 — plugin / npm-global 에 한해 version 표시 (path 기반 추출).\n const m = asset.method;\n const v = version ? ` ${c.dim(`v${version.replace(/^v/, \"\")}`)}` : \"\";\n switch (m.kind) {\n case \"skill\":\n // v26.63.3 (clarify M1): skill name 이 asset id 와 동일하면 중복 segment 생략.\n // \"skill · pbakaus/impeccable · impeccable\" → \"skill · pbakaus/impeccable\"\n if (m.skill && m.skill !== asset.id) return `skill · ${m.source} · ${m.skill}`;\n return `skill · ${m.source}`;\n case \"plugin\":\n return `plugin · ${m.pluginId}${v}`;\n case \"npm\":\n // A2 (Promise audit) — ADR-020 후 npm 자산 default 는 `--save-dev`(project), `-g` 는 global scope 만.\n // 라벨에 \"-g\" 고정은 scope 거짓 표기 → scope-중립 \"npm\" 으로 정정.\n // v26.80.0 — pinned 버전 표기 (Transparent Defaults: 실행되는 정확한 버전 노출).\n return `npm · ${m.pkg}@${m.version}`;\n case \"npx-run\":\n return `npx · ${m.cmd}@${m.version}`;\n case \"shell-script\":\n return `bash · ${m.script}`;\n case \"internal\":\n // v26.81.0 (ADR-022) — 내부 템플릿 자산 (Phase 1 manifest 가 설치 주체).\n return `internal · templates (${m.key})`;\n }\n}\n\n/**\n * Phase 1 rows 출력. baseline-complete progress event에서 호출 — 외부 자산 설치\n * 시작 전 즉시 화면에 표시되어야 한다 (멈춰 보임 방지).\n */\nfunction renderPhase1Rows(\n log: (msg: string) => void,\n baseline: BaselineReport,\n verbose = false,\n withEcc = false,\n // v26.102.0 (ADR-031) — ecc 힌트의 `--with ecc-plugin` 안내는 claude 도달 시에만 참\n // (plugin 은 claude 전용 — codex 단독에선 그 명령이 no-op, SOD 리뷰 F2).\n claudeSelected = true,\n): void {\n // Update mode rows\n if (baseline.updateMode) {\n if (baseline.backup) {\n log(assetRow(\"success\", \"backup\", shortenPath(baseline.backup)));\n }\n for (const [dir, count] of Object.entries(baseline.updateMode.updated)) {\n if (count > 0) log(assetRow(\"success\", dir, `${count} files updated`));\n }\n // #283 — 릴리즈로 새로 생긴 자산. 갱신 건수에 합치지 않고 따로 낸다: 사용자가 안 만든\n // 파일이 늘어난 것이므로 \"몇 개 갱신\"과는 다른 사실이고, 조용하면 자기 것으로 오인한다.\n // 한 줄에 이어 붙이지 않는다 — 레거시 설치본에서는 십수 개가 한 번에 나온다.\n for (const path of baseline.updateMode.installedNew) {\n log(assetRow(\"success\", path, \"added by this release\"));\n }\n // 원인이 다르면 문구도 달라야 한다. 이쪽은 전에 깔아 준 적이 있는 파일이라 사용자가\n // 지웠을 수 있다 — \"이번 릴리즈에 추가됨\"이라고 적으면 그 사용자에게는 거짓말이고,\n // 자기가 지운 파일이 왜 돌아왔는지 추적할 단서가 사라진다.\n for (const path of baseline.updateMode.restored) {\n log(assetRow(\"success\", path, \"was missing — reinstalled (delete it again if intentional)\"));\n }\n // 깔지 **못한** 것은 더 크게 말해야 한다. 훅은 배선이 있어야 발화하는데 update 는\n // settings.json 을 동기화하지 않는다 — 조용하면 사용자는 최신 상태라고 믿는다.\n if (baseline.updateMode.needsReinstall.length > 0) {\n log(\n assetRow(\n \"skip\",\n \"needs reinstall\",\n `${baseline.updateMode.needsReinstall.join(\", \")} · new in this release but update cannot wire them — run \\`agent-harness install\\` to get them`,\n ),\n );\n }\n for (const [dir, removed] of Object.entries(baseline.updateMode.pruned)) {\n if (removed.length > 0) {\n log(assetRow(\"skip\", `${dir} orphan prune`, `${removed.length} removed`));\n }\n }\n // #480 — 고르지 않은 묶음은 건드리지 않았음을 말한다. 조용하면 \"update 를 돌렸는데 룰이\n // 그대로다\"가 결함으로 읽힌다.\n if (baseline.updateMode.skippedGroups.length > 0) {\n log(\n assetRow(\n \"skip\",\n \"not selected — untouched\",\n `${baseline.updateMode.skippedGroups.join(\", \")} · run again with --only or the wizard to include`,\n ),\n );\n }\n if (baseline.updateMode.claudeMdUpdated) {\n log(assetRow(\"success\", HARNESS_ANCHOR_FILE, \"refreshed from template\"));\n }\n // #480 — 편집분을 백업했다는 사실은 반드시 화면에 남긴다(룰 `edited policy files` 행과 같은 이유).\n if (baseline.updateMode.anchorBackedUp) {\n log(\n assetRow(\n \"skip\",\n `${HARNESS_ANCHOR_FILE} edited`,\n \"your edits backed up as *.backup-<time> — latest template is now active\",\n ),\n );\n }\n // P5 이행 (ADR-060) — v26.140.0 이전 설치본은 앵커가 `.claude/CLAUDE.md` 라 루트에 없다.\n // 이번 update 가 만든 앵커와 사용자 `CLAUDE.md` 에 얹은 import 줄을 **화면에 남긴다**:\n // 조용히 하면 앵커 계약이 바뀐 사실도, 자기 CLAUDE.md 가 한 줄 늘었다는 사실도 모른다.\n if (baseline.updateMode.anchorCreated) {\n log(assetRow(\"success\", HARNESS_ANCHOR_FILE, \"created from template (anchor migration)\"));\n }\n if (baseline.updateMode.rootImportAdded) {\n log(assetRow(\"success\", \"CLAUDE.md\", `${HARNESS_IMPORT_LINE} import added`));\n }\n // ADR-085 — 관리 블록 안(상시 스킬 안내)이 깔린 스킬을 따라 바뀌었으면 말한다. 사용자\n // 파일의 한 구간이 바뀐 것이라 조용히 넘기지 않는다.\n if (baseline.updateMode.rootBlockRefreshed) {\n log(\n assetRow(\n \"success\",\n \"CLAUDE.md\",\n \"harness block refreshed (skills that apply continuously)\",\n ),\n );\n }\n // 구 앵커는 지우지 않는다(사용자 편집 여부 판정 불가) — 대신 죽은 사본이라는 사실을 알린다.\n if (baseline.updateMode.legacyAnchor) {\n log(\n assetRow(\n \"skip\",\n baseline.updateMode.legacyAnchor,\n `legacy anchor · no longer updated — content now in ${HARNESS_ANCHOR_FILE}; delete it when you no longer need it`,\n ),\n );\n }\n // v26.126.0 (R-3a) — 편집분을 백업했다는 사실은 **반드시 화면에 남긴다**. 갱신 건수만 보이면\n // 사용자는 자기가 고친 내용이 어디로 갔는지 알 수 없고, 그게 R-3a 를 만든 침묵과 같은 실패다.\n if (baseline.updateMode.skillsBackedUp.length > 0) {\n log(\n assetRow(\n \"skip\",\n \".claude/skills edited files\",\n `${baseline.updateMode.skillsBackedUp.length} backed up as *.backup-<time>`,\n ),\n );\n }\n // #477 — 번들에서 사라진 파일을 지웠다는 사실도 화면에 남긴다. 조용히 지우면 사용자는\n // 파일이 왜 없어졌는지 모르고, 안 지우면 옛 서식이 디스크에 남는다 — 둘 다 침묵이 문제다.\n if (baseline.updateMode.skillsPruned.length > 0) {\n log(\n assetRow(\n \"success\",\n \".claude/skills not in bundle\",\n `${baseline.updateMode.skillsPruned.length} files deleted · ${baseline.updateMode.skillsPruned.join(\", \")}`,\n ),\n );\n }\n // 2026-08-02 (ADR-062) — 다른 도구(`npx skills add`)가 소유한 자리는 건너뛴다. 그 사실을\n // 안 보이면 사용자는 \"이 스킬만 왜 안 갱신되지\"를 추적할 방법이 없고, 반대로 조용히\n // 덮어썼다면 자기 저장소가 바뀐 줄도 모른다. 둘 다 침묵이 문제라 건수가 아니라 이름을 낸다.\n // #343 — **종류를 단정하지 않는다.** 판정이 `isSymbolicLink()` 에서 \"디렉터리가 아닌 것\n // 전부\"로 넓어져 일반 파일·FIFO·깨진 링크도 이 행에 들어온다. 아래 install 행과 같은 어휘다.\n if (baseline.updateMode.skillsSkippedLinks.length > 0) {\n log(\n assetRow(\n \"skip\",\n \".claude/skills owned by another tool\",\n `${baseline.updateMode.skillsSkippedLinks.join(\", \")} · 그 자리가 우리 디렉터리가 아니라 갱신하지 않았다`,\n ),\n );\n }\n // #343 — 외부 CLI 산출물(`.agents/skills/<id>` 등)에서 같은 이유로 건너뛴 자리.\n // `.claude/skills linked` 와 나눠 내는 이유는 자리가 달라서다 — 옮겨야 할 경로를 그대로 낸다.\n if (baseline.updateMode.foreignOwned.length > 0) {\n log(\n assetRow(\n \"skip\",\n \"owned by another tool\",\n `${baseline.updateMode.foreignOwned.join(\", \")} · 그 자리가 우리 것이 아니라 건드리지 않았다`,\n ),\n );\n }\n // 2026-08-16 (ADR-072) — `.mcp-allowlist` 은퇴. 이 줄이 없으면 사용자는 자기 저장소에서\n // 파일 하나가 사라진 것만 보고 이유를 못 찾는다. 백업 경로를 함께 내는 이유는 그게\n // \"되돌릴 수 있다\"의 유일한 증거이기 때문이다 — 경로 없이 \"백업했다\"는 안내는 검증 불가다.\n if (baseline.updateMode.mcpAllowlistRetired) {\n log(\n assetRow(\n \"skip\",\n \".mcp-allowlist\",\n `retired · the hook that read it is gone — backed up as ${shortenPath(baseline.updateMode.mcpAllowlistRetired)}`,\n ),\n );\n }\n // ADR-089 (#445) — 은퇴한 에이전트. 스킬 은퇴 행(아래)과 같은 규율이다: 지우지 않고\n // **지워도 된다는 사실과 대신 쓸 것**을 말한다. 대안을 함께 적는 이유는 은퇴가 곧 기능\n // 상실로 읽히면 사용자가 파일을 붙들기 때문이다 — 여기서는 벤더 기본 기능이 대안이다.\n for (const id of baseline.updateMode.retiredAgents) {\n const instead = RETIRED_AGENTS.find((a) => a.id === id)?.instead;\n log(\n assetRow(\n \"skip\",\n \"agents\",\n `${id} · 이 릴리즈에서 은퇴 — .claude/agents/${id}.md 를 지워도 된다` +\n (instead === undefined ? \"\" : ` · ${instead}`),\n ),\n );\n }\n // ADR-090 (#458) — 트랙에서 **강등된** 에이전트. 위 은퇴 행과 사용자가 할 일은 같고(지워도\n // 된다) 사실은 다르다 — 이건 **다른 트랙에는 여전히 가는** 자산이다. 그래서 \"은퇴\"라 하지\n // 않고 어느 트랙 전용인지를 말한다. 목록을 여기 적지 않으려고 배선 SSOT(`TRACK_AGENTS`)를\n // 돌면서 보고된 id 만 찍는다 — 트랙명도 그 패턴에서 그대로 derive 한다.\n for (const [id, pattern] of TRACK_AGENTS) {\n if (!baseline.updateMode.demotedAgents.includes(id)) continue;\n log(\n assetRow(\n \"skip\",\n \"agents\",\n `${id} · 이 트랙에서는 더 이상 설치하지 않는다 — ${pattern.split(\"|\").join(\" · \")} 트랙 전용 · .claude/agents/${id}.md 를 지워도 된다`,\n ),\n );\n }\n // v26.132.0 (ADR-047) — 룰·훅 편집분도 같은 이유로 노출. 자산 종류에 따라 보이고 안 보이면\n // 사용자는 \"룰은 백업 안 되나 보다\"로 학습한다.\n if (baseline.updateMode.policyBackedUp.length > 0) {\n log(\n assetRow(\n \"skip\",\n \"edited policy files\",\n `${baseline.updateMode.policyBackedUp.length} backed up as *.backup-<time>`,\n ),\n );\n }\n // M-1 (표면 대칭) — fresh 분기(`renderFinalSummary`)와 **같은 정보량**으로 어느 파일이\n // 지워졌는지 나열한다. 논거는 update 쪽이 더 강하다: install 은 settings.json 을 템플릿으로\n // 덮어쓰지만 update 는 사용자가 손댄 settings.json 을 **제자리에서** 고치는 유일한 경로라,\n // 사용자 자신이 적어 넣은 훅이 실제로 사라질 수 있는 쪽이다. 건수만 찍으면 그 사용자는\n // 자기 훅이 왜 없어졌는지 추적할 방법이 없다.\n const staleRefs = baseline.updateMode.staleHookRefs;\n if (staleRefs.length > 0) {\n log(\n assetRow(\n \"skip\",\n \"settings.json stale hook refs\",\n `${staleRefs.length} removed (${staleRefs.join(\", \")})`,\n ),\n );\n }\n // v26.134.0 (R-3j-A · ADR-049) — 외부 CLI 산출물도 갱신 대상이 됐다. 화면에 안 보이면\n // codex/opencode 사용자는 update 가 자기 CLI 를 건드렸는지 알 수 없고, 그 침묵이 곧\n // \"update 는 .claude/ 만 한다\"는 오해를 유지시킨다.\n if (baseline.updateMode.externalUpdated > 0) {\n log(\n assetRow(\n \"success\",\n \"external CLI artifacts\",\n `${baseline.updateMode.externalUpdated} files updated`,\n ),\n );\n }\n if (baseline.updateMode.externalBackedUp.length > 0) {\n log(\n assetRow(\n \"skip\",\n \"edited external CLI files\",\n `${baseline.updateMode.externalBackedUp.length} backed up as *.backup-<time>`,\n ),\n );\n }\n // #374 — 외부 스킬(`npx skills add` 로 깐 것)은 위 행과 **다른 자산**이다. 이 행이 없던\n // 동안 사용자는 `external CLI artifacts` 를 보고 스킬도 갱신된 줄 알았고, 실제로는 첫 설치\n // 판본이 영영 남았다. 그래서 성공·실패·판정불가가 **각자 한 줄**을 갖는다 — 셋을 같은\n // 침묵으로 합치는 것이 이 결함의 정체였다.\n if (baseline.updateMode.externalSkillsRefreshed > 0) {\n log(\n assetRow(\n \"success\",\n \"external skills\",\n `${baseline.updateMode.externalSkillsRefreshed} refreshed from upstream`,\n ),\n );\n }\n if (baseline.updateMode.externalSkillsFailed.length > 0) {\n // 실패가 여럿이면 이름만 낸다 — 사유를 전부 이어 붙이면 한 줄이 1,000자를 넘어 표\n // 정렬이 깨진다(리뷰 NIT-1). 사유는 첫 건만 대표로 싣는다: 대개 같은 원인이다.\n const failed = baseline.updateMode.externalSkillsFailed;\n const head = failed[0] as { id: string; message: string };\n const meta =\n failed.length === 1\n ? `${head.id}: ${head.message}`\n : `${failed.map((f) => f.id).join(\", \")} (${failed.length}건) · 예: ${head.message}`;\n log(assetRow(\"skip\", \"external skills\", `${meta} · 다음 update 에서 다시 시도한다`));\n }\n // 기록에는 있는데 카탈로그에서 사라진 자산 — \"갱신했다\"에 섞이면 사용자는 일부만 갱신된\n // 것을 모른다.\n //\n // ADR-088 (#426) — 한 문구로 뭉치지 않는다. 사용자가 할 일이 셋으로 갈린다: 개명된 것은\n // **새 이름으로 다시 받아야** 하고, 은퇴한 것은 **지워도 되고**, 나머지는 우리가 모른다.\n // 같은 줄로 말하면 은퇴한 스킬의 새 판을 찾아 헤맨다. 매핑은 카탈로그가 소유한다.\n if (baseline.updateMode.externalSkillsNotInCatalog.length > 0) {\n const ids = baseline.updateMode.externalSkillsNotInCatalog;\n for (const id of ids) {\n const renamedTo = RENAMED_SKILL_IDS[id];\n if (renamedTo) {\n log(\n assetRow(\n \"skip\",\n \"skills\",\n `${id} 는 ${renamedTo} 가 됐다 · ${renamedTo} 는 new-skills 묶음이 깐다 · .claude/skills/${id} 는 지워도 된다`,\n ),\n );\n } else if (RETIRED_SKILL_IDS.includes(id)) {\n log(\n assetRow(\n \"skip\",\n \"skills\",\n `${id} · 이 릴리즈에서 은퇴 — .claude/skills/${id} 를 지워도 된다`,\n ),\n );\n }\n }\n const unknown = ids.filter((id) => !RENAMED_SKILL_IDS[id] && !RETIRED_SKILL_IDS.includes(id));\n if (unknown.length > 0) {\n log(\n assetRow(\n \"skip\",\n \"external skills\",\n `${unknown.join(\", \")} · 카탈로그에 없어 갱신 대상이 아니다`,\n ),\n );\n }\n }\n // 레거시 설치본 — \"갱신할 게 없다\"와 \"무엇을 갱신할지 모른다\"는 다른 사실이다.\n if (baseline.updateMode.externalSkillsUnknown) {\n log(\n assetRow(\n \"skip\",\n \"external skills\",\n \"설치 기록이 없어 갱신 대상을 판정할 수 없다 · `agent-harness install` 로 다시 깔면 기록된다\",\n ),\n );\n }\n return;\n }\n\n // Fresh / add / reinstall — Phase 1 rows\n // audit SEC-1/CODE-2 — 기존 settings.json·CLAUDE.md 를 덮어쓰기 전 백업했으면 fail-loud 노출.\n if (baseline.backups) {\n for (const b of baseline.backups) {\n log(assetRow(\"success\", \"backup\", shortenPath(b)));\n }\n }\n // v26.57.1 (F2) — multi-line 구조 (header + use + files). visual hierarchy + width-safe.\n // 사용자 image 검증 (2026-05-17): 단일 라인 description 이 width 좁을 때 wrap → 들여쓰기 깨짐.\n const cats = baseline.categories;\n if (cats) {\n // v26.63.0 — files 라인은 verbose 옵션 시만. 기본은 카운트 + use 1 줄.\n // v26.63.2 — polish: label + count 칼럼 fixed-width 정렬 (28 char). spacing scale 일관.\n const phase1Row = (label: string, count: number, useText: string, files?: string[]) => {\n const labelCol = `${c.bold(label)} ${c.dim(`(${count})`)}`;\n const padded = padDisplay(labelCol, 28);\n log(` ${c.green(\"✓\")} ${padded} ${c.dim(useText)}`);\n if (verbose && files && files.length > 0) {\n log(` ${c.dim(\"└ files:\")} ${c.dim(files.join(\", \"))}`);\n }\n };\n\n if (cats.rules.length > 0) {\n phase1Row(\n \"rules\",\n cats.rules.length,\n \"coding · git/PR · tests · ship checklist · MCP policy\",\n cats.rules,\n );\n }\n if (cats.agents.length > 0) {\n // ADR-090 (#452) — 라벨을 자산 중립으로. 은퇴·강등으로 목록이 트랙마다 달라졌고, 이름을\n // 부르면 화면이 없는 자산을 계속 부른다(ADR-073 의 `/ecc:*` 와 같은 형태). 실제 이름은\n // `--verbose` 의 files 줄이 낸다.\n phase1Row(\n \"agents\",\n cats.agents.length,\n \"independent verifier · implementation lane · domain lanes (track)\",\n cats.agents,\n );\n }\n if (cats.hooks.length > 0) {\n phase1Row(\"hooks\", cats.hooks.length, \"session-start · protect-files\", cats.hooks);\n }\n // 2026-08-16 (ADR-073) — 라벨에서 `/ecc:*` 를 뺐다. 명령 템플릿이 하나도 남지 않아 이 행은\n // 지금 뜨지 않지만, 분류기(`.claude/commands/` 접두)는 일반형이라 명령이 다시 생기면 그대로\n // 센다. 그때 화면이 없어진 자산의 이름을 부르지 않도록 라벨을 자산 중립으로 둔다.\n if (cats.commands > 0) {\n phase1Row(\"commands\", cats.commands, \"slash commands\");\n }\n if (cats.skills.length > 0) {\n phase1Row(\n \"skills\",\n cats.skills.length,\n \"bundled methodology skills (track · opt-in)\",\n cats.skills,\n );\n }\n } else {\n // v0.6.0 backwards compat — categories 없는 fakeReport 등\n log(assetRow(\"success\", \"rules + hooks + commands + agents\", `${baseline.filesCopied} files`));\n log(assetRow(\"success\", \"skeleton\", `${baseline.dirsCopied} dirs`));\n }\n // 2026-08-16 — 사용자가 3단계에서 **체크를 푼** 트랙 자산. 위 행들은 \"무엇이 깔렸는가\"만\n // 말하므로, 이 줄이 없으면 해제가 실제로 먹혔는지 화면에서 확인할 길이 없다. 0건이면 안 뜬다.\n // 이름을 전부 낸다 — 건수만 찍으면 어느 것이 빠졌는지 추적할 수 없다.\n if (baseline.baselineExcluded.length > 0) {\n // 디스크에 남은 것은 이름 옆에 표시한다. `add`·`reinstall` 은 이전 설치본을 지우지 않으므로\n // 표시가 없으면 화면이 \"빠졌다\"고 말하는 동안 그 룰은 계속 상주한다 (체크 해제 ≠ 제거).\n const onDisk = new Set(baseline.baselineExcludedOnDisk);\n const names = baseline.baselineExcluded.map((t) => {\n const name = t.replace(/^\\.claude\\//, \"\");\n return onDisk.has(t) ? `${name} (still on disk)` : name;\n });\n log(\n assetRow(\n \"skip\",\n \"excluded by you\",\n onDisk.size > 0\n ? `${names.length} — ${names.join(\", \")} · \"still on disk\" = 이전 설치본이라 지우지 않는다 (제거: agent-harness uninstall)`\n : `${names.length} — ${names.join(\", \")}`,\n ),\n );\n }\n // #343 — 자리가 남의 것이라 건너뛴 자산. 이 줄이 없으면 사용자는 자기가 3단계에서 고른\n // 스킬이 왜 없는지 알 방법이 없다 (설치는 성공으로 끝났으니 실패 메시지도 없다).\n // 어떻게 해야 받을 수 있는지까지 적는다 — 원인만 알려주는 안내는 다음 행동을 못 만든다.\n if (baseline.baselineForeignOwned.length > 0) {\n // 경로를 **자르지 않는다**. 목록에 `.claude/skills/<id>` 와 `.agents/skills/<id>` 가 섞이고\n // (전자는 Claude Code, 후자는 codex·antigravity 자리), 접두를 지우면 둘이 같은 것처럼 보인다.\n // 종류를 열거하지도 않는다 — 링크 말고 파일·FIFO·하드링크도 실제로 도달 가능하다.\n log(\n assetRow(\n \"skip\",\n \"owned by another tool\",\n `${baseline.baselineForeignOwned.length} — ${baseline.baselineForeignOwned.join(\", \")} · 그 자리가 우리 것이 아니라 건드리지 않았다 · 하네스 판본을 받으려면 그 자리를 옮기고 재설치`,\n ),\n );\n }\n // v26.63.4 (P3): Templates section 의 assetRow 호출 labelWidth=28 명시 → phase1Row 와 column 정렬.\n // default 40 은 External assets 의 긴 asset id (python-performance-optimization 등) 용 — 별개.\n const TEMPLATES_COL = 28;\n if (baseline.rootClaudeMd) {\n const n = baseline.rootClaudeMd.tracks.length;\n // 기존 사용자 파일에는 스캐폴드를 쓰지 않는다 — 두 경우를 같은 문구로 보고하면 그게 곧\n // 거짓 보고다 (P5 · ADR-060: 루트 CLAUDE.md 는 더 이상 덮어쓰지 않는다).\n log(\n assetRow(\n \"success\",\n \"CLAUDE.md (root)\",\n baseline.rootClaudeMd.created\n ? `fill-in scaffold + @import · ${n} track${n > 1 ? \"s\" : \"\"} noted`\n : `@import ${HARNESS_ANCHOR_FILE} (body preserved)`,\n TEMPLATES_COL,\n ),\n );\n }\n // v26.108.0 (ADR-037) — CI 스캐폴드 (opt-in). no-clobber: 기존 파일 보존은 skip 행으로\n // 정직 보고 (숨기면 \"설치됨\" 오인 — no-false-ship).\n if (baseline.ciScaffold) {\n for (const f of baseline.ciScaffold.written) {\n log(assetRow(\"success\", f, \"CI scaffold (fill-in template)\", TEMPLATES_COL));\n }\n for (const f of baseline.ciScaffold.skippedExisting) {\n log(assetRow(\"skip\", f, \"exists — preserved (no overwrite)\", TEMPLATES_COL));\n }\n }\n if (baseline.skipped > 0) {\n log(\n assetRow(\n \"skip\",\n \"manifest entries (applies → false)\",\n `${baseline.skipped} skipped`,\n TEMPLATES_COL,\n ),\n );\n }\n if (baseline.backup) {\n log(assetRow(\"success\", \"backup\", shortenPath(baseline.backup), TEMPLATES_COL));\n }\n const mcpList = baseline.mcpServers.join(\", \") || \"(none)\";\n log(assetRow(\"success\", \".mcp.json\", mcpList, TEMPLATES_COL));\n // v26.63.3 (distill H2): ECC fallback hint — Templates section 마지막에 통합 표시.\n // withEcc=true (ECC plugin opt-in) 사용자에게는 hint 미표시.\n // 2026-08-18 (#338 리뷰) — 괄호절에서 대상 경로를 뺐다. 이 hint 는 claude 미선택 설치에서도\n // 뜨는데(`withEcc` 는 claude 를 안 고르면 항상 false, `baseline.categories` 는 CLI 중립\n // 경로에서도 truthy), 그 설치에는 `.claude/` 가 만들어지지 않는다. 옛 문구의 숫자도 틀렸다\n // (실측 4 agents + 6 skills + 0 commands) — 숫자를 고쳐 적는 대신 세지 않아도 참인 문장으로.\n if (!withEcc && baseline.categories) {\n log(\"\");\n log(` ${c.dim(\"·\")} ${c.dim(\"ECC plugin not selected — cherry-pick fallback active\")}`);\n if (claudeSelected) {\n log(` ${c.dim(\"·\")} ${c.dim(\"Use --with ecc-plugin to install ECC plugin instead\")}`);\n }\n }\n if (baseline.envFiles.envExampleCreated) {\n log(assetRow(\"success\", \".env.example\", \"Supabase token guide\"));\n }\n if (baseline.envFiles.gitignoreEnvAdded) {\n log(assetRow(\"success\", \".gitignore\", \"+ .env\"));\n }\n if (baseline.envFiles.gitignoreNpxSkillsAdded.length > 0) {\n log(\n assetRow(\n \"success\",\n \".gitignore\",\n `+ ${baseline.envFiles.gitignoreNpxSkillsAdded.join(\" \")} (agent CLI / harness artifacts)`,\n ),\n );\n }\n log(\"\");\n}\n\nfunction formatOptions(spec: InstallSpec): string {\n // v26.81.0 (ADR-022) — 자산 플래그 13종 삭제 후 동작 옵션만. 키 순회로 enumeration drift 차단.\n const flags = (Object.keys(spec.options) as Array<keyof OptionFlags>)\n .filter((k) => spec.options[k])\n .map((k) =>\n k\n .replace(/^with/, \"\")\n .replace(/([a-z])([A-Z])/g, \"$1-$2\")\n .toLowerCase(),\n );\n // v26.63.3 (clarify H1): \"(defaults only)\" 모호 → \"(none added)\" 명료.\n return flags.length > 0 ? flags.join(\", \") : c.dim(\"(none added)\");\n}\n\n/**\n * Shorten an absolute path for display:\n * /Users/foo/bar → ~/bar (HOME relative)\n * /private/tmp/x.X → /tmp/x.X\n * /a/very/long/path → …/long/path (≥3 segs from end if > 50 chars)\n *\n * v26.48.0 — export for direct unit test (branch coverage 복구).\n */\nexport function shortenPath(p: string): string {\n if (p.length <= 50) return p;\n const home = process.env.HOME ?? \"\";\n if (home && p.startsWith(home)) {\n const rel = p.slice(home.length);\n return `~${rel.startsWith(\"/\") ? \"\" : \"/\"}${rel}`;\n }\n // private/tmp prefix on macOS — drop /private\n if (p.startsWith(\"/private/tmp/\")) {\n return p.slice(\"/private\".length);\n }\n // Last 3 segments\n const segs = p.split(\"/\").filter(Boolean);\n if (segs.length > 3) {\n return `…/${segs.slice(-3).join(\"/\")}`;\n }\n return p;\n}\n\n/**\n * v0.7.0 — CliTargets에서 codex/opencode 포함 여부에 따라 title 결정.\n * Phase 3는 codex 또는 opencode 1개 이상 포함 시 호출됨.\n * v26.48.0 — export for direct unit test (branch coverage 복구).\n */\nexport function formatCliPhaseTitle(targets: CliTargets): string {\n // v26.78.1 (R2) — antigravity 추가. 누락 시 `--cli antigravity` 산출물 헤더가\n // \"CLI artifacts\" generic 으로만 떠 antigravity 가 invisible 했음.\n const labels: string[] = [];\n if (targets.includes(\"codex\")) labels.push(\"Codex\");\n if (targets.includes(\"opencode\")) labels.push(\"OpenCode\");\n if (targets.includes(\"antigravity\")) labels.push(\"Antigravity\");\n return labels.length > 0 ? `${labels.join(\" + \")} artifacts` : \"CLI artifacts\";\n}\n","/**\n * Preset → Step 2 의 추천 ✓ 자산 매핑 (v26.44.0).\n *\n * Step 1 에서 선택된 preset 들의 condition 을 만족하는 외부 자산(plugin/skill/npm/npx)\n * 의 id 를 반환한다. Step 2 multiselect 의 초기 체크 상태에 사용.\n *\n * Option-gated 자산은 추천에 포함 X — 사용자가 의식적으로 토글해야 함\n * (superpowers, addy-agent-skills, ECC suite 등).\n */\n\nimport { assetTrustTier, EXTERNAL_ASSETS, filterApplicableAssets } from \"./external-assets.js\";\nimport { DEFAULT_OPTIONS, type InstallSpec, type Track } from \"./types.js\";\n\n/**\n * preset 1개 또는 N개 선택 시 추천 ✓ 자산 id 의 안정 정렬 배열.\n * - DEFAULT_OPTIONS 로 호출 → option-gated 자산은 모두 false → 추천에서 제외.\n * - v26.71.0 (PRD v26-71 R6) — experimental(T3) 자산은 pre-check 제외 (opt-in). official/vetted 만 추천.\n * - 결과는 자산 id 알파벳 정렬 (deterministic).\n */\nexport function recommendedExternalAssets(presets: ReadonlyArray<Track>): ReadonlyArray<string> {\n if (presets.length === 0) {\n return [];\n }\n const apps = filterApplicableAssets(EXTERNAL_ASSETS, {\n tracks: presets,\n options: DEFAULT_OPTIONS,\n });\n return apps\n .filter((a) => assetTrustTier(a.id) !== \"experimental\")\n .map((a) => a.id)\n .sort();\n}\n\n/**\n * v26.82.0 (Phase R, S6) — preset recommended + userOverride 적용 후 최종 선택 자산 id (정렬).\n * 이전엔 install.ts `computeFinalAssets` ↔ interactive.ts `formatSummary` 에 동일 merge 가\n * 중복 구현 (v26.62.4 주석이 본 위치를 통합 지점으로 지목). 우선순위: forceExclude > forceInclude.\n */\nexport function finalSelectedAssets(\n tracks: ReadonlyArray<Track>,\n userOverride?: InstallSpec[\"userOverride\"],\n): string[] {\n const selected = new Set(recommendedExternalAssets(tracks));\n if (userOverride) {\n for (const id of userOverride.forceExclude) selected.delete(id);\n for (const id of userOverride.forceInclude) selected.add(id);\n }\n return [...selected].sort();\n}\n\n/**\n * v26.82.0 (Phase R, S6) — 자산 id 를 카테고리별로 묶어 정렬된 entries 반환 (출력 hierarchy 용).\n * install header ASSETS row 와 wizard confirm summary 가 공유.\n */\nexport function groupAssetsByCategory(assetIds: ReadonlyArray<string>): Array<[string, string[]]> {\n const map = new Map<string, string[]>();\n for (const id of assetIds) {\n const asset = EXTERNAL_ASSETS.find((a) => a.id === id);\n const cat = asset?.category ?? \"other\";\n const list = map.get(cat) ?? [];\n list.push(id);\n map.set(cat, list);\n }\n return [...map.entries()];\n}\n","/**\n * List command — v26.123.0 (F-1b).\n *\n * `.uzys-agent-harness/.harness-install.json` 을 사람이 읽는 표로 출력한다. 기록은 v26.64.0(ADR-020)부터\n * 있었지만 **사용자가 볼 수단이 없었다** — 무엇이 깔렸는지 알 수 없으면 항목별 제거(`--only`)의\n * 입력값도 알 수 없다. 본 커맨드가 그 입력값(자산 id)을 보여주는 곳이다.\n *\n * 읽기 전용 — 어떤 파일도 쓰지 않는다.\n */\n\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { join, resolve } from \"node:path\";\nimport { c, padDisplay, status } from \"../design.js\";\nimport {\n hashContent,\n type InstallLog,\n type InstallLogAsset,\n type InstallLogRootFile,\n installLogPath,\n readInstallLog,\n} from \"../install-log.js\";\n\nexport interface ListOptions {\n projectDir?: string;\n}\n\nexport interface ListActionDeps {\n log?: (msg: string) => void;\n err?: (msg: string) => void;\n exit?: (code: number) => never;\n}\n\nexport function listAction(options: ListOptions = {}, deps: ListActionDeps = {}): void {\n const log = deps.log ?? console.log;\n const err = deps.err ?? console.error;\n const exit = deps.exit ?? ((code: number) => process.exit(code) as never);\n\n const projectDir = resolve(options.projectDir ?? process.cwd());\n const installLog = readInstallLog(projectDir);\n if (!installLog) {\n err(status.failure(c.red(`ERROR: install log not found at ${installLogPath(projectDir)}`)));\n err(c.dim(\" Nothing installed here by agent-harness.\"));\n exit(1);\n return;\n }\n\n log(\"\");\n log(c.bold(\"uzys-agent-harness · installed\"));\n log(\"\");\n log(c.dim(` installed: ${installLog.installedAt}`));\n log(c.dim(` scope: ${installLog.scope}`));\n log(c.dim(` tracks: ${installLog.spec.tracks.join(\", \") || \"(none)\"}`));\n log(c.dim(` cli: ${installLog.spec.cli.join(\", \") || \"(none)\"}`));\n log(\"\");\n\n log(c.bold(` Assets (${installLog.assets.length})`));\n if (installLog.assets.length === 0) {\n log(c.dim(\" (none — 내부 템플릿만 설치됨)\"));\n }\n for (const line of formatAssetRows(installLog.assets)) {\n log(line);\n }\n\n log(\"\");\n log(c.bold(\" Templates\"));\n for (const line of formatTemplateRows(installLog, projectDir)) {\n log(line);\n }\n\n const rootRows = formatRootFileRows(installLog.rootFiles ?? [], projectDir);\n if (rootRows.length > 0) {\n log(\"\");\n log(c.bold(\" Root files\"));\n log(c.dim(\" (uninstall 이 지우지 않는다 — 사용자 내용이 섞인다)\"));\n for (const line of rootRows) log(line);\n }\n\n log(\"\");\n log(c.dim(\" remove one: agent-harness uninstall --only <id>\"));\n log(c.dim(\" remove all: agent-harness uninstall\"));\n log(\"\");\n exit(0);\n}\n\n/** 자산 행 — id / method / scope / version. global 은 uninstall 이 자동 삭제하지 않으므로 표시한다. */\nexport function formatAssetRows(assets: ReadonlyArray<InstallLogAsset>): string[] {\n const idWidth = Math.max(0, ...assets.map((a) => a.id.length));\n const methodWidth = Math.max(0, ...assets.map((a) => a.method.length));\n return assets.map((a) => {\n const marker = a.scope === \"global\" ? c.yellow(\"!\") : c.green(\"✓\");\n const version = a.version ? c.dim(` v${a.version}`) : \"\";\n const note = a.scope === \"global\" ? c.yellow(\" (manual removal — D16)\") : \"\";\n return ` ${marker} ${padDisplay(a.id, idWidth)} ${c.dim(padDisplay(a.method, methodWidth))} ${c.dim(a.scope)}${version}${note}`;\n });\n}\n\n/** templates 행 — 디렉토리 + root CLAUDE.md 수정 여부(uninstall 이 보존할지 여부와 같은 판정). */\nfunction formatTemplateRows(log: InstallLog, projectDir: string): string[] {\n const dirs = [log.templates.claudeDir, log.templates.codexDir, log.templates.opencodeDir].filter(\n (d): d is string => Boolean(d),\n );\n const rows = [` ${c.dim(dirs.join(\" \"))}`];\n const rootMd = log.templates.rootClaudeMd;\n if (rootMd) {\n const path = join(projectDir, rootMd.path);\n const modified = existsSync(path) && hashContent(readFileSync(path, \"utf8\")) !== rootMd.sha256;\n rows.push(\n modified\n ? ` ${c.dim(rootMd.path)} ${c.yellow(\"(수정됨 — uninstall 시 보존)\")}`\n : ` ${c.dim(rootMd.path)}`,\n );\n }\n return rows;\n}\n\n/**\n * v26.124.0 (F-1f) — `.claude/` 밖 루트 파일 행. uninstall 안내와 같은 규율로\n * **실재하는 것만** 낸다 (사용자가 이미 지운 파일을 인벤토리에 남기면 그게 거짓 기록이다).\n */\nfunction formatRootFileRows(\n rootFiles: ReadonlyArray<InstallLogRootFile>,\n projectDir: string,\n): string[] {\n const present = rootFiles.filter((f) => existsSync(join(projectDir, f.path)));\n const width = Math.max(0, ...present.map((f) => f.path.length));\n return present.map(\n (f) =>\n ` ${c.dim(padDisplay(f.path, width))} ${c.dim(f.change === \"created\" ? \"생성\" : \"병합\")} ${c.dim(f.notes.join(\" / \"))}`,\n );\n}\n\nexport function registerListCommand(cli: import(\"../cli.js\").Cli): void {\n cli\n .command(\"list\", \"Show what agent-harness installed in this project\")\n .option(\"--project-dir <path>\", \"[Project] Target project directory\", {\n default: process.cwd(),\n })\n /* v8 ignore next 3 — cac action callback. listAction 자체는 별도 tests 로 검증. */\n .action((options: ListOptions) => {\n listAction(options);\n });\n}\n","/**\n * Uninstall command — v26.64.0 (ADR-020).\n *\n * 동작:\n * 1. `.uzys-agent-harness/.harness-install.json` 읽기 (구 위치 `.claude/` 폴백).\n * 2. assets[] 별 reverse:\n * - scope=project: 실제 reverse (`claude plugin uninstall --scope project`, `npm uninstall`, fs rm).\n * - scope=global: 안내만 (D16 — 글로벌 영역 자동 삭제 금지). 사용자가 직접 명령 실행.\n * 3. templates 폴더 rm (`.claude/`, `.codex/`, `.opencode/`) — `--keep-templates` 시 보존.\n * 4. install log 자체도 함께 제거.\n *\n * 옵션:\n * --dry-run 실제 변경 없이 reverse list 만 출력.\n * --keep-templates `.claude/`, `.codex/`, `.opencode/` 보존.\n * --only <ids> v26.123.0 (F-1c) — 항목별 제거. templates 미변경 + 로그는 남은 자산으로 재기록.\n *\n * 안전:\n * - log 없으면 명확 에러 + early exit.\n * - scope=global 자산은 절대 자동 삭제 X (D16).\n * - 되돌리기에 성공한 것만 로그에서 뺀다. 자동 경로가 없거나 실패한 자산은 기록에 남기고,\n * 아무것도 못 되돌렸으면 성공으로 보고하지 않는다 (no-false-ship).\n */\n\nimport { type SpawnSyncReturns, spawnSync } from \"node:child_process\";\nimport {\n existsSync,\n lstatSync,\n readdirSync,\n readFileSync,\n rmdirSync,\n rmSync,\n writeFileSync,\n} from \"node:fs\";\nimport { dirname, join, relative, resolve, sep } from \"node:path\";\nimport { c, status } from \"../design.js\";\nimport { skillsCliSpec } from \"../external-installer.js\";\nimport {\n hashContent,\n INSTALL_LOG_DIR,\n type InstallLog,\n type InstallLogAsset,\n type InstallLogRootFile,\n installLogPath,\n legacyInstallLogPath,\n readInstallLog,\n writeInstallLog,\n} from \"../install-log.js\";\nimport { stripHarnessImport } from \"../project-claude-merge.js\";\nimport { runInteractiveUninstall } from \"../uninstall-interactive.js\";\n\nexport interface UninstallOptions {\n projectDir?: string;\n dryRun?: boolean;\n keepTemplates?: boolean;\n /**\n * v26.123.0 (F-1c) — 항목별 제거. 쉼표 구분 자산 id.\n * 지정 시 templates(`.claude/` 등)는 건드리지 않고, 로그도 지우지 않고 **남은 자산으로 다시 쓴다**.\n */\n only?: string;\n /** v26.125.0 — 대화형 선택 화면을 건너뛰고 전량 제거 (비대화형 스크립트용). */\n yes?: boolean;\n}\n\nexport interface UninstallActionDeps {\n log?: (msg: string) => void;\n err?: (msg: string) => void;\n exit?: (code: number) => never;\n spawn?: (cmd: string, args: ReadonlyArray<string>) => SpawnSyncReturns<string>;\n rm?: (path: string) => void;\n /** `--only` 후 로그 재기록. 실패 경로를 테스트에서 재현하기 위해 주입 가능. */\n writeLog?: (projectDir: string, log: InstallLog) => void;\n}\n\ninterface ReverseStep {\n /** 어느 자산의 reverse 인지 — `--only` 성공분만 로그에서 빼기 위해 필요. */\n assetId: string;\n /** 사람이 읽는 라벨 (한 줄) */\n label: string;\n /** 실제 동작 — dry-run 일 때는 호출 안 함. */\n execute: () => { ok: boolean; message?: string };\n}\n\ninterface GlobalAdvisory {\n asset: InstallLogAsset;\n /** 사용자에게 안내할 reverse 명령 */\n command: string;\n}\n\n/**\n * 크기 예외 사유 (code-style 50줄 상한): 본 함수의 내용은 **단계의 순서와 전제조건 분기**이고,\n * 각 단계는 이미 이름 있는 함수로 빠져 있다 (`planReverse` → `headerLines` → `dryRunLines` |\n * `executeReverse` → `removeTemplates` → `settleLog` → `advisoryLines` → `summarize`).\n * 남은 것은 early return 3개(로그 없음 / 모르는 id / dry-run)와 호출 순서뿐 — 더 쪼개면\n * 그 순서가 흩어진다.\n */\nexport function uninstallAction(options: UninstallOptions, deps: UninstallActionDeps = {}): void {\n const log = deps.log ?? console.log;\n const err = deps.err ?? console.error;\n const exit = deps.exit ?? ((code: number) => process.exit(code) as never);\n const spawn = deps.spawn ?? defaultSpawn;\n const rm = deps.rm ?? defaultRm;\n const writeLog = deps.writeLog ?? writeInstallLog;\n\n const projectDir = resolve(options.projectDir ?? process.cwd());\n const installLog = readInstallLog(projectDir);\n if (!installLog) {\n err(status.failure(c.red(`ERROR: install log not found at ${installLogPath(projectDir)}`)));\n err(c.dim(\" Was this project installed by agent-harness? Nothing to uninstall.\"));\n exit(1);\n return;\n }\n\n const selectedIds = parseOnly(options.only);\n // `--only ,` 처럼 값이 있는데 id 가 하나도 안 나오면 **전량 제거로 흘려보내지 않는다** —\n // 하나만 빼려던 사용자가 templates 까지 잃는다. 무응답보다 명시적 거절이 안전하다.\n if (options.only !== undefined && selectedIds === null) {\n err(status.failure(c.red(\"ERROR: --only 에 자산 id 가 없다\")));\n err(c.dim(\" 예: --only code-review (id 는 `agent-harness list` 에서 확인)\"));\n exit(1);\n return;\n }\n const unknown = selectedIds ? unknownIds(installLog, selectedIds) : [];\n if (unknown.length > 0) {\n err(status.failure(c.red(`ERROR: not in install log: ${unknown.join(\", \")}`)));\n err(c.dim(` installed: ${installLog.assets.map((a) => a.id).join(\", \") || \"(none)\"}`));\n exit(1);\n return;\n }\n const targetAssets = selectedIds\n ? installLog.assets.filter((a) => selectedIds.includes(a.id))\n : installLog.assets;\n // `--only` 는 자산만 건드린다 — templates 를 지우면 \"하나만 빼기\"가 아니게 된다.\n const keepTemplates = options.keepTemplates || selectedIds !== null;\n\n // v26.124.0 (F-1f) — `.claude/` 밖 루트 파일 안내. `--only` 는 특정 자산만 건드리는 작업이라\n // 설치 전반이 만든 루트 파일은 대상이 아니다. 구 로그(rootFiles 부재)는 빈 배열 = 안내 없음.\n const rootFiles = selectedIds ? [] : (installLog.rootFiles ?? []);\n\n const plan = planReverse(targetAssets, spawn);\n for (const line of headerLines(installLog, selectedIds, targetAssets.length)) log(line);\n\n if (options.dryRun) {\n for (const line of dryRunLines(plan, installLog, projectDir, keepTemplates, rootFiles)) {\n log(line);\n }\n exit(0);\n return;\n }\n\n // 두 사실을 분리해서 쓴다 — 셋을 하나로 묶다 리뷰 3라운드 내리 회귀가 났다.\n // `.claude/` 가 남는가 = keepTemplates (수기 안내 대상 여부)\n // install log 가 남는가 = `--only` 인가 (settleLog: --only 만 재기록, 나머지는 삭제)\n const logSurvives = selectedIds !== null;\n\n const { succeeded, failed, removedIds } = executeReverse(plan, log, logSurvives);\n\n if (!keepTemplates) {\n const { rootClaudeMdKept, importStripped, external } = removeTemplates(\n installLog,\n projectDir,\n rm,\n );\n log(` ${status.success(`templates removed: ${formatTemplateList(installLog)}`)}`);\n for (const line of externalRemovalLines(external)) log(line);\n if (importStripped) {\n log(` ${status.success(\"CLAUDE.md — harness @import removed (본문 보존)\")}`);\n }\n if (rootClaudeMdKept) {\n const kept = installLog.templates.rootClaudeMd?.path ?? \"CLAUDE.md\";\n log(` ${c.yellow(\"⊘\")} ${kept} kept — modified since install. Remove manually if intended.`);\n }\n }\n\n const logWriteFailed = settleLog(\n { installLog, projectDir, selectedIds, removedIds },\n { log, err, rm, writeLog },\n );\n\n // dry-run 과 같은 인자를 넘겨야 미리보기가 실행 결과와 맞는다.\n for (const line of advisoryLines(plan, projectDir, rootFiles)) {\n log(line);\n }\n\n const outcome = summarize({\n succeeded,\n failed,\n logWriteFailed,\n // 제거된 것도 없고 templates 도 안 지웠는데, 사용자에게 줄 방법조차 없을 때만 실패다.\n // ① `--only` = \"이걸 빼라\"는 특정 요청 — 하나도 못 뺐으면 요청 불이행 (C1).\n // ② 자동 경로가 없는 자산이 남았으면 — 안내할 명령조차 없다 (R1).\n // ③ 반면 global 자산만 남은 경우는 **정확한 수기 명령을 출력했으므로** 성공이다.\n // D16 설계대로의 정상이고, 여기까지 묶으면 global scope 사용자는 exit 0 을 영원히\n // 못 받는다 (로그가 지워져 재시도하면 더 나빠진다).\n nothingDone: succeeded === 0 && keepTemplates && (logSurvives || plan.noReversePath.length > 0),\n });\n log(\"\");\n log(outcome.line);\n exit(outcome.code);\n}\n\n/**\n * 종료 보고 — **한 일이 없으면 성공이라 하지 않는다.** 이전 판본은 reverse step 이 0개일 때\n * `0 === 0` 이 성공 판정을 통과해 `✓ uninstall complete` + exit 0 을 찍었다 (SOD CRITICAL).\n */\nfunction summarize(r: {\n succeeded: number;\n failed: number;\n nothingDone: boolean;\n logWriteFailed: boolean;\n}): { line: string; code: number } {\n if (r.failed > 0)\n return {\n line: c.yellow(`uninstall finished with ${r.failed} skip(s) (${r.succeeded} ok)`),\n code: 1,\n };\n if (r.nothingDone)\n return {\n line: c.yellow(\"아무것도 자동 제거되지 않았다 — 위 안내를 따라 직접 처리해야 한다\"),\n code: 1,\n };\n if (r.logWriteFailed)\n return {\n line: c.yellow(\"자산은 제거됐으나 install log 를 갱신하지 못했다 (기록이 실제와 다르다)\"),\n code: 1,\n };\n return { line: status.success(c.green(`uninstall complete (${r.succeeded} asset(s))`)), code: 0 };\n}\n\nfunction headerLines(\n installLog: InstallLog,\n selectedIds: string[] | null,\n targetCount: number,\n): string[] {\n return [\n \"\",\n c.bold(\"uzys-agent-harness · uninstall\"),\n \"\",\n c.dim(` installed: ${installLog.installedAt}`),\n c.dim(` scope: ${installLog.scope}`),\n c.dim(\n selectedIds\n ? ` assets: ${targetCount} selected of ${installLog.assets.length} (--only)`\n : ` assets: ${installLog.assets.length}`,\n ),\n \"\",\n ];\n}\n\nfunction executeReverse(\n plan: ReversePlan,\n log: (msg: string) => void,\n logSurvives: boolean,\n): { succeeded: number; failed: number; removedIds: string[] } {\n let succeeded = 0;\n let failed = 0;\n const removedIds: string[] = [];\n for (const step of plan.reverseSteps) {\n const result = step.execute();\n if (result.ok) {\n log(` ${status.success(step.label)}`);\n removedIds.push(step.assetId);\n succeeded++;\n } else {\n log(` ${c.yellow(\"⊘\")} ${step.label} (${result.message ?? \"failed\"})`);\n failed++;\n }\n }\n // 자동 되돌리기 경로가 없는 자산은 **말한다.** 조용히 넘기면 `uninstall complete` 가\n // 아무것도 안 한 실행에 붙어 거짓 보고가 된다 (no-false-ship).\n // \"기록 유지\"는 **로그가 남을 때만** 참이다. `--keep-templates` 는 `.claude/` 를 남기면서도\n // 로그는 지우므로(settleLog), templates 보존 여부로 판단하면 지워질 기록을 유지한다고 말한다.\n const tail = logSurvives ? \"자동 되돌리기 경로 없음, 기록 유지\" : \"자동 되돌리기 경로 없음\";\n for (const asset of plan.noReversePath) {\n log(` ${c.yellow(\"⊘\")} ${asset.id} (${asset.method}) — ${tail}`);\n }\n return { succeeded, failed, removedIds };\n}\n\n/** 제거 후 로그 처리. @returns 재기록에 실패했는가 (실패해도 uninstall 을 죽이지 않는다). */\nfunction settleLog(\n ctx: {\n installLog: InstallLog;\n projectDir: string;\n selectedIds: string[] | null;\n removedIds: string[];\n },\n io: {\n log: (msg: string) => void;\n err: (msg: string) => void;\n rm: (path: string) => void;\n writeLog: (projectDir: string, log: InstallLog) => void;\n },\n): boolean {\n const { installLog, projectDir, selectedIds, removedIds } = ctx;\n if (selectedIds) {\n // v26.123.0 (F-1c) — 로그를 지우는 게 아니라 **되돌린 것만 빼고 다시 쓴다**.\n // 실패한 항목은 남긴다 — 실제로 안 지워진 걸 기록에서 지우면 그게 곧 거짓 기록이다.\n const remaining = installLog.assets.filter((a) => !removedIds.includes(a.id));\n try {\n io.writeLog(projectDir, { ...installLog, assets: remaining });\n io.log(` ${status.success(`install log updated (${remaining.length} asset(s) remain)`)}`);\n } catch (e) {\n // 되돌리기는 이미 끝난 뒤다 — 스택트레이스로 죽으면 무엇이 지워졌는지도 사라진다.\n io.err(status.failure(c.red(`ERROR: install log 갱신 실패 — ${installLogPath(projectDir)}`)));\n io.err(c.dim(` ${e instanceof Error ? e.message : String(e)}`));\n io.err(c.dim(` 실제로 제거된 자산: ${removedIds.join(\", \") || \"(없음)\"}`));\n return true;\n }\n } else {\n // v26.135.0 (#253) — 로그가 `.claude/` 밖으로 나왔다. 예전엔 templates 제거가 `.claude/` 를\n // 통째로 지우며 로그도 딸려 갔지만 이제 그 경로가 없다 — **templates 보존 여부와 무관하게**\n // 여기서 지운다. 디렉터리째 지우는 이유: `.uzys-agent-harness/` 는 하네스 전용이라\n // 파일만 지우면 빈 디렉터리가 남고, 그러면 \"전부 지웠다\"가 거짓이 된다.\n io.rm(join(projectDir, INSTALL_LOG_DIR));\n // v26.134.1 이하로 설치한 프로젝트의 잔재. keepTemplates 면 `.claude/` 가 살아남으므로\n // 여기서 안 지우면 다음 실행이 그 구 파일을 읽어 **이미 지운 자산을 다시 보고**한다.\n io.rm(legacyInstallLogPath(projectDir));\n io.log(` ${status.success(\"install log removed\")}`);\n }\n return false;\n}\n\n/** dry-run 미리보기 — 실행 경로와 같은 판정을 쓴다 (미리보기가 실제와 어긋나면 미리보기가 아니다). */\nfunction dryRunLines(\n plan: ReversePlan,\n installLog: InstallLog,\n projectDir: string,\n keepTemplates: boolean,\n rootFiles: ReadonlyArray<InstallLogRootFile>,\n): string[] {\n const lines = [c.yellow(\"[DRY RUN] reverse list (실제 변경 없음):\"), \"\"];\n if (plan.reverseSteps.length === 0) {\n lines.push(c.dim(\" (no project-scope assets to reverse)\"));\n }\n lines.push(...plan.reverseSteps.map((s) => ` ○ ${s.label}`));\n lines.push(\n ...plan.noReversePath.map((a) =>\n c.dim(` ⊘ ${a.id} (${a.method}) — 자동 되돌리기 경로 없음, 기록 유지`),\n ),\n );\n if (!keepTemplates) {\n lines.push(` ○ remove templates: ${formatTemplateList(installLog)}`);\n const rootMd = installLog.templates.rootClaudeMd;\n if (rootMd) {\n lines.push(\n rootClaudeMdModified(installLog, projectDir)\n ? ` ○ keep ${rootMd.path} (modified since install — preserved)`\n : ` ○ remove ${rootMd.path}`,\n );\n }\n if (hasRootImport(projectDir)) {\n lines.push(\" ○ strip harness @import from CLAUDE.md (본문 보존)\");\n }\n lines.push(...previewExternalLines(installLog, projectDir));\n }\n lines.push(...advisoryLines(plan, projectDir, rootFiles), \"\");\n return lines;\n}\n\n/** global(D16) + 루트 파일 안내. dry-run 과 실행 경로가 같은 함수를 쓴다. */\nfunction advisoryLines(\n plan: ReversePlan,\n projectDir: string,\n rootFiles: ReadonlyArray<InstallLogRootFile>,\n): string[] {\n const lines: string[] = [];\n if (plan.globalAdvisories.length > 0) {\n lines.push(\n \"\",\n c.yellow(\n `[GLOBAL] ${plan.globalAdvisories.length} asset(s) at scope=global — manual removal required (D16):`,\n ),\n );\n for (const adv of plan.globalAdvisories) {\n lines.push(c.dim(` · ${adv.asset.id} (${adv.asset.method})`), c.dim(` ${adv.command}`));\n }\n }\n // templatesKept 와 무관하다 — `.claude/` 를 통째로 지우는 경로야말로 밖에 남는 것을\n // 사용자가 존재조차 모르게 되는 경우다. 그게 F-1f 가 잡는 구멍이다.\n lines.push(...rootFileAdvisoryLines(rootFiles, projectDir));\n return lines;\n}\n\n/**\n * v26.124.0 (F-1f) — `.claude/` 밖에 남는 것 안내. **지우지 않는다**:\n * `.mcp.json`/`.gitignore` 는 사용자 내용이 섞이고, `.github/workflows/` 는 설치 후 사용자\n * 소유물이다 (ci-scaffold.ts 안전 계약 2). 기계적 되돌리기는 손실 위험이라 안내로 넘긴다.\n *\n * 규율: **예측이 아니라 현재 파일 상태를 읽어** 실재하는 것만 낸다.\n */\nfunction rootFileAdvisoryLines(\n rootFiles: ReadonlyArray<InstallLogRootFile>,\n projectDir: string,\n): string[] {\n const present = rootFiles.filter((f) => existsSync(join(projectDir, f.path)));\n if (present.length === 0) return [];\n return [\n \"\",\n c.yellow(\"[ROOT] `.claude/` 밖에 남는 것 (자동으로 지우지 않는다):\"),\n ...present.flatMap((f) => [\n c.dim(\n ` · ${f.path} — ${\n f.change === \"created\"\n ? \"하네스가 생성 (수정한 적 없으면 삭제해도 안전)\"\n : \"기존 사용자 파일에 병합 (직접 확인 필요)\"\n }`,\n ),\n c.dim(` ${f.notes.join(\" / \")}`),\n ]),\n ];\n}\n\ninterface ReversePlan {\n reverseSteps: ReverseStep[];\n globalAdvisories: GlobalAdvisory[];\n /**\n * 자동 되돌리기 경로가 없는 자산 (npx-run / shell-script / internal). 전량 uninstall 에선\n * `.claude/` 통째 제거가 덮지만, `--only` 에선 **아무 일도 안 일어난다** — 그래서 따로 센다.\n */\n noReversePath: InstallLogAsset[];\n}\n\nfunction planReverse(\n assets: ReadonlyArray<InstallLogAsset>,\n spawn: (cmd: string, args: ReadonlyArray<string>) => SpawnSyncReturns<string>,\n): ReversePlan {\n const reverseSteps: ReverseStep[] = [];\n const globalAdvisories: GlobalAdvisory[] = [];\n const noReversePath: InstallLogAsset[] = [];\n\n for (const asset of assets) {\n if (asset.scope === \"global\") {\n globalAdvisories.push({ asset, command: buildGlobalAdvisoryCmd(asset) });\n continue;\n }\n const step = buildProjectReverseStep(asset, spawn);\n if (step) reverseSteps.push(step);\n else noReversePath.push(asset);\n }\n\n return { reverseSteps, globalAdvisories, noReversePath };\n}\n\nfunction buildProjectReverseStep(\n asset: InstallLogAsset,\n spawn: (cmd: string, args: ReadonlyArray<string>) => SpawnSyncReturns<string>,\n): ReverseStep | null {\n switch (asset.method) {\n case \"plugin\": {\n const pluginId = asset.detail.pluginId ?? asset.id;\n return {\n assetId: asset.id,\n label: `claude plugin uninstall --scope project ${pluginId}`,\n execute: () => {\n const r = spawn(\"claude\", [\"plugin\", \"uninstall\", \"--scope\", \"project\", pluginId]);\n return r.status === 0 ? { ok: true } : { ok: false, message: (r.stderr || \"\").trim() };\n },\n };\n }\n case \"skill\": {\n // skills CLI default 가 project — `skills remove <source>` (no -g).\n // 일부 source 는 폴더 경로/직접 id — npx skills remove 가 처리.\n const source = asset.detail.source ?? asset.id;\n return {\n assetId: asset.id,\n label: `npx skills remove ${source}`,\n execute: () => {\n const r = spawn(\"npx\", [skillsCliSpec(), \"remove\", source, \"--yes\"]);\n return r.status === 0 ? { ok: true } : { ok: false, message: (r.stderr || \"\").trim() };\n },\n };\n }\n case \"npm\": {\n const pkg = asset.detail.pkg ?? asset.id;\n return {\n assetId: asset.id,\n label: `npm uninstall --save-dev ${pkg}`,\n execute: () => {\n const r = spawn(\"npm\", [\"uninstall\", \"--save-dev\", pkg]);\n return r.status === 0 ? { ok: true } : { ok: false, message: (r.stderr || \"\").trim() };\n },\n };\n }\n case \"npx-run\":\n // fire-and-forget — reverse 없음 (예: GSD orchestrator).\n return null;\n case \"shell-script\":\n // 로컬 script 호출 — 일반 reverse 없음 (script 별 별도 cleanup 필요).\n return null;\n case \"internal\":\n // v26.81.0 (ADR-022) — 내부 템플릿 — removeTemplates 가 .claude/ 전체로 처리.\n return null;\n }\n}\n\n/**\n * 로그에 없는 `--only` id — 오타로 엉뚱한 자산이 남지 않도록 **아무것도 실행하기 전에** 본다\n * (Pre-flight: 전제조건 미충족 시 차단, 부분 작업 없음).\n */\nfunction unknownIds(installLog: InstallLog, selectedIds: ReadonlyArray<string>): string[] {\n const known = new Set(installLog.assets.map((a) => a.id));\n return selectedIds.filter((id) => !known.has(id));\n}\n\n/** `--only <a,b>` → [\"a\",\"b\"]. 미지정이면 null (= 전량 제거, 기존 동작). */\nfunction parseOnly(only: string | undefined): string[] | null {\n if (!only) return null;\n const ids = only\n .split(\",\")\n .map((s) => s.trim())\n .filter((s) => s.length > 0);\n return ids.length > 0 ? ids : null;\n}\n\n// 2026-08-02 정비 (ADR-060) — [MANUAL] 안내(`manualAdvisoryLines`)는 karpathy-coder 훅\n// 하나만을 위한 것이었고 그 자산과 함께 삭제됐다. `.claude/` 밖에 남는 것은 아래\n// `rootFileAdvisoryLines` 가 계속 안내한다.\n\nfunction buildGlobalAdvisoryCmd(asset: InstallLogAsset): string {\n switch (asset.method) {\n case \"plugin\": {\n const pid = asset.detail.pluginId ?? asset.id;\n return `claude plugin uninstall --scope user ${pid}`;\n }\n case \"skill\": {\n const s = asset.detail.source ?? asset.id;\n return `npx skills remove -g ${s}`;\n }\n case \"npm\": {\n const pkg = asset.detail.pkg ?? asset.id;\n return `npm uninstall -g ${pkg}`;\n }\n case \"npx-run\":\n case \"shell-script\":\n case \"internal\":\n return \"(no standard reverse — manual)\";\n }\n}\n\nfunction removeTemplates(\n log: InstallLog,\n projectDir: string,\n rm: (path: string) => void,\n): { rootClaudeMdKept: boolean; importStripped: boolean; external: ExternalRemoval } {\n rm(join(projectDir, log.templates.claudeDir));\n if (log.templates.codexDir) rm(join(projectDir, log.templates.codexDir));\n if (log.templates.opencodeDir) rm(join(projectDir, log.templates.opencodeDir));\n const external = removeExternalFiles(log, projectDir, rm);\n // 루트 `CLAUDE.md` 는 **사용자 소유**다 (P5 · ADR-060) — 지우지 않고 하네스가 넣은 마커\n // import 블록만 도로 걷어낸다. 안 걷으면 앵커 파일을 지운 뒤 없는 파일을 가리키는 import 가\n // 남아 매 세션 끊긴 참조가 로드된다.\n const importStripped = stripRootImport(projectDir);\n // 하네스 앵커 파일 — install 원본 그대로일 때만 삭제. 사용자가 수정했으면 보존.\n const rootMd = log.templates.rootClaudeMd;\n if (rootMd) {\n if (rootClaudeMdModified(log, projectDir))\n return { rootClaudeMdKept: true, importStripped, external };\n rm(join(projectDir, rootMd.path));\n }\n return { rootClaudeMdKept: false, importStripped, external };\n}\n\n/** `removeExternalFiles` 의 결과 — 화면이 지운 것과 남긴 것을 나눠 말할 수 있어야 한다. */\ninterface ExternalRemoval {\n removed: string[];\n /** 설치 이후 내용이 바뀌어 남긴 것 — 사용자 편집분이라 우리가 소유를 주장하지 않는다. */\n kept: string[];\n}\n\n/**\n * 외부 CLI transform 산출물 회수 (#350).\n *\n * **디렉터리를 통째로 지우지 않는다.** `templates` 는 `.claude/`·`.codex/`·`.opencode/` 세\n * 디렉터리만 알고 있어서, codex·opencode·antigravity 가 함께 쓰는 `.agents/` 는 uninstall\n * 이후에도 통째로 남았다(실측 2026-08-29 · v26.148.1: codex 는 `.agents/skills`, antigravity 는\n * `.agents/rules` + `.agents/skills` 가 잔존하고 화면에는 한 줄도 안 뜬다).\n *\n * 그렇다고 `.agents/` 를 회수 목록에 더하면 안 된다 — **그 자리는 `npx skills` 와 공유**하고,\n * 그쪽은 `.claude/skills/<id>` 를 `.agents/` 로의 심링크로 만들어 **본문을 거기 둔다**\n * (`external-installer.ts` 의 실측 주석). 통째 삭제는 남의 도구가 깐 스킬 본문을 지운다.\n * `.claude/` 통짜 삭제가 안전한 것은 그 안의 링크를 지워도 본문이 `.agents/` 에 남기 때문이고,\n * 반대 방향은 성립하지 않는다.\n *\n * 그래서 **우리가 쓴 파일만** 지운다. 무엇을 썼는지는 이미 정확히 기록돼 있다 —\n * `externalFiles`(ADR-048)는 transform 이 쓴 파일의 projectDir 상대경로와 sha256 을 담는다.\n * 열거 사본을 새로 만들지 않아도 되고, 자산이 늘어도 기록이 따라온다.\n *\n * 설치 이후 내용이 바뀐 파일은 **남긴다** — 루트 앵커에 이미 쓰는 규칙과 같다(사용자 편집분).\n */\nfunction removeExternalFiles(\n log: InstallLog,\n projectDir: string,\n rm: (path: string) => void,\n): ExternalRemoval {\n const removed: string[] = [];\n const kept: string[] = [];\n for (const { path, sha256 } of log.externalFiles ?? []) {\n const abs = join(projectDir, path);\n // `.claude/`·`.codex/`·`.opencode/` 아래 것은 위에서 이미 사라졌다 — 부재는 정상이다.\n if (!existsSync(abs)) continue;\n // 일반 파일만 회수한다. `.agents/skills/<id>` 는 `npx skills add` 가 **심링크로** 깔아 두는\n // 자리이고(#343 실사용자 신고로 관측), 그 링크는 우리가 만든 것이 아니다. 안 걸러 두면\n // 내용이 우연히 같을 때 남의 설치 포인터를 지우고, 다를 때는 \"네가 고쳤다\"고 잘못 말한다.\n if (!lstatSync(abs).isFile()) continue;\n if (hashContent(readFileSync(abs, \"utf8\")) !== sha256) {\n kept.push(path);\n continue;\n }\n rm(abs);\n removed.push(path);\n }\n for (const path of removed) pruneEmptyDirsUpward(projectDir, dirname(join(projectDir, path)));\n return { removed, kept };\n}\n\n/**\n * 파일을 지운 뒤 빈 껍데기로 남은 상위 디렉터리를 `projectDir` 직전까지 걷어낸다.\n *\n * 이게 없으면 `.agents/skills/<id>/` 같은 빈 디렉터리 트리가 남아 \"지웠다\"는 보고와 디스크가\n * 다른 말을 한다. 비어 있지 않으면 즉시 멈춘다 — 남의 파일이 하나라도 있으면 그 자리는\n * 우리 것이 아니다.\n */\nfunction pruneEmptyDirsUpward(projectDir: string, startDir: string): void {\n let cursor = startDir;\n while (true) {\n const rel = relative(projectDir, cursor);\n // projectDir 자신이거나 그 밖으로 나갔으면 멈춘다.\n if (rel === \"\" || rel.startsWith(`..${sep}`) || rel === \"..\") return;\n try {\n if (readdirSync(cursor).length > 0) return;\n rmdirSync(cursor);\n } catch {\n // 이미 없거나 지울 수 없다 — uninstall 을 죽일 이유가 아니다.\n return;\n }\n cursor = dirname(cursor);\n }\n}\n\n/**\n * 루트 `CLAUDE.md` 에서 하네스 import 블록만 제거. 마커가 없으면 아무것도 쓰지 않는다.\n *\n * 실패해도 uninstall 을 죽이지 않는다 — 여기서 throw 하면 자산은 이미 다 지운 상태에서\n * 명령이 실패로 끝나고, 사용자는 무엇이 남았는지 알 수 없게 된다 (D16 과 같은 방침).\n */\nfunction stripRootImport(projectDir: string): boolean {\n const current = readRootClaudeMd(projectDir);\n const stripped = current === null ? null : stripHarnessImport(current);\n if (stripped === null) return false;\n try {\n writeFileSync(join(projectDir, \"CLAUDE.md\"), stripped, \"utf8\");\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * dry-run 이 실행과 **같은 판정**으로 미리 보여 준다. 실행 경로가 회수를 판정하는 술어\n * (기록에 있고 · 디스크에 있고 · sha256 이 그대로)를 그대로 다시 쓴다.\n */\nfunction previewExternalLines(installLog: InstallLog, projectDir: string): string[] {\n const lines: string[] = [];\n let removable = 0;\n for (const { path, sha256 } of installLog.externalFiles ?? []) {\n const abs = join(projectDir, path);\n if (!existsSync(abs)) continue;\n if (hashContent(readFileSync(abs, \"utf8\")) !== sha256) {\n lines.push(` ○ keep ${path} (modified since install — preserved)`);\n continue;\n }\n removable += 1;\n }\n if (removable > 0) lines.unshift(` ○ remove ${removable} CLI output file(s)`);\n return lines;\n}\n\n/**\n * `.claude/`·`.codex/`·`.opencode/` 밖(주로 `.agents/`)에서 회수한 산출물 보고.\n *\n * 지운 게 없으면 아무 줄도 내지 않는다 — codex·opencode·antigravity 를 안 고른 설치가\n * 대부분이고, 거기서 \"0 files\" 를 찍으면 화면만 길어진다.\n */\nfunction externalRemovalLines(external: ExternalRemoval): string[] {\n const lines: string[] = [];\n if (external.removed.length > 0) {\n lines.push(` ${status.success(`CLI outputs removed: ${external.removed.length} file(s)`)}`);\n }\n for (const path of external.kept) {\n lines.push(\n ` ${c.yellow(\"⊘\")} ${path} kept — modified since install. Remove manually if intended.`,\n );\n }\n return lines;\n}\n\n/** 미리보기용 — 실행 경로와 **같은 술어**를 쓴다 (미리보기가 실제와 어긋나면 미리보기가 아니다). */\nfunction hasRootImport(projectDir: string): boolean {\n const current = readRootClaudeMd(projectDir);\n return current !== null && stripHarnessImport(current) !== null;\n}\n\nfunction readRootClaudeMd(projectDir: string): string | null {\n const path = join(projectDir, \"CLAUDE.md\");\n if (!existsSync(path)) return null;\n try {\n return readFileSync(path, \"utf8\");\n } catch {\n return null;\n }\n}\n\n/** 하네스 앵커 파일이 install 이후 수정됐는지. log 에 없거나 파일 부재 시 false (= 삭제 대상). */\nfunction rootClaudeMdModified(log: InstallLog, projectDir: string): boolean {\n const rootMd = log.templates.rootClaudeMd;\n if (!rootMd) return false;\n const path = join(projectDir, rootMd.path);\n if (!existsSync(path)) return false;\n return hashContent(readFileSync(path, \"utf8\")) !== rootMd.sha256;\n}\n\nfunction formatTemplateList(log: InstallLog): string {\n const items: string[] = [log.templates.claudeDir];\n if (log.templates.codexDir) items.push(log.templates.codexDir);\n if (log.templates.opencodeDir) items.push(log.templates.opencodeDir);\n return items.join(\", \");\n}\n\n/* v8 ignore start — thin dep-inject defaults. tests 는 항상 mock 주입. */\nfunction defaultSpawn(cmd: string, args: ReadonlyArray<string>): SpawnSyncReturns<string> {\n return spawnSync(cmd, [...args], { encoding: \"utf8\", stdio: \"pipe\", timeout: 120_000 });\n}\n\nfunction defaultRm(path: string): void {\n if (existsSync(path)) {\n rmSync(path, { recursive: true, force: true });\n }\n}\n/* v8 ignore stop */\n\nexport function registerUninstallCommand(cli: import(\"../cli.js\").Cli): void {\n cli\n .command(\"uninstall\", \"Uninstall harness assets (log-based reverse)\")\n .option(\"--project-dir <path>\", \"[Project] Target project directory\", {\n default: process.cwd(),\n })\n .option(\"--dry-run\", \"[Mode] List reverse steps without executing\")\n .option(\n \"--keep-templates\",\n \"[Mode] Keep `.claude/`, `.codex/`, `.opencode/` templates (remove only external assets)\",\n )\n .option(\n \"--only <ids>\",\n \"[Scope] Remove only these assets (comma-separated ids from `agent-harness list`). Templates untouched\",\n )\n .option(\"--yes\", \"[Mode] Skip the interactive picker and remove everything (non-interactive)\")\n /* v8 ignore next 3 — cac action callback. 분기 판정은 shouldRunInteractive 가 갖고 tests 로 검증. */\n .action(async (options: UninstallOptions) => {\n await dispatchUninstall(options);\n });\n}\n\n/**\n * v26.125.0 — 대화형 선택 화면으로 들어갈 것인가.\n *\n * 들어가지 **않는** 조건은 전부 \"사용자가 이미 무엇을 원하는지 말한 경우\"다:\n * `--only` = 뺄 대상을 지정함 · `--dry-run` = 미리보기 · `--yes` = 묻지 말라는 명시.\n * 그 외 TTY 라면 화면으로 들어간다 — 플래그 없는 `uninstall` 이 즉시 전량 삭제하던 것이\n * 이 명령에서 가장 위험한 기본값이었다. TTY 가 아니면(CI·파이프) 기존 동작 그대로다.\n */\nexport function shouldRunInteractive(options: UninstallOptions, isTty: boolean): boolean {\n if (!isTty) return false;\n if (options.yes || options.dryRun) return false;\n return options.only === undefined;\n}\n\n/* v8 ignore start — 얇은 배선. 판정은 shouldRunInteractive, 선택은 uninstall-interactive, 실행은 uninstallAction 이 각각 tests 로 검증. */\nasync function dispatchUninstall(options: UninstallOptions): Promise<void> {\n if (!shouldRunInteractive(options, Boolean(process.stdin.isTTY))) {\n uninstallAction(options);\n return;\n }\n const projectDir = resolve(options.projectDir ?? process.cwd());\n const picked = await runInteractiveUninstall(projectDir);\n if (!picked.ok || !picked.options) {\n if (picked.reason === \"no-log\") {\n console.error(\n status.failure(c.red(`ERROR: install log not found at ${installLogPath(projectDir)}`)),\n );\n console.error(c.dim(\" Nothing installed here by agent-harness.\"));\n process.exit(1);\n }\n // no-tty 는 위 분기에서 이미 걸러졌고, 나머지(cancelled/nothing-selected)는 정상 종료다.\n return;\n }\n uninstallAction(picked.options);\n}\n/* v8 ignore stop */\n","/**\n * Interactive uninstall — v26.125.0 (사용자 요청 2026-07-19).\n *\n * `agent-harness uninstall` 을 TTY 에서 실행하면 **무엇을 뺄지 고르는 화면**으로 들어간다.\n *\n * 왜 install 위저드가 아니라 별도 명령인가 (사용자 결정): install 화면의 체크 해제는\n * \"이번에 설치하지 않음\"이지 제거가 아니다. 설치 화면 안에서 삭제가 일어나면 실수 한 번이\n * 되돌릴 수 없는 삭제가 되고, \"install 은 지우지 않는다\"는 불변식도 깨진다. 그래서 제거는\n * 이 명령으로만 들어온다.\n *\n * 본 모듈은 **선택만** 한다 — 실제 되돌리기는 기존 `uninstallAction` 이 그대로 수행한다.\n * 두 모드가 각각 기존 경로 하나에 1:1 로 대응하므로 새로운 파괴적 조합이 생기지 않는다:\n * 선택 제거 → `--only <ids>` (templates 유지, 로그는 남은 자산으로 재기록)\n * 전량 제거 → 플래그 없음 (templates 포함)\n */\n\nimport { cancel, confirm, intro, isCancel, multiselect, outro, select } from \"@clack/prompts\";\nimport { type InstallLog, type InstallLogAsset, readInstallLog } from \"./install-log.js\";\n\nexport interface RemovableRow {\n value: string;\n label: string;\n hint: string;\n}\n\nexport type UninstallMode = \"selected\" | \"all\";\n\nexport interface UninstallPrompts {\n intro: (msg: string) => void;\n outro: (msg: string) => void;\n cancel: (msg: string) => void;\n /** null = ESC/취소 */\n selectMode: (rowCount: number) => Promise<UninstallMode | null>;\n /** null = ESC/취소. 빈 배열 = 아무것도 안 고름 */\n selectAssets: (rows: ReadonlyArray<RemovableRow>) => Promise<ReadonlyArray<string> | null>;\n confirm: (summary: string) => Promise<boolean | null>;\n}\n\nexport interface InteractiveUninstallDeps {\n prompts?: UninstallPrompts;\n isTty?: () => boolean;\n readLog?: (projectDir: string) => InstallLog | null;\n}\n\nexport interface InteractiveUninstallResult {\n ok: boolean;\n /** ok=true 일 때 `uninstallAction` 에 그대로 넘길 옵션. */\n options?: { projectDir: string; only?: string };\n reason?: \"no-tty\" | \"no-log\" | \"cancelled\" | \"nothing-selected\";\n message?: string;\n}\n\n/**\n * 로그의 자산 → 선택 화면 행.\n *\n * hint 에 **고르면 실제로 무슨 일이 일어나는지**를 적는다. global(D16) 과 자동 되돌리기 경로가\n * 없는 method 는 골라도 자동 삭제되지 않으므로, 고르기 전에 말해야 한다 — 안 그러면 사용자가\n * 체크하고 Enter 를 눌렀는데 아무 일도 안 일어나는 것을 결과 화면에서야 알게 된다.\n */\nexport function buildRemovableRows(\n assets: ReadonlyArray<InstallLogAsset>,\n): ReadonlyArray<RemovableRow> {\n return assets.map((a) => ({\n value: a.id,\n label: `${a.id} [${a.method}]${a.version ? ` v${a.version}` : \"\"}`,\n hint: hintFor(a),\n }));\n}\n\nfunction hintFor(asset: InstallLogAsset): string {\n if (asset.scope === \"global\") return \"global scope — 자동 삭제 안 함, 수기 제거 명령을 출력한다\";\n switch (asset.method) {\n case \"plugin\":\n return \"claude plugin uninstall --scope project\";\n case \"skill\":\n return \"npx skills remove\";\n case \"npm\":\n return \"npm uninstall --save-dev\";\n case \"npx-run\":\n case \"shell-script\":\n case \"internal\":\n return \"자동 되돌리기 경로 없음 — 전량 제거(`.claude/` 삭제)로만 사라진다\";\n }\n}\n\nexport async function runInteractiveUninstall(\n projectDir: string,\n deps: InteractiveUninstallDeps = {},\n): Promise<InteractiveUninstallResult> {\n const prompts = deps.prompts ?? defaultUninstallPrompts();\n const isTty = deps.isTty ?? (() => Boolean(process.stdin.isTTY));\n const readLog = deps.readLog ?? readInstallLog;\n\n // CI/파이프에서 프롬프트가 뜨면 그대로 멈춘다 — install 위저드와 같은 게이트.\n if (!isTty()) return { ok: false, reason: \"no-tty\" };\n\n const log = readLog(projectDir);\n if (!log) return { ok: false, reason: \"no-log\" };\n\n prompts.intro(\"uzys-agent-harness · uninstall\");\n const rows = buildRemovableRows(log.assets);\n\n const mode = await prompts.selectMode(rows.length);\n if (mode === null) {\n prompts.cancel(\"Cancelled.\");\n return { ok: false, reason: \"cancelled\" };\n }\n\n if (mode === \"all\") {\n const ok = await prompts.confirm(\n [\n \"전량 제거 — 되돌릴 수 없다:\",\n ` · 자산 ${log.assets.length}개 (자동 경로가 있는 것만 실제 제거)`,\n \" · templates 삭제: `.claude/` 등 (설치 기록도 함께 사라진다)\",\n \" · `.claude/` 밖 파일(`.mcp.json` 등)은 삭제하지 않고 안내만 한다\",\n ].join(\"\\n\"),\n );\n if (!ok) {\n prompts.cancel(\"Cancelled.\");\n return { ok: false, reason: \"cancelled\" };\n }\n return { ok: true, options: { projectDir } };\n }\n\n const picked = await prompts.selectAssets(rows);\n if (picked === null) {\n prompts.cancel(\"Cancelled.\");\n return { ok: false, reason: \"cancelled\" };\n }\n // 빈 선택을 그대로 흘리면 `--only` 가 비어 **전량 제거로 떨어진다**. 하나만 빼려던 사용자가\n // templates 까지 잃는 경로라 여기서 끊는다 (uninstall.ts 의 `--only ,` 방어와 같은 이유).\n if (picked.length === 0) {\n prompts.outro(\"아무것도 선택하지 않았다 — 변경 없음.\");\n return { ok: false, reason: \"nothing-selected\" };\n }\n\n const ok = await prompts.confirm(\n [\n `선택 제거 (${picked.length}개):`,\n ...picked.map((id) => ` · ${id}`),\n \"\",\n \"templates 는 그대로 둔다.\",\n ].join(\"\\n\"),\n );\n if (!ok) {\n prompts.cancel(\"Cancelled.\");\n return { ok: false, reason: \"cancelled\" };\n }\n return { ok: true, options: { projectDir, only: picked.join(\",\") } };\n}\n\n/* v8 ignore start — @clack/prompts 어댑터. 선택 로직은 위 순수 함수들이 갖고 tests 로 검증. */\nfunction defaultUninstallPrompts(): UninstallPrompts {\n return {\n intro: (m) => intro(m),\n outro: (m) => outro(m),\n cancel: (m) => cancel(m),\n selectMode: async (rowCount) => {\n const r = await select({\n message: `무엇을 제거할까? (설치된 자산 ${rowCount}개)`,\n options: [\n {\n value: \"selected\",\n label: \"항목 선택해서 제거\",\n hint: \"templates(`.claude/` 등)는 그대로 둔다\",\n },\n {\n value: \"all\",\n label: \"전부 제거\",\n hint: \"자산 + templates. 설치 기록도 사라진다\",\n },\n ],\n });\n return isCancel(r) ? null : (r as UninstallMode);\n },\n selectAssets: async (rows) => {\n if (rows.length === 0) return [];\n const r = await multiselect({\n message: \"제거할 항목 (Space 토글 · Enter 확정 · ESC 취소)\",\n options: rows.map((x) => ({ value: x.value, label: x.label, hint: x.hint })),\n required: false,\n });\n return isCancel(r) ? null : (r as string[]);\n },\n confirm: async (summary) => {\n const r = await confirm({ message: `${summary}\\n\\n진행할까?`, initialValue: false });\n return isCancel(r) ? null : r;\n },\n };\n}\n/* v8 ignore stop */\n",null,"/* IMPORT */\nimport fastStringTruncatedWidth from 'fast-string-truncated-width';\n/* HELPERS */\nconst NO_TRUNCATION = {\n limit: Infinity,\n ellipsis: '',\n ellipsisWidth: 0,\n};\n/* MAIN */\nconst fastStringWidth = (input, options = {}) => {\n return fastStringTruncatedWidth(input, NO_TRUNCATION, options).width;\n};\n/* EXPORT */\nexport default fastStringWidth;\n","/* IMPORT */\nimport { getCodePointsLength, isFullWidth, isWideNotCJKTNotEmoji } from './utils.js';\n/* HELPERS */\nconst ANSI_RE = /[\\u001b\\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]|\\u001b\\]8;[^;]*;.*?(?:\\u0007|\\u001b\\u005c)/y;\nconst CONTROL_RE = /[\\x00-\\x08\\x0A-\\x1F\\x7F-\\x9F]{1,1000}/y;\nconst CJKT_WIDE_RE = /(?:(?![\\uFF61-\\uFF9F\\uFF00-\\uFFEF])[\\p{Script=Han}\\p{Script=Hiragana}\\p{Script=Katakana}\\p{Script=Hangul}\\p{Script=Tangut}]){1,1000}/yu;\nconst TAB_RE = /\\t{1,1000}/y;\nconst EMOJI_RE = /[\\u{1F1E6}-\\u{1F1FF}]{2}|\\u{1F3F4}[\\u{E0061}-\\u{E007A}]{2}[\\u{E0030}-\\u{E0039}\\u{E0061}-\\u{E007A}]{1,3}\\u{E007F}|(?:\\p{Emoji}\\uFE0F\\u20E3?|\\p{Emoji_Modifier_Base}\\p{Emoji_Modifier}?|\\p{Emoji_Presentation})(?:\\u200D(?:\\p{Emoji_Modifier_Base}\\p{Emoji_Modifier}?|\\p{Emoji_Presentation}|\\p{Emoji}\\uFE0F\\u20E3?))*/yu;\nconst LATIN_RE = /(?:[\\x20-\\x7E\\xA0-\\xFF](?!\\uFE0F)){1,1000}/y;\nconst MODIFIER_RE = /\\p{M}+/gu;\nconst NO_TRUNCATION = { limit: Infinity, ellipsis: '' };\n/* MAIN */\nconst getStringTruncatedWidth = (input, truncationOptions = {}, widthOptions = {}) => {\n /* CONSTANTS */\n const LIMIT = truncationOptions.limit ?? Infinity;\n const ELLIPSIS = truncationOptions.ellipsis ?? '';\n const ELLIPSIS_WIDTH = truncationOptions?.ellipsisWidth ?? (ELLIPSIS ? getStringTruncatedWidth(ELLIPSIS, NO_TRUNCATION, widthOptions).width : 0);\n const ANSI_WIDTH = 0;\n const CONTROL_WIDTH = widthOptions.controlWidth ?? 0;\n const TAB_WIDTH = widthOptions.tabWidth ?? 8;\n const EMOJI_WIDTH = widthOptions.emojiWidth ?? 2;\n const FULL_WIDTH_WIDTH = 2;\n const REGULAR_WIDTH = widthOptions.regularWidth ?? 1;\n const WIDE_WIDTH = widthOptions.wideWidth ?? FULL_WIDTH_WIDTH;\n const PARSE_BLOCKS = [\n [LATIN_RE, REGULAR_WIDTH],\n [ANSI_RE, ANSI_WIDTH],\n [CONTROL_RE, CONTROL_WIDTH],\n [TAB_RE, TAB_WIDTH],\n [EMOJI_RE, EMOJI_WIDTH],\n [CJKT_WIDE_RE, WIDE_WIDTH],\n ];\n /* STATE */\n let indexPrev = 0;\n let index = 0;\n let length = input.length;\n let lengthExtra = 0;\n let truncationEnabled = false;\n let truncationIndex = length;\n let truncationLimit = Math.max(0, LIMIT - ELLIPSIS_WIDTH);\n let unmatchedStart = 0;\n let unmatchedEnd = 0;\n let width = 0;\n let widthExtra = 0;\n /* PARSE LOOP */\n outer: while (true) {\n /* UNMATCHED */\n if ((unmatchedEnd > unmatchedStart) || (index >= length && index > indexPrev)) {\n const unmatched = input.slice(unmatchedStart, unmatchedEnd) || input.slice(indexPrev, index);\n lengthExtra = 0;\n for (const char of unmatched.replaceAll(MODIFIER_RE, '')) {\n const codePoint = char.codePointAt(0) || 0;\n if (isFullWidth(codePoint)) {\n widthExtra = FULL_WIDTH_WIDTH;\n }\n else if (isWideNotCJKTNotEmoji(codePoint)) {\n widthExtra = WIDE_WIDTH;\n }\n else {\n widthExtra = REGULAR_WIDTH;\n }\n if ((width + widthExtra) > truncationLimit) {\n truncationIndex = Math.min(truncationIndex, Math.max(unmatchedStart, indexPrev) + lengthExtra);\n }\n if ((width + widthExtra) > LIMIT) {\n truncationEnabled = true;\n break outer;\n }\n lengthExtra += char.length;\n width += widthExtra;\n }\n unmatchedStart = unmatchedEnd = 0;\n }\n /* EXITING */\n if (index >= length) {\n break outer;\n }\n /* PARSE BLOCKS */\n for (let i = 0, l = PARSE_BLOCKS.length; i < l; i++) {\n const [BLOCK_RE, BLOCK_WIDTH] = PARSE_BLOCKS[i];\n BLOCK_RE.lastIndex = index;\n if (BLOCK_RE.test(input)) {\n lengthExtra = BLOCK_RE === CJKT_WIDE_RE ? getCodePointsLength(input.slice(index, BLOCK_RE.lastIndex)) : BLOCK_RE === EMOJI_RE ? 1 : BLOCK_RE.lastIndex - index;\n widthExtra = lengthExtra * BLOCK_WIDTH;\n if ((width + widthExtra) > truncationLimit) {\n truncationIndex = Math.min(truncationIndex, index + Math.floor((truncationLimit - width) / BLOCK_WIDTH));\n }\n if ((width + widthExtra) > LIMIT) {\n truncationEnabled = true;\n break outer;\n }\n width += widthExtra;\n unmatchedStart = indexPrev;\n unmatchedEnd = index;\n index = indexPrev = BLOCK_RE.lastIndex;\n continue outer;\n }\n }\n /* UNMATCHED INDEX */\n index += 1;\n }\n /* RETURN */\n return {\n width: truncationEnabled ? truncationLimit : width,\n index: truncationEnabled ? truncationIndex : length,\n truncated: truncationEnabled,\n ellipsed: truncationEnabled && LIMIT >= ELLIPSIS_WIDTH\n };\n};\n/* EXPORT */\nexport default getStringTruncatedWidth;\n","/* MAIN */\nconst getCodePointsLength = (() => {\n const SURROGATE_PAIR_RE = /[\\uD800-\\uDBFF][\\uDC00-\\uDFFF]/g;\n return (input) => {\n let surrogatePairsNr = 0;\n SURROGATE_PAIR_RE.lastIndex = 0;\n while (SURROGATE_PAIR_RE.test(input)) {\n surrogatePairsNr += 1;\n }\n return input.length - surrogatePairsNr;\n };\n})();\nconst isFullWidth = (x) => {\n return x === 0x3000 || x >= 0xFF01 && x <= 0xFF60 || x >= 0xFFE0 && x <= 0xFFE6;\n};\nconst isWideNotCJKTNotEmoji = (x) => {\n return x === 0x231B || x === 0x2329 || x >= 0x2FF0 && x <= 0x2FFF || x >= 0x3001 && x <= 0x303E || x >= 0x3099 && x <= 0x30FF || x >= 0x3105 && x <= 0x312F || x >= 0x3131 && x <= 0x318E || x >= 0x3190 && x <= 0x31E3 || x >= 0x31EF && x <= 0x321E || x >= 0x3220 && x <= 0x3247 || x >= 0x3250 && x <= 0x4DBF || x >= 0xFE10 && x <= 0xFE19 || x >= 0xFE30 && x <= 0xFE52 || x >= 0xFE54 && x <= 0xFE66 || x >= 0xFE68 && x <= 0xFE6B || x >= 0x1F200 && x <= 0x1F202 || x >= 0x1F210 && x <= 0x1F23B || x >= 0x1F240 && x <= 0x1F248 || x >= 0x20000 && x <= 0x2FFFD || x >= 0x30000 && x <= 0x3FFFD;\n};\n/* EXPORT */\nexport { getCodePointsLength, isFullWidth, isWideNotCJKTNotEmoji };\n","export function findCursor<T extends { disabled?: boolean }>(\n\tcursor: number,\n\tdelta: number,\n\toptions: T[]\n) {\n\tconst hasEnabledOptions = options.some((opt) => !opt.disabled);\n\tif (!hasEnabledOptions) {\n\t\treturn cursor;\n\t}\n\tconst newCursor = cursor + delta;\n\tconst maxCursor = Math.max(options.length - 1, 0);\n\tconst clampedCursor = newCursor < 0 ? maxCursor : newCursor > maxCursor ? 0 : newCursor;\n\tconst newOption = options[clampedCursor];\n\tif (newOption.disabled) {\n\t\treturn findCursor(clampedCursor, delta < 0 ? -1 : 1, options);\n\t}\n\treturn clampedCursor;\n}\n\nexport function findTextCursor(\n\tcursor: number,\n\tdeltaX: number,\n\tdeltaY: number,\n\tvalue: string\n): number {\n\tconst lines = value.split('\\n');\n\tlet cursorY = 0;\n\tlet cursorX = cursor;\n\n\tfor (const line of lines) {\n\t\tif (cursorX <= line.length) {\n\t\t\tbreak;\n\t\t}\n\t\tcursorX -= line.length + 1;\n\t\tcursorY++;\n\t}\n\n\tcursorY = Math.max(0, Math.min(lines.length - 1, cursorY + deltaY));\n\n\tcursorX = Math.min(cursorX, lines[cursorY].length) + deltaX;\n\twhile (cursorX < 0 && cursorY > 0) {\n\t\tcursorY--;\n\t\tcursorX += lines[cursorY].length + 1;\n\t}\n\twhile (cursorX > lines[cursorY].length && cursorY < lines.length - 1) {\n\t\tcursorX -= lines[cursorY].length + 1;\n\t\tcursorY++;\n\t}\n\tcursorX = Math.max(0, Math.min(lines[cursorY].length, cursorX));\n\n\tlet newCursor = 0;\n\tfor (let i = 0; i < cursorY; i++) {\n\t\tnewCursor += lines[i].length + 1;\n\t}\n\treturn newCursor + cursorX;\n}\n","const actions = ['up', 'down', 'left', 'right', 'space', 'enter', 'cancel'] as const;\nexport type Action = (typeof actions)[number];\n\nconst DEFAULT_MONTH_NAMES = [\n\t'January',\n\t'February',\n\t'March',\n\t'April',\n\t'May',\n\t'June',\n\t'July',\n\t'August',\n\t'September',\n\t'October',\n\t'November',\n\t'December',\n];\n\n/** Global settings for Clack programs, stored in memory */\ninterface InternalClackSettings {\n\tactions: Set<Action>;\n\taliases: Map<string, Action>;\n\tmessages: {\n\t\tcancel: string;\n\t\terror: string;\n\t};\n\twithGuide: boolean;\n\tdate: {\n\t\tmonthNames: string[];\n\t\tmessages: {\n\t\t\tinvalidMonth: string;\n\t\t\trequired: string;\n\t\t\tinvalidDay: (days: number, month: string) => string;\n\t\t\tafterMin: (min: Date) => string;\n\t\t\tbeforeMax: (max: Date) => string;\n\t\t};\n\t};\n}\n\nexport const settings: InternalClackSettings = {\n\tactions: new Set(actions),\n\taliases: new Map<string, Action>([\n\t\t// vim support\n\t\t['k', 'up'],\n\t\t['j', 'down'],\n\t\t['h', 'left'],\n\t\t['l', 'right'],\n\t\t['\\x03', 'cancel'],\n\t\t// opinionated defaults!\n\t\t['escape', 'cancel'],\n\t]),\n\tmessages: {\n\t\tcancel: 'Canceled',\n\t\terror: 'Something went wrong',\n\t},\n\twithGuide: true,\n\tdate: {\n\t\tmonthNames: [...DEFAULT_MONTH_NAMES],\n\t\tmessages: {\n\t\t\trequired: 'Please enter a valid date',\n\t\t\tinvalidMonth: 'There are only 12 months in a year',\n\t\t\tinvalidDay: (days, month) => `There are only ${days} days in ${month}`,\n\t\t\tafterMin: (min) => `Date must be on or after ${min.toISOString().slice(0, 10)}`,\n\t\t\tbeforeMax: (max) => `Date must be on or before ${max.toISOString().slice(0, 10)}`,\n\t\t},\n\t},\n};\n\nexport interface ClackSettings {\n\t/**\n\t * Set custom global aliases for the default actions.\n\t * This will not overwrite existing aliases, it will only add new ones!\n\t *\n\t * @param aliases - An object that maps aliases to actions\n\t * @default { k: 'up', j: 'down', h: 'left', l: 'right', '\\x03': 'cancel', 'escape': 'cancel' }\n\t */\n\taliases?: Record<string, Action>;\n\n\t/**\n\t * Custom messages for prompts\n\t */\n\tmessages?: {\n\t\t/**\n\t\t * Custom message to display when a spinner is cancelled\n\t\t * @default \"Canceled\"\n\t\t */\n\t\tcancel?: string;\n\t\t/**\n\t\t * Custom message to display when a spinner encounters an error\n\t\t * @default \"Something went wrong\"\n\t\t */\n\t\terror?: string;\n\t};\n\n\twithGuide?: boolean;\n\n\t/**\n\t * Date prompt localization\n\t */\n\tdate?: {\n\t\t/** Month names for validation messages (January, February, ...) */\n\t\tmonthNames?: string[];\n\t\tmessages?: {\n\t\t\t/** Shown when date is missing */\n\t\t\trequired?: string;\n\t\t\t/** Shown when month > 12 */\n\t\t\tinvalidMonth?: string;\n\t\t\t/** (days, monthName) => message for invalid day */\n\t\t\tinvalidDay?: (days: number, month: string) => string;\n\t\t\t/** (min) => message when date is before minDate */\n\t\t\tafterMin?: (min: Date) => string;\n\t\t\t/** (max) => message when date is after maxDate */\n\t\t\tbeforeMax?: (max: Date) => string;\n\t\t};\n\t};\n}\n\nexport function updateSettings(updates: ClackSettings) {\n\t// Handle each property in the updates\n\tif (updates.aliases !== undefined) {\n\t\tconst aliases = updates.aliases;\n\t\tfor (const alias in aliases) {\n\t\t\tif (!Object.hasOwn(aliases, alias)) continue;\n\n\t\t\tconst action = aliases[alias];\n\t\t\tif (!settings.actions.has(action)) continue;\n\n\t\t\tif (!settings.aliases.has(alias)) {\n\t\t\t\tsettings.aliases.set(alias, action);\n\t\t\t}\n\t\t}\n\t}\n\n\tif (updates.messages !== undefined) {\n\t\tconst messages = updates.messages;\n\t\tif (messages.cancel !== undefined) {\n\t\t\tsettings.messages.cancel = messages.cancel;\n\t\t}\n\t\tif (messages.error !== undefined) {\n\t\t\tsettings.messages.error = messages.error;\n\t\t}\n\t}\n\n\tif (updates.withGuide !== undefined) {\n\t\tsettings.withGuide = updates.withGuide !== false;\n\t}\n\n\tif (updates.date !== undefined) {\n\t\tconst date = updates.date;\n\t\tif (date.monthNames !== undefined) {\n\t\t\tsettings.date.monthNames = [...date.monthNames];\n\t\t}\n\t\tif (date.messages !== undefined) {\n\t\t\tif (date.messages.required !== undefined) {\n\t\t\t\tsettings.date.messages.required = date.messages.required;\n\t\t\t}\n\t\t\tif (date.messages.invalidMonth !== undefined) {\n\t\t\t\tsettings.date.messages.invalidMonth = date.messages.invalidMonth;\n\t\t\t}\n\t\t\tif (date.messages.invalidDay !== undefined) {\n\t\t\t\tsettings.date.messages.invalidDay = date.messages.invalidDay;\n\t\t\t}\n\t\t\tif (date.messages.afterMin !== undefined) {\n\t\t\t\tsettings.date.messages.afterMin = date.messages.afterMin;\n\t\t\t}\n\t\t\tif (date.messages.beforeMax !== undefined) {\n\t\t\t\tsettings.date.messages.beforeMax = date.messages.beforeMax;\n\t\t\t}\n\t\t}\n\t}\n}\n\n/**\n * Check if a key is an alias for a default action\n * @param key - The raw key which might match to an action\n * @param action - The action to match\n * @returns boolean\n */\nexport function isActionKey(key: string | Array<string | undefined>, action: Action) {\n\tif (typeof key === 'string') {\n\t\treturn settings.aliases.get(key) === action;\n\t}\n\n\tfor (const value of key) {\n\t\tif (value === undefined) continue;\n\t\tif (isActionKey(value, action)) {\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n","export function diffLines(a: string, b: string) {\n\tif (a === b) return;\n\n\tconst aLines = a.split('\\n');\n\tconst bLines = b.split('\\n');\n\tconst numLines = Math.max(aLines.length, bLines.length);\n\tconst diff: number[] = [];\n\n\tfor (let i = 0; i < numLines; i++) {\n\t\tif (aLines[i] !== bLines[i]) diff.push(i);\n\t}\n\n\treturn {\n\t\tlines: diff,\n\t\tnumLinesBefore: aLines.length,\n\t\tnumLinesAfter: bLines.length,\n\t\tnumLines,\n\t};\n}\n","import { stdin, stdout } from 'node:process';\nimport type { Key } from 'node:readline';\nimport * as readline from 'node:readline';\nimport type { Readable, Writable } from 'node:stream';\nimport { ReadStream } from 'node:tty';\nimport { wrapAnsi } from 'fast-wrap-ansi';\nimport { cursor } from 'sisteransi';\nimport { isActionKey } from './settings.js';\n\nexport * from './settings.js';\nexport * from './string.js';\n\nconst isWindows = globalThis.process.platform.startsWith('win');\n\nexport const CANCEL_SYMBOL = Symbol('clack:cancel');\n\nexport function isCancel(value: unknown): value is symbol {\n\treturn value === CANCEL_SYMBOL;\n}\n\nexport function setRawMode(input: Readable, value: boolean) {\n\tconst i = input as typeof stdin;\n\n\tif (i.isTTY) i.setRawMode(value);\n}\n\ninterface BlockOptions {\n\tinput?: Readable;\n\toutput?: Writable;\n\toverwrite?: boolean;\n\thideCursor?: boolean;\n}\n\nexport function block({\n\tinput = stdin,\n\toutput = stdout,\n\toverwrite = true,\n\thideCursor = true,\n}: BlockOptions = {}) {\n\tconst rl = readline.createInterface({\n\t\tinput,\n\t\toutput,\n\t\tprompt: '',\n\t\ttabSize: 1,\n\t});\n\treadline.emitKeypressEvents(input, rl);\n\n\tif (input instanceof ReadStream && input.isTTY) {\n\t\tinput.setRawMode(true);\n\t}\n\n\tconst clear = (data: Buffer, { name, sequence }: Key) => {\n\t\tconst str = String(data);\n\t\tif (isActionKey([str, name, sequence], 'cancel')) {\n\t\t\tif (hideCursor) output.write(cursor.show);\n\t\t\tprocess.exit(0);\n\t\t\treturn;\n\t\t}\n\t\tif (!overwrite) return;\n\t\tconst dx = name === 'return' ? 0 : -1;\n\t\tconst dy = name === 'return' ? -1 : 0;\n\n\t\treadline.moveCursor(output, dx, dy, () => {\n\t\t\treadline.clearLine(output, 1, () => {\n\t\t\t\tinput.once('keypress', clear);\n\t\t\t});\n\t\t});\n\t};\n\tif (hideCursor) output.write(cursor.hide);\n\tinput.once('keypress', clear);\n\n\treturn () => {\n\t\tinput.off('keypress', clear);\n\t\tif (hideCursor) output.write(cursor.show);\n\n\t\t// Prevent Windows specific issues: https://github.com/bombshell-dev/clack/issues/176\n\t\tif (input instanceof ReadStream && input.isTTY && !isWindows) {\n\t\t\tinput.setRawMode(false);\n\t\t}\n\n\t\t// @ts-expect-error fix for https://github.com/nodejs/node/issues/31762#issuecomment-1441223907\n\t\trl.terminal = false;\n\t\trl.close();\n\t};\n}\n\nexport const getColumns = (output: Writable): number => {\n\tif ('columns' in output && typeof output.columns === 'number') {\n\t\treturn output.columns;\n\t}\n\treturn 80;\n};\n\nexport const getRows = (output: Writable): number => {\n\tif ('rows' in output && typeof output.rows === 'number') {\n\t\treturn output.rows;\n\t}\n\treturn 20;\n};\n\nexport function wrapTextWithPrefix(\n\toutput: Writable | undefined,\n\ttext: string,\n\tprefix: string,\n\tstartPrefix: string = prefix,\n\tlineFormatter?: (line: string, index: number) => string\n): string {\n\tconst columns = getColumns(output ?? stdout);\n\tconst wrapped = wrapAnsi(text, columns - prefix.length, {\n\t\thard: true,\n\t\ttrim: false,\n\t});\n\tconst lines = wrapped\n\t\t.split('\\n')\n\t\t.map((line, index) => {\n\t\t\tconst lineString = lineFormatter ? lineFormatter(line, index) : line;\n\t\t\treturn `${index === 0 ? startPrefix : prefix}${lineString}`;\n\t\t})\n\t\t.join('\\n');\n\treturn lines;\n}\n","import { stdin, stdout } from 'node:process';\nimport readline, { type Key, type ReadLine } from 'node:readline';\nimport type { Readable, Writable } from 'node:stream';\nimport { wrapAnsi } from 'fast-wrap-ansi';\nimport { cursor, erase } from 'sisteransi';\nimport type { ClackEvents, ClackState } from '../types.js';\nimport type { Action } from '../utils/index.js';\nimport {\n\tCANCEL_SYMBOL,\n\tdiffLines,\n\tgetRows,\n\tisActionKey,\n\tsetRawMode,\n\tsettings,\n} from '../utils/index.js';\n\nexport interface PromptOptions<TValue, Self extends Prompt<TValue>> {\n\trender(this: Omit<Self, 'prompt'>): string | undefined;\n\tinitialValue?: any;\n\tinitialUserInput?: string;\n\tvalidate?: ((value: TValue | undefined) => string | Error | undefined) | undefined;\n\tinput?: Readable;\n\toutput?: Writable;\n\tsignal?: AbortSignal;\n}\n\nexport default class Prompt<TValue> {\n\tprotected input: Readable;\n\tprotected output: Writable;\n\tprivate _abortSignal?: AbortSignal;\n\n\tprivate rl: ReadLine | undefined;\n\tprivate opts: Omit<PromptOptions<TValue, Prompt<TValue>>, 'render' | 'input' | 'output'>;\n\tprivate _render: (context: Omit<Prompt<TValue>, 'prompt'>) => string | undefined;\n\tprivate _track = false;\n\tprivate _prevFrame = '';\n\tprivate _subscribers = new Map<string, { cb: (...args: any) => any; once?: boolean }[]>();\n\tprotected _cursor = 0;\n\n\tpublic state: ClackState = 'initial';\n\tpublic error = '';\n\tpublic value: TValue | undefined;\n\tpublic userInput = '';\n\n\tconstructor(options: PromptOptions<TValue, Prompt<TValue>>, trackValue = true) {\n\t\tconst { input = stdin, output = stdout, render, signal, ...opts } = options;\n\n\t\tthis.opts = opts;\n\t\tthis.onKeypress = this.onKeypress.bind(this);\n\t\tthis.close = this.close.bind(this);\n\t\tthis.render = this.render.bind(this);\n\t\tthis._render = render.bind(this);\n\t\tthis._track = trackValue;\n\t\tthis._abortSignal = signal;\n\n\t\tthis.input = input;\n\t\tthis.output = output;\n\t}\n\n\t/**\n\t * Unsubscribe all listeners\n\t */\n\tprotected unsubscribe() {\n\t\tthis._subscribers.clear();\n\t}\n\n\t/**\n\t * Set a subscriber with opts\n\t * @param event - The event name\n\t */\n\tprivate setSubscriber<T extends keyof ClackEvents<TValue>>(\n\t\tevent: T,\n\t\topts: { cb: ClackEvents<TValue>[T]; once?: boolean }\n\t) {\n\t\tconst params = this._subscribers.get(event) ?? [];\n\t\tparams.push(opts);\n\t\tthis._subscribers.set(event, params);\n\t}\n\n\t/**\n\t * Subscribe to an event\n\t * @param event - The event name\n\t * @param cb - The callback\n\t */\n\tpublic on<T extends keyof ClackEvents<TValue>>(event: T, cb: ClackEvents<TValue>[T]) {\n\t\tthis.setSubscriber(event, { cb });\n\t}\n\n\t/**\n\t * Subscribe to an event once\n\t * @param event - The event name\n\t * @param cb - The callback\n\t */\n\tpublic once<T extends keyof ClackEvents<TValue>>(event: T, cb: ClackEvents<TValue>[T]) {\n\t\tthis.setSubscriber(event, { cb, once: true });\n\t}\n\n\t/**\n\t * Emit an event with data\n\t * @param event - The event name\n\t * @param data - The data to pass to the callback\n\t */\n\tpublic emit<T extends keyof ClackEvents<TValue>>(\n\t\tevent: T,\n\t\t...data: Parameters<ClackEvents<TValue>[T]>\n\t) {\n\t\tconst cbs = this._subscribers.get(event) ?? [];\n\t\tconst cleanup: (() => void)[] = [];\n\n\t\tfor (const subscriber of cbs) {\n\t\t\tsubscriber.cb(...data);\n\n\t\t\tif (subscriber.once) {\n\t\t\t\tcleanup.push(() => cbs.splice(cbs.indexOf(subscriber), 1));\n\t\t\t}\n\t\t}\n\n\t\tfor (const cb of cleanup) {\n\t\t\tcb();\n\t\t}\n\t}\n\n\tpublic prompt() {\n\t\treturn new Promise<TValue | symbol | undefined>((resolve) => {\n\t\t\tif (this._abortSignal) {\n\t\t\t\tif (this._abortSignal.aborted) {\n\t\t\t\t\tthis.state = 'cancel';\n\n\t\t\t\t\tthis.close();\n\t\t\t\t\treturn resolve(CANCEL_SYMBOL);\n\t\t\t\t}\n\n\t\t\t\tthis._abortSignal.addEventListener(\n\t\t\t\t\t'abort',\n\t\t\t\t\t() => {\n\t\t\t\t\t\tthis.state = 'cancel';\n\t\t\t\t\t\tthis.close();\n\t\t\t\t\t},\n\t\t\t\t\t{ once: true }\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tthis.rl = readline.createInterface({\n\t\t\t\tinput: this.input,\n\t\t\t\ttabSize: 2,\n\t\t\t\tprompt: '',\n\t\t\t\tescapeCodeTimeout: 50,\n\t\t\t\tterminal: true,\n\t\t\t});\n\t\t\tthis.rl.prompt();\n\n\t\t\tif (this.opts.initialUserInput !== undefined) {\n\t\t\t\tthis._setUserInput(this.opts.initialUserInput, true);\n\t\t\t}\n\n\t\t\tthis.input.on('keypress', this.onKeypress);\n\t\t\tsetRawMode(this.input, true);\n\t\t\tthis.output.on('resize', this.render);\n\n\t\t\tthis.render();\n\n\t\t\tthis.once('submit', () => {\n\t\t\t\tthis.output.write(cursor.show);\n\t\t\t\tthis.output.off('resize', this.render);\n\t\t\t\tsetRawMode(this.input, false);\n\t\t\t\tresolve(this.value);\n\t\t\t});\n\t\t\tthis.once('cancel', () => {\n\t\t\t\tthis.output.write(cursor.show);\n\t\t\t\tthis.output.off('resize', this.render);\n\t\t\t\tsetRawMode(this.input, false);\n\t\t\t\tresolve(CANCEL_SYMBOL);\n\t\t\t});\n\t\t});\n\t}\n\n\tprotected _isActionKey(char: string | undefined, _key: Key): boolean {\n\t\treturn char === '\\t';\n\t}\n\n\tprotected _shouldSubmit(_char: string | undefined, _key: Key): boolean {\n\t\treturn true;\n\t}\n\n\tprotected _setValue(value: TValue | undefined): void {\n\t\tthis.value = value;\n\t\tthis.emit('value', this.value);\n\t}\n\n\tprotected _setUserInput(value: string | undefined, write?: boolean): void {\n\t\tthis.userInput = value ?? '';\n\t\tthis.emit('userInput', this.userInput);\n\t\tif (write && this._track && this.rl) {\n\t\t\tthis.rl.write(this.userInput);\n\t\t\tthis._cursor = this.rl.cursor;\n\t\t}\n\t}\n\n\tprotected _clearUserInput(): void {\n\t\tthis.rl?.write(null, { ctrl: true, name: 'u' });\n\t\tthis._setUserInput('');\n\t}\n\n\tprivate onKeypress(char: string | undefined, key: Key) {\n\t\tif (this._track && key.name !== 'return') {\n\t\t\tif (key.name && this._isActionKey(char, key)) {\n\t\t\t\tthis.rl?.write(null, { ctrl: true, name: 'h' });\n\t\t\t}\n\t\t\tthis._cursor = this.rl?.cursor ?? 0;\n\t\t\tthis._setUserInput(this.rl?.line);\n\t\t}\n\n\t\tif (this.state === 'error') {\n\t\t\tthis.state = 'active';\n\t\t}\n\t\tif (key?.name) {\n\t\t\tif (!this._track && settings.aliases.has(key.name)) {\n\t\t\t\tthis.emit('cursor', settings.aliases.get(key.name));\n\t\t\t}\n\t\t\tif (settings.actions.has(key.name as Action)) {\n\t\t\t\tthis.emit('cursor', key.name as Action);\n\t\t\t}\n\t\t}\n\t\tif (char && (char.toLowerCase() === 'y' || char.toLowerCase() === 'n')) {\n\t\t\tthis.emit('confirm', char.toLowerCase() === 'y');\n\t\t}\n\n\t\t// Call the key event handler and emit the key event\n\t\tthis.emit('key', char?.toLowerCase(), key);\n\n\t\tif (key?.name === 'return' && this._shouldSubmit(char, key)) {\n\t\t\tif (this.opts.validate) {\n\t\t\t\tconst problem = this.opts.validate(this.value);\n\t\t\t\tif (problem) {\n\t\t\t\t\tthis.error = problem instanceof Error ? problem.message : problem;\n\t\t\t\t\tthis.state = 'error';\n\t\t\t\t\tthis.rl?.write(this.userInput);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (this.state !== 'error') {\n\t\t\t\tthis.state = 'submit';\n\t\t\t}\n\t\t}\n\n\t\tif (isActionKey([char, key?.name, key?.sequence], 'cancel')) {\n\t\t\tthis.state = 'cancel';\n\t\t}\n\n\t\tif (this.state === 'submit' || this.state === 'cancel') {\n\t\t\tthis.emit('finalize');\n\t\t}\n\t\tthis.render();\n\t\tif (this.state === 'submit' || this.state === 'cancel') {\n\t\t\tthis.close();\n\t\t}\n\t}\n\n\tprotected close() {\n\t\tthis.input.unpipe();\n\t\tthis.input.removeListener('keypress', this.onKeypress);\n\t\tthis.output.write('\\n');\n\t\tsetRawMode(this.input, false);\n\t\tthis.rl?.close();\n\t\tthis.rl = undefined;\n\t\tthis.emit(`${this.state}`, this.value);\n\t\tthis.unsubscribe();\n\t}\n\n\tprivate restoreCursor() {\n\t\tconst lines =\n\t\t\twrapAnsi(this._prevFrame, process.stdout.columns, { hard: true, trim: false }).split('\\n')\n\t\t\t\t.length - 1;\n\t\tthis.output.write(cursor.move(-999, lines * -1));\n\t}\n\n\tprivate render() {\n\t\tconst frame = wrapAnsi(this._render(this) ?? '', process.stdout.columns, {\n\t\t\thard: true,\n\t\t\ttrim: false,\n\t\t});\n\t\tif (frame === this._prevFrame) return;\n\n\t\tif (this.state === 'initial') {\n\t\t\tthis.output.write(cursor.hide);\n\t\t} else {\n\t\t\tconst diff = diffLines(this._prevFrame, frame);\n\t\t\tconst rows = getRows(this.output);\n\t\t\tthis.restoreCursor();\n\t\t\tif (diff) {\n\t\t\t\tconst diffOffsetAfter = Math.max(0, diff.numLinesAfter - rows);\n\t\t\t\tconst diffOffsetBefore = Math.max(0, diff.numLinesBefore - rows);\n\t\t\t\tlet diffLine = diff.lines.find((line) => line >= diffOffsetAfter);\n\n\t\t\t\tif (diffLine === undefined) {\n\t\t\t\t\tthis._prevFrame = frame;\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// If a single line has changed, only update that line\n\t\t\t\tif (diff.lines.length === 1) {\n\t\t\t\t\tthis.output.write(cursor.move(0, diffLine - diffOffsetBefore));\n\t\t\t\t\tthis.output.write(erase.lines(1));\n\t\t\t\t\tconst lines = frame.split('\\n');\n\t\t\t\t\tthis.output.write(lines[diffLine]);\n\t\t\t\t\tthis._prevFrame = frame;\n\t\t\t\t\tthis.output.write(cursor.move(0, lines.length - diffLine - 1));\n\t\t\t\t\treturn;\n\t\t\t\t\t// If many lines have changed, rerender everything past the first line\n\t\t\t\t} else if (diff.lines.length > 1) {\n\t\t\t\t\tif (diffOffsetAfter < diffOffsetBefore) {\n\t\t\t\t\t\tdiffLine = diffOffsetAfter;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tconst adjustedDiffLine = diffLine - diffOffsetBefore;\n\t\t\t\t\t\tif (adjustedDiffLine > 0) {\n\t\t\t\t\t\t\tthis.output.write(cursor.move(0, adjustedDiffLine));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tthis.output.write(erase.down());\n\t\t\t\t\tconst lines = frame.split('\\n');\n\t\t\t\t\tconst newLines = lines.slice(diffLine);\n\t\t\t\t\tthis.output.write(newLines.join('\\n'));\n\t\t\t\t\tthis._prevFrame = frame;\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis.output.write(erase.down());\n\t\t}\n\n\t\tthis.output.write(frame);\n\t\tif (this.state === 'initial') {\n\t\t\tthis.state = 'active';\n\t\t}\n\t\tthis._prevFrame = frame;\n\t}\n}\n","import type { Key } from 'node:readline';\nimport { styleText } from 'node:util';\nimport { findCursor } from '../utils/cursor.js';\nimport Prompt, { type PromptOptions } from './prompt.js';\n\ninterface OptionLike {\n\tvalue: unknown;\n\tlabel?: string;\n\tdisabled?: boolean;\n}\n\ntype FilterFunction<T extends OptionLike> = (search: string, opt: T) => boolean;\n\nfunction getCursorForValue<T extends OptionLike>(\n\tselected: T['value'] | undefined,\n\titems: T[]\n): number {\n\tif (selected === undefined) {\n\t\treturn 0;\n\t}\n\n\tconst currLength = items.length;\n\n\t// If filtering changed the available options, update cursor\n\tif (currLength === 0) {\n\t\treturn 0;\n\t}\n\n\t// Try to maintain the same selected item\n\tconst index = items.findIndex((item) => item.value === selected);\n\treturn index !== -1 ? index : 0;\n}\n\nfunction defaultFilter<T extends OptionLike>(input: string, option: T): boolean {\n\tconst label = option.label ?? String(option.value);\n\treturn label.toLowerCase().includes(input.toLowerCase());\n}\n\nfunction normalisedValue<T>(multiple: boolean, values: T[] | undefined): T | T[] | undefined {\n\tif (!values) {\n\t\treturn undefined;\n\t}\n\tif (multiple) {\n\t\treturn values;\n\t}\n\treturn values[0];\n}\n\nexport interface AutocompleteOptions<T extends OptionLike>\n\textends PromptOptions<T['value'] | T['value'][], AutocompletePrompt<T>> {\n\toptions: T[] | ((this: AutocompletePrompt<T>) => T[]);\n\tfilter?: FilterFunction<T>;\n\tmultiple?: boolean;\n\t/**\n\t * When set (non-empty), pressing Tab with no input fills the field with this value\n\t * and runs the normal filter/selection logic so the user can confirm with Enter.\n\t * Tab only fills the input when the placeholder matches at least one option under\n\t * the prompt's filter (so the value remains selectable).\n\t */\n\tplaceholder?: string;\n}\n\nexport default class AutocompletePrompt<T extends OptionLike> extends Prompt<\n\tT['value'] | T['value'][]\n> {\n\tfilteredOptions: T[];\n\tmultiple: boolean;\n\tisNavigating = false;\n\tselectedValues: Array<T['value']> = [];\n\n\tfocusedValue: T['value'] | undefined;\n\t#cursor = 0;\n\t#lastUserInput = '';\n\t#filterFn: FilterFunction<T> | undefined;\n\t#options: T[] | (() => T[]);\n\t#placeholder: string | undefined;\n\n\tget cursor(): number {\n\t\treturn this.#cursor;\n\t}\n\n\tget userInputWithCursor() {\n\t\tif (!this.userInput) {\n\t\t\treturn styleText(['inverse', 'hidden'], '_');\n\t\t}\n\t\tif (this._cursor >= this.userInput.length) {\n\t\t\treturn `${this.userInput}█`;\n\t\t}\n\t\tconst s1 = this.userInput.slice(0, this._cursor);\n\t\tconst [s2, ...s3] = this.userInput.slice(this._cursor);\n\t\treturn `${s1}${styleText('inverse', s2)}${s3.join('')}`;\n\t}\n\n\tget options(): T[] {\n\t\tif (typeof this.#options === 'function') {\n\t\t\treturn this.#options();\n\t\t}\n\t\treturn this.#options;\n\t}\n\n\tconstructor(opts: AutocompleteOptions<T>) {\n\t\tsuper(opts);\n\n\t\tthis.#options = opts.options;\n\t\tthis.#placeholder = opts.placeholder;\n\t\tconst options = this.options;\n\t\tthis.filteredOptions = [...options];\n\t\tthis.multiple = opts.multiple === true;\n\t\tthis.#filterFn =\n\t\t\ttypeof opts.options === 'function' ? opts.filter : (opts.filter ?? defaultFilter);\n\t\tlet initialValues: unknown[] | undefined;\n\t\tif (opts.initialValue && Array.isArray(opts.initialValue)) {\n\t\t\tif (this.multiple) {\n\t\t\t\tinitialValues = opts.initialValue;\n\t\t\t} else {\n\t\t\t\tinitialValues = opts.initialValue.slice(0, 1);\n\t\t\t}\n\t\t} else {\n\t\t\tif (!this.multiple && this.options.length > 0) {\n\t\t\t\tinitialValues = [this.options[0].value];\n\t\t\t}\n\t\t}\n\n\t\tif (initialValues) {\n\t\t\tfor (const selectedValue of initialValues) {\n\t\t\t\tconst selectedIndex = options.findIndex((opt) => opt.value === selectedValue);\n\t\t\t\tif (selectedIndex !== -1) {\n\t\t\t\t\tthis.toggleSelected(selectedValue);\n\t\t\t\t\tthis.#cursor = selectedIndex;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tthis.focusedValue = this.options[this.#cursor]?.value;\n\n\t\tthis.on('key', (char, key) => this.#onKey(char, key));\n\t\tthis.on('userInput', (value) => this.#onUserInputChanged(value));\n\t}\n\n\tprotected override _isActionKey(char: string | undefined, key: Key): boolean {\n\t\treturn (\n\t\t\tchar === '\\t' ||\n\t\t\t(this.multiple &&\n\t\t\t\tthis.isNavigating &&\n\t\t\t\tkey.name === 'space' &&\n\t\t\t\tchar !== undefined &&\n\t\t\t\tchar !== '')\n\t\t);\n\t}\n\n\t#onKey(_char: string | undefined, key: Key): void {\n\t\tconst isUpKey = key.name === 'up';\n\t\tconst isDownKey = key.name === 'down';\n\t\tconst isReturnKey = key.name === 'return';\n\n\t\t// Tab with empty input and placeholder: fill input with placeholder to trigger autocomplete\n\t\t// Only when the placeholder matches at least one (non-disabled) option so the value remains selectable\n\t\tconst isEmptyOrOnlyTab = this.userInput === '' || this.userInput === '\\t';\n\t\tconst placeholder = this.#placeholder;\n\t\tconst options = this.options;\n\t\tconst placeholderMatchesOption =\n\t\t\tplaceholder !== undefined &&\n\t\t\tplaceholder !== '' &&\n\t\t\toptions.some(\n\t\t\t\t(opt) => !opt.disabled && (this.#filterFn ? this.#filterFn(placeholder, opt) : true)\n\t\t\t);\n\t\tif (key.name === 'tab' && isEmptyOrOnlyTab && placeholderMatchesOption) {\n\t\t\tif (this.userInput === '\\t') {\n\t\t\t\tthis._clearUserInput();\n\t\t\t}\n\t\t\tthis._setUserInput(placeholder, true);\n\t\t\tthis.isNavigating = false;\n\t\t\treturn;\n\t\t}\n\n\t\t// Start navigation mode with up/down arrows\n\t\tif (isUpKey || isDownKey) {\n\t\t\tthis.#cursor = findCursor(this.#cursor, isUpKey ? -1 : 1, this.filteredOptions);\n\t\t\tthis.focusedValue = this.filteredOptions[this.#cursor]?.value;\n\t\t\tif (!this.multiple) {\n\t\t\t\tthis.selectedValues = [this.focusedValue];\n\t\t\t}\n\t\t\tthis.isNavigating = true;\n\t\t} else if (isReturnKey) {\n\t\t\tthis.value = normalisedValue(this.multiple, this.selectedValues);\n\t\t} else {\n\t\t\tif (this.multiple) {\n\t\t\t\tif (\n\t\t\t\t\tthis.focusedValue !== undefined &&\n\t\t\t\t\t(key.name === 'tab' || (this.isNavigating && key.name === 'space'))\n\t\t\t\t) {\n\t\t\t\t\tthis.toggleSelected(this.focusedValue);\n\t\t\t\t} else {\n\t\t\t\t\tthis.isNavigating = false;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif (this.focusedValue) {\n\t\t\t\t\tthis.selectedValues = [this.focusedValue];\n\t\t\t\t}\n\t\t\t\tthis.isNavigating = false;\n\t\t\t}\n\t\t}\n\t}\n\n\tdeselectAll() {\n\t\tthis.selectedValues = [];\n\t}\n\n\ttoggleSelected(value: T['value']) {\n\t\tif (this.filteredOptions.length === 0) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (this.multiple) {\n\t\t\tif (this.selectedValues.includes(value)) {\n\t\t\t\tthis.selectedValues = this.selectedValues.filter((v) => v !== value);\n\t\t\t} else {\n\t\t\t\tthis.selectedValues = [...this.selectedValues, value];\n\t\t\t}\n\t\t} else {\n\t\t\tthis.selectedValues = [value];\n\t\t}\n\t}\n\n\t#onUserInputChanged(value: string): void {\n\t\tif (value !== this.#lastUserInput) {\n\t\t\tthis.#lastUserInput = value;\n\n\t\t\tconst options = this.options;\n\n\t\t\tif (value && this.#filterFn) {\n\t\t\t\tthis.filteredOptions = options.filter((opt) => this.#filterFn?.(value, opt));\n\t\t\t} else {\n\t\t\t\tthis.filteredOptions = [...options];\n\t\t\t}\n\t\t\tconst valueCursor = getCursorForValue(this.focusedValue, this.filteredOptions);\n\t\t\tthis.#cursor = findCursor(valueCursor, 0, this.filteredOptions);\n\t\t\tconst focusedOption = this.filteredOptions[this.#cursor];\n\t\t\tif (focusedOption && !focusedOption.disabled) {\n\t\t\t\tthis.focusedValue = focusedOption.value;\n\t\t\t} else {\n\t\t\t\tthis.focusedValue = undefined;\n\t\t\t}\n\t\t\tif (!this.multiple) {\n\t\t\t\tif (this.focusedValue !== undefined) {\n\t\t\t\t\tthis.toggleSelected(this.focusedValue);\n\t\t\t\t} else {\n\t\t\t\t\tthis.deselectAll();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n","import { cursor } from 'sisteransi';\nimport Prompt, { type PromptOptions } from './prompt.js';\n\nexport interface ConfirmOptions extends PromptOptions<boolean, ConfirmPrompt> {\n\tactive: string;\n\tinactive: string;\n\tinitialValue?: boolean;\n}\n\nexport default class ConfirmPrompt extends Prompt<boolean> {\n\tget cursor() {\n\t\treturn this.value ? 0 : 1;\n\t}\n\n\tprivate get _value() {\n\t\treturn this.cursor === 0;\n\t}\n\n\tconstructor(opts: ConfirmOptions) {\n\t\tsuper(opts, false);\n\t\tthis.value = !!opts.initialValue;\n\n\t\tthis.on('userInput', () => {\n\t\t\tthis.value = this._value;\n\t\t});\n\n\t\tthis.on('confirm', (confirm) => {\n\t\t\tthis.output.write(cursor.move(0, -1));\n\t\t\tthis.value = confirm;\n\t\t\tthis.state = 'submit';\n\t\t\tthis.close();\n\t\t});\n\n\t\tthis.on('cursor', () => {\n\t\t\tthis.value = !this.value;\n\t\t});\n\t}\n}\n","import type { Key } from 'node:readline';\nimport { settings } from '../utils/settings.js';\nimport Prompt, { type PromptOptions } from './prompt.js';\n\ninterface SegmentConfig {\n\ttype: 'year' | 'month' | 'day';\n\tlen: number;\n}\n\nexport interface DateParts {\n\tyear: string;\n\tmonth: string;\n\tday: string;\n}\n\nexport type DateFormat = 'YMD' | 'MDY' | 'DMY';\n\nconst SEGMENTS: Record<string, SegmentConfig> = {\n\tY: { type: 'year', len: 4 },\n\tM: { type: 'month', len: 2 },\n\tD: { type: 'day', len: 2 },\n} as const;\n\nfunction segmentsFor(fmt: DateFormat): SegmentConfig[] {\n\treturn [...fmt].map((c) => SEGMENTS[c as keyof typeof SEGMENTS]);\n}\n\nfunction detectLocaleFormat(locale?: string): { segments: SegmentConfig[]; separator: string } {\n\tconst fmt = new Intl.DateTimeFormat(locale, {\n\t\tyear: 'numeric',\n\t\tmonth: '2-digit',\n\t\tday: '2-digit',\n\t});\n\tconst parts = fmt.formatToParts(new Date(2000, 0, 15));\n\tconst segments: SegmentConfig[] = [];\n\tlet separator = '/';\n\tfor (const p of parts) {\n\t\tif (p.type === 'literal') {\n\t\t\tseparator = p.value.trim() || p.value;\n\t\t} else if (p.type === 'year' || p.type === 'month' || p.type === 'day') {\n\t\t\tsegments.push({ type: p.type, len: p.type === 'year' ? 4 : 2 });\n\t\t}\n\t}\n\treturn { segments, separator };\n}\n\n/** Parse string segment values to numbers, treating blanks as 0 */\nfunction parseSegmentToNum(s: string): number {\n\treturn Number.parseInt((s || '0').replace(/_/g, '0'), 10) || 0;\n}\n\nfunction parse(parts: DateParts): { year: number; month: number; day: number } {\n\treturn {\n\t\tyear: parseSegmentToNum(parts.year),\n\t\tmonth: parseSegmentToNum(parts.month),\n\t\tday: parseSegmentToNum(parts.day),\n\t};\n}\n\nfunction daysInMonth(year: number, month: number): number {\n\treturn new Date(year || 2001, month || 1, 0).getDate();\n}\n\n/** Validate and return calendar parts, or undefined if invalid */\nfunction validParts(parts: DateParts): { year: number; month: number; day: number } | undefined {\n\tconst { year, month, day } = parse(parts);\n\tif (!year || year < 0 || year > 9999) return undefined;\n\tif (!month || month < 1 || month > 12) return undefined;\n\tif (!day || day < 1) return undefined;\n\tconst d = new Date(Date.UTC(year, month - 1, day));\n\tif (d.getUTCFullYear() !== year || d.getUTCMonth() !== month - 1 || d.getUTCDate() !== day)\n\t\treturn undefined;\n\treturn { year, month, day };\n}\n\nfunction toDate(parts: DateParts): Date | undefined {\n\tconst p = validParts(parts);\n\treturn p ? new Date(Date.UTC(p.year, p.month - 1, p.day)) : undefined;\n}\n\nfunction segmentBounds(\n\ttype: 'year' | 'month' | 'day',\n\tctx: { year: number; month: number },\n\tminDate: Date | undefined,\n\tmaxDate: Date | undefined\n): { min: number; max: number } {\n\tconst minP = minDate\n\t\t? {\n\t\t\t\tyear: minDate.getUTCFullYear(),\n\t\t\t\tmonth: minDate.getUTCMonth() + 1,\n\t\t\t\tday: minDate.getUTCDate(),\n\t\t\t}\n\t\t: null;\n\tconst maxP = maxDate\n\t\t? {\n\t\t\t\tyear: maxDate.getUTCFullYear(),\n\t\t\t\tmonth: maxDate.getUTCMonth() + 1,\n\t\t\t\tday: maxDate.getUTCDate(),\n\t\t\t}\n\t\t: null;\n\n\tif (type === 'year') {\n\t\treturn { min: minP?.year ?? 1, max: maxP?.year ?? 9999 };\n\t}\n\tif (type === 'month') {\n\t\treturn {\n\t\t\tmin: minP && ctx.year === minP.year ? minP.month : 1,\n\t\t\tmax: maxP && ctx.year === maxP.year ? maxP.month : 12,\n\t\t};\n\t}\n\treturn {\n\t\tmin: minP && ctx.year === minP.year && ctx.month === minP.month ? minP.day : 1,\n\t\tmax:\n\t\t\tmaxP && ctx.year === maxP.year && ctx.month === maxP.month\n\t\t\t\t? maxP.day\n\t\t\t\t: daysInMonth(ctx.year, ctx.month),\n\t};\n}\n\nexport interface DateOptions extends PromptOptions<Date, DatePrompt> {\n\tformat?: DateFormat;\n\tlocale?: string;\n\tseparator?: string;\n\tdefaultValue?: Date;\n\tinitialValue?: Date;\n\tminDate?: Date;\n\tmaxDate?: Date;\n}\n\nexport default class DatePrompt extends Prompt<Date> {\n\t#segments: SegmentConfig[];\n\t#separator: string;\n\t#segmentValues: DateParts;\n\t#minDate: Date | undefined;\n\t#maxDate: Date | undefined;\n\t#cursor = { segmentIndex: 0, positionInSegment: 0 };\n\t#segmentSelected = true;\n\t#pendingTensDigit: string | null = null;\n\n\tinlineError = '';\n\n\tget segmentCursor() {\n\t\treturn { ...this.#cursor };\n\t}\n\n\tget segmentValues(): DateParts {\n\t\treturn { ...this.#segmentValues };\n\t}\n\n\tget segments(): readonly SegmentConfig[] {\n\t\treturn this.#segments;\n\t}\n\n\tget separator(): string {\n\t\treturn this.#separator;\n\t}\n\n\tget formattedValue(): string {\n\t\treturn this.#format(this.#segmentValues);\n\t}\n\n\t#format(parts: DateParts): string {\n\t\treturn this.#segments.map((s) => parts[s.type]).join(this.#separator);\n\t}\n\n\t#refresh() {\n\t\tthis._setUserInput(this.#format(this.#segmentValues));\n\t\tthis._setValue(toDate(this.#segmentValues) ?? undefined);\n\t}\n\n\tconstructor(opts: DateOptions) {\n\t\tconst detected = opts.format\n\t\t\t? { segments: segmentsFor(opts.format), separator: opts.separator ?? '/' }\n\t\t\t: detectLocaleFormat(opts.locale);\n\t\tconst sep = opts.separator ?? detected.separator;\n\t\tconst segments = opts.format ? segmentsFor(opts.format) : detected.segments;\n\n\t\tconst initialDate = opts.initialValue ?? opts.defaultValue;\n\t\tconst segmentValues: DateParts = initialDate\n\t\t\t? {\n\t\t\t\t\tyear: String(initialDate.getUTCFullYear()).padStart(4, '0'),\n\t\t\t\t\tmonth: String(initialDate.getUTCMonth() + 1).padStart(2, '0'),\n\t\t\t\t\tday: String(initialDate.getUTCDate()).padStart(2, '0'),\n\t\t\t\t}\n\t\t\t: { year: '____', month: '__', day: '__' };\n\n\t\tconst initialDisplay = segments.map((s) => segmentValues[s.type]).join(sep);\n\n\t\tsuper({ ...opts, initialUserInput: initialDisplay }, false);\n\t\tthis.#segments = segments;\n\t\tthis.#separator = sep;\n\t\tthis.#segmentValues = segmentValues;\n\t\tthis.#minDate = opts.minDate;\n\t\tthis.#maxDate = opts.maxDate;\n\t\tthis.#refresh();\n\n\t\tthis.on('cursor', (key) => this.#onCursor(key));\n\t\tthis.on('key', (char, key) => this.#onKey(char, key));\n\t\tthis.on('finalize', () => this.#onFinalize(opts));\n\t}\n\n\t#seg(): { segment: SegmentConfig; index: number } | undefined {\n\t\tconst index = Math.max(0, Math.min(this.#cursor.segmentIndex, this.#segments.length - 1));\n\t\tconst segment = this.#segments[index];\n\t\tif (!segment) return undefined;\n\t\tthis.#cursor.positionInSegment = Math.max(\n\t\t\t0,\n\t\t\tMath.min(this.#cursor.positionInSegment, segment.len - 1)\n\t\t);\n\t\treturn { segment, index };\n\t}\n\n\t#navigate(direction: 1 | -1) {\n\t\tthis.inlineError = '';\n\t\tthis.#pendingTensDigit = null;\n\t\tconst ctx = this.#seg();\n\t\tif (!ctx) return;\n\t\tthis.#cursor.segmentIndex = Math.max(\n\t\t\t0,\n\t\t\tMath.min(this.#segments.length - 1, ctx.index + direction)\n\t\t);\n\t\tthis.#cursor.positionInSegment = 0;\n\t\tthis.#segmentSelected = true;\n\t}\n\n\t#adjust(direction: 1 | -1) {\n\t\tconst ctx = this.#seg();\n\t\tif (!ctx) return;\n\t\tconst { segment } = ctx;\n\t\tconst raw = this.#segmentValues[segment.type];\n\t\tconst isBlank = !raw || raw.replace(/_/g, '') === '';\n\t\tconst num = Number.parseInt((raw || '0').replace(/_/g, '0'), 10) || 0;\n\t\tconst bounds = segmentBounds(\n\t\t\tsegment.type,\n\t\t\tparse(this.#segmentValues),\n\t\t\tthis.#minDate,\n\t\t\tthis.#maxDate\n\t\t);\n\n\t\tlet next: number;\n\t\tif (isBlank) {\n\t\t\tnext = direction === 1 ? bounds.min : bounds.max;\n\t\t} else {\n\t\t\tnext = Math.max(Math.min(bounds.max, num + direction), bounds.min);\n\t\t}\n\n\t\tthis.#segmentValues = {\n\t\t\t...this.#segmentValues,\n\t\t\t[segment.type]: next.toString().padStart(segment.len, '0'),\n\t\t};\n\t\tthis.#segmentSelected = true;\n\t\tthis.#pendingTensDigit = null;\n\t\tthis.#refresh();\n\t}\n\n\t#onCursor(key?: string) {\n\t\tif (!key) return;\n\t\tswitch (key) {\n\t\t\tcase 'right':\n\t\t\t\treturn this.#navigate(1);\n\t\t\tcase 'left':\n\t\t\t\treturn this.#navigate(-1);\n\t\t\tcase 'up':\n\t\t\t\treturn this.#adjust(1);\n\t\t\tcase 'down':\n\t\t\t\treturn this.#adjust(-1);\n\t\t}\n\t}\n\n\t#onKey(char: string | undefined, key: Key) {\n\t\t// Backspace\n\t\tconst isBackspace =\n\t\t\tkey?.name === 'backspace' ||\n\t\t\tkey?.sequence === '\\x7f' ||\n\t\t\tkey?.sequence === '\\b' ||\n\t\t\tchar === '\\x7f' ||\n\t\t\tchar === '\\b';\n\t\tif (isBackspace) {\n\t\t\tthis.inlineError = '';\n\t\t\tconst ctx = this.#seg();\n\t\t\tif (!ctx) return;\n\t\t\tif (!this.#segmentValues[ctx.segment.type].replace(/_/g, '')) {\n\t\t\t\tthis.#navigate(-1);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthis.#segmentValues[ctx.segment.type] = '_'.repeat(ctx.segment.len);\n\t\t\tthis.#segmentSelected = true;\n\t\t\tthis.#cursor.positionInSegment = 0;\n\t\t\tthis.#refresh();\n\t\t\treturn;\n\t\t}\n\n\t\t// Tab navigation\n\t\tif (key?.name === 'tab') {\n\t\t\tthis.inlineError = '';\n\t\t\tconst ctx = this.#seg();\n\t\t\tif (!ctx) return;\n\t\t\tconst dir = key.shift ? -1 : 1;\n\t\t\tconst next = ctx.index + dir;\n\t\t\tif (next >= 0 && next < this.#segments.length) {\n\t\t\t\tthis.#cursor.segmentIndex = next;\n\t\t\t\tthis.#cursor.positionInSegment = 0;\n\t\t\t\tthis.#segmentSelected = true;\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Digit input\n\t\tif (char && /^[0-9]$/.test(char)) {\n\t\t\tconst ctx = this.#seg();\n\t\t\tif (!ctx) return;\n\t\t\tconst { segment } = ctx;\n\t\t\tconst isBlank = !this.#segmentValues[segment.type].replace(/_/g, '');\n\n\t\t\t// Pending tens digit: complete the two-digit entry\n\t\t\tif (this.#segmentSelected && this.#pendingTensDigit !== null && !isBlank) {\n\t\t\t\tconst newVal = this.#pendingTensDigit + char;\n\t\t\t\tconst newParts = { ...this.#segmentValues, [segment.type]: newVal };\n\t\t\t\tconst err = this.#validateSegment(newParts, segment);\n\t\t\t\tif (err) {\n\t\t\t\t\tthis.inlineError = err;\n\t\t\t\t\tthis.#pendingTensDigit = null;\n\t\t\t\t\tthis.#segmentSelected = false;\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tthis.inlineError = '';\n\t\t\t\tthis.#segmentValues[segment.type] = newVal;\n\t\t\t\tthis.#pendingTensDigit = null;\n\t\t\t\tthis.#segmentSelected = false;\n\t\t\t\tthis.#refresh();\n\t\t\t\tif (ctx.index < this.#segments.length - 1) {\n\t\t\t\t\tthis.#cursor.segmentIndex = ctx.index + 1;\n\t\t\t\t\tthis.#cursor.positionInSegment = 0;\n\t\t\t\t\tthis.#segmentSelected = true;\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Clear-on-type: typing into a selected filled segment clears it first\n\t\t\tif (this.#segmentSelected && !isBlank) {\n\t\t\t\tthis.#segmentValues[segment.type] = '_'.repeat(segment.len);\n\t\t\t\tthis.#cursor.positionInSegment = 0;\n\t\t\t}\n\t\t\tthis.#segmentSelected = false;\n\t\t\tthis.#pendingTensDigit = null;\n\n\t\t\tconst display = this.#segmentValues[segment.type];\n\t\t\tconst firstBlank = display.indexOf('_');\n\t\t\tconst pos =\n\t\t\t\tfirstBlank >= 0 ? firstBlank : Math.min(this.#cursor.positionInSegment, segment.len - 1);\n\t\t\tif (pos < 0 || pos >= segment.len) return;\n\n\t\t\tlet newVal = display.slice(0, pos) + char + display.slice(pos + 1);\n\n\t\t\t// Smart digit placement\n\t\t\tlet shouldStaySelected = false;\n\t\t\tif (pos === 0 && display === '__' && (segment.type === 'month' || segment.type === 'day')) {\n\t\t\t\tconst digit = Number.parseInt(char, 10);\n\t\t\t\tnewVal = `0${char}`;\n\t\t\t\tshouldStaySelected = digit <= (segment.type === 'month' ? 1 : 2);\n\t\t\t}\n\t\t\tif (segment.type === 'year') {\n\t\t\t\tconst digits = display.replace(/_/g, '');\n\t\t\t\tnewVal = (digits + char).padStart(segment.len, '_');\n\t\t\t}\n\n\t\t\tif (!newVal.includes('_')) {\n\t\t\t\tconst newParts = { ...this.#segmentValues, [segment.type]: newVal };\n\t\t\t\tconst err = this.#validateSegment(newParts, segment);\n\t\t\t\tif (err) {\n\t\t\t\t\tthis.inlineError = err;\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.inlineError = '';\n\n\t\t\tthis.#segmentValues[segment.type] = newVal;\n\n\t\t\t// Clamp only when the current segment is fully entered\n\t\t\tconst parsed = !newVal.includes('_') ? validParts(this.#segmentValues) : undefined;\n\t\t\tif (parsed) {\n\t\t\t\tconst { year, month } = parsed;\n\t\t\t\tconst maxDay = daysInMonth(year, month);\n\t\t\t\tthis.#segmentValues = {\n\t\t\t\t\tyear: String(Math.max(0, Math.min(9999, year))).padStart(4, '0'),\n\t\t\t\t\tmonth: String(Math.max(1, Math.min(12, month))).padStart(2, '0'),\n\t\t\t\t\tday: String(Math.max(1, Math.min(maxDay, parsed.day))).padStart(2, '0'),\n\t\t\t\t};\n\t\t\t}\n\t\t\tthis.#refresh();\n\n\t\t\t// Advance cursor\n\t\t\tconst nextBlank = newVal.indexOf('_');\n\t\t\tif (shouldStaySelected) {\n\t\t\t\tthis.#segmentSelected = true;\n\t\t\t\tthis.#pendingTensDigit = char;\n\t\t\t} else if (nextBlank >= 0) {\n\t\t\t\tthis.#cursor.positionInSegment = nextBlank;\n\t\t\t} else if (firstBlank >= 0 && ctx.index < this.#segments.length - 1) {\n\t\t\t\tthis.#cursor.segmentIndex = ctx.index + 1;\n\t\t\t\tthis.#cursor.positionInSegment = 0;\n\t\t\t\tthis.#segmentSelected = true;\n\t\t\t} else {\n\t\t\t\tthis.#cursor.positionInSegment = Math.min(pos + 1, segment.len - 1);\n\t\t\t}\n\t\t}\n\t}\n\n\t#validateSegment(parts: DateParts, seg: SegmentConfig): string | undefined {\n\t\tconst { month, day } = parse(parts);\n\t\tif (seg.type === 'month' && (month < 0 || month > 12)) {\n\t\t\treturn settings.date.messages.invalidMonth;\n\t\t}\n\t\tif (seg.type === 'day' && (day < 0 || day > 31)) {\n\t\t\treturn settings.date.messages.invalidDay(31, 'any month');\n\t\t}\n\t\treturn undefined;\n\t}\n\n\t#onFinalize(opts: DateOptions) {\n\t\tconst { year, month, day } = parse(this.#segmentValues);\n\t\tif (year && month && day) {\n\t\t\tconst maxDay = daysInMonth(year, month);\n\t\t\tthis.#segmentValues = {\n\t\t\t\t...this.#segmentValues,\n\t\t\t\tday: String(Math.min(day, maxDay)).padStart(2, '0'),\n\t\t\t};\n\t\t}\n\t\tthis.value = toDate(this.#segmentValues) ?? opts.defaultValue ?? undefined;\n\t}\n}\n","import Prompt, { type PromptOptions } from './prompt.js';\n\nexport interface GroupMultiSelectOptions<T extends { value: any }>\n\textends PromptOptions<T['value'][], GroupMultiSelectPrompt<T>> {\n\toptions: Record<string, T[]>;\n\tinitialValues?: T['value'][];\n\trequired?: boolean;\n\tcursorAt?: T['value'];\n\tselectableGroups?: boolean;\n}\nexport default class GroupMultiSelectPrompt<T extends { value: any }> extends Prompt<T['value'][]> {\n\toptions: (T & { group: string | boolean })[];\n\tcursor = 0;\n\t#selectableGroups: boolean;\n\n\tgetGroupItems(group: string): T[] {\n\t\treturn this.options.filter((o) => o.group === group);\n\t}\n\n\tisGroupSelected(group: string) {\n\t\tconst items = this.getGroupItems(group);\n\t\tconst value = this.value;\n\t\tif (value === undefined) {\n\t\t\treturn false;\n\t\t}\n\t\treturn items.every((i) => value.includes(i.value));\n\t}\n\n\tprivate toggleValue() {\n\t\tconst item = this.options[this.cursor];\n\t\tif (this.value === undefined) {\n\t\t\tthis.value = [];\n\t\t}\n\t\tif (item.group === true) {\n\t\t\tconst group = item.value;\n\t\t\tconst groupedItems = this.getGroupItems(group);\n\t\t\tif (this.isGroupSelected(group)) {\n\t\t\t\tthis.value = this.value.filter(\n\t\t\t\t\t(v: string) => groupedItems.findIndex((i) => i.value === v) === -1\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tthis.value = [...this.value, ...groupedItems.map((i) => i.value)];\n\t\t\t}\n\t\t\tthis.value = Array.from(new Set(this.value));\n\t\t} else {\n\t\t\tconst selected = this.value.includes(item.value);\n\t\t\tthis.value = selected\n\t\t\t\t? this.value.filter((v: T['value']) => v !== item.value)\n\t\t\t\t: [...this.value, item.value];\n\t\t}\n\t}\n\n\tconstructor(opts: GroupMultiSelectOptions<T>) {\n\t\tsuper(opts, false);\n\t\tconst { options } = opts;\n\t\tthis.#selectableGroups = opts.selectableGroups !== false;\n\t\tthis.options = Object.entries(options).flatMap(([key, option]) => [\n\t\t\t{ value: key, group: true, label: key },\n\t\t\t...option.map((opt) => ({ ...opt, group: key })),\n\t\t]) as any;\n\t\tthis.value = [...(opts.initialValues ?? [])];\n\t\tthis.cursor = Math.max(\n\t\t\tthis.options.findIndex(({ value }) => value === opts.cursorAt),\n\t\t\tthis.#selectableGroups ? 0 : 1\n\t\t);\n\n\t\tthis.on('cursor', (key) => {\n\t\t\tswitch (key) {\n\t\t\t\tcase 'left':\n\t\t\t\tcase 'up': {\n\t\t\t\t\tthis.cursor = this.cursor === 0 ? this.options.length - 1 : this.cursor - 1;\n\t\t\t\t\tconst currentIsGroup = this.options[this.cursor]?.group === true;\n\t\t\t\t\tif (!this.#selectableGroups && currentIsGroup) {\n\t\t\t\t\t\tthis.cursor = this.cursor === 0 ? this.options.length - 1 : this.cursor - 1;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase 'down':\n\t\t\t\tcase 'right': {\n\t\t\t\t\tthis.cursor = this.cursor === this.options.length - 1 ? 0 : this.cursor + 1;\n\t\t\t\t\tconst currentIsGroup = this.options[this.cursor]?.group === true;\n\t\t\t\t\tif (!this.#selectableGroups && currentIsGroup) {\n\t\t\t\t\t\tthis.cursor = this.cursor === this.options.length - 1 ? 0 : this.cursor + 1;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase 'space':\n\t\t\t\t\tthis.toggleValue();\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t});\n\t}\n}\n","import type { Key } from 'node:readline';\nimport { styleText } from 'node:util';\nimport { findTextCursor } from '../utils/cursor.js';\nimport { type Action, settings } from '../utils/index.js';\nimport Prompt, { type PromptOptions } from './prompt.js';\n\nexport interface MultiLineOptions extends PromptOptions<string, MultiLinePrompt> {\n\tplaceholder?: string;\n\tdefaultValue?: string;\n\tshowSubmit?: boolean;\n}\n\nexport default class MultiLinePrompt extends Prompt<string> {\n\t#lastKeyWasReturn = false;\n\t#showSubmit: boolean;\n\tpublic focused: 'editor' | 'submit' = 'editor';\n\n\tget userInputWithCursor() {\n\t\tif (this.state === 'submit') {\n\t\t\treturn this.userInput;\n\t\t}\n\t\tconst userInput = this.userInput;\n\t\tif (this.cursor >= userInput.length) {\n\t\t\treturn `${userInput}█`;\n\t\t}\n\t\tconst s1 = userInput.slice(0, this.cursor);\n\t\tconst s2 = userInput[this.cursor];\n\t\tconst s3 = userInput.slice(this.cursor + 1);\n\t\tif (s2 === '\\n') return `${s1}█\\n${s3}`;\n\t\treturn `${s1}${styleText('inverse', s2)}${s3}`;\n\t}\n\tget cursor() {\n\t\treturn this._cursor;\n\t}\n\t#insertAtCursor(char: string) {\n\t\tif (this.userInput.length === 0) {\n\t\t\tthis._setUserInput(char);\n\t\t\treturn;\n\t\t}\n\t\tthis._setUserInput(\n\t\t\tthis.userInput.slice(0, this.cursor) + char + this.userInput.slice(this.cursor)\n\t\t);\n\t}\n\t#handleCursor(key?: Action) {\n\t\tconst text = this.value ?? '';\n\t\tswitch (key) {\n\t\t\tcase 'up':\n\t\t\t\tthis._cursor = findTextCursor(this._cursor, 0, -1, text);\n\t\t\t\treturn;\n\t\t\tcase 'down':\n\t\t\t\tthis._cursor = findTextCursor(this._cursor, 0, 1, text);\n\t\t\t\treturn;\n\t\t\tcase 'left':\n\t\t\t\tthis._cursor = findTextCursor(this._cursor, -1, 0, text);\n\t\t\t\treturn;\n\t\t\tcase 'right':\n\t\t\t\tthis._cursor = findTextCursor(this._cursor, 1, 0, text);\n\t\t\t\treturn;\n\t\t}\n\t}\n\n\tprotected override _shouldSubmit(_char: string | undefined, _key: Key): boolean {\n\t\tif (this.#showSubmit) {\n\t\t\tif (this.focused === 'submit') {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tthis.#insertAtCursor('\\n');\n\t\t\tthis._cursor++;\n\t\t\treturn false;\n\t\t}\n\t\tconst wasReturn = this.#lastKeyWasReturn;\n\t\tthis.#lastKeyWasReturn = true;\n\t\tif (wasReturn) {\n\t\t\tif (this.userInput[this.cursor - 1] === '\\n') {\n\t\t\t\tthis._setUserInput(\n\t\t\t\t\tthis.userInput.slice(0, this.cursor - 1) + this.userInput.slice(this.cursor)\n\t\t\t\t);\n\t\t\t\tthis._cursor--;\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\tthis.#insertAtCursor('\\n');\n\t\tthis._cursor++;\n\t\treturn false;\n\t}\n\n\tconstructor(opts: MultiLineOptions) {\n\t\tsuper(opts, false);\n\t\tthis.#showSubmit = opts.showSubmit ?? false;\n\n\t\tthis.on('key', (char, key) => {\n\t\t\tif (key?.name && settings.actions.has(key.name as Action)) {\n\t\t\t\tthis.#handleCursor(key.name as Action);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (char === '\\t' && this.#showSubmit) {\n\t\t\t\tthis.focused = this.focused === 'editor' ? 'submit' : 'editor';\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (key?.name === 'return') {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthis.#lastKeyWasReturn = false;\n\t\t\tif (key?.name === 'backspace' && this.cursor > 0) {\n\t\t\t\tthis._setUserInput(\n\t\t\t\t\tthis.userInput.slice(0, this.cursor - 1) + this.userInput.slice(this.cursor)\n\t\t\t\t);\n\t\t\t\tthis._cursor--;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (key?.name === 'delete' && this.cursor < this.userInput.length) {\n\t\t\t\tthis._setUserInput(\n\t\t\t\t\tthis.userInput.slice(0, this.cursor) + this.userInput.slice(this.cursor + 1)\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (char) {\n\t\t\t\tif (this.#showSubmit && this.focused === 'submit') {\n\t\t\t\t\tthis.focused = 'editor';\n\t\t\t\t}\n\t\t\t\tthis.#insertAtCursor(char ?? '');\n\t\t\t\tthis._cursor++;\n\t\t\t}\n\t\t});\n\n\t\tthis.on('userInput', (input) => {\n\t\t\tthis._setValue(input);\n\t\t});\n\t\tthis.on('finalize', () => {\n\t\t\tif (!this.value) {\n\t\t\t\tthis.value = opts.defaultValue;\n\t\t\t}\n\t\t\tif (this.value === undefined) {\n\t\t\t\tthis.value = '';\n\t\t\t}\n\t\t});\n\t}\n}\n","import { findCursor } from '../utils/cursor.js';\nimport Prompt, { type PromptOptions } from './prompt.js';\n\ninterface OptionLike {\n\tvalue: any;\n\tdisabled?: boolean;\n}\n\nexport interface MultiSelectOptions<T extends OptionLike>\n\textends PromptOptions<T['value'][], MultiSelectPrompt<T>> {\n\toptions: T[];\n\tinitialValues?: T['value'][];\n\trequired?: boolean;\n\tcursorAt?: T['value'];\n}\nexport default class MultiSelectPrompt<T extends OptionLike> extends Prompt<T['value'][]> {\n\toptions: T[];\n\tcursor = 0;\n\n\tprivate get _value(): T['value'] {\n\t\treturn this.options[this.cursor].value;\n\t}\n\n\tprivate get _enabledOptions(): T[] {\n\t\treturn this.options.filter((option) => option.disabled !== true);\n\t}\n\n\tprivate toggleAll() {\n\t\tconst enabledOptions = this._enabledOptions;\n\t\tconst allSelected = this.value !== undefined && this.value.length === enabledOptions.length;\n\t\tthis.value = allSelected ? [] : enabledOptions.map((v) => v.value);\n\t}\n\n\tprivate toggleInvert() {\n\t\tconst value = this.value;\n\t\tif (!value) {\n\t\t\treturn;\n\t\t}\n\t\tconst notSelected = this._enabledOptions.filter((v) => !value.includes(v.value));\n\t\tthis.value = notSelected.map((v) => v.value);\n\t}\n\n\tprivate toggleValue() {\n\t\tif (this.value === undefined) {\n\t\t\tthis.value = [];\n\t\t}\n\t\tconst selected = this.value.includes(this._value);\n\t\tthis.value = selected\n\t\t\t? this.value.filter((value) => value !== this._value)\n\t\t\t: [...this.value, this._value];\n\t}\n\n\tconstructor(opts: MultiSelectOptions<T>) {\n\t\tsuper(opts, false);\n\n\t\tthis.options = opts.options;\n\t\tthis.value = [...(opts.initialValues ?? [])];\n\t\tconst cursor = Math.max(\n\t\t\tthis.options.findIndex(({ value }) => value === opts.cursorAt),\n\t\t\t0\n\t\t);\n\t\tthis.cursor = this.options[cursor].disabled ? findCursor<T>(cursor, 1, this.options) : cursor;\n\t\tthis.on('key', (char) => {\n\t\t\tif (char === 'a') {\n\t\t\t\tthis.toggleAll();\n\t\t\t}\n\t\t\tif (char === 'i') {\n\t\t\t\tthis.toggleInvert();\n\t\t\t}\n\t\t});\n\n\t\tthis.on('cursor', (key) => {\n\t\t\tswitch (key) {\n\t\t\t\tcase 'left':\n\t\t\t\tcase 'up':\n\t\t\t\t\tthis.cursor = findCursor<T>(this.cursor, -1, this.options);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'down':\n\t\t\t\tcase 'right':\n\t\t\t\t\tthis.cursor = findCursor<T>(this.cursor, 1, this.options);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'space':\n\t\t\t\t\tthis.toggleValue();\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t});\n\t}\n}\n","import { styleText } from 'node:util';\nimport Prompt, { type PromptOptions } from './prompt.js';\n\nexport interface PasswordOptions extends PromptOptions<string, PasswordPrompt> {\n\tmask?: string;\n}\nexport default class PasswordPrompt extends Prompt<string> {\n\tprivate _mask = '•';\n\tget cursor() {\n\t\treturn this._cursor;\n\t}\n\tget masked() {\n\t\treturn this.userInput.replaceAll(/./g, this._mask);\n\t}\n\tget userInputWithCursor() {\n\t\tif (this.state === 'submit' || this.state === 'cancel') {\n\t\t\treturn this.masked;\n\t\t}\n\t\tconst userInput = this.userInput;\n\t\tif (this.cursor >= userInput.length) {\n\t\t\treturn `${this.masked}${styleText(['inverse', 'hidden'], '_')}`;\n\t\t}\n\t\tconst masked = this.masked;\n\t\tconst s1 = masked.slice(0, this.cursor);\n\t\tconst s2 = masked.slice(this.cursor);\n\t\treturn `${s1}${styleText('inverse', s2[0])}${s2.slice(1)}`;\n\t}\n\tclear() {\n\t\tthis._clearUserInput();\n\t}\n\tconstructor({ mask, ...opts }: PasswordOptions) {\n\t\tsuper(opts);\n\t\tthis._mask = mask ?? '•';\n\t\tthis.on('userInput', (input) => {\n\t\t\tthis._setValue(input);\n\t\t});\n\t}\n}\n","import { findCursor } from '../utils/cursor.js';\nimport Prompt, { type PromptOptions } from './prompt.js';\n\nexport interface SelectOptions<T extends { value: any; disabled?: boolean }>\n\textends PromptOptions<T['value'], SelectPrompt<T>> {\n\toptions: T[];\n\tinitialValue?: T['value'];\n}\nexport default class SelectPrompt<T extends { value: any; disabled?: boolean }> extends Prompt<\n\tT['value']\n> {\n\toptions: T[];\n\tcursor = 0;\n\n\tprivate get _selectedValue() {\n\t\treturn this.options[this.cursor];\n\t}\n\n\tprivate changeValue() {\n\t\tthis.value = this._selectedValue.value;\n\t}\n\n\tconstructor(opts: SelectOptions<T>) {\n\t\tsuper(opts, false);\n\n\t\tthis.options = opts.options;\n\n\t\tconst initialCursor = this.options.findIndex(({ value }) => value === opts.initialValue);\n\t\tconst cursor = initialCursor === -1 ? 0 : initialCursor;\n\t\tthis.cursor = this.options[cursor].disabled ? findCursor<T>(cursor, 1, this.options) : cursor;\n\t\tthis.changeValue();\n\n\t\tthis.on('cursor', (key) => {\n\t\t\tswitch (key) {\n\t\t\t\tcase 'left':\n\t\t\t\tcase 'up':\n\t\t\t\t\tthis.cursor = findCursor<T>(this.cursor, -1, this.options);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'down':\n\t\t\t\tcase 'right':\n\t\t\t\t\tthis.cursor = findCursor<T>(this.cursor, 1, this.options);\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tthis.changeValue();\n\t\t});\n\t}\n}\n","import Prompt, { type PromptOptions } from './prompt.js';\n\nexport interface SelectKeyOptions<T extends { value: string }>\n\textends PromptOptions<T['value'], SelectKeyPrompt<T>> {\n\toptions: T[];\n\tcaseSensitive?: boolean;\n}\nexport default class SelectKeyPrompt<T extends { value: string }> extends Prompt<T['value']> {\n\toptions: T[];\n\tcursor = 0;\n\n\tconstructor(opts: SelectKeyOptions<T>) {\n\t\tsuper(opts, false);\n\n\t\tthis.options = opts.options;\n\t\tconst caseSensitive = opts.caseSensitive === true;\n\t\tconst keys = this.options.map(({ value: [initial] }) => {\n\t\t\treturn caseSensitive ? initial : initial?.toLowerCase();\n\t\t});\n\t\tthis.cursor = Math.max(keys.indexOf(opts.initialValue), 0);\n\n\t\tthis.on('key', (key, keyInfo) => {\n\t\t\tif (!key) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst casedKey = caseSensitive && keyInfo.shift ? key.toUpperCase() : key;\n\t\t\tif (!keys.includes(casedKey)) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst value = this.options.find(({ value: [initial] }) => {\n\t\t\t\treturn caseSensitive ? initial === casedKey : initial?.toLowerCase() === key;\n\t\t\t});\n\t\t\tif (value) {\n\t\t\t\tthis.value = value.value;\n\t\t\t\tthis.state = 'submit';\n\t\t\t\tthis.emit('submit');\n\t\t\t}\n\t\t});\n\t}\n}\n","import { styleText } from 'node:util';\nimport Prompt, { type PromptOptions } from './prompt.js';\n\nexport interface TextOptions extends PromptOptions<string, TextPrompt> {\n\tplaceholder?: string;\n\tdefaultValue?: string;\n}\n\nexport default class TextPrompt extends Prompt<string> {\n\tget userInputWithCursor() {\n\t\tif (this.state === 'submit') {\n\t\t\treturn this.userInput;\n\t\t}\n\t\tconst userInput = this.userInput;\n\t\tif (this.cursor >= userInput.length) {\n\t\t\treturn `${this.userInput}█`;\n\t\t}\n\t\tconst s1 = userInput.slice(0, this.cursor);\n\t\tconst [s2, ...s3] = userInput.slice(this.cursor);\n\t\treturn `${s1}${styleText('inverse', s2)}${s3.join('')}`;\n\t}\n\tget cursor() {\n\t\treturn this._cursor;\n\t}\n\tconstructor(opts: TextOptions) {\n\t\tsuper({\n\t\t\t...opts,\n\t\t\tinitialUserInput: opts.initialUserInput ?? opts.initialValue,\n\t\t});\n\n\t\tthis.on('userInput', (input) => {\n\t\t\tthis._setValue(input);\n\t\t});\n\t\tthis.on('finalize', () => {\n\t\t\tif (!this.value) {\n\t\t\t\tthis.value = opts.defaultValue;\n\t\t\t}\n\t\t\tif (this.value === undefined) {\n\t\t\t\tthis.value = '';\n\t\t\t}\n\t\t});\n\t}\n}\n","import process from 'node:process';\n\nexport default function isUnicodeSupported() {\n\tif (process.platform !== 'win32') {\n\t\treturn process.env.TERM !== 'linux'; // Linux console (kernel)\n\t}\n\n\treturn Boolean(process.env.CI)\n\t\t|| Boolean(process.env.WT_SESSION) // Windows Terminal\n\t\t|| Boolean(process.env.TERMINUS_SUBLIME) // Terminus (<0.2.27)\n\t\t|| process.env.ConEmuTask === '{cmd::Cmder}' // ConEmu and cmder\n\t\t|| process.env.TERM_PROGRAM === 'Terminus-Sublime'\n\t\t|| process.env.TERM_PROGRAM === 'vscode'\n\t\t|| process.env.TERM === 'xterm-256color'\n\t\t|| process.env.TERM === 'alacritty'\n\t\t|| process.env.TERMINAL_EMULATOR === 'JetBrains-JediTerm';\n}\n","import type { Readable, Writable } from 'node:stream';\nimport { styleText } from 'node:util';\nimport type { State } from '@clack/core';\nimport isUnicodeSupported from 'is-unicode-supported';\n\nexport const unicode = isUnicodeSupported();\nexport const isCI = (): boolean => process.env.CI === 'true';\nexport const isTTY = (output: Writable): boolean => {\n\treturn (output as Writable & { isTTY?: boolean }).isTTY === true;\n};\nexport const unicodeOr = (c: string, fallback: string) => (unicode ? c : fallback);\nexport const S_STEP_ACTIVE = unicodeOr('◆', '*');\nexport const S_STEP_CANCEL = unicodeOr('■', 'x');\nexport const S_STEP_ERROR = unicodeOr('▲', 'x');\nexport const S_STEP_SUBMIT = unicodeOr('◇', 'o');\n\nexport const S_BAR_START = unicodeOr('┌', 'T');\nexport const S_BAR = unicodeOr('│', '|');\nexport const S_BAR_END = unicodeOr('└', '—');\nexport const S_BAR_START_RIGHT = unicodeOr('┐', 'T');\nexport const S_BAR_END_RIGHT = unicodeOr('┘', '—');\n\nexport const S_RADIO_ACTIVE = unicodeOr('●', '>');\nexport const S_RADIO_INACTIVE = unicodeOr('○', ' ');\nexport const S_CHECKBOX_ACTIVE = unicodeOr('◻', '[•]');\nexport const S_CHECKBOX_SELECTED = unicodeOr('◼', '[+]');\nexport const S_CHECKBOX_INACTIVE = unicodeOr('◻', '[ ]');\nexport const S_PASSWORD_MASK = unicodeOr('▪', '•');\n\nexport const S_BAR_H = unicodeOr('─', '-');\nexport const S_CORNER_TOP_RIGHT = unicodeOr('╮', '+');\nexport const S_CONNECT_LEFT = unicodeOr('├', '+');\nexport const S_CORNER_BOTTOM_RIGHT = unicodeOr('╯', '+');\nexport const S_CORNER_BOTTOM_LEFT = unicodeOr('╰', '+');\nexport const S_CORNER_TOP_LEFT = unicodeOr('╭', '+');\n\nexport const S_INFO = unicodeOr('●', '•');\nexport const S_SUCCESS = unicodeOr('◆', '*');\nexport const S_WARN = unicodeOr('▲', '!');\nexport const S_ERROR = unicodeOr('■', 'x');\n\nexport const symbol = (state: State) => {\n\tswitch (state) {\n\t\tcase 'initial':\n\t\tcase 'active':\n\t\t\treturn styleText('cyan', S_STEP_ACTIVE);\n\t\tcase 'cancel':\n\t\t\treturn styleText('red', S_STEP_CANCEL);\n\t\tcase 'error':\n\t\t\treturn styleText('yellow', S_STEP_ERROR);\n\t\tcase 'submit':\n\t\t\treturn styleText('green', S_STEP_SUBMIT);\n\t}\n};\n\nexport const symbolBar = (state: State) => {\n\tswitch (state) {\n\t\tcase 'initial':\n\t\tcase 'active':\n\t\t\treturn styleText('cyan', S_BAR);\n\t\tcase 'cancel':\n\t\t\treturn styleText('red', S_BAR);\n\t\tcase 'error':\n\t\t\treturn styleText('yellow', S_BAR);\n\t\tcase 'submit':\n\t\t\treturn styleText('green', S_BAR);\n\t}\n};\n\nexport interface CommonOptions {\n\tinput?: Readable;\n\toutput?: Writable;\n\tsignal?: AbortSignal;\n\twithGuide?: boolean;\n}\n","import { styleText } from 'node:util';\nimport { getColumns, getRows } from '@clack/core';\nimport { wrapAnsi } from 'fast-wrap-ansi';\nimport type { CommonOptions } from './common.js';\n\nexport interface LimitOptionsParams<TOption> extends CommonOptions {\n\toptions: TOption[];\n\tcursor: number;\n\tstyle: (option: TOption, active: boolean) => string;\n\tmaxItems?: number;\n\tcolumnPadding?: number;\n\trowPadding?: number;\n}\n\nconst trimLines = (\n\tgroups: Array<string[]>,\n\tinitialLineCount: number,\n\tstartIndex: number,\n\tendIndex: number,\n\tmaxLines: number\n) => {\n\tlet lineCount = initialLineCount;\n\tlet removals = 0;\n\tfor (let i = startIndex; i < endIndex; i++) {\n\t\tconst group = groups[i];\n\t\tlineCount = lineCount - group.length;\n\t\tremovals++;\n\t\tif (lineCount <= maxLines) {\n\t\t\tbreak;\n\t\t}\n\t}\n\treturn { lineCount, removals };\n};\n\nexport const limitOptions = <TOption>({\n\tcursor,\n\toptions,\n\tstyle,\n\toutput = process.stdout,\n\tmaxItems = Number.POSITIVE_INFINITY,\n\tcolumnPadding = 0,\n\trowPadding = 4,\n}: LimitOptionsParams<TOption>): string[] => {\n\tconst columns = getColumns(output);\n\tconst maxWidth = columns - columnPadding;\n\tconst rows = getRows(output);\n\tconst overflowFormat = styleText('dim', '...');\n\n\tconst outputMaxItems = Math.max(rows - rowPadding, 0);\n\t// We clamp to minimum 5 because anything less doesn't make sense UX wise\n\tconst computedMaxItems = Math.max(Math.min(maxItems, outputMaxItems), 5);\n\tlet slidingWindowLocation = 0;\n\n\tif (cursor >= computedMaxItems - 3) {\n\t\tslidingWindowLocation = Math.max(\n\t\t\tMath.min(cursor - computedMaxItems + 3, options.length - computedMaxItems),\n\t\t\t0\n\t\t);\n\t}\n\n\tlet shouldRenderTopEllipsis = computedMaxItems < options.length && slidingWindowLocation > 0;\n\tlet shouldRenderBottomEllipsis =\n\t\tcomputedMaxItems < options.length && slidingWindowLocation + computedMaxItems < options.length;\n\n\tconst slidingWindowLocationEnd = Math.min(\n\t\tslidingWindowLocation + computedMaxItems,\n\t\toptions.length\n\t);\n\tconst lineGroups: Array<string[]> = [];\n\tlet lineCount = 0;\n\tif (shouldRenderTopEllipsis) {\n\t\tlineCount++;\n\t}\n\tif (shouldRenderBottomEllipsis) {\n\t\tlineCount++;\n\t}\n\n\tconst slidingWindowLocationWithEllipsis =\n\t\tslidingWindowLocation + (shouldRenderTopEllipsis ? 1 : 0);\n\tconst slidingWindowLocationEndWithEllipsis =\n\t\tslidingWindowLocationEnd - (shouldRenderBottomEllipsis ? 1 : 0);\n\n\tfor (let i = slidingWindowLocationWithEllipsis; i < slidingWindowLocationEndWithEllipsis; i++) {\n\t\tconst wrappedLines = wrapAnsi(style(options[i], i === cursor), maxWidth, {\n\t\t\thard: true,\n\t\t\ttrim: false,\n\t\t}).split('\\n');\n\t\tlineGroups.push(wrappedLines);\n\t\tlineCount += wrappedLines.length;\n\t}\n\n\tif (lineCount > outputMaxItems) {\n\t\tlet precedingRemovals = 0;\n\t\tlet followingRemovals = 0;\n\t\tlet newLineCount = lineCount;\n\t\tconst cursorGroupIndex = cursor - slidingWindowLocationWithEllipsis;\n\t\tconst trimLinesLocal = (startIndex: number, endIndex: number) =>\n\t\t\ttrimLines(lineGroups, newLineCount, startIndex, endIndex, outputMaxItems);\n\n\t\tif (shouldRenderTopEllipsis) {\n\t\t\t({ lineCount: newLineCount, removals: precedingRemovals } = trimLinesLocal(\n\t\t\t\t0,\n\t\t\t\tcursorGroupIndex\n\t\t\t));\n\t\t\tif (newLineCount > outputMaxItems) {\n\t\t\t\t({ lineCount: newLineCount, removals: followingRemovals } = trimLinesLocal(\n\t\t\t\t\tcursorGroupIndex + 1,\n\t\t\t\t\tlineGroups.length\n\t\t\t\t));\n\t\t\t}\n\t\t} else {\n\t\t\t({ lineCount: newLineCount, removals: followingRemovals } = trimLinesLocal(\n\t\t\t\tcursorGroupIndex + 1,\n\t\t\t\tlineGroups.length\n\t\t\t));\n\t\t\tif (newLineCount > outputMaxItems) {\n\t\t\t\t({ lineCount: newLineCount, removals: precedingRemovals } = trimLinesLocal(\n\t\t\t\t\t0,\n\t\t\t\t\tcursorGroupIndex\n\t\t\t\t));\n\t\t\t}\n\t\t}\n\n\t\tif (precedingRemovals > 0) {\n\t\t\tshouldRenderTopEllipsis = true;\n\t\t\tlineGroups.splice(0, precedingRemovals);\n\t\t}\n\t\tif (followingRemovals > 0) {\n\t\t\tshouldRenderBottomEllipsis = true;\n\t\t\tlineGroups.splice(lineGroups.length - followingRemovals, followingRemovals);\n\t\t}\n\t}\n\n\tconst result: string[] = [];\n\tif (shouldRenderTopEllipsis) {\n\t\tresult.push(overflowFormat);\n\t}\n\tfor (const lineGroup of lineGroups) {\n\t\tfor (const line of lineGroup) {\n\t\t\tresult.push(line);\n\t\t}\n\t}\n\tif (shouldRenderBottomEllipsis) {\n\t\tresult.push(overflowFormat);\n\t}\n\n\treturn result;\n};\n","import { styleText } from 'node:util';\nimport { AutocompletePrompt, settings } from '@clack/core';\nimport {\n\ttype CommonOptions,\n\tS_BAR,\n\tS_BAR_END,\n\tS_CHECKBOX_INACTIVE,\n\tS_CHECKBOX_SELECTED,\n\tS_RADIO_ACTIVE,\n\tS_RADIO_INACTIVE,\n\tsymbol,\n} from './common.js';\nimport { limitOptions } from './limit-options.js';\nimport type { Option } from './select.js';\n\nfunction getLabel<T>(option: Option<T>) {\n\treturn option.label ?? String(option.value ?? '');\n}\n\nfunction getFilteredOption<T>(searchText: string, option: Option<T>): boolean {\n\tif (!searchText) {\n\t\treturn true;\n\t}\n\tconst label = (option.label ?? String(option.value ?? '')).toLowerCase();\n\tconst hint = (option.hint ?? '').toLowerCase();\n\tconst value = String(option.value).toLowerCase();\n\tconst term = searchText.toLowerCase();\n\n\treturn label.includes(term) || hint.includes(term) || value.includes(term);\n}\n\nfunction getSelectedOptions<T>(values: T[], options: Option<T>[]): Option<T>[] {\n\tconst results: Option<T>[] = [];\n\n\tfor (const option of options) {\n\t\tif (values.includes(option.value)) {\n\t\t\tresults.push(option);\n\t\t}\n\t}\n\n\treturn results;\n}\n\ninterface AutocompleteSharedOptions<Value> extends CommonOptions {\n\t/**\n\t * The message to display to the user.\n\t */\n\tmessage: string;\n\t/**\n\t * Available options for the autocomplete prompt.\n\t */\n\toptions: Option<Value>[] | ((this: AutocompletePrompt<Option<Value>>) => Option<Value>[]);\n\t/**\n\t * Maximum number of items to display at once.\n\t */\n\tmaxItems?: number;\n\t/**\n\t * Placeholder text to display when no input is provided.\n\t */\n\tplaceholder?: string;\n\t/**\n\t * Validates the value\n\t */\n\tvalidate?: (value: Value | Value[] | undefined) => string | Error | undefined;\n\t/**\n\t * Custom filter function to match options against search input.\n\t * If not provided, a default filter that matches label, hint, and value is used.\n\t */\n\tfilter?: (search: string, option: Option<Value>) => boolean;\n}\n\nexport interface AutocompleteOptions<Value> extends AutocompleteSharedOptions<Value> {\n\t/**\n\t * The initial selected value.\n\t */\n\tinitialValue?: Value;\n\t/**\n\t * The initial user input\n\t */\n\tinitialUserInput?: string;\n}\n\nexport const autocomplete = <Value>(opts: AutocompleteOptions<Value>) => {\n\tconst prompt = new AutocompletePrompt({\n\t\toptions: opts.options,\n\t\tinitialValue: opts.initialValue ? [opts.initialValue] : undefined,\n\t\tinitialUserInput: opts.initialUserInput,\n\t\tplaceholder: opts.placeholder,\n\t\tfilter:\n\t\t\topts.filter ??\n\t\t\t((search: string, opt: Option<Value>) => {\n\t\t\t\treturn getFilteredOption(search, opt);\n\t\t\t}),\n\t\tsignal: opts.signal,\n\t\tinput: opts.input,\n\t\toutput: opts.output,\n\t\tvalidate: opts.validate,\n\t\trender() {\n\t\t\tconst hasGuide = opts.withGuide ?? settings.withGuide;\n\t\t\t// Title and message display\n\t\t\tconst headings = hasGuide\n\t\t\t\t? [`${styleText('gray', S_BAR)}`, `${symbol(this.state)} ${opts.message}`]\n\t\t\t\t: [`${symbol(this.state)} ${opts.message}`];\n\t\t\tconst userInput = this.userInput;\n\t\t\tconst options = this.options;\n\t\t\tconst placeholder = opts.placeholder;\n\t\t\tconst showPlaceholder = userInput === '' && placeholder !== undefined;\n\t\t\tconst opt = (option: Option<Value>, state: 'inactive' | 'active' | 'disabled') => {\n\t\t\t\tconst label = getLabel(option);\n\t\t\t\tconst hint =\n\t\t\t\t\toption.hint && option.value === this.focusedValue\n\t\t\t\t\t\t? styleText('dim', ` (${option.hint})`)\n\t\t\t\t\t\t: '';\n\t\t\t\tswitch (state) {\n\t\t\t\t\tcase 'active':\n\t\t\t\t\t\treturn `${styleText('green', S_RADIO_ACTIVE)} ${label}${hint}`;\n\t\t\t\t\tcase 'inactive':\n\t\t\t\t\t\treturn `${styleText('dim', S_RADIO_INACTIVE)} ${styleText('dim', label)}`;\n\t\t\t\t\tcase 'disabled':\n\t\t\t\t\t\treturn `${styleText('gray', S_RADIO_INACTIVE)} ${styleText(['strikethrough', 'gray'], label)}`;\n\t\t\t\t}\n\t\t\t};\n\n\t\t\t// Handle different states\n\t\t\tswitch (this.state) {\n\t\t\t\tcase 'submit': {\n\t\t\t\t\t// Show selected value\n\t\t\t\t\tconst selected = getSelectedOptions(this.selectedValues, options);\n\t\t\t\t\tconst label =\n\t\t\t\t\t\tselected.length > 0 ? ` ${styleText('dim', selected.map(getLabel).join(', '))}` : '';\n\t\t\t\t\tconst submitPrefix = hasGuide ? styleText('gray', S_BAR) : '';\n\t\t\t\t\treturn `${headings.join('\\n')}\\n${submitPrefix}${label}`;\n\t\t\t\t}\n\n\t\t\t\tcase 'cancel': {\n\t\t\t\t\tconst userInputText = userInput\n\t\t\t\t\t\t? ` ${styleText(['strikethrough', 'dim'], userInput)}`\n\t\t\t\t\t\t: '';\n\t\t\t\t\tconst cancelPrefix = hasGuide ? styleText('gray', S_BAR) : '';\n\t\t\t\t\treturn `${headings.join('\\n')}\\n${cancelPrefix}${userInputText}`;\n\t\t\t\t}\n\n\t\t\t\tdefault: {\n\t\t\t\t\tconst barStyle = this.state === 'error' ? 'yellow' : 'cyan';\n\t\t\t\t\tconst guidePrefix = hasGuide ? `${styleText(barStyle, S_BAR)} ` : '';\n\t\t\t\t\tconst guidePrefixEnd = hasGuide ? styleText(barStyle, S_BAR_END) : '';\n\t\t\t\t\t// Display cursor position - show plain text in navigation mode\n\t\t\t\t\tlet searchText = '';\n\t\t\t\t\tif (this.isNavigating || showPlaceholder) {\n\t\t\t\t\t\tconst searchTextValue = showPlaceholder ? placeholder : userInput;\n\t\t\t\t\t\tsearchText = searchTextValue !== '' ? ` ${styleText('dim', searchTextValue)}` : '';\n\t\t\t\t\t} else {\n\t\t\t\t\t\tsearchText = ` ${this.userInputWithCursor}`;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Show match count if filtered\n\t\t\t\t\tconst matches =\n\t\t\t\t\t\tthis.filteredOptions.length !== options.length\n\t\t\t\t\t\t\t? styleText(\n\t\t\t\t\t\t\t\t\t'dim',\n\t\t\t\t\t\t\t\t\t` (${this.filteredOptions.length} match${this.filteredOptions.length === 1 ? '' : 'es'})`\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t: '';\n\n\t\t\t\t\t// No matches message\n\t\t\t\t\tconst noResults =\n\t\t\t\t\t\tthis.filteredOptions.length === 0 && userInput\n\t\t\t\t\t\t\t? [`${guidePrefix}${styleText('yellow', 'No matches found')}`]\n\t\t\t\t\t\t\t: [];\n\n\t\t\t\t\tconst validationError =\n\t\t\t\t\t\tthis.state === 'error' ? [`${guidePrefix}${styleText('yellow', this.error)}`] : [];\n\n\t\t\t\t\tif (hasGuide) {\n\t\t\t\t\t\theadings.push(`${guidePrefix.trimEnd()}`);\n\t\t\t\t\t}\n\t\t\t\t\theadings.push(\n\t\t\t\t\t\t`${guidePrefix}${styleText('dim', 'Search:')}${searchText}${matches}`,\n\t\t\t\t\t\t...noResults,\n\t\t\t\t\t\t...validationError\n\t\t\t\t\t);\n\n\t\t\t\t\t// Show instructions\n\t\t\t\t\tconst instructions = [\n\t\t\t\t\t\t`${styleText('dim', '↑/↓')} to select`,\n\t\t\t\t\t\t`${styleText('dim', 'Enter:')} confirm`,\n\t\t\t\t\t\t`${styleText('dim', 'Type:')} to search`,\n\t\t\t\t\t];\n\n\t\t\t\t\tconst footers = [`${guidePrefix}${instructions.join(' • ')}`, guidePrefixEnd];\n\n\t\t\t\t\t// Render options with selection\n\t\t\t\t\tconst displayOptions =\n\t\t\t\t\t\tthis.filteredOptions.length === 0\n\t\t\t\t\t\t\t? []\n\t\t\t\t\t\t\t: limitOptions({\n\t\t\t\t\t\t\t\t\tcursor: this.cursor,\n\t\t\t\t\t\t\t\t\toptions: this.filteredOptions,\n\t\t\t\t\t\t\t\t\tcolumnPadding: hasGuide ? 3 : 0, // for `| ` when guide is shown\n\t\t\t\t\t\t\t\t\trowPadding: headings.length + footers.length,\n\t\t\t\t\t\t\t\t\tstyle: (option, active) => {\n\t\t\t\t\t\t\t\t\t\treturn opt(\n\t\t\t\t\t\t\t\t\t\t\toption,\n\t\t\t\t\t\t\t\t\t\t\toption.disabled ? 'disabled' : active ? 'active' : 'inactive'\n\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\tmaxItems: opts.maxItems,\n\t\t\t\t\t\t\t\t\toutput: opts.output,\n\t\t\t\t\t\t\t\t});\n\n\t\t\t\t\t// Return the formatted prompt\n\t\t\t\t\treturn [\n\t\t\t\t\t\t...headings,\n\t\t\t\t\t\t...displayOptions.map((option) => `${guidePrefix}${option}`),\n\t\t\t\t\t\t...footers,\n\t\t\t\t\t].join('\\n');\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t});\n\n\t// Return the result or cancel symbol\n\treturn prompt.prompt() as Promise<Value | symbol>;\n};\n\n// Type definition for the autocompleteMultiselect component\nexport interface AutocompleteMultiSelectOptions<Value> extends AutocompleteSharedOptions<Value> {\n\t/**\n\t * The initial selected values\n\t */\n\tinitialValues?: Value[];\n\t/**\n\t * If true, at least one option must be selected\n\t */\n\trequired?: boolean;\n}\n\n/**\n * Integrated autocomplete multiselect - combines type-ahead filtering with multiselect in one UI\n */\nexport const autocompleteMultiselect = <Value>(opts: AutocompleteMultiSelectOptions<Value>) => {\n\tconst formatOption = (\n\t\toption: Option<Value>,\n\t\tactive: boolean,\n\t\tselectedValues: Value[],\n\t\tfocusedValue: Value | undefined\n\t) => {\n\t\tconst isSelected = selectedValues.includes(option.value);\n\t\tconst label = option.label ?? String(option.value ?? '');\n\t\tconst hint =\n\t\t\toption.hint && focusedValue !== undefined && option.value === focusedValue\n\t\t\t\t? styleText('dim', ` (${option.hint})`)\n\t\t\t\t: '';\n\t\tconst checkbox = isSelected\n\t\t\t? styleText('green', S_CHECKBOX_SELECTED)\n\t\t\t: styleText('dim', S_CHECKBOX_INACTIVE);\n\n\t\tif (option.disabled) {\n\t\t\treturn `${styleText('gray', S_CHECKBOX_INACTIVE)} ${styleText(['strikethrough', 'gray'], label)}`;\n\t\t}\n\t\tif (active) {\n\t\t\treturn `${checkbox} ${label}${hint}`;\n\t\t}\n\t\treturn `${checkbox} ${styleText('dim', label)}`;\n\t};\n\n\t// Create text prompt which we'll use as foundation\n\tconst prompt = new AutocompletePrompt<Option<Value>>({\n\t\toptions: opts.options,\n\t\tmultiple: true,\n\t\tplaceholder: opts.placeholder,\n\t\tfilter:\n\t\t\topts.filter ??\n\t\t\t((search, opt) => {\n\t\t\t\treturn getFilteredOption(search, opt);\n\t\t\t}),\n\t\tvalidate: () => {\n\t\t\tif (opts.required && prompt.selectedValues.length === 0) {\n\t\t\t\treturn 'Please select at least one item';\n\t\t\t}\n\t\t\treturn undefined;\n\t\t},\n\t\tinitialValue: opts.initialValues,\n\t\tsignal: opts.signal,\n\t\tinput: opts.input,\n\t\toutput: opts.output,\n\t\trender() {\n\t\t\tconst hasGuide = opts.withGuide ?? settings.withGuide;\n\t\t\t// Title and symbol\n\t\t\tconst title = `${hasGuide ? `${styleText('gray', S_BAR)}\\n` : ''}${symbol(this.state)} ${\n\t\t\t\topts.message\n\t\t\t}\\n`;\n\n\t\t\t// Selection counter\n\t\t\tconst userInput = this.userInput;\n\t\t\tconst placeholder = opts.placeholder;\n\t\t\tconst showPlaceholder = userInput === '' && placeholder !== undefined;\n\n\t\t\t// Search input display\n\t\t\tconst searchText =\n\t\t\t\tthis.isNavigating || showPlaceholder\n\t\t\t\t\t? styleText('dim', showPlaceholder ? placeholder : userInput) // Just show plain text when in navigation mode\n\t\t\t\t\t: this.userInputWithCursor;\n\n\t\t\tconst options = this.options;\n\n\t\t\tconst matches =\n\t\t\t\tthis.filteredOptions.length !== options.length\n\t\t\t\t\t? styleText(\n\t\t\t\t\t\t\t'dim',\n\t\t\t\t\t\t\t` (${this.filteredOptions.length} match${this.filteredOptions.length === 1 ? '' : 'es'})`\n\t\t\t\t\t\t)\n\t\t\t\t\t: '';\n\n\t\t\t// Render prompt state\n\t\t\tswitch (this.state) {\n\t\t\t\tcase 'submit': {\n\t\t\t\t\treturn `${title}${hasGuide ? `${styleText('gray', S_BAR)} ` : ''}${styleText(\n\t\t\t\t\t\t'dim',\n\t\t\t\t\t\t`${this.selectedValues.length} items selected`\n\t\t\t\t\t)}`;\n\t\t\t\t}\n\t\t\t\tcase 'cancel': {\n\t\t\t\t\treturn `${title}${hasGuide ? `${styleText('gray', S_BAR)} ` : ''}${styleText(\n\t\t\t\t\t\t['strikethrough', 'dim'],\n\t\t\t\t\t\tuserInput\n\t\t\t\t\t)}`;\n\t\t\t\t}\n\t\t\t\tdefault: {\n\t\t\t\t\tconst barStyle = this.state === 'error' ? 'yellow' : 'cyan';\n\t\t\t\t\tconst guidePrefix = hasGuide ? `${styleText(barStyle, S_BAR)} ` : '';\n\t\t\t\t\tconst guidePrefixEnd = hasGuide ? styleText(barStyle, S_BAR_END) : '';\n\t\t\t\t\t// Instructions\n\t\t\t\t\tconst instructions = [\n\t\t\t\t\t\t`${styleText('dim', '↑/↓')} to navigate`,\n\t\t\t\t\t\t`${styleText('dim', this.isNavigating ? 'Space/Tab:' : 'Tab:')} select`,\n\t\t\t\t\t\t`${styleText('dim', 'Enter:')} confirm`,\n\t\t\t\t\t\t`${styleText('dim', 'Type:')} to search`,\n\t\t\t\t\t];\n\n\t\t\t\t\t// No results message\n\t\t\t\t\tconst noResults =\n\t\t\t\t\t\tthis.filteredOptions.length === 0 && userInput\n\t\t\t\t\t\t\t? [`${guidePrefix}${styleText('yellow', 'No matches found')}`]\n\t\t\t\t\t\t\t: [];\n\n\t\t\t\t\tconst errorMessage =\n\t\t\t\t\t\tthis.state === 'error' ? [`${guidePrefix}${styleText('yellow', this.error)}`] : [];\n\n\t\t\t\t\t// Calculate header and footer line counts for rowPadding\n\t\t\t\t\tconst headerLines = [\n\t\t\t\t\t\t...`${title}${hasGuide ? styleText(barStyle, S_BAR) : ''}`.split('\\n'),\n\t\t\t\t\t\t`${guidePrefix}${styleText('dim', 'Search:')} ${searchText}${matches}`,\n\t\t\t\t\t\t...noResults,\n\t\t\t\t\t\t...errorMessage,\n\t\t\t\t\t];\n\t\t\t\t\tconst footerLines = [`${guidePrefix}${instructions.join(' • ')}`, guidePrefixEnd];\n\n\t\t\t\t\t// Get limited options for display\n\t\t\t\t\tconst displayOptions = limitOptions({\n\t\t\t\t\t\tcursor: this.cursor,\n\t\t\t\t\t\toptions: this.filteredOptions,\n\t\t\t\t\t\tstyle: (option, active) =>\n\t\t\t\t\t\t\tformatOption(option, active, this.selectedValues, this.focusedValue),\n\t\t\t\t\t\tmaxItems: opts.maxItems,\n\t\t\t\t\t\toutput: opts.output,\n\t\t\t\t\t\trowPadding: headerLines.length + footerLines.length,\n\t\t\t\t\t});\n\n\t\t\t\t\t// Build the prompt display\n\t\t\t\t\treturn [\n\t\t\t\t\t\t...headerLines,\n\t\t\t\t\t\t...displayOptions.map((option) => `${guidePrefix}${option}`),\n\t\t\t\t\t\t...footerLines,\n\t\t\t\t\t].join('\\n');\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t});\n\n\t// Return the result or cancel symbol\n\treturn prompt.prompt() as Promise<Value[] | symbol>;\n};\n","import type { Writable } from 'node:stream';\nimport { getColumns, settings } from '@clack/core';\nimport stringWidth from 'fast-string-width';\nimport { wrapAnsi } from 'fast-wrap-ansi';\nimport {\n\ttype CommonOptions,\n\tS_BAR,\n\tS_BAR_END,\n\tS_BAR_END_RIGHT,\n\tS_BAR_H,\n\tS_BAR_START,\n\tS_BAR_START_RIGHT,\n\tS_CORNER_BOTTOM_LEFT,\n\tS_CORNER_BOTTOM_RIGHT,\n\tS_CORNER_TOP_LEFT,\n\tS_CORNER_TOP_RIGHT,\n} from './common.js';\n\nexport type BoxAlignment = 'left' | 'center' | 'right';\n\ntype BoxSymbols = [topLeft: string, topRight: string, bottomLeft: string, bottomRight: string];\n\nconst roundedSymbols: BoxSymbols = [\n\tS_CORNER_TOP_LEFT,\n\tS_CORNER_TOP_RIGHT,\n\tS_CORNER_BOTTOM_LEFT,\n\tS_CORNER_BOTTOM_RIGHT,\n];\nconst squareSymbols: BoxSymbols = [S_BAR_START, S_BAR_START_RIGHT, S_BAR_END, S_BAR_END_RIGHT];\n\nexport interface BoxOptions extends CommonOptions {\n\tcontentAlign?: BoxAlignment;\n\ttitleAlign?: BoxAlignment;\n\twidth?: number | 'auto';\n\ttitlePadding?: number;\n\tcontentPadding?: number;\n\trounded?: boolean;\n\tformatBorder?: (text: string) => string;\n}\n\nfunction getPaddingForLine(\n\tlineLength: number,\n\tinnerWidth: number,\n\tpadding: number,\n\tcontentAlign: BoxAlignment | undefined\n): [number, number] {\n\tlet leftPadding = padding;\n\tlet rightPadding = padding;\n\tif (contentAlign === 'center') {\n\t\tleftPadding = Math.floor((innerWidth - lineLength) / 2);\n\t} else if (contentAlign === 'right') {\n\t\tleftPadding = innerWidth - lineLength - padding;\n\t}\n\n\trightPadding = innerWidth - leftPadding - lineLength;\n\n\treturn [leftPadding, rightPadding];\n}\n\nconst defaultFormatBorder = (text: string) => text;\n\nexport const box = (message = '', title = '', opts?: BoxOptions) => {\n\tconst output: Writable = opts?.output ?? process.stdout;\n\tconst columns = getColumns(output);\n\tconst borderWidth = 1;\n\tconst borderTotalWidth = borderWidth * 2;\n\tconst titlePadding = opts?.titlePadding ?? 1;\n\tconst contentPadding = opts?.contentPadding ?? 2;\n\tconst width = opts?.width === undefined || opts.width === 'auto' ? 1 : Math.min(1, opts.width);\n\tconst hasGuide = opts?.withGuide ?? settings.withGuide;\n\tconst linePrefix = !hasGuide ? '' : `${S_BAR} `;\n\tconst formatBorder = opts?.formatBorder ?? defaultFormatBorder;\n\tconst symbols = (opts?.rounded ? roundedSymbols : squareSymbols).map(formatBorder);\n\tconst hSymbol = formatBorder(S_BAR_H);\n\tconst vSymbol = formatBorder(S_BAR);\n\tconst linePrefixWidth = stringWidth(linePrefix);\n\tconst titleWidth = stringWidth(title);\n\tconst maxBoxWidth = columns - linePrefixWidth;\n\tlet boxWidth = Math.floor(columns * width) - linePrefixWidth;\n\tif (opts?.width === 'auto') {\n\t\tconst lines = message.split('\\n');\n\t\tlet longestLine = titleWidth + titlePadding * 2;\n\t\tfor (const line of lines) {\n\t\t\tconst lineWithPadding = stringWidth(line) + contentPadding * 2;\n\t\t\tif (lineWithPadding > longestLine) {\n\t\t\t\tlongestLine = lineWithPadding;\n\t\t\t}\n\t\t}\n\t\tconst longestLineWidth = longestLine + borderTotalWidth;\n\t\tif (longestLineWidth < boxWidth) {\n\t\t\tboxWidth = longestLineWidth;\n\t\t}\n\t}\n\tif (boxWidth % 2 !== 0) {\n\t\tif (boxWidth < maxBoxWidth) {\n\t\t\tboxWidth++;\n\t\t} else {\n\t\t\tboxWidth--;\n\t\t}\n\t}\n\tconst innerWidth = boxWidth - borderTotalWidth;\n\tconst maxTitleLength = innerWidth - titlePadding * 2;\n\tconst truncatedTitle =\n\t\ttitleWidth > maxTitleLength ? `${title.slice(0, maxTitleLength - 3)}...` : title;\n\tconst [titlePaddingLeft, titlePaddingRight] = getPaddingForLine(\n\t\tstringWidth(truncatedTitle),\n\t\tinnerWidth,\n\t\ttitlePadding,\n\t\topts?.titleAlign\n\t);\n\tconst wrappedMessage = wrapAnsi(message, innerWidth - contentPadding * 2, {\n\t\thard: true,\n\t\ttrim: false,\n\t});\n\toutput.write(\n\t\t`${linePrefix}${symbols[0]}${hSymbol.repeat(titlePaddingLeft)}${truncatedTitle}${hSymbol.repeat(titlePaddingRight)}${symbols[1]}\\n`\n\t);\n\tconst wrappedLines = wrappedMessage.split('\\n');\n\tfor (const line of wrappedLines) {\n\t\tconst [leftLinePadding, rightLinePadding] = getPaddingForLine(\n\t\t\tstringWidth(line),\n\t\t\tinnerWidth,\n\t\t\tcontentPadding,\n\t\t\topts?.contentAlign\n\t\t);\n\t\toutput.write(\n\t\t\t`${linePrefix}${vSymbol}${' '.repeat(leftLinePadding)}${line}${' '.repeat(rightLinePadding)}${vSymbol}\\n`\n\t\t);\n\t}\n\toutput.write(`${linePrefix}${symbols[2]}${hSymbol.repeat(innerWidth)}${symbols[3]}\\n`);\n};\n","import { styleText } from 'node:util';\nimport { ConfirmPrompt, settings, wrapTextWithPrefix } from '@clack/core';\nimport {\n\ttype CommonOptions,\n\tS_BAR,\n\tS_BAR_END,\n\tS_RADIO_ACTIVE,\n\tS_RADIO_INACTIVE,\n\tsymbol,\n} from './common.js';\n\nexport interface ConfirmOptions extends CommonOptions {\n\tmessage: string;\n\tactive?: string;\n\tinactive?: string;\n\tinitialValue?: boolean;\n\tvertical?: boolean;\n}\nexport const confirm = (opts: ConfirmOptions) => {\n\tconst active = opts.active ?? 'Yes';\n\tconst inactive = opts.inactive ?? 'No';\n\treturn new ConfirmPrompt({\n\t\tactive,\n\t\tinactive,\n\t\tsignal: opts.signal,\n\t\tinput: opts.input,\n\t\toutput: opts.output,\n\t\tinitialValue: opts.initialValue ?? true,\n\t\trender() {\n\t\t\tconst hasGuide = opts.withGuide ?? settings.withGuide;\n\t\t\tconst titlePrefix = `${symbol(this.state)} `;\n\t\t\tconst titlePrefixBar = hasGuide ? `${styleText('gray', S_BAR)} ` : '';\n\t\t\tconst messageLines = wrapTextWithPrefix(\n\t\t\t\topts.output,\n\t\t\t\topts.message,\n\t\t\t\ttitlePrefixBar,\n\t\t\t\ttitlePrefix\n\t\t\t);\n\t\t\tconst title = `${hasGuide ? `${styleText('gray', S_BAR)}\\n` : ''}${messageLines}\\n`;\n\t\t\tconst value = this.value ? active : inactive;\n\n\t\t\tswitch (this.state) {\n\t\t\t\tcase 'submit': {\n\t\t\t\t\tconst submitPrefix = hasGuide ? `${styleText('gray', S_BAR)} ` : '';\n\t\t\t\t\treturn `${title}${submitPrefix}${styleText('dim', value)}`;\n\t\t\t\t}\n\t\t\t\tcase 'cancel': {\n\t\t\t\t\tconst cancelPrefix = hasGuide ? `${styleText('gray', S_BAR)} ` : '';\n\t\t\t\t\treturn `${title}${cancelPrefix}${styleText(['strikethrough', 'dim'], value)}${\n\t\t\t\t\t\thasGuide ? `\\n${styleText('gray', S_BAR)}` : ''\n\t\t\t\t\t}`;\n\t\t\t\t}\n\t\t\t\tdefault: {\n\t\t\t\t\tconst defaultPrefix = hasGuide ? `${styleText('cyan', S_BAR)} ` : '';\n\t\t\t\t\tconst defaultPrefixEnd = hasGuide ? styleText('cyan', S_BAR_END) : '';\n\t\t\t\t\treturn `${title}${defaultPrefix}${\n\t\t\t\t\t\tthis.value\n\t\t\t\t\t\t\t? `${styleText('green', S_RADIO_ACTIVE)} ${active}`\n\t\t\t\t\t\t\t: `${styleText('dim', S_RADIO_INACTIVE)} ${styleText('dim', active)}`\n\t\t\t\t\t}${opts.vertical ? (hasGuide ? `\\n${styleText('cyan', S_BAR)} ` : '\\n') : ` ${styleText('dim', '/')} `}${\n\t\t\t\t\t\t!this.value\n\t\t\t\t\t\t\t? `${styleText('green', S_RADIO_ACTIVE)} ${inactive}`\n\t\t\t\t\t\t\t: `${styleText('dim', S_RADIO_INACTIVE)} ${styleText('dim', inactive)}`\n\t\t\t\t\t}\\n${defaultPrefixEnd}\\n`;\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t}).prompt() as Promise<boolean | symbol>;\n};\n","import { styleText } from 'node:util';\nimport type { DateFormat, State } from '@clack/core';\nimport { DatePrompt, settings } from '@clack/core';\nimport { type CommonOptions, S_BAR, S_BAR_END, symbol } from './common.js';\n\nexport type { DateFormat };\n\nexport interface DateOptions extends CommonOptions {\n\tmessage: string;\n\tformat?: DateFormat;\n\tlocale?: string;\n\tdefaultValue?: Date;\n\tinitialValue?: Date;\n\tminDate?: Date;\n\tmaxDate?: Date;\n\tvalidate?: (value: Date | undefined) => string | Error | undefined;\n}\n\nexport const date = (opts: DateOptions) => {\n\tconst validate = opts.validate;\n\treturn new DatePrompt({\n\t\t...opts,\n\t\tvalidate(value: Date | undefined) {\n\t\t\tif (value === undefined) {\n\t\t\t\tif (opts.defaultValue !== undefined) return undefined;\n\t\t\t\tif (validate) return validate(value);\n\t\t\t\treturn settings.date.messages.required;\n\t\t\t}\n\t\t\tconst iso = (d: Date) => d.toISOString().slice(0, 10);\n\t\t\tif (opts.minDate && iso(value) < iso(opts.minDate)) {\n\t\t\t\treturn settings.date.messages.afterMin(opts.minDate);\n\t\t\t}\n\t\t\tif (opts.maxDate && iso(value) > iso(opts.maxDate)) {\n\t\t\t\treturn settings.date.messages.beforeMax(opts.maxDate);\n\t\t\t}\n\t\t\tif (validate) return validate(value);\n\t\t\treturn undefined;\n\t\t},\n\t\trender() {\n\t\t\tconst hasGuide = (opts?.withGuide ?? settings.withGuide) !== false;\n\t\t\tconst titlePrefix = `${hasGuide ? `${styleText('gray', S_BAR)}\\n` : ''}${symbol(this.state)} `;\n\t\t\tconst title = `${titlePrefix}${opts.message}\\n`;\n\n\t\t\tconst state = this.state !== 'initial' ? this.state : 'active';\n\n\t\t\tconst userInput = renderDate(this, state);\n\t\t\tconst value = this.value instanceof Date ? this.formattedValue : '';\n\n\t\t\tswitch (this.state) {\n\t\t\t\tcase 'error': {\n\t\t\t\t\tconst errorText = this.error ? ` ${styleText('yellow', this.error)}` : '';\n\t\t\t\t\tconst bar = hasGuide ? `${styleText('yellow', S_BAR)} ` : '';\n\t\t\t\t\tconst barEnd = hasGuide ? styleText('yellow', S_BAR_END) : '';\n\t\t\t\t\treturn `${title.trim()}\\n${bar}${userInput}\\n${barEnd}${errorText}\\n`;\n\t\t\t\t}\n\t\t\t\tcase 'submit': {\n\t\t\t\t\tconst valueText = value ? ` ${styleText('dim', value)}` : '';\n\t\t\t\t\tconst bar = hasGuide ? styleText('gray', S_BAR) : '';\n\t\t\t\t\treturn `${title}${bar}${valueText}`;\n\t\t\t\t}\n\t\t\t\tcase 'cancel': {\n\t\t\t\t\tconst valueText = value ? ` ${styleText(['strikethrough', 'dim'], value)}` : '';\n\t\t\t\t\tconst bar = hasGuide ? styleText('gray', S_BAR) : '';\n\t\t\t\t\treturn `${title}${bar}${valueText}${value.trim() ? `\\n${bar}` : ''}`;\n\t\t\t\t}\n\t\t\t\tdefault: {\n\t\t\t\t\tconst bar = hasGuide ? `${styleText('cyan', S_BAR)} ` : '';\n\t\t\t\t\tconst barEnd = hasGuide ? styleText('cyan', S_BAR_END) : '';\n\t\t\t\t\tconst inlineBar = hasGuide ? `${styleText('cyan', S_BAR)} ` : '';\n\t\t\t\t\tconst inlineError = this.inlineError\n\t\t\t\t\t\t? `\\n${inlineBar}${styleText('yellow', this.inlineError)}`\n\t\t\t\t\t\t: '';\n\t\t\t\t\treturn `${title}${bar}${userInput}${inlineError}\\n${barEnd}\\n`;\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t}).prompt() as Promise<Date | symbol>;\n};\n\nfunction renderDate(prompt: Omit<InstanceType<typeof DatePrompt>, 'prompt'>, state: State): string {\n\tconst parts = prompt.segmentValues;\n\tconst cursor = prompt.segmentCursor;\n\n\tif (state === 'submit' || state === 'cancel') {\n\t\treturn prompt.formattedValue;\n\t}\n\n\tconst sep = styleText('gray', prompt.separator);\n\treturn prompt.segments\n\t\t.map((seg, i) => {\n\t\t\tconst isActive = i === cursor.segmentIndex && !['submit', 'cancel'].includes(state);\n\t\t\tconst label = DEFAULT_LABELS[seg.type];\n\t\t\treturn renderSegment(parts[seg.type], { isActive, label });\n\t\t})\n\t\t.join(sep);\n}\n\ninterface SegmentOptions {\n\tisActive: boolean;\n\tlabel: string;\n}\nfunction renderSegment(value: string, opts: SegmentOptions): string {\n\tconst isBlank = !value || value.replace(/_/g, '') === '';\n\tif (opts.isActive) return styleText('inverse', isBlank ? opts.label : value.replace(/_/g, ' '));\n\tif (isBlank) return styleText('dim', opts.label);\n\treturn value.replace(/_/g, styleText('dim', ' '));\n}\n\nconst DEFAULT_LABELS: Record<'year' | 'month' | 'day', string> = {\n\tyear: 'yyyy',\n\tmonth: 'mm',\n\tday: 'dd',\n};\n","import { isCancel } from '@clack/core';\n\ntype Prettify<T> = {\n\t[P in keyof T]: T[P];\n} & {};\n\nexport type PromptGroupAwaitedReturn<T> = {\n\t[P in keyof T]: Exclude<Awaited<T[P]>, symbol>;\n};\n\nexport interface PromptGroupOptions<T> {\n\t/**\n\t * Control how the group can be canceled\n\t * if one of the prompts is canceled.\n\t */\n\tonCancel?: (opts: { results: Prettify<Partial<PromptGroupAwaitedReturn<T>>> }) => void;\n}\n\nexport type PromptGroup<T> = {\n\t[P in keyof T]: (opts: {\n\t\tresults: Prettify<Partial<PromptGroupAwaitedReturn<Omit<T, P>>>>;\n\t}) => undefined | Promise<T[P] | undefined>;\n};\n\n/**\n * Define a group of prompts to be displayed\n * and return a results of objects within the group\n */\nexport const group = async <T>(\n\tprompts: PromptGroup<T>,\n\topts?: PromptGroupOptions<T>\n): Promise<Prettify<PromptGroupAwaitedReturn<T>>> => {\n\tconst results = {} as any;\n\tconst promptNames = Object.keys(prompts);\n\n\tfor (const name of promptNames) {\n\t\tconst prompt = prompts[name as keyof T];\n\t\tconst result = await prompt({ results })?.catch((e) => {\n\t\t\tthrow e;\n\t\t});\n\n\t\t// Pass the results to the onCancel function\n\t\t// so the user can decide what to do with the results\n\t\t// TODO: Switch to callback within core to avoid isCancel Fn\n\t\tif (typeof opts?.onCancel === 'function' && isCancel(result)) {\n\t\t\tresults[name] = 'canceled';\n\t\t\topts.onCancel({ results });\n\t\t\tcontinue;\n\t\t}\n\n\t\tresults[name] = result;\n\t}\n\n\treturn results;\n};\n","import { styleText } from 'node:util';\nimport { GroupMultiSelectPrompt, settings } from '@clack/core';\nimport {\n\ttype CommonOptions,\n\tS_BAR,\n\tS_BAR_END,\n\tS_CHECKBOX_ACTIVE,\n\tS_CHECKBOX_INACTIVE,\n\tS_CHECKBOX_SELECTED,\n\tsymbol,\n} from './common.js';\nimport type { Option } from './select.js';\n\nexport interface GroupMultiSelectOptions<Value> extends CommonOptions {\n\tmessage: string;\n\toptions: Record<string, Option<Value>[]>;\n\tinitialValues?: Value[];\n\trequired?: boolean;\n\tcursorAt?: Value;\n\tselectableGroups?: boolean;\n\tgroupSpacing?: number;\n}\nexport const groupMultiselect = <Value>(opts: GroupMultiSelectOptions<Value>) => {\n\tconst { selectableGroups = true, groupSpacing = 0 } = opts;\n\tconst opt = (\n\t\toption: Option<Value> & { group: string | boolean },\n\t\tstate:\n\t\t\t| 'inactive'\n\t\t\t| 'active'\n\t\t\t| 'selected'\n\t\t\t| 'active-selected'\n\t\t\t| 'group-active'\n\t\t\t| 'group-active-selected'\n\t\t\t| 'submitted'\n\t\t\t| 'cancelled',\n\t\toptions: (Option<Value> & { group: string | boolean })[] = []\n\t) => {\n\t\tconst label = option.label ?? String(option.value);\n\t\tconst isItem = typeof option.group === 'string';\n\t\tconst next = isItem && (options[options.indexOf(option) + 1] ?? { group: true });\n\t\tconst isLast = isItem && next && next.group === true;\n\t\tconst prefix = isItem ? (selectableGroups ? `${isLast ? S_BAR_END : S_BAR} ` : ' ') : '';\n\t\tlet spacingPrefix = '';\n\t\tif (groupSpacing > 0 && !isItem) {\n\t\t\tconst spacingPrefixText = `\\n${styleText('cyan', S_BAR)}`;\n\t\t\tspacingPrefix = `${spacingPrefixText.repeat(groupSpacing - 1)}${spacingPrefixText} `;\n\t\t}\n\n\t\tif (state === 'active') {\n\t\t\treturn `${spacingPrefix}${styleText('dim', prefix)}${styleText('cyan', S_CHECKBOX_ACTIVE)} ${label}${\n\t\t\t\toption.hint ? ` ${styleText('dim', `(${option.hint})`)}` : ''\n\t\t\t}`;\n\t\t}\n\t\tif (state === 'group-active') {\n\t\t\treturn `${spacingPrefix}${prefix}${styleText('cyan', S_CHECKBOX_ACTIVE)} ${styleText('dim', label)}`;\n\t\t}\n\t\tif (state === 'group-active-selected') {\n\t\t\treturn `${spacingPrefix}${prefix}${styleText('green', S_CHECKBOX_SELECTED)} ${styleText('dim', label)}`;\n\t\t}\n\t\tif (state === 'selected') {\n\t\t\tconst selectedCheckbox =\n\t\t\t\tisItem || selectableGroups ? styleText('green', S_CHECKBOX_SELECTED) : '';\n\t\t\treturn `${spacingPrefix}${styleText('dim', prefix)}${selectedCheckbox} ${styleText('dim', label)}${\n\t\t\t\toption.hint ? ` ${styleText('dim', `(${option.hint})`)}` : ''\n\t\t\t}`;\n\t\t}\n\t\tif (state === 'cancelled') {\n\t\t\treturn `${styleText(['strikethrough', 'dim'], label)}`;\n\t\t}\n\t\tif (state === 'active-selected') {\n\t\t\treturn `${spacingPrefix}${styleText('dim', prefix)}${styleText('green', S_CHECKBOX_SELECTED)} ${label}${\n\t\t\t\toption.hint ? ` ${styleText('dim', `(${option.hint})`)}` : ''\n\t\t\t}`;\n\t\t}\n\t\tif (state === 'submitted') {\n\t\t\treturn `${styleText('dim', label)}`;\n\t\t}\n\t\tconst unselectedCheckbox =\n\t\t\tisItem || selectableGroups ? styleText('dim', S_CHECKBOX_INACTIVE) : '';\n\t\treturn `${spacingPrefix}${styleText('dim', prefix)}${unselectedCheckbox} ${styleText('dim', label)}`;\n\t};\n\tconst required = opts.required ?? true;\n\n\treturn new GroupMultiSelectPrompt({\n\t\toptions: opts.options,\n\t\tsignal: opts.signal,\n\t\tinput: opts.input,\n\t\toutput: opts.output,\n\t\tinitialValues: opts.initialValues,\n\t\trequired,\n\t\tcursorAt: opts.cursorAt,\n\t\tselectableGroups,\n\t\tvalidate(selected: Value[] | undefined) {\n\t\t\tif (required && (selected === undefined || selected.length === 0))\n\t\t\t\treturn `Please select at least one option.\\n${styleText(\n\t\t\t\t\t'reset',\n\t\t\t\t\tstyleText(\n\t\t\t\t\t\t'dim',\n\t\t\t\t\t\t`Press ${styleText(['gray', 'bgWhite', 'inverse'], ' space ')} to select, ${styleText(\n\t\t\t\t\t\t\t'gray',\n\t\t\t\t\t\t\tstyleText(['bgWhite', 'inverse'], ' enter ')\n\t\t\t\t\t\t)} to submit`\n\t\t\t\t\t)\n\t\t\t\t)}`;\n\t\t},\n\t\trender() {\n\t\t\tconst hasGuide = opts.withGuide ?? settings.withGuide;\n\t\t\tconst title = `${hasGuide ? `${styleText('gray', S_BAR)}\\n` : ''}${symbol(this.state)} ${opts.message}\\n`;\n\t\t\tconst value = this.value ?? [];\n\n\t\t\tswitch (this.state) {\n\t\t\t\tcase 'submit': {\n\t\t\t\t\tconst selectedOptions = this.options\n\t\t\t\t\t\t.filter(({ value: optionValue }) => value.includes(optionValue))\n\t\t\t\t\t\t.map((option) => opt(option, 'submitted'));\n\t\t\t\t\tconst optionsText =\n\t\t\t\t\t\tselectedOptions.length === 0 ? '' : ` ${selectedOptions.join(styleText('dim', ', '))}`;\n\t\t\t\t\treturn `${title}${hasGuide ? styleText('gray', S_BAR) : ''}${optionsText}`;\n\t\t\t\t}\n\t\t\t\tcase 'cancel': {\n\t\t\t\t\tconst label = this.options\n\t\t\t\t\t\t.filter(({ value: optionValue }) => value.includes(optionValue))\n\t\t\t\t\t\t.map((option) => opt(option, 'cancelled'))\n\t\t\t\t\t\t.join(styleText('dim', ', '));\n\t\t\t\t\treturn `${title}${hasGuide ? `${styleText('gray', S_BAR)} ` : ''}${\n\t\t\t\t\t\tlabel.trim() ? `${label}${hasGuide ? `\\n${styleText('gray', S_BAR)}` : ''}` : ''\n\t\t\t\t\t}`;\n\t\t\t\t}\n\t\t\t\tcase 'error': {\n\t\t\t\t\tconst footer = this.error\n\t\t\t\t\t\t.split('\\n')\n\t\t\t\t\t\t.map((ln, i) =>\n\t\t\t\t\t\t\ti === 0\n\t\t\t\t\t\t\t\t? `${hasGuide ? `${styleText('yellow', S_BAR_END)} ` : ''}${styleText('yellow', ln)}`\n\t\t\t\t\t\t\t\t: ` ${ln}`\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.join('\\n');\n\t\t\t\t\treturn `${title}${hasGuide ? `${styleText('yellow', S_BAR)} ` : ''}${this.options\n\t\t\t\t\t\t.map((option, i, options) => {\n\t\t\t\t\t\t\tconst selected =\n\t\t\t\t\t\t\t\tvalue.includes(option.value) ||\n\t\t\t\t\t\t\t\t(option.group === true && this.isGroupSelected(`${option.value}`));\n\t\t\t\t\t\t\tconst active = i === this.cursor;\n\t\t\t\t\t\t\tconst groupActive =\n\t\t\t\t\t\t\t\t!active &&\n\t\t\t\t\t\t\t\ttypeof option.group === 'string' &&\n\t\t\t\t\t\t\t\tthis.options[this.cursor].value === option.group;\n\t\t\t\t\t\t\tif (groupActive) {\n\t\t\t\t\t\t\t\treturn opt(option, selected ? 'group-active-selected' : 'group-active', options);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (active && selected) {\n\t\t\t\t\t\t\t\treturn opt(option, 'active-selected', options);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (selected) {\n\t\t\t\t\t\t\t\treturn opt(option, 'selected', options);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn opt(option, active ? 'active' : 'inactive', options);\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.join(`\\n${hasGuide ? `${styleText('yellow', S_BAR)} ` : ''}`)}\\n${footer}\\n`;\n\t\t\t\t}\n\t\t\t\tdefault: {\n\t\t\t\t\tconst optionsText = this.options\n\t\t\t\t\t\t.map((option, i, options) => {\n\t\t\t\t\t\t\tconst selected =\n\t\t\t\t\t\t\t\tvalue.includes(option.value) ||\n\t\t\t\t\t\t\t\t(option.group === true && this.isGroupSelected(`${option.value}`));\n\t\t\t\t\t\t\tconst active = i === this.cursor;\n\t\t\t\t\t\t\tconst groupActive =\n\t\t\t\t\t\t\t\t!active &&\n\t\t\t\t\t\t\t\ttypeof option.group === 'string' &&\n\t\t\t\t\t\t\t\tthis.options[this.cursor].value === option.group;\n\t\t\t\t\t\t\tlet optionText = '';\n\t\t\t\t\t\t\tif (groupActive) {\n\t\t\t\t\t\t\t\toptionText = opt(\n\t\t\t\t\t\t\t\t\toption,\n\t\t\t\t\t\t\t\t\tselected ? 'group-active-selected' : 'group-active',\n\t\t\t\t\t\t\t\t\toptions\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t} else if (active && selected) {\n\t\t\t\t\t\t\t\toptionText = opt(option, 'active-selected', options);\n\t\t\t\t\t\t\t} else if (selected) {\n\t\t\t\t\t\t\t\toptionText = opt(option, 'selected', options);\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\toptionText = opt(option, active ? 'active' : 'inactive', options);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tconst prefix = i !== 0 && !optionText.startsWith('\\n') ? ' ' : '';\n\t\t\t\t\t\t\treturn `${prefix}${optionText}`;\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.join(`\\n${hasGuide ? styleText('cyan', S_BAR) : ''}`);\n\t\t\t\t\tconst optionsPrefix = optionsText.startsWith('\\n') ? '' : ' ';\n\t\t\t\t\treturn `${title}${hasGuide ? styleText('cyan', S_BAR) : ''}${optionsPrefix}${optionsText}\\n${\n\t\t\t\t\t\thasGuide ? styleText('cyan', S_BAR_END) : ''\n\t\t\t\t\t}\\n`;\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t}).prompt() as Promise<Value[] | symbol>;\n};\n","import { styleText } from 'node:util';\nimport { settings } from '@clack/core';\nimport {\n\ttype CommonOptions,\n\tS_BAR,\n\tS_ERROR,\n\tS_INFO,\n\tS_STEP_SUBMIT,\n\tS_SUCCESS,\n\tS_WARN,\n} from './common.js';\n\nexport interface LogMessageOptions extends CommonOptions {\n\tsymbol?: string;\n\tspacing?: number;\n\tsecondarySymbol?: string;\n}\n\nexport const log = {\n\tmessage: (\n\t\tmessage: string | string[] = [],\n\t\t{\n\t\t\tsymbol = styleText('gray', S_BAR),\n\t\t\tsecondarySymbol = styleText('gray', S_BAR),\n\t\t\toutput = process.stdout,\n\t\t\tspacing = 1,\n\t\t\twithGuide,\n\t\t}: LogMessageOptions = {}\n\t) => {\n\t\tconst parts: string[] = [];\n\t\tconst hasGuide = withGuide ?? settings.withGuide;\n\t\tconst spacingString = !hasGuide ? '' : secondarySymbol;\n\t\tconst prefix = !hasGuide ? '' : `${symbol} `;\n\t\tconst secondaryPrefix = !hasGuide ? '' : `${secondarySymbol} `;\n\n\t\tfor (let i = 0; i < spacing; i++) {\n\t\t\tparts.push(spacingString);\n\t\t}\n\n\t\tconst messageParts = Array.isArray(message) ? message : message.split('\\n');\n\t\tif (messageParts.length > 0) {\n\t\t\tconst [firstLine, ...lines] = messageParts;\n\t\t\tif (firstLine.length > 0) {\n\t\t\t\tparts.push(`${prefix}${firstLine}`);\n\t\t\t} else {\n\t\t\t\tparts.push(hasGuide ? symbol : '');\n\t\t\t}\n\t\t\tfor (const ln of lines) {\n\t\t\t\tif (ln.length > 0) {\n\t\t\t\t\tparts.push(`${secondaryPrefix}${ln}`);\n\t\t\t\t} else {\n\t\t\t\t\tparts.push(hasGuide ? secondarySymbol : '');\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\toutput.write(`${parts.join('\\n')}\\n`);\n\t},\n\tinfo: (message: string, opts?: LogMessageOptions) => {\n\t\tlog.message(message, { ...opts, symbol: styleText('blue', S_INFO) });\n\t},\n\tsuccess: (message: string, opts?: LogMessageOptions) => {\n\t\tlog.message(message, { ...opts, symbol: styleText('green', S_SUCCESS) });\n\t},\n\tstep: (message: string, opts?: LogMessageOptions) => {\n\t\tlog.message(message, { ...opts, symbol: styleText('green', S_STEP_SUBMIT) });\n\t},\n\twarn: (message: string, opts?: LogMessageOptions) => {\n\t\tlog.message(message, { ...opts, symbol: styleText('yellow', S_WARN) });\n\t},\n\t/** alias for `log.warn()`. */\n\twarning: (message: string, opts?: LogMessageOptions) => {\n\t\tlog.warn(message, opts);\n\t},\n\terror: (message: string, opts?: LogMessageOptions) => {\n\t\tlog.message(message, { ...opts, symbol: styleText('red', S_ERROR) });\n\t},\n};\n","import type { Writable } from 'node:stream';\nimport { styleText } from 'node:util';\nimport { settings } from '@clack/core';\nimport { type CommonOptions, S_BAR, S_BAR_END, S_BAR_START } from './common.js';\n\nexport const cancel = (message = '', opts?: CommonOptions) => {\n\tconst output: Writable = opts?.output ?? process.stdout;\n\tconst hasGuide = opts?.withGuide ?? settings.withGuide;\n\tconst prefix = hasGuide ? `${styleText('gray', S_BAR_END)} ` : '';\n\toutput.write(`${prefix}${styleText('red', message)}\\n\\n`);\n};\n\nexport const intro = (title = '', opts?: CommonOptions) => {\n\tconst output: Writable = opts?.output ?? process.stdout;\n\tconst hasGuide = opts?.withGuide ?? settings.withGuide;\n\tconst prefix = hasGuide ? `${styleText('gray', S_BAR_START)} ` : '';\n\toutput.write(`${prefix}${title}\\n`);\n};\n\nexport const outro = (message = '', opts?: CommonOptions) => {\n\tconst output: Writable = opts?.output ?? process.stdout;\n\tconst hasGuide = opts?.withGuide ?? settings.withGuide;\n\tconst prefix = hasGuide ? `${styleText('gray', S_BAR)}\\n${styleText('gray', S_BAR_END)} ` : '';\n\toutput.write(`${prefix}${message}\\n\\n`);\n};\n","import { styleText } from 'node:util';\nimport { MultiLinePrompt, settings, wrapTextWithPrefix } from '@clack/core';\nimport { S_BAR, S_BAR_END, symbol } from './common.js';\nimport type { TextOptions } from './text.js';\n\nexport interface MultiLineOptions extends TextOptions {\n\tshowSubmit?: boolean;\n}\n\nexport const multiline = (opts: MultiLineOptions) => {\n\treturn new MultiLinePrompt({\n\t\tvalidate: opts.validate,\n\t\tplaceholder: opts.placeholder,\n\t\tdefaultValue: opts.defaultValue,\n\t\tinitialValue: opts.initialValue,\n\t\tshowSubmit: opts.showSubmit,\n\t\toutput: opts.output,\n\t\tsignal: opts.signal,\n\t\tinput: opts.input,\n\t\trender() {\n\t\t\tconst hasGuide = opts?.withGuide ?? settings.withGuide;\n\t\t\tconst titlePrefix = `${hasGuide ? `${styleText('gray', S_BAR)}\\n` : ''}${symbol(this.state)} `;\n\t\t\tconst title = `${titlePrefix}${opts.message}\\n`;\n\t\t\tconst placeholder = opts.placeholder\n\t\t\t\t? styleText('inverse', opts.placeholder[0]) + styleText('dim', opts.placeholder.slice(1))\n\t\t\t\t: styleText(['inverse', 'hidden'], '_');\n\t\t\tconst userInput = !this.userInput ? placeholder : this.userInputWithCursor;\n\t\t\tconst value = this.value ?? '';\n\t\t\tconst submitButton = opts.showSubmit\n\t\t\t\t? `\\n ${styleText(this.focused === 'submit' ? 'cyan' : 'dim', '[ submit ]')}`\n\t\t\t\t: '';\n\t\t\tswitch (this.state) {\n\t\t\t\tcase 'error': {\n\t\t\t\t\tconst errorPrefix = `${styleText('yellow', S_BAR)} `;\n\t\t\t\t\tconst lines = hasGuide\n\t\t\t\t\t\t? wrapTextWithPrefix(opts.output, userInput, errorPrefix, undefined)\n\t\t\t\t\t\t: userInput;\n\t\t\t\t\tconst errorPrefixEnd = styleText('yellow', S_BAR_END);\n\t\t\t\t\treturn `${title}${lines}\\n${errorPrefixEnd} ${styleText('yellow', this.error)}${submitButton}\\n`;\n\t\t\t\t}\n\t\t\t\tcase 'submit': {\n\t\t\t\t\tconst submitPrefix = `${styleText('gray', S_BAR)} `;\n\t\t\t\t\tconst lines = hasGuide\n\t\t\t\t\t\t? wrapTextWithPrefix(opts.output, value, submitPrefix, undefined, (str) =>\n\t\t\t\t\t\t\t\tstyleText('dim', str)\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t: value\n\t\t\t\t\t\t\t? styleText('dim', value)\n\t\t\t\t\t\t\t: '';\n\t\t\t\t\treturn `${title}${lines}`;\n\t\t\t\t}\n\t\t\t\tcase 'cancel': {\n\t\t\t\t\tconst cancelPrefix = `${styleText('gray', S_BAR)} `;\n\t\t\t\t\tconst lines = hasGuide\n\t\t\t\t\t\t? wrapTextWithPrefix(opts.output, value, cancelPrefix, undefined, (str) =>\n\t\t\t\t\t\t\t\tstyleText(['strikethrough', 'dim'], str)\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t: value\n\t\t\t\t\t\t\t? styleText(['strikethrough', 'dim'], value)\n\t\t\t\t\t\t\t: '';\n\t\t\t\t\treturn `${title}${lines}`;\n\t\t\t\t}\n\t\t\t\tdefault: {\n\t\t\t\t\tconst defaultPrefix = hasGuide ? `${styleText('cyan', S_BAR)} ` : '';\n\t\t\t\t\tconst defaultPrefixEnd = hasGuide ? styleText('cyan', S_BAR_END) : '';\n\t\t\t\t\tconst lines = hasGuide\n\t\t\t\t\t\t? wrapTextWithPrefix(opts.output, userInput, defaultPrefix)\n\t\t\t\t\t\t: userInput;\n\t\t\t\t\treturn `${title}${lines}\\n${defaultPrefixEnd}${submitButton}\\n`;\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t}).prompt() as Promise<string | symbol>;\n};\n","import { styleText } from 'node:util';\nimport { MultiSelectPrompt, settings, wrapTextWithPrefix } from '@clack/core';\nimport {\n\ttype CommonOptions,\n\tS_BAR,\n\tS_BAR_END,\n\tS_CHECKBOX_ACTIVE,\n\tS_CHECKBOX_INACTIVE,\n\tS_CHECKBOX_SELECTED,\n\tsymbol,\n\tsymbolBar,\n} from './common.js';\nimport { limitOptions } from './limit-options.js';\nimport type { Option } from './select.js';\n\nexport interface MultiSelectOptions<Value> extends CommonOptions {\n\tmessage: string;\n\toptions: Option<Value>[];\n\tinitialValues?: Value[];\n\tmaxItems?: number;\n\trequired?: boolean;\n\tcursorAt?: Value;\n}\nconst computeLabel = (label: string, format: (text: string) => string) => {\n\treturn label\n\t\t.split('\\n')\n\t\t.map((line) => format(line))\n\t\t.join('\\n');\n};\n\nexport const multiselect = <Value>(opts: MultiSelectOptions<Value>) => {\n\tconst opt = (\n\t\toption: Option<Value>,\n\t\tstate:\n\t\t\t| 'inactive'\n\t\t\t| 'active'\n\t\t\t| 'selected'\n\t\t\t| 'active-selected'\n\t\t\t| 'submitted'\n\t\t\t| 'cancelled'\n\t\t\t| 'disabled'\n\t) => {\n\t\tconst label = option.label ?? String(option.value);\n\t\tif (state === 'disabled') {\n\t\t\treturn `${styleText('gray', S_CHECKBOX_INACTIVE)} ${computeLabel(label, (str) => styleText(['strikethrough', 'gray'], str))}${\n\t\t\t\toption.hint ? ` ${styleText('dim', `(${option.hint ?? 'disabled'})`)}` : ''\n\t\t\t}`;\n\t\t}\n\t\tif (state === 'active') {\n\t\t\treturn `${styleText('cyan', S_CHECKBOX_ACTIVE)} ${label}${\n\t\t\t\toption.hint ? ` ${styleText('dim', `(${option.hint})`)}` : ''\n\t\t\t}`;\n\t\t}\n\t\tif (state === 'selected') {\n\t\t\treturn `${styleText('green', S_CHECKBOX_SELECTED)} ${computeLabel(label, (text) => styleText('dim', text))}${\n\t\t\t\toption.hint ? ` ${styleText('dim', `(${option.hint})`)}` : ''\n\t\t\t}`;\n\t\t}\n\t\tif (state === 'cancelled') {\n\t\t\treturn `${computeLabel(label, (text) => styleText(['strikethrough', 'dim'], text))}`;\n\t\t}\n\t\tif (state === 'active-selected') {\n\t\t\treturn `${styleText('green', S_CHECKBOX_SELECTED)} ${label}${\n\t\t\t\toption.hint ? ` ${styleText('dim', `(${option.hint})`)}` : ''\n\t\t\t}`;\n\t\t}\n\t\tif (state === 'submitted') {\n\t\t\treturn `${computeLabel(label, (text) => styleText('dim', text))}`;\n\t\t}\n\t\treturn `${styleText('dim', S_CHECKBOX_INACTIVE)} ${computeLabel(label, (text) => styleText('dim', text))}`;\n\t};\n\tconst required = opts.required ?? true;\n\n\treturn new MultiSelectPrompt({\n\t\toptions: opts.options,\n\t\tsignal: opts.signal,\n\t\tinput: opts.input,\n\t\toutput: opts.output,\n\t\tinitialValues: opts.initialValues,\n\t\trequired,\n\t\tcursorAt: opts.cursorAt,\n\t\tvalidate(selected: Value[] | undefined) {\n\t\t\tif (required && (selected === undefined || selected.length === 0))\n\t\t\t\treturn `Please select at least one option.\\n${styleText(\n\t\t\t\t\t'reset',\n\t\t\t\t\tstyleText(\n\t\t\t\t\t\t'dim',\n\t\t\t\t\t\t`Press ${styleText(['gray', 'bgWhite', 'inverse'], ' space ')} to select, ${styleText(\n\t\t\t\t\t\t\t'gray',\n\t\t\t\t\t\t\tstyleText('bgWhite', styleText('inverse', ' enter '))\n\t\t\t\t\t\t)} to submit`\n\t\t\t\t\t)\n\t\t\t\t)}`;\n\t\t},\n\t\trender() {\n\t\t\tconst hasGuide = opts.withGuide ?? settings.withGuide;\n\t\t\tconst wrappedMessage = wrapTextWithPrefix(\n\t\t\t\topts.output,\n\t\t\t\topts.message,\n\t\t\t\thasGuide ? `${symbolBar(this.state)} ` : '',\n\t\t\t\t`${symbol(this.state)} `\n\t\t\t);\n\t\t\tconst title = `${hasGuide ? `${styleText('gray', S_BAR)}\\n` : ''}${wrappedMessage}\\n`;\n\t\t\tconst value = this.value ?? [];\n\n\t\t\tconst styleOption = (option: Option<Value>, active: boolean) => {\n\t\t\t\tif (option.disabled) {\n\t\t\t\t\treturn opt(option, 'disabled');\n\t\t\t\t}\n\t\t\t\tconst selected = value.includes(option.value);\n\t\t\t\tif (active && selected) {\n\t\t\t\t\treturn opt(option, 'active-selected');\n\t\t\t\t}\n\t\t\t\tif (selected) {\n\t\t\t\t\treturn opt(option, 'selected');\n\t\t\t\t}\n\t\t\t\treturn opt(option, active ? 'active' : 'inactive');\n\t\t\t};\n\n\t\t\tswitch (this.state) {\n\t\t\t\tcase 'submit': {\n\t\t\t\t\tconst submitText =\n\t\t\t\t\t\tthis.options\n\t\t\t\t\t\t\t.filter(({ value: optionValue }) => value.includes(optionValue))\n\t\t\t\t\t\t\t.map((option) => opt(option, 'submitted'))\n\t\t\t\t\t\t\t.join(styleText('dim', ', ')) || styleText('dim', 'none');\n\t\t\t\t\tconst wrappedSubmitText = wrapTextWithPrefix(\n\t\t\t\t\t\topts.output,\n\t\t\t\t\t\tsubmitText,\n\t\t\t\t\t\thasGuide ? `${styleText('gray', S_BAR)} ` : ''\n\t\t\t\t\t);\n\t\t\t\t\treturn `${title}${wrappedSubmitText}`;\n\t\t\t\t}\n\t\t\t\tcase 'cancel': {\n\t\t\t\t\tconst label = this.options\n\t\t\t\t\t\t.filter(({ value: optionValue }) => value.includes(optionValue))\n\t\t\t\t\t\t.map((option) => opt(option, 'cancelled'))\n\t\t\t\t\t\t.join(styleText('dim', ', '));\n\t\t\t\t\tif (label.trim() === '') {\n\t\t\t\t\t\treturn `${title}${styleText('gray', S_BAR)}`;\n\t\t\t\t\t}\n\t\t\t\t\tconst wrappedLabel = wrapTextWithPrefix(\n\t\t\t\t\t\topts.output,\n\t\t\t\t\t\tlabel,\n\t\t\t\t\t\thasGuide ? `${styleText('gray', S_BAR)} ` : ''\n\t\t\t\t\t);\n\t\t\t\t\treturn `${title}${wrappedLabel}${hasGuide ? `\\n${styleText('gray', S_BAR)}` : ''}`;\n\t\t\t\t}\n\t\t\t\tcase 'error': {\n\t\t\t\t\tconst prefix = hasGuide ? `${styleText('yellow', S_BAR)} ` : '';\n\t\t\t\t\tconst footer = this.error\n\t\t\t\t\t\t.split('\\n')\n\t\t\t\t\t\t.map((ln, i) =>\n\t\t\t\t\t\t\ti === 0\n\t\t\t\t\t\t\t\t? `${hasGuide ? `${styleText('yellow', S_BAR_END)} ` : ''}${styleText('yellow', ln)}`\n\t\t\t\t\t\t\t\t: ` ${ln}`\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.join('\\n');\n\t\t\t\t\t// Calculate rowPadding: title lines + footer lines (error message + trailing newline)\n\t\t\t\t\tconst titleLineCount = title.split('\\n').length;\n\t\t\t\t\tconst footerLineCount = footer.split('\\n').length + 1; // footer + trailing newline\n\t\t\t\t\treturn `${title}${prefix}${limitOptions({\n\t\t\t\t\t\toutput: opts.output,\n\t\t\t\t\t\toptions: this.options,\n\t\t\t\t\t\tcursor: this.cursor,\n\t\t\t\t\t\tmaxItems: opts.maxItems,\n\t\t\t\t\t\tcolumnPadding: prefix.length,\n\t\t\t\t\t\trowPadding: titleLineCount + footerLineCount,\n\t\t\t\t\t\tstyle: styleOption,\n\t\t\t\t\t}).join(`\\n${prefix}`)}\\n${footer}\\n`;\n\t\t\t\t}\n\t\t\t\tdefault: {\n\t\t\t\t\tconst prefix = hasGuide ? `${styleText('cyan', S_BAR)} ` : '';\n\t\t\t\t\t// Calculate rowPadding: title lines + footer lines (S_BAR_END + trailing newline)\n\t\t\t\t\tconst titleLineCount = title.split('\\n').length;\n\t\t\t\t\tconst footerLineCount = hasGuide ? 2 : 1; // S_BAR_END + trailing newline\n\t\t\t\t\treturn `${title}${prefix}${limitOptions({\n\t\t\t\t\t\toutput: opts.output,\n\t\t\t\t\t\toptions: this.options,\n\t\t\t\t\t\tcursor: this.cursor,\n\t\t\t\t\t\tmaxItems: opts.maxItems,\n\t\t\t\t\t\tcolumnPadding: prefix.length,\n\t\t\t\t\t\trowPadding: titleLineCount + footerLineCount,\n\t\t\t\t\t\tstyle: styleOption,\n\t\t\t\t\t}).join(`\\n${prefix}`)}\\n${hasGuide ? styleText('cyan', S_BAR_END) : ''}\\n`;\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t}).prompt() as Promise<Value[] | symbol>;\n};\n","import process from 'node:process';\nimport type { Writable } from 'node:stream';\nimport { styleText } from 'node:util';\nimport { getColumns, settings } from '@clack/core';\nimport stringWidth from 'fast-string-width';\nimport { type Options as WrapAnsiOptions, wrapAnsi } from 'fast-wrap-ansi';\nimport {\n\ttype CommonOptions,\n\tS_BAR,\n\tS_BAR_H,\n\tS_CONNECT_LEFT,\n\tS_CORNER_BOTTOM_LEFT,\n\tS_CORNER_BOTTOM_RIGHT,\n\tS_CORNER_TOP_RIGHT,\n\tS_STEP_SUBMIT,\n} from './common.js';\n\ntype FormatFn = (line: string) => string;\nexport interface NoteOptions extends CommonOptions {\n\tformat?: FormatFn;\n}\n\nconst defaultNoteFormatter = (line: string): string => styleText('dim', line);\n\nconst wrapWithFormat = (message: string, width: number, format: FormatFn): string => {\n\tconst opts: WrapAnsiOptions = {\n\t\thard: true,\n\t\ttrim: false,\n\t};\n\tconst wrapMsg = wrapAnsi(message, width, opts).split('\\n');\n\tconst maxWidthNormal = wrapMsg.reduce((sum, ln) => Math.max(stringWidth(ln), sum), 0);\n\tconst maxWidthFormat = wrapMsg.map(format).reduce((sum, ln) => Math.max(stringWidth(ln), sum), 0);\n\tconst wrapWidth = width - (maxWidthFormat - maxWidthNormal);\n\treturn wrapAnsi(message, wrapWidth, opts);\n};\n\nexport const note = (message = '', title = '', opts?: NoteOptions) => {\n\tconst output: Writable = opts?.output ?? process.stdout;\n\tconst hasGuide = opts?.withGuide ?? settings.withGuide;\n\tconst format = opts?.format ?? defaultNoteFormatter;\n\tconst wrapMsg = wrapWithFormat(message, getColumns(output) - 6, format);\n\tconst lines = ['', ...wrapMsg.split('\\n').map(format), ''];\n\tconst titleLen = stringWidth(title);\n\tconst len =\n\t\tMath.max(\n\t\t\tlines.reduce((sum, ln) => {\n\t\t\t\tconst width = stringWidth(ln);\n\t\t\t\treturn width > sum ? width : sum;\n\t\t\t}, 0),\n\t\t\ttitleLen\n\t\t) + 2;\n\tconst msg = lines\n\t\t.map(\n\t\t\t(ln) =>\n\t\t\t\t`${styleText('gray', S_BAR)} ${ln}${' '.repeat(len - stringWidth(ln))}${styleText('gray', S_BAR)}`\n\t\t)\n\t\t.join('\\n');\n\tconst leadingBorder = hasGuide ? `${styleText('gray', S_BAR)}\\n` : '';\n\tconst bottomLeft = hasGuide ? S_CONNECT_LEFT : S_CORNER_BOTTOM_LEFT;\n\toutput.write(\n\t\t`${leadingBorder}${styleText('green', S_STEP_SUBMIT)} ${styleText('reset', title)} ${styleText(\n\t\t\t'gray',\n\t\t\tS_BAR_H.repeat(Math.max(len - titleLen - 1, 1)) + S_CORNER_TOP_RIGHT\n\t\t)}\\n${msg}\\n${styleText('gray', bottomLeft + S_BAR_H.repeat(len + 2) + S_CORNER_BOTTOM_RIGHT)}\\n`\n\t);\n};\n","import { styleText } from 'node:util';\nimport { PasswordPrompt, settings } from '@clack/core';\nimport { type CommonOptions, S_BAR, S_BAR_END, S_PASSWORD_MASK, symbol } from './common.js';\n\nexport interface PasswordOptions extends CommonOptions {\n\tmessage: string;\n\tmask?: string;\n\tvalidate?: (value: string | undefined) => string | Error | undefined;\n\tclearOnError?: boolean;\n}\nexport const password = (opts: PasswordOptions) => {\n\treturn new PasswordPrompt({\n\t\tvalidate: opts.validate,\n\t\tmask: opts.mask ?? S_PASSWORD_MASK,\n\t\tsignal: opts.signal,\n\t\tinput: opts.input,\n\t\toutput: opts.output,\n\t\trender() {\n\t\t\tconst hasGuide = opts.withGuide ?? settings.withGuide;\n\t\t\tconst title = `${hasGuide ? `${styleText('gray', S_BAR)}\\n` : ''}${symbol(this.state)} ${opts.message}\\n`;\n\t\t\tconst userInput = this.userInputWithCursor;\n\t\t\tconst masked = this.masked;\n\n\t\t\tswitch (this.state) {\n\t\t\t\tcase 'error': {\n\t\t\t\t\tconst errorPrefix = hasGuide ? `${styleText('yellow', S_BAR)} ` : '';\n\t\t\t\t\tconst errorPrefixEnd = hasGuide ? `${styleText('yellow', S_BAR_END)} ` : '';\n\t\t\t\t\tconst maskedText = masked ?? '';\n\t\t\t\t\tif (opts.clearOnError) {\n\t\t\t\t\t\tthis.clear();\n\t\t\t\t\t}\n\t\t\t\t\treturn `${title.trim()}\\n${errorPrefix}${maskedText}\\n${errorPrefixEnd}${styleText('yellow', this.error)}\\n`;\n\t\t\t\t}\n\t\t\t\tcase 'submit': {\n\t\t\t\t\tconst submitPrefix = hasGuide ? `${styleText('gray', S_BAR)} ` : '';\n\t\t\t\t\tconst maskedText = masked ? styleText('dim', masked) : '';\n\t\t\t\t\treturn `${title}${submitPrefix}${maskedText}`;\n\t\t\t\t}\n\t\t\t\tcase 'cancel': {\n\t\t\t\t\tconst cancelPrefix = hasGuide ? `${styleText('gray', S_BAR)} ` : '';\n\t\t\t\t\tconst maskedText = masked ? styleText(['strikethrough', 'dim'], masked) : '';\n\t\t\t\t\treturn `${title}${cancelPrefix}${maskedText}${\n\t\t\t\t\t\tmasked && hasGuide ? `\\n${styleText('gray', S_BAR)}` : ''\n\t\t\t\t\t}`;\n\t\t\t\t}\n\t\t\t\tdefault: {\n\t\t\t\t\tconst defaultPrefix = hasGuide ? `${styleText('cyan', S_BAR)} ` : '';\n\t\t\t\t\tconst defaultPrefixEnd = hasGuide ? styleText('cyan', S_BAR_END) : '';\n\t\t\t\t\treturn `${title}${defaultPrefix}${userInput}\\n${defaultPrefixEnd}\\n`;\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t}).prompt() as Promise<string | symbol>;\n};\n","import { existsSync, lstatSync, readdirSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { autocomplete } from './autocomplete.js';\nimport type { CommonOptions } from './common.js';\n\nexport interface PathOptions extends CommonOptions {\n\troot?: string;\n\tdirectory?: boolean;\n\tinitialValue?: string;\n\tmessage: string;\n\tvalidate?: (value: string | undefined) => string | Error | undefined;\n}\n\nexport const path = (opts: PathOptions) => {\n\tconst validate = opts.validate;\n\n\treturn autocomplete({\n\t\t...opts,\n\t\tinitialUserInput: opts.initialValue ?? opts.root ?? process.cwd(),\n\t\tmaxItems: 5,\n\t\tvalidate(value) {\n\t\t\tif (Array.isArray(value)) {\n\t\t\t\t// Shouldn't ever happen since we don't enable `multiple: true`\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t\tif (!value) {\n\t\t\t\treturn 'Please select a path';\n\t\t\t}\n\t\t\tif (validate) {\n\t\t\t\treturn validate(value);\n\t\t\t}\n\t\t\treturn undefined;\n\t\t},\n\t\toptions() {\n\t\t\tconst userInput = this.userInput;\n\t\t\tif (userInput === '') {\n\t\t\t\treturn [];\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tlet searchPath: string;\n\n\t\t\t\tif (!existsSync(userInput)) {\n\t\t\t\t\tsearchPath = dirname(userInput);\n\t\t\t\t} else {\n\t\t\t\t\tconst stat = lstatSync(userInput);\n\t\t\t\t\tif (stat.isDirectory() && (!opts.directory || userInput.endsWith('/'))) {\n\t\t\t\t\t\tsearchPath = userInput;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tsearchPath = dirname(userInput);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Strip trailing slash so startsWith matches the directory itself among its siblings\n\t\t\t\tconst prefix =\n\t\t\t\t\tuserInput.length > 1 && userInput.endsWith('/') ? userInput.slice(0, -1) : userInput;\n\n\t\t\t\tconst items = readdirSync(searchPath)\n\t\t\t\t\t.map((item) => {\n\t\t\t\t\t\tconst path = join(searchPath, item);\n\t\t\t\t\t\tconst stats = lstatSync(path);\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tname: item,\n\t\t\t\t\t\t\tpath,\n\t\t\t\t\t\t\tisDirectory: stats.isDirectory(),\n\t\t\t\t\t\t};\n\t\t\t\t\t})\n\t\t\t\t\t.filter(\n\t\t\t\t\t\t({ path, isDirectory }) => path.startsWith(prefix) && (isDirectory || !opts.directory)\n\t\t\t\t\t);\n\n\t\t\t\treturn items.map((item) => ({\n\t\t\t\t\tvalue: item.path,\n\t\t\t\t}));\n\t\t\t} catch (_e) {\n\t\t\t\treturn [];\n\t\t\t}\n\t\t},\n\t});\n};\n","import { styleText } from 'node:util';\nimport { block, getColumns, settings } from '@clack/core';\nimport { wrapAnsi } from 'fast-wrap-ansi';\nimport { cursor, erase } from 'sisteransi';\nimport {\n\ttype CommonOptions,\n\tisCI as isCIFn,\n\tS_BAR,\n\tS_STEP_CANCEL,\n\tS_STEP_ERROR,\n\tS_STEP_SUBMIT,\n\tunicode,\n} from './common.js';\n\nexport interface SpinnerOptions extends CommonOptions {\n\tindicator?: 'dots' | 'timer';\n\tonCancel?: () => void;\n\tcancelMessage?: string;\n\terrorMessage?: string;\n\tframes?: string[];\n\tdelay?: number;\n\tstyleFrame?: (frame: string) => string;\n}\n\nexport interface SpinnerResult {\n\tstart(msg?: string): void;\n\tstop(msg?: string): void;\n\tcancel(msg?: string): void;\n\terror(msg?: string): void;\n\tmessage(msg?: string): void;\n\tclear(): void;\n\treadonly isCancelled: boolean;\n}\n\nconst defaultStyleFn: SpinnerOptions['styleFrame'] = (frame) => styleText('magenta', frame);\n\nexport const spinner = ({\n\tindicator = 'dots',\n\tonCancel,\n\toutput = process.stdout,\n\tcancelMessage,\n\terrorMessage,\n\tframes = unicode ? ['◒', '◐', '◓', '◑'] : ['•', 'o', 'O', '0'],\n\tdelay = unicode ? 80 : 120,\n\tsignal,\n\t...opts\n}: SpinnerOptions = {}): SpinnerResult => {\n\tconst isCI = isCIFn();\n\n\tlet unblock: () => void;\n\tlet loop: NodeJS.Timeout;\n\tlet isSpinnerActive = false;\n\tlet isCancelled = false;\n\tlet _message = '';\n\tlet _prevMessage: string | undefined;\n\tlet _origin: number = performance.now();\n\tconst columns = getColumns(output);\n\tconst styleFn = opts?.styleFrame ?? defaultStyleFn;\n\n\tconst handleExit = (code: number) => {\n\t\tconst msg =\n\t\t\tcode > 1\n\t\t\t\t? (errorMessage ?? settings.messages.error)\n\t\t\t\t: (cancelMessage ?? settings.messages.cancel);\n\t\tisCancelled = code === 1;\n\t\tif (isSpinnerActive) {\n\t\t\t_stop(msg, code);\n\t\t\tif (isCancelled && typeof onCancel === 'function') {\n\t\t\t\tonCancel();\n\t\t\t}\n\t\t}\n\t};\n\n\tconst errorEventHandler = () => handleExit(2);\n\tconst signalEventHandler = () => handleExit(1);\n\n\tconst registerHooks = () => {\n\t\t// Reference: https://nodejs.org/api/process.html#event-uncaughtexception\n\t\tprocess.on('uncaughtExceptionMonitor', errorEventHandler);\n\t\t// Reference: https://nodejs.org/api/process.html#event-unhandledrejection\n\t\tprocess.on('unhandledRejection', errorEventHandler);\n\t\t// Reference Signal Events: https://nodejs.org/api/process.html#signal-events\n\t\tprocess.on('SIGINT', signalEventHandler);\n\t\tprocess.on('SIGTERM', signalEventHandler);\n\t\tprocess.on('exit', handleExit);\n\n\t\tif (signal) {\n\t\t\tsignal.addEventListener('abort', signalEventHandler);\n\t\t}\n\t};\n\n\tconst clearHooks = () => {\n\t\tprocess.removeListener('uncaughtExceptionMonitor', errorEventHandler);\n\t\tprocess.removeListener('unhandledRejection', errorEventHandler);\n\t\tprocess.removeListener('SIGINT', signalEventHandler);\n\t\tprocess.removeListener('SIGTERM', signalEventHandler);\n\t\tprocess.removeListener('exit', handleExit);\n\n\t\tif (signal) {\n\t\t\tsignal.removeEventListener('abort', signalEventHandler);\n\t\t}\n\t};\n\n\tconst clearPrevMessage = () => {\n\t\tif (_prevMessage === undefined) return;\n\t\tif (isCI) output.write('\\n');\n\t\tconst wrapped = wrapAnsi(_prevMessage, columns, {\n\t\t\thard: true,\n\t\t\ttrim: false,\n\t\t});\n\t\tconst prevLines = wrapped.split('\\n');\n\t\tif (prevLines.length > 1) {\n\t\t\toutput.write(cursor.up(prevLines.length - 1));\n\t\t}\n\t\toutput.write(cursor.to(0));\n\t\toutput.write(erase.down());\n\t};\n\n\tconst removeTrailingDots = (msg: string): string => {\n\t\treturn msg.replace(/\\.+$/, '');\n\t};\n\n\tconst formatTimer = (origin: number): string => {\n\t\tconst duration = (performance.now() - origin) / 1000;\n\t\tconst min = Math.floor(duration / 60);\n\t\tconst secs = Math.floor(duration % 60);\n\t\treturn min > 0 ? `[${min}m ${secs}s]` : `[${secs}s]`;\n\t};\n\n\tconst hasGuide = opts.withGuide ?? settings.withGuide;\n\n\tconst start = (msg = ''): void => {\n\t\tisSpinnerActive = true;\n\t\tunblock = block({ output });\n\t\t_message = removeTrailingDots(msg);\n\t\t_origin = performance.now();\n\t\tif (hasGuide) {\n\t\t\toutput.write(`${styleText('gray', S_BAR)}\\n`);\n\t\t}\n\t\tlet frameIndex = 0;\n\t\tlet indicatorTimer = 0;\n\t\tregisterHooks();\n\t\tloop = setInterval(() => {\n\t\t\tif (isCI && _message === _prevMessage) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tclearPrevMessage();\n\t\t\t_prevMessage = _message;\n\t\t\tconst frame = styleFn(frames[frameIndex]);\n\t\t\tlet outputMessage: string;\n\n\t\t\tif (isCI) {\n\t\t\t\toutputMessage = `${frame} ${_message}...`;\n\t\t\t} else if (indicator === 'timer') {\n\t\t\t\toutputMessage = `${frame} ${_message} ${formatTimer(_origin)}`;\n\t\t\t} else {\n\t\t\t\tconst loadingDots = '.'.repeat(Math.floor(indicatorTimer)).slice(0, 3);\n\t\t\t\toutputMessage = `${frame} ${_message}${loadingDots}`;\n\t\t\t}\n\n\t\t\tconst wrapped = wrapAnsi(outputMessage, columns, {\n\t\t\t\thard: true,\n\t\t\t\ttrim: false,\n\t\t\t});\n\t\t\toutput.write(wrapped);\n\n\t\t\tframeIndex = frameIndex + 1 < frames.length ? frameIndex + 1 : 0;\n\t\t\t// indicator increase by 1 every 8 frames\n\t\t\tindicatorTimer = indicatorTimer < 4 ? indicatorTimer + 0.125 : 0;\n\t\t}, delay);\n\t};\n\n\tconst _stop = (msg = '', code = 0, silent: boolean = false): void => {\n\t\tif (!isSpinnerActive) return;\n\t\tisSpinnerActive = false;\n\t\tclearInterval(loop);\n\t\tclearPrevMessage();\n\t\tconst step =\n\t\t\tcode === 0\n\t\t\t\t? styleText('green', S_STEP_SUBMIT)\n\t\t\t\t: code === 1\n\t\t\t\t\t? styleText('red', S_STEP_CANCEL)\n\t\t\t\t\t: styleText('red', S_STEP_ERROR);\n\t\t_message = msg ?? _message;\n\t\tif (!silent) {\n\t\t\tif (indicator === 'timer') {\n\t\t\t\toutput.write(`${step} ${_message} ${formatTimer(_origin)}\\n`);\n\t\t\t} else {\n\t\t\t\toutput.write(`${step} ${_message}\\n`);\n\t\t\t}\n\t\t}\n\t\tclearHooks();\n\t\tunblock();\n\t};\n\n\tconst stop = (msg = ''): void => _stop(msg, 0);\n\tconst cancel = (msg = ''): void => _stop(msg, 1);\n\tconst error = (msg = ''): void => _stop(msg, 2);\n\t// TODO (43081j): this will leave the initial S_BAR since we purposely\n\t// don't erase that in `clearPrevMessage`. In future, we may want to treat\n\t// `clear` as a special case and remove the bar too.\n\tconst clear = (): void => _stop('', 0, true);\n\n\tconst message = (msg = ''): void => {\n\t\t_message = removeTrailingDots(msg ?? _message);\n\t};\n\n\treturn {\n\t\tstart,\n\t\tstop,\n\t\tmessage,\n\t\tcancel,\n\t\terror,\n\t\tclear,\n\t\tget isCancelled() {\n\t\t\treturn isCancelled;\n\t\t},\n\t};\n};\n","import { styleText } from 'node:util';\nimport type { State } from '@clack/core';\nimport { unicodeOr } from './common.js';\nimport { type SpinnerOptions, type SpinnerResult, spinner } from './spinner.js';\n\nconst S_PROGRESS_CHAR: Record<NonNullable<ProgressOptions['style']>, string> = {\n\tlight: unicodeOr('─', '-'),\n\theavy: unicodeOr('━', '='),\n\tblock: unicodeOr('█', '#'),\n};\n\nexport interface ProgressOptions extends SpinnerOptions {\n\tstyle?: 'light' | 'heavy' | 'block';\n\tmax?: number;\n\tsize?: number;\n}\n\nexport interface ProgressResult extends SpinnerResult {\n\tadvance(step?: number, msg?: string): void;\n}\n\nexport function progress({\n\tstyle = 'heavy',\n\tmax: userMax = 100,\n\tsize: userSize = 40,\n\t...spinnerOptions\n}: ProgressOptions = {}): ProgressResult {\n\tconst spin = spinner(spinnerOptions);\n\tlet value = 0;\n\tlet previousMessage = '';\n\n\tconst max = Math.max(1, userMax);\n\tconst size = Math.max(1, userSize);\n\n\tconst activeStyle = (state: State) => {\n\t\tswitch (state) {\n\t\t\tcase 'initial':\n\t\t\tcase 'active':\n\t\t\t\treturn (text: string) => styleText('magenta', text);\n\t\t\tcase 'error':\n\t\t\tcase 'cancel':\n\t\t\t\treturn (text: string) => styleText('red', text);\n\t\t\tcase 'submit':\n\t\t\t\treturn (text: string) => styleText('green', text);\n\t\t\tdefault:\n\t\t\t\treturn (text: string) => styleText('magenta', text);\n\t\t}\n\t};\n\tconst drawProgress = (state: State, msg: string) => {\n\t\tconst active = Math.floor((value / max) * size);\n\t\treturn `${activeStyle(state)(S_PROGRESS_CHAR[style].repeat(active))}${styleText('dim', S_PROGRESS_CHAR[style].repeat(size - active))} ${msg}`;\n\t};\n\n\tconst start = (msg = '') => {\n\t\tpreviousMessage = msg;\n\t\tspin.start(drawProgress('initial', msg));\n\t};\n\tconst advance = (step = 1, msg?: string): void => {\n\t\tvalue = Math.min(max, step + value);\n\t\tspin.message(drawProgress('active', msg ?? previousMessage));\n\t\tpreviousMessage = msg ?? previousMessage;\n\t};\n\treturn {\n\t\tstart,\n\t\tstop: spin.stop,\n\t\tcancel: spin.cancel,\n\t\terror: spin.error,\n\t\tclear: spin.clear,\n\t\tadvance,\n\t\tisCancelled: spin.isCancelled,\n\t\tmessage: (msg: string) => advance(0, msg),\n\t};\n}\n","import { styleText } from 'node:util';\nimport { SelectPrompt, settings, wrapTextWithPrefix } from '@clack/core';\nimport {\n\ttype CommonOptions,\n\tS_BAR,\n\tS_BAR_END,\n\tS_RADIO_ACTIVE,\n\tS_RADIO_INACTIVE,\n\tsymbol,\n\tsymbolBar,\n} from './common.js';\nimport { limitOptions } from './limit-options.js';\n\ntype Primitive = Readonly<string | boolean | number>;\n\nexport type Option<Value> = Value extends Primitive\n\t? {\n\t\t\t/**\n\t\t\t * Internal data for this option.\n\t\t\t */\n\t\t\tvalue: Value;\n\t\t\t/**\n\t\t\t * The optional, user-facing text for this option.\n\t\t\t *\n\t\t\t * By default, the `value` is converted to a string.\n\t\t\t */\n\t\t\tlabel?: string;\n\t\t\t/**\n\t\t\t * An optional hint to display to the user when\n\t\t\t * this option might be selected.\n\t\t\t *\n\t\t\t * By default, no `hint` is displayed.\n\t\t\t */\n\t\t\thint?: string;\n\t\t\t/**\n\t\t\t * Whether this option is disabled.\n\t\t\t * Disabled options are visible but cannot be selected.\n\t\t\t *\n\t\t\t * By default, options are not disabled.\n\t\t\t */\n\t\t\tdisabled?: boolean;\n\t\t}\n\t: {\n\t\t\t/**\n\t\t\t * Internal data for this option.\n\t\t\t */\n\t\t\tvalue: Value;\n\t\t\t/**\n\t\t\t * Required. The user-facing text for this option.\n\t\t\t */\n\t\t\tlabel: string;\n\t\t\t/**\n\t\t\t * An optional hint to display to the user when\n\t\t\t * this option might be selected.\n\t\t\t *\n\t\t\t * By default, no `hint` is displayed.\n\t\t\t */\n\t\t\thint?: string;\n\t\t\t/**\n\t\t\t * Whether this option is disabled.\n\t\t\t * Disabled options are visible but cannot be selected.\n\t\t\t *\n\t\t\t * By default, options are not disabled.\n\t\t\t */\n\t\t\tdisabled?: boolean;\n\t\t};\n\nexport interface SelectOptions<Value> extends CommonOptions {\n\tmessage: string;\n\toptions: Option<Value>[];\n\tinitialValue?: Value;\n\tmaxItems?: number;\n}\n\nconst computeLabel = (label: string, format: (text: string) => string) => {\n\tif (!label.includes('\\n')) {\n\t\treturn format(label);\n\t}\n\treturn label\n\t\t.split('\\n')\n\t\t.map((line) => format(line))\n\t\t.join('\\n');\n};\n\nexport const select = <Value>(opts: SelectOptions<Value>) => {\n\tconst opt = (\n\t\toption: Option<Value>,\n\t\tstate: 'inactive' | 'active' | 'selected' | 'cancelled' | 'disabled'\n\t) => {\n\t\tconst label = option.label ?? String(option.value);\n\t\tswitch (state) {\n\t\t\tcase 'disabled':\n\t\t\t\treturn `${styleText('gray', S_RADIO_INACTIVE)} ${computeLabel(label, (text) => styleText('gray', text))}${\n\t\t\t\t\toption.hint ? ` ${styleText('dim', `(${option.hint ?? 'disabled'})`)}` : ''\n\t\t\t\t}`;\n\t\t\tcase 'selected':\n\t\t\t\treturn `${computeLabel(label, (text) => styleText('dim', text))}`;\n\t\t\tcase 'active':\n\t\t\t\treturn `${styleText('green', S_RADIO_ACTIVE)} ${label}${\n\t\t\t\t\toption.hint ? ` ${styleText('dim', `(${option.hint})`)}` : ''\n\t\t\t\t}`;\n\t\t\tcase 'cancelled':\n\t\t\t\treturn `${computeLabel(label, (str) => styleText(['strikethrough', 'dim'], str))}`;\n\t\t\tdefault:\n\t\t\t\treturn `${styleText('dim', S_RADIO_INACTIVE)} ${computeLabel(label, (text) => styleText('dim', text))}`;\n\t\t}\n\t};\n\n\treturn new SelectPrompt({\n\t\toptions: opts.options,\n\t\tsignal: opts.signal,\n\t\tinput: opts.input,\n\t\toutput: opts.output,\n\t\tinitialValue: opts.initialValue,\n\t\trender() {\n\t\t\tconst hasGuide = opts.withGuide ?? settings.withGuide;\n\t\t\tconst titlePrefix = `${symbol(this.state)} `;\n\t\t\tconst titlePrefixBar = `${symbolBar(this.state)} `;\n\t\t\tconst messageLines = wrapTextWithPrefix(\n\t\t\t\topts.output,\n\t\t\t\topts.message,\n\t\t\t\ttitlePrefixBar,\n\t\t\t\ttitlePrefix\n\t\t\t);\n\t\t\tconst title = `${hasGuide ? `${styleText('gray', S_BAR)}\\n` : ''}${messageLines}\\n`;\n\n\t\t\tswitch (this.state) {\n\t\t\t\tcase 'submit': {\n\t\t\t\t\tconst submitPrefix = hasGuide ? `${styleText('gray', S_BAR)} ` : '';\n\t\t\t\t\tconst wrappedLines = wrapTextWithPrefix(\n\t\t\t\t\t\topts.output,\n\t\t\t\t\t\topt(this.options[this.cursor], 'selected'),\n\t\t\t\t\t\tsubmitPrefix\n\t\t\t\t\t);\n\t\t\t\t\treturn `${title}${wrappedLines}`;\n\t\t\t\t}\n\t\t\t\tcase 'cancel': {\n\t\t\t\t\tconst cancelPrefix = hasGuide ? `${styleText('gray', S_BAR)} ` : '';\n\t\t\t\t\tconst wrappedLines = wrapTextWithPrefix(\n\t\t\t\t\t\topts.output,\n\t\t\t\t\t\topt(this.options[this.cursor], 'cancelled'),\n\t\t\t\t\t\tcancelPrefix\n\t\t\t\t\t);\n\t\t\t\t\treturn `${title}${wrappedLines}${hasGuide ? `\\n${styleText('gray', S_BAR)}` : ''}`;\n\t\t\t\t}\n\t\t\t\tdefault: {\n\t\t\t\t\tconst prefix = hasGuide ? `${styleText('cyan', S_BAR)} ` : '';\n\t\t\t\t\tconst prefixEnd = hasGuide ? styleText('cyan', S_BAR_END) : '';\n\t\t\t\t\t// Calculate rowPadding: title lines + footer lines (S_BAR_END + trailing newline)\n\t\t\t\t\tconst titleLineCount = title.split('\\n').length;\n\t\t\t\t\tconst footerLineCount = hasGuide ? 2 : 1; // S_BAR_END + trailing newline (or just trailing newline)\n\t\t\t\t\treturn `${title}${prefix}${limitOptions({\n\t\t\t\t\t\toutput: opts.output,\n\t\t\t\t\t\tcursor: this.cursor,\n\t\t\t\t\t\toptions: this.options,\n\t\t\t\t\t\tmaxItems: opts.maxItems,\n\t\t\t\t\t\tcolumnPadding: prefix.length,\n\t\t\t\t\t\trowPadding: titleLineCount + footerLineCount,\n\t\t\t\t\t\tstyle: (item, active) =>\n\t\t\t\t\t\t\topt(item, item.disabled ? 'disabled' : active ? 'active' : 'inactive'),\n\t\t\t\t\t}).join(`\\n${prefix}`)}\\n${prefixEnd}\\n`;\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t}).prompt() as Promise<Value | symbol>;\n};\n","import { styleText } from 'node:util';\nimport { SelectKeyPrompt, settings, wrapTextWithPrefix } from '@clack/core';\nimport { type CommonOptions, S_BAR, S_BAR_END, symbol } from './common.js';\nimport type { Option } from './select.js';\n\nexport interface SelectKeyOptions<Value extends string> extends CommonOptions {\n\tmessage: string;\n\toptions: Option<Value>[];\n\tinitialValue?: Value;\n\tcaseSensitive?: boolean;\n}\n\nexport const selectKey = <Value extends string>(opts: SelectKeyOptions<Value>) => {\n\tconst opt = (\n\t\toption: Option<Value>,\n\t\tstate: 'inactive' | 'active' | 'selected' | 'cancelled' = 'inactive'\n\t) => {\n\t\tconst label = option.label ?? String(option.value);\n\t\tif (state === 'selected') {\n\t\t\treturn `${styleText('dim', label)}`;\n\t\t}\n\t\tif (state === 'cancelled') {\n\t\t\treturn `${styleText(['strikethrough', 'dim'], label)}`;\n\t\t}\n\t\tif (state === 'active') {\n\t\t\treturn `${styleText(['bgCyan', 'gray'], ` ${option.value} `)} ${label}${\n\t\t\t\toption.hint ? ` ${styleText('dim', `(${option.hint})`)}` : ''\n\t\t\t}`;\n\t\t}\n\t\treturn `${styleText(['gray', 'bgWhite', 'inverse'], ` ${option.value} `)} ${label}${\n\t\t\toption.hint ? ` ${styleText('dim', `(${option.hint})`)}` : ''\n\t\t}`;\n\t};\n\n\treturn new SelectKeyPrompt({\n\t\toptions: opts.options,\n\t\tsignal: opts.signal,\n\t\tinput: opts.input,\n\t\toutput: opts.output,\n\t\tinitialValue: opts.initialValue,\n\t\tcaseSensitive: opts.caseSensitive,\n\t\trender() {\n\t\t\tconst hasGuide = opts.withGuide ?? settings.withGuide;\n\t\t\tconst title = `${hasGuide ? `${styleText('gray', S_BAR)}\\n` : ''}${symbol(this.state)} ${opts.message}\\n`;\n\n\t\t\tswitch (this.state) {\n\t\t\t\tcase 'submit': {\n\t\t\t\t\tconst submitPrefix = hasGuide ? `${styleText('gray', S_BAR)} ` : '';\n\t\t\t\t\tconst selectedOption =\n\t\t\t\t\t\tthis.options.find((opt) => opt.value === this.value) ?? opts.options[0];\n\t\t\t\t\tconst wrapped = wrapTextWithPrefix(\n\t\t\t\t\t\topts.output,\n\t\t\t\t\t\topt(selectedOption, 'selected'),\n\t\t\t\t\t\tsubmitPrefix\n\t\t\t\t\t);\n\t\t\t\t\treturn `${title}${wrapped}`;\n\t\t\t\t}\n\t\t\t\tcase 'cancel': {\n\t\t\t\t\tconst cancelPrefix = hasGuide ? `${styleText('gray', S_BAR)} ` : '';\n\t\t\t\t\tconst wrapped = wrapTextWithPrefix(\n\t\t\t\t\t\topts.output,\n\t\t\t\t\t\topt(this.options[0], 'cancelled'),\n\t\t\t\t\t\tcancelPrefix\n\t\t\t\t\t);\n\t\t\t\t\treturn `${title}${wrapped}${hasGuide ? `\\n${styleText('gray', S_BAR)}` : ''}`;\n\t\t\t\t}\n\t\t\t\tdefault: {\n\t\t\t\t\tconst defaultPrefix = hasGuide ? `${styleText('cyan', S_BAR)} ` : '';\n\t\t\t\t\tconst defaultPrefixEnd = hasGuide ? styleText('cyan', S_BAR_END) : '';\n\t\t\t\t\tconst wrapped = this.options\n\t\t\t\t\t\t.map((option, i) =>\n\t\t\t\t\t\t\twrapTextWithPrefix(\n\t\t\t\t\t\t\t\topts.output,\n\t\t\t\t\t\t\t\topt(option, i === this.cursor ? 'active' : 'inactive'),\n\t\t\t\t\t\t\t\tdefaultPrefix\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.join('\\n');\n\t\t\t\t\treturn `${title}${wrapped}\\n${defaultPrefixEnd}\\n`;\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t}).prompt() as Promise<Value | symbol>;\n};\n","import { stripVTControlCharacters as strip, styleText } from 'node:util';\nimport { S_BAR, S_ERROR, S_INFO, S_STEP_SUBMIT, S_SUCCESS, S_WARN } from './common.js';\nimport type { LogMessageOptions } from './log.js';\n\nconst prefix = `${styleText('gray', S_BAR)} `;\n\n// TODO (43081j): this currently doesn't support custom `output` writables\n// because we rely on `columns` existing (i.e. `process.stdout.columns).\n//\n// If we want to support `output` being passed in, we will need to use\n// a condition like `if (output insance Writable)` to check if it has columns\nexport const stream = {\n\tmessage: async (\n\t\titerable: Iterable<string> | AsyncIterable<string>,\n\t\t{ symbol = styleText('gray', S_BAR) }: LogMessageOptions = {}\n\t) => {\n\t\tprocess.stdout.write(`${styleText('gray', S_BAR)}\\n${symbol} `);\n\t\tlet lineWidth = 3;\n\t\tfor await (let chunk of iterable) {\n\t\t\tchunk = chunk.replace(/\\n/g, `\\n${prefix}`);\n\t\t\tif (chunk.includes('\\n')) {\n\t\t\t\tlineWidth = 3 + strip(chunk.slice(chunk.lastIndexOf('\\n'))).length;\n\t\t\t}\n\t\t\tconst chunkLen = strip(chunk).length;\n\t\t\tif (lineWidth + chunkLen < process.stdout.columns) {\n\t\t\t\tlineWidth += chunkLen;\n\t\t\t\tprocess.stdout.write(chunk);\n\t\t\t} else {\n\t\t\t\tprocess.stdout.write(`\\n${prefix}${chunk.trimStart()}`);\n\t\t\t\tlineWidth = 3 + strip(chunk.trimStart()).length;\n\t\t\t}\n\t\t}\n\t\tprocess.stdout.write('\\n');\n\t},\n\tinfo: (iterable: Iterable<string> | AsyncIterable<string>) => {\n\t\treturn stream.message(iterable, { symbol: styleText('blue', S_INFO) });\n\t},\n\tsuccess: (iterable: Iterable<string> | AsyncIterable<string>) => {\n\t\treturn stream.message(iterable, { symbol: styleText('green', S_SUCCESS) });\n\t},\n\tstep: (iterable: Iterable<string> | AsyncIterable<string>) => {\n\t\treturn stream.message(iterable, { symbol: styleText('green', S_STEP_SUBMIT) });\n\t},\n\twarn: (iterable: Iterable<string> | AsyncIterable<string>) => {\n\t\treturn stream.message(iterable, { symbol: styleText('yellow', S_WARN) });\n\t},\n\t/** alias for `log.warn()`. */\n\twarning: (iterable: Iterable<string> | AsyncIterable<string>) => {\n\t\treturn stream.warn(iterable);\n\t},\n\terror: (iterable: Iterable<string> | AsyncIterable<string>) => {\n\t\treturn stream.message(iterable, { symbol: styleText('red', S_ERROR) });\n\t},\n};\n","import type { CommonOptions } from './common.js';\nimport { spinner } from './spinner.js';\n\nexport type Task = {\n\t/**\n\t * Task title\n\t */\n\ttitle: string;\n\t/**\n\t * Task function\n\t */\n\ttask: (message: (string: string) => void) => string | Promise<string> | void | Promise<void>;\n\n\t/**\n\t * If enabled === false the task will be skipped\n\t */\n\tenabled?: boolean;\n};\n\n/**\n * Define a group of tasks to be executed\n */\nexport const tasks = async (tasks: Task[], opts?: CommonOptions) => {\n\tfor (const task of tasks) {\n\t\tif (task.enabled === false) continue;\n\n\t\tconst s = spinner(opts);\n\t\ts.start(task.title);\n\t\tconst result = await task.task(s.message);\n\t\ts.stop(result || task.title);\n\t}\n};\n","import type { Writable } from 'node:stream';\nimport { styleText } from 'node:util';\nimport { getColumns } from '@clack/core';\nimport { erase } from 'sisteransi';\nimport {\n\ttype CommonOptions,\n\tisCI as isCIFn,\n\tisTTY as isTTYFn,\n\tS_BAR,\n\tS_STEP_SUBMIT,\n} from './common.js';\nimport { log } from './log.js';\n\nexport interface TaskLogOptions extends CommonOptions {\n\ttitle: string;\n\tlimit?: number;\n\tspacing?: number;\n\tretainLog?: boolean;\n}\n\nexport interface TaskLogMessageOptions {\n\traw?: boolean;\n}\n\nexport interface TaskLogCompletionOptions {\n\tshowLog?: boolean;\n}\n\ninterface BufferEntry {\n\theader?: string;\n\tvalue: string;\n\tfull: string;\n\tresult?: {\n\t\tstatus: 'success' | 'error';\n\t\tmessage: string;\n\t};\n}\n\nconst stripDestructiveANSI = (input: string): string => {\n\t// biome-ignore lint/suspicious/noControlCharactersInRegex: intentional\n\treturn input.replace(/\\x1b\\[(?:\\d+;)*\\d*[ABCDEFGHfJKSTsu]|\\x1b\\[(s|u)/g, '');\n};\n\n/**\n * Renders a log which clears on success and remains on failure\n */\nexport const taskLog = (opts: TaskLogOptions) => {\n\tconst output: Writable = opts.output ?? process.stdout;\n\tconst columns = getColumns(output);\n\tconst secondarySymbol = styleText('gray', S_BAR);\n\tconst spacing = opts.spacing ?? 1;\n\tconst barSize = 3;\n\tconst retainLog = opts.retainLog === true;\n\tconst isTTY = !isCIFn() && isTTYFn(output);\n\n\toutput.write(`${secondarySymbol}\\n`);\n\toutput.write(`${styleText('green', S_STEP_SUBMIT)} ${opts.title}\\n`);\n\tfor (let i = 0; i < spacing; i++) {\n\t\toutput.write(`${secondarySymbol}\\n`);\n\t}\n\n\tconst buffers: BufferEntry[] = [\n\t\t{\n\t\t\tvalue: '',\n\t\t\tfull: '',\n\t\t},\n\t];\n\tlet lastMessageWasRaw = false;\n\n\tconst clear = (clearTitle: boolean): void => {\n\t\tif (buffers.length === 0) {\n\t\t\treturn;\n\t\t}\n\n\t\tlet lines = 0;\n\n\t\tif (clearTitle) {\n\t\t\tlines += spacing + 2;\n\t\t}\n\n\t\tfor (const buffer of buffers) {\n\t\t\tconst { value, result } = buffer;\n\t\t\tlet text = result?.message ?? value;\n\n\t\t\tif (text.length === 0) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (result === undefined && buffer.header !== undefined && buffer.header !== '') {\n\t\t\t\ttext += `\\n${buffer.header}`;\n\t\t\t}\n\n\t\t\tconst bufferHeight = text.split('\\n').reduce((count, line) => {\n\t\t\t\tif (line === '') {\n\t\t\t\t\treturn count + 1;\n\t\t\t\t}\n\t\t\t\treturn count + Math.ceil((line.length + barSize) / columns);\n\t\t\t}, 0);\n\n\t\t\tlines += bufferHeight;\n\t\t}\n\n\t\tif (lines > 0) {\n\t\t\tlines += 1;\n\t\t\toutput.write(erase.lines(lines));\n\t\t}\n\t};\n\tconst printBuffer = (buffer: BufferEntry, messageSpacing?: number, full?: boolean): void => {\n\t\tconst messages = full ? `${buffer.full}\\n${buffer.value}` : buffer.value;\n\t\tif (buffer.header !== undefined && buffer.header !== '') {\n\t\t\tlog.message(\n\t\t\t\tbuffer.header.split('\\n').map((line) => styleText('bold', line)),\n\t\t\t\t{\n\t\t\t\t\toutput,\n\t\t\t\t\tsecondarySymbol,\n\t\t\t\t\tsymbol: secondarySymbol,\n\t\t\t\t\tspacing: 0,\n\t\t\t\t}\n\t\t\t);\n\t\t}\n\t\tlog.message(\n\t\t\tmessages.split('\\n').map((line) => styleText('dim', line)),\n\t\t\t{\n\t\t\t\toutput,\n\t\t\t\tsecondarySymbol,\n\t\t\t\tsymbol: secondarySymbol,\n\t\t\t\tspacing: messageSpacing ?? spacing,\n\t\t\t}\n\t\t);\n\t};\n\tconst renderBuffer = (): void => {\n\t\tfor (const buffer of buffers) {\n\t\t\tconst { header, value, full } = buffer;\n\t\t\tif ((header === undefined || header.length === 0) && value.length === 0) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tprintBuffer(buffer, undefined, retainLog === true && full.length > 0);\n\t\t}\n\t};\n\tconst message = (buffer: BufferEntry, msg: string, mopts?: TaskLogMessageOptions) => {\n\t\tclear(false);\n\t\tif ((mopts?.raw !== true || !lastMessageWasRaw) && buffer.value !== '') {\n\t\t\tbuffer.value += '\\n';\n\t\t}\n\t\tbuffer.value += stripDestructiveANSI(msg);\n\t\tlastMessageWasRaw = mopts?.raw === true;\n\t\tif (opts.limit !== undefined) {\n\t\t\tconst lines = buffer.value.split('\\n');\n\t\t\tconst linesToRemove = lines.length - opts.limit;\n\t\t\tif (linesToRemove > 0) {\n\t\t\t\tconst removedLines = lines.splice(0, linesToRemove);\n\t\t\t\tif (retainLog) {\n\t\t\t\t\tbuffer.full += (buffer.full === '' ? '' : '\\n') + removedLines.join('\\n');\n\t\t\t\t}\n\t\t\t}\n\t\t\tbuffer.value = lines.join('\\n');\n\t\t}\n\t\tif (isTTY) {\n\t\t\tprintBuffers();\n\t\t}\n\t};\n\tconst printBuffers = (): void => {\n\t\tfor (const buffer of buffers) {\n\t\t\tif (buffer.result) {\n\t\t\t\tif (buffer.result.status === 'error') {\n\t\t\t\t\tlog.error(buffer.result.message, { output, secondarySymbol, spacing: 0 });\n\t\t\t\t} else {\n\t\t\t\t\tlog.success(buffer.result.message, { output, secondarySymbol, spacing: 0 });\n\t\t\t\t}\n\t\t\t} else if (buffer.value !== '') {\n\t\t\t\tprintBuffer(buffer, 0);\n\t\t\t}\n\t\t}\n\t};\n\tconst completeBuffer = (buffer: BufferEntry, result: BufferEntry['result']): void => {\n\t\tclear(false);\n\n\t\tbuffer.result = result;\n\n\t\tif (isTTY) {\n\t\t\tprintBuffers();\n\t\t}\n\t};\n\n\treturn {\n\t\tmessage(msg: string, mopts?: TaskLogMessageOptions) {\n\t\t\tmessage(buffers[0], msg, mopts);\n\t\t},\n\t\tgroup(name: string) {\n\t\t\tconst buffer: BufferEntry = {\n\t\t\t\theader: name,\n\t\t\t\tvalue: '',\n\t\t\t\tfull: '',\n\t\t\t};\n\t\t\tbuffers.push(buffer);\n\t\t\treturn {\n\t\t\t\tmessage(msg: string, mopts?: TaskLogMessageOptions) {\n\t\t\t\t\tmessage(buffer, msg, mopts);\n\t\t\t\t},\n\t\t\t\terror(message: string) {\n\t\t\t\t\tcompleteBuffer(buffer, {\n\t\t\t\t\t\tstatus: 'error',\n\t\t\t\t\t\tmessage,\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t\tsuccess(message: string) {\n\t\t\t\t\tcompleteBuffer(buffer, {\n\t\t\t\t\t\tstatus: 'success',\n\t\t\t\t\t\tmessage,\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t};\n\t\t},\n\t\terror(message: string, opts?: TaskLogCompletionOptions): void {\n\t\t\tclear(true);\n\t\t\tlog.error(message, { output, secondarySymbol, spacing: 1 });\n\t\t\tif (opts?.showLog !== false) {\n\t\t\t\trenderBuffer();\n\t\t\t}\n\t\t\t// clear buffer since error is an end state\n\t\t\tbuffers.splice(1, buffers.length - 1);\n\t\t\tbuffers[0].value = '';\n\t\t\tbuffers[0].full = '';\n\t\t},\n\t\tsuccess(message: string, opts?: TaskLogCompletionOptions): void {\n\t\t\tclear(true);\n\t\t\tlog.success(message, { output, secondarySymbol, spacing: 1 });\n\t\t\tif (opts?.showLog === true) {\n\t\t\t\trenderBuffer();\n\t\t\t}\n\t\t\t// clear buffer since success is an end state\n\t\t\tbuffers.splice(1, buffers.length - 1);\n\t\t\tbuffers[0].value = '';\n\t\t\tbuffers[0].full = '';\n\t\t},\n\t};\n};\n","import { styleText } from 'node:util';\nimport { settings, TextPrompt } from '@clack/core';\nimport { type CommonOptions, S_BAR, S_BAR_END, symbol } from './common.js';\n\nexport interface TextOptions extends CommonOptions {\n\tmessage: string;\n\tplaceholder?: string;\n\tdefaultValue?: string;\n\tinitialValue?: string;\n\tvalidate?: (value: string | undefined) => string | Error | undefined;\n}\n\nexport const text = (opts: TextOptions) => {\n\treturn new TextPrompt({\n\t\tvalidate: opts.validate,\n\t\tplaceholder: opts.placeholder,\n\t\tdefaultValue: opts.defaultValue,\n\t\tinitialValue: opts.initialValue,\n\t\toutput: opts.output,\n\t\tsignal: opts.signal,\n\t\tinput: opts.input,\n\t\trender() {\n\t\t\tconst hasGuide = opts?.withGuide ?? settings.withGuide;\n\t\t\tconst titlePrefix = `${hasGuide ? `${styleText('gray', S_BAR)}\\n` : ''}${symbol(this.state)} `;\n\t\t\tconst title = `${titlePrefix}${opts.message}\\n`;\n\t\t\tconst placeholder = opts.placeholder\n\t\t\t\t? styleText('inverse', opts.placeholder[0]) + styleText('dim', opts.placeholder.slice(1))\n\t\t\t\t: styleText(['inverse', 'hidden'], '_');\n\t\t\tconst userInput = !this.userInput ? placeholder : this.userInputWithCursor;\n\t\t\tconst value = this.value ?? '';\n\n\t\t\tswitch (this.state) {\n\t\t\t\tcase 'error': {\n\t\t\t\t\tconst errorText = this.error ? ` ${styleText('yellow', this.error)}` : '';\n\t\t\t\t\tconst errorPrefix = hasGuide ? `${styleText('yellow', S_BAR)} ` : '';\n\t\t\t\t\tconst errorPrefixEnd = hasGuide ? styleText('yellow', S_BAR_END) : '';\n\t\t\t\t\treturn `${title.trim()}\\n${errorPrefix}${userInput}\\n${errorPrefixEnd}${errorText}\\n`;\n\t\t\t\t}\n\t\t\t\tcase 'submit': {\n\t\t\t\t\tconst valueText = value ? ` ${styleText('dim', value)}` : '';\n\t\t\t\t\tconst submitPrefix = hasGuide ? styleText('gray', S_BAR) : '';\n\t\t\t\t\treturn `${title}${submitPrefix}${valueText}`;\n\t\t\t\t}\n\t\t\t\tcase 'cancel': {\n\t\t\t\t\tconst valueText = value ? ` ${styleText(['strikethrough', 'dim'], value)}` : '';\n\t\t\t\t\tconst cancelPrefix = hasGuide ? styleText('gray', S_BAR) : '';\n\t\t\t\t\treturn `${title}${cancelPrefix}${valueText}${value.trim() ? `\\n${cancelPrefix}` : ''}`;\n\t\t\t\t}\n\t\t\t\tdefault: {\n\t\t\t\t\tconst defaultPrefix = hasGuide ? `${styleText('cyan', S_BAR)} ` : '';\n\t\t\t\t\tconst defaultPrefixEnd = hasGuide ? styleText('cyan', S_BAR_END) : '';\n\t\t\t\t\treturn `${title}${defaultPrefix}${userInput}\\n${defaultPrefixEnd}\\n`;\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t}).prompt() as Promise<string | symbol>;\n};\n","/**\n * Update command — 비대화형 정책 파일 갱신.\n *\n * 왜 있나: `install`·`list`·`uninstall` 은 전부 플래그로 비대화형 실행이 되는데 `update` 만\n * 위저드 전용이었다. CI 로 하네스를 **깔 수는 있는데 갱신할 수는 없다**는 건 수요의 문제가\n * 아니라 명령 계열의 비대칭이다 — 빠진 쪽이 사유를 대야 한다 (사용자 지시 2026-07-20).\n *\n * 동작: 기존 설치를 감지해 위저드의 update 액션과 **동일한 spec**(`buildUpdateSpec`)으로\n * `mode: \"update\"` 파이프라인을 돈다. 백업은 update mode 가 자동으로 뜬다 (`.claude.backup-*`).\n *\n * 하지 않는 것: track 추가(=`install`), 자산 재설치, 대화형 확인. update 는 이미 깔린\n * 정책 파일만 최신판으로 맞춘다.\n */\n\nimport { resolve } from \"node:path\";\nimport { c, status } from \"../design.js\";\nimport { type DetectedInstall, detectInstallState } from \"../state.js\";\nimport { type InstallSpec, UPDATE_GROUPS } from \"../types.js\";\nimport { buildUpdateSpec, parseUpdateOnly } from \"../update-mode.js\";\nimport { type ExecuteSpecDeps, executeSpec } from \"./install.js\";\n\nexport interface UpdateOptions {\n projectDir?: string;\n /** #480 — `--only <group>` (반복). cac 는 한 번이면 string, 여러 번이면 string[] 을 준다. */\n only?: string | string[];\n}\n\nexport interface UpdateActionDeps {\n log?: (msg: string) => void;\n err?: (msg: string) => void;\n exit?: (code: number) => never;\n detect?: (projectDir: string) => DetectedInstall;\n execute?: (spec: InstallSpec, deps: ExecuteSpecDeps) => void;\n}\n\nexport function updateAction(options: UpdateOptions = {}, deps: UpdateActionDeps = {}): void {\n const log = deps.log ?? console.log;\n const err = deps.err ?? console.error;\n const exit = deps.exit ?? ((code: number) => process.exit(code) as never);\n const detect = deps.detect ?? detectInstallState;\n const execute = deps.execute ?? executeSpec;\n\n const projectDir = resolve(options.projectDir ?? process.cwd());\n const state = detect(projectDir);\n\n // Pre-flight: 갱신할 대상이 없으면 조용히 성공하지 않는다. 파이프라인도 같은 조건에서\n // throw 하지만, 그건 \"install failed\" 로 렌더돼 원인이 안 보인다.\n // v26.135.0 (#253) — 판정 기준이 `.claude/` 존재에서 **설치 여부**로 바뀌었다. update 는\n // v26.134.0(ADR-049)부터 외부 CLI 산출물도 갱신하므로, `.claude/` 없는 opencode/codex 단독\n // 설치도 정당한 update 대상이다. `.claude/` 로 막으면 갱신할 게 있는데 거절한다.\n if (state.state === \"new\") {\n err(status.failure(c.red(`No harness install found at ${projectDir}`)));\n err(c.dim(\" Run `agent-harness install --track <name>` first.\"));\n exit(1);\n return;\n }\n\n // cac 는 `--only` 를 값 없이 주면 boolean true 를 준다 — \"true\" 가 모르는 값으로 거절되게 문자열로.\n const rawOnly =\n options.only === undefined\n ? []\n : [options.only].flat().map((v) => (typeof v === \"string\" ? v : \"(missing value)\"));\n const parsed = parseUpdateOnly(rawOnly);\n if (!parsed.ok) {\n err(status.failure(c.red(`Unknown --only value: ${parsed.invalid.join(\", \")}`)));\n err(c.dim(` Valid groups: ${UPDATE_GROUPS.join(\" | \")}`));\n exit(1);\n return;\n }\n log(c.dim(`Updating installed harness files in ${projectDir}`));\n execute(buildUpdateSpec(projectDir, state.tracks, parsed.groups), {\n log,\n err,\n exit,\n mode: \"update\",\n });\n}\n\nexport function registerUpdateCommand(cli: import(\"../cli.js\").Cli): void {\n cli\n .command(\n \"update\",\n \"Refresh installed policy files (rules / agents / commands / hooks / skills)\",\n )\n .option(\"--project-dir <path>\", \"[Project] Target project directory\", {\n default: process.cwd(),\n })\n .option(\n \"--only <group>\",\n `[Scope] Update only this group (repeatable): ${UPDATE_GROUPS.join(\" | \")}`,\n )\n /* v8 ignore next 3 — cac action callback. updateAction 자체는 별도 tests 로 검증. */\n .action((options: UpdateOptions) => {\n updateAction(options);\n });\n}\n","import { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { readInstallLog } from \"./install-log.js\";\nimport { isTrack, type Track } from \"./types.js\";\n\nexport type InstallState = \"new\" | \"existing\";\n\nexport interface DetectedInstall {\n state: InstallState;\n tracks: Track[];\n /**\n * Source of the detected tracks: \"metafile\" (v27.17+), \"legacy\" (rules/*.md heuristic),\n * \"install-log\" (v26.135.0 — `.claude/` 없는 설치), or \"none\".\n */\n source: \"metafile\" | \"legacy\" | \"install-log\" | \"none\";\n /**\n * `.claude/` 가 실제로 있는가. **\"설치됐는가\"가 아니다** — opencode/codex 단독 설치는\n * `.claude/` 없이도 설치 상태다 (v26.135.0 · #253). 설치 여부는 `state` 를 봐라.\n */\n hasClaudeDir: boolean;\n}\n\nconst META_FILE = \".claude/.installed-tracks\";\n\ninterface LegacySignature {\n rule: string;\n track: Track;\n}\n\nconst LEGACY_SIGNATURES: readonly LegacySignature[] = [\n { rule: \"htmx.md\", track: \"ssr-htmx\" },\n { rule: \"nextjs.md\", track: \"ssr-nextjs\" },\n { rule: \"data-analysis.md\", track: \"data\" },\n { rule: \"pyside6.md\", track: \"data\" },\n { rule: \"cli-development.md\", track: \"tooling\" },\n];\n\n/**\n * Detect what was previously installed in the given project directory.\n * Mirrors the v27.17 detect_install_state shell function (setup-harness.sh:191).\n */\nexport function detectInstallState(projectDir: string): DetectedInstall {\n const claudeDir = join(projectDir, \".claude\");\n const hasClaudeDir = existsSync(claudeDir);\n\n if (hasClaudeDir) {\n const metaPath = join(projectDir, META_FILE);\n if (existsSync(metaPath)) {\n const tracks = readMetafile(metaPath);\n return { state: \"existing\", tracks, source: \"metafile\", hasClaudeDir: true };\n }\n const tracks = inferFromLegacySignatures(projectDir);\n return { state: \"existing\", tracks, source: \"legacy\", hasClaudeDir: true };\n }\n\n // v26.135.0 (#253) — `.claude/` 부재 ≠ 미설치. opencode/codex 단독 설치는 `.claude/` 를\n // 만들지 않는다(만들던 것이 그 이슈다). 설치 로그가 있으면 설치된 것이고, 이걸 안 보면\n // 위저드가 기설치 프로젝트에 \"new install\" 흐름을 태운다.\n const log = readInstallLog(projectDir);\n if (log) {\n return {\n state: \"existing\",\n tracks: tracksFromLog(log.spec.tracks),\n source: \"install-log\",\n hasClaudeDir: false,\n };\n }\n\n return { state: \"new\", tracks: [], source: \"none\", hasClaudeDir: false };\n}\n\n/** 로그의 tracks 는 문자열이라 현재 Track 어휘로 좁힌다 (구 버전 로그에 폐기된 track 이 있을 수 있다). */\nfunction tracksFromLog(tracks: ReadonlyArray<string>): Track[] {\n return [...new Set(tracks.filter(isTrack))].sort();\n}\n\nfunction readMetafile(path: string): Track[] {\n const raw = readFileSync(path, \"utf8\");\n const seen = new Set<Track>();\n for (const line of raw.split(/\\s+/)) {\n const trimmed = line.trim();\n if (isTrack(trimmed)) {\n seen.add(trimmed);\n }\n }\n return [...seen].sort();\n}\n\nfunction inferFromLegacySignatures(projectDir: string): Track[] {\n const rulesDir = join(projectDir, \".claude/rules\");\n if (!existsSync(rulesDir)) {\n return [];\n }\n const found = new Set<Track>();\n for (const sig of LEGACY_SIGNATURES) {\n if (existsSync(join(rulesDir, sig.rule))) {\n found.add(sig.track);\n }\n }\n return [...found].sort();\n}\n","import { BASELINE_PREFIX, isBaselineExcluded, listBaselineTargets } from \"./baseline-targets.js\";\nimport {\n formatResidentCostLine,\n landsOnDisk,\n residentCost,\n summarizeContextCost,\n} from \"./context-cost.js\";\nimport { assetReachesCli, EXTERNAL_ASSETS } from \"./external-assets.js\";\nimport { readInstallLog } from \"./install-log.js\";\nimport { buildManifestSpec, type InstallMode } from \"./installer.js\";\nimport { buildManifest } from \"./manifest.js\";\nimport {\n finalSelectedAssets,\n groupAssetsByCategory,\n recommendedExternalAssets,\n} from \"./preset-recommend.js\";\nimport {\n defaultPrompts,\n type InstallTargetId,\n type Prompts,\n VISIBLE_OPTION_DEFS,\n} from \"./prompts.js\";\nimport { type DetectedInstall, detectInstallState } from \"./state.js\";\nimport { type InstallSpec, type OptionFlags, type Track, UPDATE_GROUPS } from \"./types.js\";\nimport { buildUpdateSpec } from \"./update-mode.js\";\nimport { stepLabel, WIZARD } from \"./wizard-steps.js\";\n\n/**\n * v26.54.0 — All-in-one 결과 → option keys + asset id list 분리.\n * `option:<key>` → OptionFlags key\n * `asset:<id>` → EXTERNAL_ASSETS id\n */\nexport function splitInstallTargets(targets: ReadonlyArray<InstallTargetId>): {\n optionKeys: Array<keyof OptionFlags>;\n assetIds: Array<string>;\n /** 2026-08-16 — 사용자가 **체크한 채로 둔** baseline id. 제외는 이것의 여집합으로 낸다. */\n baselineIds: Array<string>;\n} {\n const optionKeys: Array<keyof OptionFlags> = [];\n const assetIds: Array<string> = [];\n const baselineIds: Array<string> = [];\n for (const t of targets) {\n if (t.startsWith(\"option:\")) {\n optionKeys.push(t.slice(\"option:\".length) as keyof OptionFlags);\n } else if (t.startsWith(\"asset:\")) {\n assetIds.push(t.slice(\"asset:\".length));\n } else if (t.startsWith(BASELINE_PREFIX)) {\n baselineIds.push(t);\n }\n }\n return { optionKeys, assetIds, baselineIds };\n}\n\n/**\n * 체크 결과 → **제외 목록**. 화면은 \"설치할 것\"을 체크로 보여주는데 spec 이 받는 것은\n * \"빼는 것\"이라, 이 뒤집기를 한 곳에 둔다.\n *\n * 전부 체크된 기본 상태에서는 빈 배열이 나온다 — 즉 아무것도 안 고른 사용자의 설치 결과는\n * 이 기능이 생기기 전과 **바이트 단위로 같다**.\n */\nexport function baselineExcludeFrom(\n offered: ReadonlyArray<{ id: string }>,\n checked: ReadonlyArray<string>,\n): string[] {\n const keep = new Set(checked);\n return offered.filter((t) => !keep.has(t.id)).map((t) => t.id);\n}\n\n/**\n * v26.81.0 (ADR-022) — 자산 1:1 boolean 13종 삭제 후 잔존 동작 옵션만 매핑.\n * wizard 의 자산 선택은 전부 `asset:<id>` → userOverride.forceInclude 경로.\n */\nexport function toOptionFlags(keys: ReadonlyArray<keyof OptionFlags>): OptionFlags {\n const picked = new Set<keyof OptionFlags>(keys);\n return {\n withPrune: picked.has(\"withPrune\"),\n withCodexTrust: picked.has(\"withCodexTrust\"),\n };\n}\n\nexport interface InteractiveDeps {\n prompts?: Prompts;\n detect?: (projectDir: string) => DetectedInstall;\n isTty?: () => boolean;\n /** v26.125.0 — 설치 상태 주입 (테스트용). 미주입 시 install log 를 읽는다. */\n readInstalled?: (projectDir: string) => InstalledTargetState;\n}\n\n/**\n * v26.125.0 — wizard 가 참조하는 설치 상태.\n *\n * `installed` 와 `projectScoped` 를 나누는 이유: global scope 자산까지 사전 체크하면\n * step 4 에서 project 를 고른 순간 **같은 자산이 project 에 한 벌 더 깔린다**. 그래서\n * 표시(마커)는 전부 하고, 사전 체크는 project scope 만 한다.\n */\nexport interface InstalledTargetState {\n /** 마커를 붙일 자산 id — scope 무관 (설치돼 있다는 사실 자체는 참) */\n installed: ReadonlyArray<string>;\n /** 사전 체크할 자산 id — project scope 만 */\n projectScoped: ReadonlyArray<string>;\n}\n\n/** install log → wizard 표시용 설치 상태. 로그가 없으면(최초 설치) 빈 상태. */\nexport function installedTargetState(projectDir: string): InstalledTargetState {\n const log = readInstallLog(projectDir);\n if (!log) return { installed: [], projectScoped: [] };\n return {\n installed: log.assets.map((a) => a.id),\n projectScoped: log.assets.filter((a) => a.scope !== \"global\").map((a) => a.id),\n };\n}\n\n/**\n * step 3 의 초기 체크 = **트랙 추천 ∪ 이미 설치된 project 자산**.\n *\n * 추천만 쓰면 추천 밖의 설치된 자산이 빈칸으로 보여, 사용자가 \"안 깔렸다\"고 읽는다.\n * 합집합이므로 중복은 생기지 않는다 (헤더의 `n/m ✓` 카운트가 거짓이 되지 않아야 한다).\n */\nexport function initialTargetSelection(\n tracks: ReadonlyArray<Track>,\n installedProjectAssetIds: ReadonlyArray<string>,\n): InstallTargetId[] {\n const ids = new Set<string>(recommendedExternalAssets(tracks));\n for (const id of installedProjectAssetIds) ids.add(id);\n const assets = [...ids].map((id) => `asset:${id}` as InstallTargetId);\n // 트랙이 고르는 자산은 **전부 체크된 채로** 시작한다. 기본값을 바꾸는 것이 아니라 기본값을\n // 보이게 하는 것이 목적이다 — 아무것도 안 건드리면 설치 결과는 이전과 같다.\n const baseline = listBaselineTargets({ tracks }).map((t) => t.id as InstallTargetId);\n return [...assets, ...baseline];\n}\n\nexport interface InteractiveResult {\n ok: boolean;\n spec?: InstallSpec;\n mode?: InstallMode;\n reason?: \"no-tty\" | \"cancelled\" | \"disabled-action\" | \"exit\";\n message?: string;\n}\n\n/**\n * v26.54.0 — 3-step wizard. SPEC: docs/specs/v26-54-all-in-one-installer.md\n *\n * Step 1: tracks (ESC = exit + cancel msg)\n * Step 2: cli (ESC = silent back to tracks)\n * Step 3: install-targets all-in-one (ESC = silent back to cli)\n * confirm prompt (ESC = silent back to targets)\n *\n * 이전 5-step 의 options + 2-tier asset navigator 를 step 3 1 화면 group multiselect 로 흡수.\n */\nexport async function runInteractive(\n projectDir: string,\n deps: InteractiveDeps = {},\n): Promise<InteractiveResult> {\n const prompts = deps.prompts ?? defaultPrompts;\n const detect = deps.detect ?? detectInstallState;\n const isTty = deps.isTty ?? (() => Boolean(process.stdin.isTTY));\n\n if (!isTty()) {\n return {\n ok: false,\n reason: \"no-tty\",\n message:\n \"Interactive mode requires a TTY. Use `agent-harness install --track <name>` for non-interactive use.\",\n };\n }\n\n prompts.intro(\"uzys-agent-harness installer\");\n const state = detect(projectDir);\n // v26.125.0 — 위저드가 설치 상태를 읽는다. 이전에는 step 3 의 체크가 트랙 추천에서만 나와\n // **이미 깔린 자산이 빈칸으로 보였다** — 사용자가 그 체크박스를 설치 상태로 읽으므로 화면이\n // 거짓을 말한 셈이다. 마커는 표시 전용이고 체크를 풀어도 제거되지 않는다 (제거 = `uninstall`).\n const installed = (deps.readInstalled ?? installedTargetState)(projectDir);\n\n let initialTracks: Track[] | undefined;\n let mode: InstallMode = \"fresh\";\n if (state.state === \"existing\") {\n const action = await prompts.selectAction(state);\n if (action === null) {\n prompts.cancel(\"Cancelled.\");\n return { ok: false, reason: \"cancelled\" };\n }\n if (action === \"exit\") {\n prompts.outro(\"Exiting without changes.\");\n return { ok: false, reason: \"exit\" };\n }\n if (action === \"remove\") {\n prompts.cancel(\"Track removal is not automated — manually edit `.claude/`. Aborting.\");\n return { ok: false, reason: \"disabled-action\" };\n }\n if (action === \"update\") {\n mode = \"update\";\n // spec 은 `buildUpdateSpec` 단일 출처 — 비대화형 `update` 명령과 같은 것을 쓴다.\n // #480 — 무엇을 갱신할지 고른다. 프롬프트가 없는 구현(테스트 픽스처)은 전부.\n const groups = prompts.selectUpdateGroups ? await prompts.selectUpdateGroups() : null;\n if (groups === null && prompts.selectUpdateGroups) {\n prompts.cancel(\"Cancelled.\");\n return { ok: false, reason: \"cancelled\" };\n }\n const spec = buildUpdateSpec(projectDir, state.tracks, groups ?? undefined);\n const scopeLine = spec.updateOnly\n ? `UPDATE ${spec.updateOnly.join(\" · \")} only (untouched: ${UPDATE_GROUPS.filter((g) => !spec.updateOnly?.includes(g)).join(\", \")}):`\n : \"UPDATE installed harness files:\";\n const confirmed = await prompts.confirmInstall(`${scopeLine}\\n${formatSummary(spec)}`);\n if (!confirmed) {\n prompts.outro(\"Cancelled.\");\n return { ok: false, reason: \"cancelled\" };\n }\n prompts.outro(\"Running update mode...\");\n return { ok: true, mode: \"update\", spec };\n }\n if (action === \"add\") {\n mode = \"add\";\n initialTracks = state.tracks;\n } else if (action === \"reinstall\") {\n mode = \"reinstall\";\n }\n }\n\n // v26.64.0 (ADR-020) — scope step 추가. Default \"project\". Step 3.5 (targets 직후, confirm 직전).\n type Step = \"tracks\" | \"cli\" | \"targets\" | \"scope\" | \"confirm\";\n let step: Step = \"tracks\";\n let tracks: Track[] | null = null;\n let cli: import(\"./types.js\").CliTargets | null = null;\n let targetSelections: ReadonlyArray<InstallTargetId> | null = null;\n let scope: import(\"./types.js\").InstallScope = \"project\";\n\n while (true) {\n if (step === \"tracks\") {\n const result = await prompts.selectTracks(tracks ?? initialTracks, WIZARD.TRACKS);\n if (result === null) {\n // Step 1 ESC = exit with cancel message (only step where ESC is \"cancel\")\n prompts.cancel(\"Cancelled.\");\n return { ok: false, reason: \"cancelled\" };\n }\n // preset 변경 감지 → install-targets reset (v26.50 정책 유지)\n if (tracks !== null && !tracksEqual(tracks, result)) {\n targetSelections = null;\n }\n tracks = result;\n step = \"cli\";\n } else if (step === \"cli\") {\n const result = await prompts.selectCli(cli ?? [\"claude\"], WIZARD.CLI);\n if (result === null) {\n step = \"tracks\"; // silent back\n continue;\n }\n cli = result;\n step = \"targets\";\n } else if (step === \"targets\") {\n const initial: InstallTargetId[] =\n targetSelections !== null\n ? [...targetSelections]\n : initialTargetSelection(tracks ?? [], installed.projectScoped);\n // v26.65.0 — step indicator SSOT (wizard-steps.ts). Phase: 3 targets → 4 scope → 5 confirm → 6 install.\n const result = await prompts.selectInstallTargets(initial, WIZARD.TARGETS, {\n tracks: tracks ?? [],\n cli: cli ?? [\"claude\"],\n installed: installed.installed.map((id) => `asset:${id}`),\n });\n if (result === null) {\n step = \"cli\"; // silent back\n continue;\n }\n targetSelections = result;\n step = \"scope\";\n } else if (step === \"scope\") {\n // v26.64.0 (ADR-020) — Installation scope select. Default \"project\" (D16).\n const result = await prompts.selectScope(scope, WIZARD.SCOPE);\n if (result === null) {\n step = \"targets\"; // silent back\n continue;\n }\n scope = result;\n step = \"confirm\";\n } else {\n // confirm\n // biome-ignore lint/style/noNonNullAssertion: confirm step 도달 = 모든 이전 step 완료 보장\n const finalTracks = tracks!;\n // biome-ignore lint/style/noNonNullAssertion: same as above\n const finalCli = cli!;\n const { optionKeys, assetIds, baselineIds } = splitInstallTargets(targetSelections ?? []);\n const options = toOptionFlags(optionKeys);\n const userOverride =\n targetSelections === null ? undefined : computeUserOverride(finalTracks, assetIds);\n // 제외는 \"화면에 낸 것\" 대비로 낸다. 화면에 안 낸 자산(`settings.json` 등)은 후보가\n // 아니므로 어떤 조작으로도 빠지지 않는다.\n const baselineExclude =\n targetSelections === null\n ? []\n : baselineExcludeFrom(listBaselineTargets({ tracks: finalTracks }), baselineIds);\n // v26.64.0 (ADR-020) — Confirm summary 에 SCOPE 명시 (사용자 인지 + D16).\n const scopeLabel =\n scope === \"global\"\n ? \"Global (writes to ~/.claude/, ~/.codex/, npm -g)\"\n : \"Project (current directory only)\";\n const summary = `${formatSummary({\n tracks: finalTracks,\n options,\n cli: finalCli,\n projectDir,\n ...(userOverride ? { userOverride } : {}),\n // 해제 목록을 안 넘기면 이 화면이 **제외 유무와 문자열이 완전히 같아진다** — 같은 화면이\n // 외부 자산 제거는 `-User removed:` 로 이미 보고하므로, 없음은 \"아무것도 안 빠졌다\"로\n // 읽힌다. 상주 비용을 줄이려고 20개를 푼 사용자가 그대로인 숫자를 보게 된다.\n ...(baselineExclude.length > 0 ? { baselineExclude } : {}),\n })}\\n SCOPE ${scopeLabel}`;\n const confirmed = await prompts.confirmInstall(\n `${stepLabel(WIZARD.CONFIRM, \"Confirm\")}\\n${summary}`,\n );\n if (confirmed === null) {\n step = \"scope\"; // silent back\n continue;\n }\n if (!confirmed) {\n prompts.outro(\"Cancelled by user.\");\n return { ok: false, reason: \"cancelled\" };\n }\n\n const spec: InstallSpec = {\n tracks: finalTracks,\n options,\n cli: finalCli,\n projectDir,\n scope,\n ...(userOverride ? { userOverride } : {}),\n ...(baselineExclude.length > 0 ? { baselineExclude } : {}),\n };\n\n prompts.outro(stepLabel(WIZARD.INSTALL, \"Installing...\"));\n return { ok: true, mode, spec };\n }\n }\n}\n\n/**\n * Track 배열 동등 비교 (순서 무관). Preset 변경 감지에 사용.\n */\nfunction tracksEqual(a: ReadonlyArray<Track>, b: ReadonlyArray<Track>): boolean {\n if (a.length !== b.length) return false;\n const sortedA = [...a].sort();\n const sortedB = [...b].sort();\n return sortedA.every((t, i) => t === sortedB[i]);\n}\n\n/**\n * v26.54.0 — Asset 선택 결과 (id 만) 와 preset 추천 비교 → forceInclude / forceExclude.\n * - `recommended - selected` → forceExclude (사용자가 unchecked)\n * - `selected - recommended` → forceInclude (사용자가 추가 선택)\n * 둘 다 비어있으면 undefined (no override).\n */\nexport function computeUserOverride(\n tracks: ReadonlyArray<Track>,\n assetIds: ReadonlyArray<string>,\n): { forceInclude: ReadonlyArray<string>; forceExclude: ReadonlyArray<string> } | undefined {\n const recommended = new Set(recommendedExternalAssets(tracks));\n const selected = new Set(assetIds);\n const forceExclude = [...recommended].filter((id) => !selected.has(id)).sort();\n const forceInclude = [...selected].filter((id) => !recommended.has(id)).sort();\n if (forceInclude.length === 0 && forceExclude.length === 0) return undefined;\n return { forceInclude, forceExclude };\n}\n\nexport function formatSummary(spec: InstallSpec): string {\n const baselineExcluded = new Set(spec.baselineExclude ?? []);\n const opts = (Object.keys(spec.options) as Array<keyof OptionFlags>)\n .filter((k) => spec.options[k])\n .map((k) => k.replace(/^with/, \"\").toLowerCase());\n // v26.63.3 (clarify H1): \"(defaults only)\" 모호 → \"(none added)\" 명료.\n const optsLabel = opts.length > 0 ? opts.join(\", \") : \"(none added)\";\n const lines = [\n `Tracks: ${spec.tracks.join(\", \")}`,\n `Options: ${optsLabel}`,\n `CLI: ${spec.cli.join(\" · \")}`,\n `Target: ${spec.projectDir}`,\n ];\n\n // v26.62.3 — 실제 install 될 자산 list 명시. defaults 만으로는 사용자가\n // Step 3 에서 무엇을 confirm 했는지 알 수 없음. preset recommended +\n // userOverride 적용 후 최종 selected assets list 표시.\n // v26.82.0 (Phase R, S6) — merge/그룹화는 preset-recommend.ts 단일 구현 사용 (중복 제거).\n const finalAssets = finalSelectedAssets(spec.tracks, spec.userOverride);\n if (finalAssets.length > 0) {\n // v26.102.0 (ADR-031) — confirm 화면의 약속 숫자에 CLI 도달 분해 병기 (SOD 리뷰 F3:\n // \"4 selected\" 확정 후 0 설치이던 불일치 — 숨김 없이 고지만).\n const unreachable = finalAssets.filter((id) => {\n const asset = EXTERNAL_ASSETS.find((a) => a.id === id);\n return asset ? !assetReachesCli(asset, spec.cli) : false;\n });\n lines.push(\n unreachable.length > 0\n ? `Assets: ${finalAssets.length} selected (${unreachable.length} outside [${spec.cli.join(\", \")}] reach — not installed)`\n : `Assets: ${finalAssets.length} selected`,\n );\n for (const [cat, ids] of groupAssetsByCategory(finalAssets)) {\n lines.push(` · ${cat}: ${ids.join(\", \")}`);\n }\n // v26.103.0 (ADR-032) — header 와 동일 문구 (표면별 상이 문구 금지, v26.88.0 교훈).\n // 해제분을 빼고 센다 (ADR-074). 이 숫자는 이 저장소의 1차 지표(Context Cost per Install)이고,\n // 상주 비용을 줄이려고 항목을 푼 사용자에게 안 줄어든 숫자를 보이면 그 자체가 거짓 보고다.\n // #320 H1 — 헤더와 같은 이유로 **설치기와 같은 spec**(`buildManifestSpec`)으로 센다.\n const assetSpec = buildManifestSpec(spec);\n const cost = formatResidentCostLine(\n residentCost(\n buildManifest(assetSpec).filter(\n (e) =>\n e.applies(assetSpec) &&\n !isBaselineExcluded(e.target, baselineExcluded) &&\n landsOnDisk(e.target, spec.cli),\n ),\n ),\n summarizeContextCost(finalAssets).unmeasuredCount,\n );\n if (cost) lines.push(` · ${cost}`);\n }\n\n if (spec.userOverride) {\n if (spec.userOverride.forceInclude.length > 0) {\n lines.push(` +User added: ${spec.userOverride.forceInclude.join(\", \")}`);\n }\n if (spec.userOverride.forceExclude.length > 0) {\n lines.push(` -User removed: ${spec.userOverride.forceExclude.join(\", \")}`);\n }\n }\n // 트랙 baseline 해제분. 설치 화면과 **같은 문구**를 쓴다 (표면별 상이 문구 금지) — 다르면\n // 사용자가 확인 화면과 설치 결과를 대조할 수 없다.\n if (baselineExcluded.size > 0) {\n lines.push(\n ` -Excluded by you: ${baselineExcluded.size} — ${[...baselineExcluded]\n .map((id) => id.replace(BASELINE_PREFIX, \"\"))\n .join(\", \")}`,\n );\n }\n return lines.join(\"\\n\");\n}\n\n// v26.54.0 — Re-exports to keep test imports stable (test의 mock 구조 변경 없음)\nexport { EXTERNAL_ASSETS, VISIBLE_OPTION_DEFS };\n","// prompts.ts is a thin adapter over @clack/prompts. It deliberately contains\n// no transformation logic — interactive.ts owns the business rules. This file\n// is excluded from coverage in vitest.config.ts (justification at exclude line).\n\nimport {\n cancel,\n confirm,\n groupMultiselect,\n intro,\n isCancel,\n multiselect,\n outro,\n select,\n} from \"@clack/prompts\";\nimport {\n type BaselineKind,\n type BaselineTarget,\n listBaselineTargets,\n withBaselineHints,\n} from \"./baseline-targets.js\";\nimport { CATEGORIES, CATEGORY_TITLES, type Category } from \"./categories.js\";\nimport { CLI_BASE_SORT_ORDER } from \"./cli-targets.js\";\nimport { assetTrustTier, EXTERNAL_ASSETS } from \"./external-assets.js\";\nimport { buildRouterChoices, type RouterAction, summarizeState } from \"./router.js\";\nimport type { DetectedInstall } from \"./state.js\";\nimport {\n type CliBase,\n type CliTargets,\n type InstallScope,\n type OptionFlags,\n TRACKS,\n type Track,\n UPDATE_GROUPS,\n type UpdateGroup,\n} from \"./types.js\";\nimport { stepLabel, type WizardStep } from \"./wizard-steps.js\";\n\n/**\n * v26.54.0 — All-in-one install-targets value scheme.\n * groupMultiselect 의 단일 string value 에 두 source 통합:\n * - `option:<key>` → OPTION_DEFS 의 manifest-영향 build option (현재 빈 배열 — 아래 OPTION_DEFS 참조)\n * - `asset:<id>` → EXTERNAL_ASSETS 의 외부 자산\n * 다른 OptionFlags 항목 (withGsd, withEcc, withPrune, withTob,\n * withAddyAgentSkills, withSuperpowers, withWshobsonAgents, withOpenspec, withBmad) 은\n * EXTERNAL_ASSETS 에 1:1 자산이 있어\n * 자산 체크 → userOverride.forceInclude 로 처리. UI 중복 표시 없음.\n */\nexport type InstallTargetId = `option:${keyof OptionFlags}` | `asset:${string}`;\n\nexport interface Prompts {\n intro: (msg: string) => void;\n outro: (msg: string) => void;\n cancel: (msg: string) => void;\n\n /**\n * v26.65.0 — step optional 두 번째 인자. 호출자가 `WIZARD.TRACKS` 전달 시 message 에\n * \"Step N/M — Select Track(s)\" 형식 indicator 자동 삽입. 미전달 시 prefix 없이 raw label.\n */\n selectTracks: (initial?: Track[], step?: WizardStep) => Promise<Track[] | null>;\n /** v0.7.0 — single select → multiselect (3 base 체크박스). default `[\"claude\"]`. */\n selectCli: (initial?: CliTargets, step?: WizardStep) => Promise<CliTargets | null>;\n selectAction: (state: DetectedInstall) => Promise<RouterAction | null>;\n /**\n * v26.64.0 (ADR-020) — Installation scope 선택. Default = \"project\" (pre-selected).\n * Global 은 사용자 명시 opt-in. null = silent back.\n */\n selectScope: (initial?: InstallScope, step?: WizardStep) => Promise<InstallScope | null>;\n confirmInstall: (summary: string) => Promise<boolean | null>;\n /**\n * #480 — update 위저드의 \"무엇을 갱신할까\" 체크박스. 옵셔널: 없는 구현(테스트 픽스처)은 전부\n * 갱신으로 읽는다. null = 취소.\n */\n selectUpdateGroups?: () => Promise<UpdateGroup[] | null>;\n\n /**\n * v26.54.0 — Step 3 (all-in-one). EXTERNAL_ASSETS + 표시-대상 OPTION_DEFS 를\n * 카테고리 그룹화. 추천 ✓ pre-check. ESC → null (silent back).\n * v26.61.0 — recap (tracks/cli) 추가. alt screen 안에서 동작 — terminal scrollback\n * 본질 차단 → wizard scroll 시 cursor highlight 항상 visible.\n */\n selectInstallTargets: (\n initialChecked: ReadonlyArray<InstallTargetId>,\n step: { current: number; total: number },\n recap?: {\n tracks: ReadonlyArray<Track>;\n cli: CliTargets;\n /**\n * v26.125.0 — 이미 설치된 target id (표시 전용 마커). 체크를 풀어도 제거되지 않는다 —\n * 제거는 `agent-harness uninstall` 을 따로 실행한다 (사용자 결정 2026-07-19).\n */\n installed?: ReadonlyArray<string>;\n },\n ) => Promise<ReadonlyArray<InstallTargetId> | null>;\n}\n\nconst TRACK_LABELS: Record<Track, string> = {\n base: \"base — principles · methodology · tests only (no stack)\",\n tooling: \"tooling — Bash + Markdown meta-project\",\n \"csr-supabase\": \"csr-supabase — Vite + React + Supabase\",\n \"csr-fastify\": \"csr-fastify — Vite + React + Fastify\",\n \"csr-fastapi\": \"csr-fastapi — Vite + React + FastAPI\",\n \"ssr-htmx\": \"ssr-htmx — htmx + FastAPI\",\n \"ssr-nextjs\": \"ssr-nextjs — Next.js (App Router)\",\n data: \"data — Python data / DuckDB / PySide6\",\n executive: \"executive — proposals / DD / pitch (no agent-skills)\",\n full: \"full — all dev capabilities\",\n \"project-management\": \"project-management — PM / Scrum / Jira / Confluence\",\n \"growth-marketing\": \"growth-marketing — Growth / Marketing / Content\",\n};\n\ninterface OptionDef {\n key: keyof OptionFlags;\n label: string;\n hint: string;\n category: Category;\n source: string;\n}\n\n/**\n * v26.54.0 — 표시 대상 OPTION_DEFS.\n * v26.81.0 (ADR-022) — **빈 배열로 소멸**. 마지막 항목(withTauri)이 내부\n * 자산(tauri-desktop — EXTERNAL_ASSETS `kind:\"internal\"`)으로 흡수돼 wizard 의\n * `option:` 특례가 사라짐. 자산 선택은 전부 `asset:<id>` 경로. 잔존 동작 옵션(D16 글로벌\n * 4종/prune)은 wizard 미노출 (CLI `--with-*` 동작 플래그 전용 — 기존과 동일).\n */\nexport const VISIBLE_OPTION_DEFS: ReadonlyArray<OptionDef> = [];\n\nconst CLI_BASE_LABELS: Record<CliBase, string> = {\n claude: \"Claude Code\",\n codex: \"Codex (OpenAI)\",\n opencode: \"OpenCode (anomalyco)\",\n antigravity: \"Antigravity (Google)\",\n};\n\n/**\n * v26.78.1 — Step 3 wizard page layout (SSOT). 카테고리를 페이지로 묶어 clack\n * groupMultiselect 의 maxItems 한계(페이지당 옵션 ≤ ~30)를 우회.\n *\n * ⚠️ drift 가드 (no-false-ship): 모든 Category 는 정확히 한 페이지에 등장해야 한다.\n * 누락 시 해당 카테고리 자산이 wizard 에서 선택 불가가 되어 \"출하 거짓 광고\"가 된다\n * (v26.78.0 understanding 누락 회귀 — pages 가 selectInstallTargets 안에 하드코딩되어\n * CATEGORIES 추가와 drift). 아래 assertPagesCoverAllCategories 가 모듈 로드 시점에\n * 강제 — 신규 카테고리 미배치 시 즉시 throw. (tests/wizard-page-parity.test.ts 가 이중 가드)\n *\n * v26.99.0 — Dev 단일 페이지가 37행(옵션 32 + 헤더 5)으로 위 \"≤ ~30\" 제약을 스스로 위반하고\n * 있었다(실측). 터미널을 넘겨 스크롤이 생기는 것이 사용자가 보고한 \"선택 row 가 너무 많다\"의\n * 실제 메커니즘. → Dev 를 도메인(Core)과 도구(Tools)로 분할. 번들링(아래 DEV_METHOD_BUNDLE)만으론\n * 33행이라 부족했다 — Dev 32항목 중 dev-method 는 4개뿐이고, 번들 row 는 workflow 에 렌더되어\n * Dev 페이지에 남지 않으므로 37-4=33. ADR-028.\n *\n * 페이지 묶음 (전 페이지 ≤ 30 행):\n * Page 1: Dev Core — frontend + backend + data (20행)\n * Page 2: Dev Tools — dev-tools + understanding (13행)\n * Page 3: Business — business (documents) ( 9행)\n * Page 4: Visual&Media — visual-media (slides·diagrams·motion·video) (10행)\n * Page 5: Workflow/ECC — workflow(+ 방법론 번들 1행) + ecc-suite (10행)\n */\nexport interface InstallTargetPage {\n label: string;\n cats: ReadonlyArray<Category>;\n /**\n * 2026-08-16 — 이 페이지가 렌더하는 **트랙 baseline** 종류. 외부 자산 카테고리와 별도 축이다:\n * `cats` 는 `EXTERNAL_ASSETS` 를 보고, 이쪽은 `manifest.ts` 가 트랙에서 유도하는 설치 대상을\n * 본다. 한 축에 섞으면 `assertPagesCoverAllCategories` 의 전수 가드가 baseline 을 미배치\n * 카테고리로 오인한다.\n */\n baseline?: ReadonlyArray<BaselineKind>;\n}\n\nexport const INSTALL_TARGET_PAGES: ReadonlyArray<InstallTargetPage> = [\n // 2026-08-16 — 트랙이 고르는 자산을 **맨 앞에서** 전부 보여준다. 그전까지 이 항목들은 화면에\n // 한 번도 안 나온 채 깔렸고, 사용자는 설치가 끝난 뒤 요약에서 처음 봤다.\n //\n // 2페이지로 나눈 근거는 실측이다: 최대 트랙(`full`)이 32항목(당시 실측 — rules 6 · agents 9 · hooks 3 ·\n // skills 14)이라 clack groupMultiselect 의 \"페이지당 ≤ ~30행\" 제약을 한 페이지로는 못 지킨다.\n // 가르는 축은 역할이다 — 정책·가드가 한쪽, 실행 능력이 다른 쪽.\n { label: \"Track baseline — Rules & Hooks (정책·가드)\", cats: [], baseline: [\"rules\", \"hooks\"] },\n {\n label: \"Track baseline — Agents & Skills (에이전트·스킬)\",\n cats: [],\n baseline: [\"agents\", \"skills\"],\n },\n { label: \"Dev Core (Frontend · Backend · Data)\", cats: [\"frontend\", \"backend\", \"data\"] },\n { label: \"Dev Tools (Security · Quality · Understanding)\", cats: [\"dev-tools\", \"understanding\"] },\n { label: \"Business (PM · Executive · Documents)\", cats: [\"business\"] },\n // v26.85.0 — 코드-퍼스트 비주얼/미디어 제작 (용도별 섹션, 전부 opt-in).\n { label: \"Visual & Media (Slides · Diagrams · Motion · Video)\", cats: [\"visual-media\"] },\n { label: \"Workflow & ECC Suite\", cats: [\"workflow\", \"ecc-suite\"] },\n];\n\n/**\n * #421 ② (사용자 결정 2026-09-20, ADR-092 — ADR-028 ⓐ 대체) — 방법론 스킬은 **개별 행**으로 렌더한다.\n * 전에는 5종을 \"uzys 하네스 방법론 N종\" 한 행으로 접었는데(ADR-028), 하나만 빼고 싶어도 못 빼고\n * 각각의 쓸모가 다른데 설명이 한 줄뿐이었다. 다섯 전부 기본 체크(has-dev-track)는 그대로다.\n */\nexport interface PageItem {\n value: string;\n label: string;\n hint?: string;\n}\n\n/**\n * v26.99.0 (ADR-028) — Step 3 한 페이지의 group/item 구성. clack 의존 0 인 순수 함수.\n *\n * `selectInstallTargets` 안의 클로저였으나 export 로 승격했다 (SOD 리뷰 Important #2):\n * **이 함수가 dev-method 스킬 전원을 wizard 에서 도달 가능하게 만드는 유일한 코드**인데, 테스트가\n * 하나도 닿지 않았다. `wizard-page-parity` 는 \"자산의 **카테고리**가 페이지에 있다\"만 보는데,\n * 구성원은 자기 카테고리에서 필터링되고 번들 row 로만 대표되므로 그 단언은 이제 구성원에 대해\n * 비논리(non sequitur)다 — 번들 row 블록을 지워도 parity 는 통과하고 CI 는 green 인 채\n * 구성원이 **어디서도 선택 불가**가 된다. v26.78.0 거짓출하의 정확한 재현이다.\n * (`prompts.ts` 는 coverage 제외 대상이라 커버리지 수치도 이 코드에 대해 아무 보장을 못 준다.)\n */\nconst BASELINE_TITLES: Record<BaselineKind, string> = {\n rules: \"Rules — 상시 상주하는 대원칙 (4 CLI 전부에 설치)\",\n hooks: \"Hooks — 결정론적 가드 (해제하면 settings.json 배선도 함께 빠진다)\",\n agents: \"Agents — 독립 레인 (리뷰·검증)\",\n skills: \"Skills — 필요할 때 열리는 작업 절차\",\n};\n\nexport function buildPageGroups(\n cats: ReadonlyArray<Category>,\n initialSet: ReadonlySet<string>,\n /**\n * v26.125.0 — 이미 설치된 target id. 라벨에 `● installed` 마커만 붙인다.\n * **체크 상태와 별개다** — 체크는 \"이번에 설치할 것\", 마커는 \"이미 있는 것\"이고,\n * 체크를 풀어도 제거되지 않는다(제거는 `uninstall` 을 따로 실행).\n */\n installedSet: ReadonlySet<string> = new Set(),\n /**\n * 2026-08-16 — 이 페이지에 렌더할 트랙 baseline. 호출자가 트랙에서 유도해 넘긴다 —\n * 여기서 다시 고르면 화면과 설치가 서로 다른 목록을 보게 된다.\n */\n baselineKinds: ReadonlyArray<BaselineKind> = [],\n baselineTargets: ReadonlyArray<BaselineTarget> = [],\n): { groups: Record<string, PageItem[]>; flatItems: PageItem[] } {\n const installedMark = (value: string): string => (installedSet.has(value) ? \" ● installed\" : \"\");\n const groups: Record<string, PageItem[]> = {};\n const flatItems: PageItem[] = [];\n for (const kind of baselineKinds) {\n const items: PageItem[] = baselineTargets\n .filter((t) => t.kind === kind)\n .map((t) => ({\n value: t.id,\n label: ` ${t.name}${installedMark(t.id)}`,\n ...(t.hint ? { hint: t.hint } : {}),\n }));\n if (items.length === 0) continue;\n groups[BASELINE_TITLES[kind]] = items;\n flatItems.push(...items);\n }\n for (const cat of cats) {\n const items: PageItem[] = [];\n for (const o of VISIBLE_OPTION_DEFS.filter((d) => d.category === cat)) {\n items.push({\n value: `option:${o.key}`,\n // v26.62.3 — group header 와 옵션 사이 시각 hierarchy 강화. label prefix 4 space.\n label: ` ${o.label} [${o.source}]${installedMark(`option:${o.key}`)}`,\n hint: o.hint,\n });\n }\n // v26.71.0 (PRD v26-71) — tier 우선 정렬 (official → vetted → experimental) + 배지.\n const tierOrder = { official: 0, vetted: 1, experimental: 2 } as const;\n // #421 ② — 방법론 스킬도 개별 행(ADR-092). 카탈로그 description 이 그대로 hint 가 된다.\n const catAssets = [...EXTERNAL_ASSETS.filter((x) => x.category === cat)].sort(\n (a, b) => tierOrder[assetTrustTier(a.id)] - tierOrder[assetTrustTier(b.id)],\n );\n for (const a of catAssets) {\n const tier = assetTrustTier(a.id);\n const badge =\n tier === \"official\"\n ? \" ★ official\"\n : tier === \"experimental\"\n ? \" ⚠ experimental (opt-in)\"\n : \"\";\n items.push({\n value: `asset:${a.id}`,\n label: ` ${a.id} [${a.source}]${badge}${installedMark(`asset:${a.id}`)}`,\n hint: a.description,\n });\n }\n if (items.length === 0) continue;\n const selectedInCat = items.filter((it) => initialSet.has(it.value)).length;\n const header = `${CATEGORY_TITLES[cat]} [${selectedInCat}/${items.length} ✓ default]`;\n groups[header] = items;\n flatItems.push(...items);\n }\n return { groups, flatItems };\n}\n\n/**\n * 모든 Category 가 정확히 한 페이지에 등장하는지 모듈 로드 시 검증.\n * 누락(미배치) 또는 중복(2개 페이지)이면 throw — 값싼 fail-loud pre-flight.\n */\nfunction assertPagesCoverAllCategories(pages: ReadonlyArray<InstallTargetPage>): void {\n const counts = new Map<Category, number>();\n for (const page of pages) {\n for (const cat of page.cats) counts.set(cat, (counts.get(cat) ?? 0) + 1);\n }\n const missing = CATEGORIES.filter((c) => !counts.has(c));\n const duplicated = CATEGORIES.filter((c) => (counts.get(c) ?? 0) > 1);\n if (missing.length > 0 || duplicated.length > 0) {\n throw new Error(\n `INSTALL_TARGET_PAGES drift vs CATEGORIES — missing=[${missing.join(\", \")}] ` +\n `duplicated=[${duplicated.join(\", \")}]. 모든 카테고리는 정확히 한 페이지에 배치해야 한다 ` +\n `(no-false-ship: wizard 미노출 = 거짓 광고).`,\n );\n }\n}\n\nassertPagesCoverAllCategories(INSTALL_TARGET_PAGES);\n\n/**\n * v26.58.1 — Wizard viewport size. clack 의 limitOptions 가 maxItems 안에서만\n * cursor follow + ↕ ... indicator. 미지정 시 Infinity → terminal height 초과 시\n * 위 항목이 scrollback 으로 밀려 selected indicator 안 보임 (사용자 보고된 문제).\n *\n * rowPadding 10 = message line + status footer + safety margin.\n * floor 8 = 너무 작아도 최소한의 viewport 보장.\n */\nfunction viewportItems(itemCount: number): number {\n const rows = process.stdout.rows ?? 24;\n return Math.max(8, Math.min(itemCount, rows - 10));\n}\n\nexport const defaultPrompts: Prompts = {\n intro: (msg) => intro(msg),\n outro: (msg) => outro(msg),\n cancel: (msg) => cancel(msg),\n\n selectTracks: async (initial, step) => {\n // v26.65.0 — step indicator SSOT (wizard-steps.ts). 6-step 통합 (1 tracks · 2 cli · 3 targets · 4 scope · 5 confirm · 6 installing).\n const result = await multiselect({\n message: stepLabel(step, \"Select Track(s)\"),\n options: TRACKS.map((t) => ({ value: t, label: TRACK_LABELS[t] })),\n ...(initial ? { initialValues: initial } : {}),\n maxItems: viewportItems(11),\n required: true,\n });\n return isCancel(result) ? null : (result as Track[]);\n },\n\n selectCli: async (initial, step) => {\n const initialValues: CliBase[] = initial && initial.length > 0 ? [...initial] : [\"claude\"];\n const result = await multiselect({\n message: stepLabel(step, \"Target CLI(s)\"),\n options: [\n { value: \"claude\" as const, label: CLI_BASE_LABELS.claude },\n { value: \"codex\" as const, label: CLI_BASE_LABELS.codex },\n { value: \"opencode\" as const, label: CLI_BASE_LABELS.opencode },\n { value: \"antigravity\" as const, label: CLI_BASE_LABELS.antigravity },\n ],\n initialValues,\n required: true,\n });\n if (isCancel(result)) return null;\n return [...(result as CliBase[])].sort(\n (a, b) => CLI_BASE_SORT_ORDER[a] - CLI_BASE_SORT_ORDER[b],\n );\n },\n\n selectAction: async (state) => {\n const result = await select({\n message: summarizeState(state),\n options: buildRouterChoices(state).map((c) => {\n const label = c.enabled ? c.label : `${c.label} [disabled]`;\n // disabled:true → clack 이 cursor skip + strikethrough (선택 자체 차단).\n return {\n value: c.value,\n label,\n ...(c.hint ? { hint: c.hint } : {}),\n ...(c.enabled ? {} : { disabled: true }),\n };\n }),\n });\n return isCancel(result) ? null : (result as RouterAction);\n },\n\n /**\n * v26.64.0 (ADR-020) — Installation scope select. Default Project (D16 — no global write).\n * Global 은 사용자 명시 opt-in 시에만.\n */\n selectScope: async (initial = \"project\", step) => {\n const result = await select({\n message: stepLabel(step, \"Installation scope\"),\n initialValue: initial,\n options: [\n {\n value: \"project\",\n label: \"Project\",\n hint: \"Install in current directory (committed with your project)\",\n },\n {\n value: \"global\",\n label: \"Global\",\n hint: \"Write to ~/.claude/, ~/.codex/, npm -g (shared across all projects)\",\n },\n ],\n });\n return isCancel(result) ? null : (result as InstallScope);\n },\n\n selectUpdateGroups: async () => {\n const result = await multiselect({\n message: \"What to update (space = toggle · your edits are always backed up)\",\n options: [\n { value: \"skills\" as const, label: \"Skills\", hint: \"installed bundled skills → latest\" },\n {\n value: \"new-skills\" as const,\n label: \"New skills\",\n hint: \"bundled skills added since your install\",\n },\n {\n value: \"rules\" as const,\n label: \"Rules · agents · commands\",\n hint: \"your edited files → *.backup-<time>\",\n },\n {\n value: \"anchor\" as const,\n label: \"CLAUDE-uzys-harness.md anchor\",\n hint: \"your edited anchor → *.backup-<time>\",\n },\n {\n value: \"hooks\" as const,\n label: \"Hooks · settings.json\",\n hint: \"stale hook refs cleaned\",\n },\n {\n value: \"external\" as const,\n label: \"Codex / OpenCode / Antigravity + external skills\",\n hint: \"rendered artifacts + npx skills\",\n },\n ],\n initialValues: [...UPDATE_GROUPS],\n required: true,\n });\n return isCancel(result) ? null : (result as UpdateGroup[]);\n },\n\n confirmInstall: async (summary) => {\n const result = await confirm({\n message: `${summary}\\n\\nProceed?`,\n initialValue: true,\n });\n return isCancel(result) ? null : result;\n },\n\n selectInstallTargets: async (initialChecked, step, recap) => {\n // v26.62.2 — groupMultiselect 복귀 + page paginate.\n // v26.62.1 에서 multiselect + disabled separator 시도 → clack 가 disabled option 에\n // 체크박스 강제 + dim/strikethrough 효과 → 카테고리 헤더가 \"옵션 같지만 선택 불가\"\n // 처럼 보여 사용자 보고. groupMultiselect 의 group header 는 라이브러리에서 본래\n // 설명 라인 형태로 자연 표시 → 시각 명료.\n //\n // maxItems 한계 (GroupMultiSelectPrompt 미지원) 는 page 묶음으로 cover:\n // 한 page 안 옵션 ≤ ~30 → 사용자 iTerm2 (30+ rows) 환경에서 fit. 매우 작은 terminal\n // (< 25 rows) 한계는 follow-up.\n //\n // 페이지 정의 = 모듈 스코프 INSTALL_TARGET_PAGES (SSOT, 카테고리 전수 가드됨).\n const pages = INSTALL_TARGET_PAGES;\n // 트랙에서 유도한다 — 화면과 설치가 같은 목록을 보게 하는 유일한 방법이다.\n // #421 — 이름만으로는 체크를 풀지 말지 판단할 수 없다. 파일에서 뽑은 한 줄을 붙인다.\n const baselineTargets = withBaselineHints(listBaselineTargets({ tracks: recap?.tracks ?? [] }));\n const initialSet = new Set<string>(initialChecked);\n const collected = new Set<string>(initialChecked);\n\n const recapLine = recap\n ? `Tracks: ${recap.tracks.join(\", \")} · CLIs: ${recap.cli.join(\", \")}`\n : \"\";\n // v26.125.0 — 마커 전용. 체크 상태와 별개이며, 체크를 풀어도 제거되지 않는다.\n const installedSet = new Set<string>((recap?.installed ?? []) as ReadonlyArray<string>);\n\n // alt screen for the whole Step 3 loop. page 전환 시 buffer 안에서 redraw.\n process.stdout.write(\"\\x1b[?1049h\");\n let resultIds: ReadonlyArray<InstallTargetId> | null = null;\n let aborted = false;\n try {\n let pageIdx = 0;\n // 이동 방향. 빈 페이지 건너뛰기가 이걸 따라간다 — 아래 주석 참조.\n let dir: 1 | -1 = 1;\n while (pageIdx < pages.length) {\n const page = pages[pageIdx];\n if (!page) break;\n const { groups, flatItems } = buildPageGroups(\n page.cats,\n initialSet,\n installedSet,\n page.baseline ?? [],\n baselineTargets,\n );\n // 항목이 0개인 페이지는 건너뛴다 — 빈 화면에서 Enter 를 치게 만들 이유가 없다.\n //\n // **오늘 이 분기는 도달하지 않는다**(독립 리뷰 실측). `listBaselineTargets` 는 `tracks`\n // 만 보고 `spec.cli` 를 아예 안 받으므로 \"CLI 조합 때문에 비는 페이지\"는 없다 — codex\n // 단독 설치도 24행을 다 렌더한다. 실제 하한은 구조적이다: `COMMON_RULES`(3)·\n // `ALWAYS_HOOKS`(3)가 `applies: all` 이라 페이지 0 은 어떤 트랙에서도 6항목 아래로\n // 안 내려가고, 페이지 1 도 전 트랙 최소 9항목이다. 트랙이 자산을 다 잃으면 살아난다.\n //\n // **이동 방향을 따라간다.** 앞으로만 건너뛰면 빈 페이지가 일방통행 문이 된다 — 뒤에서\n // ESC 로 돌아온 사용자를 다시 앞으로 밀어내 Step 2 로 나갈 길이 사라진다.\n if (flatItems.length === 0) {\n pageIdx += dir;\n if (pageIdx < 0) {\n aborted = true; // 뒤로 건너뛰다 첫 페이지를 지났다 = Step 2 로\n break;\n }\n continue;\n }\n const selectedNow = flatItems.filter((it) => collected.has(it.value)).map((it) => it.value);\n const pageDefault = flatItems.filter((it) => initialSet.has(it.value)).length;\n const totalSelected = collected.size;\n const message = [\n `Step ${step.current}/${step.total} · Page ${pageIdx + 1}/${pages.length} · ${page.label}`,\n recapLine ? ` ${recapLine}` : \"\",\n ` Selected so far: ${totalSelected} items · This page default ✓ ${pageDefault}/${flatItems.length}`,\n // 마커의 뜻을 화면에서 바로 알려준다 — 체크 해제를 제거로 오해하는 것이 이 화면의\n // 원래 문제였으므로, 마커가 보일 때는 제거 경로를 같은 자리에서 말한다.\n installedSet.size > 0\n ? \" ● installed = 이미 설치됨 · 체크 해제해도 제거되지 않는다 (제거: agent-harness uninstall)\"\n : \"\",\n \" Space toggle · Enter → next · ESC → prev\",\n ]\n .filter(Boolean)\n .join(\"\\n\");\n\n const groupOpts = {\n message,\n options: groups,\n initialValues: selectedNow,\n required: false,\n selectableGroups: false,\n } as Parameters<typeof groupMultiselect>[0];\n const result = await groupMultiselect(groupOpts);\n if (isCancel(result)) {\n if (pageIdx === 0) {\n aborted = true; // first page ESC → Step 2 back\n break;\n }\n dir = -1;\n pageIdx--; // prev page\n continue;\n }\n // update collected: remove page items + add the new selection\n for (const it of flatItems) collected.delete(it.value);\n for (const v of result as ReadonlyArray<string>) collected.add(v);\n dir = 1;\n pageIdx++;\n }\n if (!aborted) {\n resultIds = [...collected] as ReadonlyArray<InstallTargetId>;\n }\n } finally {\n process.stdout.write(\"\\x1b[?1049l\");\n }\n // v26.63.1 — alt screen exit 후 main buffer 에 Step 3 완료 라인 출력. alt buffer\n // 안 동작은 main 에 흔적 0 → Step 1·2·4 사이 Step 3 missing → 사용자 보고.\n // clack `◇` marker + `│` line 으로 다른 step 과 시각 일관성 유지.\n if (resultIds !== null) {\n process.stdout.write(\n `◇ Step ${step.current}/${step.total} — Install targets · ${resultIds.length} selected\\n│\\n`,\n );\n }\n return resultIds;\n },\n};\n","import type { DetectedInstall } from \"./state.js\";\n\nexport type RouterAction = \"add\" | \"update\" | \"remove\" | \"reinstall\" | \"exit\";\n\nexport interface RouterChoice {\n value: RouterAction;\n label: string;\n hint?: string;\n enabled: boolean;\n}\n\n/**\n * 5-action menu for an existing install. Mirrors prompt_action_router (setup-harness.sh:255).\n *\n * \"remove\" is exposed but disabled — no reliable file-ownership mapping yet (would risk data loss).\n */\nexport function buildRouterChoices(state: DetectedInstall): RouterChoice[] {\n const detected = state.tracks.length > 0 ? state.tracks.join(\", \") : \"(none detected)\";\n return [\n {\n value: \"add\",\n label: \"Add a new Track\",\n hint: `Current: ${detected}`,\n enabled: true,\n },\n {\n value: \"update\",\n label: \"Update policy files (auto-backup)\",\n // v26.126.0 (R-3a) — skills 가 목록에 들어왔다. 이 문구가 곧 update 의 광고이고\n // (update 는 위저드로만 도달한다) 실동작과 어긋나면 그 자체로 거짓출하다.\n hint: \"Refresh rules / agents / commands / hooks / skills — your edits are backed up\",\n enabled: true,\n },\n {\n value: \"remove\",\n label: \"Remove a Track (unsupported)\",\n hint: \"Manual edit of .claude/ required — not automated\",\n enabled: false,\n },\n {\n value: \"reinstall\",\n label: \"Reinstall (backs up current .claude/ first)\",\n hint: \"Use when state is corrupted\",\n enabled: true,\n },\n {\n value: \"exit\",\n label: \"Exit\",\n enabled: true,\n },\n ];\n}\n\nexport function summarizeState(state: DetectedInstall): string {\n if (state.state === \"new\") {\n return \"No prior install detected — new install flow.\";\n }\n const trackList = state.tracks.length > 0 ? state.tracks.join(\", \") : \"(no tracks resolved)\";\n const sourceLabel =\n state.source === \"metafile\"\n ? \"via .claude/.installed-tracks\"\n : state.source === \"legacy\"\n ? \"via legacy rules/*.md heuristic\"\n : state.source === \"install-log\"\n ? \"via .uzys-agent-harness/ install log\"\n : \"via no source\";\n return `Existing install detected ${sourceLabel}. Tracks: ${trackList}.`;\n}\n","/**\n * Wizard step single source of truth — v26.65.0.\n *\n * v26.64.0 에서 wizard 가 5→6 step 으로 변경됐는데 prompts.ts 의 message 가 hardcoded\n * (\"Step 1/5\") 였음 → step indicator drift 가 무성. 본 모듈이 SSOT.\n *\n * 추가 step 도입 시 본 파일만 수정 → message 자동 정합.\n */\n\nexport interface WizardStep {\n current: number;\n total: number;\n}\n\nexport const WIZARD_TOTAL = 6;\n\nexport const WIZARD: {\n TRACKS: WizardStep;\n CLI: WizardStep;\n TARGETS: WizardStep;\n SCOPE: WizardStep;\n CONFIRM: WizardStep;\n INSTALL: WizardStep;\n} = {\n TRACKS: { current: 1, total: WIZARD_TOTAL },\n CLI: { current: 2, total: WIZARD_TOTAL },\n TARGETS: { current: 3, total: WIZARD_TOTAL },\n SCOPE: { current: 4, total: WIZARD_TOTAL },\n CONFIRM: { current: 5, total: WIZARD_TOTAL },\n INSTALL: { current: 6, total: WIZARD_TOTAL },\n};\n\n/**\n * Wizard step header — `Step N/M — <suffix>` 형식.\n *\n * step 미지정 시 suffix 만 반환 (backward compat — tests / non-wizard 호출).\n */\nexport function stepLabel(step: WizardStep | undefined, suffix: string): string {\n if (!step) return suffix;\n return `Step ${step.current}/${step.total} — ${suffix}`;\n}\n"],"mappings":"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","names":["ESC","CSI","k","j","v","b","cli","resolve","k","b","b","chmodSync","existsSync","mkdirSync","readdirSync","readFileSync","writeFileSync","basename","dirname","join","existsSync","join","v","join","existsSync","existsSync","readFileSync","join","existsSync","readFileSync","join","join","existsSync","readFileSync","existsSync","readFileSync","join","existsSync","readFileSync","existsSync","readFileSync","join","join","existsSync","readFileSync","join","existsSync","mkdirSync","readFileSync","writeFileSync","dirname","e","join","chmodSync","existsSync","readFileSync","basename","join","k","v","join","basename","existsSync","readFileSync","chmodSync","existsSync","readFileSync","basename","join","renameSlashes","renderAgentsMd","readRequired","join","basename","readOptionalJson","renderAgentsMd","existsSync","readFileSync","cli","existsSync","readFileSync","writeFileSync","join","sep","existsSync","readdirSync","readFileSync","homedir","join","b","cli","v","join","existsSync","c","at","homedir","readdirSync","readFileSync","existsSync","readFileSync","writeFileSync","readFileSync","existsSync","writeFileSync","existsSync","mkdirSync","readdirSync","readFileSync","unlinkSync","writeFileSync","basename","dirname","join","v","join","d","existsSync","readdirSync","e","basename","mkdirSync","dirname","b","unlinkSync","writeFileSync","readFileSync","isHarnessOwned","at","join","existsSync","readFileSync","foreign","e","mkdirSync","dirname","writeFileSync","basename","chmodSync","readdirSync","existsSync","readdirSync","join","e","join","existsSync","readdirSync","b","cli","v","k","p","w","resolve","e","cli","existsSync","readFileSync","join","resolve","resolve","d","join","existsSync","readFileSync","cli","spawnSync","existsSync","lstatSync","readdirSync","readFileSync","rmdirSync","writeFileSync","dirname","join","relative","resolve","sep","NO_TRUNCATION","dist_default","dist_default","findCursor","cursor","delta","options","opt","newCursor","maxCursor","clampedCursor","actions","DEFAULT_MONTH_NAMES","settings","days","month","min","max","isActionKey","key","action","settings","value","diffLines","a","b","aLines","bLines","numLines","diff","i","isWindows","CANCEL_SYMBOL","isCancel","setRawMode","input","getColumns","output","getRows","wrapTextWithPrefix","text","prefix","startPrefix","lineFormatter","columns","stdout","wrapAnsi","line","index","lineString","g$1","options","trackValue","input","stdin","render","signal","opts","event","params","cb","data","cbs","cleanup","subscriber","resolve","CANCEL_SYMBOL","readline","setRawMode","cursor","char","_key","_char","value","write","key","settings","problem","isActionKey","lines","frame","diff","diffLines","rows","diffOffsetAfter","diffOffsetBefore","diffLine","erase","adjustedDiffLine","newLines","ConfirmPrompt","Prompt","opts","confirm","cursor","GroupMultiSelectPrompt","Prompt","#selectableGroups","group","o","items","value","i","item","groupedItems","v","selected","opts","options","key","option","opt","currentIsGroup","Prompt","option","enabledOptions","allSelected","v","value","notSelected","selected","opts","cursor","findCursor","char","key","SelectPrompt","Prompt","opts","initialCursor","value","cursor","findCursor","key","isUnicodeSupported","process","unicode","unicodeOr","c","fallback","unicode","S_STEP_ACTIVE","S_STEP_CANCEL","S_STEP_ERROR","S_STEP_SUBMIT","S_BAR_START","S_BAR","S_BAR_END","S_BAR_START_RIGHT","S_BAR_END_RIGHT","S_RADIO_ACTIVE","S_RADIO_INACTIVE","S_CHECKBOX_ACTIVE","S_CHECKBOX_SELECTED","S_CHECKBOX_INACTIVE","S_PASSWORD_MASK","S_BAR_H","S_CORNER_TOP_RIGHT","S_CONNECT_LEFT","S_CORNER_BOTTOM_RIGHT","S_CORNER_BOTTOM_LEFT","S_CORNER_TOP_LEFT","S_INFO","S_SUCCESS","S_WARN","S_ERROR","symbol","state","styleText","symbolBar","trimLines","groups","initialLineCount","startIndex","endIndex","maxLines","lineCount","removals","i","group","limitOptions","cursor","options","style","output","maxItems","columnPadding","rowPadding","maxWidth","getColumns","rows","getRows","overflowFormat","outputMaxItems","computedMaxItems","slidingWindowLocation","shouldRenderTopEllipsis","shouldRenderBottomEllipsis","slidingWindowLocationEnd","lineGroups","slidingWindowLocationWithEllipsis","slidingWindowLocationEndWithEllipsis","wrappedLines","wrapAnsi","precedingRemovals","followingRemovals","newLineCount","cursorGroupIndex","trimLinesLocal","result","lineGroup","line","confirm","opts","active","inactive","ConfirmPrompt","hasGuide","settings","titlePrefix","symbol","titlePrefixBar","styleText","S_BAR","messageLines","wrapTextWithPrefix","title","value","submitPrefix","cancelPrefix","defaultPrefix","defaultPrefixEnd","S_BAR_END","S_RADIO_ACTIVE","S_RADIO_INACTIVE","groupMultiselect","opts","selectableGroups","groupSpacing","opt","option","state","options","label","isItem","next","isLast","prefix","S_BAR_END","S_BAR","spacingPrefix","spacingPrefixText","styleText","S_CHECKBOX_ACTIVE","S_CHECKBOX_SELECTED","selectedCheckbox","unselectedCheckbox","S_CHECKBOX_INACTIVE","required","GroupMultiSelectPrompt","selected","hasGuide","settings","title","symbol","value","selectedOptions","optionValue","optionsText","footer","ln","i","active","groupActive","optionText","optionsPrefix","cancel","message","opts","output","prefix","settings","styleText","S_BAR_END","intro","title","S_BAR_START","outro","S_BAR","computeLabel","label","format","line","multiselect","opts","opt","option","state","styleText","S_CHECKBOX_INACTIVE","str","S_CHECKBOX_ACTIVE","S_CHECKBOX_SELECTED","text","required","MultiSelectPrompt","selected","hasGuide","settings","wrappedMessage","wrapTextWithPrefix","symbolBar","symbol","title","S_BAR","value","styleOption","active","submitText","optionValue","wrappedSubmitText","wrappedLabel","prefix","footer","ln","i","S_BAR_END","titleLineCount","footerLineCount","limitOptions","S_PROGRESS_CHAR","unicodeOr","computeLabel","label","format","line","select","opts","opt","option","state","styleText","S_RADIO_INACTIVE","text","S_RADIO_ACTIVE","str","SelectPrompt","hasGuide","settings","titlePrefix","symbol","titlePrefixBar","symbolBar","messageLines","wrapTextWithPrefix","title","S_BAR","submitPrefix","wrappedLines","cancelPrefix","prefix","prefixEnd","S_BAR_END","titleLineCount","footerLineCount","limitOptions","item","active","prefix","styleText","S_BAR","ok","defaultSpawn","resolve","e","join","existsSync","lstatSync","readFileSync","dirname","relative","sep","readdirSync","rmdirSync","writeFileSync","spawnSync","cli","resolve","existsSync","readFileSync","join","join","existsSync","tracks","readFileSync","resolve","v","cli","d","b","it","c","v","cli","b","k","e","cli"]}