claudekit-cli 4.1.1 → 4.2.1

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/cli-manifest.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
- "version": "4.1.1",
3
- "generatedAt": "2026-05-11T16:31:15.910Z",
2
+ "version": "4.2.1",
3
+ "generatedAt": "2026-05-12T16:32:49.942Z",
4
4
  "commands": {
5
5
  "agents": {
6
6
  "name": "agents",
package/dist/index.js CHANGED
@@ -62890,7 +62890,7 @@ var package_default;
62890
62890
  var init_package = __esm(() => {
62891
62891
  package_default = {
62892
62892
  name: "claudekit-cli",
62893
- version: "4.1.1",
62893
+ version: "4.2.1",
62894
62894
  description: "CLI tool for bootstrapping and updating ClaudeKit projects",
62895
62895
  type: "module",
62896
62896
  repository: {
@@ -62934,6 +62934,7 @@ var init_package = __esm(() => {
62934
62934
  "dev:all": "./scripts/dev-quick-start.sh all",
62935
62935
  metrics: "bun run scripts/workflow-metrics.ts",
62936
62936
  validate: "bun run typecheck && bun run lint && bun test && bun run ui:test && bun run build",
62937
+ "ci:local": "bash scripts/ci-local.sh",
62937
62938
  "install:hooks": "./.githooks/install.sh",
62938
62939
  prepare: `node -e "try{require('child_process').execSync('git rev-parse --git-dir',{stdio:'ignore'});require('child_process').execSync('bash .githooks/install.sh',{stdio:'inherit'})}catch(e){console.warn('[i] Hook install skipped:',e.message)}"`,
62939
62940
  "help:check-parity": "bun run scripts/check-help-parity.ts",
@@ -64888,8 +64889,20 @@ function buildInitCommand(isGlobal, kit, beta, yes) {
64888
64889
  function resolveCkExecutable(platformName = process.platform) {
64889
64890
  return platformName === "win32" ? "ck.cmd" : "ck";
64890
64891
  }
64891
- function shouldRunCkExecutableInShell(platformName = process.platform) {
64892
- return platformName === "win32";
64892
+ function resolveCkInitSpawnCommand(initArgs, options2 = {}) {
64893
+ const execPath = options2.execPath ?? process.execPath;
64894
+ const argv = options2.argv ?? process.argv;
64895
+ const currentEntrypoint = argv[1];
64896
+ if (currentEntrypoint) {
64897
+ return {
64898
+ command: execPath,
64899
+ args: [currentEntrypoint, ...initArgs]
64900
+ };
64901
+ }
64902
+ return {
64903
+ command: resolveCkExecutable(options2.platformName),
64904
+ args: initArgs
64905
+ };
64893
64906
  }
64894
64907
  async function fetchLatestReleaseTag(kit, beta) {
64895
64908
  try {
@@ -64999,10 +65012,8 @@ async function promptKitUpdate(beta, yes, deps) {
64999
65012
  const displayCmd = `ck ${args.join(" ")}`;
65000
65013
  logger.info(`Running: ${displayCmd}`);
65001
65014
  const spawnFn = deps?.spawnInitFn ?? ((spawnArgs) => new Promise((resolve30) => {
65002
- const child = spawn2(resolveCkExecutable(), spawnArgs, {
65003
- stdio: "inherit",
65004
- shell: shouldRunCkExecutableInShell()
65005
- });
65015
+ const initCommand = resolveCkInitSpawnCommand(spawnArgs);
65016
+ const child = spawn2(initCommand.command, initCommand.args, { stdio: "inherit" });
65006
65017
  child.on("close", (code) => resolve30(code ?? 1));
65007
65018
  child.on("error", (err) => {
65008
65019
  logger.verbose(`Failed to spawn ck init: ${err.message}`);
@@ -74173,9 +74184,9 @@ __export(exports_worktree_manager, {
74173
74184
  });
74174
74185
  import { existsSync as existsSync70 } from "node:fs";
74175
74186
  import { readFile as readFile66, writeFile as writeFile37 } from "node:fs/promises";
74176
- import { join as join151 } from "node:path";
74187
+ import { join as join152 } from "node:path";
74177
74188
  async function createWorktree(projectDir, issueNumber, baseBranch) {
74178
- const worktreePath = join151(projectDir, WORKTREE_DIR, `issue-${issueNumber}`);
74189
+ const worktreePath = join152(projectDir, WORKTREE_DIR, `issue-${issueNumber}`);
74179
74190
  const branchName = `ck-watch/issue-${issueNumber}`;
74180
74191
  await spawnAndCollect("git", ["fetch", "origin", baseBranch], projectDir).catch(() => {
74181
74192
  logger.warning(`[worktree] Could not fetch origin/${baseBranch}, using local`);
@@ -74193,7 +74204,7 @@ async function createWorktree(projectDir, issueNumber, baseBranch) {
74193
74204
  return worktreePath;
74194
74205
  }
74195
74206
  async function removeWorktree(projectDir, issueNumber) {
74196
- const worktreePath = join151(projectDir, WORKTREE_DIR, `issue-${issueNumber}`);
74207
+ const worktreePath = join152(projectDir, WORKTREE_DIR, `issue-${issueNumber}`);
74197
74208
  const branchName = `ck-watch/issue-${issueNumber}`;
74198
74209
  try {
74199
74210
  await spawnAndCollect("git", ["worktree", "remove", worktreePath, "--force"], projectDir);
@@ -74207,7 +74218,7 @@ async function listActiveWorktrees(projectDir) {
74207
74218
  try {
74208
74219
  const output2 = await spawnAndCollect("git", ["worktree", "list", "--porcelain"], projectDir);
74209
74220
  const issueNumbers = [];
74210
- const worktreePrefix = join151(projectDir, WORKTREE_DIR, "issue-").replace(/\\/g, "/");
74221
+ const worktreePrefix = join152(projectDir, WORKTREE_DIR, "issue-").replace(/\\/g, "/");
74211
74222
  for (const line of output2.split(`
74212
74223
  `)) {
74213
74224
  if (line.startsWith("worktree ")) {
@@ -74235,7 +74246,7 @@ async function cleanupAllWorktrees(projectDir) {
74235
74246
  await spawnAndCollect("git", ["worktree", "prune"], projectDir).catch(() => {});
74236
74247
  }
74237
74248
  async function ensureGitignore(projectDir) {
74238
- const gitignorePath = join151(projectDir, ".gitignore");
74249
+ const gitignorePath = join152(projectDir, ".gitignore");
74239
74250
  try {
74240
74251
  const content = existsSync70(gitignorePath) ? await readFile66(gitignorePath, "utf-8") : "";
74241
74252
  if (!content.includes(".worktrees")) {
@@ -74342,7 +74353,7 @@ import { createHash as createHash9 } from "node:crypto";
74342
74353
  import { existsSync as existsSync76, mkdirSync as mkdirSync5, readFileSync as readFileSync18, readdirSync as readdirSync11, statSync as statSync14 } from "node:fs";
74343
74354
  import { rename as rename14, writeFile as writeFile39 } from "node:fs/promises";
74344
74355
  import { homedir as homedir53 } from "node:os";
74345
- import { basename as basename31, join as join158 } from "node:path";
74356
+ import { basename as basename31, join as join159 } from "node:path";
74346
74357
  function getCachedContext(repoPath) {
74347
74358
  const cachePath = getCacheFilePath(repoPath);
74348
74359
  if (!existsSync76(cachePath))
@@ -74385,25 +74396,25 @@ function computeSourceHash(repoPath) {
74385
74396
  }
74386
74397
  function getDocSourcePaths(repoPath) {
74387
74398
  const paths = [];
74388
- const docsDir = join158(repoPath, "docs");
74399
+ const docsDir = join159(repoPath, "docs");
74389
74400
  if (existsSync76(docsDir)) {
74390
74401
  try {
74391
74402
  const files = readdirSync11(docsDir);
74392
74403
  for (const f3 of files) {
74393
74404
  if (f3.endsWith(".md"))
74394
- paths.push(join158(docsDir, f3));
74405
+ paths.push(join159(docsDir, f3));
74395
74406
  }
74396
74407
  } catch {}
74397
74408
  }
74398
- const readme = join158(repoPath, "README.md");
74409
+ const readme = join159(repoPath, "README.md");
74399
74410
  if (existsSync76(readme))
74400
74411
  paths.push(readme);
74401
- const stylesDir = join158(repoPath, "assets", "writing-styles");
74412
+ const stylesDir = join159(repoPath, "assets", "writing-styles");
74402
74413
  if (existsSync76(stylesDir)) {
74403
74414
  try {
74404
74415
  const files = readdirSync11(stylesDir);
74405
74416
  for (const f3 of files) {
74406
- paths.push(join158(stylesDir, f3));
74417
+ paths.push(join159(stylesDir, f3));
74407
74418
  }
74408
74419
  } catch {}
74409
74420
  }
@@ -74412,11 +74423,11 @@ function getDocSourcePaths(repoPath) {
74412
74423
  function getCacheFilePath(repoPath) {
74413
74424
  const repoName = basename31(repoPath).replace(/[^a-zA-Z0-9_-]/g, "_");
74414
74425
  const pathHash = createHash9("sha256").update(repoPath).digest("hex").slice(0, 8);
74415
- return join158(CACHE_DIR, `${repoName}-${pathHash}-context-cache.json`);
74426
+ return join159(CACHE_DIR, `${repoName}-${pathHash}-context-cache.json`);
74416
74427
  }
74417
74428
  var CACHE_DIR, CACHE_TTL_MS5;
74418
74429
  var init_context_cache_manager = __esm(() => {
74419
- CACHE_DIR = join158(homedir53(), ".claudekit", "cache");
74430
+ CACHE_DIR = join159(homedir53(), ".claudekit", "cache");
74420
74431
  CACHE_TTL_MS5 = 24 * 60 * 60 * 1000;
74421
74432
  });
74422
74433
 
@@ -74597,7 +74608,7 @@ function extractContentFromResponse(response) {
74597
74608
  // src/commands/content/phases/docs-summarizer.ts
74598
74609
  import { execSync as execSync7 } from "node:child_process";
74599
74610
  import { existsSync as existsSync77, readFileSync as readFileSync19, readdirSync as readdirSync12 } from "node:fs";
74600
- import { join as join159 } from "node:path";
74611
+ import { join as join160 } from "node:path";
74601
74612
  async function summarizeProjectDocs(repoPath, contentLogger) {
74602
74613
  const rawContent = collectRawDocs(repoPath);
74603
74614
  if (rawContent.total.length < 200) {
@@ -74651,12 +74662,12 @@ function collectRawDocs(repoPath) {
74651
74662
  return capped;
74652
74663
  };
74653
74664
  const docsContent = [];
74654
- const docsDir = join159(repoPath, "docs");
74665
+ const docsDir = join160(repoPath, "docs");
74655
74666
  if (existsSync77(docsDir)) {
74656
74667
  try {
74657
74668
  const files = readdirSync12(docsDir).filter((f3) => f3.endsWith(".md")).sort();
74658
74669
  for (const f3 of files) {
74659
- const content = readCapped(join159(docsDir, f3), 5000);
74670
+ const content = readCapped(join160(docsDir, f3), 5000);
74660
74671
  if (content) {
74661
74672
  docsContent.push(`### ${f3}
74662
74673
  ${content}`);
@@ -74670,21 +74681,21 @@ ${content}`);
74670
74681
  let brand = "";
74671
74682
  const brandCandidates = ["docs/brand-guidelines.md", "docs/design-guidelines.md"];
74672
74683
  for (const p of brandCandidates) {
74673
- brand = readCapped(join159(repoPath, p), 3000);
74684
+ brand = readCapped(join160(repoPath, p), 3000);
74674
74685
  if (brand)
74675
74686
  break;
74676
74687
  }
74677
74688
  let styles3 = "";
74678
- const stylesDir = join159(repoPath, "assets", "writing-styles");
74689
+ const stylesDir = join160(repoPath, "assets", "writing-styles");
74679
74690
  if (existsSync77(stylesDir)) {
74680
74691
  try {
74681
74692
  const files = readdirSync12(stylesDir).slice(0, 3);
74682
- styles3 = files.map((f3) => readCapped(join159(stylesDir, f3), 1000)).filter(Boolean).join(`
74693
+ styles3 = files.map((f3) => readCapped(join160(stylesDir, f3), 1000)).filter(Boolean).join(`
74683
74694
 
74684
74695
  `);
74685
74696
  } catch {}
74686
74697
  }
74687
- const readme = readCapped(join159(repoPath, "README.md"), 3000);
74698
+ const readme = readCapped(join160(repoPath, "README.md"), 3000);
74688
74699
  const total = [docs, brand, styles3, readme].join(`
74689
74700
  `);
74690
74701
  return { docs, brand, styles: styles3, readme, total };
@@ -74871,9 +74882,9 @@ IMPORTANT: Generate the image and output the path as JSON: {"imagePath": "/path/
74871
74882
  import { execSync as execSync8 } from "node:child_process";
74872
74883
  import { existsSync as existsSync78, mkdirSync as mkdirSync6, readdirSync as readdirSync13 } from "node:fs";
74873
74884
  import { homedir as homedir54 } from "node:os";
74874
- import { join as join160 } from "node:path";
74885
+ import { join as join161 } from "node:path";
74875
74886
  async function generatePhoto(_content, context, config, platform18, contentId, contentLogger) {
74876
- const mediaDir = join160(config.contentDir.replace(/^~/, homedir54()), "media", String(contentId));
74887
+ const mediaDir = join161(config.contentDir.replace(/^~/, homedir54()), "media", String(contentId));
74877
74888
  if (!existsSync78(mediaDir)) {
74878
74889
  mkdirSync6(mediaDir, { recursive: true });
74879
74890
  }
@@ -74898,7 +74909,7 @@ async function generatePhoto(_content, context, config, platform18, contentId, c
74898
74909
  const imageFile = files.find((f3) => /\.(png|jpg|jpeg|webp)$/i.test(f3));
74899
74910
  if (imageFile) {
74900
74911
  const ext2 = imageFile.split(".").pop() ?? "png";
74901
- return { path: join160(mediaDir, imageFile), ...dimensions, format: ext2 };
74912
+ return { path: join161(mediaDir, imageFile), ...dimensions, format: ext2 };
74902
74913
  }
74903
74914
  contentLogger.warn(`Photo generation produced no image for content ${contentId}`);
74904
74915
  return null;
@@ -74988,7 +74999,7 @@ var init_content_creator = __esm(() => {
74988
74999
  // src/commands/content/phases/content-logger.ts
74989
75000
  import { createWriteStream as createWriteStream4, existsSync as existsSync79, mkdirSync as mkdirSync7, statSync as statSync15 } from "node:fs";
74990
75001
  import { homedir as homedir55 } from "node:os";
74991
- import { join as join161 } from "node:path";
75002
+ import { join as join162 } from "node:path";
74992
75003
 
74993
75004
  class ContentLogger {
74994
75005
  stream = null;
@@ -74996,7 +75007,7 @@ class ContentLogger {
74996
75007
  logDir;
74997
75008
  maxBytes;
74998
75009
  constructor(maxBytes = 0) {
74999
- this.logDir = join161(homedir55(), ".claudekit", "logs");
75010
+ this.logDir = join162(homedir55(), ".claudekit", "logs");
75000
75011
  this.maxBytes = maxBytes;
75001
75012
  }
75002
75013
  init() {
@@ -75028,7 +75039,7 @@ class ContentLogger {
75028
75039
  }
75029
75040
  }
75030
75041
  getLogPath() {
75031
- return join161(this.logDir, `content-${this.getDateStr()}.log`);
75042
+ return join162(this.logDir, `content-${this.getDateStr()}.log`);
75032
75043
  }
75033
75044
  write(level, message) {
75034
75045
  this.rotateIfNeeded();
@@ -75045,18 +75056,18 @@ class ContentLogger {
75045
75056
  if (dateStr !== this.currentDate) {
75046
75057
  this.close();
75047
75058
  this.currentDate = dateStr;
75048
- const logPath = join161(this.logDir, `content-${dateStr}.log`);
75059
+ const logPath = join162(this.logDir, `content-${dateStr}.log`);
75049
75060
  this.stream = createWriteStream4(logPath, { flags: "a", mode: 384 });
75050
75061
  return;
75051
75062
  }
75052
75063
  if (this.maxBytes > 0 && this.stream) {
75053
- const logPath = join161(this.logDir, `content-${this.currentDate}.log`);
75064
+ const logPath = join162(this.logDir, `content-${this.currentDate}.log`);
75054
75065
  try {
75055
75066
  const stat25 = statSync15(logPath);
75056
75067
  if (stat25.size >= this.maxBytes) {
75057
75068
  this.close();
75058
75069
  const suffix = Date.now();
75059
- const rotatedPath = join161(this.logDir, `content-${this.currentDate}-${suffix}.log`);
75070
+ const rotatedPath = join162(this.logDir, `content-${this.currentDate}-${suffix}.log`);
75060
75071
  import("node:fs/promises").then(({ rename: rename15 }) => rename15(logPath, rotatedPath).catch(() => {}));
75061
75072
  this.stream = createWriteStream4(logPath, { flags: "w", mode: 384 });
75062
75073
  }
@@ -75282,7 +75293,7 @@ function isNoiseCommit(title, author) {
75282
75293
  // src/commands/content/phases/change-detector.ts
75283
75294
  import { execSync as execSync10, spawnSync as spawnSync9 } from "node:child_process";
75284
75295
  import { existsSync as existsSync81, readFileSync as readFileSync20, readdirSync as readdirSync14, statSync as statSync16 } from "node:fs";
75285
- import { join as join162 } from "node:path";
75296
+ import { join as join163 } from "node:path";
75286
75297
  function detectCommits(repo, since) {
75287
75298
  try {
75288
75299
  const fetchUrl = sshToHttps(repo.remoteUrl);
@@ -75391,7 +75402,7 @@ function detectTags(repo, since) {
75391
75402
  }
75392
75403
  }
75393
75404
  function detectCompletedPlans(repo, since) {
75394
- const plansDir = join162(repo.path, "plans");
75405
+ const plansDir = join163(repo.path, "plans");
75395
75406
  if (!existsSync81(plansDir))
75396
75407
  return [];
75397
75408
  const sinceMs = new Date(since).getTime();
@@ -75401,7 +75412,7 @@ function detectCompletedPlans(repo, since) {
75401
75412
  for (const entry of entries) {
75402
75413
  if (!entry.isDirectory())
75403
75414
  continue;
75404
- const planFile = join162(plansDir, entry.name, "plan.md");
75415
+ const planFile = join163(plansDir, entry.name, "plan.md");
75405
75416
  if (!existsSync81(planFile))
75406
75417
  continue;
75407
75418
  try {
@@ -75479,7 +75490,7 @@ function classifyCommit(event) {
75479
75490
  // src/commands/content/phases/repo-discoverer.ts
75480
75491
  import { execSync as execSync11 } from "node:child_process";
75481
75492
  import { readdirSync as readdirSync15 } from "node:fs";
75482
- import { join as join163 } from "node:path";
75493
+ import { join as join164 } from "node:path";
75483
75494
  function discoverRepos2(cwd2) {
75484
75495
  const repos = [];
75485
75496
  if (isGitRepoRoot(cwd2)) {
@@ -75492,7 +75503,7 @@ function discoverRepos2(cwd2) {
75492
75503
  for (const entry of entries) {
75493
75504
  if (!entry.isDirectory() || entry.name.startsWith("."))
75494
75505
  continue;
75495
- const dirPath = join163(cwd2, entry.name);
75506
+ const dirPath = join164(cwd2, entry.name);
75496
75507
  if (isGitRepoRoot(dirPath)) {
75497
75508
  const info = getRepoInfo(dirPath);
75498
75509
  if (info)
@@ -76160,9 +76171,9 @@ var init_types6 = __esm(() => {
76160
76171
 
76161
76172
  // src/commands/content/phases/state-manager.ts
76162
76173
  import { readFile as readFile68, rename as rename15, writeFile as writeFile40 } from "node:fs/promises";
76163
- import { join as join164 } from "node:path";
76174
+ import { join as join165 } from "node:path";
76164
76175
  async function loadContentConfig(projectDir) {
76165
- const configPath = join164(projectDir, CK_CONFIG_FILE2);
76176
+ const configPath = join165(projectDir, CK_CONFIG_FILE2);
76166
76177
  try {
76167
76178
  const raw2 = await readFile68(configPath, "utf-8");
76168
76179
  const json = JSON.parse(raw2);
@@ -76172,13 +76183,13 @@ async function loadContentConfig(projectDir) {
76172
76183
  }
76173
76184
  }
76174
76185
  async function saveContentConfig(projectDir, config) {
76175
- const configPath = join164(projectDir, CK_CONFIG_FILE2);
76186
+ const configPath = join165(projectDir, CK_CONFIG_FILE2);
76176
76187
  const json = await readJsonSafe(configPath);
76177
76188
  json.content = { ...json.content, ...config };
76178
76189
  await atomicWrite2(configPath, json);
76179
76190
  }
76180
76191
  async function loadContentState(projectDir) {
76181
- const configPath = join164(projectDir, CK_CONFIG_FILE2);
76192
+ const configPath = join165(projectDir, CK_CONFIG_FILE2);
76182
76193
  try {
76183
76194
  const raw2 = await readFile68(configPath, "utf-8");
76184
76195
  const json = JSON.parse(raw2);
@@ -76189,7 +76200,7 @@ async function loadContentState(projectDir) {
76189
76200
  }
76190
76201
  }
76191
76202
  async function saveContentState(projectDir, state) {
76192
- const configPath = join164(projectDir, CK_CONFIG_FILE2);
76203
+ const configPath = join165(projectDir, CK_CONFIG_FILE2);
76193
76204
  const sevenDaysAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString().slice(0, 10);
76194
76205
  for (const key of Object.keys(state.dailyPostCounts)) {
76195
76206
  const dateStr = key.slice(-10);
@@ -76471,7 +76482,7 @@ var init_platform_setup_x = __esm(() => {
76471
76482
 
76472
76483
  // src/commands/content/phases/setup-wizard.ts
76473
76484
  import { existsSync as existsSync82 } from "node:fs";
76474
- import { join as join165 } from "node:path";
76485
+ import { join as join166 } from "node:path";
76475
76486
  async function runSetupWizard2(cwd2, contentLogger) {
76476
76487
  console.log();
76477
76488
  oe(import_picocolors43.default.bgCyan(import_picocolors43.default.white(" CK Content — Multi-Channel Content Engine ")));
@@ -76539,8 +76550,8 @@ async function showRepoSummary(cwd2) {
76539
76550
  function detectBrandAssets(cwd2, contentLogger) {
76540
76551
  const repos = discoverRepos2(cwd2);
76541
76552
  for (const repo of repos) {
76542
- const hasGuidelines = existsSync82(join165(repo.path, "docs", "brand-guidelines.md"));
76543
- const hasStyles = existsSync82(join165(repo.path, "assets", "writing-styles"));
76553
+ const hasGuidelines = existsSync82(join166(repo.path, "docs", "brand-guidelines.md"));
76554
+ const hasStyles = existsSync82(join166(repo.path, "assets", "writing-styles"));
76544
76555
  if (!hasGuidelines) {
76545
76556
  f2.warning(`${repo.name}: No docs/brand-guidelines.md — content will use generic tone.`);
76546
76557
  contentLogger.warn(`${repo.name}: missing docs/brand-guidelines.md`);
@@ -76682,9 +76693,9 @@ __export(exports_content_subcommands, {
76682
76693
  });
76683
76694
  import { existsSync as existsSync84, readFileSync as readFileSync21, unlinkSync as unlinkSync6 } from "node:fs";
76684
76695
  import { homedir as homedir57 } from "node:os";
76685
- import { join as join166 } from "node:path";
76696
+ import { join as join167 } from "node:path";
76686
76697
  function isDaemonRunning() {
76687
- const lockFile = join166(LOCK_DIR, `${LOCK_NAME2}.lock`);
76698
+ const lockFile = join167(LOCK_DIR, `${LOCK_NAME2}.lock`);
76688
76699
  if (!existsSync84(lockFile))
76689
76700
  return { running: false, pid: null };
76690
76701
  try {
@@ -76716,7 +76727,7 @@ async function startContent(options2) {
76716
76727
  await contentCommand(options2);
76717
76728
  }
76718
76729
  async function stopContent() {
76719
- const lockFile = join166(LOCK_DIR, `${LOCK_NAME2}.lock`);
76730
+ const lockFile = join167(LOCK_DIR, `${LOCK_NAME2}.lock`);
76720
76731
  if (!existsSync84(lockFile)) {
76721
76732
  logger.info("Content daemon is not running.");
76722
76733
  return;
@@ -76755,9 +76766,9 @@ async function statusContent() {
76755
76766
  } catch {}
76756
76767
  }
76757
76768
  async function logsContent(options2) {
76758
- const logDir = join166(homedir57(), ".claudekit", "logs");
76769
+ const logDir = join167(homedir57(), ".claudekit", "logs");
76759
76770
  const dateStr = new Date().toISOString().slice(0, 10).replace(/-/g, "");
76760
- const logPath = join166(logDir, `content-${dateStr}.log`);
76771
+ const logPath = join167(logDir, `content-${dateStr}.log`);
76761
76772
  if (!existsSync84(logPath)) {
76762
76773
  logger.info("No content logs found for today.");
76763
76774
  return;
@@ -76789,13 +76800,13 @@ var init_content_subcommands = __esm(() => {
76789
76800
  init_setup_wizard();
76790
76801
  init_state_manager();
76791
76802
  init_content_review_commands();
76792
- LOCK_DIR = join166(homedir57(), ".claudekit", "locks");
76803
+ LOCK_DIR = join167(homedir57(), ".claudekit", "locks");
76793
76804
  });
76794
76805
 
76795
76806
  // src/commands/content/content-command.ts
76796
76807
  import { existsSync as existsSync85, mkdirSync as mkdirSync9, unlinkSync as unlinkSync7, writeFileSync as writeFileSync7 } from "node:fs";
76797
76808
  import { homedir as homedir58 } from "node:os";
76798
- import { join as join167 } from "node:path";
76809
+ import { join as join168 } from "node:path";
76799
76810
  async function contentCommand(options2) {
76800
76811
  const cwd2 = process.cwd();
76801
76812
  const contentLogger = new ContentLogger;
@@ -76973,8 +76984,8 @@ var init_content_command = __esm(() => {
76973
76984
  init_publisher();
76974
76985
  init_review_manager();
76975
76986
  init_state_manager();
76976
- LOCK_DIR2 = join167(homedir58(), ".claudekit", "locks");
76977
- LOCK_FILE = join167(LOCK_DIR2, "ck-content.lock");
76987
+ LOCK_DIR2 = join168(homedir58(), ".claudekit", "locks");
76988
+ LOCK_FILE = join168(LOCK_DIR2, "ck-content.lock");
76978
76989
  });
76979
76990
 
76980
76991
  // src/commands/content/index.ts
@@ -85272,7 +85283,7 @@ function buildDuplicateInventoryCheck(projectSkills, globalSkills) {
85272
85283
  if (duplicates.length === 0) {
85273
85284
  return pass("ck-skill-inventory", "Skill Inventory", "No duplicate project/global skills");
85274
85285
  }
85275
- return warn("ck-skill-inventory", "Skill Inventory", `${duplicates.length} duplicate project/global skill(s)`, duplicates, "Keep one installation scope per skill; inspect with: ck skills list --installed");
85286
+ return warn("ck-skill-inventory", "Skill Inventory", `${duplicates.length} duplicate project/global skill(s)`, duplicates, "Keep one installation scope per skill; inspect with: ck skills --list --installed");
85276
85287
  }
85277
85288
  function buildSkillOverridesCheck(settingsPath, read) {
85278
85289
  if (!read.settings || !Object.prototype.hasOwnProperty.call(read.settings, "skillOverrides")) {
@@ -111449,14 +111460,48 @@ init_skill_catalog_generator();
111449
111460
  init_skill_search_index();
111450
111461
  init_logger();
111451
111462
  init_agents();
111452
- init_skills_discovery();
111453
- init_skills_installer();
111454
- init_skills_registry();
111455
- init_skills_uninstaller();
111456
111463
  var import_gray_matter12 = __toESM(require_gray_matter(), 1);
111457
111464
  var import_picocolors37 = __toESM(require_picocolors(), 1);
111458
111465
  import { readFile as readFile65 } from "node:fs/promises";
111466
+ import { join as join149 } from "node:path";
111467
+
111468
+ // src/commands/skills/installed-skills-inventory.ts
111459
111469
  import { join as join148 } from "node:path";
111470
+ init_path_resolver();
111471
+ var SCOPE_SORT_ORDER = {
111472
+ project: 0,
111473
+ global: 1
111474
+ };
111475
+ async function getActiveClaudeSkillInstallations(options2 = {}) {
111476
+ const projectDir = options2.projectDir ?? process.cwd();
111477
+ const globalDir = options2.globalDir ?? PathResolver.getGlobalKitDir();
111478
+ const [projectSkills, globalSkills] = await Promise.all([
111479
+ scanSkills2(join148(projectDir, ".claude", "skills")),
111480
+ scanSkills2(join148(globalDir, "skills"))
111481
+ ]);
111482
+ const projectIds = new Set(projectSkills.map((skill) => skill.id));
111483
+ const globalIds = new Set(globalSkills.map((skill) => skill.id));
111484
+ return [
111485
+ ...projectSkills.map((skill) => ({
111486
+ skill: skill.id,
111487
+ scope: "project",
111488
+ path: skill.file,
111489
+ duplicateAcrossScopes: globalIds.has(skill.id)
111490
+ })),
111491
+ ...globalSkills.map((skill) => ({
111492
+ skill: skill.id,
111493
+ scope: "global",
111494
+ path: skill.file,
111495
+ duplicateAcrossScopes: projectIds.has(skill.id)
111496
+ }))
111497
+ ].sort((a3, b3) => a3.skill.localeCompare(b3.skill) || SCOPE_SORT_ORDER[a3.scope] - SCOPE_SORT_ORDER[b3.scope]);
111498
+ }
111499
+
111500
+ // src/commands/skills/skills-command.ts
111501
+ init_skills_discovery();
111502
+ init_skills_installer();
111503
+ init_skills_registry();
111504
+ init_skills_uninstaller();
111460
111505
 
111461
111506
  // src/commands/skills/types.ts
111462
111507
  init_zod();
@@ -111592,7 +111637,7 @@ async function handleValidate2(sourcePath) {
111592
111637
  spinner.stop(`Checked ${skills.length} skill(s)`);
111593
111638
  let hasIssues = false;
111594
111639
  for (const skill of skills) {
111595
- const skillMdPath = join148(skill.path, "SKILL.md");
111640
+ const skillMdPath = join149(skill.path, "SKILL.md");
111596
111641
  try {
111597
111642
  const content = await readFile65(skillMdPath, "utf-8");
111598
111643
  const { data } = import_gray_matter12.default(content, {
@@ -111628,30 +111673,55 @@ async function detectInstalledAgents2() {
111628
111673
  }
111629
111674
  async function listSkills2(showInstalled) {
111630
111675
  if (showInstalled) {
111631
- const installations = await getInstalledSkills();
111632
- if (installations.length === 0) {
111633
- f2.warn("No skills installed via ck skills.");
111676
+ const [installations, activeClaudeSkills] = await Promise.all([
111677
+ getInstalledSkills(),
111678
+ getActiveClaudeSkillInstallations()
111679
+ ]);
111680
+ if (installations.length === 0 && activeClaudeSkills.length === 0) {
111681
+ f2.warn("No installed skills found.");
111634
111682
  return;
111635
111683
  }
111636
111684
  console.log();
111637
- f2.step(import_picocolors37.default.bold("Installed Skills"));
111638
- console.log();
111639
- const bySkill = new Map;
111640
- for (const inst of installations) {
111641
- const list3 = bySkill.get(inst.skill) || [];
111642
- list3.push(inst);
111643
- bySkill.set(inst.skill, list3);
111685
+ if (activeClaudeSkills.length > 0) {
111686
+ f2.step(import_picocolors37.default.bold("Active Claude Code Skills"));
111687
+ console.log();
111688
+ const bySkill = new Map;
111689
+ for (const inst of activeClaudeSkills) {
111690
+ const list3 = bySkill.get(inst.skill) || [];
111691
+ list3.push(inst);
111692
+ bySkill.set(inst.skill, list3);
111693
+ }
111694
+ for (const [skill, installs] of bySkill) {
111695
+ const duplicate = installs.some((inst) => inst.duplicateAcrossScopes) ? ` ${import_picocolors37.default.yellow("(project/global duplicate)")}` : "";
111696
+ console.log(` ${import_picocolors37.default.cyan(skill)}${duplicate}`);
111697
+ for (const inst of installs) {
111698
+ console.log(` ${import_picocolors37.default.dim("→")} ${inst.scope}: ${import_picocolors37.default.dim(inst.path)}`);
111699
+ }
111700
+ }
111701
+ console.log();
111702
+ console.log(import_picocolors37.default.dim(` ${activeClaudeSkills.length} active Claude Code skill installation(s)`));
111703
+ console.log();
111644
111704
  }
111645
- for (const [skill, installs] of bySkill) {
111646
- console.log(` ${import_picocolors37.default.cyan(skill)}`);
111647
- for (const inst of installs) {
111648
- const scope = inst.global ? "global" : "project";
111649
- console.log(` ${import_picocolors37.default.dim("→")} ${inst.agent} (${scope}): ${import_picocolors37.default.dim(inst.path)}`);
111705
+ if (installations.length > 0) {
111706
+ f2.step(import_picocolors37.default.bold("Registry-managed Agent Skills"));
111707
+ console.log();
111708
+ const bySkill = new Map;
111709
+ for (const inst of installations) {
111710
+ const list3 = bySkill.get(inst.skill) || [];
111711
+ list3.push(inst);
111712
+ bySkill.set(inst.skill, list3);
111650
111713
  }
111714
+ for (const [skill, installs] of bySkill) {
111715
+ console.log(` ${import_picocolors37.default.cyan(skill)}`);
111716
+ for (const inst of installs) {
111717
+ const scope = inst.global ? "global" : "project";
111718
+ console.log(` ${import_picocolors37.default.dim("→")} ${inst.agent} (${scope}): ${import_picocolors37.default.dim(inst.path)}`);
111719
+ }
111720
+ }
111721
+ console.log();
111722
+ console.log(import_picocolors37.default.dim(` ${installations.length} registry-managed installation(s) across ${bySkill.size} skill(s)`));
111651
111723
  }
111652
111724
  console.log();
111653
- console.log(import_picocolors37.default.dim(` ${installations.length} installation(s) across ${bySkill.size} skill(s)`));
111654
- console.log();
111655
111725
  return;
111656
111726
  }
111657
111727
  const sourcePath = getSkillSourcePath();
@@ -112150,7 +112220,7 @@ async function detectInstallations() {
112150
112220
 
112151
112221
  // src/commands/uninstall/removal-handler.ts
112152
112222
  import { readdirSync as readdirSync10, rmSync as rmSync5 } from "node:fs";
112153
- import { basename as basename30, join as join150, resolve as resolve53, sep as sep13 } from "node:path";
112223
+ import { basename as basename30, join as join151, resolve as resolve53, sep as sep13 } from "node:path";
112154
112224
  init_logger();
112155
112225
  init_safe_prompts();
112156
112226
  init_safe_spinner();
@@ -112159,7 +112229,7 @@ var import_fs_extra41 = __toESM(require_lib(), 1);
112159
112229
  // src/commands/uninstall/analysis-handler.ts
112160
112230
  init_metadata_migration();
112161
112231
  import { readdirSync as readdirSync9, rmSync as rmSync4 } from "node:fs";
112162
- import { dirname as dirname45, join as join149 } from "node:path";
112232
+ import { dirname as dirname45, join as join150 } from "node:path";
112163
112233
  init_logger();
112164
112234
  init_safe_prompts();
112165
112235
  var import_fs_extra40 = __toESM(require_lib(), 1);
@@ -112219,7 +112289,7 @@ async function analyzeInstallation(installation, forceOverwrite, kit) {
112219
112289
  const remainingFiles = metadata.kits?.[remainingKit]?.files || [];
112220
112290
  for (const file of remainingFiles) {
112221
112291
  const relativePath = normalizeTrackedPath(file.path);
112222
- if (await import_fs_extra40.pathExists(join149(installation.path, relativePath))) {
112292
+ if (await import_fs_extra40.pathExists(join150(installation.path, relativePath))) {
112223
112293
  result.retainedManifestPaths.push(relativePath);
112224
112294
  }
112225
112295
  }
@@ -112227,7 +112297,7 @@ async function analyzeInstallation(installation, forceOverwrite, kit) {
112227
112297
  const kitFiles = metadata.kits[kit].files || [];
112228
112298
  for (const trackedFile of kitFiles) {
112229
112299
  const relativePath = normalizeTrackedPath(trackedFile.path);
112230
- const filePath = join149(installation.path, relativePath);
112300
+ const filePath = join150(installation.path, relativePath);
112231
112301
  if (preservedPaths.has(relativePath)) {
112232
112302
  result.toPreserve.push({ path: relativePath, reason: "shared with other kit" });
112233
112303
  continue;
@@ -112260,7 +112330,7 @@ async function analyzeInstallation(installation, forceOverwrite, kit) {
112260
112330
  }
112261
112331
  for (const trackedFile of allTrackedFiles) {
112262
112332
  const relativePath = normalizeTrackedPath(trackedFile.path);
112263
- const filePath = join149(installation.path, relativePath);
112333
+ const filePath = join150(installation.path, relativePath);
112264
112334
  const ownershipResult = await OwnershipChecker.checkOwnership(filePath, metadata, installation.path);
112265
112335
  if (!ownershipResult.exists)
112266
112336
  continue;
@@ -112403,7 +112473,7 @@ async function removeInstallations(installations, options2) {
112403
112473
  let removedCount = 0;
112404
112474
  let cleanedDirs = 0;
112405
112475
  for (const item of analysis.toDelete) {
112406
- const filePath = join150(installation.path, item.path);
112476
+ const filePath = join151(installation.path, item.path);
112407
112477
  if (!await import_fs_extra41.pathExists(filePath))
112408
112478
  continue;
112409
112479
  if (!await isPathSafeToRemove(filePath, installation.path)) {
@@ -112812,7 +112882,7 @@ ${import_picocolors40.default.bold(import_picocolors40.default.cyan(result.kitCo
112812
112882
  init_logger();
112813
112883
  import { existsSync as existsSync75 } from "node:fs";
112814
112884
  import { rm as rm17 } from "node:fs/promises";
112815
- import { join as join157 } from "node:path";
112885
+ import { join as join158 } from "node:path";
112816
112886
  var import_picocolors41 = __toESM(require_picocolors(), 1);
112817
112887
 
112818
112888
  // src/commands/watch/phases/implementation-runner.ts
@@ -113331,7 +113401,7 @@ function spawnAndCollect3(command, args) {
113331
113401
 
113332
113402
  // src/commands/watch/phases/issue-processor.ts
113333
113403
  import { mkdir as mkdir39, writeFile as writeFile38 } from "node:fs/promises";
113334
- import { join as join153 } from "node:path";
113404
+ import { join as join154 } from "node:path";
113335
113405
 
113336
113406
  // src/commands/watch/phases/approval-detector.ts
113337
113407
  init_logger();
@@ -113709,9 +113779,9 @@ async function checkAwaitingApproval(state, setup, options2, watchLog, projectDi
113709
113779
 
113710
113780
  // src/commands/watch/phases/plan-dir-finder.ts
113711
113781
  import { readdir as readdir44, stat as stat23 } from "node:fs/promises";
113712
- import { join as join152 } from "node:path";
113782
+ import { join as join153 } from "node:path";
113713
113783
  async function findRecentPlanDir(cwd2, issueNumber, watchLog) {
113714
- const plansRoot = join152(cwd2, "plans");
113784
+ const plansRoot = join153(cwd2, "plans");
113715
113785
  try {
113716
113786
  const entries = await readdir44(plansRoot);
113717
113787
  const tenMinAgo = Date.now() - 10 * 60 * 1000;
@@ -113720,14 +113790,14 @@ async function findRecentPlanDir(cwd2, issueNumber, watchLog) {
113720
113790
  for (const entry of entries) {
113721
113791
  if (entry === "watch" || entry === "reports" || entry === "visuals")
113722
113792
  continue;
113723
- const dirPath = join152(plansRoot, entry);
113793
+ const dirPath = join153(plansRoot, entry);
113724
113794
  const dirStat = await stat23(dirPath);
113725
113795
  if (!dirStat.isDirectory())
113726
113796
  continue;
113727
113797
  if (dirStat.mtimeMs < tenMinAgo)
113728
113798
  continue;
113729
113799
  try {
113730
- await stat23(join152(dirPath, "plan.md"));
113800
+ await stat23(join153(dirPath, "plan.md"));
113731
113801
  } catch {
113732
113802
  continue;
113733
113803
  }
@@ -113958,13 +114028,13 @@ async function handlePlanGeneration(issue, state, config, setup, options2, watch
113958
114028
  stats.plansCreated++;
113959
114029
  const detectedPlanDir = await findRecentPlanDir(projectDir, issue.number, watchLog);
113960
114030
  if (detectedPlanDir) {
113961
- state.activeIssues[numStr].planPath = join153(detectedPlanDir, "plan.md");
114031
+ state.activeIssues[numStr].planPath = join154(detectedPlanDir, "plan.md");
113962
114032
  watchLog.info(`Plan directory detected: ${detectedPlanDir}`);
113963
114033
  } else {
113964
114034
  try {
113965
- const planDir = join153(projectDir, "plans", "watch");
114035
+ const planDir = join154(projectDir, "plans", "watch");
113966
114036
  await mkdir39(planDir, { recursive: true });
113967
- const planFilePath = join153(planDir, `issue-${issue.number}-plan.md`);
114037
+ const planFilePath = join154(planDir, `issue-${issue.number}-plan.md`);
113968
114038
  await writeFile38(planFilePath, planResult.planText, "utf-8");
113969
114039
  state.activeIssues[numStr].planPath = planFilePath;
113970
114040
  watchLog.info(`Plan saved (fallback) to ${planFilePath}`);
@@ -114271,18 +114341,18 @@ init_logger();
114271
114341
  import { spawnSync as spawnSync7 } from "node:child_process";
114272
114342
  import { existsSync as existsSync72 } from "node:fs";
114273
114343
  import { readdir as readdir45, stat as stat24 } from "node:fs/promises";
114274
- import { join as join154 } from "node:path";
114344
+ import { join as join155 } from "node:path";
114275
114345
  async function scanForRepos(parentDir) {
114276
114346
  const repos = [];
114277
114347
  const entries = await readdir45(parentDir);
114278
114348
  for (const entry of entries) {
114279
114349
  if (entry.startsWith("."))
114280
114350
  continue;
114281
- const fullPath = join154(parentDir, entry);
114351
+ const fullPath = join155(parentDir, entry);
114282
114352
  const entryStat = await stat24(fullPath);
114283
114353
  if (!entryStat.isDirectory())
114284
114354
  continue;
114285
- const gitDir = join154(fullPath, ".git");
114355
+ const gitDir = join155(fullPath, ".git");
114286
114356
  if (!existsSync72(gitDir))
114287
114357
  continue;
114288
114358
  const result = spawnSync7("gh", ["repo", "view", "--json", "owner,name"], {
@@ -114309,7 +114379,7 @@ init_logger();
114309
114379
  import { spawnSync as spawnSync8 } from "node:child_process";
114310
114380
  import { existsSync as existsSync73 } from "node:fs";
114311
114381
  import { homedir as homedir52 } from "node:os";
114312
- import { join as join155 } from "node:path";
114382
+ import { join as join156 } from "node:path";
114313
114383
  async function validateSetup(cwd2) {
114314
114384
  const workDir = cwd2 ?? process.cwd();
114315
114385
  const ghVersion = spawnSync8("gh", ["--version"], { encoding: "utf-8", timeout: 1e4 });
@@ -114340,7 +114410,7 @@ Run this command from a directory with a GitHub remote.`);
114340
114410
  } catch {
114341
114411
  throw new Error(`Failed to parse repository info: ${ghRepo.stdout}`);
114342
114412
  }
114343
- const skillsPath = join155(homedir52(), ".claude", "skills");
114413
+ const skillsPath = join156(homedir52(), ".claude", "skills");
114344
114414
  const skillsAvailable = existsSync73(skillsPath);
114345
114415
  if (!skillsAvailable) {
114346
114416
  logger.warning(`ClaudeKit Engineer skills not found at ${skillsPath}`);
@@ -114359,7 +114429,7 @@ init_path_resolver();
114359
114429
  import { createWriteStream as createWriteStream3, statSync as statSync13 } from "node:fs";
114360
114430
  import { existsSync as existsSync74 } from "node:fs";
114361
114431
  import { mkdir as mkdir41, rename as rename13 } from "node:fs/promises";
114362
- import { join as join156 } from "node:path";
114432
+ import { join as join157 } from "node:path";
114363
114433
 
114364
114434
  class WatchLogger {
114365
114435
  logStream = null;
@@ -114367,7 +114437,7 @@ class WatchLogger {
114367
114437
  logPath = null;
114368
114438
  maxBytes;
114369
114439
  constructor(logDir, maxBytes = 0) {
114370
- this.logDir = logDir ?? join156(PathResolver.getClaudeKitDir(), "logs");
114440
+ this.logDir = logDir ?? join157(PathResolver.getClaudeKitDir(), "logs");
114371
114441
  this.maxBytes = maxBytes;
114372
114442
  }
114373
114443
  async init() {
@@ -114376,7 +114446,7 @@ class WatchLogger {
114376
114446
  await mkdir41(this.logDir, { recursive: true });
114377
114447
  }
114378
114448
  const dateStr = formatDate(new Date);
114379
- this.logPath = join156(this.logDir, `watch-${dateStr}.log`);
114449
+ this.logPath = join157(this.logDir, `watch-${dateStr}.log`);
114380
114450
  this.logStream = createWriteStream3(this.logPath, { flags: "a", mode: 384 });
114381
114451
  } catch (error) {
114382
114452
  logger.warning(`Cannot create watch log file: ${error instanceof Error ? error.message : "Unknown"}`);
@@ -114558,7 +114628,7 @@ async function watchCommand(options2) {
114558
114628
  }
114559
114629
  async function discoverRepos(options2, watchLog) {
114560
114630
  const cwd2 = process.cwd();
114561
- const isGitRepo = existsSync75(join157(cwd2, ".git"));
114631
+ const isGitRepo = existsSync75(join158(cwd2, ".git"));
114562
114632
  if (options2.force) {
114563
114633
  await forceRemoveLock(watchLog);
114564
114634
  }
@@ -114816,7 +114886,7 @@ function registerCommands(cli) {
114816
114886
  init_package();
114817
114887
  init_config_version_checker();
114818
114888
  import { existsSync as existsSync87, readFileSync as readFileSync22 } from "node:fs";
114819
- import { join as join169 } from "node:path";
114889
+ import { join as join170 } from "node:path";
114820
114890
 
114821
114891
  // src/domains/versioning/version-checker.ts
114822
114892
  init_version_utils();
@@ -114831,14 +114901,14 @@ init_logger();
114831
114901
  init_path_resolver();
114832
114902
  import { existsSync as existsSync86 } from "node:fs";
114833
114903
  import { mkdir as mkdir42, readFile as readFile69, writeFile as writeFile41 } from "node:fs/promises";
114834
- import { join as join168 } from "node:path";
114904
+ import { join as join169 } from "node:path";
114835
114905
 
114836
114906
  class VersionCacheManager {
114837
114907
  static CACHE_FILENAME = "version-check.json";
114838
114908
  static CACHE_TTL_MS = 7 * 24 * 60 * 60 * 1000;
114839
114909
  static getCacheFile() {
114840
114910
  const cacheDir = PathResolver.getCacheDir(false);
114841
- return join168(cacheDir, VersionCacheManager.CACHE_FILENAME);
114911
+ return join169(cacheDir, VersionCacheManager.CACHE_FILENAME);
114842
114912
  }
114843
114913
  static async load() {
114844
114914
  const cacheFile = VersionCacheManager.getCacheFile();
@@ -115149,9 +115219,9 @@ async function displayVersion() {
115149
115219
  let localInstalledKits = [];
115150
115220
  let globalInstalledKits = [];
115151
115221
  const globalKitDir = PathResolver.getGlobalKitDir();
115152
- const globalMetadataPath = join169(globalKitDir, "metadata.json");
115222
+ const globalMetadataPath = join170(globalKitDir, "metadata.json");
115153
115223
  const prefix = PathResolver.getPathPrefix(false);
115154
- const localMetadataPath = prefix ? join169(process.cwd(), prefix, "metadata.json") : join169(process.cwd(), "metadata.json");
115224
+ const localMetadataPath = prefix ? join170(process.cwd(), prefix, "metadata.json") : join170(process.cwd(), "metadata.json");
115155
115225
  const isLocalSameAsGlobal = localMetadataPath === globalMetadataPath;
115156
115226
  if (!isLocalSameAsGlobal && existsSync87(localMetadataPath)) {
115157
115227
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claudekit-cli",
3
- "version": "4.1.1",
3
+ "version": "4.2.1",
4
4
  "description": "CLI tool for bootstrapping and updating ClaudeKit projects",
5
5
  "type": "module",
6
6
  "repository": {
@@ -44,6 +44,7 @@
44
44
  "dev:all": "./scripts/dev-quick-start.sh all",
45
45
  "metrics": "bun run scripts/workflow-metrics.ts",
46
46
  "validate": "bun run typecheck && bun run lint && bun test && bun run ui:test && bun run build",
47
+ "ci:local": "bash scripts/ci-local.sh",
47
48
  "install:hooks": "./.githooks/install.sh",
48
49
  "prepare": "node -e \"try{require('child_process').execSync('git rev-parse --git-dir',{stdio:'ignore'});require('child_process').execSync('bash .githooks/install.sh',{stdio:'inherit'})}catch(e){console.warn('[i] Hook install skipped:',e.message)}\"",
49
50
  "help:check-parity": "bun run scripts/check-help-parity.ts",