Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
uses: actions-rust-lang/setup-rust-toolchain@v1

- name: Run tests
run: cargo test
run: cargo test --target x86_64-unknown-linux-gnu

build-wasm:
runs-on: ubuntu-latest
Expand Down
174 changes: 174 additions & 0 deletions .planning/codebase/ARCHITECTURE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
# Architecture

**Analysis Date:** 2026-01-13

## Pattern Overview

**Overall:** Plugin-based architecture with layered state management and event-driven processing

**Key Characteristics:**
- Zellij plugin using WASM (WebAssembly) with WASI sandbox
- Event-driven: Zellij sends events → Plugin processes → State updates → UI renders
- Single executable with two-screen UI (Main list, NewSession creation)
- Stateless request handling within plugin instance

## Layers

**Plugin Layer:**
- Purpose: Zellij plugin trait implementation, event dispatcher
- Contains: `ZellijPlugin` trait methods (`load`, `update`, `pipe`, `render`)
- Location: `src/main.rs`
- Depends on: State layer for coordination
- Used by: Zellij runtime

**State Layer:**
- Purpose: Central state management, business logic coordination
- Contains: `PluginState` struct, key handlers, item combination logic
- Location: `src/state.rs`
- Depends on: Config, Session, Zoxide, NewSessionInfo, UI modules
- Used by: Plugin layer

**Session Layer:**
- Purpose: Zellij session operations, lifecycle management, stability tracking
- Contains: `SessionManager`, `SessionItem`, `SessionAction`
- Location: `src/session/manager.rs`, `src/session/types.rs`
- Depends on: No internal dependencies (isolated)
- Used by: State layer

**Zoxide Layer:**
- Purpose: Directory discovery and fuzzy search
- Contains: `ZoxideDirectory`, `SearchEngine`
- Location: `src/zoxide/directory.rs`, `src/zoxide/search.rs`
- Depends on: Session types (for `SessionItem`)
- Used by: State layer

**UI Layer:**
- Purpose: Terminal rendering
- Contains: `PluginRenderer`, `Theme`, `Colors`, components
- Location: `src/ui/renderer.rs`, `src/ui/theme.rs`, `src/ui/components.rs`
- Depends on: Session types, State for display data
- Used by: Plugin layer (render method)

**Configuration Layer:**
- Purpose: Zellij layout-based configuration parsing
- Contains: `Config` struct
- Location: `src/config.rs`
- Depends on: None
- Used by: State layer

## Data Flow

**Plugin Initialization:**

1. `ZellijPlugin::load()` in `src/main.rs:16` → Registers plugin with Zellij
2. Requests permissions (RunCommands, ReadApplicationState, ChangeApplicationState, MessageAndLaunchOtherPlugins)
3. Subscribes to events (ModeUpdate, SessionUpdate, Key, RunCommandResult, PermissionRequestResult)

**Permission Grant & Zoxide Query:**

1. User receives PermissionRequestResult → `update()` in `src/main.rs:50`
2. `fetch_zoxide_directories()` runs `zoxide query -l -s` command
3. Results returned via RunCommandResult event

**Zoxide Output Processing:**

1. `process_zoxide_output()` in `src/main.rs:161` parses score+path lines
2. `generate_smart_session_names()` creates context-aware names (`src/main.rs:197-235`)
3. Names conflict-resolved with parent context and truncated to 29 chars
4. `update_zoxide_directories()` in `src/state.rs:102` stores directories

**Session Updates:**

1. Zellij sends SessionUpdate event with current and resurrectable sessions
2. `update_sessions()` in `src/state.rs:57` uses stability tracking (MISSING_THRESHOLD=3)
3. Prevents UI flickering from Zellij's inconsistent event timing

**User Interaction:**

1. Key event in `update()` → `handle_key()` in `src/state.rs:108`
2. Routes to main screen or new session screen handlers
3. Actions trigger session switches, deletions, or new session creation

**State Management:**
- File-based state for previous session: `/tmp/zsm-previous-session`
- Each plugin instance has isolated state (per Zellij session)
- State does not transfer between sessions automatically

## Key Abstractions

**SessionManager:**
- Purpose: Orchestrates Zellij session operations with stability tracking
- Examples: `update_sessions_stable()`, `execute_action()`, `generate_incremented_name()`
- Location: `src/session/manager.rs`
- Pattern: Stability threshold (MISSING_THRESHOLD=3) prevents UI flicker from inconsistent Zellij events

**SessionItem:**
- Purpose: Represents displayable items in the session list
- Examples: `ExistingSession`, `ResurrectableSession`, `Directory`
- Location: `src/session/types.rs`
- Pattern: Enum with variant data

**SearchEngine:**
- Purpose: Fuzzy matching for search functionality
- Examples: `search()`, `update_search()`, `get_results()`
- Location: `src/zoxide/search.rs`
- Pattern: Uses `SkimMatcherV2` from `fuzzy-matcher` crate

**PluginState:**
- Purpose: Central orchestrator holding all plugin state
- Examples: `combined_items()`, `display_items()`, `handle_key()`
- Location: `src/state.rs`
- Pattern: Singleton state container, coordinates between all layers

## Entry Points

**Plugin Entry:**
- Location: `src/main.rs:13` - `register_plugin!(PluginState)` macro
- Triggers: Zellij loads plugin WASM binary
- Responsibilities: Register plugin with Zellij runtime

**ZellijPlugin Implementation:**
- Location: `src/main.rs:15-152`
- Triggers: Zellij events (permissions, keys, session updates)
- Responsibilities: Event dispatch, zoxide command execution, rendering

**Renderer Entry:**
- Location: `src/ui/renderer.rs:14`
- Triggers: Zellij render call
- Responsibilities: Determine active screen, render UI, display overlays

## Error Handling

**Strategy:** Uses `Option<T>` for nullable values, UI error display for user feedback

**Patterns:**
- Validation errors shown via `.set_error()` method (`src/state.rs:26`)
- Error cleared on next keypress
- Permission denial shows error, prevents zoxide fetch
- Invalid session names blocked with descriptive messages

## Cross-Cutting Concerns

**Logging:**
- Plugin logs to Zellij's plugin log output
- No external logging framework

**Validation:**
- Session name validation at creation time (`src/state.rs:614-622`)
- Max 108 bytes, no `/` characters
- Path validation for zoxide results

**Smart Session Naming:**
- Complex algorithm spanning `src/main.rs:197-502`
- Conflict detection, context-aware naming, truncation to 29 chars
- Respects Unix socket path limits

**WASM Sandbox Handling:**
- Direct filesystem writes use sandboxed paths
- Shelling out via `run_command` for persistent file operations
- Inter-plugin communication via `pipe_message_to_plugin`

---

*Architecture analysis: 2026-01-13*
*Update when major patterns change*
165 changes: 165 additions & 0 deletions .planning/codebase/CONCERNS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
# Codebase Concerns

**Analysis Date:** 2026-01-13

## Tech Debt

**Code Duplication - Range Calculation:**
- Issue: Duplicate range-to-render logic
- Location: `src/new_session_info.rs:271` - TODO comment: "merge with similar function in zoxide_directories"
- Why: Rapid development, similar patterns emerged independently
- Impact: Maintenance burden, risk of divergent behavior
- Fix approach: Extract shared utility function

**Complex Smart Naming Algorithm:**
- Issue: 300+ lines of interrelated functions for session name generation
- Location: `src/main.rs:197-502` - `generate_smart_session_names`, `generate_context_aware_name`, `normalize_path`, `apply_smart_truncation`, `abbreviate_segment`
- Why: Feature complexity (conflict detection, context-awareness, truncation limits)
- Impact: Difficult to test, maintain, and debug
- Fix approach: Extract to dedicated module, add comprehensive unit tests

## Known Bugs

**None identified during analysis.**

The codebase appears stable with no obvious bugs. Session stability tracking specifically addresses Zellij's inconsistent event behavior.

## Security Considerations

**Shell Command Injection (Medium Risk):**
- Risk: Session names interpolated into shell commands without escaping
- Location: `src/state.rs:675-686` (write_previous_session function)
- Code: `format!("echo '{}' > /tmp/zsm-previous-session", session_name)`
- Current mitigation: Session name validation blocks `/` characters (`src/state.rs:619-621`)
- Recommendations: Use shell-safe escaping, or write directly if WASM sandbox permits

**World-Writable Temp File:**
- Risk: `/tmp/zsm-previous-session` is world-readable/writable
- Location: `src/state.rs:675, 690`
- Current mitigation: File only contains session name (low sensitivity)
- Recommendations: Use user-specific path or XDG_RUNTIME_DIR

## Performance Bottlenecks

**None identified.**

The codebase handles small datasets (typically <100 directories/sessions). No performance issues expected at current scale.

**Potential Future Concern - Combined Items Rebuild:**
- Location: `src/state.rs:524-529` (update_search_if_needed)
- Pattern: `combined_items()` rebuilds full list on every search update
- Impact: Low with current dataset sizes
- Improvement path: Cache combined items, use incremental updates

## Fragile Areas

**Smart Session Naming Algorithm:**
- Location: `src/main.rs:197-502`
- Why fragile: Complex interdependent functions, many edge cases (Unicode, symlinks, deep paths)
- Common failures: Edge cases with unusual path structures
- Safe modification: Add comprehensive unit tests before changes
- Test coverage: **None** - HIGH RISK

**WASM Sandbox Limitations:**
- Location: `src/state.rs:675-702` (file I/O workarounds)
- Why fragile: Relies on shelling out to bypass WASM restrictions
- Common failures: File persistence issues, race conditions between plugin instances
- Safe modification: Understand WASM sandbox constraints before changes
- Test coverage: Cannot test in isolation (WASM-specific)

## Scaling Limits

**Not applicable.**

Plugin runs locally with user's directory history. No cloud services or shared resources.

## Dependencies at Risk

**All dependencies current (as of Jan 2026):**
- `zellij-tile 0.43.1` - Actively maintained with Zellij
- `zellij-utils 0.43.1` - Same lifecycle as zellij-tile
- `serde 1.0.164` - Stable, widely used
- `fuzzy-matcher 0.3.7` - Stable, minimal updates expected
- `uuid 1.8.0` - Stable, widely used
- `humantime 2.2.0` - Stable, low update frequency

No immediate risks identified.

## Missing Critical Features

**None identified.**

The plugin provides complete session/directory management functionality as designed.

## Test Coverage Gaps

**Smart Naming Algorithm (HIGH PRIORITY):**
- What's not tested: 503 lines of logic in `src/main.rs` - conflict detection, context naming, truncation
- Location: `src/main.rs:197-502`
- Risk: Naming bugs affect user experience, edge cases unknown
- Priority: HIGH
- Difficulty to test: Medium - need to mock or construct path data

**State Coordination Logic:**
- What's not tested: 709 lines in `src/state.rs` - display logic, selection, item combination
- Location: `src/state.rs`
- Risk: State management bugs could cause UI issues
- Priority: Medium
- Difficulty to test: Medium - need to simulate events

**Fuzzy Search Engine:**
- What's not tested: 204 lines in `src/zoxide/search.rs`
- Location: `src/zoxide/search.rs`
- Risk: Search ranking issues
- Priority: Low (uses well-tested fuzzy-matcher crate)
- Difficulty to test: Low - pure function logic

**UI Rendering:**
- What's not tested: 431 lines in `src/ui/renderer.rs`
- Location: `src/ui/renderer.rs`
- Risk: Display issues
- Priority: Low (visual verification during development)
- Difficulty to test: High - Zellij rendering API

## Minor Issues

**Error Messages Clear on Any Keypress:**
- Location: `src/state.rs:108-113`
- Behavior: Any keypress clears error, even if user didn't read it
- UX impact: Users might miss validation errors
- Recommendation: Only clear errors on meaningful actions

**Unicode Truncation Edge Case:**
- Location: `src/main.rs:343-344`
- Problem: `.len()` counts bytes, not characters; multibyte UTF-8 could truncate mid-character
- Impact: Low probability (requires non-ASCII paths)
- Recommendation: Use `.chars().count()` or check byte boundaries

**Configuration Delimiter Fragility:**
- Location: `src/config.rs:43-51`
- Issue: `base_paths` uses `|` delimiter with no escape mechanism
- Impact: Cannot use `|` in paths (edge case)
- Recommendation: Document limitation or add escape support

---

## Summary

**Overall Assessment:** Clean, well-structured codebase with no critical issues.

**Priorities:**
1. **HIGH**: Add unit tests for smart naming algorithm (`src/main.rs:197-502`)
2. **MEDIUM**: Address shell command injection concern in session persistence
3. **LOW**: Extract duplicate range calculation logic

**Positive Notes:**
- No `unsafe` blocks
- No unchecked `unwrap()` or `expect()` calls
- Good inline documentation
- CI/CD properly configured with tests, clippy, and fmt checks
- Error handling is defensive and informative

---

*Concerns audit: 2026-01-13*
*Update as issues are fixed or new ones discovered*
Loading
Loading