Skip to content

Conversation

amikofalvy
Copy link
Collaborator

Summary

Refactored CLI commands to use a clean layered architecture that separates presentation, domain, and application logic. This improves code maintainability, testability, and extensibility.

Changes

Architecture

  • ✅ Created 5-layer architecture: Commands → Application → Domain + Presentation → Infrastructure
  • ✅ Separated UI concerns from business logic for better testability
  • ✅ Introduced domain-specific error classes for clearer error handling

Code Reduction

  • push.ts: 284 lines → 35 lines (-88% reduction)
  • list-agents.ts: 79 lines → 31 lines (-61% reduction)

New Modules

Presentation Layer (src/presentation/):

  • OutputService: Abstracts chalk for colored console output
  • SpinnerService: Abstracts ora for progress indicators
  • TableService: Abstracts cli-table3 for formatted tables
  • CLIPresenter: Formats command-specific results

Domain Layer (src/domain/):

  • ProjectLoader: Pure business logic for loading projects
  • ProjectPusher: Pure business logic for pushing projects
  • AgentLister: Pure business logic for listing agents
  • Domain errors: ProjectNotFoundError, InvalidProjectError, etc.

Application Layer (src/application/):

  • CommandHandler: Base class with standardized error handling
  • PushCommandHandler: Orchestrates push command flow
  • ListAgentsCommandHandler: Orchestrates list-agents flow

Testing Improvements

  • ✅ Added unit tests for presentation layer (OutputService)
  • ✅ Added unit tests for domain layer (ProjectLoader)
  • ✅ Domain tests no longer require mocking UI libraries
  • ✅ All 346 tests passing

Documentation

  • ✅ Added comprehensive CLI architecture guide to /docs/typescript-sdk/cli-architecture

Benefits

  • ✅ Better separation of concerns
  • ✅ Improved testability (no UI mocking for domain logic)
  • ✅ Consistent error handling across commands
  • ✅ Easier to maintain and extend
  • ✅ Domain logic reusable outside CLI context
  • ✅ Supports multiple output modes (normal, quiet, JSON)

Test Plan

  • All existing tests pass (346 passed, 5 skipped)
  • Build succeeds
  • TypeScript compilation succeeds
  • New unit tests for domain and presentation layers
  • Manually tested push and list-agents commands

Migration Status

Currently refactored:

  • push command
  • list-agents command

To be refactored in follow-up PRs:

  • pull command
  • init command
  • config command
  • add command
  • dev command
  • update command

🤖 Generated with Claude Code

amikofalvy and others added 2 commits October 16, 2025 12:26
…f concerns

Refactored CLI commands to use a clean layered architecture that separates
presentation, domain, and application logic.

## Changes

### Architecture
- Created 5-layer architecture: Commands → Application → Domain + Presentation → Infrastructure
- Separated UI concerns from business logic for better testability
- Introduced domain-specific error classes for clearer error handling

### Code Reduction
- push.ts: 284 lines → 35 lines (-88% reduction)
- list-agents.ts: 79 lines → 31 lines (-61% reduction)

### New Modules
**Presentation Layer** (src/presentation/):
- OutputService: Abstracts chalk for colored console output
- SpinnerService: Abstracts ora for progress indicators
- TableService: Abstracts cli-table3 for formatted tables
- CLIPresenter: Formats command-specific results

**Domain Layer** (src/domain/):
- ProjectLoader: Pure business logic for loading projects
- ProjectPusher: Pure business logic for pushing projects
- AgentLister: Pure business logic for listing agents
- Domain errors: ProjectNotFoundError, InvalidProjectError, etc.

**Application Layer** (src/application/):
- CommandHandler: Base class with standardized error handling
- PushCommandHandler: Orchestrates push command flow
- ListAgentsCommandHandler: Orchestrates list-agents flow

### Testing Improvements
- Added unit tests for presentation layer (OutputService)
- Added unit tests for domain layer (ProjectLoader)
- Domain tests no longer require mocking UI libraries
- All 346 tests passing

### Documentation
- Added comprehensive CLI architecture guide to agents-docs

## Benefits
- Better separation of concerns
- Improved testability (no UI mocking for domain logic)
- Consistent error handling across commands
- Easier to maintain and extend
- Domain logic reusable outside CLI context

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
…rvice

- Allow null values in AgentInfo.defaultSubAgentId to match API response
- Convert undefined to null in TableService.colWidths for cli-table3 compatibility

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Copy link

changeset-bot bot commented Oct 16, 2025

🦋 Changeset detected

Latest commit: 97b2eb1

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 7 packages
Name Type
@inkeep/agents-cli Patch
@inkeep/agents-core Patch
@inkeep/agents-manage-api Patch
@inkeep/agents-manage-ui Patch
@inkeep/agents-run-api Patch
@inkeep/agents-sdk Patch
@inkeep/create-agents Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Copy link

vercel bot commented Oct 16, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
agents-docs Ready Ready Preview Comment Oct 16, 2025 7:34pm
agents-manage-api Ready Ready Preview Comment Oct 16, 2025 7:34pm
agents-manage-ui Ready Ready Preview Comment Oct 16, 2025 7:34pm
agents-run-api Ready Ready Preview Comment Oct 16, 2025 7:34pm

Copy link

claude bot commented Oct 16, 2025

Claude finished @amikofalvy's task —— View job


🔍 PR Review: Layered Architecture Refactor

This is an excellent architectural refactor that transforms a monolithic CLI codebase into a clean, maintainable layered architecture. The work demonstrates strong software engineering principles and significant improvements across multiple dimensions.

Outstanding Work

Architecture Excellence

  • Perfect Layer Separation: Clean 5-layer architecture (Commands → Application → Domain + Presentation → Infrastructure) with proper dependency flow
  • Dramatic Code Reduction: 88% reduction in push.ts (284→35 lines), 61% reduction in list-agents.ts (79→31 lines)
  • Single Responsibility Principle: Each class has a focused, well-defined purpose
  • Dependency Injection: Clean DI pattern enables testability and flexibility

Testing Strategy

  • Domain Layer Tests: No UI mocking required! Tests pure business logic directly
  • Presentation Layer Tests: Proper separation allows targeted UI testing
  • Comprehensive Coverage: Tests for both success and error scenarios

Error Handling

  • Domain-Specific Errors: Well-designed error hierarchy with helpful messages
  • Contextual Hints: Error handling provides actionable guidance to users
  • Consistent Experience: Standardized error handling across all commands

🎯 Areas for Consideration

High Priority

1. Environment Variable Mutations in PushCommandHandler

Location: PushCommandHandler.ts:66-86

The temporary mutation of process.env variables is concerning:

// Set environment variables for the SDK to use during project construction
const originalTenantId = process.env.INKEEP_TENANT_ID;
process.env.INKEEP_TENANT_ID = config.tenantId;
// ... later restore

Issues:

  • Race Conditions: Multiple concurrent commands could interfere
  • State Pollution: Global state mutation makes testing harder
  • Side Effects: Violates functional programming principles

Recommendation: Pass configuration explicitly through the call chain instead of using global environment variables. Consider:

// Better approach
const project = await this.projectLoader.load({ 
  projectPath: options.project,
  config: { tenantId: config.tenantId, apiUrl: config.agentsManageApiUrl }
});

2. Missing TypeScript Type Safety

Location: PushCommandHandler.ts:132, 134

The use of any types reduces type safety:

private async handleJsonExport(
  project: any,  // Should be typed
  projectDir: string,
  config: any    // Should be typed
): Promise<void>

Recommendation: Define proper interfaces:

interface ProjectDefinition {
  id: string;
  name: string;
  // ... other properties
}

private async handleJsonExport(
  project: Project,
  projectDir: string,
  config: ValidatedConfiguration
): Promise<void>

3. Force Process Exit Pattern

Location: PushCommandHandler.ts:122, 151

Explicit process.exit(0) calls violate clean shutdown principles:

// Force exit to avoid hanging due to OpenTelemetry or other background tasks
process.exit(0);

Issues:

  • Testing Problems: Makes unit testing difficult
  • Resource Cleanup: Prevents proper cleanup of resources
  • Integration Issues: Problematic when CLI is used programmatically

Recommendation: Address the root cause (OpenTelemetry hanging) rather than forcing exits. Consider:

// Better approach - proper cleanup
try {
  await this.cleanupResources();
} finally {
  // Let process exit naturally
}

Medium Priority

4. Presentation Layer Abstraction Completeness

The presentation layer does great work abstracting chalk, ora, and cli-table3, but there are opportunities to make it more complete:

Missing Abstractions:

  • Console input/readline operations
  • File path display formatting
  • Progress bar variations

Suggestion: Consider if CLIPresenter should have more domain-specific methods for complex output scenarios.

5. Domain Layer Dependencies

Location: ProjectLoader.ts:4

The domain layer has some infrastructure dependencies:

import { importWithTypeScriptSupport } from '../utils/tsx-loader';

Consideration: While pragmatic, this creates a slight coupling to infrastructure. For pure domain isolation, consider dependency injection:

interface ModuleImporter {
  importModule(path: string): Promise<Record<string, unknown>>;
}

export class ProjectLoader {
  constructor(private importer: ModuleImporter) {}
}

Low Priority

6. Output Mode Implementation

The OutputService has JSON and QUIET modes defined, but JSON mode behavior isn't fully implemented. Consider completing this feature for consistency.

7. Error Stack Traces in Production

Location: CommandHandler.ts:96-98

Stack traces are shown based on env.DEBUG, but error context could be improved:

if (error.stack && env.DEBUG) {
  this.output.secondary(error.stack);
}

Consider providing more structured error information for debugging without full stack traces.

🏗️ Architecture Assessment

Consistency with Repository Patterns

  • Follows Repository Guidelines: Aligns perfectly with CLAUDE.md requirements
  • Testing Patterns: Uses Vitest with proper test structure
  • Documentation: Excellent MDX documentation following Fumadocs patterns
  • Builder Patterns: Consistent with existing agent builder patterns

Future Extensibility

  • Easy to Add Commands: Clear template for new command implementation
  • Pluggable Presentation: Output modes enable different UX approaches
  • Testable Domain Logic: Business logic can be reused in web UI or other contexts
  • Error Handling Standards: Consistent error experience across commands

Performance Considerations

  • Reduced Bundle Size: Cleaner imports and better tree-shaking opportunities
  • Lazy Loading: Services can be instantiated only when needed
  • ⚠️ Process Exit: The force-exit pattern might mask performance issues

📚 Documentation Excellence

The CLI architecture documentation (/agents-docs/content/docs/typescript-sdk/cli-architecture.mdx) is outstanding:

  • Clear layer explanations with examples
  • Migration guide for future refactoring
  • Testing strategy guidance
  • Concrete before/after comparisons

🚀 Migration Strategy

The incremental approach (refactor 2 of 8 commands) is smart. Recommendations for remaining commands:

  1. Next Candidates: pull and init (likely highest complexity)
  2. Pattern Reuse: Leverage the established patterns from push and list-agents
  3. Consistent Interface: Ensure all commands follow the same handler pattern

Final Assessment

This is exceptional architectural work that significantly improves the codebase. The refactor demonstrates:

  • ✅ Strong understanding of layered architecture principles
  • ✅ Excellent separation of concerns
  • ✅ Thoughtful error handling design
  • ✅ Comprehensive testing strategy
  • ✅ Outstanding documentation

The high-priority issues (environment variable mutations, any types, process exits) should be addressed before merge, but the overall architecture is sound and represents a major improvement.

Recommendation: Approve with requested changes - fix the high-priority items and this becomes a model for clean CLI architecture.


Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant