Agent Skills › mereyabdenbekuly-ctrl/clodex-ide

mereyabdenbekuly-ctrl/clodex-ide

GitHub

提供Figma插件指南,涵盖REST API访问、实时选择监控及交互式UI。指导如何获取凭证、调用API查询文件与组件、提取节点ID,并强调每次交互前需查询最新选中内容。

15 个 Skill 840

安装全部 Skills

npx skills add mereyabdenbekuly-ctrl/clodex-ide --all -g -y
更多选项

预览集合内 Skills

npx skills add mereyabdenbekuly-ctrl/clodex-ide --list

集合内 Skills (15)

提供Figma插件指南,涵盖REST API访问、实时选择监控及交互式UI。指导如何获取凭证、调用API查询文件与组件、提取节点ID,并强调每次交互前需查询最新选中内容。
用户请求操作Figma设计文件 用户询问Figma中的特定元素或组件 需要导出Figma设计图片 用户提到Figma URL或文件密钥
apps/browser/bundled/plugins/figma/SKILL.md
npx skills add mereyabdenbekuly-ctrl/clodex-ide --skill figma -g -y
SKILL.md
Frontmatter
{
    "name": "figma",
    "description": "Complete guide for the Figma plugin — REST API access, real-time selection monitoring via CDP, and the figma-app interactive UI. Read this IMMEDIATELY when the user asks to work with Figma."
}

Figma Plugin

This plugin provides three capabilities:

  1. Figma REST API access for reading designs, components, and images.
  2. Real-time selection monitoring — watch which nodes the user selects in a Figma tab via CDP.
  3. figma-app — an interactive UI in a browser tab that displays the current selection as live badges.

Querying the Current Selection (IMPORTANT)

The user's Figma selection is not automatically attached to their message. Before acting on any Figma-related request, always query the latest selection so you know which nodes the user is referring to:

const selectedNodes = globalThis.figmaSelection || [];
return selectedNodes;

If the result is an empty array and the user clearly expects a selection, remind them to select elements in the Figma tab first.

Do this at the start of every Figma-related turn — even if you set up monitoring earlier, the selection may have changed since then.


Authentication

Request the stored Figma credential. The token field is an opaque placeholder that the sandbox fetch proxy substitutes automatically — pass it directly in the X-Figma-Token header and never try to decode or transform it.

const cred = await API.getCredential('figma-pat');
if (!cred) {
  return 'Figma credential is not configured. Ask the user to add a Figma Personal Access Token in Settings → Security → Personal access tokens.';
}

Making Requests

Always use fetch with the credential header:

async function figmaGet(path) {
  const res = await fetch(`https://api.figma.com/v1${path}`, {
    headers: { 'X-Figma-Token': cred.token },
  });
  if (!res.ok) throw new Error(`Figma API ${res.status}: ${await res.text()}`);
  return res.json();
}

Key Endpoints

Get a file

GET /v1/files/:file_key Returns the full document tree. Add ?depth=1 or ?depth=2 to limit nesting and reduce response size.

Get file nodes

GET /v1/files/:file_key/nodes?ids=:node_ids Fetch specific nodes by comma-separated IDs (e.g. 1:2,3:4). Much faster than fetching the entire file.

List components

GET /v1/files/:file_key/components Returns published components in the file with their metadata.

Export images

GET /v1/images/:file_key?ids=:node_ids&format=png&scale=2 Renders nodes as images. Supported formats: png, jpg, svg, pdf.

List project files

GET /v1/projects/:project_id/files Lists all files in a project.

Get file styles

GET /v1/files/:file_key/styles Returns published styles (colors, text, effects, grids).

Extracting a File Key

A Figma URL looks like: https://www.figma.com/design/<file_key>/<file_name>?node-id=<node_id>

The file key is the first path segment after /design/ (or /file/ in older URLs).

Important Rules

  • Always request credentials with API.getCredential('figma-pat') before making any Figma API call. If it returns null, tell the user to configure the token.
  • Use ?depth= when fetching files to avoid huge payloads.
  • Prefer /files/:key/nodes?ids= over fetching the whole file when you only need specific frames or components.
  • Rate limits: Figma allows ~30 requests/minute per token. Avoid tight loops; batch node IDs into a single request where possible.
  • Image export URLs returned by /v1/images expire after 14 days.

Selection Monitoring (CDP)

Whenever the user asks to work with Figma designs, eagerly set up selection monitoring so you can see which nodes the user selects in the Figma tab. Briefly tell the user that monitoring is active and they can start selecting elements.

The flow uses Runtime.addBinding to create a push channel from the Figma tab into the sandbox, combined with a polling script injected via Runtime.evaluate.

Full setup recipe

Run this in a single IIFE for each Figma tab. The entire setup is fire-and-forget — it does not block future IIFEs.

// 1. Open the figma-app so the user sees live selection badges
await API.openApp('figma-app', { pluginId: 'figma', title: 'Figma Selection' });

// 2. Identify the Figma tab (use the tab whose URL contains figma.com/design/)
const tabId = "<figma-tab-id>";

// 3. Inject a Runtime binding so the tab can push data to the sandbox
await API.sendCDP(tabId, "Runtime.addBinding", { name: "sendToAgent" });

// 4. Subscribe to binding calls — accumulate AND forward to app
globalThis.figmaSelection = globalThis.figmaSelection || [];
globalThis._unsubFigmaBinding = API.onCDPEvent(tabId, "Runtime.bindingCalled", (event) => {
  if (event.name === "sendToAgent") {
    try {
      const payload = JSON.parse(event.payload);
      // Store for agent reads
      globalThis.figmaSelection = payload.nodes || [];
      // Forward to figma-app for live badge rendering
      API.sendMessage('figma-app', {
        type: 'selectionChanged',
        nodes: payload.nodes || [],
        timestamp: payload.timestamp,
      }, { pluginId: 'figma' });
    } catch {}
  }
});

// 5. Inject a polling script into the Figma tab
//    It reads figma.currentPage.selection every 200ms and pushes
//    changes through the binding.
await API.sendCDP(tabId, "Runtime.evaluate", {
  expression: `
    (function() {
      if (window.__figmaSelectionWatcher) return; // prevent duplicates
      window.__figmaSelectionWatcher = true;
      window.__lastSelKey = '';
      setInterval(() => {
        try {
          if (typeof figma === 'undefined' || !figma.currentPage) return;
          const sel = figma.currentPage.selection;
          const key = sel.map(n => n.id).join(',');
          if (key === window.__lastSelKey) return;
          window.__lastSelKey = key;
          window.sendToAgent(JSON.stringify({
            timestamp: Date.now(),
            nodes: sel.map(n => ({
              id: n.id,
              name: n.name,
              type: n.type,
              width: n.width,
              height: n.height,
            })),
          }));
        } catch {}
      }, 200);
    })();
  `,
});

API.output("Figma selection monitoring active. Select elements in the Figma tab — they will appear in the Figma App preview tab.");

Reading the current selection in a later IIFE

// Returns the latest list of selected nodes (or [] if none)
return globalThis.figmaSelection;

Cleaning up

if (globalThis._unsubFigmaBinding) globalThis._unsubFigmaBinding();
await API.closeApp();

Multiple Figma tabs

If the user opens additional Figma tabs, run the setup recipe again with the new tabId. Each tab gets its own binding + polling script. The onCDPEvent callback updates the same globalThis.figmaSelection so the agent always sees the most recent selection regardless of which tab it came from.


Available Apps

This plugin provides interactive apps that can be opened in browser tabs using API.openApp().

App ID Description
figma-app Displays the user's current Figma node selection as live badges. Also accepts arbitrary data messages from the agent.

Opening the app

await API.openApp('figma-app', { pluginId: 'figma', title: 'Figma Selection' });

Closing the app

await API.closeApp();

The app renders in a dedicated preview tab. Calling API.openApp() again opens a refreshed preview tab. The user can also dismiss it manually.

Sending data to the app

The figma-app listens for messages with a type field. The primary message type is selectionChanged:

await API.sendMessage('figma-app', {
  type: 'selectionChanged',
  nodes: [
    { id: '1:23', name: 'Header Frame', type: 'FRAME', width: 1440, height: 900 },
    { id: '4:56', name: 'Button', type: 'INSTANCE', width: 200, height: 48 },
  ],
  timestamp: Date.now(),
}, { pluginId: 'figma' });

You can also send arbitrary data (the app will render it as JSON):

await API.sendMessage('figma-app', {
  type: 'custom',
  data: someObject,
}, { pluginId: 'figma' });

Receiving messages from the app

Register a listener that persists across IIFE executions:

globalThis.figmaAppMessages = globalThis.figmaAppMessages || [];
API.onMessage('figma-app', (msg) => {
  globalThis.figmaAppMessages.push(msg);
}, { pluginId: 'figma' });

Read collected messages in a later IIFE:

return globalThis.figmaAppMessages;
GitHub插件,通过REST API操作仓库、Issue、PR、Actions和Release。需配置PAT令牌,使用Bearer认证调用API,支持增删改查及搜索功能。
查询或管理GitHub仓库信息 创建或处理Issue与Pull Request 查看或触发GitHub Actions工作流 搜索代码或问题
apps/browser/bundled/plugins/github/SKILL.md
npx skills add mereyabdenbekuly-ctrl/clodex-ide --skill github -g -y
SKILL.md
Frontmatter
{
    "name": "github",
    "description": "Complete guide for the GitHub plugin — REST API access for repositories, issues, pull requests, actions, releases, and search using a GitHub Personal Access Token."
}

GitHub Plugin

This plugin provides access to the GitHub REST API on the user's behalf, using a stored Personal Access Token.

Capabilities:

  • List, inspect, and search repositories
  • Create, read, update, and comment on issues
  • Create, list, review, and merge pull requests
  • List branches, commits, and compare refs
  • Trigger, monitor, and inspect GitHub Actions workflow runs
  • List and create releases
  • Search code, issues, and repositories

Authentication

Request the stored GitHub credential. The token field is an opaque placeholder — the sandbox fetch proxy substitutes the real value automatically. Never decode or transform it.

const cred = await API.getCredential('github-pat');
if (!cred) {
  return 'GitHub credential is not configured. Ask the user to add a GitHub Personal Access Token at github.com/settings/tokens, then store it in Settings.';
}

Making Requests

Always pass the token as a Bearer header and include the recommended API version:

async function ghGet(path) {
  const res = await fetch(`https://api.github.com${path}`, {
    headers: {
      Accept: 'application/vnd.github+json',
      Authorization: `Bearer ${cred.token}`,
      'X-GitHub-Api-Version': '2022-11-28',
    },
  });
  if (!res.ok) throw new Error(`GitHub API ${res.status}: ${await res.text()}`);
  return res.json();
}

async function ghPost(path, body) {
  const res = await fetch(`https://api.github.com${path}`, {
    method: 'POST',
    headers: {
      Accept: 'application/vnd.github+json',
      Authorization: `Bearer ${cred.token}`,
      'X-GitHub-Api-Version': '2022-11-28',
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(body),
  });
  if (!res.ok) throw new Error(`GitHub API ${res.status}: ${await res.text()}`);
  return res.json();
}

async function ghPatch(path, body) {
  const res = await fetch(`https://api.github.com${path}`, {
    method: 'PATCH',
    headers: {
      Accept: 'application/vnd.github+json',
      Authorization: `Bearer ${cred.token}`,
      'X-GitHub-Api-Version': '2022-11-28',
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(body),
  });
  if (!res.ok) throw new Error(`GitHub API ${res.status}: ${await res.text()}`);
  return res.json();
}

Key Endpoints

Repositories

Get a repository GET /repos/{owner}/{repo} Returns full repository metadata including default branch, visibility, and description.

List repositories for a user GET /users/{username}/repos?sort=updated&per_page=30 Use ?type=owner to list only owned repos (excludes forks/member repos).

List repositories for the authenticated user GET /user/repos?sort=updated&per_page=30 Add ?affiliation=owner to show only owned repos.

List organization repositories GET /orgs/{org}/repos?sort=updated&per_page=30

Issues

List issues for a repository GET /repos/{owner}/{repo}/issues?state=open&per_page=30 Add &labels=bug to filter by label. Add &sort=updated to sort by update time. Note: pull requests are included in issue listings. Filter them out by checking that pull_request is absent.

Get a single issue GET /repos/{owner}/{repo}/issues/{issue_number}

Create an issue POST /repos/{owner}/{repo}/issues Body: { "title": "...", "body": "...", "labels": ["bug"], "assignees": ["username"] }

Update an issue PATCH /repos/{owner}/{repo}/issues/{issue_number} Body: { "state": "closed" } or { "title": "New title", "body": "Updated body" }

List issue comments GET /repos/{owner}/{repo}/issues/{issue_number}/comments?per_page=100

Create an issue comment POST /repos/{owner}/{repo}/issues/{issue_number}/comments Body: { "body": "Comment text" }

Pull Requests

List pull requests GET /repos/{owner}/{repo}/pulls?state=open&per_page=30 Add &sort=updated to sort by update time. Use &head=owner:branch to filter by head branch.

Get a pull request GET /repos/{owner}/{repo}/pulls/{pull_number} Returns diff stats, mergeable status, head/base refs, and review state.

Create a pull request POST /repos/{owner}/{repo}/pulls Body: { "title": "...", "body": "...", "head": "feature-branch", "base": "main" }

Merge a pull request PUT /repos/{owner}/{repo}/pulls/{pull_number}/merge Body: { "merge_method": "squash" } — merge_method can be merge, squash, or rebase.

List pull request files GET /repos/{owner}/{repo}/pulls/{pull_number}/files?per_page=100 Returns the list of changed files with patch diffs, additions, deletions, and status.

List review comments on a pull request GET /repos/{owner}/{repo}/pulls/{pull_number}/comments?per_page=100

Branches and Commits

List branches GET /repos/{owner}/{repo}/branches?per_page=100

Get a branch GET /repos/{owner}/{repo}/branches/{branch} Returns the branch tip commit SHA and protection status.

List commits GET /repos/{owner}/{repo}/commits?sha={branch}&per_page=30 Add &since=2024-01-01T00:00:00Z to scope by date.

Compare two refs GET /repos/{owner}/{repo}/compare/{base}...{head} Returns ahead/behind counts, diff stats, and the list of commits between two refs.

GitHub Actions

List workflows GET /repos/{owner}/{repo}/actions/workflows

List workflow runs GET /repos/{owner}/{repo}/actions/runs?per_page=10 Add &status=failure to filter failed runs. Add &branch=main to scope to a branch.

Get a workflow run GET /repos/{owner}/{repo}/actions/runs/{run_id} Returns status, conclusion, timing, and associated commits.

List jobs for a workflow run GET /repos/{owner}/{repo}/actions/runs/{run_id}/jobs Returns individual job names, statuses, conclusions, and step details.

Trigger a workflow dispatch POST /repos/{owner}/{repo}/actions/workflows/{workflow_id}/dispatches Body: { "ref": "main", "inputs": { "key": "value" } } The workflow_id can be the workflow file name (e.g. ci.yml) or numeric ID.

Re-run a workflow POST /repos/{owner}/{repo}/actions/runs/{run_id}/rerun

Cancel a workflow run POST /repos/{owner}/{repo}/actions/runs/{run_id}/cancel

Download workflow run logs GET /repos/{owner}/{repo}/actions/runs/{run_id}/logs Returns a 302 redirect to a zip archive of the logs.

Releases

List releases GET /repos/{owner}/{repo}/releases?per_page=10

Get the latest release GET /repos/{owner}/{repo}/releases/latest

Create a release POST /repos/{owner}/{repo}/releases Body: { "tag_name": "v1.0.0", "name": "v1.0.0", "body": "Release notes", "draft": false, "prerelease": false }

Search

Search repositories GET /search/repositories?q={query}&sort=stars&order=desc&per_page=10

Search issues and pull requests GET /search/issues?q={query}+repo:{owner}/{repo}+is:issue&per_page=10

Search code GET /search/code?q={query}+repo:{owner}/{repo}&per_page=10 Note: code search requires authentication and has stricter rate limits.


Common Patterns

Check CI status for a branch

const cred = await API.getCredential('github-pat');
const runs = await ghGet('/repos/OWNER/REPO/actions/runs?branch=main&per_page=1');
const latest = runs.workflow_runs[0];
return {
  status: latest.status,
  conclusion: latest.conclusion,
  name: latest.name,
  url: latest.html_url,
  createdAt: latest.created_at,
};

List open PRs with their review status

const cred = await API.getCredential('github-pat');
const prs = await ghGet('/repos/OWNER/REPO/pulls?state=open&per_page=10');
return prs.map(pr => ({
  number: pr.number,
  title: pr.title,
  author: pr.user.login,
  head: pr.head.ref,
  base: pr.base.ref,
  draft: pr.draft,
  url: pr.html_url,
}));

Create an issue and add a comment

const cred = await API.getCredential('github-pat');
const issue = await ghPost('/repos/OWNER/REPO/issues', {
  title: 'Bug: login fails on mobile',
  body: 'Steps to reproduce...',
  labels: ['bug'],
});
await ghPost(`/repos/OWNER/REPO/issues/${issue.number}/comments`, {
  body: 'Investigating this now.',
});
return { issueNumber: issue.number, url: issue.html_url };

Watch a workflow run until completion

const cred = await API.getCredential('github-pat');
const runId = 12345678;
let status = 'in_progress';
let attempts = 0;
while (['queued', 'in_progress', 'waiting'].includes(status) && attempts < 30) {
  await new Promise(r => setTimeout(r, 5000));
  const run = await ghGet(`/repos/OWNER/REPO/actions/runs/${runId}`);
  status = run.status;
  attempts++;
  API.output(`Run status: ${status} (conclusion: ${run.conclusion ?? 'pending'})`);
}
const final = await ghGet(`/repos/OWNER/REPO/actions/runs/${runId}`);
return { status: final.status, conclusion: final.conclusion };

Extracting Owner and Repo from a GitHub URL

A GitHub URL looks like: https://github.com/{owner}/{repo}/...

The owner is the first path segment after github.com/, and the repo is the second. Strip any trailing .git if present.


Important Rules

  • Always call API.getCredential('github-pat') before any GitHub API call. If it returns null, prompt the user to configure it.
  • Always include the Accept: application/vnd.github+json and X-GitHub-Api-Version: 2022-11-28 headers.
  • Pagination: most list endpoints return 30 items by default. Use ?per_page=100 (max) and ?page=N to paginate. Check the Link response header for rel="next" to detect more pages.
  • Rate limits: authenticated PATs allow 5,000 requests/hour. Check x-ratelimit-remaining in response headers. If exhausted, wait until the time in x-ratelimit-reset (UTC epoch seconds).
  • Search endpoints have a stricter rate limit of 30 requests/minute. Space out search calls accordingly.
  • Treat tokens as sensitive — never log or display them.
提供 clodex JavaScript 沙箱的最佳实践,涵盖超时控制、输出与附件创建、CDP 调试、文件系统访问及凭据获取等核心 API 使用指南。
需要在隔离环境中执行 JavaScript 脚本 进行浏览器自动化或 CDP 调试 处理文件上传下载或二进制数据 运行迷你应用 (Mini Apps)
apps/browser/bundled/plugins/javascript-sandbox/SKILL.md
npx skills add mereyabdenbekuly-ctrl/clodex-ide --skill javascript-sandbox -g -y
SKILL.md
Frontmatter
{
    "name": "javascript-sandbox",
    "description": "Best practices for using the clodex built-in JavaScript sandbox. Explains how to access APIs for browser debugging\/interaction, use external dependencies, file system access, running mini-apps, etc."
}

JavaScript Sandbox

The sandbox is an isolated, persistent Node.js VM context. Data and functions stored on globalThis survive across calls and messages. Scripts run inside an async IIFE. The sandbox offers a standard clodex runtime API through the global object API.


Timeouts

  • Inactivity timeout: 45 seconds. Each call to API.output() or API.createAttachment() resets the timer.
  • Hard cap: 3 minutes wall-clock (non-resettable).
  • NEVER use await Promise.resolve() or unbounded while(true) loops — these permanently block the sandbox worker.
  • In loops, yield with await new Promise(r => setTimeout(r, 0)) every ~1000 sync iterations.
  • Always use bounded loops. Return partial results if hitting the limit.
  • For long-running tasks, call API.output() periodically as a heartbeat. Split work across multiple invocations if needed.

Creating Outputs

Use API.output(data: any): void to generate outputs. Can be called multiple times; outputs are concatenated. NEVER use console.log() or other console methods.


Creating Attachments

Use API.createAttachment(originalFileName: string, data: Buffer | string): Promise<string>.

  • originalFileName: user-visible name with extension (e.g. screenshot.png)
  • data: binary content or base64-encoded string
  • Returns the obfuscated file name in att/always use this returned name when referencing the attachment afterwards.

Chrome DevTools Protocol (CDP)

Send commands via API.sendCDP(tabId, method, params?): Promise<any>. Listen to events via API.onCDPEvent(tabId, event, callback): void (listeners persist across IIFEs; use globalThis to accumulate).

  • Pre-enabled (do NOT call .enable): DOM, CSS, Page, Runtime, Log, Console
  • No enable method (use directly): Input, Emulation, IO, Target, Browser, SystemInfo, Schema
  • All others (e.g. Network, Overlay, Debugger, Fetch): call <Domain>.enable first.

Filesystem Access

Sandboxed fs and fsPromises globals are available directly (also via require('fs')). Scoped to mounted workspaces.

  • Paths use mount prefixes: w1/src/index.ts, w2/package.json. Optional if one workspace mounted.
  • All mounts (w1/, att/, apps/, plugins/) share the same API — cross-mount copy/move works.
  • All standard fs methods available (callback, sync, and promise APIs).
  • att/ — read-only access to attachments. Create with API.createAttachment().
  • plugins/ — read-only access to plugin files.
  • Sandbox fs is well-suited for binary operations and cross-mount copies.

Credentials

Use API.getCredential(typeId): Promise<string> to retrieve stored credentials. Secret fields contain opaque placeholders auto-substituted in outgoing fetch calls. Plain fields contain real values.


Mini Apps

Mini apps are interactive web UIs rendered in dedicated browser tabs. Use API.openApp(appId, opts?) to open one. See the mini-apps skill for full details on building, messaging, and best practices.


Available Runtime

Global APIs: Promise, Map, Set, Array, Object, JSON, Math, RegExp, Date, Error, typed arrays, setTimeout, setInterval, setImmediate, fetch, Headers, Request, Response, AbortController, URL, TextEncoder, TextDecoder, atob, btoa, Buffer, Blob, FormData, structuredClone, queueMicrotask, crypto.randomUUID(), process (shim: env.NODE_ENV, nextTick). NO DOM or Navigator APIs — use CDP for tab interaction.

Node.js built-ins (via require()): buffer, crypto, events, path, querystring, stream, string_decoder, url, util, zlib, assert. Blocked: net, http, https, child_process, worker_threads, vm.

Dynamic imports (via importModule(url)): HTTPS only, prefer https://esm.sh/{package}?target=node. Modules cached per session. Do NOT use await import().


Important Rules

  • Check docs of imported modules BEFORE using them.
  • Handle both default and named exports when format is unknown.
  • Use API.output() instead of console logging.
  • Use fetch for all network requests.
  • Implement error handling with fallbacks and sensible retries.
  • Split multi-step scripts into separate invocations.
  • After writing or updating mini app files, use API.openApp to reload the app and API.sendMessage / API.onMessage for messaging.

References

For detailed usage examples, see:

  • references/examples.md — Common sandbox patterns (filesystem, CDP, attachments, data processing, long-running tasks)
指导在Clodex中构建自定义交互式Web应用(Mini Apps)。涵盖目录结构、文件编写、Iframe沙箱约束、通过API打开/重载应用,以及利用postMessage实现沙箱与应用间的双向通信。
需要创建交互式Web界面 开发仪表盘或可视化工具 构建表单或交互组件 需要在浏览器标签页中渲染HTML/CSS/JS
apps/browser/bundled/plugins/mini-apps/SKILL.md
npx skills add mereyabdenbekuly-ctrl/clodex-ide --skill mini-apps -g -y
SKILL.md
Frontmatter
{
    "name": "mini-apps",
    "description": "Guide for building custom interactive web apps (\"mini apps\") displayed in browser tabs — scaffolding, iframe constraints, bidirectional messaging with the sandbox, and iteration workflows."
}

Mini Apps

Mini apps are custom interactive web apps that render in dedicated clodex browser tabs. Useful for dashboards, visualizations, forms, interactive tools, and any UI that benefits from rich HTML/CSS/JS beyond plain text.


Apps Directory (apps/)

The apps/ mount is always available with full read-write permissions. Each app lives in its own subfolder with index.html as the required entry point. Optional sibling assets (styles.css, script.js, images, etc.) are resolved via relative references.

apps/{appId}/
  index.html      ← entry point (required)
  styles.css      ← optional
  script.js       ← optional

Writing App Files

Create and edit app files (index.html, styles.css, script.js), then open or reload via the sandbox with await API.openApp("appId", { title: 'Readable title' }).


Iframe Constraints

  • Renders inside a dedicated browser tab with normal Clodex browser chrome.
  • The app itself is sandboxed in an app:// iframe inside a trusted clodex://internal/preview/{appId} shell.
  • Design responsively. Always include responsive base styles and a viewport meta tag.

Opening Apps (Sandbox)

Use API.openApp(appId, opts?) from the sandbox. The sandbox is used only for opening apps and communicating with them.

Option Type Default Description
pluginId string Opens a plugin app instead of an agent app
title string Human-readable tab breadcrumb label
target 'tab' 'tab' Explicit tab target; retained for compatibility/documentation
setActive boolean true Whether the preview tab should become active immediately
  • API.openApp() always opens an internal preview tab with a sandboxed app:// iframe.
  • Calling with the same appId opens a refreshed tab — use after editing files.

Non-authority UI Messaging

Apps and the sandbox communicate via postMessage.

This channel is only for ordinary app UI data. It does not carry Artifact Bridge authority, sessions, grants, or capability responses.

Sandbox → App: API.sendMessage(appId, data, opts?) — sends a JSON-serializable message to the active app.

App → Sandbox: API.onMessage(appId, callback, opts?) — registers a listener for messages the app sends via window.parent.postMessage(data, "*"). Returns an unsubscribe function. Listeners persist across IIFE executions; use globalThis to accumulate messages.

Inside the app (HTML/JS):

  • Receive: window.addEventListener("message", (e) => { /* e.data */ })
  • Send: window.parent.postMessage({ action: "clicked", id: 1 }, "*")

Artifact Bridge

Artifact Bridge is separate from the UI messaging channel above. When the current build, feature gates, and trusted host wiring permit it, an isolated app document receives one frozen API:

window.clodexArtifactBridge?: Readonly<{
  request(method: string, params: Record<string, unknown>): Promise<unknown>;
}>;

The API may be absent or every request may fail closed. Its presence is not a grant, and a previous successful request is not continuing permission.

Protocol methods include:

  • getCapabilities
  • callMcpTool — only a specifically granted MCP tool whose effective policy is automatic allow, whose descriptor is read-only, and which is not destructive
  • askAgent — bounded prompt and response
  • runAutomation — launch an existing automation by ID

Client usage

Minimal fail-closed client shape:

async function callClodex(method, params = {}) {
  const bridge = window.clodexArtifactBridge;
  if (!bridge || typeof bridge.request !== "function") {
    throw new Error("Artifact Bridge is unavailable");
  }
  return await bridge.request(method, params);
}

try {
  const capabilities = await callClodex("getCapabilities", {});
  // Render only actions actually reported as available.
} catch {
  // Render a non-privileged fallback UI.
}

Generated app JavaScript never receives the underlying MessagePort, session ID, navigation epoch, document binding, or connect envelope. Those values stay inside the isolated preload and trusted main-process broker. Do not implement an Artifact Bridge client with window.postMessage, do not invent or persist session values, and do not attempt to expose or transfer the hidden port.

Handle denial, revocation, expiry, navigation, timeout, and unavailable-host errors. Always provide a non-privileged fallback UI.


Best Practices

  • Sandbox usage: Use the sandbox only for openApp, sendMessage, and onMessage.
  • Responsive design: Support both narrow and wide tab widths. Use max-width: 100%, overflow-x: hidden, box-sizing: border-box.
  • Viewport meta tag: Always include <meta name="viewport" content="width=device-width, initial-scale=1">.
  • File organization: index.html as entry point. Split CSS and JS into separate files for maintainability.
  • Message protocol: Define a clear action field to distinguish message types.
  • Cleanup listeners: Unsubscribe from API.onMessage when interaction is complete.
  • Error handling: Validate incoming messages on both sides. Gracefully handle unexpected data.
  • Artifact Bridge boundary: Use only the frozen window.clodexArtifactBridge.request API; never use UI postMessage as a capability channel.

References

For detailed examples, see:

  • references/examples.md — Full mini app examples (minimal app, multi-file app, interactive picker with messaging)
集成阿里 Open Code Review CLI,支持代码审查、PR审核及安全扫描。通过ocr命令分析git差异或工作区,输出结构化JSON结果,按严重性报告问题并辅助修复,严格遵循安全规范与路径过滤。
用户请求AI代码审查 用户请求PR评审 用户请求安全审查 用户请求bug风险检查 用户请求回归测试 用户明确要求运行alibaba/open-code-review
apps/browser/bundled/plugins/open-code-review/SKILL.md
npx skills add mereyabdenbekuly-ctrl/clodex-ide --skill open-code-review -g -y
SKILL.md
Frontmatter
{
    "name": "open-code-review",
    "description": "Use Alibaba Open Code Review (`ocr`) to review git diffs, staged changes, branches, or scan a workspace, then summarize findings and optionally fix high-confidence issues."
}

Open Code Review Plugin

Use this skill when the user asks for an AI code review, PR review, security review, bug-risk review, regression check, or asks to run alibaba/open-code-review.

This plugin integrates Alibaba Open Code Review through its local CLI command ocr. It should run inside the mounted workspace, never against unrelated directories.

Prerequisites

First check whether the CLI exists:

ocr --version

If ocr is missing, tell the user to install Alibaba Open Code Review from https://github.com/alibaba/open-code-review and stop. Do not fabricate review results.

Review Modes

Prefer the narrowest review that matches the request:

# Review current working-tree changes.
ocr review

# Review staged changes only.
ocr review --staged

# Review changes against a base branch.
ocr review --base main

# Machine-readable output for agent post-processing.
ocr review --format json --audience agent

# Broader workspace scan when the user asks for a full audit.
ocr scan --format json --audience agent

If the repository's default branch is not obvious, inspect git first:

git rev-parse --show-toplevel
git branch --show-current
git remote show origin

Workflow

  1. Confirm the workspace is a git repository.
  2. Inspect the change scope with git status --short and, when useful, git diff --stat.
  3. Run ocr review --format json --audience agent for changed files, or ocr scan --format json --audience agent for an explicit full audit.
  4. Parse the JSON when possible. If the command only returns text, summarize the text faithfully.
  5. Report findings ordered by severity, with file paths and line numbers when available.
  6. If the user asks to fix findings, use normal Clodex edit tools so changes become Pending Edits for user review.

Output Style

Lead with actionable findings. Keep summaries short.

Use this shape:

Found N issue(s).

- [High] path/to/file.ts:42 — Issue title.
  Impact: ...
  Fix: ...

No high-confidence issues found.

When no issues are found, say that clearly and mention the reviewed scope.

Guardrails

  • Do not apply OCR suggestions blindly. Verify every proposed fix against the code.
  • Do not hide uncertain findings. Mark them as "Needs verification".
  • Do not show internal mount prefixes like w48b2/; use project-relative paths.
  • Do not run full scans unless the user asks for a broad audit, because they can be slow and token-heavy.
用于在Clodex中调用OpenManus执行长期自主研究、浏览器探索或数据分析等复杂任务。需配置路径,通过runOpenManus工具运行,保持提示词具体,结果视为外部报告,代码修改使用标准工具处理。
用户明确要求运行OpenManus 需要Manus风格的自动化任务 涉及长时间自主研究 复杂的Python/浏览器/数据工作流
apps/browser/bundled/plugins/openmanus/SKILL.md
npx skills add mereyabdenbekuly-ctrl/clodex-ide --skill openmanus -g -y
SKILL.md
Frontmatter
{
    "name": "openmanus",
    "description": "Use OpenManus as an external autonomous Python agent for long-running research, browser-style exploration, data analysis, or multi-step execution tasks from inside a mounted workspace."
}

OpenManus Plugin

Use this skill when the user explicitly asks to run OpenManus, Manus-style automation, a long autonomous research task, or a Python/browser/data workflow that should run outside the normal Clodex agent loop.

Prerequisites

OpenManus is not vendored into Clodex by default. Before running it, make sure one of these exists:

  • OPENMANUS_HOME points to a local FoundationAgents/OpenManus checkout that contains main.py.
  • bundled/plugins/openmanus/main.py exists in the installed Clodex app.

Optionally set OPENMANUS_PYTHON to the Python executable. If unset, Clodex uses python3.

Tool

Use the runOpenManus tool.

Parameters:

  • prompt: the autonomous task for OpenManus.
  • mountPrefix: the mounted workspace prefix where the task should operate.
  • timeoutMs: optional timeout, max 30 minutes.

Workflow

  1. Identify the current workspace mount prefix from the environment context or by listing files.
  2. Call runOpenManus with a narrow, concrete prompt.
  3. Treat its output as an external agent report.
  4. If code changes are needed after OpenManus finishes, use normal Clodex edit tools (write/multiEdit) so the user gets Pending Edits.

Guardrails

  • Do not claim OpenManus changed files unless its output proves it.
  • Prefer normal Clodex tools for small code edits.
  • Do not run OpenManus against unrelated paths.
  • Keep prompts narrow; OpenManus can run for a long time.
PostHog插件通过REST API提供分析数据查询、事件与用户管理、功能标志及实验操作等功能。支持HogQL查询,需配置API密钥并识别US/EU区域以构建请求。
需要查询PostHog分析数据或运行HogQL查询 需要管理功能标志、实验、受众群体或调查 需要查找特定用户(Person)或查看事件详情
apps/browser/bundled/plugins/posthog/SKILL.md
npx skills add mereyabdenbekuly-ctrl/clodex-ide --skill posthog -g -y
SKILL.md
Frontmatter
{
    "name": "posthog",
    "description": "Complete guide for the PostHog plugin — REST API access for querying analytics with HogQL, managing feature flags, inspecting events and persons, reading insights, experiments, cohorts, surveys, and more."
}

PostHog Plugin

This plugin provides access to the PostHog REST API on the user's behalf, using a stored Personal API Key (phx_...).

Capabilities:

  • Run arbitrary HogQL (SQL-like) queries against the analytics database
  • List, filter, and inspect events
  • Look up persons (users) by ID, email, or properties
  • Read saved insights (trends, funnels, retention, paths, etc.)
  • CRUD feature flags
  • Read and manage experiments (A/B tests)
  • Read cohorts, annotations, actions, dashboards, and surveys
  • Query web analytics stats

Authentication

Request the stored PostHog credential. The personalApiKey field is an opaque placeholder — the sandbox fetch proxy substitutes the real value automatically. Never decode or transform it.

const cred = await API.getCredential('posthog-pat');
if (!cred) {
  return 'PostHog credential is not configured. Ask the user to create a Personal API Key at Settings → Personal API Keys in their PostHog dashboard, then store it in Settings.';
}

Region & Base URL

PostHog Cloud has two regions. The agent must determine which one the user is on:

Region Base URL
US Cloud https://us.posthog.com
EU Cloud https://eu.posthog.com

How to determine the region:

  • Ask the user, OR
  • Look at any PostHog dashboard tab open in the browser — the URL hostname reveals the region.

Store the base URL in a variable and use it for all requests.


Making Requests

All API paths are relative to the region base URL. Always pass the key as a Bearer header:

const BASE = 'https://us.posthog.com'; // or https://eu.posthog.com

async function phGet(path) {
  const res = await fetch(`${BASE}${path}`, {
    headers: {
      Authorization: `Bearer ${cred.personalApiKey}`,
      'Content-Type': 'application/json',
    },
  });
  if (!res.ok) throw new Error(`PostHog API ${res.status}: ${await res.text()}`);
  return res.json();
}

async function phPost(path, body) {
  const res = await fetch(`${BASE}${path}`, {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${cred.personalApiKey}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(body),
  });
  if (!res.ok) throw new Error(`PostHog API ${res.status}: ${await res.text()}`);
  return res.json();
}

async function phPatch(path, body) {
  const res = await fetch(`${BASE}${path}`, {
    method: 'PATCH',
    headers: {
      Authorization: `Bearer ${cred.personalApiKey}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(body),
  });
  if (!res.ok) throw new Error(`PostHog API ${res.status}: ${await res.text()}`);
  return res.json();
}

async function phDelete(path) {
  const res = await fetch(`${BASE}${path}`, {
    method: 'DELETE',
    headers: {
      Authorization: `Bearer ${cred.personalApiKey}`,
      'Content-Type': 'application/json',
    },
  });
  if (!res.ok) throw new Error(`PostHog API ${res.status}: ${await res.text()}`);
  if (res.status === 204) return null;
  return res.json();
}

Identifying the Project

Most endpoints require a project ID — a numeric identifier visible in the PostHog dashboard URL:

https://us.posthog.com/project/{project_id}/...

If the user hasn't specified a project ID:

  1. Check any open PostHog dashboard tab in the browser — the project ID is in the URL path.
  2. Ask the user directly.

Key Endpoints

All paths below are relative to the base URL (e.g. https://us.posthog.com).

HogQL Queries (The Primary Analytical Tool)

Run a HogQL query POST /api/projects/{project_id}/query Body:

{
  "query": {
    "kind": "HogQLQuery",
    "query": "SELECT event, count() AS cnt FROM events WHERE timestamp > now() - interval 7 day GROUP BY event ORDER BY cnt DESC LIMIT 20"
  }
}

Response contains results (array of rows), columns (column names), and types (column types).

HogQL is a SQL-like query language on top of ClickHouse. It can query events, persons, sessions, groups, and more. This is the most powerful endpoint for analytics — prefer it over the REST list endpoints when aggregation or filtering is needed.

Run a web stats query POST /api/projects/{project_id}/query Body:

{
  "query": {
    "kind": "WebStatsTableQuery",
    "dateRange": { "date_from": "2025-01-01", "date_to": "2025-01-31" },
    "breakdownBy": "Page",
    "limit": 10
  }
}

Common HogQL Patterns

Top events in the last 7 days:

SELECT event, count() AS cnt
FROM events
WHERE timestamp > now() - interval 7 day
GROUP BY event
ORDER BY cnt DESC
LIMIT 20

Unique users in the last 30 days:

SELECT uniq(person_id) AS unique_users
FROM events
WHERE timestamp > now() - interval 30 day

Pageviews by path:

SELECT properties.$pathname AS path, count() AS views
FROM events
WHERE event = '$pageview'
  AND timestamp > now() - interval 7 day
GROUP BY path
ORDER BY views DESC
LIMIT 20

Daily active users trend:

SELECT toDate(timestamp) AS day, uniq(person_id) AS dau
FROM events
WHERE timestamp > now() - interval 30 day
GROUP BY day
ORDER BY day

Funnel analysis (pageview → signup):

SELECT
  uniq(person_id) AS total_users,
  uniqIf(person_id, event = '$pageview') AS step1,
  uniqIf(person_id, event = 'signed_up') AS step2
FROM events
WHERE timestamp > now() - interval 7 day

User sessions with duration:

SELECT
  session.session_id,
  min(timestamp) AS start,
  max(timestamp) AS end,
  dateDiff('second', min(timestamp), max(timestamp)) AS duration_s,
  count() AS event_count
FROM events
WHERE timestamp > now() - interval 1 day
  AND session.session_id IS NOT NULL
GROUP BY session.session_id
ORDER BY duration_s DESC
LIMIT 20

Events for a specific person:

SELECT event, timestamp, properties
FROM events
WHERE person_id = 'PERSON_UUID'
ORDER BY timestamp DESC
LIMIT 50

Events

List events GET /api/projects/{project_id}/events/ Query params: ?event=<event_name>, ?person_id=<id>, ?limit=<n>, ?after=<cursor> Returns paginated event list.

Get a single event GET /api/projects/{project_id}/events/{event_id}/

Persons

List persons GET /api/projects/{project_id}/persons/ Query params: ?search=<term>, ?email=<email>, ?limit=<n>

Get a specific person GET /api/projects/{project_id}/persons/{person_id}/

Insights (Saved Analytics)

List insights GET /api/projects/{project_id}/insights/ Query params: ?search=<term>, ?short_id=<id>, ?limit=<n> Returns saved trends, funnels, retention, paths, lifecycle, and stickiness queries.

Get a specific insight GET /api/projects/{project_id}/insights/{id}/ Returns the insight definition and its last-calculated results.

Feature Flags

List feature flags GET /api/projects/{project_id}/feature_flags/ Query params: ?search=<term>, ?active=true

Get a specific feature flag GET /api/projects/{project_id}/feature_flags/{id}/

Create a feature flag POST /api/projects/{project_id}/feature_flags/ Body:

{
  "name": "My Flag",
  "key": "my-flag-key",
  "description": "Enables the new onboarding flow",
  "filters": { "groups": [] },
  "active": true
}

Update a feature flag PATCH /api/projects/{project_id}/feature_flags/{id}/ Body: partial update (e.g. { "active": false } to disable).

Delete a feature flag DELETE /api/projects/{project_id}/feature_flags/{id}/

Experiments

List experiments GET /api/projects/{project_id}/experiments/

Get a specific experiment GET /api/projects/{project_id}/experiments/{id}/

Get experiment results GET /api/projects/{project_id}/experiments/{id}/results/

Cohorts

List cohorts GET /api/projects/{project_id}/cohorts/

Get a specific cohort GET /api/projects/{project_id}/cohorts/{id}/

Actions

List actions GET /api/projects/{project_id}/actions/

Get a specific action GET /api/projects/{project_id}/actions/{id}/

Annotations

List annotations GET /api/projects/{project_id}/annotations/

Create an annotation POST /api/projects/{project_id}/annotations/ Body:

{
  "content": "Deployed v2.1.0",
  "date_marker": "2025-03-15T12:00:00Z",
  "scope": "project"
}

Update an annotation PATCH /api/projects/{project_id}/annotations/{id}/

Delete an annotation DELETE /api/projects/{project_id}/annotations/{id}/

Dashboards

List dashboards GET /api/projects/{project_id}/dashboards/

Get a specific dashboard GET /api/projects/{project_id}/dashboards/{id}/

Surveys

List surveys GET /api/projects/{project_id}/surveys/

Get a specific survey GET /api/projects/{project_id}/surveys/{id}/


Common Patterns

Query top events for the past week

const cred = await API.getCredential('posthog-pat');
const BASE = 'https://us.posthog.com';
const projectId = 12345;

const result = await phPost(`/api/projects/${projectId}/query`, {
  query: {
    kind: 'HogQLQuery',
    query: `SELECT event, count() AS cnt FROM events WHERE timestamp > now() - interval 7 day GROUP BY event ORDER BY cnt DESC LIMIT 20`
  }
});
return { columns: result.columns, rows: result.results };

Look up a person by email

const cred = await API.getCredential('posthog-pat');
const BASE = 'https://us.posthog.com';
const projectId = 12345;

const persons = await phGet(`/api/projects/${projectId}/persons/?email=user@example.com`);
return persons.results;

List active feature flags

const cred = await API.getCredential('posthog-pat');
const BASE = 'https://us.posthog.com';
const projectId = 12345;

const flags = await phGet(`/api/projects/${projectId}/feature_flags/?active=true`);
return flags.results.map(f => ({
  id: f.id,
  key: f.key,
  name: f.name,
  active: f.active,
  rollout: f.rollout_percentage,
}));

Create a feature flag

const cred = await API.getCredential('posthog-pat');
const BASE = 'https://us.posthog.com';
const projectId = 12345;

const flag = await phPost(`/api/projects/${projectId}/feature_flags/`, {
  name: 'New Onboarding',
  key: 'new-onboarding',
  description: 'Enables the redesigned onboarding flow',
  filters: { groups: [{ properties: [], rollout_percentage: 50 }] },
  active: true,
});
return flag;

Get daily active users trend

const cred = await API.getCredential('posthog-pat');
const BASE = 'https://us.posthog.com';
const projectId = 12345;

const result = await phPost(`/api/projects/${projectId}/query`, {
  query: {
    kind: 'HogQLQuery',
    query: `SELECT toDate(timestamp) AS day, uniq(person_id) AS dau FROM events WHERE timestamp > now() - interval 30 day GROUP BY day ORDER BY day`
  }
});
return { columns: result.columns, rows: result.results };

List experiments and their status

const cred = await API.getCredential('posthog-pat');
const BASE = 'https://us.posthog.com';
const projectId = 12345;

const experiments = await phGet(`/api/projects/${projectId}/experiments/`);
return experiments.results.map(e => ({
  id: e.id,
  name: e.name,
  start_date: e.start_date,
  end_date: e.end_date,
}));

Read insights (saved analytics)

const cred = await API.getCredential('posthog-pat');
const BASE = 'https://us.posthog.com';
const projectId = 12345;

const insights = await phGet(`/api/projects/${projectId}/insights/?limit=10`);
return insights.results.map(i => ({
  id: i.id,
  name: i.name,
  short_id: i.short_id,
  filters: i.filters,
  last_refresh: i.last_refresh,
}));

Important Rules

  • Always call API.getCredential('posthog-pat') before any PostHog API call. If it returns null, prompt the user to configure it.
  • Always determine the user's region (US or EU) before making requests. Check open PostHog tabs or ask.
  • Always identify the target project ID (numeric). Check open PostHog tabs (it's in the URL path: /project/{id}/...) or ask.
  • Prefer HogQL queries (POST /api/projects/{id}/query) for analytical questions — they are far more flexible than REST list endpoints.
  • Use LIMIT clauses in HogQL queries to keep responses manageable.
  • When creating or modifying feature flags, confirm details with the user first.
  • When deleting resources (flags, annotations, etc.), always confirm with the user first.
  • Treat API keys as sensitive — never log or display them.
  • Paginated responses use next / previous URLs. Follow next to get more pages.
  • The PostHog API may rate-limit requests. Avoid tight loops; add short delays between batch requests.
用于使用Remotion创建或编辑视频的完整流程技能。涵盖选择风格指南、初始化项目、定义规格、构建视频结构(基于故事板和布局)、收集素材及迭代优化,确保代码与风格指南同步更新。
用户请求制作视频 用户需要编辑现有Remotion视频项目 用户询问如何配置Remotion风格指南
apps/browser/bundled/plugins/remotion/SKILL.md
npx skills add mereyabdenbekuly-ctrl/clodex-ide --skill create-remotion-video -g -y
SKILL.md
Frontmatter
{
    "name": "create-remotion-video",
    "description": "Create or edit video with Remotion. First-party clodex + Remotion skill. Contains full video-making process.",
    "user-invocable": false,
    "agent-invocable": true
}

Prerequisites for creating video

  • Need at least one connected workspace where styleguide + video project can live. If none, ask user to connect one.
  • Need multiple workspaces if styleguide lives elsewhere or user wants cross-workspace references.

Process

Video work has process. Keep progress in project-root progress.md. ALWAYS FOLLOW EXACTLY THIS PROCESS!

Overview

  1. Select styleguide for video

    • Find skills for styleguides for remotion videos.
    • ALWAYS ask user which one to use.
    • If none exists, create new one with this process.
    • Do NOT continue until styleguide choice is known.
    • Keep correct styleguide in mind for whole editing session.
    • Store chosen styleguide in spec.yaml.
    • If user asks for global style changes like typography/colors, FIRST update styleguide, THEN update video.
    • Keep all colors, typography, etc. in shared TS file.
    • Shared TS file MUST include all specs from styleguide DESIGN.md. Global changes should flow from this file.
  2. Create project

    • Make clean standalone Remotion project in own folder.
    • IGNORE remotion-best-practices skill. Already included in this skill.
    • Start with minimal simple video.
    • If user gave no structure, ask where project should go.
    • Do NOT use workspace root unless user says so.
    • Install deps with workspace package manager. If fresh, prefer pnpm. npm only fallback. Respect user prefs from chat/skills.
    • Keep code, assets, story, copy in this folder.
    • Start dev server in shell. ALWAYS open it in new browser tab.
  3. Create spec.yaml + progress.md in project folder.

  4. Spec video

    • Define purpose + content.
    • Define publishing channels. Can be multiple.
    • Define target length.
  5. Create video structure with empty/prefilled sequences from storyboards

    • Prefer storyboard templates from styleguide. If none exist, create them. -New storyboard templates MUST be added to styleguide.
    • Update main styleguide SKILL.md too.
    • First create template spec. Then create code.
    • If user changes storyboard, FIRST update template in styleguide, THEN update code.
    • For sequences, use layout definitions from styleguide. If missing, create new layout.
    • ALWAYS define new layouts with this structure.
    • Update main styleguide SKILL.md too.
    • First create layout spec. Then create code.
    • On layout/template change requests, FIRST update styleguide definition, THEN update code.
    • For reusable elements like logos, use elements from styleguide.
    • New reusable elements MUST be defined like this.
    • Show user. Iterate if needed.
    • Storyboard ALWAYS source of truth for structure.
  6. Collect assets

    • Tell user what assets are needed.
    • If asset can be built directly, build it.
    • Tell user what they must gather FIRST.
    • Can reuse React components from other monorepo parts.
  7. Build video

    • Iterate until video compiles clean.
    • Check website logs for issues. If website closed, reopen and check.
    • After EVERY video change, check logs again. Logs MUST stay clean.
  8. Iterate on video

    • Ask user to watch all variants: portrait, landscape, etc.
    • If requested style change conflicts with styleguide, ask: update guide or make one-off?
    • Always update storyboard if scene order, length, or structure changes.
    • After feedback, improve and ask user to rewatch.
  9. Channel copy + thumbnails

    • Create platform copy for all relevant channels in project-root publishing.md.
    • Create thumbnails for all relevant channels.
  10. Render videos + thumbnails

    • Tell user to start render in Remotion Studio.
    • Tell user which render-setting overrides are needed.
    • Prefer high quality.
    • Standard HQ: CRF=5, JPEG=95 (always store in remotion config).
    • Help tune if user unhappy.

User may not know all details. Make good suggestions when useful, but ALWAYS ask if user likes proposal.

Guide user step by step. Always explain what you will do before doing it.

Special files

progress.md

  • Tracks progress + key decisions.
  • For each step, mark: not started, in progress, finished.
  • Keep compact.
  • Note important decisions + blockers.
  • Use it to see what remains.

spec.yaml

Defines high-level project intent: content, purpose, length, channels, style, music style, voiceover. Details live in code.

If spec.yaml changes, ALWAYS refactor video code to match.

Examples:

title: Feature launch for new credit card
length: 25-35 seconds
content: real life scenes, text overlay, 3d-style animated card in intro, animated outro
voiceover: no
music: like normal video style
channels: youtube, youtube-shorts, tiktok, linkedin, instagram
transparent-bg: no
style: reuse from skill `my-company-video-style`
title: Mini animation with car
length: 5 seconds
content: 360° rotation around 3d car model
voiceover: no
music: none
channels: none
transparent-bg: yes
style: none

Publishing channels

  • Build for channels user wants.
  • If many channels share settings, reuse shared setup.
  • If channel settings conflict, create multiple composition variants.
  • Read channel guidelines for best practices (resolution, format, etc.) - YouTube - YouTube Shorts - Facebook - LinkedIn - X/Twitter - Instagram - TikTok

Optimize for landscape/portrait

  • Video may ship in portrait and landscape.
  • Create multiple composition variants when needed.
  • Keep storyboard in sync.
  • Optimize layout for each aspect ratio.

Background music

  • If fitting, ask user if bg music is wanted.
  • Suggest royalty-free music from internet, or Suno for music generation.

Copywriting

Adhere to following rules when generating text for header, titles, hero slides etc:

  • Vary sentence lengths. Mix 3-word punchy sentences with long ones.
  • Use industry slang, "dirty" verbs, and specific nouns. AVOID Buzzwords like "Robust", "seamless", "tapestry", "delve", "pivotal" unless styleguide or user asks for them.
  • Use natural pivots or just jump to the next point. Minimize use of standard transitions like: "But here's the kicker,..." or "Why does this matter? ..."
  • Avoid unnecessary "Summary loops" starting with "Ultimately" or "In essence."
  • Do not use antithesis, “not X but Y” constructions, or dramatic contrast framing. Avoid phrases like “This is not just…,” and “Not only… but also…”.

Thumbnails

  • Some channels need thumbnails.
  • Sometimes need multiple thumbnails for A/B tests.
  • Channel decides format.
  • Use Remotion static composition for thumbnails.
  • Put the thumbnail compositions into the same root project as the source video itself.

Two paths:

  1. Reuse scene from video.
    • Overlay/resize scene text if needed.
    • Thumbnail text must stay readable at small size.
  2. Make fresh scene.
    • Reuse style + assets.
    • Keep scene clean.

Completion

Video work is done when videos, thumbnails, and platform copy all exist and are worked out.

  • ALWAYS check what work remains when finishing part of process.
  • ALWAYS keep progress.md updated.
  • ALWAYS keep spec.yaml in sync.
  • ALWAYS update styleguide if global style changes happen, or new layouts/storyboards get created.

Remotion guide

New project setup

In empty folder/workspace with no Remotion project, scaffold one:

npx create-video@latest --yes --blank --no-tailwind my-video

Replace my-video with good name. Creates new folder with full project.

Start Studio to preview video: npx remotion studio

Add packages

npx remotion add ...
bunx remotion add ...
yarn remotion add ...
pnpm exec remotion add ...

Set render defaults

// remotion.config.ts
import { Config } from "@remotion/cli/config";
Config.setVideoImageFormat("png");
Config.setPixelFormat("yuva444p10le");
Config.setCodec("prores");
Config.setProResProfile("4444");

One-frame render check

Can render one frame with CLI to sanity-check layout, color, timing. Skip for trivial edits, pure refactors, or when Studio/prior renders enough.

npx remotion still [composition-id] --scale=0.25 --frame=30

At 30 fps, --frame=30 = one-second mark. --frame is zero-based.

If working with captions/subtitles, read ./references/rules/subtitles.md.

If trimming video or doing silence detection, read ./references/rules/ffmpeg.md.

If detecting + trimming silent audio/video segments, read ./references/rules/silence-detection.md.

If visualizing audio: spectrum bars, waveforms, bass-react effects, read ./references/rules/audio-visualization.md.

If using sound effects, read ./references/rules/sfx.md.

Respect workspace code style. If none, use normal pretty formatting. Examples may be minified. Your code should not be.

How to use

Read rule files as needed:

Important rules

  • Default to 30fps
  • Unless explicitly asked for transparency, ALWAYS add bg color to project and composition
  • ALWAYS COPY image/video/audio assets into public folder when using them
  • ALWAYS PREFER existing react components from reusable packages, if in monorepo
  • ALWAYS PREFER downloading/reusing assets from websites and brand kits instead of re-creating
    • NEVER recreate Logos manually (unless it's only text in a known font). Copy/Download logo instead.
    • NEVER recreate example gradient images. Copy/Download instead.
  • Unless styleguides say different, do these things:
    • Fast and crisp transitions instead of slow fade-ins
    • Fade-in and fade-out should always include Blurring, Opacity and Translate
    • PREFER Word-by-word fade-in over Line-By-Line. Opt-in to typewriter for input demos or special technical cases
    • Light text/logos over Light BG must have slight shadow for contrast
    • NEVER use button-style CTA text formatting
      • Minimize use of button-style formatting of text unless it's in a "demo"-section of the video
    • Dynamic scene durations:
      • More text or diagrams must lead to longer scenes
      • Short outro scenes unless requested differently for social media channel
      • If voiceover exist, match it's length for scenes
      • If with bg music (only), cut on beat (ask user for BPM of song)
    • Create scene transitions with continuity (i.e. keep elements and move them instead of fading everything out)
      • Avoid presentation-like feeling of a video. Instead it must feel like a continuous story.
Supabase插件,通过PAT调用管理API。支持列出项目、执行SQL、生成TS类型、管理边缘函数/密钥/迁移及检查健康状态。提供认证与HTTP请求封装示例,需指定Project Ref进行资源操作。
用户需要查询或管理Supabase项目 用户希望在Supabase数据库中执行SQL查询 用户需要配置或查看Supabase边缘函数 用户需要管理Supabase项目密钥或环境变量 用户需要应用数据库迁移或生成TypeScript类型
apps/browser/bundled/plugins/supabase/SKILL.md
npx skills add mereyabdenbekuly-ctrl/clodex-ide --skill supabase -g -y
SKILL.md
Frontmatter
{
    "name": "supabase",
    "description": "Complete guide for the Supabase plugin — Management API access for running SQL queries, listing projects, managing edge functions, secrets, migrations, and inspecting project health."
}

Supabase Plugin

This plugin provides access to the Supabase Management API on the user's behalf, using a stored Personal Access Token (PAT).

Capabilities:

  • List and inspect Supabase projects
  • Run arbitrary SQL against any project database
  • Generate TypeScript types from the database schema
  • List, create, and manage edge functions
  • Manage project secrets
  • List and apply database migrations
  • Check project health and performance advisors
  • Retrieve project API keys and configuration

Authentication

Request the stored Supabase credential. The accessToken field is an opaque placeholder — the sandbox fetch proxy substitutes the real value automatically. Never decode or transform it.

const cred = await API.getCredential('supabase-pat');
if (!cred) {
  return 'Supabase credential is not configured. Ask the user to create a Personal Access Token at supabase.com/dashboard/account/tokens, then store it in Settings.';
}

Making Requests

All requests go to https://api.supabase.com/v1/. Always pass the token as a Bearer header:

async function sbGet(path) {
  const res = await fetch(`https://api.supabase.com/v1${path}`, {
    headers: {
      Authorization: `Bearer ${cred.accessToken}`,
      'Content-Type': 'application/json',
    },
  });
  if (!res.ok) throw new Error(`Supabase API ${res.status}: ${await res.text()}`);
  return res.json();
}

async function sbPost(path, body) {
  const res = await fetch(`https://api.supabase.com/v1${path}`, {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${cred.accessToken}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(body),
  });
  if (!res.ok) throw new Error(`Supabase API ${res.status}: ${await res.text()}`);
  return res.json();
}

async function sbPatch(path, body) {
  const res = await fetch(`https://api.supabase.com/v1${path}`, {
    method: 'PATCH',
    headers: {
      Authorization: `Bearer ${cred.accessToken}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(body),
  });
  if (!res.ok) throw new Error(`Supabase API ${res.status}: ${await res.text()}`);
  return res.json();
}

async function sbDelete(path) {
  const res = await fetch(`https://api.supabase.com/v1${path}`, {
    method: 'DELETE',
    headers: {
      Authorization: `Bearer ${cred.accessToken}`,
      'Content-Type': 'application/json',
    },
  });
  if (!res.ok) throw new Error(`Supabase API ${res.status}: ${await res.text()}`);
  if (res.status === 204) return null;
  return res.json();
}

Identifying the Project

Most endpoints require a project ref — the unique identifier for a Supabase project (e.g. ypnrbornkuwxlvjkgubi). It appears in the project's dashboard URL:

https://supabase.com/dashboard/project/{ref}/...

If the user hasn't specified a project ref, list their projects first:

const projects = await sbGet('/projects');
return projects.map(p => ({ id: p.id, name: p.name, region: p.region }));

Then ask the user which project to target.


Key Endpoints

Projects

List all projects GET /v1/projects Returns all projects the PAT has access to, including id, name, region, and status.

Get a specific project GET /v1/projects/{ref} Returns full project details.

Get project health GET /v1/projects/{ref}/health Returns the current health status of the project.

Get project API keys GET /v1/projects/{ref}/api-keys Returns the project's API keys (anon, service_role, etc.).

Running SQL (The Primary Tool)

Execute a SQL query POST /v1/projects/{ref}/database/query Body: { "query": "SELECT * FROM public.users LIMIT 10" }

This is the most powerful endpoint — it runs arbitrary SQL against the project's Postgres database with full privileges.

Execute a read-only SQL query POST /v1/projects/{ref}/database/query/read-only Body: { "query": "SELECT ..." } Same as above but restricted to read-only operations. Use this for SELECT queries to avoid accidental mutations.

Common SQL Patterns

List all tables in the public schema:

SELECT table_name, table_type
FROM information_schema.tables
WHERE table_schema = 'public'
ORDER BY table_name;

Describe a table's columns:

SELECT column_name, data_type, is_nullable, column_default
FROM information_schema.columns
WHERE table_schema = 'public' AND table_name = 'your_table'
ORDER BY ordinal_position;

List all schemas:

SELECT schema_name
FROM information_schema.schemata
WHERE schema_name NOT IN ('pg_catalog', 'information_schema', 'pg_toast')
ORDER BY schema_name;

List foreign key relationships:

SELECT
  tc.table_name,
  kcu.column_name,
  ccu.table_name AS foreign_table,
  ccu.column_name AS foreign_column
FROM information_schema.table_constraints tc
JOIN information_schema.key_column_usage kcu
  ON tc.constraint_name = kcu.constraint_name
JOIN information_schema.constraint_column_usage ccu
  ON tc.constraint_name = ccu.constraint_name
WHERE tc.constraint_type = 'FOREIGN KEY'
  AND tc.table_schema = 'public';

List indexes on a table:

SELECT indexname, indexdef
FROM pg_indexes
WHERE tablename = 'your_table' AND schemaname = 'public';

List RLS policies:

SELECT tablename, policyname, permissive, roles, cmd, qual, with_check
FROM pg_policies
WHERE schemaname = 'public';

Count rows in a table:

SELECT count(*) FROM public.your_table;

Insert a row:

INSERT INTO public.your_table (col1, col2) VALUES ('val1', 'val2') RETURNING *;

Update rows:

UPDATE public.your_table SET col1 = 'new_val' WHERE id = 123 RETURNING *;

Delete rows:

DELETE FROM public.your_table WHERE id = 123 RETURNING *;

TypeScript Types

Generate TypeScript types from the database schema GET /v1/projects/{ref}/types/typescript Returns TypeScript type definitions matching the current database schema. Useful for keeping frontend types in sync.

Edge Functions

List edge functions GET /v1/projects/{ref}/functions Returns all deployed edge functions with their slugs, status, and metadata.

Get a specific edge function GET /v1/projects/{ref}/functions/{function_slug}

Delete an edge function DELETE /v1/projects/{ref}/functions/{function_slug}

Secrets

List project secrets GET /v1/projects/{ref}/secrets Returns secret names (values are redacted).

Create a secret POST /v1/projects/{ref}/secrets Body: { "name": "MY_SECRET", "value": "secret_value" }

Delete secrets DELETE /v1/projects/{ref}/secrets Body: ["SECRET_NAME_1", "SECRET_NAME_2"]

Database Migrations

List migrations GET /v1/projects/{ref}/database/migrations

Create a migration POST /v1/projects/{ref}/database/migrations Body: { "name": "add_users_table", "statements": ["CREATE TABLE ..."] }

Configuration

Get PostgREST config GET /v1/projects/{ref}/postgrest Returns the PostgREST configuration (exposed schemas, max rows, etc.).

Update PostgREST config PATCH /v1/projects/{ref}/postgrest Body: { "max_rows": 1000 }

Get database Postgres config GET /v1/projects/{ref}/config/database/postgres

Get disk utilization GET /v1/projects/{ref}/config/disk/util

Advisors

Performance advisor GET /v1/projects/{ref}/advisors/performance Returns performance lints (unindexed foreign keys, unused indexes, etc.).

Security advisor GET /v1/projects/{ref}/advisors/security Returns security lints. Add ?lint_type=sql to filter.

Organizations

List organizations GET /v1/organizations

Get organization details GET /v1/organizations/{slug}

List organization members GET /v1/organizations/{slug}/members

List organization projects GET /v1/organizations/{slug}/projects


Common Patterns

Explore a project's database schema

const cred = await API.getCredential('supabase-pat');
const ref = 'YOUR_PROJECT_REF';

// List all tables
const tables = await sbPost(`/projects/${ref}/database/query/read-only`, {
  query: `SELECT table_name, table_type FROM information_schema.tables WHERE table_schema = 'public' ORDER BY table_name`
});
return tables;

Query data from a table

const cred = await API.getCredential('supabase-pat');
const ref = 'YOUR_PROJECT_REF';

const result = await sbPost(`/projects/${ref}/database/query/read-only`, {
  query: `SELECT * FROM public.users ORDER BY created_at DESC LIMIT 20`
});
return result;

Insert data

const cred = await API.getCredential('supabase-pat');
const ref = 'YOUR_PROJECT_REF';

const result = await sbPost(`/projects/${ref}/database/query`, {
  query: `INSERT INTO public.todos (title, completed) VALUES ('Buy milk', false) RETURNING *`
});
return result;

Check project health and performance

const cred = await API.getCredential('supabase-pat');
const ref = 'YOUR_PROJECT_REF';

const health = await sbGet(`/projects/${ref}/health`);
const perf = await sbGet(`/projects/${ref}/advisors/performance`);
const disk = await sbGet(`/projects/${ref}/config/disk/util`);

return { health, performanceLints: perf.length, disk };

Generate and display TypeScript types

const cred = await API.getCredential('supabase-pat');
const ref = 'YOUR_PROJECT_REF';

const types = await sbGet(`/projects/${ref}/types/typescript`);
return types;

List edge functions and their status

const cred = await API.getCredential('supabase-pat');
const ref = 'YOUR_PROJECT_REF';

const functions = await sbGet(`/projects/${ref}/functions`);
return functions.map(f => ({
  slug: f.slug,
  name: f.name,
  status: f.status,
  version: f.version,
}));

Important Rules

  • Always call API.getCredential('supabase-pat') before any Supabase API call. If it returns null, prompt the user to configure it.
  • Always identify the target project ref before making project-specific calls. Either ask the user or call GET /v1/projects to list available projects.
  • Use POST /v1/projects/{ref}/database/query/read-only for SELECT queries to avoid accidental writes.
  • Use POST /v1/projects/{ref}/database/query for INSERT/UPDATE/DELETE/DDL operations.
  • When running destructive SQL (DROP, DELETE, TRUNCATE), always confirm with the user first.
  • Treat tokens as sensitive — never log or display them.
  • The Management API does not have publicly documented rate limits, but be reasonable — avoid tight loops of requests.
  • SQL query results may be large. Use LIMIT clauses to keep responses manageable.
提供Vercel REST API访问能力,支持项目与部署管理、构建及运行时日志查询、状态监控、环境变量操作、重新部署及域名配置检查。通过个人访问令牌认证,实现自动化运维与开发工作流集成。
需要查看或管理Vercel项目部署状态 需要获取应用构建或运行时的日志信息 需要配置或更新项目的环境变量 需要触发项目的重新部署或取消正在进行的构建
apps/browser/bundled/plugins/vercel/SKILL.md
npx skills add mereyabdenbekuly-ctrl/clodex-ide --skill vercel -g -y
SKILL.md
Frontmatter
{
    "name": "vercel",
    "description": "Complete guide for the Vercel plugin — REST API access for deployments, logs, projects, and environment variables using a Vercel Personal Access Token."
}

Vercel Plugin

This plugin provides access to the Vercel REST API on the user's behalf, using a stored Personal Access Token.

Capabilities:

  • List and inspect projects and deployments
  • Fetch build and runtime logs
  • Check deployment status and watch for completion
  • Read, add, and update environment variables
  • Trigger redeploys or cancel running builds
  • Inspect domain and alias configuration

Authentication

Request the stored Vercel credential. The token field is an opaque placeholder — the sandbox fetch proxy substitutes the real value automatically. Never decode or transform it.

const cred = await API.getCredential('vercel-pat');
if (!cred) {
  return 'Vercel credential is not configured. Ask the user to add a Vercel Personal Access Token at vercel.com/account/tokens, then store it in Settings.';
}

Making Requests

Always pass the token as a Bearer header:

async function vercelGet(path) {
  const res = await fetch(`https://api.vercel.com${path}`, {
    headers: {
      Authorization: `Bearer ${cred.token}`,
      'Content-Type': 'application/json',
    },
  });
  if (!res.ok) throw new Error(`Vercel API ${res.status}: ${await res.text()}`);
  return res.json();
}

async function vercelPost(path, body) {
  const res = await fetch(`https://api.vercel.com${path}`, {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${cred.token}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(body),
  });
  if (!res.ok) throw new Error(`Vercel API ${res.status}: ${await res.text()}`);
  return res.json();
}

Key Endpoints

Projects

List all projects GET /v9/projects Returns paginated list of projects. Use ?limit=20 to control page size.

Get a project GET /v9/projects/:projectId :projectId can be the project ID or name (slug).

Deployments

List deployments for a project GET /v6/deployments?projectId=:projectId&limit=10 Add &target=production to filter production deployments only. Add &state=ERROR / &state=READY / &state=BUILDING to filter by status.

Get a deployment GET /v13/deployments/:deploymentId Returns full deployment details including status, meta, and build output.

Get deployment build logs GET /v2/deployments/:deploymentId/events Returns a stream of build log events. Each event has type (stdout/stderr) and text.

Redeploy POST /v13/deployments Body: { "deploymentId": "...", "name": "...", "target": "production" }

Cancel a deployment PATCH /v12/deployments/:deploymentId/cancel

Runtime Logs

Get runtime logs GET /v1/deployments/:deploymentId/events?direction=backward&limit=100 Use &since=<timestamp_ms> and &until=<timestamp_ms> to scope by time. Add &statusCode=500 or filter by level field in results (error, warning, info).

Environment Variables

List env vars for a project GET /v9/projects/:projectId/env Returns all env vars. Secret values are redacted — use /decrypt endpoint to retrieve them.

Create an env var POST /v10/projects/:projectId/env Body:

{
  "key": "MY_VAR",
  "value": "my-value",
  "type": "plain",
  "target": ["production", "preview", "development"]
}

type can be "plain", "secret", or "encrypted".

Update an env var PATCH /v9/projects/:projectId/env/:envId Body: { "value": "new-value" }

Delete an env var DELETE /v9/projects/:projectId/env/:envId

Teams

If the user's projects are under a team, many endpoints require ?teamId=:teamId.

List teams GET /v2/teams Returns all teams the token has access to. Note the id field for use as teamId.

Get a team GET /v2/teams/:teamId


Common Patterns

Check if latest deployment succeeded

const cred = await API.getCredential('vercel-pat');
const { deployments } = await vercelGet('/v6/deployments?projectId=YOUR_PROJECT&limit=1&target=production');
const latest = deployments[0];
return { status: latest.state, url: latest.url, createdAt: new Date(latest.createdAt).toISOString() };

Fetch recent error logs for a deployment

const cred = await API.getCredential('vercel-pat');
const events = await vercelGet('/v2/deployments/DEPLOYMENT_ID/events?direction=backward&limit=200');
const errors = events.filter(e => e.level === 'error' || e.type === 'stderr');
return errors.map(e => e.text || e.payload?.text).filter(Boolean).join('\n');

Watch a deployment until it finishes

const cred = await API.getCredential('vercel-pat');
const deploymentId = 'dpl_...';
let status = 'BUILDING';
let attempts = 0;
while (['BUILDING', 'INITIALIZING', 'QUEUED'].includes(status) && attempts < 30) {
  await new Promise(r => setTimeout(r, 5000));
  const { state } = await vercelGet(`/v13/deployments/${deploymentId}`);
  status = state;
  attempts++;
  API.output(`Deployment status: ${status}`);
}
return `Final status: ${status}`;

Important Rules

  • Always call API.getCredential('vercel-pat') before any Vercel API call. If it returns null, prompt the user to configure it.
  • Many endpoints are paginated — check for pagination.next in the response and page through if needed.
  • Team-scoped projects require ?teamId= on most endpoints. If requests return 403 or empty results, ask the user for their team ID or fetch it via GET /v2/teams.
  • Treat env var values as sensitive — do not log or display secret/encrypted values.
  • Rate limits: Vercel enforces per-token rate limits. Avoid tight polling loops; use at least 2–5s between status checks.
通过注入fetch请求将结构化日志发送至本地HTTP服务,支持创建通道、写入JSONL文件、读取分析、监控变更及清理代码,用于深入调试。
需要深入调试代码逻辑时 分析运行时状态或数据流时
apps/browser/bundled/skills/debug/SKILL.md
npx skills add mereyabdenbekuly-ctrl/clodex-ide --skill Debug -g -y
SKILL.md
Frontmatter
{
    "name": "Debug",
    "description": "Use logging calls for in-depth debugging via local log files",
    "user-invocable": true,
    "agent-invocable": true
}

Inject fetch() logging calls into user code that send structured data to a local HTTP ingest server. Log entries accumulate in JSONL files you can read for analysis.

Prerequisites

The env-snapshot includes a logIngest field once the ingest server is running:

logIngest: { port: <number>, token: "<uuid>" }

If logIngest is null, the server has not started yet — wait for an env-change.

Protocol

1. Create a log channel

Use write to create an empty JSONL file. The channel name must be kebab-case ([a-z0-9]+(-[a-z0-9]+)*).

write('logs/react-renders.jsonl', '')

This registers the channel. You will receive a log-channel-created env-change.

2. Construct the ingest URL

http://127.0.0.1:{port}/ingest/{channel-name}?token={token}

Example: http://127.0.0.1:54321/ingest/react-renders?token=abc-123

3. Inject instrumentation code

Wrap all injected debug code in region markers for easy identification and removal:

// #region @clodex-debug
fetch('http://127.0.0.1:54321/ingest/react-renders?token=abc-123', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    level: 'info',
    source: 'useEffect-mount',
    data: { component: 'App', props: { userId: 42 } }
  })
}).catch(() => {});
// #endregion @clodex-debug

Rules:

  • Always wrap in // #region @clodex-debug / // #endregion @clodex-debug.
  • Always .catch(() => {}) — instrumentation must never break user code.
  • Keep payloads small. The source field distinguishes instrumentation points.

4. POST body schema

{
  "level": "info" | "warn" | "error" | "debug" | "log",
  "source": "descriptive-label",
  "data": { ... }
}

Body must be a JSON object. All fields are optional. level defaults to "log". The server prepends a ts field (epoch milliseconds) automatically — do not send your own.

5. Read logs

read('logs/react-renders.jsonl')

Each line is a JSON object with a server-injected timestamp: { "ts": 1713000000000, "level": "info", "source": "useEffect-mount", "data": { ... } }.

Use grepSearch with mount_prefix targeting logs for filtered searches.

6. Env-change notifications

You receive these for owned log channels only:

Event Attributes Meaning
log-channel-created channel New channel file appeared
log-entries-added channel, newLines New lines were appended
log-channel-removed channel Channel file was deleted

7. Size limits

  • 64 KB max per POST body. Split large payloads.
  • 2 MB max per log file. The server returns 409 when full.

When a file is full: read and analyze the data, then truncate with write('logs/{name}.jsonl', '') and continue.

8. Cleanup (mandatory)

When debugging is complete, always perform both steps:

  1. Remove instrumentation — delete all // #region @clodex-debug// #endregion @clodex-debug blocks from user code.
  2. Delete log filesdelete('logs/{name}.jsonl') for every channel you created.

Never leave debug instrumentation or log files behind.

Notes

  • Browser-side code: The ingest server handles CORS (Access-Control-Allow-Origin: *). fetch() works from any origin.
  • Node.js server code: fetch() works directly to 127.0.0.1.
  • Write buffering: Entries are buffered and flushed every 500ms or 50 entries. There may be a short delay before new entries appear in the file.
用于创建、从对话中提取或更新技能。支持从零构建、提取会话知识及修正过时内容,遵循特定结构和写作规范,确保技能精准可用。
用户要求创建新技能 从当前对话中提取经验或捕获知识 用户纠正行为或技能内容过时/错误需更新
apps/browser/bundled/skills/learn-skill/SKILL.md
npx skills add mereyabdenbekuly-ctrl/clodex-ide --skill Learn -g -y
SKILL.md
Frontmatter
{
    "name": "Learn",
    "description": "Create, extract, or update a skill. Use when authoring a new skill from scratch, extracting knowledge from the current session, or updating an existing skill when the user's intentions contradicted skill guidance or content is confusing\/outdated.",
    "user-invocable": true,
    "agent-invocable": false
}

Learn: write skills

Workflows

1. New skill from scratch

Trigger: "create a skill for X". Ask user intensively, follow their guidance. Build using structure + rules below.

2. Extract from conversation

Trigger: "learn-skill for what we did", "capture this", "extract a skill from above".

  1. Scan — collect: decisions + rationale, constraints, working patterns, workflows, gotchas, non-obvious domain facts
  2. Filter — keep only non-obvious, project-specific knowledge. Cut what agent already knows.
  3. Scope — workspace skill (.clodex/skills/) if content uses specific paths/tooling; user skill (.agents/skills/) if broadly reusable
  4. Write — structure into files using rules below, write to path

→ Load references/session-extraction.md for full checklist + signal/noise examples.

3. Update existing skill

Trigger: user corrected agent behavior, overrode guidance, skill is wrong/outdated.

  1. Read full skill before touching anything
  2. Replace — overwrite wrong content. Never append "Note: above is outdated"
  3. Remove redundancies. Every sentence earns its place.
  4. Preserve what's still accurate

→ Load references/skill-updating.md for full checklist.


After every operation

Output compact summary:

  • Created — path + one-line description
  • Updated — path + bullets: added / replaced / removed
  • Excluded — what was cut + why (extractions only)

Skill structure

skill-name/
├── SKILL.md           required, target <200 lines
├── references/        optional, on-demand
│   └── *.md
├── scripts/           optional, executable scripts
└── assets/            optional, output files (templates, icons, etc.)

Frontmatter (required):

---
name: skill-name
description: What it does + when to use it. Specific — drives discovery.
---

No frontmatter duplication. Body must not restate description. Frontmatter = discovery blurb. Body = triggers, steps, constraints.

References section — required if any reference files exist: List every file: what it contains + when to load it. Unlisted = invisible to agent.

## References
- `references/render-loops-debugging.md` — workflow for diagnosing excess re-renders. Load when investigating render performance.

Authoring rules

SKILL.md vs. references

SKILL.md → everything needed for the typical case: triggers, core steps, universal constraints.

References → content needed only sometimes:

  • Workflow-specific detail (e.g. extraction checklist, update checklist)
  • Examples — illustrative, not required to act
  • Rules that apply only in certain cases
  • Deep guidance for uncommon situations

Never put universal rules in references. Always-applies rule → SKILL.md. Agent might skip references.

The test: agent handle common case from SKILL.md alone (or + 1 reference max)? No → split is wrong. Fold needed content back into SKILL.md.

Writing style — caveman speak

Write dense. Drop articles, filler, throat-clearing. Fragments OK. Use , =, + as connectors. Bold key terms. Keep all technical content.

❌ Fluffy ✅ Caveman
"You should make sure to always read the file before editing" "Read before edit. Always."
"This step is important because it ensures that..." "→ prevents X"
"I'd recommend using useMemo to memoize the object" "Wrap in useMemo."

Rules:

  • Imperative/verb-first: "Do X" not "You should do X"
  • No meta phrasing: never "this skill tells you to…"
  • One term per concept — never synonyms
  • Every sentence must add info agent doesn't already have. Cut the rest.

Progressive disclosure

  • SKILL.md → target <200 lines. Up to ~500 acceptable; split when approaching limit.
  • Reference files → also target <200 lines each.
  • One level deep — references link from SKILL.md only. No ref → ref chains.

Degrees of freedom

Match specificity to task fragility:

  • High (prose): multiple valid approaches, context-dependent
  • Medium (pseudocode/parameterized): preferred pattern exists, variation OK
  • Low (exact script): fragile or order-sensitive

References

  • references/session-extraction.md — extraction checklist, signal/noise examples, scope decisions. Load for workflow 2.
  • references/skill-updating.md — update checklist: conflicts, redundancy, deprecated content. Load for workflow 3.
  • references/workflow-patterns.md — how to document per-process workflows (steps, primitives, examples). Load when skill needs to document multiple distinct processes.
  • references/authoring-guide.md — naming, descriptions, content guidelines, anti-patterns, quality checklist. Load for comprehensive authoring guidance.
在编码前创建结构化的实施计划。通过研究代码、澄清歧义并生成包含自包含上下文和具体任务的Markdown计划文件,确保代理能独立执行,且在用户批准前不实施任何更改。
需要编写复杂功能代码前 涉及多文件或数据流变更的重构任务 需要明确实施步骤以指导后续编码的指令
apps/browser/bundled/skills/plan/SKILL.md
npx skills add mereyabdenbekuly-ctrl/clodex-ide --skill Plan -g -y
SKILL.md
Frontmatter
{
    "name": "Plan",
    "description": "Create a structured implementation plan before coding",
    "user-invocable": true,
    "agent-invocable": false
}

You must create a structured implementation plan. Follow these instructions:

Workflow

  1. Research — read all affected files, trace data flow, understand the full change surface.
  2. Clarify — if genuinely ambiguous, batch questions into one askUserQuestions call.
  3. Write — create a kebab-case .md in plans/.
  4. Stop — present a summary and wait for approval. Do NOT implement until the user confirms.

Plan File Spec

  • Line 1: # Plan Title (h1) — used as display name in UI.
  • Line 2: One-sentence plain-text description — used as summary in UI.
  • Body: Freeform markdown. Use whatever sections best explain the change — Problem, Approach, Context, Changes, File Map, Key Details, etc. No fixed structure required; scale detail to complexity. Do not use emojis.
    • Self-contained: A fresh agent with no prior context should be able to implement the plan without re-researching the codebase. Include relevant file paths (mount-prefixed, e.g. w4ba9/src/...), current behavior, key decisions and rationale in prose sections. Omit sections that add no value.
    • Checkboxes: Use GFM checkboxes (- [ ]) for tasks. See Checkbox Rules below.

Checkbox Rules

Every checkbox = one concrete, completable work item.

  • Agent-completable only — every checkbox MUST be doable by you alone with your own tools. Never write tasks that require the user to act (manual testing, running commands in their shell, restarting a dev server, exporting env vars, reviewing, clicking through a UI, obtaining credentials, etc.). If such work is genuinely needed, describe it in a prose ## Follow-ups for the user section — never as a checkbox.
  • Short label — the - [ ] line is a concise, scannable summary (one sentence). It says what to do, not how.
  • Details below — implementation specifics (file paths, code snippets, sub-steps) go in indented content underneath the checkbox line. Never cram these into the checkbox line itself.
  • No alternatives — resolve choices in prose above, then write one task for the chosen approach.
  • No optional/decorative items — if conditional, resolve the condition; include as firm task or omit.
  • Parent-child consistency — parent is complete only when all children are complete.

Example

# Rename Commands to Skills

Rename all "command" terminology to "skill" across shared types, backend, and UI.

## Context

The codebase currently uses "command" (`CommandDefinition`, `CommandSource`, etc.) but the
user-facing terminology has shifted to "skill". The rename touches 3 files and 6 import sites.

## Tasks

- [ ] Rename `CommandDefinition` type and its source file

  Rename `w4ba9/src/shared/commands.ts` → `skills.ts`. Inside the file:
  - `CommandSource` → `SkillSource`
  - `CommandDefinition` → `SkillDefinition`
  - `toCommandDefinitionUI` → `toSkillDefinitionUI`

  Update all 6 import sites listed in the Context section.

- [ ] Update Karton state key from `commands` to `skills`

  In `w4ba9/src/shared/karton-contracts/ui/index.ts` (line ~629),
  change the state key and update the JSDoc comment above it.

- [ ] Verify typecheck passes

  Run `pnpm -F clodex typecheck` for both `tsconfig.ui.json` and `tsconfig.backend.json`.
该技能用于在浏览器标签页中创建交互式设计预览。通过构建小型应用(mini-app),生成HTML文件并调用API以Tab形式展示,支持响应式布局、迭代编辑及CDP调试,确保预览独立于聊天窗口显示。
需要创建交互式设计预览 请求在浏览器标签页中查看UI效果 构建前端组件或页面的实时预览
apps/browser/bundled/skills/preview/SKILL.md
npx skills add mereyabdenbekuly-ctrl/clodex-ide --skill Preview -g -y
SKILL.md
Frontmatter
{
    "name": "Preview",
    "description": "Create an interactive design-preview in a browser tab",
    "user-invocable": true,
    "agent-invocable": false
}

Build a mini-app to fulfill the request. Use the apps/ directory and API.openApp(appId, { target: 'tab', title: 'Readable preview title' }) as documented in the Application Environment section.

Workflow

  1. Scaffold — create apps/{appId}/index.html (+ optional styles.css, script.js).
  2. Design for a full browser tab — the preview opens on an internal clodex://internal/preview/{appId} page, so use responsive layouts that work from narrow to full-width viewports.
  3. Open in tab — call await API.openApp(appId, { target: 'tab', title: 'Readable preview title' }). The title is shown in the tab breadcrumbs. The returned { tabId } can be used with API.sendCDP() if the preview needs inspection.
  4. Iterate — use file tools (multiEdit, overwriteFile) for edits, then call API.openApp(appId, { target: 'tab', title: 'Readable preview title' }) again to open the refreshed preview.

Rules

  • One index.html per app. Sibling assets resolve via relative paths (./styles.css).
  • Prefer self-contained HTML (inline or sibling files) — no external build step.
  • Do not open preview apps in the chat. Use target: 'tab'.
  • Use API.sendMessage / API.onMessage only when bidirectional communication is needed.
执行最新计划,按序完成所有任务。严格执行即时更新复选框规则,不批量操作;保持专注,遵循架构决策;自动处理需用户操作的任务至后续跟进区,确保100%完成。
需要执行已制定的实施计划 任务列表中有待实现的具体开发步骤
apps/browser/bundled/skills/implement/SKILL.md
npx skills add mereyabdenbekuly-ctrl/clodex-ide --skill Implement -g -y
SKILL.md
Frontmatter
{
    "name": "Implement",
    "description": "Implement the most recent plan",
    "user-invocable": false,
    "agent-invocable": false
}

Implement the most recently created plan.

Workflow

  1. Read — understand context, decisions, and all tasks.
  2. Execute — work through tasks in order. For each:
    • Implement the change.
    • Update - [ ]- [x] in the plan file immediately — before moving on. Do NOT batch updates.
  3. Continue — do not stop or ask whether to proceed. Complete every checkbox.

Rules

  • Follow the plan's decisions — respect architectural choices unless a concrete reason forces deviation (update plan with rationale).
  • Stay focused — no unplanned features or refactors.
  • Update the plan — if new information or edge cases emerge, reflect them in the plan file.
  • Completion = 100% — every checkbox must be [x]. Unnecessary tasks: delete the checkbox + add brief rationale. Never leave unchecked boxes.
  • No user-side tasks — if a checkbox turns out to require user action (manual testing, shell commands, credential setup, etc.), delete it, move its content into a ## Follow-ups for the user prose section, and continue. Never leave it unchecked waiting on the user.

首页 - Wiki
Copyright © 2011-2026 iteam. Current version is 2.155.2. UTC+08:00, 2026-07-21 20:07
浙ICP备14020137号-1 $访客地图$