claudekit-cli 4.5.0-dev.3 → 4.5.0-dev.5

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
@@ -64421,7 +64421,7 @@ var package_default;
64421
64421
  var init_package = __esm(() => {
64422
64422
  package_default = {
64423
64423
  name: "claudekit-cli",
64424
- version: "4.5.0-dev.3",
64424
+ version: "4.5.0-dev.5",
64425
64425
  description: "CLI tool for bootstrapping and updating ClaudeKit projects",
64426
64426
  type: "module",
64427
64427
  repository: {
@@ -65083,7 +65083,7 @@ import { spawnSync as spawnSync3 } from "node:child_process";
65083
65083
  import { existsSync as existsSync46, readFileSync as readFileSync14, readdirSync as readdirSync9, statSync as statSync10, writeFileSync as writeFileSync5 } from "node:fs";
65084
65084
  import { readdir as readdir17 } from "node:fs/promises";
65085
65085
  import { homedir as homedir40, tmpdir } from "node:os";
65086
- import { dirname as dirname29, join as join65, resolve as resolve33 } from "node:path";
65086
+ import { dirname as dirname29, join as join65, resolve as resolve33, sep as sep11 } from "node:path";
65087
65087
  function resolveDoctorCkExecutable(platformName = process.platform) {
65088
65088
  return platformName === "win32" ? "ck.cmd" : "ck";
65089
65089
  }
@@ -66085,6 +66085,21 @@ async function countMissingHookFileReferences(projectDir = process.cwd()) {
66085
66085
  }
66086
66086
  return count;
66087
66087
  }
66088
+ async function countMissingHookFileReferencesForClaudeDir(claudeDir3, projectRoot = dirname29(claudeDir3)) {
66089
+ const hooksDirPrefix = `${resolve33(claudeDir3, "hooks")}${sep11}`;
66090
+ let count = 0;
66091
+ for (const fileName of ["settings.json", "settings.local.json"]) {
66092
+ const filePath = resolve33(claudeDir3, fileName);
66093
+ if (!existsSync46(filePath))
66094
+ continue;
66095
+ const settings = await SettingsMerger.readSettingsFile(filePath);
66096
+ if (!settings)
66097
+ continue;
66098
+ const descriptor = { path: filePath, label: fileName, root: "$HOME" };
66099
+ count += collectMissingHookReferences(settings, descriptor, projectRoot).filter((finding) => finding.resolvedPath.startsWith(hooksDirPrefix)).length;
66100
+ }
66101
+ return count;
66102
+ }
66088
66103
  async function repairMissingHookFileReferences(projectDir = process.cwd()) {
66089
66104
  const result = await checkHookFileReferences(projectDir);
66090
66105
  if (result.status !== "fail" || !result.fix) {
@@ -66589,6 +66604,118 @@ This is a bug. Please open an issue at https://github.com/mrgoonie/claudekit-cli
66589
66604
  };
66590
66605
  });
66591
66606
 
66607
+ // src/domains/installation/plugin/codex-plugin-installer.ts
66608
+ import { execFile as execFile8 } from "node:child_process";
66609
+ import { promisify as promisify9 } from "node:util";
66610
+ function resolveCodexExecutable(platformName = process.platform) {
66611
+ return platformName === "win32" ? "codex.cmd" : "codex";
66612
+ }
66613
+ function shouldRunCodexInShell(platformName = process.platform) {
66614
+ return platformName === "win32";
66615
+ }
66616
+
66617
+ class CodexPluginInstaller {
66618
+ run;
66619
+ codexHome;
66620
+ constructor(run = defaultCodexRunner, codexHome) {
66621
+ this.run = run;
66622
+ this.codexHome = codexHome;
66623
+ }
66624
+ opts(timeoutMs) {
66625
+ return { codexHome: this.codexHome, timeoutMs };
66626
+ }
66627
+ async isCodexAvailable() {
66628
+ return (await this.run(["--version"], this.opts(15000))).ok;
66629
+ }
66630
+ async isPluginSupported() {
66631
+ const r2 = await this.run(["plugin", "--help"], this.opts(15000));
66632
+ return r2.ok && /marketplace/i.test(r2.stdout + r2.stderr);
66633
+ }
66634
+ async marketplaceAdd(source) {
66635
+ return this.run(["plugin", "marketplace", "add", source], this.opts());
66636
+ }
66637
+ async add() {
66638
+ return this.run(["plugin", "add", `${CK_PLUGIN_NAME}@${CK_MARKETPLACE_NAME}`], this.opts());
66639
+ }
66640
+ async listJson() {
66641
+ return this.run(["plugin", "list", "--json"], this.opts(15000));
66642
+ }
66643
+ async verifyInstalled() {
66644
+ const r2 = await this.listJson();
66645
+ if (!r2.ok)
66646
+ return false;
66647
+ try {
66648
+ const parsed = JSON.parse(r2.stdout);
66649
+ return (parsed.installed ?? []).some((plugin) => plugin.pluginId === `${CK_PLUGIN_NAME}@${CK_MARKETPLACE_NAME}` && plugin.installed === true && plugin.enabled === true);
66650
+ } catch {
66651
+ return false;
66652
+ }
66653
+ }
66654
+ }
66655
+ async function installCodexPlugin(opts) {
66656
+ const installer = opts.installer ?? new CodexPluginInstaller(undefined, opts.codexHome);
66657
+ if (!await installer.isCodexAvailable() || !await installer.isPluginSupported()) {
66658
+ return { action: "skipped-codex-unsupported", pluginVerified: false };
66659
+ }
66660
+ const added = await installer.marketplaceAdd(opts.pluginSourceDir);
66661
+ if (!added.ok) {
66662
+ return {
66663
+ action: "install-failed",
66664
+ pluginVerified: false,
66665
+ error: `codex marketplace add failed: ${added.stderr.trim()}`
66666
+ };
66667
+ }
66668
+ const installed = await installer.add();
66669
+ if (!installed.ok) {
66670
+ return {
66671
+ action: "install-failed",
66672
+ pluginVerified: false,
66673
+ error: `codex plugin add failed: ${installed.stderr.trim()}`
66674
+ };
66675
+ }
66676
+ const verified = await installer.verifyInstalled();
66677
+ if (!verified) {
66678
+ return {
66679
+ action: "install-failed",
66680
+ pluginVerified: false,
66681
+ error: "codex plugin did not verify after install"
66682
+ };
66683
+ }
66684
+ return { action: "installed", pluginVerified: true };
66685
+ }
66686
+ async function shouldRefreshCodexPlugin(installer = new CodexPluginInstaller) {
66687
+ if (!await installer.isCodexAvailable() || !await installer.isPluginSupported()) {
66688
+ return false;
66689
+ }
66690
+ return !await installer.verifyInstalled();
66691
+ }
66692
+ var execFileAsync6, DEFAULT_TIMEOUT_MS2 = 120000, defaultCodexRunner = async (args, opts) => {
66693
+ const env2 = { ...process.env };
66694
+ if (opts?.codexHome)
66695
+ env2.CODEX_HOME = opts.codexHome;
66696
+ try {
66697
+ const { stdout, stderr } = await execFileAsync6(resolveCodexExecutable(), args, {
66698
+ env: env2,
66699
+ shell: shouldRunCodexInShell(),
66700
+ timeout: opts?.timeoutMs ?? DEFAULT_TIMEOUT_MS2,
66701
+ maxBuffer: 10 * 1024 * 1024
66702
+ });
66703
+ return { ok: true, stdout: String(stdout), stderr: String(stderr), code: 0 };
66704
+ } catch (err) {
66705
+ const e2 = err;
66706
+ return {
66707
+ ok: false,
66708
+ stdout: e2.stdout ? String(e2.stdout) : "",
66709
+ stderr: e2.stderr ? String(e2.stderr) : e2.message ?? "",
66710
+ code: typeof e2.code === "number" ? e2.code : null
66711
+ };
66712
+ }
66713
+ };
66714
+ var init_codex_plugin_installer = __esm(() => {
66715
+ init_install_mode_detector();
66716
+ execFileAsync6 = promisify9(execFile8);
66717
+ });
66718
+
66592
66719
  // src/domains/migration/metadata-migration.ts
66593
66720
  import { join as join66 } from "node:path";
66594
66721
  async function detectMetadataFormat(claudeDir3) {
@@ -68358,7 +68485,7 @@ import { existsSync as existsSync48 } from "node:fs";
68358
68485
  import { readdir as readdir19 } from "node:fs/promises";
68359
68486
  import { builtinModules } from "node:module";
68360
68487
  import { dirname as dirname30, join as join69 } from "node:path";
68361
- import { promisify as promisify9 } from "node:util";
68488
+ import { promisify as promisify10 } from "node:util";
68362
68489
  function selectKitForUpdate(params) {
68363
68490
  const { hasLocal, hasGlobal, localKits, globalKits } = params;
68364
68491
  const hasLocalKit = localKits.length > 0 || hasLocal;
@@ -68567,6 +68694,7 @@ async function promptKitUpdate(beta, yes, deps) {
68567
68694
  const findMissingHookDepsFn = deps?.findMissingHookDependenciesFn ?? findMissingHookDependencies;
68568
68695
  const detectInstallModeFn = deps?.detectInstallModeFn ?? detectInstallMode;
68569
68696
  const hasTrackedPluginSuppliedLegacyFilesFn = deps?.hasTrackedPluginSuppliedLegacyFilesFn ?? hasTrackedPluginSuppliedLegacyFiles;
68697
+ const shouldRefreshCodexPluginFn = deps?.shouldRefreshCodexPluginFn ?? shouldRefreshCodexPlugin;
68570
68698
  const setup = await getSetupFn();
68571
68699
  const hasLocal = !!setup.project.metadata;
68572
68700
  const hasGlobal = !!setup.global.metadata;
@@ -68637,6 +68765,10 @@ async function promptKitUpdate(beta, yes, deps) {
68637
68765
  logger.warning(`Detected ${installMode.mode} global Engineer install; migrating to plugin format`);
68638
68766
  forceKitReinstall = true;
68639
68767
  }
68768
+ if (await shouldRefreshCodexPluginFn()) {
68769
+ logger.warning("Detected missing Codex ClaudeKit plugin; reinstalling global Engineer content");
68770
+ forceKitReinstall = true;
68771
+ }
68640
68772
  }
68641
68773
  } catch (error) {
68642
68774
  logger.verbose(`Plugin install-mode self-heal check skipped: ${error instanceof Error ? error.message : "unknown"}`);
@@ -68927,6 +69059,7 @@ var import_fs_extra8, execAsync2, SAFE_PROVIDER_NAME, HOOK_DEPENDENCY_EXTENSIONS
68927
69059
  var init_post_update_handler = __esm(() => {
68928
69060
  init_ck_config_manager();
68929
69061
  init_hook_health_checker();
69062
+ init_codex_plugin_installer();
68930
69063
  init_install_mode_detector();
68931
69064
  init_metadata_migration();
68932
69065
  init_version_utils();
@@ -68937,7 +69070,7 @@ var init_post_update_handler = __esm(() => {
68937
69070
  init_codex_sync_notice();
68938
69071
  init_version_comparator();
68939
69072
  import_fs_extra8 = __toESM(require_lib(), 1);
68940
- execAsync2 = promisify9(exec2);
69073
+ execAsync2 = promisify10(exec2);
68941
69074
  SAFE_PROVIDER_NAME = /^[a-z0-9-]+$/;
68942
69075
  HOOK_DEPENDENCY_EXTENSIONS = [".js", ".cjs", ".mjs", ".json"];
68943
69076
  });
@@ -68948,8 +69081,8 @@ function getDefaultUpdateCliCommandDeps() {
68948
69081
  currentVersion: package_default.version,
68949
69082
  execAsyncFn: async (cmd, opts) => {
68950
69083
  const { exec: exec3 } = await import("node:child_process");
68951
- const { promisify: promisify10 } = await import("node:util");
68952
- const result = await promisify10(exec3)(cmd, opts);
69084
+ const { promisify: promisify11 } = await import("node:util");
69085
+ const result = await promisify11(exec3)(cmd, opts);
68953
69086
  return { stdout: String(result.stdout ?? ""), stderr: String(result.stderr ?? "") };
68954
69087
  },
68955
69088
  packageManagerDetector: PackageManagerDetector,
@@ -69335,14 +69468,14 @@ var init_config_version_checker = __esm(() => {
69335
69468
 
69336
69469
  // src/domains/web-server/routes/system-routes.ts
69337
69470
  import { spawn as spawn3 } from "node:child_process";
69338
- import { execFile as execFile8 } from "node:child_process";
69471
+ import { execFile as execFile9 } from "node:child_process";
69339
69472
  import { existsSync as existsSync49 } from "node:fs";
69340
69473
  import { readFile as readFile40 } from "node:fs/promises";
69341
69474
  import { cpus, homedir as homedir42, totalmem } from "node:os";
69342
69475
  import { join as join71 } from "node:path";
69343
69476
  function runCommand(cmd, args, fallback2) {
69344
69477
  return new Promise((resolve35) => {
69345
- execFile8(cmd, args, { timeout: 5000 }, (err, stdout) => {
69478
+ execFile9(cmd, args, { timeout: 5000 }, (err, stdout) => {
69346
69479
  if (err) {
69347
69480
  resolve35(fallback2);
69348
69481
  } else {
@@ -74607,8 +74740,8 @@ var require_picomatch2 = __commonJS((exports, module) => {
74607
74740
  });
74608
74741
 
74609
74742
  // src/services/package-installer/process-executor.ts
74610
- import { exec as exec7, execFile as execFile9, spawn as spawn4 } from "node:child_process";
74611
- import { promisify as promisify14 } from "node:util";
74743
+ import { exec as exec7, execFile as execFile10, spawn as spawn4 } from "node:child_process";
74744
+ import { promisify as promisify15 } from "node:util";
74612
74745
  function executeInteractiveScript(command, args, options2) {
74613
74746
  return new Promise((resolve38, reject) => {
74614
74747
  const child = spawn4(command, args, {
@@ -74645,10 +74778,10 @@ function getNpmCommand() {
74645
74778
  const platform10 = process.platform;
74646
74779
  return platform10 === "win32" ? "npm.cmd" : "npm";
74647
74780
  }
74648
- var execAsync7, execFileAsync6;
74781
+ var execAsync7, execFileAsync7;
74649
74782
  var init_process_executor = __esm(() => {
74650
- execAsync7 = promisify14(exec7);
74651
- execFileAsync6 = promisify14(execFile9);
74783
+ execAsync7 = promisify15(exec7);
74784
+ execFileAsync7 = promisify15(execFile10);
74652
74785
  });
74653
74786
 
74654
74787
  // src/services/package-installer/validators.ts
@@ -74879,14 +75012,14 @@ async function installOpenCode() {
74879
75012
  const tempScriptPath = join90(tmpdir4(), "opencode-install.sh");
74880
75013
  try {
74881
75014
  logger.info("Downloading OpenCode installation script...");
74882
- await execFileAsync6("curl", ["-fsSL", "https://opencode.ai/install", "-o", tempScriptPath], {
75015
+ await execFileAsync7("curl", ["-fsSL", "https://opencode.ai/install", "-o", tempScriptPath], {
74883
75016
  timeout: 30000
74884
75017
  });
74885
- await execFileAsync6("chmod", ["+x", tempScriptPath], {
75018
+ await execFileAsync7("chmod", ["+x", tempScriptPath], {
74886
75019
  timeout: 5000
74887
75020
  });
74888
75021
  logger.info("Executing OpenCode installation script...");
74889
- await execFileAsync6("bash", [tempScriptPath], {
75022
+ await execFileAsync7("bash", [tempScriptPath], {
74890
75023
  timeout: 120000
74891
75024
  });
74892
75025
  } finally {
@@ -75077,8 +75210,8 @@ async function checkNeedsSudoPackages() {
75077
75210
  return false;
75078
75211
  }
75079
75212
  const { exec: exec8 } = await import("node:child_process");
75080
- const { promisify: promisify15 } = await import("node:util");
75081
- const execAsync8 = promisify15(exec8);
75213
+ const { promisify: promisify16 } = await import("node:util");
75214
+ const execAsync8 = promisify16(exec8);
75082
75215
  try {
75083
75216
  await Promise.all([
75084
75217
  execAsync8("which ffmpeg", { timeout: WHICH_COMMAND_TIMEOUT_MS }),
@@ -75649,7 +75782,7 @@ __export(exports_package_installer, {
75649
75782
  getPackageVersion: () => getPackageVersion,
75650
75783
  getNpmCommand: () => getNpmCommand,
75651
75784
  executeInteractiveScript: () => executeInteractiveScript,
75652
- execFileAsync: () => execFileAsync6,
75785
+ execFileAsync: () => execFileAsync7,
75653
75786
  execAsync: () => execAsync7,
75654
75787
  PARTIAL_INSTALL_VERSION: () => PARTIAL_INSTALL_VERSION,
75655
75788
  EXIT_CODE_PARTIAL_SUCCESS: () => EXIT_CODE_PARTIAL_SUCCESS,
@@ -77576,11 +77709,11 @@ var require_extract_zip = __commonJS((exports, module) => {
77576
77709
  var { createWriteStream: createWriteStream3, promises: fs19 } = __require("fs");
77577
77710
  var getStream = require_get_stream();
77578
77711
  var path15 = __require("path");
77579
- var { promisify: promisify15 } = __require("util");
77712
+ var { promisify: promisify16 } = __require("util");
77580
77713
  var stream = __require("stream");
77581
77714
  var yauzl = require_yauzl();
77582
- var openZip = promisify15(yauzl.open);
77583
- var pipeline = promisify15(stream.pipeline);
77715
+ var openZip = promisify16(yauzl.open);
77716
+ var pipeline = promisify16(stream.pipeline);
77584
77717
 
77585
77718
  class Extractor {
77586
77719
  constructor(zipPath, opts) {
@@ -77665,7 +77798,7 @@ var require_extract_zip = __commonJS((exports, module) => {
77665
77798
  if (isDir2)
77666
77799
  return;
77667
77800
  debug("opening read stream", dest);
77668
- const readStream = await promisify15(this.zipfile.openReadStream.bind(this.zipfile))(entry);
77801
+ const readStream = await promisify16(this.zipfile.openReadStream.bind(this.zipfile))(entry);
77669
77802
  if (symlink4) {
77670
77803
  const link = await getStream(readStream);
77671
77804
  debug("creating symlink", link, dest);
@@ -77950,10 +78083,10 @@ __export(exports_worktree_manager, {
77950
78083
  cleanupAllWorktrees: () => cleanupAllWorktrees
77951
78084
  });
77952
78085
  import { existsSync as existsSync77 } from "node:fs";
77953
- import { readFile as readFile69, writeFile as writeFile40 } from "node:fs/promises";
77954
- import { join as join158 } from "node:path";
78086
+ import { readFile as readFile70, writeFile as writeFile41 } from "node:fs/promises";
78087
+ import { join as join159 } from "node:path";
77955
78088
  async function createWorktree(projectDir, issueNumber, baseBranch) {
77956
- const worktreePath = join158(projectDir, WORKTREE_DIR, `issue-${issueNumber}`);
78089
+ const worktreePath = join159(projectDir, WORKTREE_DIR, `issue-${issueNumber}`);
77957
78090
  const branchName = `ck-watch/issue-${issueNumber}`;
77958
78091
  await spawnAndCollect("git", ["fetch", "origin", baseBranch], projectDir).catch(() => {
77959
78092
  logger.warning(`[worktree] Could not fetch origin/${baseBranch}, using local`);
@@ -77971,7 +78104,7 @@ async function createWorktree(projectDir, issueNumber, baseBranch) {
77971
78104
  return worktreePath;
77972
78105
  }
77973
78106
  async function removeWorktree(projectDir, issueNumber) {
77974
- const worktreePath = join158(projectDir, WORKTREE_DIR, `issue-${issueNumber}`);
78107
+ const worktreePath = join159(projectDir, WORKTREE_DIR, `issue-${issueNumber}`);
77975
78108
  const branchName = `ck-watch/issue-${issueNumber}`;
77976
78109
  try {
77977
78110
  await spawnAndCollect("git", ["worktree", "remove", worktreePath, "--force"], projectDir);
@@ -77985,7 +78118,7 @@ async function listActiveWorktrees(projectDir) {
77985
78118
  try {
77986
78119
  const output2 = await spawnAndCollect("git", ["worktree", "list", "--porcelain"], projectDir);
77987
78120
  const issueNumbers = [];
77988
- const worktreePrefix = join158(projectDir, WORKTREE_DIR, "issue-").replace(/\\/g, "/");
78121
+ const worktreePrefix = join159(projectDir, WORKTREE_DIR, "issue-").replace(/\\/g, "/");
77989
78122
  for (const line of output2.split(`
77990
78123
  `)) {
77991
78124
  if (line.startsWith("worktree ")) {
@@ -78013,16 +78146,16 @@ async function cleanupAllWorktrees(projectDir) {
78013
78146
  await spawnAndCollect("git", ["worktree", "prune"], projectDir).catch(() => {});
78014
78147
  }
78015
78148
  async function ensureGitignore(projectDir) {
78016
- const gitignorePath = join158(projectDir, ".gitignore");
78149
+ const gitignorePath = join159(projectDir, ".gitignore");
78017
78150
  try {
78018
- const content = existsSync77(gitignorePath) ? await readFile69(gitignorePath, "utf-8") : "";
78151
+ const content = existsSync77(gitignorePath) ? await readFile70(gitignorePath, "utf-8") : "";
78019
78152
  if (!content.includes(".worktrees")) {
78020
78153
  const newContent = content.endsWith(`
78021
78154
  `) ? `${content}.worktrees/
78022
78155
  ` : `${content}
78023
78156
  .worktrees/
78024
78157
  `;
78025
- await writeFile40(gitignorePath, newContent, "utf-8");
78158
+ await writeFile41(gitignorePath, newContent, "utf-8");
78026
78159
  logger.info("[worktree] Added .worktrees/ to .gitignore");
78027
78160
  }
78028
78161
  } catch (err) {
@@ -78117,16 +78250,16 @@ var init_content_validator = __esm(() => {
78117
78250
 
78118
78251
  // src/commands/content/phases/context-cache-manager.ts
78119
78252
  import { createHash as createHash9 } from "node:crypto";
78120
- import { existsSync as existsSync83, mkdirSync as mkdirSync7, readFileSync as readFileSync21, readdirSync as readdirSync14, statSync as statSync15 } from "node:fs";
78121
- import { rename as rename16, writeFile as writeFile42 } from "node:fs/promises";
78253
+ import { existsSync as existsSync83, mkdirSync as mkdirSync7, readFileSync as readFileSync22, readdirSync as readdirSync14, statSync as statSync15 } from "node:fs";
78254
+ import { rename as rename16, writeFile as writeFile43 } from "node:fs/promises";
78122
78255
  import { homedir as homedir54 } from "node:os";
78123
- import { basename as basename34, join as join165 } from "node:path";
78256
+ import { basename as basename34, join as join166 } from "node:path";
78124
78257
  function getCachedContext(repoPath) {
78125
78258
  const cachePath = getCacheFilePath(repoPath);
78126
78259
  if (!existsSync83(cachePath))
78127
78260
  return null;
78128
78261
  try {
78129
- const raw2 = readFileSync21(cachePath, "utf-8");
78262
+ const raw2 = readFileSync22(cachePath, "utf-8");
78130
78263
  const cache5 = JSON.parse(raw2);
78131
78264
  const age = Date.now() - new Date(cache5.createdAt).getTime();
78132
78265
  if (age >= CACHE_TTL_MS5)
@@ -78145,7 +78278,7 @@ async function saveCachedContext(repoPath, cache5) {
78145
78278
  }
78146
78279
  const cachePath = getCacheFilePath(repoPath);
78147
78280
  const tmpPath = `${cachePath}.tmp`;
78148
- await writeFile42(tmpPath, JSON.stringify(cache5, null, 2), "utf-8");
78281
+ await writeFile43(tmpPath, JSON.stringify(cache5, null, 2), "utf-8");
78149
78282
  await rename16(tmpPath, cachePath);
78150
78283
  }
78151
78284
  function computeSourceHash(repoPath) {
@@ -78163,25 +78296,25 @@ function computeSourceHash(repoPath) {
78163
78296
  }
78164
78297
  function getDocSourcePaths(repoPath) {
78165
78298
  const paths = [];
78166
- const docsDir = join165(repoPath, "docs");
78299
+ const docsDir = join166(repoPath, "docs");
78167
78300
  if (existsSync83(docsDir)) {
78168
78301
  try {
78169
78302
  const files = readdirSync14(docsDir);
78170
78303
  for (const f3 of files) {
78171
78304
  if (f3.endsWith(".md"))
78172
- paths.push(join165(docsDir, f3));
78305
+ paths.push(join166(docsDir, f3));
78173
78306
  }
78174
78307
  } catch {}
78175
78308
  }
78176
- const readme = join165(repoPath, "README.md");
78309
+ const readme = join166(repoPath, "README.md");
78177
78310
  if (existsSync83(readme))
78178
78311
  paths.push(readme);
78179
- const stylesDir = join165(repoPath, "assets", "writing-styles");
78312
+ const stylesDir = join166(repoPath, "assets", "writing-styles");
78180
78313
  if (existsSync83(stylesDir)) {
78181
78314
  try {
78182
78315
  const files = readdirSync14(stylesDir);
78183
78316
  for (const f3 of files) {
78184
- paths.push(join165(stylesDir, f3));
78317
+ paths.push(join166(stylesDir, f3));
78185
78318
  }
78186
78319
  } catch {}
78187
78320
  }
@@ -78190,11 +78323,11 @@ function getDocSourcePaths(repoPath) {
78190
78323
  function getCacheFilePath(repoPath) {
78191
78324
  const repoName = basename34(repoPath).replace(/[^a-zA-Z0-9_-]/g, "_");
78192
78325
  const pathHash = createHash9("sha256").update(repoPath).digest("hex").slice(0, 8);
78193
- return join165(CACHE_DIR, `${repoName}-${pathHash}-context-cache.json`);
78326
+ return join166(CACHE_DIR, `${repoName}-${pathHash}-context-cache.json`);
78194
78327
  }
78195
78328
  var CACHE_DIR, CACHE_TTL_MS5;
78196
78329
  var init_context_cache_manager = __esm(() => {
78197
- CACHE_DIR = join165(homedir54(), ".claudekit", "cache");
78330
+ CACHE_DIR = join166(homedir54(), ".claudekit", "cache");
78198
78331
  CACHE_TTL_MS5 = 24 * 60 * 60 * 1000;
78199
78332
  });
78200
78333
 
@@ -78374,8 +78507,8 @@ function extractContentFromResponse(response) {
78374
78507
 
78375
78508
  // src/commands/content/phases/docs-summarizer.ts
78376
78509
  import { execSync as execSync7 } from "node:child_process";
78377
- import { existsSync as existsSync84, readFileSync as readFileSync22, readdirSync as readdirSync15 } from "node:fs";
78378
- import { join as join166 } from "node:path";
78510
+ import { existsSync as existsSync84, readFileSync as readFileSync23, readdirSync as readdirSync15 } from "node:fs";
78511
+ import { join as join167 } from "node:path";
78379
78512
  async function summarizeProjectDocs(repoPath, contentLogger) {
78380
78513
  const rawContent = collectRawDocs(repoPath);
78381
78514
  if (rawContent.total.length < 200) {
@@ -78423,18 +78556,18 @@ function collectRawDocs(repoPath) {
78423
78556
  return "";
78424
78557
  if (totalChars >= MAX_RAW_CONTENT_CHARS)
78425
78558
  return "";
78426
- const content = readFileSync22(filePath, "utf-8");
78559
+ const content = readFileSync23(filePath, "utf-8");
78427
78560
  const capped = content.slice(0, Math.min(maxChars, MAX_RAW_CONTENT_CHARS - totalChars));
78428
78561
  totalChars += capped.length;
78429
78562
  return capped;
78430
78563
  };
78431
78564
  const docsContent = [];
78432
- const docsDir = join166(repoPath, "docs");
78565
+ const docsDir = join167(repoPath, "docs");
78433
78566
  if (existsSync84(docsDir)) {
78434
78567
  try {
78435
78568
  const files = readdirSync15(docsDir).filter((f3) => f3.endsWith(".md")).sort();
78436
78569
  for (const f3 of files) {
78437
- const content = readCapped(join166(docsDir, f3), 5000);
78570
+ const content = readCapped(join167(docsDir, f3), 5000);
78438
78571
  if (content) {
78439
78572
  docsContent.push(`### ${f3}
78440
78573
  ${content}`);
@@ -78448,21 +78581,21 @@ ${content}`);
78448
78581
  let brand = "";
78449
78582
  const brandCandidates = ["docs/brand-guidelines.md", "docs/design-guidelines.md"];
78450
78583
  for (const p of brandCandidates) {
78451
- brand = readCapped(join166(repoPath, p), 3000);
78584
+ brand = readCapped(join167(repoPath, p), 3000);
78452
78585
  if (brand)
78453
78586
  break;
78454
78587
  }
78455
78588
  let styles3 = "";
78456
- const stylesDir = join166(repoPath, "assets", "writing-styles");
78589
+ const stylesDir = join167(repoPath, "assets", "writing-styles");
78457
78590
  if (existsSync84(stylesDir)) {
78458
78591
  try {
78459
78592
  const files = readdirSync15(stylesDir).slice(0, 3);
78460
- styles3 = files.map((f3) => readCapped(join166(stylesDir, f3), 1000)).filter(Boolean).join(`
78593
+ styles3 = files.map((f3) => readCapped(join167(stylesDir, f3), 1000)).filter(Boolean).join(`
78461
78594
 
78462
78595
  `);
78463
78596
  } catch {}
78464
78597
  }
78465
- const readme = readCapped(join166(repoPath, "README.md"), 3000);
78598
+ const readme = readCapped(join167(repoPath, "README.md"), 3000);
78466
78599
  const total = [docs, brand, styles3, readme].join(`
78467
78600
  `);
78468
78601
  return { docs, brand, styles: styles3, readme, total };
@@ -78649,9 +78782,9 @@ IMPORTANT: Generate the image and output the path as JSON: {"imagePath": "/path/
78649
78782
  import { execSync as execSync8 } from "node:child_process";
78650
78783
  import { existsSync as existsSync85, mkdirSync as mkdirSync8, readdirSync as readdirSync16 } from "node:fs";
78651
78784
  import { homedir as homedir55 } from "node:os";
78652
- import { join as join167 } from "node:path";
78785
+ import { join as join168 } from "node:path";
78653
78786
  async function generatePhoto(_content, context, config, platform18, contentId, contentLogger) {
78654
- const mediaDir = join167(config.contentDir.replace(/^~/, homedir55()), "media", String(contentId));
78787
+ const mediaDir = join168(config.contentDir.replace(/^~/, homedir55()), "media", String(contentId));
78655
78788
  if (!existsSync85(mediaDir)) {
78656
78789
  mkdirSync8(mediaDir, { recursive: true });
78657
78790
  }
@@ -78676,7 +78809,7 @@ async function generatePhoto(_content, context, config, platform18, contentId, c
78676
78809
  const imageFile = files.find((f3) => /\.(png|jpg|jpeg|webp)$/i.test(f3));
78677
78810
  if (imageFile) {
78678
78811
  const ext2 = imageFile.split(".").pop() ?? "png";
78679
- return { path: join167(mediaDir, imageFile), ...dimensions, format: ext2 };
78812
+ return { path: join168(mediaDir, imageFile), ...dimensions, format: ext2 };
78680
78813
  }
78681
78814
  contentLogger.warn(`Photo generation produced no image for content ${contentId}`);
78682
78815
  return null;
@@ -78766,7 +78899,7 @@ var init_content_creator = __esm(() => {
78766
78899
  // src/commands/content/phases/content-logger.ts
78767
78900
  import { createWriteStream as createWriteStream4, existsSync as existsSync86, mkdirSync as mkdirSync9, statSync as statSync16 } from "node:fs";
78768
78901
  import { homedir as homedir56 } from "node:os";
78769
- import { join as join168 } from "node:path";
78902
+ import { join as join169 } from "node:path";
78770
78903
 
78771
78904
  class ContentLogger {
78772
78905
  stream = null;
@@ -78774,7 +78907,7 @@ class ContentLogger {
78774
78907
  logDir;
78775
78908
  maxBytes;
78776
78909
  constructor(maxBytes = 0) {
78777
- this.logDir = join168(homedir56(), ".claudekit", "logs");
78910
+ this.logDir = join169(homedir56(), ".claudekit", "logs");
78778
78911
  this.maxBytes = maxBytes;
78779
78912
  }
78780
78913
  init() {
@@ -78806,7 +78939,7 @@ class ContentLogger {
78806
78939
  }
78807
78940
  }
78808
78941
  getLogPath() {
78809
- return join168(this.logDir, `content-${this.getDateStr()}.log`);
78942
+ return join169(this.logDir, `content-${this.getDateStr()}.log`);
78810
78943
  }
78811
78944
  write(level, message) {
78812
78945
  this.rotateIfNeeded();
@@ -78823,18 +78956,18 @@ class ContentLogger {
78823
78956
  if (dateStr !== this.currentDate) {
78824
78957
  this.close();
78825
78958
  this.currentDate = dateStr;
78826
- const logPath = join168(this.logDir, `content-${dateStr}.log`);
78959
+ const logPath = join169(this.logDir, `content-${dateStr}.log`);
78827
78960
  this.stream = createWriteStream4(logPath, { flags: "a", mode: 384 });
78828
78961
  return;
78829
78962
  }
78830
78963
  if (this.maxBytes > 0 && this.stream) {
78831
- const logPath = join168(this.logDir, `content-${this.currentDate}.log`);
78964
+ const logPath = join169(this.logDir, `content-${this.currentDate}.log`);
78832
78965
  try {
78833
78966
  const stat26 = statSync16(logPath);
78834
78967
  if (stat26.size >= this.maxBytes) {
78835
78968
  this.close();
78836
78969
  const suffix = Date.now();
78837
- const rotatedPath = join168(this.logDir, `content-${this.currentDate}-${suffix}.log`);
78970
+ const rotatedPath = join169(this.logDir, `content-${this.currentDate}-${suffix}.log`);
78838
78971
  import("node:fs/promises").then(({ rename: rename17 }) => rename17(logPath, rotatedPath).catch(() => {}));
78839
78972
  this.stream = createWriteStream4(logPath, { flags: "w", mode: 384 });
78840
78973
  }
@@ -79104,8 +79237,8 @@ function isNoiseCommit(title, author) {
79104
79237
 
79105
79238
  // src/commands/content/phases/change-detector.ts
79106
79239
  import { execSync as execSync10, spawnSync as spawnSync9 } from "node:child_process";
79107
- import { existsSync as existsSync88, readFileSync as readFileSync23, readdirSync as readdirSync17, statSync as statSync17 } from "node:fs";
79108
- import { join as join169 } from "node:path";
79240
+ import { existsSync as existsSync88, readFileSync as readFileSync24, readdirSync as readdirSync17, statSync as statSync17 } from "node:fs";
79241
+ import { join as join170 } from "node:path";
79109
79242
  function detectCommits(repo, since) {
79110
79243
  try {
79111
79244
  const fetchUrl = sshToHttps(repo.remoteUrl);
@@ -79214,7 +79347,7 @@ function detectTags(repo, since) {
79214
79347
  }
79215
79348
  }
79216
79349
  function detectCompletedPlans(repo, since) {
79217
- const plansDir = join169(repo.path, "plans");
79350
+ const plansDir = join170(repo.path, "plans");
79218
79351
  if (!existsSync88(plansDir))
79219
79352
  return [];
79220
79353
  const sinceMs = new Date(since).getTime();
@@ -79224,14 +79357,14 @@ function detectCompletedPlans(repo, since) {
79224
79357
  for (const entry of entries) {
79225
79358
  if (!entry.isDirectory())
79226
79359
  continue;
79227
- const planFile = join169(plansDir, entry.name, "plan.md");
79360
+ const planFile = join170(plansDir, entry.name, "plan.md");
79228
79361
  if (!existsSync88(planFile))
79229
79362
  continue;
79230
79363
  try {
79231
79364
  const stat26 = statSync17(planFile);
79232
79365
  if (stat26.mtimeMs < sinceMs)
79233
79366
  continue;
79234
- const content = readFileSync23(planFile, "utf-8");
79367
+ const content = readFileSync24(planFile, "utf-8");
79235
79368
  const frontmatterMatch = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
79236
79369
  if (!frontmatterMatch)
79237
79370
  continue;
@@ -79302,7 +79435,7 @@ function classifyCommit(event) {
79302
79435
  // src/commands/content/phases/repo-discoverer.ts
79303
79436
  import { execSync as execSync11 } from "node:child_process";
79304
79437
  import { readdirSync as readdirSync18 } from "node:fs";
79305
- import { join as join170 } from "node:path";
79438
+ import { join as join171 } from "node:path";
79306
79439
  function discoverRepos2(cwd2) {
79307
79440
  const repos = [];
79308
79441
  if (isGitRepoRoot(cwd2)) {
@@ -79315,7 +79448,7 @@ function discoverRepos2(cwd2) {
79315
79448
  for (const entry of entries) {
79316
79449
  if (!entry.isDirectory() || entry.name.startsWith("."))
79317
79450
  continue;
79318
- const dirPath = join170(cwd2, entry.name);
79451
+ const dirPath = join171(cwd2, entry.name);
79319
79452
  if (isGitRepoRoot(dirPath)) {
79320
79453
  const info = getRepoInfo(dirPath);
79321
79454
  if (info)
@@ -79982,12 +80115,12 @@ var init_types6 = __esm(() => {
79982
80115
  });
79983
80116
 
79984
80117
  // src/commands/content/phases/state-manager.ts
79985
- import { readFile as readFile71, rename as rename17, writeFile as writeFile43 } from "node:fs/promises";
79986
- import { join as join171 } from "node:path";
80118
+ import { readFile as readFile72, rename as rename17, writeFile as writeFile44 } from "node:fs/promises";
80119
+ import { join as join172 } from "node:path";
79987
80120
  async function loadContentConfig(projectDir) {
79988
- const configPath = join171(projectDir, CK_CONFIG_FILE2);
80121
+ const configPath = join172(projectDir, CK_CONFIG_FILE2);
79989
80122
  try {
79990
- const raw2 = await readFile71(configPath, "utf-8");
80123
+ const raw2 = await readFile72(configPath, "utf-8");
79991
80124
  const json = JSON.parse(raw2);
79992
80125
  return ContentConfigSchema.parse(json.content ?? {});
79993
80126
  } catch {
@@ -79995,15 +80128,15 @@ async function loadContentConfig(projectDir) {
79995
80128
  }
79996
80129
  }
79997
80130
  async function saveContentConfig(projectDir, config) {
79998
- const configPath = join171(projectDir, CK_CONFIG_FILE2);
79999
- const json = await readJsonSafe3(configPath);
80131
+ const configPath = join172(projectDir, CK_CONFIG_FILE2);
80132
+ const json = await readJsonSafe4(configPath);
80000
80133
  json.content = { ...json.content, ...config };
80001
80134
  await atomicWrite3(configPath, json);
80002
80135
  }
80003
80136
  async function loadContentState(projectDir) {
80004
- const configPath = join171(projectDir, CK_CONFIG_FILE2);
80137
+ const configPath = join172(projectDir, CK_CONFIG_FILE2);
80005
80138
  try {
80006
- const raw2 = await readFile71(configPath, "utf-8");
80139
+ const raw2 = await readFile72(configPath, "utf-8");
80007
80140
  const json = JSON.parse(raw2);
80008
80141
  const contentBlock = json.content ?? {};
80009
80142
  return ContentStateSchema.parse(contentBlock.state ?? {});
@@ -80012,7 +80145,7 @@ async function loadContentState(projectDir) {
80012
80145
  }
80013
80146
  }
80014
80147
  async function saveContentState(projectDir, state) {
80015
- const configPath = join171(projectDir, CK_CONFIG_FILE2);
80148
+ const configPath = join172(projectDir, CK_CONFIG_FILE2);
80016
80149
  const sevenDaysAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString().slice(0, 10);
80017
80150
  for (const key of Object.keys(state.dailyPostCounts)) {
80018
80151
  const dateStr = key.slice(-10);
@@ -80021,16 +80154,16 @@ async function saveContentState(projectDir, state) {
80021
80154
  }
80022
80155
  }
80023
80156
  ContentStateSchema.parse(state);
80024
- const json = await readJsonSafe3(configPath);
80157
+ const json = await readJsonSafe4(configPath);
80025
80158
  if (!json.content || typeof json.content !== "object") {
80026
80159
  json.content = {};
80027
80160
  }
80028
80161
  json.content.state = state;
80029
80162
  await atomicWrite3(configPath, json);
80030
80163
  }
80031
- async function readJsonSafe3(filePath) {
80164
+ async function readJsonSafe4(filePath) {
80032
80165
  try {
80033
- const raw2 = await readFile71(filePath, "utf-8");
80166
+ const raw2 = await readFile72(filePath, "utf-8");
80034
80167
  return JSON.parse(raw2);
80035
80168
  } catch {
80036
80169
  return {};
@@ -80038,7 +80171,7 @@ async function readJsonSafe3(filePath) {
80038
80171
  }
80039
80172
  async function atomicWrite3(filePath, data) {
80040
80173
  const tmpPath = `${filePath}.tmp`;
80041
- await writeFile43(tmpPath, JSON.stringify(data, null, 2), "utf-8");
80174
+ await writeFile44(tmpPath, JSON.stringify(data, null, 2), "utf-8");
80042
80175
  await rename17(tmpPath, filePath);
80043
80176
  }
80044
80177
  var CK_CONFIG_FILE2 = ".ck.json";
@@ -80294,7 +80427,7 @@ var init_platform_setup_x = __esm(() => {
80294
80427
 
80295
80428
  // src/commands/content/phases/setup-wizard.ts
80296
80429
  import { existsSync as existsSync89 } from "node:fs";
80297
- import { join as join172 } from "node:path";
80430
+ import { join as join173 } from "node:path";
80298
80431
  async function runSetupWizard2(cwd2, contentLogger) {
80299
80432
  console.log();
80300
80433
  oe(import_picocolors43.default.bgCyan(import_picocolors43.default.white(" CK Content — Multi-Channel Content Engine ")));
@@ -80362,8 +80495,8 @@ async function showRepoSummary(cwd2) {
80362
80495
  function detectBrandAssets(cwd2, contentLogger) {
80363
80496
  const repos = discoverRepos2(cwd2);
80364
80497
  for (const repo of repos) {
80365
- const hasGuidelines = existsSync89(join172(repo.path, "docs", "brand-guidelines.md"));
80366
- const hasStyles = existsSync89(join172(repo.path, "assets", "writing-styles"));
80498
+ const hasGuidelines = existsSync89(join173(repo.path, "docs", "brand-guidelines.md"));
80499
+ const hasStyles = existsSync89(join173(repo.path, "assets", "writing-styles"));
80367
80500
  if (!hasGuidelines) {
80368
80501
  f2.warning(`${repo.name}: No docs/brand-guidelines.md — content will use generic tone.`);
80369
80502
  contentLogger.warn(`${repo.name}: missing docs/brand-guidelines.md`);
@@ -80503,15 +80636,15 @@ __export(exports_content_subcommands, {
80503
80636
  logsContent: () => logsContent,
80504
80637
  approveContentCmd: () => approveContentCmd
80505
80638
  });
80506
- import { existsSync as existsSync91, readFileSync as readFileSync24, unlinkSync as unlinkSync6 } from "node:fs";
80639
+ import { existsSync as existsSync91, readFileSync as readFileSync25, unlinkSync as unlinkSync6 } from "node:fs";
80507
80640
  import { homedir as homedir58 } from "node:os";
80508
- import { join as join173 } from "node:path";
80641
+ import { join as join174 } from "node:path";
80509
80642
  function isDaemonRunning() {
80510
- const lockFile = join173(LOCK_DIR, `${LOCK_NAME2}.lock`);
80643
+ const lockFile = join174(LOCK_DIR, `${LOCK_NAME2}.lock`);
80511
80644
  if (!existsSync91(lockFile))
80512
80645
  return { running: false, pid: null };
80513
80646
  try {
80514
- const pidStr = readFileSync24(lockFile, "utf-8").trim();
80647
+ const pidStr = readFileSync25(lockFile, "utf-8").trim();
80515
80648
  const pid = Number.parseInt(pidStr, 10);
80516
80649
  if (Number.isNaN(pid)) {
80517
80650
  unlinkSync6(lockFile);
@@ -80539,13 +80672,13 @@ async function startContent(options2) {
80539
80672
  await contentCommand(options2);
80540
80673
  }
80541
80674
  async function stopContent() {
80542
- const lockFile = join173(LOCK_DIR, `${LOCK_NAME2}.lock`);
80675
+ const lockFile = join174(LOCK_DIR, `${LOCK_NAME2}.lock`);
80543
80676
  if (!existsSync91(lockFile)) {
80544
80677
  logger.info("Content daemon is not running.");
80545
80678
  return;
80546
80679
  }
80547
80680
  try {
80548
- const pidStr = readFileSync24(lockFile, "utf-8").trim();
80681
+ const pidStr = readFileSync25(lockFile, "utf-8").trim();
80549
80682
  const pid = Number.parseInt(pidStr, 10);
80550
80683
  if (!Number.isNaN(pid)) {
80551
80684
  process.kill(pid, "SIGTERM");
@@ -80578,9 +80711,9 @@ async function statusContent() {
80578
80711
  } catch {}
80579
80712
  }
80580
80713
  async function logsContent(options2) {
80581
- const logDir = join173(homedir58(), ".claudekit", "logs");
80714
+ const logDir = join174(homedir58(), ".claudekit", "logs");
80582
80715
  const dateStr = new Date().toISOString().slice(0, 10).replace(/-/g, "");
80583
- const logPath = join173(logDir, `content-${dateStr}.log`);
80716
+ const logPath = join174(logDir, `content-${dateStr}.log`);
80584
80717
  if (!existsSync91(logPath)) {
80585
80718
  logger.info("No content logs found for today.");
80586
80719
  return;
@@ -80593,7 +80726,7 @@ async function logsContent(options2) {
80593
80726
  process.exit(0);
80594
80727
  });
80595
80728
  } else {
80596
- const content = readFileSync24(logPath, "utf-8");
80729
+ const content = readFileSync25(logPath, "utf-8");
80597
80730
  console.log(content);
80598
80731
  }
80599
80732
  }
@@ -80612,13 +80745,13 @@ var init_content_subcommands = __esm(() => {
80612
80745
  init_setup_wizard();
80613
80746
  init_state_manager();
80614
80747
  init_content_review_commands();
80615
- LOCK_DIR = join173(homedir58(), ".claudekit", "locks");
80748
+ LOCK_DIR = join174(homedir58(), ".claudekit", "locks");
80616
80749
  });
80617
80750
 
80618
80751
  // src/commands/content/content-command.ts
80619
80752
  import { existsSync as existsSync92, mkdirSync as mkdirSync11, unlinkSync as unlinkSync7, writeFileSync as writeFileSync9 } from "node:fs";
80620
80753
  import { homedir as homedir59 } from "node:os";
80621
- import { join as join174 } from "node:path";
80754
+ import { join as join175 } from "node:path";
80622
80755
  async function contentCommand(options2) {
80623
80756
  const cwd2 = process.cwd();
80624
80757
  const contentLogger = new ContentLogger;
@@ -80796,8 +80929,8 @@ var init_content_command = __esm(() => {
80796
80929
  init_publisher();
80797
80930
  init_review_manager();
80798
80931
  init_state_manager();
80799
- LOCK_DIR2 = join174(homedir59(), ".claudekit", "locks");
80800
- LOCK_FILE = join174(LOCK_DIR2, "ck-content.lock");
80932
+ LOCK_DIR2 = join175(homedir59(), ".claudekit", "locks");
80933
+ LOCK_FILE = join175(LOCK_DIR2, "ck-content.lock");
80801
80934
  });
80802
80935
 
80803
80936
  // src/commands/content/index.ts
@@ -87963,7 +88096,7 @@ class CheckRunner {
87963
88096
  }
87964
88097
  // src/domains/health-checks/system-checker.ts
87965
88098
  import { exec as exec6 } from "node:child_process";
87966
- import { promisify as promisify13 } from "node:util";
88099
+ import { promisify as promisify14 } from "node:util";
87967
88100
 
87968
88101
  // src/domains/github/gh-cli-utils.ts
87969
88102
  init_environment();
@@ -88031,8 +88164,8 @@ function getGhUpgradeInstructions(currentVersion) {
88031
88164
  init_environment();
88032
88165
  init_logger();
88033
88166
  import { exec as exec3 } from "node:child_process";
88034
- import { promisify as promisify10 } from "node:util";
88035
- var execAsync3 = promisify10(exec3);
88167
+ import { promisify as promisify11 } from "node:util";
88168
+ var execAsync3 = promisify11(exec3);
88036
88169
  function getOSInfo() {
88037
88170
  const platform8 = process.platform;
88038
88171
  const arch2 = process.arch;
@@ -88253,7 +88386,7 @@ async function checkAllDependencies() {
88253
88386
  // src/services/package-installer/dependency-installer.ts
88254
88387
  init_logger();
88255
88388
  import { exec as exec5 } from "node:child_process";
88256
- import { promisify as promisify12 } from "node:util";
88389
+ import { promisify as promisify13 } from "node:util";
88257
88390
 
88258
88391
  // src/services/package-installer/dependencies/node-installer.ts
88259
88392
  var NODEJS_INSTALLERS = [
@@ -88347,8 +88480,8 @@ var PYTHON_INSTALLERS = [
88347
88480
  init_logger();
88348
88481
  import { exec as exec4 } from "node:child_process";
88349
88482
  import * as fs7 from "node:fs";
88350
- import { promisify as promisify11 } from "node:util";
88351
- var execAsync4 = promisify11(exec4);
88483
+ import { promisify as promisify12 } from "node:util";
88484
+ var execAsync4 = promisify12(exec4);
88352
88485
  async function detectOS() {
88353
88486
  const platform8 = process.platform;
88354
88487
  const info = { platform: platform8 };
@@ -88418,7 +88551,7 @@ var CLAUDE_INSTALLERS = [
88418
88551
  ];
88419
88552
 
88420
88553
  // src/services/package-installer/dependency-installer.ts
88421
- var execAsync5 = promisify12(exec5);
88554
+ var execAsync5 = promisify13(exec5);
88422
88555
  function getInstallerMethods(dependency, osInfo) {
88423
88556
  let installers = dependency === "claude" ? CLAUDE_INSTALLERS : dependency === "python" || dependency === "pip" ? PYTHON_INSTALLERS : dependency === "nodejs" ? NODEJS_INSTALLERS : [];
88424
88557
  installers = installers.filter((m2) => m2.platform === osInfo.platform);
@@ -88474,7 +88607,7 @@ async function installDependency(dependency, method) {
88474
88607
 
88475
88608
  // src/domains/health-checks/system-checker.ts
88476
88609
  init_logger();
88477
- var execAsync6 = promisify13(exec6);
88610
+ var execAsync6 = promisify14(exec6);
88478
88611
 
88479
88612
  class SystemChecker {
88480
88613
  group = "system";
@@ -101939,10 +102072,10 @@ class TarExtractor {
101939
102072
  // src/domains/installation/extraction/zip-extractor.ts
101940
102073
  init_logger();
101941
102074
  var import_extract_zip = __toESM(require_extract_zip(), 1);
101942
- import { execFile as execFile10 } from "node:child_process";
102075
+ import { execFile as execFile11 } from "node:child_process";
101943
102076
  import { copyFile as copyFile5, mkdir as mkdir29, readdir as readdir26, rm as rm12, stat as stat16 } from "node:fs/promises";
101944
102077
  import { join as join103 } from "node:path";
101945
- import { promisify as promisify15 } from "node:util";
102078
+ import { promisify as promisify16 } from "node:util";
101946
102079
 
101947
102080
  // src/domains/installation/extraction/native-zip-commands.ts
101948
102081
  var NATIVE_EXTRACT_TIMEOUT_MS = 120000;
@@ -101981,7 +102114,7 @@ function getNativeZipCommands(archivePath, destDir, platformName = process.platf
101981
102114
  }
101982
102115
 
101983
102116
  // src/domains/installation/extraction/zip-extractor.ts
101984
- var execFileAsync7 = promisify15(execFile10);
102117
+ var execFileAsync8 = promisify16(execFile11);
101985
102118
 
101986
102119
  class ZipExtractor {
101987
102120
  async tryNativeExtraction(archivePath, destDir) {
@@ -101993,7 +102126,7 @@ class ZipExtractor {
101993
102126
  try {
101994
102127
  await rm12(destDir, { recursive: true, force: true });
101995
102128
  await mkdir29(destDir, { recursive: true });
101996
- await execFileAsync7(nativeCommand.command, nativeCommand.args, {
102129
+ await execFileAsync8(nativeCommand.command, nativeCommand.args, {
101997
102130
  timeout: NATIVE_EXTRACT_TIMEOUT_MS,
101998
102131
  windowsHide: true
101999
102132
  });
@@ -102631,7 +102764,7 @@ import { join as join122 } from "node:path";
102631
102764
 
102632
102765
  // src/domains/installation/deletion-handler.ts
102633
102766
  import { existsSync as existsSync66, lstatSync as lstatSync3, readdirSync as readdirSync10, rmSync as rmSync2, rmdirSync, unlinkSync as unlinkSync4 } from "node:fs";
102634
- import { dirname as dirname38, join as join107, relative as relative21, resolve as resolve42, sep as sep11 } from "node:path";
102767
+ import { dirname as dirname38, join as join107, relative as relative21, resolve as resolve42, sep as sep12 } from "node:path";
102635
102768
 
102636
102769
  // src/services/file-operations/manifest/manifest-reader.ts
102637
102770
  init_metadata_migration();
@@ -102878,7 +103011,7 @@ function cleanupEmptyDirectories(filePath, claudeDir3) {
102878
103011
  function deletePath(fullPath, claudeDir3) {
102879
103012
  const normalizedPath = resolve42(fullPath);
102880
103013
  const normalizedClaudeDir = resolve42(claudeDir3);
102881
- if (!normalizedPath.startsWith(`${normalizedClaudeDir}${sep11}`) && normalizedPath !== normalizedClaudeDir) {
103014
+ if (!normalizedPath.startsWith(`${normalizedClaudeDir}${sep12}`) && normalizedPath !== normalizedClaudeDir) {
102882
103015
  throw new Error(`Path traversal detected: ${fullPath}`);
102883
103016
  }
102884
103017
  try {
@@ -102952,7 +103085,7 @@ async function handleDeletions(sourceMetadata, claudeDir3, kitType) {
102952
103085
  const fullPath = join107(claudeDir3, path16);
102953
103086
  const normalizedPath = resolve42(fullPath);
102954
103087
  const normalizedClaudeDir = resolve42(claudeDir3);
102955
- if (!normalizedPath.startsWith(`${normalizedClaudeDir}${sep11}`)) {
103088
+ if (!normalizedPath.startsWith(`${normalizedClaudeDir}${sep12}`)) {
102956
103089
  logger.warning(`Skipping invalid path: ${path16}`);
102957
103090
  result.errors.push(path16);
102958
103091
  continue;
@@ -104050,8 +104183,8 @@ var path16 = {
104050
104183
  win32: { sep: "\\" },
104051
104184
  posix: { sep: "/" }
104052
104185
  };
104053
- var sep12 = defaultPlatform === "win32" ? path16.win32.sep : path16.posix.sep;
104054
- minimatch.sep = sep12;
104186
+ var sep13 = defaultPlatform === "win32" ? path16.win32.sep : path16.posix.sep;
104187
+ minimatch.sep = sep13;
104055
104188
  var GLOBSTAR = Symbol("globstar **");
104056
104189
  minimatch.GLOBSTAR = GLOBSTAR;
104057
104190
  var qmark2 = "[^/]";
@@ -108818,7 +108951,8 @@ async function handlePostInstall(ctx) {
108818
108951
  };
108819
108952
  }
108820
108953
  // src/commands/init/phases/plugin-install-handler.ts
108821
- import { cpSync as cpSync2, existsSync as existsSync69, mkdirSync as mkdirSync6, rmSync as rmSync4, writeFileSync as writeFileSync8 } from "node:fs";
108954
+ init_codex_plugin_installer();
108955
+ import { cpSync as cpSync2, existsSync as existsSync69, mkdirSync as mkdirSync6, readFileSync as readFileSync21, rmSync as rmSync4, writeFileSync as writeFileSync8 } from "node:fs";
108822
108956
  import { join as join135 } from "node:path";
108823
108957
 
108824
108958
  // src/domains/installation/plugin/migrate-legacy-to-plugin.ts
@@ -108828,18 +108962,18 @@ import { dirname as dirname43, join as join134 } from "node:path";
108828
108962
 
108829
108963
  // src/domains/installation/plugin/plugin-installer.ts
108830
108964
  init_install_mode_detector();
108831
- import { execFile as execFile11 } from "node:child_process";
108832
- import { promisify as promisify16 } from "node:util";
108833
- var execFileAsync8 = promisify16(execFile11);
108834
- var DEFAULT_TIMEOUT_MS2 = 120000;
108965
+ import { execFile as execFile12 } from "node:child_process";
108966
+ import { promisify as promisify17 } from "node:util";
108967
+ var execFileAsync9 = promisify17(execFile12);
108968
+ var DEFAULT_TIMEOUT_MS3 = 120000;
108835
108969
  var defaultClaudeRunner = async (args, opts) => {
108836
108970
  const env2 = { ...process.env };
108837
108971
  if (opts?.configDir)
108838
108972
  env2.CLAUDE_CONFIG_DIR = opts.configDir;
108839
108973
  try {
108840
- const { stdout: stdout2, stderr } = await execFileAsync8("claude", args, {
108974
+ const { stdout: stdout2, stderr } = await execFileAsync9("claude", args, {
108841
108975
  env: env2,
108842
- timeout: opts?.timeoutMs ?? DEFAULT_TIMEOUT_MS2,
108976
+ timeout: opts?.timeoutMs ?? DEFAULT_TIMEOUT_MS3,
108843
108977
  maxBuffer: 10 * 1024 * 1024
108844
108978
  });
108845
108979
  return { ok: true, stdout: String(stdout2), stderr: String(stderr), code: 0 };
@@ -109065,12 +109199,23 @@ async function handlePluginInstall(ctx, deps = {}) {
109065
109199
  return ctx;
109066
109200
  }
109067
109201
  const migrate = deps.migrate ?? migrateLegacyToPlugin;
109202
+ const installCodex = deps.installCodex ?? installCodexPlugin;
109068
109203
  try {
109069
109204
  const pluginSourceDir = stagePluginSource(ctx.extractDir, deps.stageBaseDir);
109070
- const result = await migrate({ pluginSourceDir, claudeDir: ctx.claudeDir });
109071
- logPluginResult(result);
109205
+ try {
109206
+ const result = await migrate({ pluginSourceDir, claudeDir: ctx.claudeDir });
109207
+ logPluginResult(result);
109208
+ } catch (err) {
109209
+ logger.verbose(`Claude plugin install skipped (legacy copy retained): ${err.message}`);
109210
+ }
109211
+ try {
109212
+ const result = await installCodex({ pluginSourceDir });
109213
+ logCodexPluginResult(result);
109214
+ } catch (err) {
109215
+ logger.verbose(`Codex plugin install skipped: ${err.message}`);
109216
+ }
109072
109217
  } catch (err) {
109073
- logger.verbose(`Plugin install skipped (legacy copy retained): ${err.message}`);
109218
+ logger.verbose(`Plugin staging skipped (legacy copy retained): ${err.message}`);
109074
109219
  }
109075
109220
  return ctx;
109076
109221
  }
@@ -109082,17 +109227,88 @@ function stagePluginSource(extractDir, stageBaseDir) {
109082
109227
  }
109083
109228
  rmSync4(base2, { recursive: true, force: true });
109084
109229
  mkdirSync6(base2, { recursive: true });
109085
- cpSync2(payloadSrc, join135(base2, ".claude"), { recursive: true });
109086
- const marketplace = {
109230
+ const stagedPayload = join135(base2, ".claude");
109231
+ cpSync2(payloadSrc, stagedPayload, { recursive: true });
109232
+ ensureCodexPluginManifest(stagedPayload);
109233
+ const claudeMarketplace = {
109087
109234
  name: "claudekit",
109088
109235
  owner: { name: "ClaudeKit" },
109089
109236
  plugins: [{ name: "ck", source: "./.claude", description: "ClaudeKit Engineer" }]
109090
109237
  };
109091
109238
  mkdirSync6(join135(base2, ".claude-plugin"), { recursive: true });
109092
- writeFileSync8(join135(base2, ".claude-plugin", "marketplace.json"), `${JSON.stringify(marketplace, null, 2)}
109239
+ writeFileSync8(join135(base2, ".claude-plugin", "marketplace.json"), `${JSON.stringify(claudeMarketplace, null, 2)}
109240
+ `, "utf-8");
109241
+ const codexMarketplace = {
109242
+ name: "claudekit",
109243
+ interface: { displayName: "ClaudeKit" },
109244
+ plugins: [
109245
+ {
109246
+ name: "ck",
109247
+ source: { source: "local", path: "./.claude" },
109248
+ policy: { installation: "AVAILABLE", authentication: "ON_INSTALL" },
109249
+ category: "Productivity"
109250
+ }
109251
+ ]
109252
+ };
109253
+ mkdirSync6(join135(base2, ".agents", "plugins"), { recursive: true });
109254
+ writeFileSync8(join135(base2, ".agents", "plugins", "marketplace.json"), `${JSON.stringify(codexMarketplace, null, 2)}
109093
109255
  `, "utf-8");
109094
109256
  return base2;
109095
109257
  }
109258
+ function ensureCodexPluginManifest(pluginRoot) {
109259
+ const manifestPath = join135(pluginRoot, ".codex-plugin", "plugin.json");
109260
+ if (existsSync69(manifestPath))
109261
+ return;
109262
+ const claudeManifest = readJsonSafe3(join135(pluginRoot, ".claude-plugin", "plugin.json"));
109263
+ const manifest = pruneUndefined({
109264
+ name: stringField(claudeManifest, "name") ?? "ck",
109265
+ version: stringField(claudeManifest, "version") ?? "0.0.0",
109266
+ description: stringField(claudeManifest, "description") ?? "ClaudeKit Engineer — multi-agent planning, code review, debugging, and workflow skills for Codex.",
109267
+ author: authorField(claudeManifest),
109268
+ homepage: stringField(claudeManifest, "homepage"),
109269
+ repository: stringField(claudeManifest, "repository"),
109270
+ license: stringField(claudeManifest, "license"),
109271
+ keywords: ["claudekit", "codex", "skills", "agents", "workflow"],
109272
+ skills: "./skills/",
109273
+ interface: {
109274
+ displayName: "ClaudeKit Engineer",
109275
+ shortDescription: "ClaudeKit planning, review, debugging, and workflow skills.",
109276
+ longDescription: "ClaudeKit Engineer provides planning, code review, debugging, testing, browser workflow, and implementation skills for Codex.",
109277
+ developerName: "ClaudeKit",
109278
+ category: "Productivity",
109279
+ capabilities: ["Skills"],
109280
+ websiteURL: "https://github.com/claudekit/claudekit-engineer"
109281
+ }
109282
+ });
109283
+ mkdirSync6(join135(pluginRoot, ".codex-plugin"), { recursive: true });
109284
+ writeFileSync8(manifestPath, `${JSON.stringify(manifest, null, 2)}
109285
+ `, "utf-8");
109286
+ }
109287
+ function readJsonSafe3(filePath) {
109288
+ try {
109289
+ const parsed = JSON.parse(readFileSync21(filePath, "utf-8"));
109290
+ return isRecord3(parsed) ? parsed : null;
109291
+ } catch {
109292
+ return null;
109293
+ }
109294
+ }
109295
+ function stringField(source, key) {
109296
+ const value = source?.[key];
109297
+ return typeof value === "string" && value.trim() !== "" ? value : undefined;
109298
+ }
109299
+ function authorField(source) {
109300
+ const author = source?.author;
109301
+ if (isRecord3(author) && typeof author.name === "string" && author.name.trim() !== "") {
109302
+ return { name: author.name };
109303
+ }
109304
+ return { name: "ClaudeKit" };
109305
+ }
109306
+ function pruneUndefined(value) {
109307
+ return Object.fromEntries(Object.entries(value).filter(([, entry]) => entry !== undefined));
109308
+ }
109309
+ function isRecord3(value) {
109310
+ return typeof value === "object" && value !== null && !Array.isArray(value);
109311
+ }
109096
109312
  function logPluginResult(result) {
109097
109313
  switch (result.action) {
109098
109314
  case "migrated-from-legacy":
@@ -109112,6 +109328,19 @@ function logPluginResult(result) {
109112
109328
  break;
109113
109329
  }
109114
109330
  }
109331
+ function logCodexPluginResult(result) {
109332
+ switch (result.action) {
109333
+ case "installed":
109334
+ logger.info("Installed ClaudeKit Engineer as a Codex plugin.");
109335
+ break;
109336
+ case "skipped-codex-unsupported":
109337
+ logger.verbose("Codex plugin support unavailable; skipped Codex plugin install.");
109338
+ break;
109339
+ case "install-failed":
109340
+ logger.verbose(`Codex plugin install did not verify. ${result.error ?? ""}`);
109341
+ break;
109342
+ }
109343
+ }
109115
109344
  // src/commands/init/phases/selection-handler.ts
109116
109345
  init_config_manager();
109117
109346
  init_github_client();
@@ -109187,8 +109416,8 @@ async function detectAccessibleKits() {
109187
109416
  // src/domains/github/preflight-checker.ts
109188
109417
  init_logger();
109189
109418
  import { exec as exec8 } from "node:child_process";
109190
- import { promisify as promisify17 } from "node:util";
109191
- var execAsync8 = promisify17(exec8);
109419
+ import { promisify as promisify18 } from "node:util";
109420
+ var execAsync8 = promisify18(exec8);
109192
109421
  function createSuccessfulPreflightResult() {
109193
109422
  return {
109194
109423
  success: true,
@@ -109283,6 +109512,9 @@ async function runPreflightChecks() {
109283
109512
  return result;
109284
109513
  }
109285
109514
 
109515
+ // src/commands/init/phases/selection-handler.ts
109516
+ init_hook_health_checker();
109517
+
109286
109518
  // src/domains/installation/fresh-installer.ts
109287
109519
  init_metadata_migration();
109288
109520
  import { existsSync as existsSync70, readdirSync as readdirSync11, rmSync as rmSync5, rmdirSync as rmdirSync2, unlinkSync as unlinkSync5 } from "node:fs";
@@ -110028,8 +110260,13 @@ async function handleSelection(ctx) {
110028
110260
  const existingMetadata = await readManifest(claudeDir3);
110029
110261
  const installedKitVersion = existingMetadata?.kits?.[kitType]?.version;
110030
110262
  if (installedKitVersion && versionsMatch(installedKitVersion, releaseTag)) {
110031
- logger.success(`Already at latest version (${kitType}@${installedKitVersion}), skipping reinstall`);
110032
- return { ...ctx, cancelled: true };
110263
+ const missingHookFiles = await countMissingHookFileReferencesForClaudeDir(claudeDir3);
110264
+ if (missingHookFiles > 0) {
110265
+ logger.warning(`Detected ${missingHookFiles} registered hook(s) whose script is missing on disk; reinstalling to restore them`);
110266
+ } else {
110267
+ logger.success(`Already at latest version (${kitType}@${installedKitVersion}), skipping reinstall`);
110268
+ return { ...ctx, cancelled: true };
110269
+ }
110033
110270
  }
110034
110271
  } catch (error) {
110035
110272
  logger.verbose(`Metadata read failed, proceeding with installation: ${error instanceof Error ? error.message : "unknown"}`);
@@ -115778,11 +116015,11 @@ async function detectInstallations() {
115778
116015
 
115779
116016
  // src/commands/uninstall/removal-handler.ts
115780
116017
  import { readdirSync as readdirSync13, rmSync as rmSync8 } from "node:fs";
115781
- import { basename as basename33, join as join157, resolve as resolve56, sep as sep13 } from "node:path";
116018
+ import { basename as basename33, join as join158, resolve as resolve56, sep as sep14 } from "node:path";
115782
116019
  init_logger();
115783
116020
  init_safe_prompts();
115784
116021
  init_safe_spinner();
115785
- var import_fs_extra43 = __toESM(require_lib(), 1);
116022
+ var import_fs_extra44 = __toESM(require_lib(), 1);
115786
116023
 
115787
116024
  // src/commands/uninstall/analysis-handler.ts
115788
116025
  init_metadata_migration();
@@ -115934,10 +116171,170 @@ function displayDryRunPreview(analysis, installationType) {
115934
116171
  }
115935
116172
  }
115936
116173
 
116174
+ // src/commands/uninstall/settings-cleanup.ts
116175
+ init_settings_merger();
116176
+ init_command_normalizer();
116177
+ init_logger();
116178
+ var import_fs_extra43 = __toESM(require_lib(), 1);
116179
+ import { join as join157 } from "node:path";
116180
+ var CK_JSON_FILE2 = ".ck.json";
116181
+ var SETTINGS_FILE = "settings.json";
116182
+ var EMPTY_RESULT = {
116183
+ hooksRemoved: 0,
116184
+ mcpServersRemoved: 0,
116185
+ settingsFileRemoved: false,
116186
+ ckJsonRemoved: false
116187
+ };
116188
+ function resolveRemovalTargets(kits, removedKitNames, remainingKits) {
116189
+ const retainedHooks = new Set;
116190
+ const retainedServers = new Set;
116191
+ for (const remainingKit of remainingKits) {
116192
+ const installed = kits[remainingKit]?.installedSettings;
116193
+ for (const command of installed?.hooks ?? []) {
116194
+ const normalized = normalizeCommand(command);
116195
+ if (normalized)
116196
+ retainedHooks.add(normalized);
116197
+ }
116198
+ for (const server of installed?.mcpServers ?? []) {
116199
+ retainedServers.add(server);
116200
+ }
116201
+ }
116202
+ const hooks = new Set;
116203
+ const servers = new Set;
116204
+ for (const kitName of removedKitNames) {
116205
+ const installed = kits[kitName]?.installedSettings;
116206
+ for (const command of installed?.hooks ?? []) {
116207
+ const normalized = normalizeCommand(command);
116208
+ if (normalized && !retainedHooks.has(normalized))
116209
+ hooks.add(normalized);
116210
+ }
116211
+ for (const server of installed?.mcpServers ?? []) {
116212
+ if (!retainedServers.has(server))
116213
+ servers.add(server);
116214
+ }
116215
+ }
116216
+ return { hooks, servers };
116217
+ }
116218
+ function removeHooksFromSettings(settings, normalizedToRemove) {
116219
+ if (!settings.hooks || normalizedToRemove.size === 0)
116220
+ return 0;
116221
+ let removed = 0;
116222
+ const eventKeysToDelete = [];
116223
+ for (const [eventName, entries] of Object.entries(settings.hooks)) {
116224
+ const keptEntries = [];
116225
+ for (const entry of entries) {
116226
+ if (Array.isArray(entry.hooks)) {
116227
+ const keptHooks = entry.hooks.filter((hook) => {
116228
+ if (typeof hook.command === "string" && normalizedToRemove.has(normalizeCommand(hook.command))) {
116229
+ removed++;
116230
+ return false;
116231
+ }
116232
+ return true;
116233
+ });
116234
+ if (keptHooks.length > 0) {
116235
+ keptEntries.push({ ...entry, hooks: keptHooks });
116236
+ }
116237
+ continue;
116238
+ }
116239
+ if (typeof entry.command === "string" && normalizedToRemove.has(normalizeCommand(entry.command))) {
116240
+ removed++;
116241
+ continue;
116242
+ }
116243
+ keptEntries.push(entry);
116244
+ }
116245
+ if (keptEntries.length > 0) {
116246
+ settings.hooks[eventName] = keptEntries;
116247
+ } else {
116248
+ eventKeysToDelete.push(eventName);
116249
+ }
116250
+ }
116251
+ for (const eventName of eventKeysToDelete) {
116252
+ delete settings.hooks[eventName];
116253
+ }
116254
+ if (Object.keys(settings.hooks).length === 0) {
116255
+ Reflect.deleteProperty(settings, "hooks");
116256
+ }
116257
+ return removed;
116258
+ }
116259
+ function removeMcpServersFromSettings(settings, serversToRemove) {
116260
+ const servers = settings.mcp?.servers;
116261
+ if (!servers || serversToRemove.size === 0)
116262
+ return 0;
116263
+ let removed = 0;
116264
+ for (const name2 of serversToRemove) {
116265
+ if (Object.hasOwn(servers, name2)) {
116266
+ delete servers[name2];
116267
+ removed++;
116268
+ }
116269
+ }
116270
+ if (removed > 0 && settings.mcp) {
116271
+ if (Object.keys(servers).length === 0) {
116272
+ Reflect.deleteProperty(settings.mcp, "servers");
116273
+ }
116274
+ if (Object.keys(settings.mcp).length === 0) {
116275
+ Reflect.deleteProperty(settings, "mcp");
116276
+ }
116277
+ }
116278
+ return removed;
116279
+ }
116280
+ async function cleanupCkJson(ckJsonPath, data, removedKitNames, dryRun) {
116281
+ if (!data.kits)
116282
+ return false;
116283
+ for (const kitName of removedKitNames) {
116284
+ delete data.kits[kitName];
116285
+ }
116286
+ const noKitsLeft = Object.keys(data.kits).length === 0;
116287
+ const otherTopLevelKeys = Object.keys(data).filter((key) => key !== "kits");
116288
+ if (dryRun)
116289
+ return noKitsLeft && otherTopLevelKeys.length === 0;
116290
+ if (noKitsLeft && otherTopLevelKeys.length === 0) {
116291
+ await import_fs_extra43.remove(ckJsonPath);
116292
+ return true;
116293
+ }
116294
+ if (noKitsLeft) {
116295
+ Reflect.deleteProperty(data, "kits");
116296
+ }
116297
+ await import_fs_extra43.writeFile(ckJsonPath, JSON.stringify(data, null, 2), "utf-8");
116298
+ return false;
116299
+ }
116300
+ async function cleanupUninstalledSettings(installationPath, options2) {
116301
+ const ckJsonPath = join157(installationPath, CK_JSON_FILE2);
116302
+ if (!await import_fs_extra43.pathExists(ckJsonPath)) {
116303
+ return { ...EMPTY_RESULT };
116304
+ }
116305
+ let data;
116306
+ try {
116307
+ data = parseJsonContent(await import_fs_extra43.readFile(ckJsonPath, "utf-8"));
116308
+ } catch (error) {
116309
+ logger.debug(`Settings cleanup: failed to parse ${ckJsonPath}: ${error instanceof Error ? error.message : "unknown error"}`);
116310
+ return { ...EMPTY_RESULT };
116311
+ }
116312
+ const kits = data.kits ?? {};
116313
+ const removedKitNames = options2.kit ? [options2.kit] : Object.keys(kits);
116314
+ const { hooks, servers } = resolveRemovalTargets(kits, removedKitNames, options2.remainingKits);
116315
+ const result = { ...EMPTY_RESULT };
116316
+ const settingsPath = join157(installationPath, SETTINGS_FILE);
116317
+ const settings = await SettingsMerger.readSettingsFile(settingsPath);
116318
+ if (settings) {
116319
+ result.hooksRemoved = removeHooksFromSettings(settings, hooks);
116320
+ result.mcpServersRemoved = removeMcpServersFromSettings(settings, servers);
116321
+ if (!options2.dryRun && (result.hooksRemoved > 0 || result.mcpServersRemoved > 0)) {
116322
+ if (Object.keys(settings).length === 0) {
116323
+ await import_fs_extra43.remove(settingsPath);
116324
+ result.settingsFileRemoved = true;
116325
+ } else {
116326
+ await SettingsMerger.writeSettingsFile(settingsPath, settings);
116327
+ }
116328
+ }
116329
+ }
116330
+ result.ckJsonRemoved = await cleanupCkJson(ckJsonPath, data, removedKitNames, options2.dryRun ?? false);
116331
+ return result;
116332
+ }
116333
+
115937
116334
  // src/commands/uninstall/removal-handler.ts
115938
116335
  async function isDirectory2(filePath) {
115939
116336
  try {
115940
- const stats = await import_fs_extra43.lstat(filePath);
116337
+ const stats = await import_fs_extra44.lstat(filePath);
115941
116338
  return stats.isDirectory();
115942
116339
  } catch {
115943
116340
  logger.debug(`Failed to check if path is directory: ${filePath}`);
@@ -115945,10 +116342,30 @@ async function isDirectory2(filePath) {
115945
116342
  }
115946
116343
  }
115947
116344
  function getUninstallMutatePaths(options2) {
116345
+ const paths = [];
115948
116346
  if (options2.retainedManifestPaths.length > 0) {
115949
- return ["metadata.json"];
116347
+ paths.push("metadata.json");
115950
116348
  }
115951
- return [];
116349
+ if (options2.settingsFileExists) {
116350
+ paths.push("settings.json");
116351
+ }
116352
+ if (options2.ckJsonExists) {
116353
+ paths.push(".ck.json");
116354
+ }
116355
+ return paths;
116356
+ }
116357
+ function logSettingsCleanup(result, dryRun) {
116358
+ if (result.hooksRemoved === 0 && result.mcpServersRemoved === 0)
116359
+ return;
116360
+ const parts = [];
116361
+ if (result.hooksRemoved > 0) {
116362
+ parts.push(`${result.hooksRemoved} hook${result.hooksRemoved === 1 ? "" : "s"}`);
116363
+ }
116364
+ if (result.mcpServersRemoved > 0) {
116365
+ parts.push(`${result.mcpServersRemoved} MCP server${result.mcpServersRemoved === 1 ? "" : "s"}`);
116366
+ }
116367
+ const verb = dryRun ? "Would remove" : "Removed";
116368
+ log.info(`${verb} ${parts.join(" and ")} from settings.json`);
115952
116369
  }
115953
116370
  async function restoreUninstallBackup(backup) {
115954
116371
  const restoreSpinner = createSpinner("Restoring installation from recovery backup...").start();
@@ -115964,15 +116381,15 @@ async function isPathSafeToRemove(filePath, baseDir) {
115964
116381
  try {
115965
116382
  const resolvedPath = resolve56(filePath);
115966
116383
  const resolvedBase = resolve56(baseDir);
115967
- if (!resolvedPath.startsWith(resolvedBase + sep13) && resolvedPath !== resolvedBase) {
116384
+ if (!resolvedPath.startsWith(resolvedBase + sep14) && resolvedPath !== resolvedBase) {
115968
116385
  logger.debug(`Path outside installation directory: ${filePath}`);
115969
116386
  return false;
115970
116387
  }
115971
- const stats = await import_fs_extra43.lstat(filePath);
116388
+ const stats = await import_fs_extra44.lstat(filePath);
115972
116389
  if (stats.isSymbolicLink()) {
115973
- const realPath = await import_fs_extra43.realpath(filePath);
116390
+ const realPath = await import_fs_extra44.realpath(filePath);
115974
116391
  const resolvedReal = resolve56(realPath);
115975
- if (!resolvedReal.startsWith(resolvedBase + sep13) && resolvedReal !== resolvedBase) {
116392
+ if (!resolvedReal.startsWith(resolvedBase + sep14) && resolvedReal !== resolvedBase) {
115976
116393
  logger.debug(`Symlink points outside installation directory: ${filePath} -> ${realPath}`);
115977
116394
  return false;
115978
116395
  }
@@ -115994,6 +116411,12 @@ async function removeInstallations(installations, options2) {
115994
116411
  const label = options2.kit ? `${installation.type} (${options2.kit} kit)` : installation.type;
115995
116412
  const legacyLabel2 = !installation.hasMetadata ? " [legacy]" : "";
115996
116413
  displayDryRunPreview(analysis, `${label}${legacyLabel2}`);
116414
+ const settingsPreview = await cleanupUninstalledSettings(installation.path, {
116415
+ kit: options2.kit,
116416
+ remainingKits: analysis.remainingKits,
116417
+ dryRun: true
116418
+ });
116419
+ logSettingsCleanup(settingsPreview, true);
115997
116420
  if (analysis.remainingKits.length > 0) {
115998
116421
  log.info(`Remaining kits after uninstall: ${analysis.remainingKits.join(", ")}`);
115999
116422
  }
@@ -116003,7 +116426,9 @@ async function removeInstallations(installations, options2) {
116003
116426
  continue;
116004
116427
  }
116005
116428
  const mutatePaths = getUninstallMutatePaths({
116006
- retainedManifestPaths: analysis.retainedManifestPaths
116429
+ retainedManifestPaths: analysis.retainedManifestPaths,
116430
+ settingsFileExists: await import_fs_extra44.pathExists(join158(installation.path, "settings.json")),
116431
+ ckJsonExists: await import_fs_extra44.pathExists(join158(installation.path, ".ck.json"))
116007
116432
  });
116008
116433
  let backup = null;
116009
116434
  if (analysis.toDelete.length > 0 || mutatePaths.length > 0) {
@@ -116031,15 +116456,15 @@ async function removeInstallations(installations, options2) {
116031
116456
  let removedCount = 0;
116032
116457
  let cleanedDirs = 0;
116033
116458
  for (const item of analysis.toDelete) {
116034
- const filePath = join157(installation.path, item.path);
116035
- if (!await import_fs_extra43.pathExists(filePath))
116459
+ const filePath = join158(installation.path, item.path);
116460
+ if (!await import_fs_extra44.pathExists(filePath))
116036
116461
  continue;
116037
116462
  if (!await isPathSafeToRemove(filePath, installation.path)) {
116038
116463
  logger.debug(`Skipping unsafe path: ${item.path}`);
116039
116464
  continue;
116040
116465
  }
116041
116466
  const isDir2 = await isDirectory2(filePath);
116042
- await import_fs_extra43.remove(filePath);
116467
+ await import_fs_extra44.remove(filePath);
116043
116468
  removedCount++;
116044
116469
  logger.debug(`Removed ${isDir2 ? "directory" : "file"}: ${item.path}`);
116045
116470
  if (!isDir2) {
@@ -116055,6 +116480,11 @@ async function removeInstallations(installations, options2) {
116055
116480
  throw new Error("Failed to update metadata.json after partial uninstall");
116056
116481
  }
116057
116482
  }
116483
+ const settingsCleanup = await cleanupUninstalledSettings(installation.path, {
116484
+ kit: options2.kit,
116485
+ remainingKits: analysis.remainingKits
116486
+ });
116487
+ logSettingsCleanup(settingsCleanup, false);
116058
116488
  try {
116059
116489
  const remaining = readdirSync13(installation.path);
116060
116490
  if (remaining.length === 0) {
@@ -116453,7 +116883,7 @@ ${import_picocolors40.default.bold(import_picocolors40.default.cyan(result.kitCo
116453
116883
  init_logger();
116454
116884
  import { existsSync as existsSync82 } from "node:fs";
116455
116885
  import { rm as rm19 } from "node:fs/promises";
116456
- import { join as join164 } from "node:path";
116886
+ import { join as join165 } from "node:path";
116457
116887
  var import_picocolors41 = __toESM(require_picocolors(), 1);
116458
116888
 
116459
116889
  // src/commands/watch/phases/implementation-runner.ts
@@ -116971,8 +117401,8 @@ function spawnAndCollect3(command, args) {
116971
117401
  }
116972
117402
 
116973
117403
  // src/commands/watch/phases/issue-processor.ts
116974
- import { mkdir as mkdir40, writeFile as writeFile41 } from "node:fs/promises";
116975
- import { join as join160 } from "node:path";
117404
+ import { mkdir as mkdir40, writeFile as writeFile42 } from "node:fs/promises";
117405
+ import { join as join161 } from "node:path";
116976
117406
 
116977
117407
  // src/commands/watch/phases/approval-detector.ts
116978
117408
  init_logger();
@@ -117350,9 +117780,9 @@ async function checkAwaitingApproval(state, setup, options2, watchLog, projectDi
117350
117780
 
117351
117781
  // src/commands/watch/phases/plan-dir-finder.ts
117352
117782
  import { readdir as readdir46, stat as stat24 } from "node:fs/promises";
117353
- import { join as join159 } from "node:path";
117783
+ import { join as join160 } from "node:path";
117354
117784
  async function findRecentPlanDir(cwd2, issueNumber, watchLog) {
117355
- const plansRoot = join159(cwd2, "plans");
117785
+ const plansRoot = join160(cwd2, "plans");
117356
117786
  try {
117357
117787
  const entries = await readdir46(plansRoot);
117358
117788
  const tenMinAgo = Date.now() - 10 * 60 * 1000;
@@ -117361,14 +117791,14 @@ async function findRecentPlanDir(cwd2, issueNumber, watchLog) {
117361
117791
  for (const entry of entries) {
117362
117792
  if (entry === "watch" || entry === "reports" || entry === "visuals")
117363
117793
  continue;
117364
- const dirPath = join159(plansRoot, entry);
117794
+ const dirPath = join160(plansRoot, entry);
117365
117795
  const dirStat = await stat24(dirPath);
117366
117796
  if (!dirStat.isDirectory())
117367
117797
  continue;
117368
117798
  if (dirStat.mtimeMs < tenMinAgo)
117369
117799
  continue;
117370
117800
  try {
117371
- await stat24(join159(dirPath, "plan.md"));
117801
+ await stat24(join160(dirPath, "plan.md"));
117372
117802
  } catch {
117373
117803
  continue;
117374
117804
  }
@@ -117599,14 +118029,14 @@ async function handlePlanGeneration(issue, state, config, setup, options2, watch
117599
118029
  stats.plansCreated++;
117600
118030
  const detectedPlanDir = await findRecentPlanDir(projectDir, issue.number, watchLog);
117601
118031
  if (detectedPlanDir) {
117602
- state.activeIssues[numStr].planPath = join160(detectedPlanDir, "plan.md");
118032
+ state.activeIssues[numStr].planPath = join161(detectedPlanDir, "plan.md");
117603
118033
  watchLog.info(`Plan directory detected: ${detectedPlanDir}`);
117604
118034
  } else {
117605
118035
  try {
117606
- const planDir = join160(projectDir, "plans", "watch");
118036
+ const planDir = join161(projectDir, "plans", "watch");
117607
118037
  await mkdir40(planDir, { recursive: true });
117608
- const planFilePath = join160(planDir, `issue-${issue.number}-plan.md`);
117609
- await writeFile41(planFilePath, planResult.planText, "utf-8");
118038
+ const planFilePath = join161(planDir, `issue-${issue.number}-plan.md`);
118039
+ await writeFile42(planFilePath, planResult.planText, "utf-8");
117610
118040
  state.activeIssues[numStr].planPath = planFilePath;
117611
118041
  watchLog.info(`Plan saved (fallback) to ${planFilePath}`);
117612
118042
  } catch (err) {
@@ -117751,7 +118181,7 @@ init_ck_config_manager();
117751
118181
  init_file_io();
117752
118182
  init_logger();
117753
118183
  import { existsSync as existsSync78 } from "node:fs";
117754
- import { mkdir as mkdir41, readFile as readFile70 } from "node:fs/promises";
118184
+ import { mkdir as mkdir41, readFile as readFile71 } from "node:fs/promises";
117755
118185
  import { dirname as dirname54 } from "node:path";
117756
118186
  var PROCESSED_ISSUES_CAP = 500;
117757
118187
  async function readCkJson(projectDir) {
@@ -117759,7 +118189,7 @@ async function readCkJson(projectDir) {
117759
118189
  try {
117760
118190
  if (!existsSync78(configPath))
117761
118191
  return {};
117762
- const content = await readFile70(configPath, "utf-8");
118192
+ const content = await readFile71(configPath, "utf-8");
117763
118193
  return JSON.parse(content);
117764
118194
  } catch (error) {
117765
118195
  logger.warning(`Failed to parse .ck.json: ${error instanceof Error ? error.message : "Unknown"}`);
@@ -117912,18 +118342,18 @@ init_logger();
117912
118342
  import { spawnSync as spawnSync7 } from "node:child_process";
117913
118343
  import { existsSync as existsSync79 } from "node:fs";
117914
118344
  import { readdir as readdir47, stat as stat25 } from "node:fs/promises";
117915
- import { join as join161 } from "node:path";
118345
+ import { join as join162 } from "node:path";
117916
118346
  async function scanForRepos(parentDir) {
117917
118347
  const repos = [];
117918
118348
  const entries = await readdir47(parentDir);
117919
118349
  for (const entry of entries) {
117920
118350
  if (entry.startsWith("."))
117921
118351
  continue;
117922
- const fullPath = join161(parentDir, entry);
118352
+ const fullPath = join162(parentDir, entry);
117923
118353
  const entryStat = await stat25(fullPath);
117924
118354
  if (!entryStat.isDirectory())
117925
118355
  continue;
117926
- const gitDir = join161(fullPath, ".git");
118356
+ const gitDir = join162(fullPath, ".git");
117927
118357
  if (!existsSync79(gitDir))
117928
118358
  continue;
117929
118359
  const result = spawnSync7("gh", ["repo", "view", "--json", "owner,name"], {
@@ -117950,7 +118380,7 @@ init_logger();
117950
118380
  import { spawnSync as spawnSync8 } from "node:child_process";
117951
118381
  import { existsSync as existsSync80 } from "node:fs";
117952
118382
  import { homedir as homedir53 } from "node:os";
117953
- import { join as join162 } from "node:path";
118383
+ import { join as join163 } from "node:path";
117954
118384
  async function validateSetup(cwd2) {
117955
118385
  const workDir = cwd2 ?? process.cwd();
117956
118386
  const ghVersion = spawnSync8("gh", ["--version"], { encoding: "utf-8", timeout: 1e4 });
@@ -117981,7 +118411,7 @@ Run this command from a directory with a GitHub remote.`);
117981
118411
  } catch {
117982
118412
  throw new Error(`Failed to parse repository info: ${ghRepo.stdout}`);
117983
118413
  }
117984
- const skillsPath = join162(homedir53(), ".claude", "skills");
118414
+ const skillsPath = join163(homedir53(), ".claude", "skills");
117985
118415
  const skillsAvailable = existsSync80(skillsPath);
117986
118416
  if (!skillsAvailable) {
117987
118417
  logger.warning(`ClaudeKit Engineer skills not found at ${skillsPath}`);
@@ -118000,7 +118430,7 @@ init_path_resolver();
118000
118430
  import { createWriteStream as createWriteStream3, statSync as statSync14 } from "node:fs";
118001
118431
  import { existsSync as existsSync81 } from "node:fs";
118002
118432
  import { mkdir as mkdir42, rename as rename15 } from "node:fs/promises";
118003
- import { join as join163 } from "node:path";
118433
+ import { join as join164 } from "node:path";
118004
118434
 
118005
118435
  class WatchLogger {
118006
118436
  logStream = null;
@@ -118008,7 +118438,7 @@ class WatchLogger {
118008
118438
  logPath = null;
118009
118439
  maxBytes;
118010
118440
  constructor(logDir, maxBytes = 0) {
118011
- this.logDir = logDir ?? join163(PathResolver.getClaudeKitDir(), "logs");
118441
+ this.logDir = logDir ?? join164(PathResolver.getClaudeKitDir(), "logs");
118012
118442
  this.maxBytes = maxBytes;
118013
118443
  }
118014
118444
  async init() {
@@ -118017,7 +118447,7 @@ class WatchLogger {
118017
118447
  await mkdir42(this.logDir, { recursive: true });
118018
118448
  }
118019
118449
  const dateStr = formatDate(new Date);
118020
- this.logPath = join163(this.logDir, `watch-${dateStr}.log`);
118450
+ this.logPath = join164(this.logDir, `watch-${dateStr}.log`);
118021
118451
  this.logStream = createWriteStream3(this.logPath, { flags: "a", mode: 384 });
118022
118452
  } catch (error) {
118023
118453
  logger.warning(`Cannot create watch log file: ${error instanceof Error ? error.message : "Unknown"}`);
@@ -118199,7 +118629,7 @@ async function watchCommand(options2) {
118199
118629
  }
118200
118630
  async function discoverRepos(options2, watchLog) {
118201
118631
  const cwd2 = process.cwd();
118202
- const isGitRepo = existsSync82(join164(cwd2, ".git"));
118632
+ const isGitRepo = existsSync82(join165(cwd2, ".git"));
118203
118633
  if (options2.force) {
118204
118634
  await forceRemoveLock(watchLog);
118205
118635
  }
@@ -118456,8 +118886,8 @@ function registerCommands(cli) {
118456
118886
  // src/cli/version-display.ts
118457
118887
  init_package();
118458
118888
  init_config_version_checker();
118459
- import { existsSync as existsSync94, readFileSync as readFileSync25 } from "node:fs";
118460
- import { join as join176 } from "node:path";
118889
+ import { existsSync as existsSync94, readFileSync as readFileSync26 } from "node:fs";
118890
+ import { join as join177 } from "node:path";
118461
118891
 
118462
118892
  // src/domains/versioning/version-checker.ts
118463
118893
  init_version_utils();
@@ -118471,15 +118901,15 @@ init_types3();
118471
118901
  init_logger();
118472
118902
  init_path_resolver();
118473
118903
  import { existsSync as existsSync93 } from "node:fs";
118474
- import { mkdir as mkdir43, readFile as readFile72, writeFile as writeFile44 } from "node:fs/promises";
118475
- import { join as join175 } from "node:path";
118904
+ import { mkdir as mkdir43, readFile as readFile73, writeFile as writeFile45 } from "node:fs/promises";
118905
+ import { join as join176 } from "node:path";
118476
118906
 
118477
118907
  class VersionCacheManager {
118478
118908
  static CACHE_FILENAME = "version-check.json";
118479
118909
  static CACHE_TTL_MS = 7 * 24 * 60 * 60 * 1000;
118480
118910
  static getCacheFile() {
118481
118911
  const cacheDir = PathResolver.getCacheDir(false);
118482
- return join175(cacheDir, VersionCacheManager.CACHE_FILENAME);
118912
+ return join176(cacheDir, VersionCacheManager.CACHE_FILENAME);
118483
118913
  }
118484
118914
  static async load() {
118485
118915
  const cacheFile = VersionCacheManager.getCacheFile();
@@ -118488,7 +118918,7 @@ class VersionCacheManager {
118488
118918
  logger.debug("Version check cache not found");
118489
118919
  return null;
118490
118920
  }
118491
- const content = await readFile72(cacheFile, "utf-8");
118921
+ const content = await readFile73(cacheFile, "utf-8");
118492
118922
  const cache5 = JSON.parse(content);
118493
118923
  if (!cache5.lastCheck || !cache5.currentVersion || !cache5.latestVersion) {
118494
118924
  logger.debug("Invalid cache structure, ignoring");
@@ -118508,7 +118938,7 @@ class VersionCacheManager {
118508
118938
  if (!existsSync93(cacheDir)) {
118509
118939
  await mkdir43(cacheDir, { recursive: true, mode: 448 });
118510
118940
  }
118511
- await writeFile44(cacheFile, JSON.stringify(cache5, null, 2), "utf-8");
118941
+ await writeFile45(cacheFile, JSON.stringify(cache5, null, 2), "utf-8");
118512
118942
  logger.debug(`Version check cache saved to ${cacheFile}`);
118513
118943
  } catch (error) {
118514
118944
  logger.debug(`Failed to save version check cache: ${error}`);
@@ -118790,13 +119220,13 @@ async function displayVersion() {
118790
119220
  let localInstalledKits = [];
118791
119221
  let globalInstalledKits = [];
118792
119222
  const globalKitDir = PathResolver.getGlobalKitDir();
118793
- const globalMetadataPath = join176(globalKitDir, "metadata.json");
119223
+ const globalMetadataPath = join177(globalKitDir, "metadata.json");
118794
119224
  const prefix = PathResolver.getPathPrefix(false);
118795
- const localMetadataPath = prefix ? join176(process.cwd(), prefix, "metadata.json") : join176(process.cwd(), "metadata.json");
119225
+ const localMetadataPath = prefix ? join177(process.cwd(), prefix, "metadata.json") : join177(process.cwd(), "metadata.json");
118796
119226
  const isLocalSameAsGlobal = localMetadataPath === globalMetadataPath;
118797
119227
  if (!isLocalSameAsGlobal && existsSync94(localMetadataPath)) {
118798
119228
  try {
118799
- const rawMetadata = JSON.parse(readFileSync25(localMetadataPath, "utf-8"));
119229
+ const rawMetadata = JSON.parse(readFileSync26(localMetadataPath, "utf-8"));
118800
119230
  const metadata = MetadataSchema.parse(rawMetadata);
118801
119231
  const kitsDisplay = formatInstalledKits(metadata);
118802
119232
  if (kitsDisplay) {
@@ -118810,7 +119240,7 @@ async function displayVersion() {
118810
119240
  }
118811
119241
  if (existsSync94(globalMetadataPath)) {
118812
119242
  try {
118813
- const rawMetadata = JSON.parse(readFileSync25(globalMetadataPath, "utf-8"));
119243
+ const rawMetadata = JSON.parse(readFileSync26(globalMetadataPath, "utf-8"));
118814
119244
  const metadata = MetadataSchema.parse(rawMetadata);
118815
119245
  const kitsDisplay = formatInstalledKits(metadata);
118816
119246
  if (kitsDisplay) {
@@ -119180,7 +119610,7 @@ var output2 = new OutputManager2;
119180
119610
 
119181
119611
  // src/shared/temp-cleanup.ts
119182
119612
  init_logger();
119183
- var import_fs_extra44 = __toESM(require_lib(), 1);
119613
+ var import_fs_extra45 = __toESM(require_lib(), 1);
119184
119614
  import { rmSync as rmSync9 } from "node:fs";
119185
119615
  var tempDirs2 = new Set;
119186
119616
  async function cleanup() {
@@ -119189,7 +119619,7 @@ async function cleanup() {
119189
119619
  logger.debug(`Cleaning up ${tempDirs2.size} temporary director(ies)...`);
119190
119620
  for (const dir of tempDirs2) {
119191
119621
  try {
119192
- await import_fs_extra44.remove(dir);
119622
+ await import_fs_extra45.remove(dir);
119193
119623
  logger.debug(`Cleaned up temp directory: ${dir}`);
119194
119624
  } catch (error) {
119195
119625
  logger.debug(`Failed to clean temp directory ${dir}: ${error}`);