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

terminal
bun add -g @moikapy/kapy

Quick Start

Standalone mode

Run kapy directly. It ships with meta-commands — its CLI surface is empty until you install extensions.

terminal
kapy install npm:@foo/kapy-deploy
kapy deploy:aws --region us-east-1
kapy tui

Embedded mode

Build your own extensible CLI on top of kapy.

typescript
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

CommandDescription
kapy init <name>Scaffold a new kapy-powered CLI project
kapy install <pkg>Install an extension (npm/git/local)
kapy listShow 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 configView/edit configuration
kapy devRun CLI in dev mode with hot reload
kapy commandsList all registered commands
kapy inspectDump full state (extensions, config, hooks)
kapy tuiLaunch interactive terminal UI

Command Context API

Every command handler receives a ctx object with utility methods:

typescript
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:

typescript
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:

typescript
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:

terminal
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/ext

Extension API

MethodDescription
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).

terminal
kapy commands --json
kapy deploy:aws --json --no-input
kapy inspect --json

Exit Codes

CodeMeaning
0Success
1General error
2Invalid arguments / unknown command
3Extension error
4Config error
5Network error
10Aborted by hook/middleware

Config

Config hierarchy (later overrides earlier): kapy defaults → kapy.config.ts~/.kapy/config.json → env vars → CLI flags

typescript
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:

terminal
# 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:aws

TUI

Launch the interactive terminal UI. Extensions register screens via api.addScreen(). The TUI provides sidebar navigation, screen switching, and a status bar.

terminal
kapy tui
kapy tui --screen dashboard

Built-in screens: Home 📊, Extensions 📦, Config 🔧, Terminal ⚡

Packages

PackagePurpose
@moikapy/kapyRuntime + CLI bin + TUI shell + agent support. Install this.
@moikapy/kapy-componentsUI components on @opentui/core (Banner, Box, Text, Input, Select, ScrollBox, Code, Diff, Spinner, Sidebar, StatusBar). Re-exported by @moikapy/kapy.