@8pt-dev/cli 0.0.0-stage → 0.3.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 8pt
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,52 @@
1
- # Temporary Holding Version
1
+ # 8pt CLI
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ 8pt makes any AI model ship professional-grade UI: curated components, full-page templates, themes, icons and Skills, delivered to Claude Code, Cursor, Codex and Trae through one MCP server and CLI.
4
+
5
+ Search and install 8pt components, templates, themes, icons and Skills from your terminal or coding agent. Requires Node.js 18+.
6
+
7
+ ```bash
8
+ npm i -g @8pt-dev/cli
9
+ 8pt login # opens 8pt.dev to create an API Key, saves it locally
10
+ 8pt init # write 8pt design rules into AGENTS.md / CLAUDE.md / Cursor / Copilot
11
+ 8pt install-skill # teach Claude Code / Codex to use 8pt
12
+ 8pt design "landing page for a budgeting app"
13
+ 8pt search "pricing toggle"
14
+ 8pt add <slug>
15
+ ```
16
+
17
+ No global install: `npx @8pt-dev/cli <command>`. In CI or scripts skip login: `--api-key <key>` or `API_KEY_8PT=<key>`.
18
+
19
+ With `8pt init`, coding agents build every UI strictly from 8pt templates, components, one 8pt theme and 8pt icons.
20
+
21
+ Commands: init, design, rules, login, logout, status, search, add, prompt, templates, template, skills, skill, install-skill, theme, icons, mcp. Run `8pt --help`.
22
+
23
+ ## MCP
24
+
25
+ Endpoint `https://8pt.dev/api/mcp` (Streamable HTTP), header `Authorization: Bearer <your 8pt API Key>`.
26
+
27
+ ```bash
28
+ claude mcp add --transport http 8pt https://8pt.dev/api/mcp \
29
+ --header "Authorization: Bearer <YOUR_8PT_API_KEY>"
30
+ ```
31
+
32
+ Cursor / Trae / other clients:
33
+
34
+ ```json
35
+ {
36
+ "mcpServers": {
37
+ "8pt": {
38
+ "url": "https://8pt.dev/api/mcp",
39
+ "headers": { "Authorization": "Bearer <YOUR_8PT_API_KEY>" }
40
+ }
41
+ }
42
+ }
43
+ ```
44
+
45
+ `8pt mcp [claude|codex|cursor]` prints the config for your client.
46
+
47
+ ## Links
48
+
49
+ - Website: https://8pt.dev
50
+ - Docs: https://8pt.dev/docs/
51
+ - GitHub: https://github.com/8pt-dev/8pt
52
+ - Support: support@8pt.dev
package/bin/8pt.js ADDED
@@ -0,0 +1,302 @@
1
+ #!/usr/bin/env node
2
+ // 8pt CLI — search and install 8pt components, templates, themes, icons and Skills.
3
+ // Talks to the 8pt MCP endpoint (https://8pt.dev/api/mcp) with your API Key.
4
+ import fs from "node:fs";
5
+ import os from "node:os";
6
+ import path from "node:path";
7
+ import readline from "node:readline";
8
+ import { spawn } from "node:child_process";
9
+
10
+ const VERSION = "0.3.1";
11
+ const BASE = (process.env.EIGHTPT_BASE_URL || "https://8pt.dev").replace(/\/+$/, "");
12
+ const CONF_DIR = process.env.EIGHTPT_CONFIG_DIR || path.join(process.env.XDG_CONFIG_HOME || path.join(os.homedir(), ".config"), "8pt");
13
+ const CONF = path.join(CONF_DIR, "config.json");
14
+ const KEYS_URL = BASE + "/settings/api-keys/";
15
+
16
+ process.stdout.on("error", (e) => { if (e.code === "EPIPE") process.exit(0); throw e; });
17
+ const tty = process.stdout.isTTY && !process.env.NO_COLOR;
18
+ const c = (n) => (s) => (tty ? `\x1b[${n}m${s}\x1b[0m` : String(s));
19
+ const dim = c(2), bold = c(1), blue = c(34), green = c(32), red = c(31);
20
+
21
+ const out = (s = "") => process.stdout.write(s + "\n");
22
+ const die = (msg, code = 1) => { process.stderr.write(red("✕ ") + msg + "\n"); process.exit(code); };
23
+
24
+ /* ---------- args ---------- */
25
+ const argv = process.argv.slice(2);
26
+ const flags = {}, pos = [];
27
+ for (let i = 0; i < argv.length; i++) {
28
+ const a = argv[i];
29
+ if (a === "--") { pos.push(...argv.slice(i + 1)); break; }
30
+ if (a.startsWith("--")) {
31
+ const [k, v] = a.slice(2).split(/=(.*)/s);
32
+ if (v !== undefined) flags[k] = v;
33
+ else if (argv[i + 1] && !argv[i + 1].startsWith("-") && ["api-key", "out", "dir", "category", "limit", "target", "client", "only"].includes(k)) flags[k] = argv[++i];
34
+ else flags[k] = true;
35
+ } else if (a === "-h") flags.help = true;
36
+ else if (a === "-v") flags.version = true;
37
+ else if (a === "-y") flags.yes = true;
38
+ else pos.push(a);
39
+ }
40
+ const json = !!flags.json;
41
+
42
+ /* ---------- config ---------- */
43
+ const readConf = () => { try { return JSON.parse(fs.readFileSync(CONF, "utf8")); } catch { return {}; } };
44
+ const writeConf = (o) => { fs.mkdirSync(CONF_DIR, { recursive: true, mode: 0o700 }); fs.writeFileSync(CONF, JSON.stringify(o, null, 2) + "\n", { mode: 0o600 }); };
45
+ const apiKey = () => flags["api-key"] || process.env.API_KEY_8PT || process.env.EIGHTPT_API_KEY || readConf().apiKey || "";
46
+
47
+ /* ---------- MCP ---------- */
48
+ let rpcId = 0;
49
+ async function rpc(method, params, key = apiKey()) {
50
+ if (!key) die(`还没登录。运行 ${bold("8pt login")},或用 --api-key / 环境变量 API_KEY_8PT 传入 Key。`);
51
+ let res;
52
+ try {
53
+ res = await fetch(BASE + "/api/mcp", {
54
+ method: "POST",
55
+ headers: { "content-type": "application/json", accept: "application/json", authorization: "Bearer " + key, "user-agent": "8pt-cli/" + VERSION },
56
+ body: JSON.stringify({ jsonrpc: "2.0", id: ++rpcId, method, params }),
57
+ });
58
+ } catch (e) { die("连不上 " + BASE + ":" + e.message); }
59
+ const body = await res.json().catch(() => null);
60
+ if (res.status === 401) die("API Key 无效或已撤销。到 " + KEYS_URL + " 重新创建,再运行 8pt login。");
61
+ if (res.status === 429) die("请求太频繁,稍后再试。");
62
+ if (!body) die("服务器返回 " + res.status);
63
+ if (body.error) die(body.error.message || "请求失败");
64
+ return body.result;
65
+ }
66
+ async function tool(name, args = {}) {
67
+ const r = await rpc("tools/call", { name, arguments: args });
68
+ const text = (r.content || []).map((x) => x.text || "").join("\n");
69
+ if (r.isError) die(text || "调用失败");
70
+ return text;
71
+ }
72
+ const parse = (t) => { try { return JSON.parse(t); } catch { return null; } };
73
+
74
+ /* ---------- helpers ---------- */
75
+ const ask = (q) => new Promise((r) => { const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); rl.question(q, (a) => { rl.close(); r(a.trim()); }); });
76
+ function openUrl(u) {
77
+ const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "cmd" : "xdg-open";
78
+ const args = process.platform === "win32" ? ["/c", "start", "", u] : [u];
79
+ try { spawn(cmd, args, { stdio: "ignore", detached: true }).unref(); return true; } catch { return false; }
80
+ }
81
+ function write(file, content) {
82
+ if (fs.existsSync(file) && !flags.force && !flags.yes) { out(dim(" 跳过(已存在,用 --force 覆盖)") + " " + file); return false; }
83
+ fs.mkdirSync(path.dirname(file), { recursive: true });
84
+ fs.writeFileSync(file, content);
85
+ out(green(" ✓ ") + path.relative(process.cwd(), file));
86
+ return true;
87
+ }
88
+ const safe = (s) => String(s).toLowerCase().replace(/[^a-z0-9-]/g, "");
89
+ function skillDir(slug) {
90
+ const t = flags.target || (fs.existsSync(".codex") && !fs.existsSync(".claude") ? "codex" : "claude");
91
+ const base = flags.dir || { claude: ".claude/skills", codex: ".codex/skills", cursor: ".cursor/skills", agents: ".agents/skills", plain: "skills" }[t] || t;
92
+ return path.join(base, slug);
93
+ }
94
+
95
+ const RULES = `## 8pt 设计规范(本项目的界面必须遵守)
96
+
97
+ 本项目接入了 8pt(https://8pt.dev)。所有网页、落地页、应用界面和 UI 组件都严格按 8pt 的审美来做:
98
+
99
+ 1. 动手做任何界面前,先调用 8pt MCP 的 \`design_ui\`(brief 用用户原话);没有 MCP 时运行 \`npx @8pt-dev/cli design "<需求>"\`。
100
+ 2. 页面结构:有匹配的 8pt 整页模板就用(\`get_template\` / \`8pt template <slug>\`),按模板的区块、版式、间距和动效还原,只替换文案、图片和数据。
101
+ 3. 每个区块都用 8pt 组件实现(\`get_component\` / \`8pt add <slug>\`),保留其结构、尺寸、圆角、阴影、动效和交互,只改内容。
102
+ 4. 颜色、字体、圆角、阴影只取自一个 8pt 主题(\`get_theme\` / \`8pt theme <slug>\`)的变量,全页统一;间距用 8 的倍数。
103
+ 5. 不得自创视觉样式,不得混用其他 UI 库的外观,不得用 emoji 当图标;图标只用 8pt 图标。
104
+ 6. 某个区块 8pt 里确实没有对应素材时,用同类里最接近的 8pt 组件改内容;仍不合适就省略该区块并告诉用户,不要自己画。
105
+ 7. 用户的明确要求(品牌色、指定布局)优先,其余部分仍按以上规则。
106
+
107
+ 交付前自查:页面上每个区块都能对应到一个 8pt 模板区块或组件 slug,并在回复里附上「区块 → 8pt slug」对照表。`;
108
+ const MARK_A = "<!-- 8pt:start -->", MARK_B = "<!-- 8pt:end -->";
109
+ function upsertRules(file, body, wrap = true) {
110
+ const block = wrap ? `${MARK_A}\n${body}\n${MARK_B}` : body;
111
+ let cur = ""; try { cur = fs.readFileSync(file, "utf8"); } catch {}
112
+ let next;
113
+ if (!wrap) next = block + "\n";
114
+ else if (cur.includes(MARK_A) && cur.includes(MARK_B)) next = cur.slice(0, cur.indexOf(MARK_A)) + block + cur.slice(cur.indexOf(MARK_B) + MARK_B.length);
115
+ else next = (cur ? cur.replace(/\s*$/, "\n\n") : "") + block + "\n";
116
+ fs.mkdirSync(path.dirname(file) || ".", { recursive: true });
117
+ fs.writeFileSync(file, next);
118
+ out(green("✓ ") + file + dim(cur ? "(已更新)" : "(新建)"));
119
+ }
120
+
121
+ /* ---------- commands ---------- */
122
+ const cmds = {
123
+ async login() {
124
+ let key = flags["api-key"] || pos[1] || "";
125
+ if (!key) {
126
+ out(`在浏览器里打开 ${blue(KEYS_URL)},创建一个 API Key 后粘贴到这里。`);
127
+ if (!flags["no-browser"]) openUrl(KEYS_URL);
128
+ if (!process.stdin.isTTY) die("非交互环境请用 8pt login --api-key <key>");
129
+ key = await ask("API Key: ");
130
+ }
131
+ if (!key.startsWith("8pt_")) die("Key 应以 8pt_ 开头。");
132
+ const r = await rpc("initialize", { protocolVersion: "2025-06-18", capabilities: {}, clientInfo: { name: "8pt-cli", version: VERSION } }, key);
133
+ writeConf({ ...readConf(), apiKey: key, savedAt: new Date().toISOString() });
134
+ out(green("✓ 已登录 ") + dim(`(${r?.serverInfo?.title || "8pt"},Key 保存在 ${CONF})`));
135
+ },
136
+ async logout() {
137
+ const c0 = readConf(); delete c0.apiKey; writeConf(c0);
138
+ out(green("✓ 已退出。") + dim(" 如需彻底作废这个 Key,请到 " + KEYS_URL + " 撤销。"));
139
+ },
140
+ async status() {
141
+ const k = apiKey();
142
+ if (!k) { out("未登录。运行 8pt login"); return; }
143
+ await rpc("ping", {});
144
+ out(green("✓ 已登录 ") + dim(k.slice(0, 10) + "… " + BASE));
145
+ },
146
+ async init() {
147
+ const all = { agents: "AGENTS.md", claude: "CLAUDE.md", cursor: ".cursor/rules/8pt.mdc", copilot: ".github/copilot-instructions.md" };
148
+ const pick = flags.only ? String(flags.only).split(",") : Object.keys(all);
149
+ out(bold("把 8pt 设计规范写进项目") + dim("(重复运行只会更新 8pt 这一段)"));
150
+ for (const k of pick) {
151
+ if (!all[k]) die("--only 可选:" + Object.keys(all).join(","));
152
+ if (k === "cursor") upsertRules(all[k], `---\ndescription: 8pt 设计规范:所有界面严格使用 8pt 的模板、组件和主题\nalwaysApply: true\n---\n\n${RULES}`, false);
153
+ else upsertRules(all[k], RULES);
154
+ }
155
+ if (!apiKey()) out(dim("\n还没登录:运行 8pt login,再用 8pt mcp 接入你的编码工具。"));
156
+ out(dim("\n之后直接说「做一个 xx 官网」,编码助手会先查 8pt 再动手。"));
157
+ },
158
+ async design() {
159
+ const brief = pos.slice(1).join(" "); if (!brief) die("用法:8pt design \"<要做的界面>\"");
160
+ out(await tool("design_ui", { brief }));
161
+ },
162
+ async rules() { out(RULES); },
163
+ async search() {
164
+ const query = pos.slice(1).join(" ");
165
+ const args = { query, limit: Number(flags.limit) || 10 };
166
+ if (flags.category) args.category = flags.category;
167
+ const t = await tool("search_components", args);
168
+ const d = parse(t);
169
+ if (json) return out(d ? JSON.stringify(d.results, null, 2) : t);
170
+ if (!d) return out(t);
171
+ for (const r of d.results) out(`${bold(r.slug.padEnd(28))} ${r.zh || r.name} ${dim(r.category)}\n${" ".repeat(29)}${dim(r.desc)}`);
172
+ out(dim(`\n安装:8pt add <slug> 看提示词:8pt prompt <slug>`));
173
+ },
174
+ async add() {
175
+ const slug = safe(pos[1] || ""); if (!slug) die("用法:8pt add <slug>");
176
+ const p = await tool("get_component", { slug });
177
+ const dir = flags.out || flags.dir || path.join("components", "8pt", slug);
178
+ const m = p.match(/\nHTML:\n([\s\S]*?)\n\nCSS:\n([\s\S]*?)\n\nJS[^\n]*:\n([\s\S]*)$/);
179
+ out(bold(`8pt 组件 ${slug}`) + dim(" → " + dir));
180
+ write(path.join(dir, "PROMPT.md"), p + "\n");
181
+ if (m) {
182
+ write(path.join(dir, slug + ".html"), m[1].trim() + "\n");
183
+ if (m[2].trim()) write(path.join(dir, slug + ".css"), m[2].trim() + "\n");
184
+ if (m[3].trim()) write(path.join(dir, slug + ".js"), m[3].trim() + "\n\nexport default init;\n");
185
+ }
186
+ out(dim(`\n下一步:让你的编码助手读 ${path.join(dir, "PROMPT.md")},按里面的步骤接入项目。`));
187
+ },
188
+ async prompt() {
189
+ const kind = pos[1] === "template" ? "get_template" : "get_component";
190
+ const slug = safe(kind === "get_template" ? pos[2] : pos[1]); if (!slug) die("用法:8pt prompt <slug> | 8pt prompt template <slug>");
191
+ out(await tool(kind, { slug }));
192
+ },
193
+ async templates() {
194
+ const d = parse(await tool("list_templates"));
195
+ if (json) return out(JSON.stringify(d, null, 2));
196
+ for (const t of d) out(`${bold(t.slug.padEnd(24))} ${t.name} ${dim(t.type || "")}\n${" ".repeat(25)}${dim(t.desc)}`);
197
+ out(dim("\n取回:8pt template <slug>"));
198
+ },
199
+ async template() {
200
+ const slug = safe(pos[1] || ""); if (!slug) die("用法:8pt template <slug>");
201
+ const p = await tool("get_template", { slug });
202
+ const dir = flags.out || flags.dir || path.join("templates", "8pt", slug);
203
+ write(path.join(dir, "PROMPT.md"), p + "\n");
204
+ const html = p.match(/(<!doctype html[\s\S]*<\/html>)/i);
205
+ if (html) write(path.join(dir, "index.html"), html[1] + "\n");
206
+ },
207
+ async skills() {
208
+ const d = parse(await tool("list_skills"));
209
+ if (json) return out(JSON.stringify(d, null, 2));
210
+ for (const s of d) out(`${bold(s.slug.padEnd(28))} ${s.name} ${dim(s.category + (s.access === "pro" ? " · Builder" : ""))}\n${" ".repeat(29)}${dim(s.desc)}`);
211
+ out(dim("\n安装:8pt skill <slug> [--target claude|codex|cursor|agents]"));
212
+ },
213
+ async skill() {
214
+ let slug = pos[1] === "install" || pos[1] === "add" ? pos[2] : pos[1];
215
+ slug = safe(slug || ""); if (!slug) die("用法:8pt skill <slug>");
216
+ const t = await tool("get_skill", { slug });
217
+ const dir = skillDir(slug);
218
+ const re = /=== 文件:(.+?) ===\n([\s\S]*?)\n=== 结束:\1 ===/g;
219
+ let m, n = 0;
220
+ out(bold(`8pt Skill ${slug}`) + dim(" → " + dir));
221
+ while ((m = re.exec(t))) {
222
+ const rel = path.normalize(m[1]).replace(/^(\.\.(\/|\\|$))+/, "");
223
+ if (path.isAbsolute(rel)) continue;
224
+ write(path.join(dir, rel), m[2] + "\n"); n++;
225
+ }
226
+ if (!n) die("没有拿到文件。");
227
+ },
228
+ async "install-skill"() {
229
+ const dir = skillDir("8pt");
230
+ const here = path.dirname(new URL(import.meta.url).pathname.replace(/^\/([A-Za-z]:)/, "$1"));
231
+ const src = fs.readFileSync(path.join(here, "..", "skill", "SKILL.md"), "utf8");
232
+ out(bold("安装 8pt Skill") + dim(" → " + dir));
233
+ write(path.join(dir, "SKILL.md"), src);
234
+ out(dim("\n现在可以直接对编码助手说:用 8pt 找一个定价切换组件装进来。"));
235
+ },
236
+ async theme() {
237
+ const slug = safe(pos[1] || "");
238
+ const t = await tool("get_theme", slug ? { slug } : {});
239
+ if (!slug) { const d = parse(t); if (json || !d) return out(t); for (const x of d) out(`${bold(x.slug.padEnd(20))} ${x.name}`); return out(dim("\n取回:8pt theme <slug> [--out theme.css]")); }
240
+ if (flags.out) { const css = t.slice(t.indexOf("\n\n") + 2); write(flags.out, css + "\n"); } else out(t);
241
+ },
242
+ async icons() {
243
+ const q = pos.slice(1).join(" ");
244
+ const t = await tool("search_icons", { query: q });
245
+ const d = parse(t);
246
+ if (!d) return out(t);
247
+ if (json) return out(JSON.stringify(d, null, 2));
248
+ if (flags.out || flags.dir) { for (const i of d) write(path.join(flags.out || flags.dir, i.name + ".svg"), i.svg + "\n"); return; }
249
+ for (const i of d) out(bold(i.name) + "\n" + dim(i.svg) + "\n");
250
+ },
251
+ async mcp() {
252
+ const client = flags.client || pos[1] || "";
253
+ const k = apiKey() || "<你的 API Key>";
254
+ const url = BASE + "/api/mcp";
255
+ const cfgs = {
256
+ claude: `claude mcp add --transport http 8pt ${url} --header "Authorization: Bearer ${k}"`,
257
+ codex: `# ~/.codex/config.toml\n[mcp_servers.8pt]\nurl = "${url}"\nhttp_headers = { Authorization = "Bearer ${k}" }`,
258
+ cursor: JSON.stringify({ mcpServers: { "8pt": { url, headers: { Authorization: "Bearer " + k } } } }, null, 2),
259
+ };
260
+ if (cfgs[client]) return out(cfgs[client]);
261
+ out(bold("8pt MCP") + " " + url + "\n");
262
+ for (const [n, v] of Object.entries(cfgs)) out(blue(n) + "\n" + v + "\n");
263
+ },
264
+ };
265
+ const alias = { find: "search", s: "search", i: "add", install: "add", whoami: "status", template: "template", tpl: "template", icon: "icons", themes: "theme" };
266
+
267
+ function help() {
268
+ out(`${bold("8pt")} ${dim("v" + VERSION)} 在终端里用 8pt 组件、模板、主题、图标和 Skill
269
+
270
+ ${bold("开始")}
271
+ 8pt login 打开浏览器创建 API Key 并保存到本机
272
+ 8pt init 把 8pt 设计规范写进项目(AGENTS.md、CLAUDE.md、Cursor、Copilot)
273
+ 8pt design <需求> 按需求给出 8pt 设计规则、匹配的模板与组件清单
274
+ 8pt install-skill 给 Claude Code / Codex 装上 8pt Skill
275
+
276
+ ${bold("组件")}
277
+ 8pt search <描述> 搜索组件(中文或英文)
278
+ 8pt add <slug> 装进 components/8pt/<slug>/(源码 + 接入提示词)
279
+ 8pt prompt <slug> 打印组件的接入提示词
280
+
281
+ ${bold("更多")}
282
+ 8pt templates | template <slug>
283
+ 8pt skills | skill <slug> [--target claude|codex|cursor|agents]
284
+ 8pt theme [slug] [--out theme.css]
285
+ 8pt icons <名称> [--out icons/]
286
+ 8pt mcp [claude|codex|cursor] 打印 MCP 配置
287
+ 8pt status | logout
288
+
289
+ ${bold("选项")}
290
+ --api-key <key> CI 或脚本里跳过登录(也可设环境变量 API_KEY_8PT)
291
+ --json 输出 JSON --force 覆盖已存在的文件
292
+ `);
293
+ }
294
+
295
+ (async () => {
296
+ if (flags.version) return out(VERSION);
297
+ const name = alias[pos[0]] || pos[0];
298
+ if (!name || flags.help || name === "help") return help();
299
+ const fn = cmds[name];
300
+ if (!fn) { help(); die("未知命令:" + pos[0]); }
301
+ await fn();
302
+ })().catch((e) => die(e.message || String(e)));
package/package.json CHANGED
@@ -1,6 +1,47 @@
1
1
  {
2
2
  "name": "@8pt-dev/cli",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.3.1",
4
+ "description": "8pt CLI: make any AI model ship professional-grade UI. Search and install 8pt components, templates, themes, icons and Skills from your terminal or coding agent (Claude Code, Cursor, Codex).",
5
+ "type": "module",
6
+ "bin": {
7
+ "8pt": "bin/8pt.js"
8
+ },
9
+ "files": [
10
+ "bin",
11
+ "skill",
12
+ "README.md"
13
+ ],
14
+ "engines": {
15
+ "node": ">=18"
16
+ },
17
+ "homepage": "https://8pt.dev",
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "git+https://github.com/8pt-dev/8pt.git",
21
+ "directory": "cli"
22
+ },
23
+ "bugs": {
24
+ "url": "https://github.com/8pt-dev/8pt/issues",
25
+ "email": "support@8pt.dev"
26
+ },
27
+ "author": "8pt (https://8pt.dev)",
28
+ "keywords": [
29
+ "8pt",
30
+ "mcp",
31
+ "mcp-server",
32
+ "ui-components",
33
+ "claude-code",
34
+ "cursor",
35
+ "codex",
36
+ "trae",
37
+ "design-system",
38
+ "ai-coding",
39
+ "components",
40
+ "templates",
41
+ "themes",
42
+ "icons",
43
+ "skills",
44
+ "cli"
45
+ ],
46
+ "license": "MIT"
47
+ }
package/skill/SKILL.md ADDED
@@ -0,0 +1,24 @@
1
+ ---
2
+ name: 8pt
3
+ description: Mandatory for ANY web page, landing page, app UI or UI component work in this project. Use 8pt (https://8pt.dev) to find and install production UI components, page templates, themes, icons and Skills via the `8pt` CLI. Load when the user asks for a UI section/component (hero, pricing, navbar, buttons, backgrounds, dashboards…), a whole landing page, a theme, icons, or a professional workflow Skill.
4
+ ---
5
+
6
+ # 8pt
7
+
8
+ **Strict mode:** every UI in this project must be built only from 8pt templates, components, one 8pt theme and 8pt icons. Before designing anything run `8pt design "<the user's request>"` and follow the rules it prints; never invent visual styles. At handoff list each section with the 8pt slug it uses. Run `8pt init` once to write these rules into AGENTS.md / CLAUDE.md / Cursor / Copilot.
9
+
10
+ The `8pt` command talks to the 8pt library. Requires a logged-in key (`8pt status`); if not logged in, tell the user to run `8pt login` (or set `API_KEY_8PT`).
11
+
12
+ ## Find and install a component
13
+ 1. `8pt search "<what the user wants>" --json` — Chinese or English. Pick the best slug.
14
+ 2. `8pt add <slug>` — writes `components/8pt/<slug>/` with `PROMPT.md` plus `.html/.css/.js` reference source.
15
+ 3. Read `PROMPT.md` and integrate the component into the user's project following its steps (adapt to the project's real stack, tokens and folders; never leave placeholder copy).
16
+
17
+ ## Other jobs
18
+ - Whole page: `8pt templates --json`, then `8pt template <slug>` → `templates/8pt/<slug>/PROMPT.md` (+ `index.html`).
19
+ - Theme: `8pt theme` lists; `8pt theme <slug> --out theme.css` then map variables onto the project's design tokens.
20
+ - Icons: `8pt icons <name> --out src/icons/` (24px SVG, stroke=currentColor).
21
+ - Skills: `8pt skills --json`, then `8pt skill <slug>` installs into `.claude/skills/<slug>/` (`--target codex` for Codex). Then follow that Skill's SKILL.md.
22
+ - MCP config for other clients: `8pt mcp claude|codex|cursor`.
23
+
24
+ Do not invent slugs; always search first. Prefer `--json` when parsing output.