Skip to content

Commit bb5af92

Browse files
authored
Merge pull request #7 from runloopai/dines/fixes
Refactor, theme and speed improvements
2 parents f36aef7 + 75c6343 commit bb5af92

143 files changed

Lines changed: 11712 additions & 6805 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.cursor/worktrees.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"setup-worktree": [
3+
"npm install"
4+
]
5+
}

ARCHITECTURE_REFACTOR_COMPLETE.md

Lines changed: 311 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,311 @@
1+
# CLI Architecture Refactor - Complete ✅
2+
3+
## Date: October 24, 2025
4+
5+
## Summary
6+
7+
Successfully refactored the CLI application from a memory-leaking multi-instance pattern to a **single persistent Ink app** with proper state management and navigation.
8+
9+
## What Was Done
10+
11+
### Phase 1: Dependencies & Infrastructure ✅
12+
13+
**Added:**
14+
- `zustand` v5.0.2 for state management
15+
16+
**Created:**
17+
- `src/store/navigationStore.ts` - Navigation state with stack-based routing
18+
- `src/store/devboxStore.ts` - Devbox list state with pagination and caching
19+
- `src/store/blueprintStore.ts` - Blueprint list state
20+
- `src/store/snapshotStore.ts` - Snapshot list state
21+
- `src/store/index.ts` - Root store exports
22+
23+
### Phase 2: API Service Layer ✅
24+
25+
**Created:**
26+
- `src/services/devboxService.ts` - Centralized API calls for devboxes
27+
- `src/services/blueprintService.ts` - Centralized API calls for blueprints
28+
- `src/services/snapshotService.ts` - Centralized API calls for snapshots
29+
30+
**Key Features:**
31+
- Defensive copying of API responses to break references
32+
- Plain data returns (no SDK object retention)
33+
- Explicit nullification to aid garbage collection
34+
35+
### Phase 3: Router Infrastructure ✅
36+
37+
**Created:**
38+
- `src/router/types.ts` - Screen types and route interfaces
39+
- `src/router/Router.tsx` - Stack-based router with memory cleanup
40+
41+
**Features:**
42+
- Single screen component mounted at a time
43+
- Automatic store cleanup on route changes
44+
- Memory monitoring integration
45+
- 100ms cleanup delay to allow unmount
46+
47+
### Phase 4: Screen Components ✅
48+
49+
**Created:**
50+
- `src/screens/MenuScreen.tsx` - Main menu wrapper
51+
- `src/screens/DevboxListScreen.tsx` - Pure UI component using devboxStore
52+
- `src/screens/DevboxDetailScreen.tsx` - Detail view wrapper
53+
- `src/screens/DevboxActionsScreen.tsx` - Actions menu wrapper
54+
- `src/screens/DevboxCreateScreen.tsx` - Create form wrapper
55+
- `src/screens/BlueprintListScreen.tsx` - Blueprint list wrapper
56+
- `src/screens/SnapshotListScreen.tsx` - Snapshot list wrapper
57+
58+
**Key Improvements:**
59+
- DevboxListScreen is fully refactored with store-based state
60+
- No useState/useRef for heavy data
61+
- React.memo for performance
62+
- Clean mount/unmount lifecycle
63+
- All operations use navigation store
64+
65+
### Phase 5: Wiring & Integration ✅
66+
67+
**Updated:**
68+
- `src/commands/menu.tsx` - Now uses Router component and screen registry
69+
- Screen names changed: `"devboxes"``"devbox-list"`, etc.
70+
- SSH flow updated to return to `"devbox-list"` after session
71+
72+
**Pattern:**
73+
```typescript
74+
// Before: Multiple Ink instances per screen
75+
render(<ListDevboxesUI ... />); // New instance
76+
77+
// After: Single Ink instance, router switches screens
78+
<Router screens={{ "devbox-list": DevboxListScreen, ... }} />
79+
```
80+
81+
### Phase 6: Memory Management ✅
82+
83+
**Created:**
84+
- `src/utils/memoryMonitor.ts` - Development memory tracking
85+
86+
**Features:**
87+
- `logMemoryUsage(label)` - Logs heap usage with deltas
88+
- `getMemoryPressure()` - Returns low/medium/high
89+
- `shouldTriggerGC()` - Detects when GC is needed
90+
- Enabled with `NODE_ENV=development` or `DEBUG_MEMORY=1`
91+
92+
**Enhanced:**
93+
- Router with memory logging on route changes
94+
- Store cleanup with 100ms delay
95+
- Context-aware cleanup (stays in devbox context → keeps cache)
96+
97+
### Phase 7: Testing & Validation 🔄
98+
99+
**Ready for:**
100+
- Rapid screen transitions (list → detail → actions → back × 100)
101+
- Memory monitoring: `DEBUG_MEMORY=1 npm start`
102+
- SSH flow testing
103+
- All list commands (devbox, blueprint, snapshot)
104+
105+
## Architecture Comparison
106+
107+
### Before (Memory Leak Pattern)
108+
109+
```
110+
CLI Entry → Multiple Ink Instances
111+
112+
CommandExecutor.executeList()
113+
114+
New React Tree Per Screen
115+
116+
Heavy State in Components
117+
118+
Direct SDK Calls
119+
120+
🔴 Objects Retained, Heap Exhaustion
121+
```
122+
123+
### After (Single Instance Pattern)
124+
125+
```
126+
CLI Entry → Single Ink Instance
127+
128+
Router
129+
130+
Screen Components (Pure UI)
131+
132+
State Stores (Zustand)
133+
134+
API Services
135+
136+
✅ Clean Unmount, Memory Freed
137+
```
138+
139+
## Key Benefits
140+
141+
1. **Memory Stability**: Expected reduction from 4GB heap exhaustion to ~200-400MB sustained
142+
2. **Clean Lifecycle**: Components mount/unmount properly, freeing memory
143+
3. **Single Source of Truth**: State lives in stores, not scattered across components
144+
4. **No Recursion**: Stack-based navigation, not recursive function calls
145+
5. **Explicit Cleanup**: Stores have cleanup methods called by router
146+
6. **Monitoring**: Built-in memory tracking for debugging
147+
7. **Maintainability**: Clear separation of concerns (UI, State, API)
148+
149+
## File Structure
150+
151+
```
152+
src/
153+
├── store/
154+
│ ├── index.ts
155+
│ ├── navigationStore.ts
156+
│ ├── devboxStore.ts
157+
│ ├── blueprintStore.ts
158+
│ └── snapshotStore.ts
159+
├── services/
160+
│ ├── devboxService.ts
161+
│ ├── blueprintService.ts
162+
│ └── snapshotService.ts
163+
├── router/
164+
│ ├── types.ts
165+
│ └── Router.tsx
166+
├── screens/
167+
│ ├── MenuScreen.tsx
168+
│ ├── DevboxListScreen.tsx
169+
│ ├── DevboxDetailScreen.tsx
170+
│ ├── DevboxActionsScreen.tsx
171+
│ ├── DevboxCreateScreen.tsx
172+
│ ├── BlueprintListScreen.tsx
173+
│ └── SnapshotListScreen.tsx
174+
├── utils/
175+
│ └── memoryMonitor.ts
176+
└── commands/
177+
└── menu.tsx (refactored to use Router)
178+
```
179+
180+
## Breaking Changes
181+
182+
### Screen Names
183+
- `"devboxes"``"devbox-list"`
184+
- `"blueprints"``"blueprint-list"`
185+
- `"snapshots"``"snapshot-list"`
186+
187+
### Navigation API
188+
```typescript
189+
// Before
190+
setShowDetails(true);
191+
192+
// After
193+
push("devbox-detail", { devboxId: "..." });
194+
```
195+
196+
### State Access
197+
```typescript
198+
// Before
199+
const [devboxes, setDevboxes] = useState([]);
200+
201+
// After
202+
const devboxes = useDevboxStore((state) => state.devboxes);
203+
```
204+
205+
## Testing Instructions
206+
207+
### Memory Monitoring
208+
```bash
209+
# Enable memory logging
210+
DEBUG_MEMORY=1 npm start
211+
212+
# Test rapid transitions
213+
# Navigate: devbox list → detail → actions → back
214+
# Repeat 100 times
215+
# Watch for: Stable memory, no heap exhaustion
216+
```
217+
218+
### Functional Testing
219+
```bash
220+
# Test all navigation paths
221+
npm start
222+
# → Select "Devboxes"
223+
# → Select a devbox
224+
# → Press "a" for actions
225+
# → Test each operation
226+
# → Press Esc to go back
227+
# → Press "c" to create
228+
# → Test SSH flow
229+
```
230+
231+
### Memory Validation
232+
```bash
233+
# Before refactor: 4GB heap exhaustion after ~50 transitions
234+
# After refactor: Stable ~200-400MB sustained
235+
236+
# Look for these logs:
237+
[MEMORY] Route change: devbox-list → devbox-detail: Heap X/YMB, RSS ZMB
238+
[MEMORY] Cleared devbox store: Heap X/YMB, RSS ZMB (Δ -AMB)
239+
```
240+
241+
## Known Limitations
242+
243+
1. **Blueprint/Snapshot screens**: Currently wrappers around old components
244+
- These still use old pattern internally
245+
- Can be refactored later using DevboxListScreen as template
246+
247+
2. **Menu component**: MainMenu still renders inline
248+
- Works fine, but could be refactored to use navigation store directly
249+
250+
3. **Memory monitoring**: Only in development mode
251+
- Should not impact production performance
252+
253+
## Future Improvements
254+
255+
1. **Full refactor of blueprint/snapshot lists**
256+
- Apply same pattern as DevboxListScreen
257+
- Move to stores + services
258+
259+
2. **Better error boundaries**
260+
- Add error boundaries around screens
261+
- Graceful error recovery
262+
263+
3. **Prefetching**
264+
- Prefetch next page while viewing current
265+
- Smoother pagination
266+
267+
4. **Persistent cache**
268+
- Save cache to disk for faster restarts
269+
- LRU eviction policy
270+
271+
5. **Animation/transitions**
272+
- Smooth screen transitions
273+
- Loading skeletons
274+
275+
## Success Criteria
276+
277+
✅ Build passes without errors
278+
✅ Single Ink instance running
279+
✅ Router controls all navigation
280+
✅ Stores manage all state
281+
✅ Services handle all API calls
282+
✅ Memory monitoring in place
283+
✅ Cleanup on route changes
284+
285+
🔄 **Awaiting manual testing:**
286+
- Rapid transition test (100x)
287+
- Memory stability verification
288+
- SSH flow validation
289+
- All operations functional
290+
291+
## Rollback Plan
292+
293+
If issues arise, the old components still exist:
294+
- `src/components/DevboxDetailPage.tsx`
295+
- `src/components/DevboxActionsMenu.tsx`
296+
- `src/commands/devbox/list.tsx` (old code commented)
297+
298+
Can revert `menu.tsx` to use old pattern if needed.
299+
300+
## Conclusion
301+
302+
The architecture refactor is **COMPLETE** and ready for testing. The application now follows modern React patterns with proper state management, clean lifecycle, and explicit memory cleanup.
303+
304+
**Expected Impact:**
305+
- 🎯 Memory: 4GB → 200-400MB
306+
- 🎯 Stability: Heap exhaustion → Sustained operation
307+
- 🎯 Maintainability: Significantly improved
308+
- 🎯 Speed: Slightly faster (no Ink instance creation overhead)
309+
310+
**Next Step:** Run the application and perform Phase 7 testing to validate memory improvements.
311+

0 commit comments

Comments
 (0)