Agent skill
coding-patterns
Architecture and design patterns for lettactl. Reference when building new features, displays, or commands.
Install this agent skill to your Project
npx add-skill https://github.com/nouamanecodes/lettactl/tree/main/.skills/coding-patterns
SKILL.md
Display Architecture
All UX/display code lives in src/lib/ux/display/. Commands never format output directly — they transform data into typed interfaces and call display functions.
Entry List Pattern
For any list of entries shown chronologically (messages, archival memory, etc.), use the shared entry-list.ts module:
import { displayEntryList, EntryListItem } from './entry-list';
const items: EntryListItem[] = data.map(d => ({
metaLine: chalk.dim(d.timestamp) + purple(` [${d.label}]`),
content: d.text,
}));
return displayEntryList('Title (N)', items, optionalNote);
metaLine: pre-formatted metadata (timestamp, role, tags, score, etc.)content: text body — truncated for summary views, full for--fullviews- The module handles fancy/plain switching internally via
shouldUseFancyUx()
Used by: messages.ts, archival.ts
Box Pattern
For detail views and structured data (describe command output), use box.ts:
import { createBox, createBoxWithRows, BoxRow } from '../box';
// Key-value box
const rows: BoxRow[] = [
{ key: 'ID', value: data.id },
{ key: 'Name', value: data.name },
];
lines.push(...createBox('Title', rows, width));
// Free-form rows box
const items = data.map(d => chalk.white(d.name));
lines.push(...createBoxWithRows('Section (N)', items, width));
Used by: details.ts (describe command)
Table Pattern
For resource listings (get agents, get blocks, etc.), use the table helpers in resources.ts:
- Header row with column names
- Status dot + padded columns
- Fancy mode uses colored box frame, plain mode uses dashes
Used by: resources.ts (get command list views)
When to Use Which
| View Type | Pattern | Example |
|---|---|---|
| Chronological entries | Entry List | messages, archival memory |
| Resource listing | Table | get agents, get blocks, get tools |
| Single resource detail | Box | describe agent, describe block |
| Full content dump | Entry List (--full) |
get archival --full |
Display Module Structure
Every display file follows the same structure:
- Export a typed data interface (e.g.,
AgentData,ArchivalEntryData) - Export a display function that checks
shouldUseFancyUx() - Fancy variant uses chalk colors, box drawing, purple branding
- Plain variant uses simple text — no colors, no box chars
New display modules go in src/lib/ux/display/ with a re-export in index.ts.
Command → Display Flow
Command (get.ts, describe.ts)
→ Fetches raw data from LettaClientWrapper
→ Transforms to typed display interface
→ Calls OutputFormatter adapter (output-formatter.ts)
→ Calls display function (display/*.ts)
→ Returns formatted string
→ output() to stdout
Commands never import chalk or format strings. OutputFormatter adapters transform raw SDK responses to typed display data.
LettaClientWrapper
All SDK calls go through src/lib/letta-client.ts. Never call the SDK directly from commands. Wrapper methods handle:
- Normalizing response shapes
- Consistent error surfaces
- Single place to update when SDK changes
Command Directory Structure
Each command lives in its own directory under src/commands/<command>/:
src/commands/
├── get/
│ ├── index.ts # Re-exports, router logic
│ ├── types.ts # Interfaces, constants (SUPPORTED_RESOURCES, etc.)
│ ├── agents.ts # Handler for `get agents`
│ ├── blocks.ts # Handler for `get blocks`
│ └── ... # One file per resource type
├── delete/
│ ├── index.ts # Router + re-exports (deleteAgentWithCleanup for SDK)
│ ├── types.ts # DELETE_SUPPORTED_RESOURCES, options interfaces
│ ├── agent.ts # Single agent deletion
│ ├── mcp-server.ts # Single MCP server deletion
│ ├── all-agents.ts # Bulk delete handlers
│ └── ...
└── messages/
├── index.ts # Re-exports all commands
├── types.ts # ListOptions, SendOptions, etc.
├── list.ts # listMessagesCommand
├── send.ts # sendMessageCommand
├── utils.ts # Shared helpers (getMessageContent, formatElapsedTime)
└── ...
Directory Contents
| File | Purpose |
|---|---|
index.ts |
Re-exports public API, router logic if needed |
types.ts |
Interfaces, option types, constants like SUPPORTED_RESOURCES |
<handler>.ts |
One file per subcommand or resource type |
utils.ts |
Shared helpers used by multiple handlers |
Router Pattern (index.ts)
For commands with multiple subcommands (get, describe, delete), the index.ts acts as a router:
// src/commands/get/index.ts
import { getAgents } from './agents';
import { getBlocks } from './blocks';
import { SUPPORTED_RESOURCES } from './types';
export default async function getCommand(resource: string, name: string, options: GetOptions, command: any) {
if (!SUPPORTED_RESOURCES.includes(resource)) {
throw new Error(`Unsupported resource: ${resource}`);
}
switch (resource) {
case 'agents': return getAgents(name, options, command);
case 'blocks': return getBlocks(name, options, command);
// ...
}
}
Simple Command Pattern
For single-function commands (health, context, export), keep it simple:
// src/commands/health/index.ts
export { healthCommand } from './health';
// src/commands/health/health.ts
export async function healthCommand(options: HealthOptions, command: any) {
// implementation
}
Re-exporting for SDK
When a function needs to be available to the SDK (like deleteAgentWithCleanup), re-export it from index.ts:
// src/commands/delete/index.ts
export { deleteAgentWithCleanup } from './agent';
File Organization Rules
- Commands (
src/commands/<cmd>/): one directory per command, router + handlers - Display (
src/lib/ux/display/): all formatting, one file per domain - Client (
src/lib/letta-client.ts): all SDK calls - Resolver (
src/lib/agent-resolver.ts): agent name → ID resolution - Shared logic (
src/lib/): validators, error handling, resource utilities
Flags Convention
--no-ux: plain output, no colors/boxes (CI/CD mode)--no-spinner: disable loading spinners-o json: JSON output (bypasses all display formatting)--full: show full content instead of truncated (entry list views)--short: truncate content more aggressively (block content views)--query <text>: semantic search (archival memory)
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
lettactl
Manage Letta AI agent fleets with kubectl-style CLI
release-cycle
Use when releasing features - covers issues, branches, tests, commits, PRs, versioning, and publishing
resource-management
Use when managing resources like memory blocks, tools, folders, files, or MCP servers
fleet-deployment
Use when deploying agents from YAML configuration files
message-operations
Use when sending messages to agents, viewing conversation history, or managing message state
code-review
Thorough code review covering architecture, code quality, and performance. Use when asked to "review this plan", "review code", "audit architecture", "check code quality", or "review for performance". Walks through issues interactively with tradeoff analysis and opinionated recommendations.
Didn't find tool you were looking for?