claudekit-cli 1.0.1 → 1.1.0
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/.github/workflows/ci.yml +2 -0
- package/CHANGELOG.md +12 -0
- package/CLAUDE.md +7 -0
- package/README.md +20 -2
- package/dist/index.js +102 -0
- package/package.json +1 -1
- package/src/commands/version.ts +135 -0
- package/src/index.ts +11 -0
- package/src/types.ts +7 -0
- package/tests/commands/version.test.ts +297 -0
- package/.opencode/agent/code-reviewer.md +0 -141
- package/.opencode/agent/debugger.md +0 -74
- package/.opencode/agent/docs-manager.md +0 -119
- package/.opencode/agent/git-manager.md +0 -60
- package/.opencode/agent/planner-researcher.md +0 -100
- package/.opencode/agent/planner.md +0 -87
- package/.opencode/agent/project-manager.md +0 -113
- package/.opencode/agent/researcher.md +0 -173
- package/.opencode/agent/solution-brainstormer.md +0 -89
- package/.opencode/agent/system-architecture.md +0 -192
- package/.opencode/agent/tester.md +0 -96
- package/.opencode/agent/ui-ux-designer.md +0 -203
- package/.opencode/agent/ui-ux-developer.md +0 -97
- package/.opencode/command/cook.md +0 -7
- package/.opencode/command/debug.md +0 -10
- package/.opencode/command/design/3d.md +0 -65
- package/.opencode/command/design/fast.md +0 -18
- package/.opencode/command/design/good.md +0 -21
- package/.opencode/command/design/screenshot.md +0 -22
- package/.opencode/command/design/video.md +0 -22
- package/.opencode/command/fix/ci.md +0 -8
- package/.opencode/command/fix/fast.md +0 -11
- package/.opencode/command/fix/hard.md +0 -15
- package/.opencode/command/fix/logs.md +0 -16
- package/.opencode/command/fix/test.md +0 -18
- package/.opencode/command/fix/types.md +0 -10
- package/.opencode/command/git/cm.md +0 -5
- package/.opencode/command/git/cp.md +0 -4
- package/.opencode/command/plan/ci.md +0 -12
- package/.opencode/command/plan/two.md +0 -13
- package/.opencode/command/plan.md +0 -10
- package/.opencode/command/test.md +0 -7
- package/.opencode/command/watzup.md +0 -8
- package/plans/251008-claudekit-cli-implementation-plan.md +0 -1469
- package/plans/reports/251008-from-code-reviewer-to-developer-review-report.md +0 -864
- package/plans/reports/251008-from-tester-to-developer-test-summary-report.md +0 -409
- package/plans/reports/251008-researcher-download-extraction-report.md +0 -1377
- package/plans/reports/251008-researcher-github-api-report.md +0 -1339
- package/plans/research/251008-cli-frameworks-bun-research.md +0 -1051
- package/plans/templates/bug-fix-template.md +0 -69
- package/plans/templates/feature-implementation-template.md +0 -84
- package/plans/templates/refactor-template.md +0 -82
- package/plans/templates/template-usage-guide.md +0 -58
package/.github/workflows/ci.yml
CHANGED
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
# [1.1.0](https://github.com/mrgoonie/claudekit-cli/compare/v1.0.1...v1.1.0) (2025-10-17)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* format package.json keywords array to single line ([c8dd66f](https://github.com/mrgoonie/claudekit-cli/commit/c8dd66faa94a84188790947fe3ee6f562d63cd46))
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* **cli:** add versions command to list available releases ([27fbad1](https://github.com/mrgoonie/claudekit-cli/commit/27fbad1be3b5df90cb85ba9a3dd1b0eeb4fa6125))
|
|
12
|
+
|
|
1
13
|
## [1.0.1](https://github.com/mrgoonie/claudekit-cli/compare/v1.0.0...v1.0.1) (2025-10-09)
|
|
2
14
|
|
|
3
15
|
|
package/CLAUDE.md
CHANGED
|
@@ -31,3 +31,10 @@ We keep all important docs in `./docs` folder and keep updating them, structure
|
|
|
31
31
|
└── project-roadmap.md
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
+
## Related directories
|
|
35
|
+
|
|
36
|
+
- ClaudeKit CLI: current directory
|
|
37
|
+
- ClaudeKit Engineer: `../claudekit-engineer`
|
|
38
|
+
- ClaudeKit Marketing: `../claudekit-marketing`
|
|
39
|
+
- ClaudeKit Website: `../claudekit-web`
|
|
40
|
+
- ClaudeKit Docs: `../claudekit-docs`
|
package/README.md
CHANGED
|
@@ -94,10 +94,27 @@ ck update --kit engineer
|
|
|
94
94
|
ck update --kit engineer --version v1.0.0
|
|
95
95
|
```
|
|
96
96
|
|
|
97
|
+
### List Available Versions
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
# Show all available versions for all kits
|
|
101
|
+
ck versions
|
|
102
|
+
|
|
103
|
+
# Filter by specific kit
|
|
104
|
+
ck versions --kit engineer
|
|
105
|
+
ck versions --kit marketing
|
|
106
|
+
|
|
107
|
+
# Show more versions (default: 30)
|
|
108
|
+
ck versions --limit 50
|
|
109
|
+
|
|
110
|
+
# Include prereleases and drafts
|
|
111
|
+
ck versions --all
|
|
112
|
+
```
|
|
113
|
+
|
|
97
114
|
### Other Commands
|
|
98
115
|
|
|
99
116
|
```bash
|
|
100
|
-
# Show version
|
|
117
|
+
# Show CLI version
|
|
101
118
|
ck --version
|
|
102
119
|
ck -v
|
|
103
120
|
|
|
@@ -233,7 +250,8 @@ claudekit-cli/
|
|
|
233
250
|
### 1. Commands
|
|
234
251
|
- **`ck new`**: Create new project from release
|
|
235
252
|
- **`ck update`**: Update existing project
|
|
236
|
-
- **`ck
|
|
253
|
+
- **`ck versions`**: List available versions of ClaudeKit repositories
|
|
254
|
+
- **`ck --version`**: Show CLI version
|
|
237
255
|
- **`ck --help`**: Show help
|
|
238
256
|
|
|
239
257
|
### 2. Authentication (Multi-Tier Fallback)
|
package/dist/index.js
CHANGED
|
@@ -17718,6 +17718,11 @@ var UpdateCommandOptionsSchema = exports_external.object({
|
|
|
17718
17718
|
kit: KitType.optional(),
|
|
17719
17719
|
version: exports_external.string().optional()
|
|
17720
17720
|
});
|
|
17721
|
+
var VersionCommandOptionsSchema = exports_external.object({
|
|
17722
|
+
kit: KitType.optional(),
|
|
17723
|
+
limit: exports_external.number().optional(),
|
|
17724
|
+
all: exports_external.boolean().optional()
|
|
17725
|
+
});
|
|
17721
17726
|
var ConfigSchema = exports_external.object({
|
|
17722
17727
|
github: exports_external.object({
|
|
17723
17728
|
token: exports_external.string().optional()
|
|
@@ -19085,6 +19090,100 @@ Protected files (.env, etc.) were not modified.`, "Update complete");
|
|
|
19085
19090
|
}
|
|
19086
19091
|
}
|
|
19087
19092
|
|
|
19093
|
+
// src/commands/version.ts
|
|
19094
|
+
var import_picocolors4 = __toESM(require_picocolors(), 1);
|
|
19095
|
+
function formatRelativeTime(dateString) {
|
|
19096
|
+
if (!dateString)
|
|
19097
|
+
return "Unknown";
|
|
19098
|
+
const date = new Date(dateString);
|
|
19099
|
+
const now = new Date;
|
|
19100
|
+
const diffMs = now.getTime() - date.getTime();
|
|
19101
|
+
const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));
|
|
19102
|
+
if (diffDays === 0)
|
|
19103
|
+
return "Today";
|
|
19104
|
+
if (diffDays === 1)
|
|
19105
|
+
return "Yesterday";
|
|
19106
|
+
if (diffDays < 7)
|
|
19107
|
+
return `${diffDays} days ago`;
|
|
19108
|
+
if (diffDays < 30)
|
|
19109
|
+
return `${Math.floor(diffDays / 7)} weeks ago`;
|
|
19110
|
+
if (diffDays < 365)
|
|
19111
|
+
return `${Math.floor(diffDays / 30)} months ago`;
|
|
19112
|
+
return `${Math.floor(diffDays / 365)} years ago`;
|
|
19113
|
+
}
|
|
19114
|
+
function displayKitReleases(kitName, releases) {
|
|
19115
|
+
console.log(`
|
|
19116
|
+
${import_picocolors4.default.bold(import_picocolors4.default.cyan(kitName))} - Available Versions:
|
|
19117
|
+
`);
|
|
19118
|
+
if (releases.length === 0) {
|
|
19119
|
+
console.log(import_picocolors4.default.dim(" No releases found"));
|
|
19120
|
+
return;
|
|
19121
|
+
}
|
|
19122
|
+
for (const release of releases) {
|
|
19123
|
+
const version = import_picocolors4.default.green(release.tag_name);
|
|
19124
|
+
const name2 = release.name || "No title";
|
|
19125
|
+
const publishedAt = formatRelativeTime(release.published_at);
|
|
19126
|
+
const assetCount = release.assets.length;
|
|
19127
|
+
const badges = [];
|
|
19128
|
+
if (release.prerelease)
|
|
19129
|
+
badges.push(import_picocolors4.default.yellow("[prerelease]"));
|
|
19130
|
+
if (release.draft)
|
|
19131
|
+
badges.push(import_picocolors4.default.gray("[draft]"));
|
|
19132
|
+
const badgeStr = badges.length > 0 ? ` ${badges.join(" ")}` : "";
|
|
19133
|
+
const versionPart = version.padEnd(20);
|
|
19134
|
+
const namePart = name2.length > 40 ? `${name2.slice(0, 37)}...` : name2.padEnd(40);
|
|
19135
|
+
const timePart = import_picocolors4.default.dim(publishedAt.padEnd(20));
|
|
19136
|
+
const assetPart = import_picocolors4.default.dim(`(${assetCount} ${assetCount === 1 ? "asset" : "assets"})`);
|
|
19137
|
+
console.log(` ${versionPart} ${namePart} ${timePart} ${assetPart}${badgeStr}`);
|
|
19138
|
+
}
|
|
19139
|
+
console.log(import_picocolors4.default.dim(`
|
|
19140
|
+
Showing ${releases.length} ${releases.length === 1 ? "release" : "releases"}`));
|
|
19141
|
+
}
|
|
19142
|
+
async function versionCommand(options) {
|
|
19143
|
+
const prompts = new PromptsManager;
|
|
19144
|
+
prompts.intro("\uD83D\uDCE6 ClaudeKit - Available Versions");
|
|
19145
|
+
try {
|
|
19146
|
+
const validOptions = VersionCommandOptionsSchema.parse(options);
|
|
19147
|
+
const kitsToFetch = validOptions.kit ? [validOptions.kit] : Object.keys(AVAILABLE_KITS);
|
|
19148
|
+
const github = new GitHubClient;
|
|
19149
|
+
const limit = validOptions.limit || 30;
|
|
19150
|
+
const releasePromises = kitsToFetch.map(async (kitType) => {
|
|
19151
|
+
const kitConfig = AVAILABLE_KITS[kitType];
|
|
19152
|
+
try {
|
|
19153
|
+
const releases = await github.listReleases(kitConfig, limit);
|
|
19154
|
+
const filteredReleases = validOptions.all ? releases : releases.filter((r2) => !r2.draft && !r2.prerelease);
|
|
19155
|
+
return {
|
|
19156
|
+
kitType,
|
|
19157
|
+
kitConfig,
|
|
19158
|
+
releases: filteredReleases,
|
|
19159
|
+
error: null
|
|
19160
|
+
};
|
|
19161
|
+
} catch (error2) {
|
|
19162
|
+
return {
|
|
19163
|
+
kitType,
|
|
19164
|
+
kitConfig,
|
|
19165
|
+
releases: [],
|
|
19166
|
+
error: error2 instanceof Error ? error2.message : "Unknown error"
|
|
19167
|
+
};
|
|
19168
|
+
}
|
|
19169
|
+
});
|
|
19170
|
+
const results = await Promise.all(releasePromises);
|
|
19171
|
+
for (const result of results) {
|
|
19172
|
+
if (result.error) {
|
|
19173
|
+
console.log(`
|
|
19174
|
+
${import_picocolors4.default.bold(import_picocolors4.default.cyan(result.kitConfig.name))} - ${import_picocolors4.default.red("Error")}`);
|
|
19175
|
+
console.log(import_picocolors4.default.dim(` ${result.error}`));
|
|
19176
|
+
} else {
|
|
19177
|
+
displayKitReleases(result.kitConfig.name, result.releases);
|
|
19178
|
+
}
|
|
19179
|
+
}
|
|
19180
|
+
prompts.outro("✨ Done");
|
|
19181
|
+
} catch (error2) {
|
|
19182
|
+
logger.error(error2 instanceof Error ? error2.message : "Unknown error occurred");
|
|
19183
|
+
process.exit(1);
|
|
19184
|
+
}
|
|
19185
|
+
}
|
|
19186
|
+
|
|
19088
19187
|
// src/index.ts
|
|
19089
19188
|
var __dirname2 = fileURLToPath(new URL(".", import.meta.url));
|
|
19090
19189
|
var packageJson = JSON.parse(readFileSync(join5(__dirname2, "../package.json"), "utf-8"));
|
|
@@ -19095,6 +19194,9 @@ cli.command("new", "Bootstrap a new ClaudeKit project").option("--dir <dir>", "T
|
|
|
19095
19194
|
cli.command("update", "Update existing ClaudeKit project").option("--dir <dir>", "Target directory (default: .)").option("--kit <kit>", "Kit to use (engineer, marketing)").option("--version <version>", "Specific version to download (default: latest)").action(async (options) => {
|
|
19096
19195
|
await updateCommand(options);
|
|
19097
19196
|
});
|
|
19197
|
+
cli.command("versions", "List available versions of ClaudeKit repositories").option("--kit <kit>", "Filter by specific kit (engineer, marketing)").option("--limit <limit>", "Number of releases to show (default: 30)").option("--all", "Show all releases including prereleases").action(async (options) => {
|
|
19198
|
+
await versionCommand(options);
|
|
19199
|
+
});
|
|
19098
19200
|
cli.version(packageJson.version);
|
|
19099
19201
|
cli.help();
|
|
19100
19202
|
cli.parse();
|
package/package.json
CHANGED
|
@@ -0,0 +1,135 @@
|
|
|
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
CHANGED
|
@@ -6,6 +6,7 @@ import { fileURLToPath } from "node:url";
|
|
|
6
6
|
import { cac } from "cac";
|
|
7
7
|
import { newCommand } from "./commands/new.js";
|
|
8
8
|
import { updateCommand } from "./commands/update.js";
|
|
9
|
+
import { versionCommand } from "./commands/version.js";
|
|
9
10
|
|
|
10
11
|
const __dirname = fileURLToPath(new URL(".", import.meta.url));
|
|
11
12
|
|
|
@@ -34,6 +35,16 @@ cli
|
|
|
34
35
|
await updateCommand(options);
|
|
35
36
|
});
|
|
36
37
|
|
|
38
|
+
// Versions command
|
|
39
|
+
cli
|
|
40
|
+
.command("versions", "List available versions of ClaudeKit repositories")
|
|
41
|
+
.option("--kit <kit>", "Filter by specific kit (engineer, marketing)")
|
|
42
|
+
.option("--limit <limit>", "Number of releases to show (default: 30)")
|
|
43
|
+
.option("--all", "Show all releases including prereleases")
|
|
44
|
+
.action(async (options) => {
|
|
45
|
+
await versionCommand(options);
|
|
46
|
+
});
|
|
47
|
+
|
|
37
48
|
// Version
|
|
38
49
|
cli.version(packageJson.version);
|
|
39
50
|
|
package/src/types.ts
CHANGED
|
@@ -19,6 +19,13 @@ export const UpdateCommandOptionsSchema = z.object({
|
|
|
19
19
|
});
|
|
20
20
|
export type UpdateCommandOptions = z.infer<typeof UpdateCommandOptionsSchema>;
|
|
21
21
|
|
|
22
|
+
export const VersionCommandOptionsSchema = z.object({
|
|
23
|
+
kit: KitType.optional(),
|
|
24
|
+
limit: z.number().optional(),
|
|
25
|
+
all: z.boolean().optional(),
|
|
26
|
+
});
|
|
27
|
+
export type VersionCommandOptions = z.infer<typeof VersionCommandOptionsSchema>;
|
|
28
|
+
|
|
22
29
|
// Config schemas
|
|
23
30
|
export const ConfigSchema = z.object({
|
|
24
31
|
github: z
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
2
|
+
import type { GitHubRelease } from "../../src/types.js";
|
|
3
|
+
import { AVAILABLE_KITS, VersionCommandOptionsSchema } from "../../src/types.js";
|
|
4
|
+
|
|
5
|
+
describe("Version Command", () => {
|
|
6
|
+
beforeEach(() => {
|
|
7
|
+
// Set environment variable to avoid auth prompts during tests
|
|
8
|
+
process.env.GITHUB_TOKEN = "ghp_test_token_for_testing";
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
describe("VersionCommandOptionsSchema", () => {
|
|
12
|
+
test("should accept valid options with kit filter", () => {
|
|
13
|
+
const options = { kit: "engineer" as const };
|
|
14
|
+
const result = VersionCommandOptionsSchema.parse(options);
|
|
15
|
+
expect(result.kit).toBe("engineer");
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test("should accept valid options with limit", () => {
|
|
19
|
+
const options = { limit: 10 };
|
|
20
|
+
const result = VersionCommandOptionsSchema.parse(options);
|
|
21
|
+
expect(result.limit).toBe(10);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test("should accept valid options with all flag", () => {
|
|
25
|
+
const options = { all: true };
|
|
26
|
+
const result = VersionCommandOptionsSchema.parse(options);
|
|
27
|
+
expect(result.all).toBe(true);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test("should accept all options combined", () => {
|
|
31
|
+
const options = { kit: "marketing" as const, limit: 20, all: true };
|
|
32
|
+
const result = VersionCommandOptionsSchema.parse(options);
|
|
33
|
+
expect(result.kit).toBe("marketing");
|
|
34
|
+
expect(result.limit).toBe(20);
|
|
35
|
+
expect(result.all).toBe(true);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test("should accept empty options", () => {
|
|
39
|
+
const options = {};
|
|
40
|
+
const result = VersionCommandOptionsSchema.parse(options);
|
|
41
|
+
expect(result.kit).toBeUndefined();
|
|
42
|
+
expect(result.limit).toBeUndefined();
|
|
43
|
+
expect(result.all).toBeUndefined();
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test("should reject invalid kit type", () => {
|
|
47
|
+
const options = { kit: "invalid" };
|
|
48
|
+
expect(() => VersionCommandOptionsSchema.parse(options)).toThrow();
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
describe("Kit Configuration", () => {
|
|
53
|
+
test("should have engineer kit configured", () => {
|
|
54
|
+
const engineerKit = AVAILABLE_KITS.engineer;
|
|
55
|
+
expect(engineerKit.name).toBe("ClaudeKit Engineer");
|
|
56
|
+
expect(engineerKit.repo).toBe("claudekit-engineer");
|
|
57
|
+
expect(engineerKit.owner).toBe("mrgoonie");
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test("should have marketing kit configured", () => {
|
|
61
|
+
const marketingKit = AVAILABLE_KITS.marketing;
|
|
62
|
+
expect(marketingKit.name).toBe("ClaudeKit Marketing");
|
|
63
|
+
expect(marketingKit.repo).toBe("claudekit-marketing");
|
|
64
|
+
expect(marketingKit.owner).toBe("mrgoonie");
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
describe("Release Data Handling", () => {
|
|
69
|
+
test("should handle release with all fields", () => {
|
|
70
|
+
const release: GitHubRelease = {
|
|
71
|
+
id: 1,
|
|
72
|
+
tag_name: "v1.0.0",
|
|
73
|
+
name: "Release 1.0.0",
|
|
74
|
+
draft: false,
|
|
75
|
+
prerelease: false,
|
|
76
|
+
assets: [],
|
|
77
|
+
published_at: "2024-01-01T00:00:00Z",
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
expect(release.tag_name).toBe("v1.0.0");
|
|
81
|
+
expect(release.name).toBe("Release 1.0.0");
|
|
82
|
+
expect(release.draft).toBe(false);
|
|
83
|
+
expect(release.prerelease).toBe(false);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
test("should handle release without published_at", () => {
|
|
87
|
+
const release: GitHubRelease = {
|
|
88
|
+
id: 1,
|
|
89
|
+
tag_name: "v1.0.0",
|
|
90
|
+
name: "Release 1.0.0",
|
|
91
|
+
draft: false,
|
|
92
|
+
prerelease: false,
|
|
93
|
+
assets: [],
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
expect(release.published_at).toBeUndefined();
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test("should handle draft release", () => {
|
|
100
|
+
const release: GitHubRelease = {
|
|
101
|
+
id: 1,
|
|
102
|
+
tag_name: "v1.0.0-draft",
|
|
103
|
+
name: "Draft Release",
|
|
104
|
+
draft: true,
|
|
105
|
+
prerelease: false,
|
|
106
|
+
assets: [],
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
expect(release.draft).toBe(true);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
test("should handle prerelease", () => {
|
|
113
|
+
const release: GitHubRelease = {
|
|
114
|
+
id: 1,
|
|
115
|
+
tag_name: "v1.0.0-beta.1",
|
|
116
|
+
name: "Beta Release",
|
|
117
|
+
draft: false,
|
|
118
|
+
prerelease: true,
|
|
119
|
+
assets: [],
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
expect(release.prerelease).toBe(true);
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
describe("Date Formatting", () => {
|
|
127
|
+
test("should format recent dates correctly", () => {
|
|
128
|
+
const now = new Date();
|
|
129
|
+
const yesterday = new Date(now.getTime() - 24 * 60 * 60 * 1000);
|
|
130
|
+
const dateString = yesterday.toISOString();
|
|
131
|
+
|
|
132
|
+
// The actual formatting logic is in the command file
|
|
133
|
+
// We just verify the date string is valid
|
|
134
|
+
expect(dateString).toMatch(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
test("should handle undefined date", () => {
|
|
138
|
+
const dateString = undefined;
|
|
139
|
+
expect(dateString).toBeUndefined();
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
describe("Release Filtering", () => {
|
|
144
|
+
const releases: GitHubRelease[] = [
|
|
145
|
+
{
|
|
146
|
+
id: 1,
|
|
147
|
+
tag_name: "v1.0.0",
|
|
148
|
+
name: "Stable Release",
|
|
149
|
+
draft: false,
|
|
150
|
+
prerelease: false,
|
|
151
|
+
assets: [],
|
|
152
|
+
published_at: "2024-01-01T00:00:00Z",
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
id: 2,
|
|
156
|
+
tag_name: "v1.1.0-beta.1",
|
|
157
|
+
name: "Beta Release",
|
|
158
|
+
draft: false,
|
|
159
|
+
prerelease: true,
|
|
160
|
+
assets: [],
|
|
161
|
+
published_at: "2024-01-02T00:00:00Z",
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
id: 3,
|
|
165
|
+
tag_name: "v1.2.0-draft",
|
|
166
|
+
name: "Draft Release",
|
|
167
|
+
draft: true,
|
|
168
|
+
prerelease: false,
|
|
169
|
+
assets: [],
|
|
170
|
+
published_at: "2024-01-03T00:00:00Z",
|
|
171
|
+
},
|
|
172
|
+
];
|
|
173
|
+
|
|
174
|
+
test("should filter out drafts by default", () => {
|
|
175
|
+
const stable = releases.filter((r) => !r.draft && !r.prerelease);
|
|
176
|
+
expect(stable).toHaveLength(1);
|
|
177
|
+
expect(stable[0].tag_name).toBe("v1.0.0");
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
test("should filter out prereleases by default", () => {
|
|
181
|
+
const stable = releases.filter((r) => !r.draft && !r.prerelease);
|
|
182
|
+
expect(stable.every((r) => !r.prerelease)).toBe(true);
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
test("should include all when --all flag is used", () => {
|
|
186
|
+
const all = releases; // No filtering when --all is true
|
|
187
|
+
expect(all).toHaveLength(3);
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
test("should handle empty release list", () => {
|
|
191
|
+
const empty: GitHubRelease[] = [];
|
|
192
|
+
expect(empty).toHaveLength(0);
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
describe("Command Options Validation", () => {
|
|
197
|
+
test("should validate limit as number", () => {
|
|
198
|
+
const validLimit = { limit: 50 };
|
|
199
|
+
const result = VersionCommandOptionsSchema.parse(validLimit);
|
|
200
|
+
expect(result.limit).toBe(50);
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
test("should validate all as boolean", () => {
|
|
204
|
+
const validAll = { all: false };
|
|
205
|
+
const result = VersionCommandOptionsSchema.parse(validAll);
|
|
206
|
+
expect(result.all).toBe(false);
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
test("should handle optional fields", () => {
|
|
210
|
+
const minimal = {};
|
|
211
|
+
const result = VersionCommandOptionsSchema.parse(minimal);
|
|
212
|
+
expect(result).toBeDefined();
|
|
213
|
+
});
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
describe("Error Scenarios", () => {
|
|
217
|
+
test("should handle invalid option types", () => {
|
|
218
|
+
const invalidLimit = { limit: "not-a-number" };
|
|
219
|
+
expect(() => VersionCommandOptionsSchema.parse(invalidLimit)).toThrow();
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
test("should handle invalid all flag type", () => {
|
|
223
|
+
const invalidAll = { all: "not-a-boolean" };
|
|
224
|
+
expect(() => VersionCommandOptionsSchema.parse(invalidAll)).toThrow();
|
|
225
|
+
});
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
describe("Assets Handling", () => {
|
|
229
|
+
test("should handle release with multiple assets", () => {
|
|
230
|
+
const release: GitHubRelease = {
|
|
231
|
+
id: 1,
|
|
232
|
+
tag_name: "v1.0.0",
|
|
233
|
+
name: "Release",
|
|
234
|
+
draft: false,
|
|
235
|
+
prerelease: false,
|
|
236
|
+
assets: [
|
|
237
|
+
{
|
|
238
|
+
id: 1,
|
|
239
|
+
name: "package.tar.gz",
|
|
240
|
+
browser_download_url: "https://example.com/package.tar.gz",
|
|
241
|
+
size: 1024,
|
|
242
|
+
content_type: "application/gzip",
|
|
243
|
+
},
|
|
244
|
+
{
|
|
245
|
+
id: 2,
|
|
246
|
+
name: "package.zip",
|
|
247
|
+
browser_download_url: "https://example.com/package.zip",
|
|
248
|
+
size: 2048,
|
|
249
|
+
content_type: "application/zip",
|
|
250
|
+
},
|
|
251
|
+
],
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
expect(release.assets).toHaveLength(2);
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
test("should handle release with no assets", () => {
|
|
258
|
+
const release: GitHubRelease = {
|
|
259
|
+
id: 1,
|
|
260
|
+
tag_name: "v1.0.0",
|
|
261
|
+
name: "Release",
|
|
262
|
+
draft: false,
|
|
263
|
+
prerelease: false,
|
|
264
|
+
assets: [],
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
expect(release.assets).toHaveLength(0);
|
|
268
|
+
});
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
describe("Integration Scenarios", () => {
|
|
272
|
+
test("should handle both kits in parallel", () => {
|
|
273
|
+
const kits = Object.keys(AVAILABLE_KITS);
|
|
274
|
+
expect(kits).toContain("engineer");
|
|
275
|
+
expect(kits).toContain("marketing");
|
|
276
|
+
expect(kits).toHaveLength(2);
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
test("should support filtering by engineer kit", () => {
|
|
280
|
+
const options = { kit: "engineer" as const };
|
|
281
|
+
const result = VersionCommandOptionsSchema.parse(options);
|
|
282
|
+
expect(result.kit).toBe("engineer");
|
|
283
|
+
|
|
284
|
+
const kitConfig = AVAILABLE_KITS[result.kit];
|
|
285
|
+
expect(kitConfig.repo).toBe("claudekit-engineer");
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
test("should support filtering by marketing kit", () => {
|
|
289
|
+
const options = { kit: "marketing" as const };
|
|
290
|
+
const result = VersionCommandOptionsSchema.parse(options);
|
|
291
|
+
expect(result.kit).toBe("marketing");
|
|
292
|
+
|
|
293
|
+
const kitConfig = AVAILABLE_KITS[result.kit];
|
|
294
|
+
expect(kitConfig.repo).toBe("claudekit-marketing");
|
|
295
|
+
});
|
|
296
|
+
});
|
|
297
|
+
});
|