一键导入
claudeck-plugin-create
Scaffold a new Claudeck plugin with JS, CSS, and optional server routes. Requires Claudeck (npx claudeck) — the browser UI for Claude Code.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Scaffold a new Claudeck plugin with JS, CSS, and optional server routes. Requires Claudeck (npx claudeck) — the browser UI for Claude Code.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
| name | claudeck-plugin-create |
| description | Scaffold a new Claudeck plugin with JS, CSS, and optional server routes. Requires Claudeck (npx claudeck) — the browser UI for Claude Code. |
| argument-hint | <plugin-name> <short description of what the plugin does> |
| tags | claudeck, plugin, tab-sdk, ui |
| prerequisites | Claudeck must be installed (npx claudeck or npm i -g claudeck). See https://www.npmjs.com/package/claudeck |
You are scaffolding a new tab-sdk plugin called "$ARGUMENTS".
Parse the arguments: the first word is the plugin name (kebab-case), the rest is the description of what the plugin should do.
Claudeck supports two plugin directories:
| Directory | URL Path | Use When |
|---|---|---|
plugins/<name>/ (project) | /plugins/<name>/ | Building a built-in plugin that ships with Claudeck |
~/.claudeck/plugins/<name>/ (user) | /user-plugins/<name>/ | Creating a personal/user-installed plugin that persists across updates |
Default: Create user plugins in ~/.claudeck/plugins/<name>/ unless the user explicitly asks for a built-in plugin. User plugins are the recommended approach since they persist across npm upgrades and don't require modifying the Claudeck source code.
To resolve the user plugins directory, use ~/.claudeck/plugins/ (or $CLAUDECK_HOME/plugins/ if the env var is set).
my-plugin, code-metrics)GET /api/pluginsclient.js and optionally client.cssserver.js (for user plugins, requires CLAUDECK_USER_SERVER_PLUGINS=true in ~/.claudeck/.env)config.json (auto-copied to ~/.claudeck/config/ on first run)Create client.js in the plugin directory following this structure:
// <Title> — Tab SDK plugin
// <Description of what the plugin does>
import { registerTab } from '/js/ui/tab-sdk.js';
registerTab({
id: '<name>',
title: '<Title>',
icon: '<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">...</svg>',
lazy: true,
init(ctx) {
// ── Build DOM ──
const root = document.createElement('div');
root.className = '<name>-tab';
root.style.cssText = 'display:flex;flex-direction:column;flex:1;overflow:hidden;';
// Build the UI innerHTML here...
// ── Context API (ctx) ──
// ctx.pluginId — Plugin's ID string
// ctx.getProjectPath() — Current project path
// ctx.getSessionId() — Current session ID
// ctx.getTheme() — Current theme: 'dark' or 'light'
// ctx.on('projectChanged', fn) — Fires when user switches project (returns unsub fn)
// ctx.on('ws:message', fn) — Live WebSocket stream messages (returns unsub fn)
// ctx.onState('sessionId', fn) — Session switch (returns unsub fn)
// ctx.off(event, fn) — Remove an event listener
// ctx.api — Full API module for fetch calls
// ctx.storage.get(key) — Read from plugin-scoped localStorage
// ctx.storage.set(key, value) — Write to plugin-scoped localStorage
// ctx.storage.remove(key) — Remove from plugin-scoped localStorage
// ctx.toast(msg, opts) — Show notification ({duration, type: 'info'|'success'|'error'})
// ctx.showBadge(count) — Show badge on tab button
// ctx.clearBadge() — Hide badge
// ctx.setTitle(text) — Update tab button label
// ctx.dispose() — Unsubscribe all listeners (auto-called on destroy)
// ── Project-scoped data ──
// If your plugin loads data scoped to a project, follow this pattern:
//
// function loadData() {
// const project = ctx.getProjectPath();
// if (!project) return;
// fetch(`/api/plugins/<name>/?project=${encodeURIComponent(project)}`)
// .then(r => r.json()).then(data => { /* render */ });
// }
// ctx.on('projectChanged', loadData); // reload on project switch
// loadData(); // initial load
//
// IMPORTANT: ALWAYS use ctx.getProjectPath() to read the project path.
// NEVER use document.getElementById('project-select') directly.
// IMPORTANT: ALWAYS use ctx.on('projectChanged', fn) to react to project changes.
// NEVER add your own change listener to the project select element.
return root; // must return an HTMLElement
},
// onActivate(ctx) { /* tab became visible — ctx is passed */ },
// onDeactivate(ctx) { /* tab was hidden — ctx is passed */ },
// onDestroy(ctx) { /* cleanup — ctx is passed; all subscriptions auto-disposed */ },
});
IMPORTANT: Use absolute import paths (e.g. '/js/ui/tab-sdk.js', '/js/core/api.js') since plugins are served from /plugins/<name>/client.js (built-in) or /user-plugins/<name>/client.js (user). Absolute paths work identically for both locations.
Create client.css in the plugin directory with styles scoped to .<name>-tab to avoid conflicts:
/* <Title> — Tab SDK plugin styles */
.<name>-tab {
font-family: var(--font-mono);
color: var(--text);
}
Use these CSS custom properties from the app theme:
var(--text), var(--text-secondary), var(--text-dim), var(--accent), var(--success), var(--warning), var(--error)var(--bg), var(--bg-secondary), var(--bg-tertiary), var(--bg-deep)var(--border), var(--radius), var(--radius-lg), var(--font-mono)Create server.js in the plugin directory if your plugin needs API endpoints. The router is auto-mounted at /api/plugins/<name>/:
import { Router } from "express";
const router = Router();
router.get("/", (req, res) => {
res.json({ status: "ok" });
});
export default router;
Note for user plugins: Server-side routes require CLAUDECK_USER_SERVER_PLUGINS=true in ~/.claudeck/.env. This is disabled by default for security.
To use config files, import configPath. The import path depends on the plugin location:
plugins/<name>/): import { configPath } from "../../server/paths.js";~/.claudeck/plugins/<name>/): Use the server's config path directly via the route context, or read/write files using os.homedir():import { readFileSync, writeFileSync } from "fs";
import { join } from "path";
import { homedir } from "os";
const cfgDir = process.env.CLAUDECK_HOME || join(homedir(), ".claudeck", "config");
const cfgFile = join(cfgDir, "<name>-config.json");
Create config.json in the plugin directory with default values. It will be auto-copied to ~/.claudeck/config/ on first run:
{
"enabled": false,
"settings": {}
}
From /js/core/:
store.js — getState(key), setState(key, val), on(key, fn) (returns unsub), off(key, fn)events.js — on(event, fn) (returns unsub), off(event, fn), emit(event, data)dom.js — $ (cached DOM query map)constants.js — CHAT_IDS, BOT_CHAT_IDapi.js — all fetch helpers (fetchSessions, fetchFileTree, execCommand, etc.)utils.js — escapeHtml(), getToolDetail(), formatBytes(), etc.From /js/ui/:
tab-sdk.js — registerTab(), unregisterTab(), getRegisteredTabs()commands.js — registerCommand() for slash commandsformatting.js — renderMarkdown(), highlightCodeBlocks()parallel.js — getPane(), panesright-panel.js — openRightPanel(), toggleRightPanel()Now implement the plugin based on the user's description. Build a fully functional plugin — not just a skeleton. Include:
ctx.on('ws:message', ...) if relevantctx.onState('sessionId', ...) if relevantctx.getProjectPath() and ctx.on('projectChanged', ...) if the plugin loads project-scoped datactx.showBadge() where meaningfulctx.storage.get/set for client-side statectx.toast() for user feedbackCRITICAL: Never access the project select DOM element directly. Always use ctx.getProjectPath() and ctx.on('projectChanged', fn) from the tab SDK context.
server.js, remind them to set CLAUDECK_USER_SERVER_PLUGINS=true in ~/.claudeck/.env and restart the server