kapy 🐹
The agent-first CLI framework
Build AI tools from the terminal. Commands, hooks, middleware, TUI — everything snaps together. Ship extensions. Compose toolchains. Run agents. Structured exit codes, --json output, and agentHints make it machine-friendly from day one.
Install
bun add -g @moikapy/kapyQuick Start
Standalone mode
Run kapy directly. It ships with meta-commands — its CLI surface is empty until you install extensions.
kapy install npm:@foo/kapy-deploy
kapy deploy:aws --region us-east-1
kapy tuiEmbedded mode
Build your own extensible CLI on top of kapy.
import { kapy } from "@moikapy/kapy"
kapy()
.command("deploy", {
description: "Deploy your application",
args: [{ name: "env", description: "Environment", default: "staging" }],
flags: {
verbose: { type: "boolean", alias: "v", description: "Verbose output" },
},
}, async (ctx) => {
ctx.log(`Deploying to ${ctx.args.env}...`)
})
.run()Built-in Commands
| Command | Description |
|---|---|
kapy init <name> | Scaffold a new kapy-powered CLI project |
kapy install <pkg> | Install an extension (npm/git/local) |
kapy list | Show installed extensions |
kapy update [name] | Update all or a specific extension |
kapy remove <name> | Uninstall an extension |
kapy upgrade [--pm bun|npm|yarn|pnpm] | Upgrade kapy (auto-detects package manager) |
kapy config | View/edit configuration |
kapy dev | Run CLI in dev mode with hot reload |
kapy commands | List all registered commands |
kapy inspect | Dump full state (extensions, config, hooks) |
kapy tui | Launch interactive terminal UI |
Command Context API
Every command handler receives a ctx object with utility methods:
async (ctx) => {
// Basic
ctx.args // Parsed args + flags
ctx.config // Merged config
ctx.log(msg) // Styled success output
ctx.warn(msg) // Styled warning
ctx.error(msg) // Styled error
ctx.spinner(text) // Progress spinner
ctx.prompt(msg) // Interactive input
ctx.confirm(msg) // Yes/no confirm
ctx.abort(code?) // Cancel execution
// Process-Aware (v0.2.0+)
ctx.isInteractive // True when TTY + !json + !noInput
ctx.spawn(cmd, opts?) // Spawn subprocess (TTY passthrough, abort-safe)
ctx.exitCode // Writable exit code (propagated to process.exit)
ctx.teardown(fn) // Register cleanup callback (LIFO, async-safe)
}ctx.spawn() Options
Spawn subprocesses with TTY passthrough, abort-safety, and streaming control:
const result = await ctx.spawn(["tmux", "new-session", "-s", "dev"], {
tty: true, // Pass through stdin/stdout/stderr
stream: false, // Stream output in real-time (default: collect)
env: { FOO: "bar" }, // Merge env vars with process.env
cwd: "/tmp", // Working directory
abortOnError: true, // Auto-kill process on ctx.abort()
suppressOutput: true, // Suppress stdout in --json mode
})
// result: { exitCode, stdout, stderr, aborted }Extensions
Extensions are TypeScript modules that export a register function and a meta object:
import type { KapyExtensionAPI } from "@moikapy/kapy"
export async function register(api: KapyExtensionAPI) {
api.addCommand("deploy:aws", {
description: "Deploy to AWS",
}, async (ctx) => {
ctx.log("Deploying to AWS...")
})
api.addHook("before:deploy", async (ctx) => {
// auth check, etc.
})
api.addMiddleware(async (ctx, next) => {
const start = Date.now()
await next()
ctx.log(`Completed in ${Date.now() - start}ms`)
})
api.addScreen({
name: "dashboard",
label: "Dashboard",
icon: "📊",
render(ctx) {
return { type: "Text", props: { content: "Project Dashboard" } }
},
})
}
export const meta = {
name: "@foo/kapy-deploy-aws",
version: "1.0.0",
dependencies: [],
}Install from npm (with optional version), git, or local paths:
kapy install npm:@foo/kapy-ext
kapy install npm:@foo/kapy-ext@1.2.3
kapy install git:github.com/user/repo
kapy install ./path/to/extExtension API
| Method | Description |
|---|---|
addCommand(definition) | Register a command |
addCommand(name, options, handler) | Register a command with name |
addHook(event, handler) | Register a hook |
addMiddleware(middleware) | Register middleware |
declareConfig(schema) | Declare config schema (auto-namespaced) |
addScreen(screenDefinition) | Register a TUI screen |
emit(event, data?) | Emit a custom event |
on(event, handler) | Listen for a custom event |
AI Agent Support
Every command supports --json and --no-input for machine-readable, non-interactive output. Extensions declare agentHints — structured metadata that AI agents parse to understand commands and call them. Structured exit codes give agents unambiguous signal (see table below).
kapy commands --json
kapy deploy:aws --json --no-input
kapy inspect --jsonExit Codes
| Code | Meaning |
|---|---|
0 | Success |
1 | General error |
2 | Invalid arguments / unknown command |
3 | Extension error |
4 | Config error |
5 | Network error |
10 | Aborted by hook/middleware |
Config
Config hierarchy (later overrides earlier): kapy defaults → kapy.config.ts → ~/.kapy/config.json → env vars → CLI flags
import { defineConfig } from "@moikapy/kapy"
export default defineConfig({
name: "my-cli",
extensions: ["npm:@foo/kapy-deploy"],
envPrefix: "MY_CLI",
})Environment variables are auto-mapped from flags using the configured envPrefix:
# Default prefix (standalone mode)
KAPY_DEPLOY_AWS_REGION=us-west-2 kapy deploy:aws
# Custom prefix (embedded mode)
MY_CLI_DEPLOY_AWS_REGION=us-west-2 my-cli deploy:awsTUI
Launch the interactive terminal UI. Extensions register screens via api.addScreen(). The TUI provides sidebar navigation, screen switching, and a status bar.
kapy tui
kapy tui --screen dashboardBuilt-in screens: Home 📊, Extensions 📦, Config 🔧, Terminal ⚡
Packages
| Package | Purpose |
|---|---|
@moikapy/kapy | Runtime + CLI bin + TUI shell + agent support. Install this. |
@moikapy/kapy-components | UI components on @opentui/core (Banner, Box, Text, Input, Select, ScrollBox, Code, Diff, Spinner, Sidebar, StatusBar). Re-exported by @moikapy/kapy. |