claudekit-cli 4.2.0 → 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.2.0",
3
- "generatedAt": "2026-05-12T15:41:30.136Z",
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.2.0",
62893
+ version: "4.2.1",
62894
62894
  description: "CLI tool for bootstrapping and updating ClaudeKit projects",
62895
62895
  type: "module",
62896
62896
  repository: {
@@ -74184,9 +74184,9 @@ __export(exports_worktree_manager, {
74184
74184
  });
74185
74185
  import { existsSync as existsSync70 } from "node:fs";
74186
74186
  import { readFile as readFile66, writeFile as writeFile37 } from "node:fs/promises";
74187
- import { join as join151 } from "node:path";
74187
+ import { join as join152 } from "node:path";
74188
74188
  async function createWorktree(projectDir, issueNumber, baseBranch) {
74189
- const worktreePath = join151(projectDir, WORKTREE_DIR, `issue-${issueNumber}`);
74189
+ const worktreePath = join152(projectDir, WORKTREE_DIR, `issue-${issueNumber}`);
74190
74190
  const branchName = `ck-watch/issue-${issueNumber}`;
74191
74191
  await spawnAndCollect("git", ["fetch", "origin", baseBranch], projectDir).catch(() => {
74192
74192
  logger.warning(`[worktree] Could not fetch origin/${baseBranch}, using local`);
@@ -74204,7 +74204,7 @@ async function createWorktree(projectDir, issueNumber, baseBranch) {
74204
74204
  return worktreePath;
74205
74205
  }
74206
74206
  async function removeWorktree(projectDir, issueNumber) {
74207
- const worktreePath = join151(projectDir, WORKTREE_DIR, `issue-${issueNumber}`);
74207
+ const worktreePath = join152(projectDir, WORKTREE_DIR, `issue-${issueNumber}`);
74208
74208
  const branchName = `ck-watch/issue-${issueNumber}`;
74209
74209
  try {
74210
74210
  await spawnAndCollect("git", ["worktree", "remove", worktreePath, "--force"], projectDir);
@@ -74218,7 +74218,7 @@ async function listActiveWorktrees(projectDir) {
74218
74218
  try {
74219
74219
  const output2 = await spawnAndCollect("git", ["worktree", "list", "--porcelain"], projectDir);
74220
74220
  const issueNumbers = [];
74221
- const worktreePrefix = join151(projectDir, WORKTREE_DIR, "issue-").replace(/\\/g, "/");
74221
+ const worktreePrefix = join152(projectDir, WORKTREE_DIR, "issue-").replace(/\\/g, "/");
74222
74222
  for (const line of output2.split(`
74223
74223
  `)) {
74224
74224
  if (line.startsWith("worktree ")) {
@@ -74246,7 +74246,7 @@ async function cleanupAllWorktrees(projectDir) {
74246
74246
  await spawnAndCollect("git", ["worktree", "prune"], projectDir).catch(() => {});
74247
74247
  }
74248
74248
  async function ensureGitignore(projectDir) {
74249
- const gitignorePath = join151(projectDir, ".gitignore");
74249
+ const gitignorePath = join152(projectDir, ".gitignore");
74250
74250
  try {
74251
74251
  const content = existsSync70(gitignorePath) ? await readFile66(gitignorePath, "utf-8") : "";
74252
74252
  if (!content.includes(".worktrees")) {
@@ -74353,7 +74353,7 @@ import { createHash as createHash9 } from "node:crypto";
74353
74353
  import { existsSync as existsSync76, mkdirSync as mkdirSync5, readFileSync as readFileSync18, readdirSync as readdirSync11, statSync as statSync14 } from "node:fs";
74354
74354
  import { rename as rename14, writeFile as writeFile39 } from "node:fs/promises";
74355
74355
  import { homedir as homedir53 } from "node:os";
74356
- import { basename as basename31, join as join158 } from "node:path";
74356
+ import { basename as basename31, join as join159 } from "node:path";
74357
74357
  function getCachedContext(repoPath) {
74358
74358
  const cachePath = getCacheFilePath(repoPath);
74359
74359
  if (!existsSync76(cachePath))
@@ -74396,25 +74396,25 @@ function computeSourceHash(repoPath) {
74396
74396
  }
74397
74397
  function getDocSourcePaths(repoPath) {
74398
74398
  const paths = [];
74399
- const docsDir = join158(repoPath, "docs");
74399
+ const docsDir = join159(repoPath, "docs");
74400
74400
  if (existsSync76(docsDir)) {
74401
74401
  try {
74402
74402
  const files = readdirSync11(docsDir);
74403
74403
  for (const f3 of files) {
74404
74404
  if (f3.endsWith(".md"))
74405
- paths.push(join158(docsDir, f3));
74405
+ paths.push(join159(docsDir, f3));
74406
74406
  }
74407
74407
  } catch {}
74408
74408
  }
74409
- const readme = join158(repoPath, "README.md");
74409
+ const readme = join159(repoPath, "README.md");
74410
74410
  if (existsSync76(readme))
74411
74411
  paths.push(readme);
74412
- const stylesDir = join158(repoPath, "assets", "writing-styles");
74412
+ const stylesDir = join159(repoPath, "assets", "writing-styles");
74413
74413
  if (existsSync76(stylesDir)) {
74414
74414
  try {
74415
74415
  const files = readdirSync11(stylesDir);
74416
74416
  for (const f3 of files) {
74417
- paths.push(join158(stylesDir, f3));
74417
+ paths.push(join159(stylesDir, f3));
74418
74418
  }
74419
74419
  } catch {}
74420
74420
  }
@@ -74423,11 +74423,11 @@ function getDocSourcePaths(repoPath) {
74423
74423
  function getCacheFilePath(repoPath) {
74424
74424
  const repoName = basename31(repoPath).replace(/[^a-zA-Z0-9_-]/g, "_");
74425
74425
  const pathHash = createHash9("sha256").update(repoPath).digest("hex").slice(0, 8);
74426
- return join158(CACHE_DIR, `${repoName}-${pathHash}-context-cache.json`);
74426
+ return join159(CACHE_DIR, `${repoName}-${pathHash}-context-cache.json`);
74427
74427
  }
74428
74428
  var CACHE_DIR, CACHE_TTL_MS5;
74429
74429
  var init_context_cache_manager = __esm(() => {
74430
- CACHE_DIR = join158(homedir53(), ".claudekit", "cache");
74430
+ CACHE_DIR = join159(homedir53(), ".claudekit", "cache");
74431
74431
  CACHE_TTL_MS5 = 24 * 60 * 60 * 1000;
74432
74432
  });
74433
74433
 
@@ -74608,7 +74608,7 @@ function extractContentFromResponse(response) {
74608
74608
  // src/commands/content/phases/docs-summarizer.ts
74609
74609
  import { execSync as execSync7 } from "node:child_process";
74610
74610
  import { existsSync as existsSync77, readFileSync as readFileSync19, readdirSync as readdirSync12 } from "node:fs";
74611
- import { join as join159 } from "node:path";
74611
+ import { join as join160 } from "node:path";
74612
74612
  async function summarizeProjectDocs(repoPath, contentLogger) {
74613
74613
  const rawContent = collectRawDocs(repoPath);
74614
74614
  if (rawContent.total.length < 200) {
@@ -74662,12 +74662,12 @@ function collectRawDocs(repoPath) {
74662
74662
  return capped;
74663
74663
  };
74664
74664
  const docsContent = [];
74665
- const docsDir = join159(repoPath, "docs");
74665
+ const docsDir = join160(repoPath, "docs");
74666
74666
  if (existsSync77(docsDir)) {
74667
74667
  try {
74668
74668
  const files = readdirSync12(docsDir).filter((f3) => f3.endsWith(".md")).sort();
74669
74669
  for (const f3 of files) {
74670
- const content = readCapped(join159(docsDir, f3), 5000);
74670
+ const content = readCapped(join160(docsDir, f3), 5000);
74671
74671
  if (content) {
74672
74672
  docsContent.push(`### ${f3}
74673
74673
  ${content}`);
@@ -74681,21 +74681,21 @@ ${content}`);
74681
74681
  let brand = "";
74682
74682
  const brandCandidates = ["docs/brand-guidelines.md", "docs/design-guidelines.md"];
74683
74683
  for (const p of brandCandidates) {
74684
- brand = readCapped(join159(repoPath, p), 3000);
74684
+ brand = readCapped(join160(repoPath, p), 3000);
74685
74685
  if (brand)
74686
74686
  break;
74687
74687
  }
74688
74688
  let styles3 = "";
74689
- const stylesDir = join159(repoPath, "assets", "writing-styles");
74689
+ const stylesDir = join160(repoPath, "assets", "writing-styles");
74690
74690
  if (existsSync77(stylesDir)) {
74691
74691
  try {
74692
74692
  const files = readdirSync12(stylesDir).slice(0, 3);
74693
- 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(`
74694
74694
 
74695
74695
  `);
74696
74696
  } catch {}
74697
74697
  }
74698
- const readme = readCapped(join159(repoPath, "README.md"), 3000);
74698
+ const readme = readCapped(join160(repoPath, "README.md"), 3000);
74699
74699
  const total = [docs, brand, styles3, readme].join(`
74700
74700
  `);
74701
74701
  return { docs, brand, styles: styles3, readme, total };
@@ -74882,9 +74882,9 @@ IMPORTANT: Generate the image and output the path as JSON: {"imagePath": "/path/
74882
74882
  import { execSync as execSync8 } from "node:child_process";
74883
74883
  import { existsSync as existsSync78, mkdirSync as mkdirSync6, readdirSync as readdirSync13 } from "node:fs";
74884
74884
  import { homedir as homedir54 } from "node:os";
74885
- import { join as join160 } from "node:path";
74885
+ import { join as join161 } from "node:path";
74886
74886
  async function generatePhoto(_content, context, config, platform18, contentId, contentLogger) {
74887
- const mediaDir = join160(config.contentDir.replace(/^~/, homedir54()), "media", String(contentId));
74887
+ const mediaDir = join161(config.contentDir.replace(/^~/, homedir54()), "media", String(contentId));
74888
74888
  if (!existsSync78(mediaDir)) {
74889
74889
  mkdirSync6(mediaDir, { recursive: true });
74890
74890
  }
@@ -74909,7 +74909,7 @@ async function generatePhoto(_content, context, config, platform18, contentId, c
74909
74909
  const imageFile = files.find((f3) => /\.(png|jpg|jpeg|webp)$/i.test(f3));
74910
74910
  if (imageFile) {
74911
74911
  const ext2 = imageFile.split(".").pop() ?? "png";
74912
- return { path: join160(mediaDir, imageFile), ...dimensions, format: ext2 };
74912
+ return { path: join161(mediaDir, imageFile), ...dimensions, format: ext2 };
74913
74913
  }
74914
74914
  contentLogger.warn(`Photo generation produced no image for content ${contentId}`);
74915
74915
  return null;
@@ -74999,7 +74999,7 @@ var init_content_creator = __esm(() => {
74999
74999
  // src/commands/content/phases/content-logger.ts
75000
75000
  import { createWriteStream as createWriteStream4, existsSync as existsSync79, mkdirSync as mkdirSync7, statSync as statSync15 } from "node:fs";
75001
75001
  import { homedir as homedir55 } from "node:os";
75002
- import { join as join161 } from "node:path";
75002
+ import { join as join162 } from "node:path";
75003
75003
 
75004
75004
  class ContentLogger {
75005
75005
  stream = null;
@@ -75007,7 +75007,7 @@ class ContentLogger {
75007
75007
  logDir;
75008
75008
  maxBytes;
75009
75009
  constructor(maxBytes = 0) {
75010
- this.logDir = join161(homedir55(), ".claudekit", "logs");
75010
+ this.logDir = join162(homedir55(), ".claudekit", "logs");
75011
75011
  this.maxBytes = maxBytes;
75012
75012
  }
75013
75013
  init() {
@@ -75039,7 +75039,7 @@ class ContentLogger {
75039
75039
  }
75040
75040
  }
75041
75041
  getLogPath() {
75042
- return join161(this.logDir, `content-${this.getDateStr()}.log`);
75042
+ return join162(this.logDir, `content-${this.getDateStr()}.log`);
75043
75043
  }
75044
75044
  write(level, message) {
75045
75045
  this.rotateIfNeeded();
@@ -75056,18 +75056,18 @@ class ContentLogger {
75056
75056
  if (dateStr !== this.currentDate) {
75057
75057
  this.close();
75058
75058
  this.currentDate = dateStr;
75059
- const logPath = join161(this.logDir, `content-${dateStr}.log`);
75059
+ const logPath = join162(this.logDir, `content-${dateStr}.log`);
75060
75060
  this.stream = createWriteStream4(logPath, { flags: "a", mode: 384 });
75061
75061
  return;
75062
75062
  }
75063
75063
  if (this.maxBytes > 0 && this.stream) {
75064
- const logPath = join161(this.logDir, `content-${this.currentDate}.log`);
75064
+ const logPath = join162(this.logDir, `content-${this.currentDate}.log`);
75065
75065
  try {
75066
75066
  const stat25 = statSync15(logPath);
75067
75067
  if (stat25.size >= this.maxBytes) {
75068
75068
  this.close();
75069
75069
  const suffix = Date.now();
75070
- const rotatedPath = join161(this.logDir, `content-${this.currentDate}-${suffix}.log`);
75070
+ const rotatedPath = join162(this.logDir, `content-${this.currentDate}-${suffix}.log`);
75071
75071
  import("node:fs/promises").then(({ rename: rename15 }) => rename15(logPath, rotatedPath).catch(() => {}));
75072
75072
  this.stream = createWriteStream4(logPath, { flags: "w", mode: 384 });
75073
75073
  }
@@ -75293,7 +75293,7 @@ function isNoiseCommit(title, author) {
75293
75293
  // src/commands/content/phases/change-detector.ts
75294
75294
  import { execSync as execSync10, spawnSync as spawnSync9 } from "node:child_process";
75295
75295
  import { existsSync as existsSync81, readFileSync as readFileSync20, readdirSync as readdirSync14, statSync as statSync16 } from "node:fs";
75296
- import { join as join162 } from "node:path";
75296
+ import { join as join163 } from "node:path";
75297
75297
  function detectCommits(repo, since) {
75298
75298
  try {
75299
75299
  const fetchUrl = sshToHttps(repo.remoteUrl);
@@ -75402,7 +75402,7 @@ function detectTags(repo, since) {
75402
75402
  }
75403
75403
  }
75404
75404
  function detectCompletedPlans(repo, since) {
75405
- const plansDir = join162(repo.path, "plans");
75405
+ const plansDir = join163(repo.path, "plans");
75406
75406
  if (!existsSync81(plansDir))
75407
75407
  return [];
75408
75408
  const sinceMs = new Date(since).getTime();
@@ -75412,7 +75412,7 @@ function detectCompletedPlans(repo, since) {
75412
75412
  for (const entry of entries) {
75413
75413
  if (!entry.isDirectory())
75414
75414
  continue;
75415
- const planFile = join162(plansDir, entry.name, "plan.md");
75415
+ const planFile = join163(plansDir, entry.name, "plan.md");
75416
75416
  if (!existsSync81(planFile))
75417
75417
  continue;
75418
75418
  try {
@@ -75490,7 +75490,7 @@ function classifyCommit(event) {
75490
75490
  // src/commands/content/phases/repo-discoverer.ts
75491
75491
  import { execSync as execSync11 } from "node:child_process";
75492
75492
  import { readdirSync as readdirSync15 } from "node:fs";
75493
- import { join as join163 } from "node:path";
75493
+ import { join as join164 } from "node:path";
75494
75494
  function discoverRepos2(cwd2) {
75495
75495
  const repos = [];
75496
75496
  if (isGitRepoRoot(cwd2)) {
@@ -75503,7 +75503,7 @@ function discoverRepos2(cwd2) {
75503
75503
  for (const entry of entries) {
75504
75504
  if (!entry.isDirectory() || entry.name.startsWith("."))
75505
75505
  continue;
75506
- const dirPath = join163(cwd2, entry.name);
75506
+ const dirPath = join164(cwd2, entry.name);
75507
75507
  if (isGitRepoRoot(dirPath)) {
75508
75508
  const info = getRepoInfo(dirPath);
75509
75509
  if (info)
@@ -76171,9 +76171,9 @@ var init_types6 = __esm(() => {
76171
76171
 
76172
76172
  // src/commands/content/phases/state-manager.ts
76173
76173
  import { readFile as readFile68, rename as rename15, writeFile as writeFile40 } from "node:fs/promises";
76174
- import { join as join164 } from "node:path";
76174
+ import { join as join165 } from "node:path";
76175
76175
  async function loadContentConfig(projectDir) {
76176
- const configPath = join164(projectDir, CK_CONFIG_FILE2);
76176
+ const configPath = join165(projectDir, CK_CONFIG_FILE2);
76177
76177
  try {
76178
76178
  const raw2 = await readFile68(configPath, "utf-8");
76179
76179
  const json = JSON.parse(raw2);
@@ -76183,13 +76183,13 @@ async function loadContentConfig(projectDir) {
76183
76183
  }
76184
76184
  }
76185
76185
  async function saveContentConfig(projectDir, config) {
76186
- const configPath = join164(projectDir, CK_CONFIG_FILE2);
76186
+ const configPath = join165(projectDir, CK_CONFIG_FILE2);
76187
76187
  const json = await readJsonSafe(configPath);
76188
76188
  json.content = { ...json.content, ...config };
76189
76189
  await atomicWrite2(configPath, json);
76190
76190
  }
76191
76191
  async function loadContentState(projectDir) {
76192
- const configPath = join164(projectDir, CK_CONFIG_FILE2);
76192
+ const configPath = join165(projectDir, CK_CONFIG_FILE2);
76193
76193
  try {
76194
76194
  const raw2 = await readFile68(configPath, "utf-8");
76195
76195
  const json = JSON.parse(raw2);
@@ -76200,7 +76200,7 @@ async function loadContentState(projectDir) {
76200
76200
  }
76201
76201
  }
76202
76202
  async function saveContentState(projectDir, state) {
76203
- const configPath = join164(projectDir, CK_CONFIG_FILE2);
76203
+ const configPath = join165(projectDir, CK_CONFIG_FILE2);
76204
76204
  const sevenDaysAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString().slice(0, 10);
76205
76205
  for (const key of Object.keys(state.dailyPostCounts)) {
76206
76206
  const dateStr = key.slice(-10);
@@ -76482,7 +76482,7 @@ var init_platform_setup_x = __esm(() => {
76482
76482
 
76483
76483
  // src/commands/content/phases/setup-wizard.ts
76484
76484
  import { existsSync as existsSync82 } from "node:fs";
76485
- import { join as join165 } from "node:path";
76485
+ import { join as join166 } from "node:path";
76486
76486
  async function runSetupWizard2(cwd2, contentLogger) {
76487
76487
  console.log();
76488
76488
  oe(import_picocolors43.default.bgCyan(import_picocolors43.default.white(" CK Content — Multi-Channel Content Engine ")));
@@ -76550,8 +76550,8 @@ async function showRepoSummary(cwd2) {
76550
76550
  function detectBrandAssets(cwd2, contentLogger) {
76551
76551
  const repos = discoverRepos2(cwd2);
76552
76552
  for (const repo of repos) {
76553
- const hasGuidelines = existsSync82(join165(repo.path, "docs", "brand-guidelines.md"));
76554
- 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"));
76555
76555
  if (!hasGuidelines) {
76556
76556
  f2.warning(`${repo.name}: No docs/brand-guidelines.md — content will use generic tone.`);
76557
76557
  contentLogger.warn(`${repo.name}: missing docs/brand-guidelines.md`);
@@ -76693,9 +76693,9 @@ __export(exports_content_subcommands, {
76693
76693
  });
76694
76694
  import { existsSync as existsSync84, readFileSync as readFileSync21, unlinkSync as unlinkSync6 } from "node:fs";
76695
76695
  import { homedir as homedir57 } from "node:os";
76696
- import { join as join166 } from "node:path";
76696
+ import { join as join167 } from "node:path";
76697
76697
  function isDaemonRunning() {
76698
- const lockFile = join166(LOCK_DIR, `${LOCK_NAME2}.lock`);
76698
+ const lockFile = join167(LOCK_DIR, `${LOCK_NAME2}.lock`);
76699
76699
  if (!existsSync84(lockFile))
76700
76700
  return { running: false, pid: null };
76701
76701
  try {
@@ -76727,7 +76727,7 @@ async function startContent(options2) {
76727
76727
  await contentCommand(options2);
76728
76728
  }
76729
76729
  async function stopContent() {
76730
- const lockFile = join166(LOCK_DIR, `${LOCK_NAME2}.lock`);
76730
+ const lockFile = join167(LOCK_DIR, `${LOCK_NAME2}.lock`);
76731
76731
  if (!existsSync84(lockFile)) {
76732
76732
  logger.info("Content daemon is not running.");
76733
76733
  return;
@@ -76766,9 +76766,9 @@ async function statusContent() {
76766
76766
  } catch {}
76767
76767
  }
76768
76768
  async function logsContent(options2) {
76769
- const logDir = join166(homedir57(), ".claudekit", "logs");
76769
+ const logDir = join167(homedir57(), ".claudekit", "logs");
76770
76770
  const dateStr = new Date().toISOString().slice(0, 10).replace(/-/g, "");
76771
- const logPath = join166(logDir, `content-${dateStr}.log`);
76771
+ const logPath = join167(logDir, `content-${dateStr}.log`);
76772
76772
  if (!existsSync84(logPath)) {
76773
76773
  logger.info("No content logs found for today.");
76774
76774
  return;
@@ -76800,13 +76800,13 @@ var init_content_subcommands = __esm(() => {
76800
76800
  init_setup_wizard();
76801
76801
  init_state_manager();
76802
76802
  init_content_review_commands();
76803
- LOCK_DIR = join166(homedir57(), ".claudekit", "locks");
76803
+ LOCK_DIR = join167(homedir57(), ".claudekit", "locks");
76804
76804
  });
76805
76805
 
76806
76806
  // src/commands/content/content-command.ts
76807
76807
  import { existsSync as existsSync85, mkdirSync as mkdirSync9, unlinkSync as unlinkSync7, writeFileSync as writeFileSync7 } from "node:fs";
76808
76808
  import { homedir as homedir58 } from "node:os";
76809
- import { join as join167 } from "node:path";
76809
+ import { join as join168 } from "node:path";
76810
76810
  async function contentCommand(options2) {
76811
76811
  const cwd2 = process.cwd();
76812
76812
  const contentLogger = new ContentLogger;
@@ -76984,8 +76984,8 @@ var init_content_command = __esm(() => {
76984
76984
  init_publisher();
76985
76985
  init_review_manager();
76986
76986
  init_state_manager();
76987
- LOCK_DIR2 = join167(homedir58(), ".claudekit", "locks");
76988
- LOCK_FILE = join167(LOCK_DIR2, "ck-content.lock");
76987
+ LOCK_DIR2 = join168(homedir58(), ".claudekit", "locks");
76988
+ LOCK_FILE = join168(LOCK_DIR2, "ck-content.lock");
76989
76989
  });
76990
76990
 
76991
76991
  // src/commands/content/index.ts
@@ -85283,7 +85283,7 @@ function buildDuplicateInventoryCheck(projectSkills, globalSkills) {
85283
85283
  if (duplicates.length === 0) {
85284
85284
  return pass("ck-skill-inventory", "Skill Inventory", "No duplicate project/global skills");
85285
85285
  }
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");
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");
85287
85287
  }
85288
85288
  function buildSkillOverridesCheck(settingsPath, read) {
85289
85289
  if (!read.settings || !Object.prototype.hasOwnProperty.call(read.settings, "skillOverrides")) {
@@ -111460,14 +111460,48 @@ init_skill_catalog_generator();
111460
111460
  init_skill_search_index();
111461
111461
  init_logger();
111462
111462
  init_agents();
111463
- init_skills_discovery();
111464
- init_skills_installer();
111465
- init_skills_registry();
111466
- init_skills_uninstaller();
111467
111463
  var import_gray_matter12 = __toESM(require_gray_matter(), 1);
111468
111464
  var import_picocolors37 = __toESM(require_picocolors(), 1);
111469
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
111470
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();
111471
111505
 
111472
111506
  // src/commands/skills/types.ts
111473
111507
  init_zod();
@@ -111603,7 +111637,7 @@ async function handleValidate2(sourcePath) {
111603
111637
  spinner.stop(`Checked ${skills.length} skill(s)`);
111604
111638
  let hasIssues = false;
111605
111639
  for (const skill of skills) {
111606
- const skillMdPath = join148(skill.path, "SKILL.md");
111640
+ const skillMdPath = join149(skill.path, "SKILL.md");
111607
111641
  try {
111608
111642
  const content = await readFile65(skillMdPath, "utf-8");
111609
111643
  const { data } = import_gray_matter12.default(content, {
@@ -111639,30 +111673,55 @@ async function detectInstalledAgents2() {
111639
111673
  }
111640
111674
  async function listSkills2(showInstalled) {
111641
111675
  if (showInstalled) {
111642
- const installations = await getInstalledSkills();
111643
- if (installations.length === 0) {
111644
- 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.");
111645
111682
  return;
111646
111683
  }
111647
111684
  console.log();
111648
- f2.step(import_picocolors37.default.bold("Installed Skills"));
111649
- console.log();
111650
- const bySkill = new Map;
111651
- for (const inst of installations) {
111652
- const list3 = bySkill.get(inst.skill) || [];
111653
- list3.push(inst);
111654
- 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();
111655
111704
  }
111656
- for (const [skill, installs] of bySkill) {
111657
- console.log(` ${import_picocolors37.default.cyan(skill)}`);
111658
- for (const inst of installs) {
111659
- const scope = inst.global ? "global" : "project";
111660
- 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);
111661
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)`));
111662
111723
  }
111663
111724
  console.log();
111664
- console.log(import_picocolors37.default.dim(` ${installations.length} installation(s) across ${bySkill.size} skill(s)`));
111665
- console.log();
111666
111725
  return;
111667
111726
  }
111668
111727
  const sourcePath = getSkillSourcePath();
@@ -112161,7 +112220,7 @@ async function detectInstallations() {
112161
112220
 
112162
112221
  // src/commands/uninstall/removal-handler.ts
112163
112222
  import { readdirSync as readdirSync10, rmSync as rmSync5 } from "node:fs";
112164
- 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";
112165
112224
  init_logger();
112166
112225
  init_safe_prompts();
112167
112226
  init_safe_spinner();
@@ -112170,7 +112229,7 @@ var import_fs_extra41 = __toESM(require_lib(), 1);
112170
112229
  // src/commands/uninstall/analysis-handler.ts
112171
112230
  init_metadata_migration();
112172
112231
  import { readdirSync as readdirSync9, rmSync as rmSync4 } from "node:fs";
112173
- import { dirname as dirname45, join as join149 } from "node:path";
112232
+ import { dirname as dirname45, join as join150 } from "node:path";
112174
112233
  init_logger();
112175
112234
  init_safe_prompts();
112176
112235
  var import_fs_extra40 = __toESM(require_lib(), 1);
@@ -112230,7 +112289,7 @@ async function analyzeInstallation(installation, forceOverwrite, kit) {
112230
112289
  const remainingFiles = metadata.kits?.[remainingKit]?.files || [];
112231
112290
  for (const file of remainingFiles) {
112232
112291
  const relativePath = normalizeTrackedPath(file.path);
112233
- if (await import_fs_extra40.pathExists(join149(installation.path, relativePath))) {
112292
+ if (await import_fs_extra40.pathExists(join150(installation.path, relativePath))) {
112234
112293
  result.retainedManifestPaths.push(relativePath);
112235
112294
  }
112236
112295
  }
@@ -112238,7 +112297,7 @@ async function analyzeInstallation(installation, forceOverwrite, kit) {
112238
112297
  const kitFiles = metadata.kits[kit].files || [];
112239
112298
  for (const trackedFile of kitFiles) {
112240
112299
  const relativePath = normalizeTrackedPath(trackedFile.path);
112241
- const filePath = join149(installation.path, relativePath);
112300
+ const filePath = join150(installation.path, relativePath);
112242
112301
  if (preservedPaths.has(relativePath)) {
112243
112302
  result.toPreserve.push({ path: relativePath, reason: "shared with other kit" });
112244
112303
  continue;
@@ -112271,7 +112330,7 @@ async function analyzeInstallation(installation, forceOverwrite, kit) {
112271
112330
  }
112272
112331
  for (const trackedFile of allTrackedFiles) {
112273
112332
  const relativePath = normalizeTrackedPath(trackedFile.path);
112274
- const filePath = join149(installation.path, relativePath);
112333
+ const filePath = join150(installation.path, relativePath);
112275
112334
  const ownershipResult = await OwnershipChecker.checkOwnership(filePath, metadata, installation.path);
112276
112335
  if (!ownershipResult.exists)
112277
112336
  continue;
@@ -112414,7 +112473,7 @@ async function removeInstallations(installations, options2) {
112414
112473
  let removedCount = 0;
112415
112474
  let cleanedDirs = 0;
112416
112475
  for (const item of analysis.toDelete) {
112417
- const filePath = join150(installation.path, item.path);
112476
+ const filePath = join151(installation.path, item.path);
112418
112477
  if (!await import_fs_extra41.pathExists(filePath))
112419
112478
  continue;
112420
112479
  if (!await isPathSafeToRemove(filePath, installation.path)) {
@@ -112823,7 +112882,7 @@ ${import_picocolors40.default.bold(import_picocolors40.default.cyan(result.kitCo
112823
112882
  init_logger();
112824
112883
  import { existsSync as existsSync75 } from "node:fs";
112825
112884
  import { rm as rm17 } from "node:fs/promises";
112826
- import { join as join157 } from "node:path";
112885
+ import { join as join158 } from "node:path";
112827
112886
  var import_picocolors41 = __toESM(require_picocolors(), 1);
112828
112887
 
112829
112888
  // src/commands/watch/phases/implementation-runner.ts
@@ -113342,7 +113401,7 @@ function spawnAndCollect3(command, args) {
113342
113401
 
113343
113402
  // src/commands/watch/phases/issue-processor.ts
113344
113403
  import { mkdir as mkdir39, writeFile as writeFile38 } from "node:fs/promises";
113345
- import { join as join153 } from "node:path";
113404
+ import { join as join154 } from "node:path";
113346
113405
 
113347
113406
  // src/commands/watch/phases/approval-detector.ts
113348
113407
  init_logger();
@@ -113720,9 +113779,9 @@ async function checkAwaitingApproval(state, setup, options2, watchLog, projectDi
113720
113779
 
113721
113780
  // src/commands/watch/phases/plan-dir-finder.ts
113722
113781
  import { readdir as readdir44, stat as stat23 } from "node:fs/promises";
113723
- import { join as join152 } from "node:path";
113782
+ import { join as join153 } from "node:path";
113724
113783
  async function findRecentPlanDir(cwd2, issueNumber, watchLog) {
113725
- const plansRoot = join152(cwd2, "plans");
113784
+ const plansRoot = join153(cwd2, "plans");
113726
113785
  try {
113727
113786
  const entries = await readdir44(plansRoot);
113728
113787
  const tenMinAgo = Date.now() - 10 * 60 * 1000;
@@ -113731,14 +113790,14 @@ async function findRecentPlanDir(cwd2, issueNumber, watchLog) {
113731
113790
  for (const entry of entries) {
113732
113791
  if (entry === "watch" || entry === "reports" || entry === "visuals")
113733
113792
  continue;
113734
- const dirPath = join152(plansRoot, entry);
113793
+ const dirPath = join153(plansRoot, entry);
113735
113794
  const dirStat = await stat23(dirPath);
113736
113795
  if (!dirStat.isDirectory())
113737
113796
  continue;
113738
113797
  if (dirStat.mtimeMs < tenMinAgo)
113739
113798
  continue;
113740
113799
  try {
113741
- await stat23(join152(dirPath, "plan.md"));
113800
+ await stat23(join153(dirPath, "plan.md"));
113742
113801
  } catch {
113743
113802
  continue;
113744
113803
  }
@@ -113969,13 +114028,13 @@ async function handlePlanGeneration(issue, state, config, setup, options2, watch
113969
114028
  stats.plansCreated++;
113970
114029
  const detectedPlanDir = await findRecentPlanDir(projectDir, issue.number, watchLog);
113971
114030
  if (detectedPlanDir) {
113972
- state.activeIssues[numStr].planPath = join153(detectedPlanDir, "plan.md");
114031
+ state.activeIssues[numStr].planPath = join154(detectedPlanDir, "plan.md");
113973
114032
  watchLog.info(`Plan directory detected: ${detectedPlanDir}`);
113974
114033
  } else {
113975
114034
  try {
113976
- const planDir = join153(projectDir, "plans", "watch");
114035
+ const planDir = join154(projectDir, "plans", "watch");
113977
114036
  await mkdir39(planDir, { recursive: true });
113978
- const planFilePath = join153(planDir, `issue-${issue.number}-plan.md`);
114037
+ const planFilePath = join154(planDir, `issue-${issue.number}-plan.md`);
113979
114038
  await writeFile38(planFilePath, planResult.planText, "utf-8");
113980
114039
  state.activeIssues[numStr].planPath = planFilePath;
113981
114040
  watchLog.info(`Plan saved (fallback) to ${planFilePath}`);
@@ -114282,18 +114341,18 @@ init_logger();
114282
114341
  import { spawnSync as spawnSync7 } from "node:child_process";
114283
114342
  import { existsSync as existsSync72 } from "node:fs";
114284
114343
  import { readdir as readdir45, stat as stat24 } from "node:fs/promises";
114285
- import { join as join154 } from "node:path";
114344
+ import { join as join155 } from "node:path";
114286
114345
  async function scanForRepos(parentDir) {
114287
114346
  const repos = [];
114288
114347
  const entries = await readdir45(parentDir);
114289
114348
  for (const entry of entries) {
114290
114349
  if (entry.startsWith("."))
114291
114350
  continue;
114292
- const fullPath = join154(parentDir, entry);
114351
+ const fullPath = join155(parentDir, entry);
114293
114352
  const entryStat = await stat24(fullPath);
114294
114353
  if (!entryStat.isDirectory())
114295
114354
  continue;
114296
- const gitDir = join154(fullPath, ".git");
114355
+ const gitDir = join155(fullPath, ".git");
114297
114356
  if (!existsSync72(gitDir))
114298
114357
  continue;
114299
114358
  const result = spawnSync7("gh", ["repo", "view", "--json", "owner,name"], {
@@ -114320,7 +114379,7 @@ init_logger();
114320
114379
  import { spawnSync as spawnSync8 } from "node:child_process";
114321
114380
  import { existsSync as existsSync73 } from "node:fs";
114322
114381
  import { homedir as homedir52 } from "node:os";
114323
- import { join as join155 } from "node:path";
114382
+ import { join as join156 } from "node:path";
114324
114383
  async function validateSetup(cwd2) {
114325
114384
  const workDir = cwd2 ?? process.cwd();
114326
114385
  const ghVersion = spawnSync8("gh", ["--version"], { encoding: "utf-8", timeout: 1e4 });
@@ -114351,7 +114410,7 @@ Run this command from a directory with a GitHub remote.`);
114351
114410
  } catch {
114352
114411
  throw new Error(`Failed to parse repository info: ${ghRepo.stdout}`);
114353
114412
  }
114354
- const skillsPath = join155(homedir52(), ".claude", "skills");
114413
+ const skillsPath = join156(homedir52(), ".claude", "skills");
114355
114414
  const skillsAvailable = existsSync73(skillsPath);
114356
114415
  if (!skillsAvailable) {
114357
114416
  logger.warning(`ClaudeKit Engineer skills not found at ${skillsPath}`);
@@ -114370,7 +114429,7 @@ init_path_resolver();
114370
114429
  import { createWriteStream as createWriteStream3, statSync as statSync13 } from "node:fs";
114371
114430
  import { existsSync as existsSync74 } from "node:fs";
114372
114431
  import { mkdir as mkdir41, rename as rename13 } from "node:fs/promises";
114373
- import { join as join156 } from "node:path";
114432
+ import { join as join157 } from "node:path";
114374
114433
 
114375
114434
  class WatchLogger {
114376
114435
  logStream = null;
@@ -114378,7 +114437,7 @@ class WatchLogger {
114378
114437
  logPath = null;
114379
114438
  maxBytes;
114380
114439
  constructor(logDir, maxBytes = 0) {
114381
- this.logDir = logDir ?? join156(PathResolver.getClaudeKitDir(), "logs");
114440
+ this.logDir = logDir ?? join157(PathResolver.getClaudeKitDir(), "logs");
114382
114441
  this.maxBytes = maxBytes;
114383
114442
  }
114384
114443
  async init() {
@@ -114387,7 +114446,7 @@ class WatchLogger {
114387
114446
  await mkdir41(this.logDir, { recursive: true });
114388
114447
  }
114389
114448
  const dateStr = formatDate(new Date);
114390
- this.logPath = join156(this.logDir, `watch-${dateStr}.log`);
114449
+ this.logPath = join157(this.logDir, `watch-${dateStr}.log`);
114391
114450
  this.logStream = createWriteStream3(this.logPath, { flags: "a", mode: 384 });
114392
114451
  } catch (error) {
114393
114452
  logger.warning(`Cannot create watch log file: ${error instanceof Error ? error.message : "Unknown"}`);
@@ -114569,7 +114628,7 @@ async function watchCommand(options2) {
114569
114628
  }
114570
114629
  async function discoverRepos(options2, watchLog) {
114571
114630
  const cwd2 = process.cwd();
114572
- const isGitRepo = existsSync75(join157(cwd2, ".git"));
114631
+ const isGitRepo = existsSync75(join158(cwd2, ".git"));
114573
114632
  if (options2.force) {
114574
114633
  await forceRemoveLock(watchLog);
114575
114634
  }
@@ -114827,7 +114886,7 @@ function registerCommands(cli) {
114827
114886
  init_package();
114828
114887
  init_config_version_checker();
114829
114888
  import { existsSync as existsSync87, readFileSync as readFileSync22 } from "node:fs";
114830
- import { join as join169 } from "node:path";
114889
+ import { join as join170 } from "node:path";
114831
114890
 
114832
114891
  // src/domains/versioning/version-checker.ts
114833
114892
  init_version_utils();
@@ -114842,14 +114901,14 @@ init_logger();
114842
114901
  init_path_resolver();
114843
114902
  import { existsSync as existsSync86 } from "node:fs";
114844
114903
  import { mkdir as mkdir42, readFile as readFile69, writeFile as writeFile41 } from "node:fs/promises";
114845
- import { join as join168 } from "node:path";
114904
+ import { join as join169 } from "node:path";
114846
114905
 
114847
114906
  class VersionCacheManager {
114848
114907
  static CACHE_FILENAME = "version-check.json";
114849
114908
  static CACHE_TTL_MS = 7 * 24 * 60 * 60 * 1000;
114850
114909
  static getCacheFile() {
114851
114910
  const cacheDir = PathResolver.getCacheDir(false);
114852
- return join168(cacheDir, VersionCacheManager.CACHE_FILENAME);
114911
+ return join169(cacheDir, VersionCacheManager.CACHE_FILENAME);
114853
114912
  }
114854
114913
  static async load() {
114855
114914
  const cacheFile = VersionCacheManager.getCacheFile();
@@ -115160,9 +115219,9 @@ async function displayVersion() {
115160
115219
  let localInstalledKits = [];
115161
115220
  let globalInstalledKits = [];
115162
115221
  const globalKitDir = PathResolver.getGlobalKitDir();
115163
- const globalMetadataPath = join169(globalKitDir, "metadata.json");
115222
+ const globalMetadataPath = join170(globalKitDir, "metadata.json");
115164
115223
  const prefix = PathResolver.getPathPrefix(false);
115165
- 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");
115166
115225
  const isLocalSameAsGlobal = localMetadataPath === globalMetadataPath;
115167
115226
  if (!isLocalSameAsGlobal && existsSync87(localMetadataPath)) {
115168
115227
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claudekit-cli",
3
- "version": "4.2.0",
3
+ "version": "4.2.1",
4
4
  "description": "CLI tool for bootstrapping and updating ClaudeKit projects",
5
5
  "type": "module",
6
6
  "repository": {