Skip to main content

CLI (Ink) Plugin

The cli-ink plugin provides an interactive terminal interface for TokenRing AI agents using the Ink framework. It enables users to manage agents, execute commands, handle human interface requests, and interact with the system through a responsive command-line environment.

Overview

The cli-ink package serves as the primary CLI entry point for the TokenRing AI system. It provides a feature-rich terminal experience with real-time event processing, comprehensive command support, and intuitive user interactions.

Key Features

  • Interactive Terminal Interface: Built with Ink framework for responsive terminal applications
  • Agent Management: Select from running agents, create new ones, or connect to web applications
  • Real-time Event Processing: Stream agent outputs (chat, reasoning, system messages) with proper formatting
  • Comprehensive Command Support: Built-in commands like /switch for agent switching
  • Human Interface Requests: Handle confirmations, selections, password prompts, web page opening, and text input
  • Dynamic Screen Management: Switch between agent selection, chat, and interactive request handling
  • Markdown Rendering: Full markdown support with syntax highlighting for code blocks
  • Responsive Layout: Automatically adjusts to terminal window size
  • Workflow Integration: Support for spawning workflow-based agents
  • Web Application Integration: Connect to web applications via SPA resources
  • Command History: Input history with up/down arrow navigation
  • Auto-completion: Command auto-completion with tab key support

Installation

This package is part of the TokenRing AI monorepo.

# Install dependencies
bun install

# Run tests
vitest run

Usage Examples

Basic Initialization and Startup

import TokenRingApp from '@tokenring-ai/app';
import cliInkPlugin from '@tokenring-ai/cli-ink';

const app = new TokenRingApp();
app.install(cliInkPlugin);
await app.start();

Customizing the Banner

const app = new TokenRingApp();
app.install(cliInkPlugin, {
inkCLI: {
bannerNarrow: 'TokenRing CLI',
bannerWide: 'TokenRing AI CLI',
bannerCompact: 'CLI',
bannerColor: 'cyan'
}
});
await app.start();

Plugin Integration

import TokenRingApp, { TokenRingPlugin } from '@tokenring-ai/app';
import AgentInkCLI, { InkCLIConfigSchema } from '@tokenring-ai/cli-ink';
import { z } from 'zod';

import packageJSON from './package.json' with { type: 'json' };

const packageConfigSchema = z.object({
inkCLI: InkCLIConfigSchema.optional(),
});

export default {
name: packageJSON.name,
version: packageJSON.version,
description: packageJSON.description,
install(app, config) {
if (config.inkCLI) {
app.addServices(new AgentInkCLI(app, config.inkCLI));
}
},
config: packageConfigSchema,
} satisfies TokenRingPlugin<typeof packageConfigSchema>;

Configuration

CLI Configuration Schema

export const InkCLIConfigSchema = z.object({
bannerNarrow: z.string(),
bannerWide: z.string(),
bannerCompact: z.string(),
bannerColor: z.string().optional().default('cyan'),
});

Configuration Options

OptionTypeRequiredDefaultDescription
bannerNarrowstringYes-Narrow banner displayed in compact mode
bannerWidestringYes-Wide banner displayed in full mode
bannerCompactstringYes-Compact banner for agent chat screens
bannerColorstringNo'cyan'Color for the banner (uses Chalk color names)

Core Components

AgentInkCLI Service

Main service class implementing the CLI functionality.

export default class AgentInkCLI implements TokenRingService {
name = 'AgentInkCLI';
description = 'Ink-based CLI for interacting with agents';

constructor(app: TokenRingApp, config: z.infer<typeof InkCLIConfigSchema>);

async run(): Promise<void>;
}

AgentCLI Component

Top-level component managing screen state transitions and rendering.

interface AgentCLIProps extends z.infer<typeof InkCLIConfigSchema> {
agentManager: AgentManager;
app: TokenRingApp;
}

export default function AgentCLI(props: AgentCLIProps);

Screen Types

type Screen =
| { name: 'selectAgent' }
| { name: 'chat'; agentId: string }
| { name: 'question'; request: ParsedQuestionRequest, onResponse: (response: any) => void };

Screen Components

AgentChatScreen

Main chat interface displaying agent outputs and handling input.

interface AgentChatScreenProps {
currentAgent: Agent;
setScreen: (screen: Screen) => void;
}

export default function AgentChatScreen(props: AgentChatScreenProps);

Features:

  • Displays agent responses with markdown rendering
  • Shows thinking/reasoning process
  • Displays user input
  • Command input with history and auto-completion
  • Status line and busy state indicators

AgentSelectionScreen

For selecting agents from categories or connecting to existing ones.

interface AgentSelectionScreenProps {
app: TokenRingApp;
setScreen: (screen: Screen) => void;
onCancel: () => void;
}

export default function AgentSelectionScreen(props: AgentSelectionScreenProps);

Features:

  • Categorized agent list (by configuration)
  • Current running agents
  • Web applications
  • Workflows
  • Error display for agent spawning failures

QuestionInputScreen

Generic screen for handling various question types from agents.

Supports:

  • Text input (multi-line)
  • Tree selection (single or multiple)
  • File selection (placeholder - not fully implemented)

ConfirmationScreen

Confirmation prompt with timeout support.

Features:

  • Yes/No toggle with arrow key navigation
  • Visual feedback for selection
  • Countdown timer for timeout

AskScreen

Multi-line text input screen for agent questions.

Features:

  • Multi-line text editing
  • Ctrl+D to submit
  • Esc or q to cancel
  • Cursor position indicator

Reusable Components

CommandInput

Input field with history and auto-completion.

export interface CommandInputProps {
history?: string[];
autoCompletion?: string[];
onSubmit: (value: string) => void;
onCancel?: () => void;
onCtrlC?: () => void;
prompt?: string;
}

export const CommandInput: React.FC<CommandInputProps>;

Features:

  • Command history navigation (up/down arrows)
  • Tab auto-completion
  • Ctrl+C handling
  • Escape to cancel

Markdown

Renders markdown with syntax highlighting.

export interface InkMarkdownProps {
children: string;
options?: InkMarkdownOptions;
}

export function InkMarkdown(props: InkMarkdownProps): React.ReactElement;

Features:

  • Full markdown parsing using marked
  • Syntax highlighting for 40+ languages
  • Tables, lists, blockquotes, and code blocks
  • Responsive width handling

SelectInput

Generic selection input component.

export interface SelectOption<T = string> {
label: string;
value: T;
}

export interface SelectInputProps<T = string> {
message?: string;
options: SelectOption<T>[];
onSelect: (value: T) => void;
onCancel?: () => void;
}

export function SelectInput<T = string>(props: SelectInputProps<T>): React.ReactElement;

Custom Hooks

useAgentStateSlice

Subscribes to agent state slices for real-time updates.

export function useAgentStateSlice<T extends AgentStateSlice<any>>(
ClassType: new (...args: any[]) => T,
agent: Agent | null
): T | null;

useOutputBlocks

Processes agent event state into structured output blocks.

Note: This hook is available in the codebase but may not be used in the current implementation.

Output Types:

  • chat: Chat messages from agent
  • reasoning: Agent reasoning process
  • input: User input echo
  • system: System messages (info, warning, error)
export type OutputBlock =
| { type: 'chat'; message: string }
| { type: 'reasoning'; message: string }
| { type: 'input'; message: string }
| { type: 'system'; message: string; level: 'info' | 'warning' | 'error' };

export function useOutputBlocks(events: AgentEventState["events"] | null): OutputBlock[];

useScreenSize

Tracks terminal dimensions for responsive layout.

export default function useScreenSize() {
// Returns { rows: number, columns: number }
}

Integration

The cli-ink plugin integrates with several core TokenRing packages:

  • @tokenring-ai/agent: For agent management and event handling
  • @tokenring-ai/app: As the main application framework
  • @tokenring-ai/web-host: To detect and connect to web applications (SPA resources)
  • @tokenring-ai/workflow: For spawning workflow-based agents

It registers itself as a service and handles human interface requests through the agent system's event system.

Event Types

The CLI processes various agent events with color-coded output:

Event TypeColorDescription
output.chatGreenChat messages from the agent
output.reasoningYellowAgent reasoning process
output.infoBlueInformational system messages
output.warningYellowWarning system messages
output.errorRedError system messages
input.receivedYellowUser input echo
human.request-Interactive prompt handling

Keyboard Shortcuts

Ctrl+C Actions

  • Ctrl+C - Exit the application when in agent selection, return to agent selection when in chat
  • Up/Down Arrow - Navigate lists and command history
  • Tab - Auto-complete commands
  • Left/Right Arrow - Expand/collapse tree nodes (for tree selection screens)
  • Space - Toggle selections (multiple choice)
  • Enter - Select/submit
  • Esc or q - Cancel/cancel selection
  • Ctrl+D - Submit multi-line input

Development

Building

bun run build

Testing

vitest run         # Run tests
vitest run:watch # Watch mode
vitest run:coverage # Coverage report

Adding New Screens

  1. Create a new file in screens/ directory
  2. Implement the screen component
  3. Add the screen type to the Screen union type in AgentCLI.tsx
  4. Add handling in the main AgentCLI component

Adding New Commands

  1. Update CommandInput.tsx with command handlers
  2. Add documentation for the new command

Package Structure

pkg/cli-ink/
├── index.ts # Main entry point and exports
├── plugin.ts # Plugin definition for TokenRing app integration
├── AgentInkCLI.ts # Main service class
├── AgentCLI.tsx # Core component managing screen state
├── components/ # Reusable components
│ ├── CommandInput.tsx # Command input with history and auto-completion
│ ├── Markdown.tsx # Markdown rendering with syntax highlighting
│ └── SelectInput.tsx # Selection input component
├── hooks/ # Custom hooks
│ ├── useAgentStateSlice.ts # Agent event state management
│ ├── useOutputBlocks.tsx # Output block processing
│ └── useScreenSize.ts # Terminal size management
├── screens/ # Screen components
│ ├── AgentChatScreen.tsx # Agent chat interface
│ ├── AgentSelectionScreen.tsx # Agent selection interface
│ ├── AskScreen.tsx # Text input screen
│ ├── ConfirmationScreen.tsx # Confirmation prompt screen
│ └── QuestionInputScreen.tsx # Generic question input (handles text, treeSelect, fileSelect)
├── markdown.sample.md # Markdown rendering sample
├── package.json
├── tsconfig.json
├── vitest.config.ts
└── README.md

License

MIT License - see LICENSE for details.