skillett 0.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/dist/commands/add.d.ts +1 -0
- package/dist/commands/add.js +39 -0
- package/dist/commands/connect.d.ts +1 -0
- package/dist/commands/connect.js +81 -0
- package/dist/commands/disconnect.d.ts +1 -0
- package/dist/commands/disconnect.js +43 -0
- package/dist/commands/list.d.ts +1 -0
- package/dist/commands/list.js +43 -0
- package/dist/commands/login.d.ts +3 -0
- package/dist/commands/login.js +172 -0
- package/dist/commands/pull.d.ts +3 -0
- package/dist/commands/pull.js +46 -0
- package/dist/commands/remove.d.ts +1 -0
- package/dist/commands/remove.js +12 -0
- package/dist/commands/run.d.ts +1 -0
- package/dist/commands/run.js +85 -0
- package/dist/commands/seed.d.ts +3 -0
- package/dist/commands/seed.js +253 -0
- package/dist/commands/setup.d.ts +3 -0
- package/dist/commands/setup.js +149 -0
- package/dist/commands/skills.d.ts +1 -0
- package/dist/commands/skills.js +50 -0
- package/dist/commands/status.d.ts +1 -0
- package/dist/commands/status.js +67 -0
- package/dist/commands/whoami.d.ts +1 -0
- package/dist/commands/whoami.js +27 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +112 -0
- package/dist/lib/api.d.ts +25 -0
- package/dist/lib/api.js +50 -0
- package/dist/lib/config.d.ts +20 -0
- package/dist/lib/config.js +115 -0
- package/dist/lib/device-auth.d.ts +21 -0
- package/dist/lib/device-auth.js +38 -0
- package/dist/lib/paths.d.ts +4 -0
- package/dist/lib/paths.js +17 -0
- package/package.json +33 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export declare function getDashboardUrl(): string;
|
|
2
|
+
export declare function skillettFetch(path: string, apiKey: string, options?: RequestInit): Promise<Response>;
|
|
3
|
+
export declare function validateKey(apiKey: string): Promise<boolean>;
|
|
4
|
+
export interface SkillSummary {
|
|
5
|
+
name: string;
|
|
6
|
+
display_name: string;
|
|
7
|
+
description: string | null;
|
|
8
|
+
category: string | null;
|
|
9
|
+
integration: string | null;
|
|
10
|
+
}
|
|
11
|
+
export declare function listSkills(apiKey: string): Promise<SkillSummary[]>;
|
|
12
|
+
export interface SkillDownload {
|
|
13
|
+
slug: string;
|
|
14
|
+
files: {
|
|
15
|
+
file_path: string;
|
|
16
|
+
content: string;
|
|
17
|
+
}[];
|
|
18
|
+
}
|
|
19
|
+
export declare function downloadSkill(apiKey: string, name: string): Promise<SkillDownload>;
|
|
20
|
+
export interface WhoamiResponse {
|
|
21
|
+
id: string;
|
|
22
|
+
email: string | null;
|
|
23
|
+
display_name: string | null;
|
|
24
|
+
}
|
|
25
|
+
export declare function whoami(apiKey: string): Promise<WhoamiResponse>;
|
package/dist/lib/api.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
const DEFAULT_BASE = "https://api.skillett.dev";
|
|
2
|
+
const DEFAULT_DASHBOARD = "https://app.skillett.dev";
|
|
3
|
+
function getBaseUrl() {
|
|
4
|
+
return process.env.SKILLETT_API_URL || DEFAULT_BASE;
|
|
5
|
+
}
|
|
6
|
+
export function getDashboardUrl() {
|
|
7
|
+
// If using local API, point to local dashboard
|
|
8
|
+
const apiUrl = getBaseUrl();
|
|
9
|
+
if (apiUrl.includes("localhost") || apiUrl.includes("127.0.0.1")) {
|
|
10
|
+
return "http://localhost:5173";
|
|
11
|
+
}
|
|
12
|
+
return DEFAULT_DASHBOARD;
|
|
13
|
+
}
|
|
14
|
+
export async function skillettFetch(path, apiKey, options = {}) {
|
|
15
|
+
return fetch(`${getBaseUrl()}${path}`, {
|
|
16
|
+
...options,
|
|
17
|
+
headers: {
|
|
18
|
+
"Content-Type": "application/json",
|
|
19
|
+
Authorization: `Bearer ${apiKey}`,
|
|
20
|
+
...options.headers,
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
export async function validateKey(apiKey) {
|
|
25
|
+
try {
|
|
26
|
+
const res = await skillettFetch("/v1/skills", apiKey);
|
|
27
|
+
return res.ok;
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
export async function listSkills(apiKey) {
|
|
34
|
+
const res = await skillettFetch("/v1", apiKey);
|
|
35
|
+
if (!res.ok)
|
|
36
|
+
throw new Error(`Failed to list skills: ${res.status}`);
|
|
37
|
+
return res.json();
|
|
38
|
+
}
|
|
39
|
+
export async function downloadSkill(apiKey, name) {
|
|
40
|
+
const res = await skillettFetch(`/v1/${name}/download`, apiKey);
|
|
41
|
+
if (!res.ok)
|
|
42
|
+
throw new Error(`Failed to download skill: ${res.status}`);
|
|
43
|
+
return res.json();
|
|
44
|
+
}
|
|
45
|
+
export async function whoami(apiKey) {
|
|
46
|
+
const res = await skillettFetch("/auth/me", apiKey);
|
|
47
|
+
if (!res.ok)
|
|
48
|
+
throw new Error(`Failed to get user info: ${res.status}`);
|
|
49
|
+
return res.json();
|
|
50
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Load all SKILLETT_* vars from the project's .env into process.env.
|
|
3
|
+
* Call this early so the API client picks up SKILLETT_API_URL.
|
|
4
|
+
*/
|
|
5
|
+
export declare function loadEnv(): void;
|
|
6
|
+
export declare function loadApiKey(): string | null;
|
|
7
|
+
export declare function saveApiKey(key: string): void;
|
|
8
|
+
export declare function checkGitignore(): boolean;
|
|
9
|
+
export type Platform = "claude" | "cursor" | "windsurf" | "generic";
|
|
10
|
+
export declare function detectPlatform(): {
|
|
11
|
+
platform: Platform;
|
|
12
|
+
label: string;
|
|
13
|
+
} | null;
|
|
14
|
+
export declare function getPlatformLabel(platform: Platform): string;
|
|
15
|
+
export declare function loadPlatform(): Platform | null;
|
|
16
|
+
export declare function savePlatform(platform: Platform): void;
|
|
17
|
+
/** Get the directory where skill docs should be written for this platform */
|
|
18
|
+
export declare function getSkillDocsPath(platform: Platform, slug: string): string;
|
|
19
|
+
/** Get the path for the core skill pointer */
|
|
20
|
+
export declare function getCoreSkillPath(platform: Platform): string;
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync, existsSync } from "node:fs";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
const ENV_FILE = resolve(process.cwd(), ".env");
|
|
4
|
+
/**
|
|
5
|
+
* Load all SKILLETT_* vars from the project's .env into process.env.
|
|
6
|
+
* Call this early so the API client picks up SKILLETT_API_URL.
|
|
7
|
+
*/
|
|
8
|
+
export function loadEnv() {
|
|
9
|
+
if (!existsSync(ENV_FILE))
|
|
10
|
+
return;
|
|
11
|
+
const content = readFileSync(ENV_FILE, "utf8");
|
|
12
|
+
for (const line of content.split("\n")) {
|
|
13
|
+
const match = line.match(/^(SKILLETT_\w+)=(.+)$/);
|
|
14
|
+
if (match && !process.env[match[1]]) {
|
|
15
|
+
process.env[match[1]] = match[2].trim();
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
export function loadApiKey() {
|
|
20
|
+
if (!existsSync(ENV_FILE))
|
|
21
|
+
return null;
|
|
22
|
+
const content = readFileSync(ENV_FILE, "utf8");
|
|
23
|
+
const match = content.match(/^SKILLETT_API_KEY=(.+)$/m);
|
|
24
|
+
return match ? match[1].trim() : null;
|
|
25
|
+
}
|
|
26
|
+
export function saveApiKey(key) {
|
|
27
|
+
if (existsSync(ENV_FILE)) {
|
|
28
|
+
let content = readFileSync(ENV_FILE, "utf8");
|
|
29
|
+
if (content.match(/^SKILLETT_API_KEY=/m)) {
|
|
30
|
+
content = content.replace(/^SKILLETT_API_KEY=.*/m, `SKILLETT_API_KEY=${key}`);
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
content = content.trimEnd() + `\nSKILLETT_API_KEY=${key}\n`;
|
|
34
|
+
}
|
|
35
|
+
writeFileSync(ENV_FILE, content);
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
writeFileSync(ENV_FILE, `SKILLETT_API_KEY=${key}\n`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
export function checkGitignore() {
|
|
42
|
+
const gitignorePath = resolve(process.cwd(), ".gitignore");
|
|
43
|
+
if (!existsSync(gitignorePath))
|
|
44
|
+
return false;
|
|
45
|
+
const content = readFileSync(gitignorePath, "utf8");
|
|
46
|
+
return content.split("\n").some((line) => line.trim() === ".env" || line.trim() === ".env*");
|
|
47
|
+
}
|
|
48
|
+
const PLATFORM_MARKERS = [
|
|
49
|
+
{ platform: "claude", dir: ".claude", label: "Claude Code" },
|
|
50
|
+
{ platform: "cursor", dir: ".cursor", label: "Cursor" },
|
|
51
|
+
{ platform: "windsurf", dir: ".windsurf", label: "Windsurf" },
|
|
52
|
+
];
|
|
53
|
+
export function detectPlatform() {
|
|
54
|
+
const cwd = process.cwd();
|
|
55
|
+
for (const marker of PLATFORM_MARKERS) {
|
|
56
|
+
if (existsSync(resolve(cwd, marker.dir))) {
|
|
57
|
+
return { platform: marker.platform, label: marker.label };
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
export function getPlatformLabel(platform) {
|
|
63
|
+
const found = PLATFORM_MARKERS.find((m) => m.platform === platform);
|
|
64
|
+
return found?.label || "Generic";
|
|
65
|
+
}
|
|
66
|
+
export function loadPlatform() {
|
|
67
|
+
if (!existsSync(ENV_FILE))
|
|
68
|
+
return null;
|
|
69
|
+
const content = readFileSync(ENV_FILE, "utf8");
|
|
70
|
+
const match = content.match(/^SKILLETT_PLATFORM=(.+)$/m);
|
|
71
|
+
return match ? match[1].trim() : null;
|
|
72
|
+
}
|
|
73
|
+
export function savePlatform(platform) {
|
|
74
|
+
if (existsSync(ENV_FILE)) {
|
|
75
|
+
let content = readFileSync(ENV_FILE, "utf8");
|
|
76
|
+
if (content.match(/^SKILLETT_PLATFORM=/m)) {
|
|
77
|
+
content = content.replace(/^SKILLETT_PLATFORM=.*/m, `SKILLETT_PLATFORM=${platform}`);
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
content = content.trimEnd() + `\nSKILLETT_PLATFORM=${platform}\n`;
|
|
81
|
+
}
|
|
82
|
+
writeFileSync(ENV_FILE, content);
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
writeFileSync(ENV_FILE, `SKILLETT_PLATFORM=${platform}\n`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
/** Get the directory where skill docs should be written for this platform */
|
|
89
|
+
export function getSkillDocsPath(platform, slug) {
|
|
90
|
+
const cwd = process.cwd();
|
|
91
|
+
switch (platform) {
|
|
92
|
+
case "claude":
|
|
93
|
+
return resolve(cwd, ".claude", "skills", `skillett-${slug}`);
|
|
94
|
+
case "cursor":
|
|
95
|
+
return resolve(cwd, ".cursor", "rules", `skillett-${slug}`);
|
|
96
|
+
case "windsurf":
|
|
97
|
+
return resolve(cwd, ".windsurf", "rules", `skillett-${slug}`);
|
|
98
|
+
default:
|
|
99
|
+
return resolve(cwd, ".skillett", "skills", slug);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
/** Get the path for the core skill pointer */
|
|
103
|
+
export function getCoreSkillPath(platform) {
|
|
104
|
+
const cwd = process.cwd();
|
|
105
|
+
switch (platform) {
|
|
106
|
+
case "claude":
|
|
107
|
+
return resolve(cwd, ".claude", "skills", "skillett");
|
|
108
|
+
case "cursor":
|
|
109
|
+
return resolve(cwd, ".cursor", "rules", "skillett");
|
|
110
|
+
case "windsurf":
|
|
111
|
+
return resolve(cwd, ".windsurf", "rules", "skillett");
|
|
112
|
+
default:
|
|
113
|
+
return resolve(cwd, ".skillett", "skills", "skillett");
|
|
114
|
+
}
|
|
115
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export interface DeviceCodeResponse {
|
|
2
|
+
device_code: string;
|
|
3
|
+
user_code: string;
|
|
4
|
+
verification_uri: string;
|
|
5
|
+
expires_in: number;
|
|
6
|
+
interval: number;
|
|
7
|
+
}
|
|
8
|
+
export interface DeviceTokenResponse {
|
|
9
|
+
status: "pending" | "complete" | "expired";
|
|
10
|
+
error?: string;
|
|
11
|
+
user?: {
|
|
12
|
+
email: string | null;
|
|
13
|
+
display_name: string | null;
|
|
14
|
+
};
|
|
15
|
+
api_keys_url?: string;
|
|
16
|
+
}
|
|
17
|
+
export declare function requestDeviceCode(): Promise<DeviceCodeResponse>;
|
|
18
|
+
export declare function pollForToken(deviceCode: string, options?: {
|
|
19
|
+
interval?: number;
|
|
20
|
+
timeout?: number;
|
|
21
|
+
}): Promise<DeviceTokenResponse>;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
const DEFAULT_INTERVAL = 5000; // 5 seconds
|
|
2
|
+
const DEFAULT_TIMEOUT = 900000; // 15 minutes
|
|
3
|
+
function getBaseUrl() {
|
|
4
|
+
return process.env.SKILLETT_API_URL || "https://api.skillett.dev";
|
|
5
|
+
}
|
|
6
|
+
export async function requestDeviceCode() {
|
|
7
|
+
const res = await fetch(`${getBaseUrl()}/auth/device/code`, {
|
|
8
|
+
method: "POST",
|
|
9
|
+
headers: { "Content-Type": "application/json" },
|
|
10
|
+
});
|
|
11
|
+
if (!res.ok) {
|
|
12
|
+
throw new Error(`Failed to request device code: ${res.status}`);
|
|
13
|
+
}
|
|
14
|
+
return res.json();
|
|
15
|
+
}
|
|
16
|
+
export async function pollForToken(deviceCode, options = {}) {
|
|
17
|
+
const interval = options.interval || DEFAULT_INTERVAL;
|
|
18
|
+
const timeout = options.timeout || DEFAULT_TIMEOUT;
|
|
19
|
+
const deadline = Date.now() + timeout;
|
|
20
|
+
while (Date.now() < deadline) {
|
|
21
|
+
const res = await fetch(`${getBaseUrl()}/auth/device/token`, {
|
|
22
|
+
method: "POST",
|
|
23
|
+
headers: { "Content-Type": "application/json" },
|
|
24
|
+
body: JSON.stringify({ device_code: deviceCode }),
|
|
25
|
+
});
|
|
26
|
+
if (!res.ok) {
|
|
27
|
+
throw new Error(`Device auth polling failed: ${res.status}`);
|
|
28
|
+
}
|
|
29
|
+
const data = await res.json();
|
|
30
|
+
if (data.status === "complete")
|
|
31
|
+
return data;
|
|
32
|
+
if (data.status === "expired")
|
|
33
|
+
throw new Error("Device code expired. Run `skillett login` again.");
|
|
34
|
+
// Wait before next poll
|
|
35
|
+
await new Promise((r) => setTimeout(r, interval));
|
|
36
|
+
}
|
|
37
|
+
throw new Error("Login timed out. Run `skillett login` again.");
|
|
38
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { resolve } from "node:path";
|
|
2
|
+
import { existsSync, mkdirSync } from "node:fs";
|
|
3
|
+
export function getSkillsDir() {
|
|
4
|
+
return resolve(process.cwd(), ".claude", "skills");
|
|
5
|
+
}
|
|
6
|
+
export function getSkillPath(name) {
|
|
7
|
+
return resolve(getSkillsDir(), name);
|
|
8
|
+
}
|
|
9
|
+
export function skillExists(name) {
|
|
10
|
+
return existsSync(getSkillPath(name));
|
|
11
|
+
}
|
|
12
|
+
export function ensureSkillsDir() {
|
|
13
|
+
const dir = getSkillsDir();
|
|
14
|
+
if (!existsSync(dir)) {
|
|
15
|
+
mkdirSync(dir, { recursive: true });
|
|
16
|
+
}
|
|
17
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "skillett",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Skillett CLI — Agent Skills Platform",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"skillett": "./dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "tsc",
|
|
11
|
+
"dev": "tsx src/index.ts"
|
|
12
|
+
},
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"chalk": "^5.4.1",
|
|
15
|
+
"commander": "^13.1.0",
|
|
16
|
+
"dotenv": "^16.5.0",
|
|
17
|
+
"open": "^10.2.0",
|
|
18
|
+
"ora": "^8.2.0",
|
|
19
|
+
"pg": "^8.20.0"
|
|
20
|
+
},
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"@types/node": "^24.12.0",
|
|
23
|
+
"@types/pg": "^8.20.0",
|
|
24
|
+
"tsx": "^4.19.4",
|
|
25
|
+
"typescript": "~5.9.3"
|
|
26
|
+
},
|
|
27
|
+
"engines": {
|
|
28
|
+
"node": ">=18"
|
|
29
|
+
},
|
|
30
|
+
"files": [
|
|
31
|
+
"dist"
|
|
32
|
+
]
|
|
33
|
+
}
|