claudekit-cli 1.4.1 → 1.5.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.
Files changed (47) hide show
  1. package/bin/ck-darwin-arm64 +0 -0
  2. package/bin/ck-darwin-x64 +0 -0
  3. package/bin/ck-linux-x64 +0 -0
  4. package/bin/ck-win32-x64.exe +0 -0
  5. package/bin/ck.js +62 -0
  6. package/package.json +6 -2
  7. package/.github/workflows/ci.yml +0 -45
  8. package/.github/workflows/claude-code-review.yml +0 -57
  9. package/.github/workflows/claude.yml +0 -50
  10. package/.github/workflows/release.yml +0 -102
  11. package/.releaserc.json +0 -17
  12. package/.repomixignore +0 -15
  13. package/AGENTS.md +0 -217
  14. package/CHANGELOG.md +0 -95
  15. package/CLAUDE.md +0 -34
  16. package/biome.json +0 -28
  17. package/bun.lock +0 -863
  18. package/dist/index.js +0 -22511
  19. package/src/commands/new.ts +0 -185
  20. package/src/commands/update.ts +0 -174
  21. package/src/commands/version.ts +0 -135
  22. package/src/index.ts +0 -102
  23. package/src/lib/auth.ts +0 -157
  24. package/src/lib/download.ts +0 -689
  25. package/src/lib/github.ts +0 -230
  26. package/src/lib/merge.ts +0 -119
  27. package/src/lib/prompts.ts +0 -114
  28. package/src/types.ts +0 -178
  29. package/src/utils/config.ts +0 -87
  30. package/src/utils/file-scanner.ts +0 -134
  31. package/src/utils/logger.ts +0 -124
  32. package/src/utils/safe-prompts.ts +0 -44
  33. package/src/utils/safe-spinner.ts +0 -38
  34. package/src/version.json +0 -3
  35. package/tests/commands/version.test.ts +0 -297
  36. package/tests/integration/cli.test.ts +0 -252
  37. package/tests/lib/auth.test.ts +0 -116
  38. package/tests/lib/download.test.ts +0 -292
  39. package/tests/lib/github-download-priority.test.ts +0 -432
  40. package/tests/lib/github.test.ts +0 -52
  41. package/tests/lib/merge.test.ts +0 -267
  42. package/tests/lib/prompts.test.ts +0 -66
  43. package/tests/types.test.ts +0 -337
  44. package/tests/utils/config.test.ts +0 -263
  45. package/tests/utils/file-scanner.test.ts +0 -202
  46. package/tests/utils/logger.test.ts +0 -239
  47. package/tsconfig.json +0 -30
@@ -1,185 +0,0 @@
1
- import { resolve } from "node:path";
2
- import { pathExists, readdir } from "fs-extra";
3
- import { AuthManager } from "../lib/auth.js";
4
- import { DownloadManager } from "../lib/download.js";
5
- import { GitHubClient } from "../lib/github.js";
6
- import { FileMerger } from "../lib/merge.js";
7
- import { PromptsManager } from "../lib/prompts.js";
8
- import { AVAILABLE_KITS, type NewCommandOptions, NewCommandOptionsSchema } from "../types.js";
9
- import { ConfigManager } from "../utils/config.js";
10
- import { logger } from "../utils/logger.js";
11
- import { createSpinner } from "../utils/safe-spinner.js";
12
-
13
- export async function newCommand(options: NewCommandOptions): Promise<void> {
14
- const prompts = new PromptsManager();
15
-
16
- prompts.intro("🚀 ClaudeKit - Create New Project");
17
-
18
- try {
19
- // Validate and parse options
20
- const validOptions = NewCommandOptionsSchema.parse(options);
21
-
22
- // Detect non-interactive mode
23
- const isNonInteractive =
24
- !process.stdin.isTTY || process.env.CI === "true" || process.env.NON_INTERACTIVE === "true";
25
-
26
- // Load config for defaults
27
- const config = await ConfigManager.get();
28
-
29
- // Get kit selection
30
- let kit = validOptions.kit || config.defaults?.kit;
31
- if (!kit) {
32
- if (isNonInteractive) {
33
- throw new Error("Kit must be specified via --kit flag in non-interactive mode");
34
- }
35
- kit = await prompts.selectKit();
36
- }
37
-
38
- const kitConfig = AVAILABLE_KITS[kit];
39
- logger.info(`Selected kit: ${kitConfig.name}`);
40
-
41
- // Get target directory
42
- let targetDir = validOptions.dir || config.defaults?.dir || ".";
43
- if (!validOptions.dir && !config.defaults?.dir) {
44
- if (isNonInteractive) {
45
- targetDir = ".";
46
- } else {
47
- targetDir = await prompts.getDirectory(targetDir);
48
- }
49
- }
50
-
51
- const resolvedDir = resolve(targetDir);
52
- logger.info(`Target directory: ${resolvedDir}`);
53
-
54
- // Check if directory exists and is not empty
55
- if (await pathExists(resolvedDir)) {
56
- const files = await readdir(resolvedDir);
57
- const isEmpty = files.length === 0;
58
- if (!isEmpty) {
59
- if (isNonInteractive) {
60
- if (!validOptions.force) {
61
- throw new Error(
62
- "Directory is not empty. Use --force flag to overwrite in non-interactive mode",
63
- );
64
- }
65
- logger.info("Directory is not empty. Proceeding with --force flag");
66
- } else {
67
- const continueAnyway = await prompts.confirm(
68
- "Directory is not empty. Files may be overwritten. Continue?",
69
- );
70
- if (!continueAnyway) {
71
- logger.warning("Operation cancelled");
72
- return;
73
- }
74
- }
75
- }
76
- }
77
-
78
- // Initialize GitHub client
79
- const github = new GitHubClient();
80
-
81
- // Check repository access
82
- const spinner = createSpinner("Checking repository access...").start();
83
- const hasAccess = await github.checkAccess(kitConfig);
84
- if (!hasAccess) {
85
- spinner.fail("Access denied to repository");
86
- logger.error(
87
- `Cannot access ${kitConfig.name}. Make sure your GitHub token has access to private repositories.`,
88
- );
89
- return;
90
- }
91
- spinner.succeed("Repository access verified");
92
-
93
- // Get release
94
- let release;
95
- if (validOptions.version) {
96
- logger.info(`Fetching release version: ${validOptions.version}`);
97
- release = await github.getReleaseByTag(kitConfig, validOptions.version);
98
- } else {
99
- logger.info("Fetching latest release...");
100
- release = await github.getLatestRelease(kitConfig);
101
- }
102
-
103
- logger.success(`Found release: ${release.tag_name} - ${release.name}`);
104
-
105
- // Get downloadable asset (custom asset or GitHub tarball)
106
- const downloadInfo = GitHubClient.getDownloadableAsset(release);
107
-
108
- logger.info(`Download source: ${downloadInfo.type}`);
109
- logger.debug(`Download URL: ${downloadInfo.url}`);
110
-
111
- // Download asset
112
- const downloadManager = new DownloadManager();
113
-
114
- // Apply user exclude patterns if provided
115
- if (validOptions.exclude && validOptions.exclude.length > 0) {
116
- downloadManager.setExcludePatterns(validOptions.exclude);
117
- }
118
-
119
- const tempDir = await downloadManager.createTempDir();
120
-
121
- // Get authentication token for API requests
122
- const { token } = await AuthManager.getToken();
123
-
124
- let archivePath: string;
125
- try {
126
- // Try downloading the asset/tarball with authentication
127
- archivePath = await downloadManager.downloadFile({
128
- url: downloadInfo.url,
129
- name: downloadInfo.name,
130
- size: downloadInfo.size,
131
- destDir: tempDir,
132
- token, // Always pass token for private repository access
133
- });
134
- } catch (error) {
135
- // If asset download fails, fallback to GitHub tarball
136
- if (downloadInfo.type === "asset") {
137
- logger.warning("Asset download failed, falling back to GitHub tarball...");
138
- const tarballInfo = {
139
- type: "github-tarball" as const,
140
- url: release.tarball_url,
141
- name: `${kitConfig.repo}-${release.tag_name}.tar.gz`,
142
- size: 0, // Size unknown for tarball
143
- };
144
-
145
- archivePath = await downloadManager.downloadFile({
146
- url: tarballInfo.url,
147
- name: tarballInfo.name,
148
- size: tarballInfo.size,
149
- destDir: tempDir,
150
- token,
151
- });
152
- } else {
153
- throw error;
154
- }
155
- }
156
-
157
- // Extract archive
158
- const extractDir = `${tempDir}/extracted`;
159
- await downloadManager.extractArchive(archivePath, extractDir);
160
-
161
- // Validate extraction
162
- await downloadManager.validateExtraction(extractDir);
163
-
164
- // Copy files to target directory
165
- const merger = new FileMerger();
166
-
167
- // Apply user exclude patterns if provided
168
- if (validOptions.exclude && validOptions.exclude.length > 0) {
169
- merger.addIgnorePatterns(validOptions.exclude);
170
- }
171
-
172
- await merger.merge(extractDir, resolvedDir, true); // Skip confirmation for new projects
173
-
174
- prompts.outro(`✨ Project created successfully at ${resolvedDir}`);
175
-
176
- // Show next steps
177
- prompts.note(
178
- `cd ${targetDir !== "." ? targetDir : "into the directory"}\nbun install\nbun run dev`,
179
- "Next steps",
180
- );
181
- } catch (error) {
182
- logger.error(error instanceof Error ? error.message : "Unknown error occurred");
183
- process.exit(1);
184
- }
185
- }
@@ -1,174 +0,0 @@
1
- import { resolve } from "node:path";
2
- import { pathExists } from "fs-extra";
3
- import { AuthManager } from "../lib/auth.js";
4
- import { DownloadManager } from "../lib/download.js";
5
- import { GitHubClient } from "../lib/github.js";
6
- import { FileMerger } from "../lib/merge.js";
7
- import { PromptsManager } from "../lib/prompts.js";
8
- import { AVAILABLE_KITS, type UpdateCommandOptions, UpdateCommandOptionsSchema } from "../types.js";
9
- import { ConfigManager } from "../utils/config.js";
10
- import { FileScanner } from "../utils/file-scanner.js";
11
- import { logger } from "../utils/logger.js";
12
- import { createSpinner } from "../utils/safe-spinner.js";
13
-
14
- export async function updateCommand(options: UpdateCommandOptions): Promise<void> {
15
- const prompts = new PromptsManager();
16
-
17
- prompts.intro("🔄 ClaudeKit - Update Project");
18
-
19
- try {
20
- // Validate and parse options
21
- const validOptions = UpdateCommandOptionsSchema.parse(options);
22
-
23
- // Load config for defaults
24
- const config = await ConfigManager.get();
25
-
26
- // Get kit selection
27
- let kit = validOptions.kit || config.defaults?.kit;
28
- if (!kit) {
29
- kit = await prompts.selectKit();
30
- }
31
-
32
- const kitConfig = AVAILABLE_KITS[kit];
33
- logger.info(`Selected kit: ${kitConfig.name}`);
34
-
35
- // Get target directory
36
- let targetDir = validOptions.dir || config.defaults?.dir || ".";
37
- if (!validOptions.dir && !config.defaults?.dir) {
38
- targetDir = await prompts.getDirectory(targetDir);
39
- }
40
-
41
- const resolvedDir = resolve(targetDir);
42
- logger.info(`Target directory: ${resolvedDir}`);
43
-
44
- // Check if directory exists
45
- if (!(await pathExists(resolvedDir))) {
46
- logger.error(`Directory does not exist: ${resolvedDir}`);
47
- logger.info('Use "ck new" to create a new project');
48
- return;
49
- }
50
-
51
- // Initialize GitHub client
52
- const github = new GitHubClient();
53
-
54
- // Check repository access
55
- const spinner = createSpinner("Checking repository access...").start();
56
- const hasAccess = await github.checkAccess(kitConfig);
57
- if (!hasAccess) {
58
- spinner.fail("Access denied to repository");
59
- logger.error(
60
- `Cannot access ${kitConfig.name}. Make sure your GitHub token has access to private repositories.`,
61
- );
62
- return;
63
- }
64
- spinner.succeed("Repository access verified");
65
-
66
- // Get release
67
- let release;
68
- if (validOptions.version) {
69
- logger.info(`Fetching release version: ${validOptions.version}`);
70
- release = await github.getReleaseByTag(kitConfig, validOptions.version);
71
- } else {
72
- logger.info("Fetching latest release...");
73
- release = await github.getLatestRelease(kitConfig);
74
- }
75
-
76
- logger.success(`Found release: ${release.tag_name} - ${release.name}`);
77
-
78
- // Get downloadable asset (custom asset or GitHub tarball)
79
- const downloadInfo = GitHubClient.getDownloadableAsset(release);
80
-
81
- logger.info(`Download source: ${downloadInfo.type}`);
82
- logger.debug(`Download URL: ${downloadInfo.url}`);
83
-
84
- // Download asset
85
- const downloadManager = new DownloadManager();
86
-
87
- // Apply user exclude patterns if provided
88
- if (validOptions.exclude && validOptions.exclude.length > 0) {
89
- downloadManager.setExcludePatterns(validOptions.exclude);
90
- }
91
-
92
- const tempDir = await downloadManager.createTempDir();
93
-
94
- // Get authentication token for API requests
95
- const { token } = await AuthManager.getToken();
96
-
97
- let archivePath: string;
98
- try {
99
- // Try downloading the asset/tarball with authentication
100
- archivePath = await downloadManager.downloadFile({
101
- url: downloadInfo.url,
102
- name: downloadInfo.name,
103
- size: downloadInfo.size,
104
- destDir: tempDir,
105
- token, // Always pass token for private repository access
106
- });
107
- } catch (error) {
108
- // If asset download fails, fallback to GitHub tarball
109
- if (downloadInfo.type === "asset") {
110
- logger.warning("Asset download failed, falling back to GitHub tarball...");
111
- const tarballInfo = {
112
- type: "github-tarball" as const,
113
- url: release.tarball_url,
114
- name: `${kitConfig.repo}-${release.tag_name}.tar.gz`,
115
- size: 0, // Size unknown for tarball
116
- };
117
-
118
- archivePath = await downloadManager.downloadFile({
119
- url: tarballInfo.url,
120
- name: tarballInfo.name,
121
- size: tarballInfo.size,
122
- destDir: tempDir,
123
- token,
124
- });
125
- } else {
126
- throw error;
127
- }
128
- }
129
-
130
- // Extract archive
131
- const extractDir = `${tempDir}/extracted`;
132
- await downloadManager.extractArchive(archivePath, extractDir);
133
-
134
- // Validate extraction
135
- await downloadManager.validateExtraction(extractDir);
136
-
137
- // Identify custom .claude files to preserve
138
- logger.info("Scanning for custom .claude files...");
139
- const customClaudeFiles = await FileScanner.findCustomFiles(resolvedDir, extractDir, ".claude");
140
-
141
- // Merge files with confirmation
142
- const merger = new FileMerger();
143
-
144
- // Add custom .claude files to ignore patterns
145
- if (customClaudeFiles.length > 0) {
146
- merger.addIgnorePatterns(customClaudeFiles);
147
- logger.success(`Protected ${customClaudeFiles.length} custom .claude file(s)`);
148
- }
149
-
150
- // Apply user exclude patterns if provided
151
- if (validOptions.exclude && validOptions.exclude.length > 0) {
152
- merger.addIgnorePatterns(validOptions.exclude);
153
- }
154
-
155
- await merger.merge(extractDir, resolvedDir, false); // Show confirmation for updates
156
-
157
- prompts.outro(`✨ Project updated successfully at ${resolvedDir}`);
158
-
159
- // Show next steps
160
- const protectedNote =
161
- customClaudeFiles.length > 0
162
- ? "Your project has been updated with the latest version.\nProtected files (.env, .claude custom files, etc.) were not modified."
163
- : "Your project has been updated with the latest version.\nProtected files (.env, etc.) were not modified.";
164
-
165
- prompts.note(protectedNote, "Update complete");
166
- } catch (error) {
167
- if (error instanceof Error && error.message === "Merge cancelled by user") {
168
- logger.warning("Update cancelled");
169
- return;
170
- }
171
- logger.error(error instanceof Error ? error.message : "Unknown error occurred");
172
- process.exit(1);
173
- }
174
- }
@@ -1,135 +0,0 @@
1
- import pc from "picocolors";
2
- import { GitHubClient } from "../lib/github.js";
3
- import { PromptsManager } from "../lib/prompts.js";
4
- import {
5
- AVAILABLE_KITS,
6
- type GitHubRelease,
7
- type VersionCommandOptions,
8
- VersionCommandOptionsSchema,
9
- } from "../types.js";
10
- import { logger } from "../utils/logger.js";
11
-
12
- /**
13
- * Format a date as a relative time string
14
- */
15
- function formatRelativeTime(dateString?: string): string {
16
- if (!dateString) return "Unknown";
17
-
18
- const date = new Date(dateString);
19
- const now = new Date();
20
- const diffMs = now.getTime() - date.getTime();
21
- const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));
22
-
23
- if (diffDays === 0) return "Today";
24
- if (diffDays === 1) return "Yesterday";
25
- if (diffDays < 7) return `${diffDays} days ago`;
26
- if (diffDays < 30) return `${Math.floor(diffDays / 7)} weeks ago`;
27
- if (diffDays < 365) return `${Math.floor(diffDays / 30)} months ago`;
28
- return `${Math.floor(diffDays / 365)} years ago`;
29
- }
30
-
31
- /**
32
- * Display releases for a single kit
33
- */
34
- function displayKitReleases(kitName: string, releases: GitHubRelease[]): void {
35
- console.log(`\n${pc.bold(pc.cyan(kitName))} - Available Versions:\n`);
36
-
37
- if (releases.length === 0) {
38
- console.log(pc.dim(" No releases found"));
39
- return;
40
- }
41
-
42
- for (const release of releases) {
43
- const version = pc.green(release.tag_name);
44
- const name = release.name || "No title";
45
- const publishedAt = formatRelativeTime(release.published_at);
46
- const assetCount = release.assets.length;
47
-
48
- // Add badges for prerelease and draft
49
- const badges: string[] = [];
50
- if (release.prerelease) badges.push(pc.yellow("[prerelease]"));
51
- if (release.draft) badges.push(pc.gray("[draft]"));
52
- const badgeStr = badges.length > 0 ? ` ${badges.join(" ")}` : "";
53
-
54
- // Format: version | name | time | assets
55
- const versionPart = version.padEnd(20);
56
- const namePart = name.length > 40 ? `${name.slice(0, 37)}...` : name.padEnd(40);
57
- const timePart = pc.dim(publishedAt.padEnd(20));
58
- const assetPart = pc.dim(`(${assetCount} ${assetCount === 1 ? "asset" : "assets"})`);
59
-
60
- console.log(` ${versionPart} ${namePart} ${timePart} ${assetPart}${badgeStr}`);
61
- }
62
-
63
- console.log(
64
- pc.dim(`\nShowing ${releases.length} ${releases.length === 1 ? "release" : "releases"}`),
65
- );
66
- }
67
-
68
- /**
69
- * Version command - List available versions of ClaudeKit repositories
70
- */
71
- export async function versionCommand(options: VersionCommandOptions): Promise<void> {
72
- const prompts = new PromptsManager();
73
-
74
- prompts.intro("📦 ClaudeKit - Available Versions");
75
-
76
- try {
77
- // Validate and parse options
78
- const validOptions = VersionCommandOptionsSchema.parse(options);
79
-
80
- // Determine which kits to fetch
81
- const kitsToFetch = validOptions.kit
82
- ? [validOptions.kit]
83
- : (Object.keys(AVAILABLE_KITS) as Array<keyof typeof AVAILABLE_KITS>);
84
-
85
- // Initialize GitHub client
86
- const github = new GitHubClient();
87
-
88
- // Determine limit (default to 30, similar to GitHub CLI)
89
- const limit = validOptions.limit || 30;
90
-
91
- // Fetch releases for all requested kits in parallel
92
- const releasePromises = kitsToFetch.map(async (kitType) => {
93
- const kitConfig = AVAILABLE_KITS[kitType];
94
- try {
95
- const releases = await github.listReleases(kitConfig, limit);
96
-
97
- // Filter out drafts and prereleases unless --all flag is set
98
- const filteredReleases = validOptions.all
99
- ? releases
100
- : releases.filter((r) => !r.draft && !r.prerelease);
101
-
102
- return {
103
- kitType,
104
- kitConfig,
105
- releases: filteredReleases,
106
- error: null,
107
- };
108
- } catch (error) {
109
- return {
110
- kitType,
111
- kitConfig,
112
- releases: [],
113
- error: error instanceof Error ? error.message : "Unknown error",
114
- };
115
- }
116
- });
117
-
118
- const results = await Promise.all(releasePromises);
119
-
120
- // Display results
121
- for (const result of results) {
122
- if (result.error) {
123
- console.log(`\n${pc.bold(pc.cyan(result.kitConfig.name))} - ${pc.red("Error")}`);
124
- console.log(pc.dim(` ${result.error}`));
125
- } else {
126
- displayKitReleases(result.kitConfig.name, result.releases);
127
- }
128
- }
129
-
130
- prompts.outro("✨ Done");
131
- } catch (error) {
132
- logger.error(error instanceof Error ? error.message : "Unknown error occurred");
133
- process.exit(1);
134
- }
135
- }
package/src/index.ts DELETED
@@ -1,102 +0,0 @@
1
- #!/usr/bin/env bun
2
-
3
- import { cac } from "cac";
4
- import packageInfo from "../package.json" assert { type: "json" };
5
- import { newCommand } from "./commands/new.js";
6
- import { updateCommand } from "./commands/update.js";
7
- import { versionCommand } from "./commands/version.js";
8
- import { logger } from "./utils/logger.js";
9
-
10
- // Set proper output encoding to prevent unicode rendering issues
11
- if (process.stdout.setEncoding) {
12
- process.stdout.setEncoding("utf8");
13
- }
14
- if (process.stderr.setEncoding) {
15
- process.stderr.setEncoding("utf8");
16
- }
17
-
18
- const packageVersion = packageInfo.version;
19
-
20
- const cli = cac("ck");
21
-
22
- // Global options
23
- cli.option("--verbose, -v", "Enable verbose logging for debugging");
24
- cli.option("--log-file <path>", "Write logs to file");
25
-
26
- // New command
27
- cli
28
- .command("new", "Bootstrap a new ClaudeKit project")
29
- .option("--dir <dir>", "Target directory (default: .)")
30
- .option("--kit <kit>", "Kit to use (engineer, marketing)")
31
- .option("--version <version>", "Specific version to download (default: latest)")
32
- .option("--force", "Overwrite existing files without confirmation")
33
- .option("--exclude <pattern>", "Exclude files matching glob pattern (can be used multiple times)")
34
- .action(async (options) => {
35
- // Normalize exclude to always be an array (CAC may pass string for single value)
36
- if (options.exclude && !Array.isArray(options.exclude)) {
37
- options.exclude = [options.exclude];
38
- }
39
- await newCommand(options);
40
- });
41
-
42
- // Update command
43
- cli
44
- .command("update", "Update existing ClaudeKit project")
45
- .option("--dir <dir>", "Target directory (default: .)")
46
- .option("--kit <kit>", "Kit to use (engineer, marketing)")
47
- .option("--version <version>", "Specific version to download (default: latest)")
48
- .option("--exclude <pattern>", "Exclude files matching glob pattern (can be used multiple times)")
49
- .action(async (options) => {
50
- // Normalize exclude to always be an array (CAC may pass string for single value)
51
- if (options.exclude && !Array.isArray(options.exclude)) {
52
- options.exclude = [options.exclude];
53
- }
54
- await updateCommand(options);
55
- });
56
-
57
- // Versions command
58
- cli
59
- .command("versions", "List available versions of ClaudeKit repositories")
60
- .option("--kit <kit>", "Filter by specific kit (engineer, marketing)")
61
- .option("--limit <limit>", "Number of releases to show (default: 30)")
62
- .option("--all", "Show all releases including prereleases")
63
- .action(async (options) => {
64
- await versionCommand(options);
65
- });
66
-
67
- // Version
68
- cli.version(packageVersion);
69
-
70
- // Help
71
- cli.help();
72
-
73
- // Parse to get global options first
74
- const parsed = cli.parse(process.argv, { run: false });
75
-
76
- // Check environment variable
77
- const envVerbose =
78
- process.env.CLAUDEKIT_VERBOSE === "1" || process.env.CLAUDEKIT_VERBOSE === "true";
79
-
80
- // Enable verbose if flag or env var is set
81
- const isVerbose = parsed.options.verbose || envVerbose;
82
-
83
- if (isVerbose) {
84
- logger.setVerbose(true);
85
- }
86
-
87
- // Set log file if specified
88
- if (parsed.options.logFile) {
89
- logger.setLogFile(parsed.options.logFile);
90
- }
91
-
92
- // Log startup info in verbose mode
93
- logger.verbose("ClaudeKit CLI starting", {
94
- version: packageVersion,
95
- command: parsed.args[0] || "none",
96
- options: parsed.options,
97
- cwd: process.cwd(),
98
- node: process.version,
99
- });
100
-
101
- // Parse again to run the command
102
- cli.parse();