feat(targets): add multi-target CLI adapter system (Droid support)

Implement target adapter pattern enabling CCS CLI to support multiple backend targets
(Claude, Droid) via pluggable adapters. Core additions:

- TargetAdapter interface for pluggable target implementations
- ClaudeAdapter and DroidAdapter concrete implementations
- Target registry (singleton Map-based storage)
- Target resolver with precedence: --target flag > per-profile config > busybox detection
- Droid config manager with atomic writes and file locking to ~/.factory/settings.json
- Droid binary detector to validate runtime environment
- Adapter dispatch integrated into ccs.ts main execution flow
- ccsd busybox alias for seamless Droid invocation
- --target flag documentation in help
- Session tracking enriched with target metadata
- Dashboard target badge for visual identification

Testing:
- 43 unit tests covering resolver, registry, config manager, and adapters
- Full coverage of target detection logic and edge cases

Documentation:
- Refactored system-architecture.md into modular docs/system-architecture/ subdirectory
- Updated code-standards.md with target adapter guidelines
- Updated codebase-summary.md with architecture overview
- Updated maintainability baseline (33.8% → 35.2%)

This establishes extensible foundation for multi-target support without breaking
existing Claude workflows. Droid adapter is production-ready but defaults to Claude
for backward compatibility.
This commit is contained in:
Tam Nhu Tran committed 2026-02-16 10:49:09 +07:00
1 parent 8ab78f039c
commit 7d7054e2c0
27 files changed
+3157 -766

No files matched your search

@@ -133,9 +133,16 @@ export function SessionStatsCard({ data, isLoading, className }: SessionStatsCar
className="flex items-center justify-between text-xs p-1.5 rounded bg-muted/30 hover:bg-muted/50 transition-colors"
>
<div className="flex flex-col min-w-0 flex-1">
<span className="font-medium truncate" title={session.projectPath}>
{getProjectDisplayName(session.projectPath)}
</span>
<div className="flex items-center gap-1">
<span className="font-medium truncate" title={session.projectPath}>
{getProjectDisplayName(session.projectPath)}
</span>
{(session.target ?? 'claude') !== 'claude' && (
<span className="shrink-0 px-1 py-0 text-[9px] font-medium rounded bg-purple-100 text-purple-700 dark:bg-purple-900/30 dark:text-purple-300 uppercase">
{session.target}
</span>
)}
</div>
<span className="text-[10px] text-muted-foreground">
{formatDistanceToNow(new Date(session.lastActivity), { addSuffix: true })}
</span>
+2
View File
@@ -100,6 +100,8 @@ export interface Session {
cost: number;
lastActivity: string;
modelsUsed: string[];
/** Target CLI used (default: 'claude') */
target?: string;
}
export interface PaginatedSessions {