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.
- package/bin/ck-darwin-arm64 +0 -0
- package/bin/ck-darwin-x64 +0 -0
- package/bin/ck-linux-x64 +0 -0
- package/bin/ck-win32-x64.exe +0 -0
- package/bin/ck.js +62 -0
- package/package.json +6 -2
- package/.github/workflows/ci.yml +0 -45
- package/.github/workflows/claude-code-review.yml +0 -57
- package/.github/workflows/claude.yml +0 -50
- package/.github/workflows/release.yml +0 -102
- package/.releaserc.json +0 -17
- package/.repomixignore +0 -15
- package/AGENTS.md +0 -217
- package/CHANGELOG.md +0 -95
- package/CLAUDE.md +0 -34
- package/biome.json +0 -28
- package/bun.lock +0 -863
- package/dist/index.js +0 -22511
- package/src/commands/new.ts +0 -185
- package/src/commands/update.ts +0 -174
- package/src/commands/version.ts +0 -135
- package/src/index.ts +0 -102
- package/src/lib/auth.ts +0 -157
- package/src/lib/download.ts +0 -689
- package/src/lib/github.ts +0 -230
- package/src/lib/merge.ts +0 -119
- package/src/lib/prompts.ts +0 -114
- package/src/types.ts +0 -178
- package/src/utils/config.ts +0 -87
- package/src/utils/file-scanner.ts +0 -134
- package/src/utils/logger.ts +0 -124
- package/src/utils/safe-prompts.ts +0 -44
- package/src/utils/safe-spinner.ts +0 -38
- package/src/version.json +0 -3
- package/tests/commands/version.test.ts +0 -297
- package/tests/integration/cli.test.ts +0 -252
- package/tests/lib/auth.test.ts +0 -116
- package/tests/lib/download.test.ts +0 -292
- package/tests/lib/github-download-priority.test.ts +0 -432
- package/tests/lib/github.test.ts +0 -52
- package/tests/lib/merge.test.ts +0 -267
- package/tests/lib/prompts.test.ts +0 -66
- package/tests/types.test.ts +0 -337
- package/tests/utils/config.test.ts +0 -263
- package/tests/utils/file-scanner.test.ts +0 -202
- package/tests/utils/logger.test.ts +0 -239
- package/tsconfig.json +0 -30
package/src/commands/new.ts
DELETED
|
@@ -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
|
-
}
|
package/src/commands/update.ts
DELETED
|
@@ -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
|
-
}
|
package/src/commands/version.ts
DELETED
|
@@ -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();
|