claudekit
Version:
CLI tools for Claude Code development workflow
531 lines (447 loc) • 15.9 kB
Markdown
# Task Breakdown: Show Commands and Subagent Prompts
Generated: August 15, 2025
Source: specs/feat-expose-headless-prompts.md
## Overview
Implementation of `claudekit show` commands to expose agent and command prompts for external use with LLMs and tools. The feature provides simple CLI access to embedded prompts with text (default) and JSON output formats.
## Phase 1: Foundation & Core Infrastructure
### Task 1.1: Create Loader Module Structure
**Description**: Set up the directory structure and TypeScript configuration for loader modules
**Size**: Small
**Priority**: High
**Dependencies**: None
**Can run parallel with**: None
**Technical Requirements**:
- Create directory `/cli/lib/loaders/`
- Set up TypeScript imports and exports
- Configure module resolution for embedded agents/commands
**Implementation Steps**:
1. Create `/cli/lib/loaders/` directory
2. Create `/cli/lib/loaders/index.ts` for unified exports
3. Update tsconfig.json if needed for new paths
**Acceptance Criteria**:
- [ ] Directory structure created
- [ ] TypeScript compilation works with new structure
- [ ] Module imports resolve correctly
### Task 1.2: Implement AgentLoader Class
**Description**: Build the AgentLoader class with path resolution and file loading
**Size**: Large
**Priority**: High
**Dependencies**: Task 1.1
**Can run parallel with**: Task 1.3
**Technical Requirements**:
- Embedded agents path: `path.join(__dirname, '../../src/agents')`
- Support ID formats: "oracle", "typescript/expert", "typescript-expert"
- Four-strategy path resolution as specified
- gray-matter for frontmatter parsing
- glob for recursive search
**Implementation from spec**:
```typescript
export class AgentLoader {
private searchPaths = [
path.join(__dirname, '../../src/agents')
];
async loadAgent(agentId: string): Promise<AgentDefinition> {
const agentPath = await this.resolveAgentPath(agentId);
if (!agentPath) {
throw new Error(`Agent not found: ${agentId}`);
}
// ... implementation as specified
}
private async resolveAgentPath(agentId: string): Promise<string | null> {
// Strategy 1: Direct file match
// Strategy 2: Try with -expert suffix
// Strategy 3: Handle category/name pattern
// Strategy 4: Recursive search with frontmatter
}
}
```
**Acceptance Criteria**:
- [ ] All four path resolution strategies implemented
- [ ] Handles all ID formats correctly
- [ ] Returns complete AgentDefinition object
- [ ] Error handling for missing agents
- [ ] Unit tests for all strategies
### Task 1.3: Implement CommandLoader Class
**Description**: Build the CommandLoader class with namespace support
**Size**: Large
**Priority**: High
**Dependencies**: Task 1.1
**Can run parallel with**: Task 1.2
**Technical Requirements**:
- Embedded commands path: `path.join(__dirname, '../../src/commands')`
- Support namespaced commands: "spec:create", "checkpoint:list"
- Parse allowed-tools from string or array format
- Three-strategy path resolution
**Implementation from spec**:
```typescript
export class CommandLoader {
private searchPaths = [
path.join(__dirname, '../../src/commands')
];
async loadCommand(commandId: string): Promise<CommandDefinition> {
// Implementation as specified
}
private parseAllowedTools(tools: any): string[] {
if (!tools) return [];
if (typeof tools === 'string') {
return tools.split(',').map(t => t.trim());
}
if (Array.isArray(tools)) {
return tools;
}
return [];
}
}
```
**Acceptance Criteria**:
- [ ] Namespace resolution works (spec:create -> spec/create.md)
- [ ] Direct file matching works
- [ ] Recursive search works
- [ ] Handles both string and array allowed-tools
- [ ] Unit tests for all path patterns
### Task 1.4: Define TypeScript Interfaces
**Description**: Create type definitions for AgentDefinition and CommandDefinition
**Size**: Small
**Priority**: High
**Dependencies**: Task 1.1
**Can run parallel with**: Task 1.2, Task 1.3
**Technical Requirements**:
- Create `/cli/lib/loaders/types.ts`
- Export AgentDefinition and CommandDefinition interfaces
- Document all fields with comments
**Implementation from spec**:
```typescript
export interface AgentDefinition {
id: string; // e.g., "typescript-expert"
name: string; // From frontmatter: name field
description: string; // From frontmatter: description field
category: string; // From frontmatter: category field
bundle?: string[]; // From frontmatter: related agents
displayName?: string; // From frontmatter: UI display name
color?: string; // From frontmatter: UI color hint
content: string; // Raw markdown content after frontmatter
filePath: string; // Full path to source file
tools?: string[]; // From frontmatter: allowed tools
}
export interface CommandDefinition {
id: string; // e.g., "spec:create"
name: string; // Derived from filename
description: string; // From frontmatter: description field
category?: string; // From frontmatter: category field
allowedTools: string[]; // From frontmatter: allowed-tools field
argumentHint?: string; // From frontmatter: argument-hint field
content: string; // Raw markdown content after frontmatter
filePath: string; // Full path to source file
}
```
**Acceptance Criteria**:
- [ ] Interfaces match specification exactly
- [ ] All fields documented
- [ ] TypeScript compilation succeeds
## Phase 2: CLI Integration
### Task 2.1: Create Show Command Implementation
**Description**: Implement the show command with agent and command subcommands
**Size**: Medium
**Priority**: High
**Dependencies**: Task 1.2, Task 1.3, Task 1.4
**Can run parallel with**: None
**Technical Requirements**:
- Create `/cli/commands/show.ts`
- Use Commander.js for command structure
- Support --format option (text|json), default: text
- Text format: raw content only
- JSON format: full object, pretty-printed
**Implementation from spec**:
```typescript
export function registerShowCommands(program: Command) {
const showCmd = program
.command('show')
.description('Show agent or command prompts');
showCmd
.command('agent <id>')
.description('Show an agent prompt')
.option('-f, --format <format>', 'Output format (text|json)', 'text')
.action(async (id, options) => {
const loader = new AgentLoader();
const agent = await loader.loadAgent(id);
if (options.format === 'json') {
console.log(JSON.stringify(agent, null, 2));
} else {
console.log(agent.content);
}
});
showCmd
.command('command <id>')
// ... similar implementation
}
```
**Acceptance Criteria**:
- [ ] `show agent <id>` works with text output (default)
- [ ] `show agent <id> --format json` works with JSON output
- [ ] `show command <id>` works with both formats
- [ ] Error messages are user-friendly
- [ ] Exit codes set correctly on errors
### Task 2.2: Register Show Command in CLI
**Description**: Integrate the show command into the main CLI program
**Size**: Small
**Priority**: High
**Dependencies**: Task 2.1
**Can run parallel with**: None
**Technical Requirements**:
- Modify `/cli/cli.ts`
- Import registerShowCommands
- Call registration in appropriate location (around line 200-300)
**Implementation from spec**:
```typescript
import { registerShowCommands } from './commands/show.js';
// In main program setup:
registerShowCommands(program);
```
**Acceptance Criteria**:
- [ ] Show command appears in `claudekit --help`
- [ ] Show subcommands work from command line
- [ ] No conflicts with existing commands
### Task 2.3: Implement Error Handling
**Description**: Add comprehensive error handling with clear messages
**Size**: Medium
**Priority**: High
**Dependencies**: Task 2.1
**Can run parallel with**: Task 2.4
**Technical Requirements**:
- Handle missing agents/commands
- Handle invalid format parameter
- Handle file permission errors
- Set appropriate exit codes
**Implementation from spec**:
```typescript
// Missing agent/command
console.error(`Error: Agent not found: ${id}`);
console.error(`Try 'claudekit list agents' to see available agents`);
process.exit(1);
// Invalid format
if (format !== 'text' && format !== 'json') {
console.error(`Error: Invalid format '${format}'. Use 'text' or 'json'`);
process.exit(1);
}
// File errors
catch (error) {
if (error.code === 'EACCES') {
console.error(`Error: Permission denied reading file`);
} else if (error.code === 'ENOENT') {
console.error(`Error: File not found`);
} else {
console.error(`Error: ${error.message}`);
}
process.exit(1);
}
```
**Acceptance Criteria**:
- [ ] Clear error messages for all failure modes
- [ ] Exit code 1 for all errors
- [ ] Helpful suggestions in error messages
- [ ] No stack traces shown to users
### Task 2.4: Add Path Security Validation
**Description**: Implement path validation to prevent directory traversal
**Size**: Small
**Priority**: High
**Dependencies**: Task 1.2, Task 1.3
**Can run parallel with**: Task 2.3
**Technical Requirements**:
- Validate all resolved paths
- Prevent access outside allowed directories
- Use path.resolve for normalization
**Implementation from spec**:
```typescript
function validatePath(filePath: string): void {
const resolved = path.resolve(filePath);
const allowed = [
path.join(__dirname, '../../src/agents'),
path.join(__dirname, '../../src/commands')
];
if (!allowed.some(dir => resolved.startsWith(dir))) {
throw new Error('Access denied: Path outside allowed directories');
}
}
```
**Acceptance Criteria**:
- [ ] Prevents directory traversal attacks
- [ ] Allows legitimate agent/command access
- [ ] Clear error message for security violations
## Phase 3: Testing
### Task 3.1: Write Unit Tests for Loaders
**Description**: Create comprehensive unit tests for AgentLoader and CommandLoader
**Size**: Medium
**Priority**: High
**Dependencies**: Task 1.2, Task 1.3
**Can run parallel with**: Task 3.2, Task 3.3
**Test Requirements**:
- Test all path resolution strategies
- Test error cases (missing files, bad IDs)
- Test frontmatter parsing
- Test different ID formats
- Mock file system for predictable tests
**Tests from spec**:
```typescript
describe('AgentLoader', () => {
test('loads agent by simple name');
test('loads agent by category/name');
test('loads agent with -expert suffix');
test('throws error for non-existent agent');
test('parses frontmatter correctly');
});
describe('CommandLoader', () => {
test('loads namespaced command');
test('loads simple command');
test('parses allowed-tools from string');
test('parses allowed-tools from array');
});
```
**Acceptance Criteria**:
- [ ] All path strategies have tests
- [ ] Error conditions tested
- [ ] Tests are meaningful (can fail to reveal issues)
- [ ] No reliance on actual file system
### Task 3.2: Write Integration Tests for CLI
**Description**: Test the show command end-to-end through the CLI
**Size**: Medium
**Priority**: High
**Dependencies**: Task 2.1, Task 2.2
**Can run parallel with**: Task 3.1, Task 3.3
**Test Requirements**:
- Test default text output
- Test JSON format output
- Test error handling
- Test actual CLI invocation
**Tests from spec**:
```typescript
describe('Show Command', () => {
test('outputs text prompt by default', async () => {
const output = await runCLI(['show', 'agent', 'typescript-expert']);
expect(output).not.toContain('"id"');
expect(output).toContain('You are');
});
test('JSON format outputs complete definition', async () => {
const output = await runCLI(['show', 'agent', 'typescript-expert', '--format', 'json']);
const parsed = JSON.parse(output);
expect(parsed).toHaveProperty('id');
});
test('shows error for non-existent agent', async () => {
await expect(runCLI(['show', 'agent', 'non-existent']))
.rejects.toThrow('Agent not found: non-existent');
});
});
```
**Acceptance Criteria**:
- [ ] CLI tests pass with real command execution
- [ ] Output formats verified
- [ ] Error messages verified
- [ ] Exit codes verified
### Task 3.3: Write E2E Tests for Piping
**Description**: Test that output can be piped to external tools
**Size**: Small
**Priority**: Medium
**Dependencies**: Task 2.1
**Can run parallel with**: Task 3.1, Task 3.2
**Test Requirements**:
- Test piping to file
- Test piping to grep
- Test JSON parsing with jq
- Shell script tests
**Tests from spec**:
```bash
test_pipe_to_external() {
# Test pipe to file
claudekit show agent git-expert > /tmp/git-expert.md
[[ -s /tmp/git-expert.md ]] || fail "Show to file failed"
# Test pipe to grep
output=$(claudekit show agent testing-expert | grep -c "test")
[[ "$output" -gt 0 ]] || fail "Piping to grep failed"
}
```
**Acceptance Criteria**:
- [ ] Output can be piped to files
- [ ] Output can be piped to Unix tools
- [ ] JSON output is valid and parseable
- [ ] Text output is clean (no extra formatting)
## Phase 4: Documentation
### Task 4.1: Update README with Show Command
**Description**: Document the new show command in the main README
**Size**: Small
**Priority**: Medium
**Dependencies**: Task 2.1
**Can run parallel with**: Task 4.2, Task 4.3
**Documentation Requirements**:
- Add to command list
- Provide usage examples
- Explain output formats
- Show piping examples
**Content to add**:
```markdown
### Show Commands
Display agent and command prompts for use with external tools:
```bash
# Get agent prompt (text by default)
claudekit show agent typescript-expert
# Get with metadata
claudekit show agent typescript-expert --format json
# Pipe to other LLMs
claudekit show agent react-expert | openai-cli --system-prompt -
```
```
**Acceptance Criteria**:
- [ ] Command documented in README
- [ ] Examples are clear and working
- [ ] Both formats explained
### Task 4.2: Create Usage Guide
**Description**: Write guide for using prompts with external LLMs
**Size**: Small
**Priority**: Low
**Dependencies**: Task 2.1
**Can run parallel with**: Task 4.1, Task 4.3
**Guide Contents**:
- How to pipe to OpenAI CLI
- How to use with local LLMs (Ollama)
- How to integrate in CI/CD
- How to extract specific fields with jq
**Acceptance Criteria**:
- [ ] Guide covers common use cases
- [ ] Examples are tested and working
- [ ] Saved to docs/ directory
### Task 4.3: Add CLI Help Text
**Description**: Ensure comprehensive help text for show commands
**Size**: Small
**Priority**: Medium
**Dependencies**: Task 2.1
**Can run parallel with**: Task 4.1, Task 4.2
**Requirements**:
- Help for `claudekit show`
- Help for `claudekit show agent`
- Help for `claudekit show command`
- Document format options
**Acceptance Criteria**:
- [ ] `--help` works for all commands
- [ ] Options are documented
- [ ] Examples in help text
## Execution Strategy
### Parallel Execution Opportunities
- **Phase 1**: Tasks 1.2, 1.3, and 1.4 can run in parallel after 1.1
- **Phase 3**: All testing tasks (3.1, 3.2, 3.3) can run in parallel
- **Phase 4**: All documentation tasks can run in parallel
### Critical Path
1. Task 1.1 (Module Structure)
2. Tasks 1.2/1.3 (Loaders) - parallel
3. Task 2.1 (Show Command)
4. Task 2.2 (CLI Registration)
5. Testing & Documentation - parallel
### Risk Mitigation
- **Path Resolution Complexity**: Start with simple cases, add strategies incrementally
- **TypeScript Integration**: Test compilation early and often
- **File System Access**: Use mocks in tests to avoid file system dependencies
## Summary
- **Total Tasks**: 14
- **Phase 1 (Foundation)**: 4 tasks
- **Phase 2 (CLI Integration)**: 4 tasks
- **Phase 3 (Testing)**: 3 tasks
- **Phase 4 (Documentation)**: 3 tasks
- **Estimated Timeline**: 3-4 days total
- **Parallel Opportunities**: High in testing and documentation phases