oh-my-customcode 1.1.106 → 2.0.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/cli/index.js CHANGED
@@ -335,7 +335,7 @@ var init_package = __esm(() => {
335
335
  workspaces: [
336
336
  "packages/*"
337
337
  ],
338
- version: "1.1.106",
338
+ version: "2.0.0",
339
339
  description: "Batteries-included agent harness for Claude Code",
340
340
  type: "module",
341
341
  bin: {
@@ -14556,8 +14556,6 @@ var en_default = {
14556
14556
  selfUpdateReexec: "↻ Re-executing with updated CLI (v{{version}})...",
14557
14557
  reexecArgvMissing: "⚠ Cannot re-exec: process.argv[1] is empty — continuing without re-exec",
14558
14558
  allProjectSkippedSource: " ℹ Skipped: {{name}} (source project cannot self-update)",
14559
- rtkMissing: "[RTK] RTK is not installed. Attempting installation...",
14560
- rtkInstalled: "[RTK] ✓ RTK installed — 60-90% token savings activated",
14561
14559
  codexMissing: "[Codex] Codex CLI is not installed. Attempting installation...",
14562
14560
  codexInstalled: "[Codex] ✓ Codex CLI installed"
14563
14561
  },
@@ -14660,10 +14658,6 @@ var en_default = {
14660
14658
  pass: "Framework is up to date (v{{version}})",
14661
14659
  warn: "Framework is outdated: installed v{{installed}}, latest v{{latest}} ({{behind}} version(s) behind). Run 'omcustom update' to sync."
14662
14660
  },
14663
- rtk: {
14664
- pass: "RTK installed",
14665
- warn: "RTK not installed — token savings unavailable"
14666
- },
14667
14661
  codex: {
14668
14662
  pass: "Codex CLI installed",
14669
14663
  warn: "Codex CLI not installed — AI-assisted development unavailable"
@@ -14736,10 +14730,6 @@ var en_default = {
14736
14730
  }
14737
14731
  },
14738
14732
  install: {
14739
- rtk_installing: "Installing RTK...",
14740
- rtk_success: "RTK installed successfully",
14741
- rtk_already: "RTK already installed",
14742
- rtk_install_failed: "RTK installation failed",
14743
14733
  codex_installing: "Installing Codex CLI...",
14744
14734
  codex_success: "Codex CLI installed successfully",
14745
14735
  codex_already: "Codex CLI already installed",
@@ -14980,8 +14970,6 @@ var ko_default = {
14980
14970
  selfUpdateReexec: "↻ 새 CLI(v{{version}})로 재실행 중...",
14981
14971
  reexecArgvMissing: "⚠ 재실행 불가: process.argv[1]이 비어있습니다 — 재실행 없이 계속합니다",
14982
14972
  allProjectSkippedSource: " ℹ 스킵: {{name}} (소스 프로젝트는 자체 업데이트할 수 없음)",
14983
- rtkMissing: "[RTK] RTK가 설치되지 않았습니다. 설치를 시도합니다...",
14984
- rtkInstalled: "[RTK] ✓ RTK 설치 완료 — 토큰 60-90% 절감 활성화",
14985
14973
  codexMissing: "[Codex] Codex CLI가 설치되지 않았습니다. 설치를 시도합니다...",
14986
14974
  codexInstalled: "[Codex] ✓ Codex CLI 설치 완료"
14987
14975
  },
@@ -15084,10 +15072,6 @@ var ko_default = {
15084
15072
  pass: "프레임워크가 최신 상태입니다 (v{{version}})",
15085
15073
  warn: "프레임워크가 구버전입니다: 설치됨 v{{installed}}, 최신 v{{latest}} ({{behind}}개 버전 뒤처짐). 'omcustom update'를 실행하여 동기화하세요."
15086
15074
  },
15087
- rtk: {
15088
- pass: "RTK 설치됨",
15089
- warn: "RTK 미설치 — 토큰 절감 불가"
15090
- },
15091
15075
  codex: {
15092
15076
  pass: "Codex CLI 설치됨",
15093
15077
  warn: "Codex CLI 미설치 — AI 개발 지원 불가"
@@ -15160,10 +15144,6 @@ var ko_default = {
15160
15144
  }
15161
15145
  },
15162
15146
  install: {
15163
- rtk_installing: "RTK 설치 중...",
15164
- rtk_success: "RTK 설치 완료",
15165
- rtk_already: "RTK 이미 설치됨",
15166
- rtk_install_failed: "RTK 설치 실패",
15167
15147
  codex_installing: "Codex CLI 설치 중...",
15168
15148
  codex_success: "Codex CLI 설치 완료",
15169
15149
  codex_already: "Codex CLI 이미 설치됨",
@@ -15834,18 +15814,10 @@ var MESSAGES = {
15834
15814
  "codex.installing_npm": "Installing Codex CLI via npm...",
15835
15815
  "codex.install_failed": "Failed to install Codex CLI: {{error}}",
15836
15816
  "codex.unsupported_os": "Codex CLI auto-install is not supported on {{os}}",
15837
- "rtk.already_installed": "RTK is already installed",
15838
- "rtk.installing_brew": "Installing RTK via Homebrew...",
15839
- "rtk.installing_curl": "Installing RTK via install script...",
15840
- "rtk.install_failed": "Failed to install RTK: {{error}}",
15841
- "rtk.unsupported_os": "RTK auto-install is not supported on {{os}}",
15842
15817
  "mcp.ontology_rag_configured": "ontology-rag MCP server configured successfully",
15843
15818
  "install.codex_installing": "Installing Codex CLI...",
15844
15819
  "install.codex_success": "Codex CLI installed successfully",
15845
15820
  "install.codex_already": "Codex CLI is already installed",
15846
- "install.rtk_installing": "Installing RTK...",
15847
- "install.rtk_success": "RTK installed successfully",
15848
- "install.rtk_already": "RTK is already installed",
15849
15821
  "install.preserved": "Preserved {{files}} file(s) and {{dirs}} directory(ies) from the existing installation",
15850
15822
  "install.restored": "Restored {{files}} file(s) and {{dirs}} directory(ies) after installation",
15851
15823
  "install.backup_failed": "Failed to back up {{path}}: {{error}}",
@@ -15857,7 +15829,6 @@ var MESSAGES = {
15857
15829
  "preserve.restore_failed": "Failed to restore file {{file}}: {{error}}",
15858
15830
  "preserve_files.invalid_path": "Ignored invalid preserve_files entry from {{source}}: {{path}} ({{reason}})",
15859
15831
  "update.codex_missing": "Codex CLI is not installed",
15860
- "update.rtk_missing": "RTK is not installed",
15861
15832
  "update.self_update_skipped": "Skipped update: source project cannot update itself",
15862
15833
  "update.deprecated_file_invalid_path": "Skipped deprecated file with invalid path: {{path}} ({{reason}})",
15863
15834
  "update.deprecated_file_removed": "Removed deprecated file: {{path}} ({{reason}})",
@@ -15956,18 +15927,10 @@ var MESSAGES = {
15956
15927
  "codex.installing_npm": "npm으로 Codex CLI를 설치합니다...",
15957
15928
  "codex.install_failed": "Codex CLI 설치에 실패했습니다: {{error}}",
15958
15929
  "codex.unsupported_os": "{{os}}에서는 Codex CLI 자동 설치를 지원하지 않습니다",
15959
- "rtk.already_installed": "RTK가 이미 설치되어 있습니다",
15960
- "rtk.installing_brew": "Homebrew로 RTK를 설치합니다...",
15961
- "rtk.installing_curl": "설치 스크립트로 RTK를 설치합니다...",
15962
- "rtk.install_failed": "RTK 설치에 실패했습니다: {{error}}",
15963
- "rtk.unsupported_os": "{{os}}에서는 RTK 자동 설치를 지원하지 않습니다",
15964
15930
  "mcp.ontology_rag_configured": "ontology-rag MCP 서버 구성을 완료했습니다",
15965
15931
  "install.codex_installing": "Codex CLI를 설치합니다...",
15966
15932
  "install.codex_success": "Codex CLI를 설치했습니다",
15967
15933
  "install.codex_already": "Codex CLI가 이미 설치되어 있습니다",
15968
- "install.rtk_installing": "RTK를 설치합니다...",
15969
- "install.rtk_success": "RTK를 설치했습니다",
15970
- "install.rtk_already": "RTK가 이미 설치되어 있습니다",
15971
15934
  "install.preserved": "기존 설치에서 파일 {{files}}개와 디렉토리 {{dirs}}개를 보존했습니다",
15972
15935
  "install.restored": "설치 후 파일 {{files}}개와 디렉토리 {{dirs}}개를 복원했습니다",
15973
15936
  "install.backup_failed": "{{path}} 백업에 실패했습니다: {{error}}",
@@ -15979,7 +15942,6 @@ var MESSAGES = {
15979
15942
  "preserve.restore_failed": "파일 {{file}} 복원에 실패했습니다: {{error}}",
15980
15943
  "preserve_files.invalid_path": "{{source}}의 유효하지 않은 preserve_files 항목을 무시했습니다: {{path}} ({{reason}})",
15981
15944
  "update.codex_missing": "Codex CLI가 설치되어 있지 않습니다",
15982
- "update.rtk_missing": "RTK가 설치되어 있지 않습니다",
15983
15945
  "update.self_update_skipped": "업데이트를 건너뜁니다: 소스 프로젝트는 스스로 업데이트할 수 없습니다",
15984
15946
  "update.deprecated_file_invalid_path": "경로가 유효하지 않아 지원 중단 파일을 건너뜁니다: {{path}} ({{reason}})",
15985
15947
  "update.deprecated_file_removed": "지원 중단 파일을 삭제했습니다: {{path}} ({{reason}})",
@@ -16570,8 +16532,23 @@ function sortFilesByPath(files) {
16570
16532
  const sorted = Object.keys(files).sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
16571
16533
  return Object.fromEntries(sorted.map((key) => [key, files[key]]));
16572
16534
  }
16535
+ var RETIRED_RTK_PATHS = [
16536
+ ".claude/skills/rtk-exec/SKILL.md",
16537
+ ".claude/skills/rtk-exec/scripts/rtk-wrapper.cjs",
16538
+ ".claude/hooks/scripts/rtk-intercept.sh"
16539
+ ];
16540
+ function getRetiredPaths(options) {
16541
+ const retiredPaths = new Set([...RETIRED_RTK_PATHS, ...options.retiredPaths ?? []]);
16542
+ for (const path of retiredPaths) {
16543
+ if (typeof path !== "string" || /[\\:*?[\]{}\0]/.test(path) || path.split("/").some((segment) => segment === "" || segment === "." || segment === "..")) {
16544
+ throw new Error("retiredPaths must contain exact canonical relative file paths");
16545
+ }
16546
+ }
16547
+ return retiredPaths;
16548
+ }
16573
16549
  async function generateLockfile(targetDir, generatorVersion, templateVersion, options = {}) {
16574
16550
  const files = {};
16551
+ const retiredPaths = getRetiredPaths(options);
16575
16552
  const gitVisible = options.excludeGitIgnored ? await listGitVisiblePaths(targetDir) : null;
16576
16553
  const componentRoots = COMPONENT_PATHS.map(([prefix]) => join7(targetDir, prefix));
16577
16554
  for (const componentRoot of componentRoots) {
@@ -16580,7 +16557,7 @@ async function generateLockfile(targetDir, generatorVersion, templateVersion, op
16580
16557
  debug("lockfile.component_dir_missing", { path: componentRoot });
16581
16558
  continue;
16582
16559
  }
16583
- const allFiles = await collectFiles(componentRoot, targetDir, false);
16560
+ const allFiles = (await collectFiles(componentRoot, targetDir, false)).filter((absolutePath) => !retiredPaths.has(relative2(targetDir, absolutePath).replace(/\\/g, "/")));
16584
16561
  for (const absolutePath of allFiles) {
16585
16562
  const relativePath = relative2(targetDir, absolutePath).replace(/\\/g, "/");
16586
16563
  if (gitVisible !== null && !isGitVisible(gitVisible, relativePath)) {
@@ -16686,72 +16663,6 @@ function diffLockfiles(base, current) {
16686
16663
  return { added, removed, modified, unchanged };
16687
16664
  }
16688
16665
 
16689
- // src/core/rtk-installer.ts
16690
- import { execSync as execSync4 } from "node:child_process";
16691
- import { platform as platform2 } from "node:os";
16692
- var defaultDeps2 = {
16693
- exec: execSync4,
16694
- getPlatform: platform2
16695
- };
16696
- function isRtkInstalled(deps = defaultDeps2) {
16697
- try {
16698
- deps.exec("which rtk", { stdio: "pipe", timeout: 3000 });
16699
- return true;
16700
- } catch {
16701
- return false;
16702
- }
16703
- }
16704
- function getRtkVersion(deps = defaultDeps2) {
16705
- try {
16706
- return deps.exec("rtk --version", { encoding: "utf-8", stdio: "pipe", timeout: 3000 }).trim();
16707
- } catch {
16708
- return null;
16709
- }
16710
- }
16711
- function installRtk(deps = defaultDeps2) {
16712
- if (process.env.CI || false || false) {
16713
- return false;
16714
- }
16715
- if (isRtkInstalled(deps)) {
16716
- info("rtk.already_installed");
16717
- return true;
16718
- }
16719
- const os = deps.getPlatform();
16720
- try {
16721
- if (os === "darwin") {
16722
- try {
16723
- info("rtk.installing_brew");
16724
- deps.exec("brew install rtk-ai/tap/rtk", {
16725
- stdio: "inherit",
16726
- timeout: 120000
16727
- });
16728
- return true;
16729
- } catch {
16730
- info("rtk.installing_curl");
16731
- deps.exec("curl -fsSL https://raw.githubusercontent.com/rtk-ai/rtk/refs/heads/master/install.sh | sh", {
16732
- stdio: "inherit",
16733
- timeout: 120000
16734
- });
16735
- return isRtkInstalled(deps);
16736
- }
16737
- } else if (os === "linux") {
16738
- info("rtk.installing_curl");
16739
- deps.exec("curl -fsSL https://raw.githubusercontent.com/rtk-ai/rtk/refs/heads/master/install.sh | sh", {
16740
- stdio: "inherit",
16741
- timeout: 120000
16742
- });
16743
- return isRtkInstalled(deps);
16744
- } else {
16745
- warn("rtk.unsupported_os", { os });
16746
- return false;
16747
- }
16748
- } catch (err) {
16749
- const message = err instanceof Error ? err.message : String(err);
16750
- warn("rtk.install_failed", { error: message });
16751
- return false;
16752
- }
16753
- }
16754
-
16755
16666
  // src/cli/doctor.ts
16756
16667
  async function pathExists(targetPath) {
16757
16668
  try {
@@ -17051,23 +16962,6 @@ async function checkHooks(targetDir, rootDir = ".claude") {
17051
16962
  fixable: false
17052
16963
  };
17053
16964
  }
17054
- async function checkRtk() {
17055
- if (!isRtkInstalled()) {
17056
- return {
17057
- name: "RTK",
17058
- status: "warn",
17059
- message: "RTK not installed — token savings unavailable (brew install rtk-ai/tap/rtk)",
17060
- fixable: true
17061
- };
17062
- }
17063
- const version = getRtkVersion();
17064
- return {
17065
- name: "RTK",
17066
- status: "pass",
17067
- message: `RTK OK (${version ?? "unknown version"})`,
17068
- fixable: false
17069
- };
17070
- }
17071
16965
  async function checkCodex() {
17072
16966
  if (!isCodexInstalled()) {
17073
16967
  return {
@@ -17188,7 +17082,6 @@ async function fixSingleIssue(check, targetDir, rootDir = ".claude") {
17188
17082
  const fixedCount = await fixBrokenSymlinks(targetDir, fullPaths);
17189
17083
  return fixedCount > 0;
17190
17084
  },
17191
- RTK: async () => Promise.resolve(installRtk()),
17192
17085
  Codex: async () => Promise.resolve(installCodex())
17193
17086
  };
17194
17087
  const fixer = fixMap[check.name];
@@ -17338,7 +17231,6 @@ async function runAllChecks(targetDir, layout, packageVersion, includeUpdates) {
17338
17231
  checkHooks(targetDir, layout.rootDir),
17339
17232
  checkContexts(targetDir, layout.rootDir),
17340
17233
  checkCustomComponents(targetDir, layout.rootDir),
17341
- checkRtk(),
17342
17234
  checkCodex()
17343
17235
  ]);
17344
17236
  const frameworkCheck = await checkFrameworkDrift(targetDir, packageVersion);
@@ -17427,6 +17319,7 @@ var LEGACY_OMCUSTOM_DESCRIPTIONS = [
17427
17319
  ];
17428
17320
  var STANDALONE_SCRIPT_COMMAND = /^(?:bash )?(?:"(?:\$\{CLAUDE_PROJECT_DIR:-\.\}\/|\.\/)?\.claude\/hooks\/((?:scripts\/)?[A-Za-z0-9._-]+\.sh)"|(?:\.\/)?\.claude\/hooks\/((?:scripts\/)?[A-Za-z0-9._-]+\.sh))$/;
17429
17321
  var SCRIPT_REFERENCE = /\.claude\/hooks\/((?:scripts\/)?[A-Za-z0-9._-]+\.sh)/g;
17322
+ var RETIRED_RTK_DESCRIPTION = "RTK auto-intercept — transparently rewrites CLI commands through RTK proxy when available (R013 advisory)";
17430
17323
  function matcherKey(group) {
17431
17324
  return typeof group.matcher === "string" ? group.matcher : "";
17432
17325
  }
@@ -17488,15 +17381,52 @@ function isOwnedCommand(hook, owned) {
17488
17381
  const script = match?.[1] ?? match?.[2];
17489
17382
  return script !== undefined && owned.scripts.has(script);
17490
17383
  }
17491
- function userResidual(group, owned, ownership) {
17384
+ function isRetiredRtkCommand(hook, event, matcher) {
17385
+ if (event !== "PreToolUse" || matcher !== "Bash" || !isRecord(hook) || hook.type !== "command" || typeof hook.command !== "string") {
17386
+ return false;
17387
+ }
17388
+ const match = STANDALONE_SCRIPT_COMMAND.exec(hook.command);
17389
+ return (match?.[1] ?? match?.[2]) === "scripts/rtk-intercept.sh";
17390
+ }
17391
+ function retireRtkGroup(group, event) {
17492
17392
  if (!isRecord(group) || !Array.isArray(group.hooks)) {
17493
17393
  return [group];
17494
17394
  }
17395
+ const remaining = group.hooks.filter((hook) => !isRetiredRtkCommand(hook, event, matcherKey(group)));
17396
+ if (remaining.length === group.hooks.length) {
17397
+ return [group];
17398
+ }
17399
+ return remaining.length === 0 ? [] : [{ ...group, hooks: remaining }];
17400
+ }
17401
+ function retireRtkHookBlocks(existing) {
17402
+ if (!isRecord(existing)) {
17403
+ return existing;
17404
+ }
17405
+ const updates = {};
17406
+ for (const [event, value] of Object.entries(existing)) {
17407
+ if (!Array.isArray(value)) {
17408
+ continue;
17409
+ }
17410
+ const residual = value.flatMap((group) => retireRtkGroup(group, event));
17411
+ if (residual.length !== value.length || residual.some((group, index) => group !== value[index])) {
17412
+ updates[event] = residual;
17413
+ }
17414
+ }
17415
+ return Object.keys(updates).length === 0 ? existing : { ...existing, ...updates };
17416
+ }
17417
+ function userResidual(group, event, owned, ownership) {
17418
+ if (!isRecord(group) || !Array.isArray(group.hooks)) {
17419
+ return [group];
17420
+ }
17421
+ const matcher = matcherKey(group);
17422
+ if (group.description === RETIRED_RTK_DESCRIPTION) {
17423
+ return retireRtkGroup(group, event);
17424
+ }
17495
17425
  if (typeof group.description === "string" && ownership.descriptions.has(group.description)) {
17496
17426
  return [];
17497
17427
  }
17498
- const ownedForMatcher = owned?.get(matcherKey(group));
17499
- const remaining = group.hooks.filter((hook) => !isOwnedCommand(hook, ownedForMatcher));
17428
+ const ownedForMatcher = owned?.get(matcher);
17429
+ const remaining = group.hooks.filter((hook) => !isRetiredRtkCommand(hook, event, matcher) && !isOwnedCommand(hook, ownedForMatcher));
17500
17430
  if (remaining.length === group.hooks.length) {
17501
17431
  return [group];
17502
17432
  }
@@ -17519,7 +17449,7 @@ function mergeHookBlocks(existing, generated) {
17519
17449
  continue;
17520
17450
  }
17521
17451
  const owned = ownership.events.get(event);
17522
- const residual = value.flatMap((group) => userResidual(group, owned, ownership));
17452
+ const residual = value.flatMap((group) => userResidual(group, event, owned, ownership));
17523
17453
  if (residual.length === 0) {
17524
17454
  continue;
17525
17455
  }
@@ -18790,19 +18720,6 @@ async function updateInstallConfig(targetDir, options, installedComponents) {
18790
18720
  config.installedComponents = installedComponents;
18791
18721
  await saveConfig(targetDir, config);
18792
18722
  }
18793
- function installRtkIfNeeded(result) {
18794
- if (!isRtkInstalled()) {
18795
- info("install.rtk_installing");
18796
- const rtkInstalled = installRtk();
18797
- if (rtkInstalled) {
18798
- info("install.rtk_success");
18799
- } else {
18800
- result.warnings.push("RTK installation failed — install manually: brew install rtk-ai/tap/rtk");
18801
- }
18802
- } else {
18803
- info("install.rtk_already");
18804
- }
18805
- }
18806
18723
  function installCodexIfNeeded(result) {
18807
18724
  if (!isCodexInstalled()) {
18808
18725
  info("install.codex_installing");
@@ -18857,7 +18774,6 @@ async function install(options) {
18857
18774
  } else {
18858
18775
  info("install.lockfile_generated", { files: String(lockfileResult.fileCount) });
18859
18776
  }
18860
- installRtkIfNeeded(result);
18861
18777
  installCodexIfNeeded(result);
18862
18778
  result.success = true;
18863
18779
  success("install.success");
@@ -19089,7 +19005,7 @@ async function generateMCPConfig(targetDir) {
19089
19005
 
19090
19006
  // src/core/ontology-rag-setup.ts
19091
19007
  init_fs();
19092
- import { execSync as execSync5 } from "node:child_process";
19008
+ import { execSync as execSync4 } from "node:child_process";
19093
19009
  import { join as join11 } from "node:path";
19094
19010
  var MIN_PYTHON_MINOR = 10;
19095
19011
  var DETECTION_TIMEOUT_MS = 3000;
@@ -19102,7 +19018,7 @@ function parsePythonVersion(output) {
19102
19018
  }
19103
19019
  function checkPython3() {
19104
19020
  try {
19105
- const output = execSync5("python3 --version 2>&1", {
19021
+ const output = execSync4("python3 --version 2>&1", {
19106
19022
  stdio: "pipe",
19107
19023
  timeout: DETECTION_TIMEOUT_MS
19108
19024
  }).toString().trim();
@@ -19119,21 +19035,21 @@ function checkPython3() {
19119
19035
  }
19120
19036
  function checkUvAvailableForSetup() {
19121
19037
  try {
19122
- execSync5("uv --version", { stdio: "pipe", timeout: DETECTION_TIMEOUT_MS });
19038
+ execSync4("uv --version", { stdio: "pipe", timeout: DETECTION_TIMEOUT_MS });
19123
19039
  return true;
19124
19040
  } catch {
19125
19041
  return false;
19126
19042
  }
19127
19043
  }
19128
19044
  function createVenvWithUv(targetDir) {
19129
- execSync5("uv venv --python 3.12 .venv", {
19045
+ execSync4("uv venv --python 3.12 .venv", {
19130
19046
  cwd: targetDir,
19131
19047
  stdio: "pipe",
19132
19048
  timeout: INSTALL_TIMEOUT_MS
19133
19049
  });
19134
19050
  }
19135
19051
  function createVenvWithPython3(targetDir) {
19136
- execSync5("python3 -m venv .venv", {
19052
+ execSync4("python3 -m venv .venv", {
19137
19053
  cwd: targetDir,
19138
19054
  stdio: "pipe",
19139
19055
  timeout: INSTALL_TIMEOUT_MS
@@ -19143,13 +19059,13 @@ function installOntologyRagEditable(targetDir, useUv) {
19143
19059
  const packageRoot = getPackageRoot();
19144
19060
  const ontologyPkgPath = join11(packageRoot, "packages", "ontology-rag");
19145
19061
  if (useUv) {
19146
- execSync5(`uv pip install --python .venv/bin/python -e "${ontologyPkgPath}"`, {
19062
+ execSync4(`uv pip install --python .venv/bin/python -e "${ontologyPkgPath}"`, {
19147
19063
  cwd: targetDir,
19148
19064
  stdio: "pipe",
19149
19065
  timeout: INSTALL_TIMEOUT_MS
19150
19066
  });
19151
19067
  } else {
19152
- execSync5(`.venv/bin/pip install -e "${ontologyPkgPath}"`, {
19068
+ execSync4(`.venv/bin/pip install -e "${ontologyPkgPath}"`, {
19153
19069
  cwd: targetDir,
19154
19070
  stdio: "pipe",
19155
19071
  timeout: INSTALL_TIMEOUT_MS
@@ -21742,7 +21658,8 @@ import { constants as osConstants } from "node:os";
21742
21658
 
21743
21659
  // src/core/updater.ts
21744
21660
  init_package();
21745
- import { join as join19 } from "node:path";
21661
+ import { lstat, realpath as realpath2, unlink as unlink2 } from "node:fs/promises";
21662
+ import { isAbsolute as isAbsolute5, join as join19, relative as relative4, resolve as resolve5, sep as sep4 } from "node:path";
21746
21663
  init_fs();
21747
21664
 
21748
21665
  // src/core/entry-merger.ts
@@ -21869,6 +21786,86 @@ ${MANAGED_END}`;
21869
21786
 
21870
21787
  // src/core/updater.ts
21871
21788
  var CUSTOMIZATION_MANIFEST_FILE = ".omcustom-customizations.json";
21789
+ var RETIRED_RTK_FILES = [
21790
+ { path: ".claude/skills/rtk-exec/SKILL.md", component: "skills" },
21791
+ { path: ".claude/skills/rtk-exec/scripts/rtk-wrapper.cjs", component: "skills" },
21792
+ { path: ".claude/hooks/scripts/rtk-intercept.sh", component: "hooks" }
21793
+ ];
21794
+ function getRtkRetirement(result) {
21795
+ result.rtkRetirement ??= {
21796
+ removed: [],
21797
+ wouldRemove: [],
21798
+ preserved: [],
21799
+ settingsChanged: [],
21800
+ wouldChangeSettings: [],
21801
+ settingsConflicts: []
21802
+ };
21803
+ return result.rtkRetirement;
21804
+ }
21805
+ async function validateRtkTarget(targetDir, path) {
21806
+ const validation = validatePreserveFilePath(path, targetDir);
21807
+ if (!validation.valid)
21808
+ throw new Error("path escapes project");
21809
+ const fullPath = resolve5(targetDir, path);
21810
+ const fileStat = await lstat(fullPath);
21811
+ if (!fileStat.isFile() || fileStat.isSymbolicLink())
21812
+ throw new Error("not a regular owned file");
21813
+ const root = await realpath2(targetDir);
21814
+ const resolvedFile = await realpath2(fullPath);
21815
+ const within = relative4(root, resolvedFile);
21816
+ if (within === "" || within === ".." || within.startsWith(`..${sep4}`) || isAbsolute5(within)) {
21817
+ throw new Error("resolved path escapes project");
21818
+ }
21819
+ }
21820
+ function isRtkPathPreserved(path, config, customizations) {
21821
+ const paths = [
21822
+ ...config.preserveFiles ?? [],
21823
+ ...customizations?.preserveFiles ?? [],
21824
+ ...customizations?.modifiedFiles ?? [],
21825
+ ...(config.customComponents ?? []).map((component) => component.path)
21826
+ ];
21827
+ return paths.some((preserved) => path === preserved || path.startsWith(`${preserved}/`));
21828
+ }
21829
+ async function retireRtkFile(path, options, lockfile, result, preserved) {
21830
+ const retirement = getRtkRetirement(result);
21831
+ try {
21832
+ await validateRtkTarget(options.targetDir, path);
21833
+ if (preserved)
21834
+ throw new Error("explicitly preserved");
21835
+ if (retirement.settingsConflicts.length > 0)
21836
+ throw new Error("settings retirement conflict");
21837
+ const entry = lockfile?.files[path];
21838
+ if (!entry || typeof entry.templateHash !== "string" || !/^[a-f0-9]{64}$/.test(entry.templateHash)) {
21839
+ throw new Error("missing or invalid old ownership");
21840
+ }
21841
+ const fullPath = join19(options.targetDir, path);
21842
+ if (await computeFileHash(fullPath) !== entry.templateHash)
21843
+ throw new Error("user-modified file");
21844
+ if (options.dryRun) {
21845
+ retirement.wouldRemove.push(path);
21846
+ return;
21847
+ }
21848
+ await validateRtkTarget(options.targetDir, path);
21849
+ await unlink2(fullPath);
21850
+ retirement.removed.push(path);
21851
+ result.removedDeprecatedFiles.push(path);
21852
+ } catch (err) {
21853
+ if (err.code === "ENOENT")
21854
+ return;
21855
+ const reason = err instanceof Error ? err.message : "unreadable ownership or file";
21856
+ retirement.preserved.push({ path, reason });
21857
+ result.preservedFiles.push(path);
21858
+ result.warnings.push(`RTK retirement preserved ${path}: ${reason}`);
21859
+ }
21860
+ }
21861
+ async function retireRtkFiles(options, lockfile, result, config, customizations) {
21862
+ const fullUpdate = !options.components || options.components.length === 0;
21863
+ const files = RETIRED_RTK_FILES.filter((file) => fullUpdate || options.components?.includes(file.component));
21864
+ for (const file of files) {
21865
+ await retireRtkFile(file.path, options, lockfile, result, isRtkPathPreserved(file.path, config, customizations));
21866
+ }
21867
+ return RETIRED_RTK_FILES.map((file) => file.path);
21868
+ }
21872
21869
  function createUpdateResult() {
21873
21870
  return {
21874
21871
  success: false,
@@ -22056,32 +22053,52 @@ async function backfillStatusLineRefreshInterval(targetDir, options) {
22056
22053
  });
22057
22054
  }
22058
22055
  }
22059
- async function migrateHookCommandsInSettingsLocal(targetDir, options) {
22060
- if (options.dryRun) {
22061
- return;
22056
+ async function migrateHookCommandsInSettingsLocal(targetDir, options, result) {
22057
+ for (const name of ["settings.json", "settings.local.json"]) {
22058
+ await migrateRtkSettingsFile(targetDir, options, result, name);
22062
22059
  }
22060
+ }
22061
+ async function migrateRtkSettingsFile(targetDir, options, result, name) {
22063
22062
  const layout = getProviderLayout();
22064
- const settingsPath = join19(targetDir, layout.rootDir, "settings.local.json");
22065
- if (!await fileExists(settingsPath)) {
22066
- return;
22067
- }
22063
+ const path = `${layout.rootDir}/${name}`;
22064
+ const settingsPath = join19(targetDir, path);
22065
+ const retirement = getRtkRetirement(result);
22068
22066
  try {
22067
+ await validateRtkTarget(targetDir, path);
22069
22068
  const raw = await readTextFile(settingsPath);
22070
22069
  const bom = raw.startsWith("\uFEFF") ? "\uFEFF" : "";
22071
22070
  const parsed = JSON.parse(bom ? raw.slice(bom.length) : raw);
22072
22071
  if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
22072
+ throw new Error("unknown settings shape");
22073
+ }
22074
+ const current = parsed;
22075
+ const hooks = retireRtkHookBlocks(current.hooks);
22076
+ const retired = hooks !== current.hooks;
22077
+ const input = retired ? { ...current, hooks } : current;
22078
+ const { settings, rewritten } = name === "settings.local.json" ? migrateHookCommands(input) : { settings: input, rewritten: 0 };
22079
+ if (JSON.stringify(settings.hooks)?.includes("rtk-intercept.sh")) {
22080
+ retirement.settingsConflicts.push(path);
22081
+ result.warnings.push(`RTK retirement retained user or unknown wiring in ${path}`);
22082
+ }
22083
+ if (!retired && rewritten === 0) {
22073
22084
  return;
22074
22085
  }
22075
- const { settings, rewritten } = migrateHookCommands(parsed);
22076
- if (rewritten === 0) {
22086
+ if (options.dryRun) {
22087
+ retirement.wouldChangeSettings.push(path);
22077
22088
  return;
22078
22089
  }
22079
22090
  const trailingNewline = raw.endsWith(`
22080
22091
  `) ? `
22081
22092
  ` : "";
22093
+ await validateRtkTarget(targetDir, path);
22082
22094
  await writeTextFile(settingsPath, `${bom}${JSON.stringify(settings, null, 2)}${trailingNewline}`);
22095
+ retirement.settingsChanged.push(path);
22083
22096
  info("update.hook_commands_migrated", { count: String(rewritten) });
22084
- } catch {
22097
+ } catch (err) {
22098
+ if (err.code === "ENOENT")
22099
+ return;
22100
+ retirement.settingsConflicts.push(path);
22101
+ result.warnings.push(`RTK retirement could not safely migrate ${path}`);
22085
22102
  warn("update.hook_commands_migration_failed", {
22086
22103
  path: settingsPath
22087
22104
  });
@@ -22101,7 +22118,7 @@ async function runFullUpdatePostProcessing(options, result, config, customizatio
22101
22118
  }
22102
22119
  }
22103
22120
  if (isFullUpdate || options.components?.includes("hooks")) {
22104
- await migrateHookCommandsInSettingsLocal(options.targetDir, options);
22121
+ await migrateHookCommandsInSettingsLocal(options.targetDir, options, result);
22105
22122
  }
22106
22123
  if (!options.dryRun) {
22107
22124
  config.version = result.newVersion;
@@ -22128,24 +22145,16 @@ function compareSemver2(a, b) {
22128
22145
  }
22129
22146
  return 0;
22130
22147
  }
22131
- function checkAndInstallRtkAfterUpdate() {
22132
- if (!isRtkInstalled()) {
22133
- warn("update.rtk_missing");
22134
- console.log(i18n.t("cli.update.rtkMissing"));
22135
- const rtkInstalled = installRtk();
22136
- if (rtkInstalled) {
22137
- console.log(i18n.t("cli.update.rtkInstalled"));
22138
- }
22139
- }
22140
- }
22141
22148
  async function updateProjectRegistry(targetDir, newVersion) {
22142
22149
  try {
22143
22150
  await Promise.resolve().then(() => init_registry());
22144
22151
  await registerProject(targetDir, newVersion);
22145
22152
  } catch {}
22146
22153
  }
22147
- async function regenerateLockfile(targetDir, result) {
22148
- const lockfileResult = await generateAndWriteLockfileForDir(targetDir);
22154
+ async function regenerateLockfile(options, result, retiredPaths) {
22155
+ if (options.dryRun)
22156
+ return;
22157
+ const lockfileResult = await generateAndWriteLockfileForDir(options.targetDir, { retiredPaths });
22149
22158
  if (lockfileResult.warning) {
22150
22159
  result.warnings.push(lockfileResult.warning);
22151
22160
  warn("update.lockfile_failed", { error: lockfileResult.warning });
@@ -22202,7 +22211,7 @@ async function update(options) {
22202
22211
  result.skippedComponents = options.components || getAllUpdateComponents();
22203
22212
  return result;
22204
22213
  }
22205
- await handleBackupIfRequested(options.targetDir, !!options.backup, result);
22214
+ await handleBackupIfRequested(options.targetDir, !!options.backup && !options.dryRun, result);
22206
22215
  const manifestCustomizations = await resolveManifestCustomizations(options, options.targetDir);
22207
22216
  const configPreserveFiles = resolveConfigPreserveFiles(options, config);
22208
22217
  const customizations = resolveCustomizations(manifestCustomizations, configPreserveFiles, options.targetDir);
@@ -22210,9 +22219,10 @@ async function update(options) {
22210
22219
  const components = options.components || getAllUpdateComponents();
22211
22220
  await updateAllComponents(options.targetDir, components, updateCheck, customizations, options, result, config, lockfile);
22212
22221
  await runFullUpdatePostProcessing(options, result, config, customizations);
22213
- await regenerateLockfile(options.targetDir, result);
22214
- checkAndInstallRtkAfterUpdate();
22215
- checkAndInstallCodexAfterUpdate();
22222
+ const retiredPaths = await retireRtkFiles(options, lockfile, result, config, customizations);
22223
+ await regenerateLockfile(options, result, retiredPaths);
22224
+ if (!options.dryRun)
22225
+ checkAndInstallCodexAfterUpdate();
22216
22226
  if (result.success && !options.dryRun) {
22217
22227
  await updateProjectRegistry(options.targetDir, result.newVersion);
22218
22228
  }