Skip to content

Commit feb5d04

Browse files
doble196claude
andcommitted
feat(cli): add skills commands v0.5.0
- Add githat skills search/list/install/installed/init commands - Add API client for skills endpoints - Add unzipper dependency for package extraction - Bump version to 0.5.0 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent f46a69a commit feb5d04

File tree

10 files changed

+843
-9
lines changed

10 files changed

+843
-9
lines changed

package.json

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
{
22
"name": "create-githat-app",
3-
"version": "0.4.1",
4-
"description": "Scaffold enterprise-grade apps with GitHat identityfor humans, AI agents, and MCP servers",
3+
"version": "0.5.0",
4+
"description": "GitHat CLIscaffold apps and manage the skills marketplace",
55
"type": "module",
66
"bin": {
7-
"create-githat-app": "./bin/index.js"
7+
"create-githat-app": "./bin/index.js",
8+
"githat": "./bin/index.js"
89
},
910
"files": ["bin", "dist", "templates"],
1011
"scripts": {
@@ -20,12 +21,14 @@
2021
"fs-extra": "^11.2.0",
2122
"gradient-string": "^3.0.0",
2223
"handlebars": "^4.7.8",
23-
"ora": "^8.1.1"
24+
"ora": "^8.1.1",
25+
"unzipper": "^0.12.3"
2426
},
2527
"devDependencies": {
2628
"@types/figlet": "^1.7.0",
2729
"@types/fs-extra": "^11.0.4",
2830
"@types/gradient-string": "^1.1.6",
31+
"@types/unzipper": "^0.10.10",
2932
"tsup": "^8.4.0",
3033
"typescript": "^5.9.0"
3134
},

src/cli.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,19 @@ import { displayBanner } from './utils/ascii.js';
55
import { runPrompts, answersToContext } from './prompts/index.js';
66
import { scaffold } from './scaffold/index.js';
77
import { VERSION } from './constants.js';
8+
import { skillsCommand } from './commands/skills/index.js';
89

910
const program = new Command();
1011

1112
program
12-
.name('create-githat-app')
13-
.description('Scaffold enterprise-grade apps with GitHat identity')
14-
.version(VERSION)
15-
.argument('[project-name]', 'Name of the project directory')
13+
.name('githat')
14+
.description('GitHat CLI - Scaffold apps and manage skills')
15+
.version(VERSION);
16+
17+
// Default create command (backwards compatible with create-githat-app)
18+
program
19+
.command('create [project-name]', { isDefault: true })
20+
.description('Scaffold a new GitHat app')
1621
.option('--key <key>', 'GitHat publishable key (pk_live_...)')
1722
.option('--ts', 'Use TypeScript (default)')
1823
.option('--js', 'Use JavaScript')
@@ -40,4 +45,7 @@ program
4045
}
4146
});
4247

48+
// Skills marketplace commands
49+
program.addCommand(skillsCommand);
50+
4351
program.parse();

src/commands/skills/api.ts

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
/**
2+
* Skills API client for CLI
3+
*/
4+
5+
import { DEFAULT_API_URL } from '../../constants.js';
6+
7+
export interface Skill {
8+
id: string;
9+
slug: string;
10+
name: string;
11+
description: string;
12+
type: 'template' | 'integration' | 'ui' | 'ai' | 'workflow';
13+
authorName: string;
14+
latestVersion: string;
15+
downloads: number;
16+
stars: number;
17+
repository?: string;
18+
homepage?: string;
19+
license: string;
20+
keywords: string[];
21+
createdAt: string;
22+
updatedAt: string;
23+
}
24+
25+
export interface SkillVersion {
26+
id: string;
27+
version: string;
28+
changelog: string;
29+
fileSize: number;
30+
createdAt: string;
31+
}
32+
33+
export interface Installation {
34+
id: string;
35+
skillId: string;
36+
version: string;
37+
installedAt: string;
38+
skill?: {
39+
slug: string;
40+
name: string;
41+
description: string;
42+
type: string;
43+
latestVersion: string;
44+
authorName: string;
45+
};
46+
}
47+
48+
async function fetchApi<T>(
49+
endpoint: string,
50+
options: RequestInit = {}
51+
): Promise<T> {
52+
const url = `${DEFAULT_API_URL}${endpoint}`;
53+
const response = await fetch(url, {
54+
...options,
55+
headers: {
56+
'Content-Type': 'application/json',
57+
...options.headers,
58+
},
59+
});
60+
61+
if (!response.ok) {
62+
const error = await response.json().catch(() => ({ error: response.statusText }));
63+
throw new Error(error.error || `API error: ${response.status}`);
64+
}
65+
66+
return response.json();
67+
}
68+
69+
export async function searchSkills(query: string, type?: string): Promise<{ skills: Skill[] }> {
70+
const params = new URLSearchParams();
71+
if (type) params.set('type', type);
72+
const url = `/skills?${params.toString()}`;
73+
const result = await fetchApi<{ skills: Skill[] }>(url);
74+
75+
// Client-side filter by query (API doesn't support search yet)
76+
const q = query.toLowerCase();
77+
return {
78+
skills: result.skills.filter(
79+
(s) =>
80+
s.name.toLowerCase().includes(q) ||
81+
s.description.toLowerCase().includes(q) ||
82+
s.keywords.some((k) => k.toLowerCase().includes(q))
83+
),
84+
};
85+
}
86+
87+
export async function listSkills(options: {
88+
type?: string;
89+
limit?: number;
90+
cursor?: string;
91+
}): Promise<{ skills: Skill[]; nextCursor: string | null }> {
92+
const params = new URLSearchParams();
93+
if (options.type) params.set('type', options.type);
94+
if (options.limit) params.set('limit', options.limit.toString());
95+
if (options.cursor) params.set('cursor', options.cursor);
96+
97+
return fetchApi(`/skills?${params.toString()}`);
98+
}
99+
100+
export async function getSkill(slug: string): Promise<Skill> {
101+
return fetchApi(`/skills/${slug}`);
102+
}
103+
104+
export async function getSkillVersions(
105+
slug: string
106+
): Promise<{ skill: { id: string; slug: string; name: string }; versions: SkillVersion[] }> {
107+
return fetchApi(`/skills/${slug}/versions`);
108+
}
109+
110+
export async function getDownloadUrl(
111+
slug: string,
112+
version?: string
113+
): Promise<{
114+
skill: { id: string; slug: string; name: string };
115+
version: { version: string; changelog: string; fileSize: number };
116+
downloadUrl: string;
117+
expiresIn: number;
118+
}> {
119+
const params = version ? `?version=${version}` : '';
120+
return fetchApi(`/skills/${slug}/download${params}`);
121+
}
122+
123+
export async function listInstalledSkills(
124+
token: string
125+
): Promise<{ installations: Installation[] }> {
126+
return fetchApi('/skills/installed', {
127+
headers: { Authorization: `Bearer ${token}` },
128+
});
129+
}
130+
131+
export async function recordInstallation(
132+
token: string,
133+
slug: string,
134+
version?: string
135+
): Promise<{ installation: { id: string; version: string } }> {
136+
return fetchApi(`/skills/${slug}/install`, {
137+
method: 'POST',
138+
headers: { Authorization: `Bearer ${token}` },
139+
body: JSON.stringify({ version }),
140+
});
141+
}

src/commands/skills/index.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { Command } from 'commander';
2+
import chalk from 'chalk';
3+
import { searchCommand } from './search.js';
4+
import { listCommand } from './list.js';
5+
import { installCommand } from './install.js';
6+
import { installedCommand } from './installed.js';
7+
import { initCommand } from './init.js';
8+
9+
export const skillsCommand = new Command('skills')
10+
.description('Manage GitHat skills marketplace')
11+
.addCommand(searchCommand)
12+
.addCommand(listCommand)
13+
.addCommand(installCommand)
14+
.addCommand(installedCommand)
15+
.addCommand(initCommand);
16+
17+
// Default action shows help
18+
skillsCommand.action(() => {
19+
console.log(chalk.cyan('\n📦 GitHat Skills Marketplace\n'));
20+
console.log('Commands:');
21+
console.log(' search <query> Search skills by keyword');
22+
console.log(' list List skills (filterable by type)');
23+
console.log(' install <slug> Install a skill to your project');
24+
console.log(' installed List installed skills');
25+
console.log(' init <name> Initialize a new skill package');
26+
console.log('\nExamples:');
27+
console.log(' githat skills search stripe');
28+
console.log(' githat skills list --type=integration');
29+
console.log(' githat skills install stripe-billing');
30+
console.log(' githat skills init my-skill --type=integration');
31+
console.log('');
32+
});

0 commit comments

Comments
 (0)