-
Notifications
You must be signed in to change notification settings - Fork 39
refactor(cli): implement layered architecture for better separation of concerns #720
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
…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]>
🦋 Changeset detectedLatest commit: 97b2eb1 The changes in this PR will be included in the next version bump. This PR includes changesets to release 7 packages
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 |
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Claude finished @amikofalvy's task —— View job 🔍 PR Review: Layered Architecture RefactorThis 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 WorkArchitecture Excellence
Testing Strategy
Error Handling
🎯 Areas for ConsiderationHigh Priority1. Environment Variable Mutations in PushCommandHandlerLocation: The temporary mutation of // 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:
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 SafetyLocation: The use of 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 PatternLocation: Explicit // Force exit to avoid hanging due to OpenTelemetry or other background tasks
process.exit(0); Issues:
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 Priority4. Presentation Layer Abstraction CompletenessThe presentation layer does great work abstracting chalk, ora, and cli-table3, but there are opportunities to make it more complete: Missing Abstractions:
Suggestion: Consider if 5. Domain Layer DependenciesLocation: 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 Priority6. Output Mode ImplementationThe 7. Error Stack Traces in ProductionLocation: Stack traces are shown based on if (error.stack && env.DEBUG) {
this.output.secondary(error.stack);
} Consider providing more structured error information for debugging without full stack traces. 🏗️ Architecture AssessmentConsistency with Repository Patterns
Future Extensibility
Performance Considerations
📚 Documentation ExcellenceThe CLI architecture documentation (
🚀 Migration StrategyThe incremental approach (refactor 2 of 8 commands) is smart. Recommendations for remaining commands:
Final AssessmentThis is exceptional architectural work that significantly improves the codebase. The refactor demonstrates:
The high-priority issues (environment variable mutations, Recommendation: Approve with requested changes - fix the high-priority items and this becomes a model for clean CLI architecture. |
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
Code Reduction
New Modules
Presentation Layer (
src/presentation/
):OutputService
: Abstracts chalk for colored console outputSpinnerService
: Abstracts ora for progress indicatorsTableService
: Abstracts cli-table3 for formatted tablesCLIPresenter
: Formats command-specific resultsDomain Layer (
src/domain/
):ProjectLoader
: Pure business logic for loading projectsProjectPusher
: Pure business logic for pushing projectsAgentLister
: Pure business logic for listing agentsProjectNotFoundError
,InvalidProjectError
, etc.Application Layer (
src/application/
):CommandHandler
: Base class with standardized error handlingPushCommandHandler
: Orchestrates push command flowListAgentsCommandHandler
: Orchestrates list-agents flowTesting Improvements
OutputService
)ProjectLoader
)Documentation
/docs/typescript-sdk/cli-architecture
Benefits
Test Plan
push
andlist-agents
commandsMigration Status
Currently refactored:
push
commandlist-agents
commandTo be refactored in follow-up PRs:
pull
commandinit
commandconfig
commandadd
commanddev
commandupdate
command🤖 Generated with Claude Code