@uzysjung/agent-harness 26.158.0 → 26.160.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 CHANGED
@@ -5,6 +5,7 @@ import {
5
5
  CATEGORY_TITLES,
6
6
  CLI_BASES,
7
7
  CONTINUOUS_SKILLS,
8
+ CONTINUOUS_SKILLS_HEADING,
8
9
  DEFAULT_OPTIONS,
9
10
  EXTERNAL_ASSETS,
10
11
  HARNESS_ANCHOR_FILE,
@@ -57,6 +58,7 @@ import {
57
58
  listFilesRecursive,
58
59
  mergeExternalFiles,
59
60
  readInstallLog,
61
+ renderContinuousSkillsNote,
60
62
  renderFillScaffold,
61
63
  residentCost,
62
64
  resolveBundleRoot,
@@ -67,7 +69,7 @@ import {
67
69
  upsertHarnessImport,
68
70
  withContinuousSkillsNote,
69
71
  writeInstallLog
70
- } from "./chunk-HHCNPPGS.js";
72
+ } from "./chunk-ZXWOY5VO.js";
71
73
 
72
74
  // node_modules/sisteransi/src/index.js
73
75
  var require_src = __commonJS({
@@ -752,7 +754,7 @@ var cac = (name = "") => new CAC(name);
752
754
  // package.json
753
755
  var package_default = {
754
756
  name: "@uzysjung/agent-harness",
755
- version: "26.158.0",
757
+ version: "26.160.0",
756
758
  description: "Curate vetted AI-coding skills & plugins by your tech stack \u2014 install only what you need, across Claude Code, Codex, OpenCode & Antigravity",
757
759
  type: "module",
758
760
  publishConfig: {
@@ -767,7 +769,6 @@ var package_default = {
767
769
  files: [
768
770
  "dist",
769
771
  "templates",
770
- "scripts/prune-ecc.sh",
771
772
  "README.md",
772
773
  "LICENSE"
773
774
  ],
@@ -1085,11 +1086,137 @@ import { basename, join as join6 } from "path";
1085
1086
 
1086
1087
  // src/codex/agents-md.ts
1087
1088
  init_esm_shims();
1089
+
1090
+ // src/agents-md-merge.ts
1091
+ init_esm_shims();
1092
+ var marker = (name) => ({
1093
+ start: `<!-- uzys-harness:${name}:start -->`,
1094
+ end: `<!-- uzys-harness:${name}:end -->`
1095
+ });
1096
+ var ANCHOR_BLOCK = marker("anchor");
1097
+ var SKILLS_BLOCK = marker("skills");
1098
+ var CONTEXT_SECTION = "Project Context";
1099
+ var RULES_SECTION = "Project Rules";
1100
+ function wrapHarnessBlock(m, body) {
1101
+ return `${m.start}
1102
+ ${body}
1103
+ ${m.end}`;
1104
+ }
1105
+ function withMarkedContinuousSkillsNote(scaffold, selectedInternalSkills) {
1106
+ const note = renderContinuousSkillsNote(selectedInternalSkills);
1107
+ return note ? `${scaffold}
1108
+
1109
+ ${wrapHarnessBlock(SKILLS_BLOCK, note)}` : scaffold;
1110
+ }
1111
+ function sectionNames(template) {
1112
+ return new Set([...template.matchAll(/^## (.+)$/gm)].map((m) => (m[1] ?? "").trim()));
1113
+ }
1114
+ function headingName(line, names) {
1115
+ const name = /^## (.+?)\s*$/.exec(line.replace(/\r$/, ""))?.[1];
1116
+ return name !== void 0 && names.has(name) ? name : null;
1117
+ }
1118
+ function sectionBody(text, names, want) {
1119
+ const lines = text.split("\n");
1120
+ let start = -1;
1121
+ for (const [i, line] of lines.entries()) {
1122
+ const name = headingName(line, names);
1123
+ if (name === null) continue;
1124
+ if (start === -1) {
1125
+ if (name === want) start = i;
1126
+ } else {
1127
+ return lines.slice(start + 1, i);
1128
+ }
1129
+ }
1130
+ return start === -1 ? null : lines.slice(start + 1);
1131
+ }
1132
+ function assemble(rendered, names, bodies) {
1133
+ const out = [];
1134
+ let replacing = false;
1135
+ for (const line of rendered.split("\n")) {
1136
+ const name = headingName(line, names);
1137
+ if (name !== null) {
1138
+ out.push(line);
1139
+ const override = bodies.get(name);
1140
+ replacing = override !== void 0;
1141
+ if (override !== void 0) out.push(...override);
1142
+ continue;
1143
+ }
1144
+ if (!replacing) out.push(line);
1145
+ }
1146
+ return out.join("\n");
1147
+ }
1148
+ function blockRange(lines, m) {
1149
+ const start = lines.findIndex((l) => l.trim() === m.start);
1150
+ if (start === -1) return null;
1151
+ const end = lines.findIndex((l, i) => i > start && l.trim() === m.end);
1152
+ return end === -1 ? null : [start, end];
1153
+ }
1154
+ function trimTrailingBlanks(lines) {
1155
+ const out = [...lines];
1156
+ while (out.length > 0 && (out.at(-1) ?? "").trim() === "") out.pop();
1157
+ return out;
1158
+ }
1159
+ function refreshBlock(existing, rendered, m, legacyHeading) {
1160
+ const at2 = blockRange(rendered, m);
1161
+ const fresh = at2 === null ? null : rendered.slice(at2[0], at2[1] + 1);
1162
+ const here = blockRange(existing, m);
1163
+ if (here !== null) {
1164
+ const head2 = existing.slice(0, here[0]);
1165
+ const tail2 = existing.slice(here[1] + 1);
1166
+ if (fresh === null) {
1167
+ const kept = trimTrailingBlanks(head2);
1168
+ return tail2.length > 0 ? [...kept, ...tail2] : [...kept, ""];
1169
+ }
1170
+ return [...head2, ...fresh, ...tail2];
1171
+ }
1172
+ const legacyAt = legacyHeading === void 0 ? -1 : existing.findIndex((l) => l.replace(/\r$/, "").trim() === legacyHeading);
1173
+ if (legacyAt === -1) {
1174
+ const kept = trimTrailingBlanks(existing);
1175
+ return fresh === null ? [...kept, ""] : [...kept, "", ...fresh, ""];
1176
+ }
1177
+ const head = trimTrailingBlanks(existing.slice(0, legacyAt));
1178
+ const tail = existing.slice(legacyNoteEnd(existing, legacyAt));
1179
+ while (tail.length > 0 && (tail[0] ?? "").trim() === "") tail.shift();
1180
+ const body = fresh === null ? [...head] : [...head, "", ...fresh];
1181
+ return tail.length > 0 ? [...body, "", ...tail] : [...body, ""];
1182
+ }
1183
+ function legacyNoteEnd(lines, headingAt) {
1184
+ let i = headingAt + 1;
1185
+ while (i < lines.length) {
1186
+ const l = (lines[i] ?? "").replace(/\r$/, "").trim();
1187
+ const isNote = l === "" || l.startsWith("- `") || l.startsWith("These installed skills apply");
1188
+ if (!isNote) break;
1189
+ i++;
1190
+ }
1191
+ return i;
1192
+ }
1193
+ function mergeAgentsMd(params) {
1194
+ const { rendered, existing, template } = params;
1195
+ if (existing === null) return rendered;
1196
+ const names = sectionNames(template);
1197
+ const bodies = /* @__PURE__ */ new Map();
1198
+ const contextNow = sectionBody(existing, names, CONTEXT_SECTION);
1199
+ const contextNew = sectionBody(rendered, names, CONTEXT_SECTION);
1200
+ if (contextNow !== null && contextNew !== null) {
1201
+ bodies.set(
1202
+ CONTEXT_SECTION,
1203
+ refreshBlock(contextNow, contextNew, SKILLS_BLOCK, CONTINUOUS_SKILLS_HEADING)
1204
+ );
1205
+ }
1206
+ const rulesNow = sectionBody(existing, names, RULES_SECTION);
1207
+ const rulesNew = sectionBody(rendered, names, RULES_SECTION);
1208
+ if (rulesNow !== null && rulesNew !== null && blockRange(rulesNow, ANCHOR_BLOCK) !== null) {
1209
+ bodies.set(RULES_SECTION, refreshBlock(rulesNow, rulesNew, ANCHOR_BLOCK));
1210
+ }
1211
+ return bodies.size === 0 ? rendered : assemble(rendered, names, bodies);
1212
+ }
1213
+
1214
+ // src/codex/agents-md.ts
1088
1215
  function renameSlashes(text) {
1089
1216
  return text.replaceAll("/uzys:", "/uzys-");
1090
1217
  }
1091
1218
  function renderAgentsMd(params) {
1092
- const body = params.claudeMd.replace(/^#\s+.*\r?\n/, "").trim();
1219
+ const body = wrapHarnessBlock(ANCHOR_BLOCK, params.claudeMd.replace(/^#\s+.*\r?\n/, "").trim());
1093
1220
  const replaced = params.template.replaceAll("{PROJECT_NAME}", params.projectName).replaceAll("{PROJECT_RULES}", body).replaceAll("{PROJECT_CONTEXT}", params.projectContext).replaceAll("{HARNESS_RULES}", params.harnessRules ?? "");
1094
1221
  return renameSlashes(replaced);
1095
1222
  }
@@ -1313,7 +1440,8 @@ function writeRules(harnessRoot, projectDir, writer, selectedInternalSkills) {
1313
1440
  selectedInternalSkills
1314
1441
  )
1315
1442
  });
1316
- return writer.write(target, rulesOut) ? target : null;
1443
+ const withoutMarkers = rulesOut.split("\n").filter((l) => !/^<!-- uzys-harness:(?:anchor|skills):(?:start|end) -->$/.test(l.trim())).join("\n");
1444
+ return writer.write(target, withoutMarkers) ? target : null;
1317
1445
  }
1318
1446
 
1319
1447
  // src/codex/opt-in.ts
@@ -1458,14 +1586,22 @@ function runCodexTransform(params) {
1458
1586
  claudeMd,
1459
1587
  projectName,
1460
1588
  // ADR-085 — 상시 스킬 안내는 앵커가 아니라 여기(프로젝트 맥락)에, 깔린 것만.
1461
- projectContext: withContinuousSkillsNote(
1589
+ // #503 — 그 조각은 설치자 소유 절 안에 사니 마커로 감싼다.
1590
+ projectContext: withMarkedContinuousSkillsNote(
1462
1591
  renderFillScaffold("agents-md"),
1463
1592
  selectedInternalSkills
1464
1593
  ),
1465
1594
  // Codex 는 룰 디렉터리가 없다 — 룰이 AGENTS.md 본문에 들어가야 도달한다(§Harness Rules).
1466
1595
  harnessRules: renderRulesBlock(portRules(harnessRoot, rules))
1467
1596
  });
1468
- writer.write(agentsMdPath, agentsMdOut);
1597
+ writer.write(
1598
+ agentsMdPath,
1599
+ mergeAgentsMd({
1600
+ rendered: agentsMdOut,
1601
+ existing: existsSync8(agentsMdPath) ? readFileSync7(agentsMdPath, "utf8") : null,
1602
+ template: agentsTemplate
1603
+ })
1604
+ );
1469
1605
  const configTomlPath = join8(projectDir, ".codex/config.toml");
1470
1606
  writer.write(
1471
1607
  configTomlPath,
@@ -1531,7 +1667,7 @@ function renameSlashes2(text) {
1531
1667
  return text.replaceAll("/uzys:", "/uzys-");
1532
1668
  }
1533
1669
  function renderAgentsMd2(params) {
1534
- const body = params.claudeMd.replace(/^#\s+.*\r?\n/, "").trim();
1670
+ const body = wrapHarnessBlock(ANCHOR_BLOCK, params.claudeMd.replace(/^#\s+.*\r?\n/, "").trim());
1535
1671
  const replaced = params.template.replaceAll("{PROJECT_NAME}", params.projectName).replaceAll("{PROJECT_RULES}", body).replaceAll("{PROJECT_CONTEXT}", params.projectContext).replaceAll("{HARNESS_RULES}", params.harnessRules ?? "");
1536
1672
  return renameSlashes2(replaced);
1537
1673
  }
@@ -1580,7 +1716,8 @@ function runOpencodeTransform(params) {
1580
1716
  claudeMd,
1581
1717
  projectName,
1582
1718
  // ADR-085 — 상시 스킬 안내는 앵커가 아니라 여기(프로젝트 맥락)에, 깔린 것만.
1583
- projectContext: withContinuousSkillsNote(
1719
+ // #503 — 그 조각은 설치자 소유 절 안에 사니 마커로 감싼다 (codex 와 같은 마커).
1720
+ projectContext: withMarkedContinuousSkillsNote(
1584
1721
  renderFillScaffold("agents-md"),
1585
1722
  selectedInternalSkills
1586
1723
  ),
@@ -1589,7 +1726,14 @@ function runOpencodeTransform(params) {
1589
1726
  // (독립 검증 C-1 실측). 같은 내용을 쓰면 순서가 결과를 바꾸지 않는다.
1590
1727
  harnessRules: renderRulesBlock(portRules(harnessRoot, rules))
1591
1728
  });
1592
- writer.write(agentsMdPath, agentsMdOut);
1729
+ writer.write(
1730
+ agentsMdPath,
1731
+ mergeAgentsMd({
1732
+ rendered: agentsMdOut,
1733
+ existing: existsSync9(agentsMdPath) ? readFileSync8(agentsMdPath, "utf8") : null,
1734
+ template: agentsTemplate
1735
+ })
1736
+ );
1593
1737
  const opencodeJsonPath = join9(projectDir, "opencode.json");
1594
1738
  writer.write(opencodeJsonPath, renderOpencodeJson({ template: opencodeTemplate, mcp }));
1595
1739
  const skillFiles = writeBundledSkillDirs({
@@ -1834,7 +1978,6 @@ function runExternalInstall(ctx, deps = {}) {
1834
1978
  const warn = deps.warn ?? console.error;
1835
1979
  const spawn = deps.spawn ?? defaultSpawn;
1836
1980
  const assets = deps.assets ?? EXTERNAL_ASSETS;
1837
- const harnessRoot = deps.harnessRoot ?? process.cwd();
1838
1981
  const projectDir = ctx.projectDir ?? process.cwd();
1839
1982
  const { targets: applicable, excludedByCli } = selectExternalTargets(assets, ctx);
1840
1983
  const sorted = [...applicable].sort((a, b2) => {
@@ -1848,7 +1991,7 @@ function runExternalInstall(ctx, deps = {}) {
1848
1991
  for (const asset of sorted) {
1849
1992
  deps.onAssetStart?.(asset);
1850
1993
  log(` \u2192 ${asset.description}`);
1851
- const baseResult = installOne(asset, { spawn, harnessRoot, cli: cli2, scope, projectDir });
1994
+ const baseResult = installOne(asset, { spawn, cli: cli2, scope, projectDir });
1852
1995
  let result = baseResult;
1853
1996
  if (baseResult.ok) {
1854
1997
  const v2 = detectVersion(asset.method, spawn);
@@ -1893,17 +2036,6 @@ function installOne(asset, ctx) {
1893
2036
  [`${method.cmd}@${method.version}`, ...method.args ?? []],
1894
2037
  cwd
1895
2038
  );
1896
- case "shell-script": {
1897
- const scriptPath = join11(ctx.harnessRoot, method.script);
1898
- if (!existsSync11(scriptPath)) {
1899
- return {
1900
- asset,
1901
- ok: false,
1902
- message: `script not found: ${scriptPath}`
1903
- };
1904
- }
1905
- return runSpawn(asset, ctx.spawn, "bash", [scriptPath, ...method.args], cwd);
1906
- }
1907
2039
  case "internal":
1908
2040
  return { asset, ok: true, message: "internal template (installed by Phase 1 manifest)" };
1909
2041
  }
@@ -2379,11 +2511,13 @@ function installNewSkillDirs(projectDir, templatesDir, tracks) {
2379
2511
  const foreignOwned = [];
2380
2512
  const log = readInstallLog(projectDir);
2381
2513
  const excluded = new Set(log?.spec.baselineExclude ?? []);
2514
+ const skillExcluded = new Set(log?.spec.skillExclude ?? []);
2382
2515
  if (!(log?.spec.cli ?? ["claude"]).includes("claude")) return { installed, foreignOwned };
2383
2516
  const spec = buildAssetSpec({ tracks, options: DEFAULT_OPTIONS });
2384
2517
  for (const entry of buildManifest(spec)) {
2385
2518
  if (entry.type !== "dir" || !entry.target.startsWith(".claude/skills/")) continue;
2386
2519
  if (!entry.applies(spec) || isBaselineExcluded(entry.target, excluded)) continue;
2520
+ if (skillExcluded.has(entry.target.slice(".claude/skills/".length))) continue;
2387
2521
  const target = join12(projectDir, entry.target);
2388
2522
  const foreign = foreignOwnedTarget(projectDir, entry.target);
2389
2523
  if (foreign !== null) {
@@ -2413,8 +2547,8 @@ function recordNewSkillBaseline(projectDir, templatesDir, targets) {
2413
2547
  }
2414
2548
  function trackOnlyFileAssets(tracks) {
2415
2549
  const base = { tracks, selectedInternalSkills: [] };
2416
- const optedOut = { ...base, withEcc: false, withTauri: false };
2417
- const optedIn = { ...base, withEcc: true, withTauri: true };
2550
+ const optedOut = { ...base, withTauri: false };
2551
+ const optedIn = { ...base, withTauri: true };
2418
2552
  return buildManifest(optedOut).filter(
2419
2553
  (e2) => e2.type === "file" && e2.applies(optedOut) && e2.applies(optedIn)
2420
2554
  );
@@ -2486,6 +2620,11 @@ function recordAnchorBaseline(projectDir, anchor) {
2486
2620
  } catch {
2487
2621
  }
2488
2622
  }
2623
+ function installedBundledSkills(projectDir) {
2624
+ return INTERNAL_BUNDLED_SKILL_IDS.filter(
2625
+ (id) => existsSync13(join12(projectDir, ".claude/skills", id)) || existsSync13(join12(projectDir, ".agents/skills", id))
2626
+ );
2627
+ }
2489
2628
  function refreshExternalCli(projectDir, harnessRoot) {
2490
2629
  const log = readInstallLog(projectDir);
2491
2630
  const baselineExcluded = new Set(log?.spec.baselineExclude ?? []);
@@ -2493,8 +2632,11 @@ function refreshExternalCli(projectDir, harnessRoot) {
2493
2632
  harnessRoot,
2494
2633
  projectDir,
2495
2634
  cli: ALL_CLI_TARGETS,
2496
- selectedInternalSkills: INTERNAL_BUNDLED_SKILL_IDS,
2497
- // 스킬은 **전부** 넘긴다 — 안 깔린 스킬은 대상 파일이 없어 refreshOnly 가 건너뛴다.
2635
+ // 스킬은 **디스크에 있는 것만** 넘긴다. 스킬 *파일*은 전체 목록을 넘겨도 refreshOnly 가
2636
+ // 없는 것을 건너뛰지만, `AGENTS.md` 의 상시 스킬 안내(ADR-085)는 이 목록 그대로 렌더돼
2637
+ // `--without` 으로 뺀 스킬(#505)·opt-in 스킬을 "열어라"고 적었다(실측 2026-09-21). 판정은
2638
+ // `upsertRootImport` 와 같다 — `.claude/skills/<id>` 또는 `.agents/skills/<id>` 가 있으면 깔린 것.
2639
+ selectedInternalSkills: installedBundledSkills(projectDir),
2498
2640
  rules: ALL_RULES.filter((r) => !isBaselineExcluded(`.claude/rules/${r}.md`, baselineExcluded)),
2499
2641
  previousExternal: log?.externalFiles ?? [],
2500
2642
  refreshOnly: true
@@ -2993,10 +3135,9 @@ function runExternalPhase(ctx) {
2993
3135
  if (ctx.runExternal === null) {
2994
3136
  return null;
2995
3137
  }
2996
- const { harnessRoot, projectDir, spec } = ctx;
3138
+ const { projectDir, spec } = ctx;
2997
3139
  const runExt = ctx.runExternal ?? runExternalInstall;
2998
3140
  const externalDeps = {
2999
- harnessRoot,
3000
3141
  log: () => {
3001
3142
  },
3002
3143
  warn: () => {
@@ -3231,9 +3372,7 @@ function createInstallRenderer(log, spec, verbose) {
3231
3372
  const callbacks = {
3232
3373
  onProgress: (event) => {
3233
3374
  if (event.type === "baseline-complete") {
3234
- const claudeSelected = targetsInclude(spec.cli, "claude");
3235
- const eccWillInstall = claudeSelected && (isAssetSelected("ecc-plugin", spec) || spec.options.withPrune === true);
3236
- renderPhase1Rows(log, event.baseline, verbose, eccWillInstall, claudeSelected);
3375
+ renderPhase1Rows(log, event.baseline, verbose);
3237
3376
  } else if (event.type === "external-start" && event.assetCount > 0) {
3238
3377
  log(unifiedSection(`External assets (${event.assetCount})`));
3239
3378
  log("");
@@ -3494,13 +3633,11 @@ function formatAssetMeta(asset, version) {
3494
3633
  return `npm \xB7 ${m.pkg}@${m.version}`;
3495
3634
  case "npx-run":
3496
3635
  return `npx \xB7 ${m.cmd}@${m.version}`;
3497
- case "shell-script":
3498
- return `bash \xB7 ${m.script}`;
3499
3636
  case "internal":
3500
3637
  return `internal \xB7 templates (${m.key})`;
3501
3638
  }
3502
3639
  }
3503
- function renderPhase1Rows(log, baseline, verbose = false, withEcc = false, claudeSelected = true) {
3640
+ function renderPhase1Rows(log, baseline, verbose = false) {
3504
3641
  if (baseline.updateMode) {
3505
3642
  if (baseline.backup) {
3506
3643
  log(assetRow("success", "backup", shortenPath(baseline.backup)));
@@ -3841,13 +3978,6 @@ function renderPhase1Rows(log, baseline, verbose = false, withEcc = false, claud
3841
3978
  }
3842
3979
  const mcpList = baseline.mcpServers.join(", ") || "(none)";
3843
3980
  log(assetRow("success", ".mcp.json", mcpList, TEMPLATES_COL));
3844
- if (!withEcc && baseline.categories) {
3845
- log("");
3846
- log(` ${c.dim("\xB7")} ${c.dim("ECC plugin not selected \u2014 cherry-pick fallback active")}`);
3847
- if (claudeSelected) {
3848
- log(` ${c.dim("\xB7")} ${c.dim("Use --with ecc-plugin to install ECC plugin instead")}`);
3849
- }
3850
- }
3851
3981
  if (baseline.envFiles.envExampleCreated) {
3852
3982
  log(assetRow("success", ".env.example", "Supabase token guide"));
3853
3983
  }
@@ -3981,7 +4111,6 @@ function installAction(options, deps = {}) {
3981
4111
  // v26.81.0 (ADR-022, BREAKING) — 자산 1:1 boolean 13종 삭제. 자산 선택은 위
3982
4112
  // userOverride(--with <id>)로 일원화. 잔존 = 설치 동작 옵션만.
3983
4113
  options: {
3984
- withPrune: options.withPrune === true,
3985
4114
  withCodexTrust: options.withCodexTrust === true
3986
4115
  },
3987
4116
  cli: validated.cli,
@@ -4077,9 +4206,6 @@ function registerInstallCommand(cli2) {
4077
4206
  ).option(
4078
4207
  "--with-codex-trust",
4079
4208
  "[Codex] Codex global opt-in: register trust entry in ~/.codex/config.toml"
4080
- ).option(
4081
- "--with-prune",
4082
- "[Behavior] Prune ECC items beyond curated 89 (use with --with ecc-plugin)"
4083
4209
  ).option("--verbose", "[Misc] Show installed file lists per category (default: counts only)").example("install --track tooling --with marketingskills").example("install --track csr-supabase --cli claude --cli codex").example("install --track csr-supabase --without netlify-cli --with railway-skills").action((options) => installAction(options));
4084
4210
  }
4085
4211
 
@@ -4136,10 +4262,10 @@ function formatAssetRows(assets) {
4136
4262
  const idWidth = Math.max(0, ...assets.map((a) => a.id.length));
4137
4263
  const methodWidth = Math.max(0, ...assets.map((a) => a.method.length));
4138
4264
  return assets.map((a) => {
4139
- const marker = a.scope === "global" ? c.yellow("!") : c.green("\u2713");
4265
+ const marker2 = a.scope === "global" ? c.yellow("!") : c.green("\u2713");
4140
4266
  const version = a.version ? c.dim(` v${a.version}`) : "";
4141
4267
  const note = a.scope === "global" ? c.yellow(" (manual removal \u2014 D16)") : "";
4142
- return ` ${marker} ${padDisplay(a.id, idWidth)} ${c.dim(padDisplay(a.method, methodWidth))} ${c.dim(a.scope)}${version}${note}`;
4268
+ return ` ${marker2} ${padDisplay(a.id, idWidth)} ${c.dim(padDisplay(a.method, methodWidth))} ${c.dim(a.scope)}${version}${note}`;
4143
4269
  });
4144
4270
  }
4145
4271
  function formatTemplateRows(log, projectDir) {
@@ -5177,6 +5303,7 @@ function hintFor(asset) {
5177
5303
  case "npm":
5178
5304
  return "npm uninstall --save-dev";
5179
5305
  case "npx-run":
5306
+ // shell-script = #492 에서 은퇴한 legacy kind. 옛 로그가 그대로 들어온다.
5180
5307
  case "shell-script":
5181
5308
  case "internal":
5182
5309
  return "\uC790\uB3D9 \uB418\uB3CC\uB9AC\uAE30 \uACBD\uB85C \uC5C6\uC74C \u2014 \uC804\uB7C9 \uC81C\uAC70(`.claude/` \uC0AD\uC81C)\uB85C\uB9CC \uC0AC\uB77C\uC9C4\uB2E4";
@@ -5294,7 +5421,7 @@ function uninstallAction(options, deps = {}) {
5294
5421
  const selectedIds = parseOnly(options.only);
5295
5422
  if (options.only !== void 0 && selectedIds === null) {
5296
5423
  err(status.failure(c.red("ERROR: --only \uC5D0 \uC790\uC0B0 id \uAC00 \uC5C6\uB2E4")));
5297
- err(c.dim(" \uC608: --only code-review (id \uB294 `agent-harness list` \uC5D0\uC11C \uD655\uC778)"));
5424
+ err(c.dim(" \uC608: --only openspec (id \uB294 `agent-harness list` \uC5D0\uC11C \uD655\uC778)"));
5298
5425
  exit(1);
5299
5426
  return;
5300
5427
  }
@@ -5950,7 +6077,7 @@ var INSTALL_TARGET_PAGES = [
5950
6077
  { label: "Business (PM \xB7 Executive \xB7 Documents)", cats: ["business"] },
5951
6078
  // v26.85.0 — 코드-퍼스트 비주얼/미디어 제작 (용도별 섹션, 전부 opt-in).
5952
6079
  { label: "Visual & Media (Slides \xB7 Diagrams \xB7 Motion \xB7 Video)", cats: ["visual-media"] },
5953
- { label: "Workflow & ECC Suite", cats: ["workflow", "ecc-suite"] }
6080
+ { label: "Workflow", cats: ["workflow"] }
5954
6081
  ];
5955
6082
  var BASELINE_TITLES = {
5956
6083
  rules: "Rules \u2014 \uC0C1\uC2DC \uC0C1\uC8FC\uD558\uB294 \uB300\uC6D0\uCE59 (4 CLI \uC804\uBD80\uC5D0 \uC124\uCE58)",
@@ -6241,7 +6368,6 @@ function baselineExcludeFrom(offered, checked) {
6241
6368
  function toOptionFlags(keys) {
6242
6369
  const picked = new Set(keys);
6243
6370
  return {
6244
- withPrune: picked.has("withPrune"),
6245
6371
  withCodexTrust: picked.has("withCodexTrust")
6246
6372
  };
6247
6373
  }