extension-installer
A workflow for installing and setting up Freshell extension panes.
Install
mkdir -p .claude/skills/extension-installer && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/12755" && unzip -o skill.zip -d .claude/skills/extension-installer && rm skill.zipInstalls to .claude/skills/extension-installer
Activation
This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.
Use when installing, creating, or setting up Freshell extensions — from GitHub repos, local directories, or from scratch as custom panes.Key capabilities
- →Add new pane types to Freshell
- →Create custom extensions (server, client, or CLI)
- →Debug why an installed extension is not showing up
- →Validate extension manifests against strict schema
- →Configure server extension environment variables with interpolation
- →Expand homedir paths in environment variables
How it works
This skill guides the installation and configuration of Freshell extensions by validating manifests, managing symlinks, and ensuring proper loading based on category and pre-built artifacts.
Inputs & outputs
When to use extension-installer
- →Add new pane type
- →Install local Freshell extension
- →Configure custom pane manifest
- →Debug extension loading issues
About this skill
Installing Freshell Extensions
When to Use
Use this skill when a user wants to:
- Add a new pane type to Freshell (from GitHub, a local project, or from scratch)
- Create a custom extension (server, client, or CLI)
- Debug why an installed extension isn't showing up
Do NOT use for modifying built-in pane types (terminal, browser, picker, etc.).
Critical Facts
Read this box before doing anything. These are the non-obvious rules that cause silent failures.
-
Extensions must be pre-built. Freshell does NOT run
npm install,npm run build, or any build step. The extension directory must contain ready-to-run artifacts before symlinking. -
Scan only on startup. Extensions are discovered once when the server starts. After installing or changing an extension, Freshell must be restarted.
-
z.strictObjectrejects unknown keys. The manifest schema uses strict validation. Any key not in the schema (typos, extra fields) causes the entire manifest to silently fail validation and the extension is skipped. Check server logs for warnings. -
Exactly one category config block. The manifest must have exactly one of
client,server, orcli— and it must match thecategoryfield. Having zero, two, or a mismatched block fails validation. -
Symlinks are the recommended dev pattern. The scanner follows symlinks. Point
~/.freshell/extensions/<name>at your project directory for development. -
Template interpolation in
server.env. Values support{{port}}(allocated port) and{{varName}}(contentSchema field defaults). Unresolved templates are left as-is. -
~/expands to homedir. After template interpolation, env values starting with~/are expanded to the user's home directory. -
Two scan directories. Freshell scans
~/.freshell/extensions/(user-installed) and.freshell/extensions/(local dev, relative to cwd). First match wins for duplicate names.
Category Decision Tree
| Extension needs... | Category | Required config block |
|---|---|---|
| Its own HTTP server process (Express, Flask, etc.) | server | server: { command, ... } |
| Just static HTML/JS/CSS served by Freshell | client | client: { entry } |
| A TUI/CLI tool running in a terminal | cli | cli: { command, ... } |
Manifest Reference
All fields below are derived from the Zod schema in server/extension-manifest.ts. Use only these keys — any others cause silent rejection.
Top-level fields
| Field | Type | Required | Notes |
|---|---|---|---|
name | string | yes | Unique identifier, min 1 char |
version | string | yes | Semver recommended, min 1 char |
label | string | yes | Human-readable display name |
description | string | yes | Short description for picker |
category | "client" | "server" | "cli" | yes | Must match the config block |
icon | string | no | Path to icon file (relative to extension dir) |
url | string | no | URL path template for iframe src (server and client extensions). Supports {{fieldName}} interpolation from contentSchema. Defaults to "/" |
contentSchema | object | no | Defines dynamic fields for pane props (see below) |
picker | object | no | Picker UI config (see below) |
client | object | conditional | Required when category: "client" |
server | object | conditional | Required when category: "server" |
cli | object | conditional | Required when category: "cli" |
contentSchema fields
Each key in contentSchema maps to a field descriptor:
| Field | Type | Required | Notes |
|---|---|---|---|
type | "string" | "number" | "boolean" | yes | |
label | string | yes | Display label |
required | boolean | no | |
default | string | number | boolean | no | Must match the declared type (e.g., type: "string" requires a string default) |
picker fields
| Field | Type | Required | Notes |
|---|---|---|---|
shortcut | string | no | Keyboard shortcut letter in picker |
group | string | no | Picker group name |
client config
| Field | Type | Required | Notes |
|---|---|---|---|
entry | string | yes | Path to HTML file (relative to extension dir), min 1 char |
server config
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
command | string | yes | — | Executable to run (e.g., "node") |
args | string[] | no | [] | Arguments to command |
env | Record<string, string> | no | — | Environment variables; supports {{port}} and {{varName}} interpolation |
readyPattern | string | no | — | Regex matched against stdout/stderr; server is "ready" when matched |
readyTimeout | number (positive int) | no | 10000 | Milliseconds to wait for readyPattern before killing |
healthCheck | string | no | — | Reserved for future use. Accepted by schema but not used at runtime yet. |
singleton | boolean | no | true | Reserved for future use. Accepted by schema but not used at runtime yet (currently always one process per extension). |
cli config
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
command | string | yes | — | Executable to run |
args | string[] | no | [] | Arguments to command |
env | Record<string, string> | no | — | Environment variables |
Copy-paste templates
Server extension:
{
"name": "my-server-ext",
"version": "0.1.0",
"label": "My Server Extension",
"description": "Does a thing with a server",
"category": "server",
"server": {
"command": "node",
"args": ["dist/index.js"],
"env": {
"PORT": "{{port}}"
},
"readyPattern": "listening on"
}
}
Client extension:
{
"name": "my-client-ext",
"version": "0.1.0",
"label": "My Client Extension",
"description": "A static HTML pane",
"category": "client",
"client": {
"entry": "index.html"
}
}
CLI extension:
{
"name": "my-cli-ext",
"version": "0.1.0",
"label": "My CLI Extension",
"description": "Wraps a TUI tool",
"category": "cli",
"cli": {
"command": "htop"
}
}
Workflow: Install from GitHub/URL
- Clone the repo to a local directory (e.g.,
~/code/<name>). - Install dependencies —
npm install(or equivalent for the project's stack). - Build —
npm run build(or equivalent). Verify build artifacts exist (e.g.,dist/). - Check for
freshell.jsonin the project root.- If missing, create one. Examine the project to determine:
- Category: Does it start a server? →
server. Static HTML? →client. CLI tool? →cli. - Command: What starts the server or CLI? (e.g.,
node dist/index.js) - readyPattern: What does the server print to stdout when ready? (e.g.,
"listening on")
- Category: Does it start a server? →
- If missing, create one. Examine the project to determine:
- Validate the manifest mentally against the schema above — no extra keys, category block matches
categoryfield, required fields present. - Symlink into extensions directory:
Use absolute paths — relative symlinks break when the working directory changes.mkdir -p ~/.freshell/extensions ln -sf /absolute/path/to/extension ~/.freshell/extensions/<name> - Restart Freshell for the extension to be discovered.
- Verify — open the pane picker, confirm the extension appears, open it, confirm it works.
Workflow: Install from Local Directory
- Check for
freshell.json— if missing, create one (see manifest reference above). - Build if needed — check if the project requires a build step and run it.
- Symlink:
mkdir -p ~/.freshell/extensions ln -sf /absolute/path/to/project ~/.freshell/extensions/<name> - Restart Freshell.
- Verify in the pane picker.
Workflow: Create from Scratch
Minimal server extension
Create a directory with two files:
index.js:
const http = require('http');
const port = process.env.PORT || 3000;
const server = http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end('<h1>Hello from my extension</h1>');
});
server.listen(port, () => console.log(`Listening on port ${port}`));
freshell.json:
{
"name": "hello-server",
"version": "0.1.0",
"label": "Hello Server",
"description": "Minimal server extension example",
"category": "server",
"server": {
"command": "node",
"args": ["index.js"],
"env": { "PORT": "{{port}}" },
"readyPattern": "Listening on"
}
}
No build step needed. Symlink and restart.
Single-file client extension
Create a directory with two files:
index.html:
<!DOCTYPE html>
<html>
<head><title>My Pane</title></head>
<body>
<h1>Hello from a client extension</h1>
<script>
// Your pane logic here
</script>
</body>
</html>
freshell.json:
{
"name": "hello-client",
"version": "0.1.0",
"label": "Hello Client",
"description": "Minimal client extension example",
"category": "client",
"client": {
"entry": "index.html"
}
}
No build step, no dependencies. Symlink and restart.
CLI wrapper
Just a manifest pointing at an existing binary. Single file:
freshell.json:
{
"name": "htop-pane",
"version": "0.1.0",
"label": "htop",
"description": "System monitor in a pane",
"category": "cli",
"cli": {
"command": "htop"
}
}
Create a directory with just this file, symlink, and restart.
Validation Checklist
Run through this before declaring an extension installed:
-
freshell.jsonis valid JSON - All 5 required top-level fields present (
name,version,label,description,category) - No unknown keys at any level (check typos —
readypatternvsreadyPattern) - Exactly one category config block, matching
categoryfield -
contentSchemadefaults match their declaredtype(string default fortype: "string", etc.) - Server: build artifact
Content truncated.
When not to use it
- →For modifying built-in pane types
- →When extensions are not pre-built
- →When expecting hot-reload after changes
Limitations
- →Does not run npm install, npm run build, or any build step for extensions
- →Extensions are discovered only once when the server starts
- →Manifest schema uses strict validation, rejecting unknown keys
How it compares
This workflow provides a structured approach to extending Freshell with custom functionalities, enforcing strict manifest validation and pre-built requirements, unlike ad-hoc extension development.
Compared to similar skills
extension-installer side by side with the closest alternatives in the catalog.
| Skill | Installs | Updated | Safety | Difficulty |
|---|---|---|---|---|
| extension-installer (this skill) | 0 | 5mo | Review | Intermediate |
| playwright-browser-automation | 29 | 7mo | Review | Intermediate |
| desktop | 9 | 3mo | No flags | Advanced |
| telegram-mini-app | 62 | 6mo | Review | Advanced |
Try saying
Example prompts that trigger this skill in your AI assistant.
You might also like
playwright-browser-automation
lackeyjb
Complete browser automation with Playwright. Auto-detects dev servers, writes clean test scripts to /tmp. Test pages, fill forms, take screenshots, check responsive design, validate UX, test login flows, check links, automate any browser task. Use when user wants to test websites, automate browser interactions, validate web functionality, or perform any browser-based testing.
desktop
lobehub
Electron desktop development guide. Use when implementing desktop features, IPC handlers, controllers, preload scripts, window management, menu configuration, or Electron-specific functionality. Triggers on desktop app development, Electron IPC, or desktop local tools implementation.
telegram-mini-app
davila7
Expert in building Telegram Mini Apps (TWA) - web apps that run inside Telegram with native-like experience. Covers the TON ecosystem, Telegram Web App API, payments, user authentication, and building viral mini apps that monetize. Use when: telegram mini app, TWA, telegram web app, TON app, mini app.
codex-skill
feiskyer
Use when user asks to leverage codex, gpt-5, or gpt-5.1 to implement something (usually implement a plan or feature designed by Claude). Provides non-interactive automation mode for hands-off task execution without approval prompts.
bullmq-specialist
davila7
BullMQ expert for Redis-backed job queues, background processing, and reliable async execution in Node.js/TypeScript applications. Use when: bullmq, bull queue, redis queue, background job, job queue.
shopify-development
davila7
Build Shopify apps, extensions, themes using GraphQL Admin API, Shopify CLI, Polaris UI, and Liquid. TRIGGER: "shopify", "shopify app", "checkout extension", "admin extension", "POS extension", "shopify theme", "liquid template", "polaris", "shopify graphql", "shopify webhook", "shopify billing", "app subscription", "metafields", "shopify functions"