Skip to content

cloudcli-ai/cloudcli-plugin-starter

Repository files navigation

CloudCLI

CloudCLI Plugin Starter — Project Stats

A starter plugin for CloudCLI Cloud and CloudCLI UI

CloudCLI Cloud · Discord · Bug Reports · Plugin Docs

CloudCLI Cloud Join our Discord


Project Stats Screenshot

This plugin scans the currently selected project and shows file counts, lines of code, a file-type breakdown chart, largest files, and recently modified files.

It demonstrates the full plugin API — frontend rendering with live context updates and a Node.js backend server with RPC communication. Fork this repo to build your own plugin.

For a complete guide to the plugin system, see the Plugin Overview.

Installation

From git (recommended): Open Settings > Plugins in CloudCLI UI, paste this repository's URL, and click Install. The repo is cloned, dependencies are installed, TypeScript is compiled, and the plugin is ready to enable.

Manual: Clone or copy this repository into your plugins directory:

git clone https://github.com/cloudcli-ai/cloudcli-plugin-starter.git ~/.claude-code-ui/plugins/project-stats
cd ~/.claude-code-ui/plugins/project-stats
npm install
npm run build

Then open Settings > Plugins — "Project Stats" should appear. Enable it to add the tab.

Plugin structure

project-stats/
  manifest.json       # Required — plugin metadata
  package.json        # Dependencies and build script
  tsconfig.json       # TypeScript configuration
  src/
    types.ts          # Plugin API type definitions
    index.ts          # Frontend entry point (ES module)
    server.ts         # Backend entry point (Node.js subprocess)
  dist/               # Compiled output (auto-generated, not committed)
    index.js          # Compiled frontend — referenced by manifest "entry"
    server.js         # Compiled backend — referenced by manifest "server"
  icon.svg            # Custom SVG icon for the tab

How it works

Plugins are TypeScript modules compiled to ES modules. The host calls your exported mount(container, api) function when the plugin tab is activated and unmount(container) when it is torn down.

Plugins that need server-side logic can declare a "server" entry in their manifest. The host spawns it as a child process and proxies requests to it via api.rpc().

┌─────────────────────────────────────────────────────┐
│  CloudCLI UI Host                                   │
│                                                     │
│  Plugin subprocess (dist/server.js):                │
│    Runs as a child process with restricted env      │
│    Listens on random local port                     │
│    Receives secrets via X-Plugin-Secret-* headers   │
└───────────┬─────────────────────────┬───────────────┘
            │ serves static files     │ proxies RPC
┌───────────▼─────────────────────────▼───────────────┐
│  Frontend (browser)                                 │
│                                                     │
│  Plugin module (dist/index.js)                      │
│    import(url) → mount(container, api)              │
│    api.context          — theme / project / session │
│    api.onContextChange  — subscribe to changes      │
│    api.rpc(method, path, body) → Promise            │
└─────────────────────────────────────────────────────┘

TypeScript

This plugin is written in TypeScript. The src/types.ts file contains full type definitions for the plugin API (PluginAPI, PluginContext, PluginModule). Copy this file into your own plugin for type-safe development.

Build: npm run build compiles src/dist/ via tsc.

Watch: npm run dev recompiles on file changes.

When installing via Settings > Plugins, the host automatically runs npm install and npm run build — no manual build step needed.

Frontend — Module API

The host dynamically imports your compiled entry file and calls mount(container, api). Render any UI inside the container — plain HTML, styled components, charts, dashboards, anything you can build with vanilla JavaScript and the DOM.

import type { PluginAPI, PluginContext } from './types.js';

export function mount(container: HTMLElement, api: PluginAPI): void {
  const ctx: PluginContext = api.context;
  container.innerHTML = `<p>Hello! Theme: ${ctx.theme}</p>`;

  const unsub = api.onContextChange((ctx) => {
    container.style.background = ctx.theme === 'dark' ? '#111' : '#fff';
  });

  (container as any)._cleanup = unsub;
}

export function unmount(container: HTMLElement): void {
  (container as any)._cleanup?.();
  container.innerHTML = '';
}

Context object

interface PluginContext {
  theme: 'dark' | 'light';
  project: { name: string; path: string } | null;
  session: { id: string; title: string } | null;
}

RPC helper

const data = await api.rpc('GET', '/stats?path=/my/project');
const result = await api.rpc('POST', '/echo', { greeting: 'hi' });

Backend — Server subprocess

Plugins that need filesystem access, external APIs, npm packages, or persistent state can declare a "server" entry in their manifest. The host manages the lifecycle:

  1. When the plugin is enabled, the host spawns node dist/server.js
  2. The subprocess must print a JSON line to stdout: {"ready": true, "port": 12345}
  3. The host proxies requests from api.rpc() to that port
  4. When the plugin is disabled or uninstalled, the host sends SIGTERM

Secrets

Per-plugin secrets are configured in Settings > Plugins and injected as HTTP headers on every proxied request (x-plugin-secret-<name>). They are never stored in the subprocess environment.

Restricted environment

The subprocess runs with a minimal env — only PATH, HOME, NODE_ENV, and PLUGIN_NAME. It does not inherit the host's API keys or other secrets.

manifest.json

{
  "name": "project-stats",        // Unique id — alphanumeric, hyphens, underscores
  "displayName": "Project Stats",  // Shown in the UI
  "version": "1.0.0",
  "description": "Short description shown in settings.",
  "author": "Your Name",
  "icon": "icon.svg",             // Custom SVG icon
  "type": "module",               // Must be "module"
  "slot": "tab",                  // Where the plugin appears — only "tab" today
  "entry": "dist/index.js",      // Compiled frontend entry file
  "server": "dist/server.js",    // Optional — compiled backend entry file
  "permissions": []                // Reserved for future use
}

Constraints

Plugins cannot:

  • Modify built-in tabs or appear outside the tab area
  • Interact with Claude's chat system
  • Communicate with other plugins
  • Access authentication tokens or intercept requests

Security & Trust Model

Plugins run at your own risk. A plugin's frontend code executes in the same JavaScript context as the host application — there is no runtime sandbox. Only install plugins whose source code you have reviewed or that come from authors you trust.

CloudCLI follows a trust-through-transparency model: plugins are installed from git repositories with visible source, not from an anonymous marketplace. You are responsible for reviewing plugin code before installing.

Frontend — Plugin modules run in the same JS context as the host and only receive the api object. All server communication goes through api.rpc(). However, because there is no runtime sandbox, a malicious plugin could in principle reach beyond the provided API.

Backend — The subprocess runs as a separate OS process with restricted env, per-call secret injection via headers, and process-level isolation. Auth headers are stripped before proxying.

Install-time — npm postinstall scripts are blocked (--ignore-scripts). Ship pre-built or use packages that work without postinstall hooks. The npm run build step is run automatically after install.

Contributing

If you've built a plugin and would like it added to the official CloudCLI UI plugin repository, open an issue with a link to your plugin's repository. The team will review it for inclusion.

License

MIT

About

A starter plugin for CloudCLI UI that displays project stats, file counts, lines of code, file-type breakdown, and recent activity. Use as a template to build your own plugins

Topics

Resources

License

Stars

Watchers

Forks