Documentation: - Rewrite README.md with current features and git operations - Rewrite docs/ARCHITECTURE.md with layered architecture details - Rewrite docs/tui-ide-spec.md with Alt-key shortcuts - Add docs/code-organization.md for component architecture - Add docs/user-manual.md for end users - Update TODO.md to mark completed items Code fixes: - Fix undefined 'event' variable in diff_pane.py (was _event) - Use ternary operator in editor.py save_file method - Clean up imports in claude_events.py and syntax_service.py - Auto-fix import sorting across multiple files Config: - Add snapshot report path to pyproject.toml pytest options - Exclude clide/vendor from ruff linting - Ignore TCH002/TCH003 type-checking import rules Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
10 KiB
Code Organization
Clide follows a layered architecture separating UI components from business logic, enabling testability and maintainability.
Directory Structure
clide/
├── app.py # Main application, layout, keybindings
├── cli.py # Typer entry point
├── models/ # Pydantic data models
├── services/ # Background services and utilities
├── controllers/ # Business logic (no UI)
├── widgets/
│ ├── panels/ # Main layout containers
│ └── components/ # Reusable UI pieces
├── themes/ # Theme definitions and registry
├── extensions/ # Plugin system (hookspecs, manager)
├── templates/ # Bundled templates (skills, etc.)
└── vendor/ # Vendored dependencies (pyte)
Layers
Models (clide/models/)
Pure data structures using Pydantic with strict mode. Models are immutable and contain no business logic.
from pydantic import BaseModel, ConfigDict
class GitChange(BaseModel):
model_config = ConfigDict(strict=True, frozen=True)
path: str
status: Literal["added", "modified", "deleted", "untracked", "renamed"]
staged: bool
Key models:
git.py— Git-related types (GitBranch, GitCommit, GitChange, GitStatus)config.py— Application settings (ClideSettings, PanelConfig)problems.py— Linter output (Problem, Severity)todos.py— TODO items (TodoItem, ProjectTodoItem, TodoType)
Services (clide/services/)
Stateless utilities that perform work without UI interaction. Services may be async or use background threads.
class GitService:
"""Git operations via subprocess."""
def __init__(self, workdir: Path):
self._workdir = workdir
async def get_status(self) -> GitStatus:
"""Get current repository status."""
...
async def get_branches(self) -> list[GitBranch]:
"""List all branches."""
...
Key services:
git_service.py— Git CLI operationsfile_service.py— File read/write operationstodo_scanner.py— Scans codebase for TODO/FIXME commentssettings_service.py— User settings persistenceskill_installer.py— Claude Code skill managementfile_watcher.py— File system change monitoringsyntax_service.py— Tree-sitter syntax highlighting
Controllers (clide/controllers/)
Bridge between services and UI. Controllers contain business logic, manage state, and emit Textual messages. Controllers have no direct UI rendering.
from clide.controllers.base import controller
@controller
class GitController:
"""Manages git state and operations."""
def __init__(self, workdir: Path):
self._service = GitService(workdir)
self._status: GitStatus | None = None
async def refresh_status(self) -> GitStatus:
"""Refresh and cache git status."""
self._status = await self._service.get_status()
return self._status
def stage_file(self, path: str) -> None:
"""Stage a file for commit."""
...
Key controllers:
git.py— Git operations, skill integrationeditor.py— File editing statediff.py— Diff viewing and managementproblems.py— Linter integrationtodos.py— TODO trackingjira.py— Jira CLI integration
Widgets (clide/widgets/)
UI components split into panels (layout containers) and components (reusable pieces).
Panels (clide/widgets/panels/)
Top-level layout containers that compose the application UI.
class SidebarPanel(Vertical):
"""Left sidebar with Files, Git, and Tree tabs."""
class FileSelected(Message):
"""Emitted when a file is selected."""
def __init__(self, path: Path):
self.path = path
super().__init__()
def compose(self) -> ComposeResult:
with TabbedContent():
with TabPane("Files"):
yield FilesView(path=self._workdir)
with TabPane("Git"):
yield GitChangesView()
with TabPane("Tree"):
yield GitGraphView()
yield BranchStatus()
Panels:
sidebar.py— Left sidebar (files, git, graph)context.py— Right sidebar (problems, todos, jira)workspace.py— Center workspace (editor, diff, terminal)claude.py— Claude Code terminal integration
Components (clide/widgets/components/)
Reusable UI pieces composed into panels.
File browsing:
files_view.py— Project file treefile_entry.py— Single file/directory entry
Git:
git_changes.py— Staged/unstaged file listsgit_graph.py— Visual branch graphbranch_status.py— Branch indicator with popout selector
Context:
problems_view.py— Linter problems listtodos_view.py— TODO/FIXME list with sub-tabsjira_view.py— Jira issue display
Editor:
editor_pane.py— Code editor with syntax highlightingdiff_pane.py— Side-by-side diff viewerterminal_pane.py— Command execution terminal
Communication Patterns
Message Flow
Components communicate via Textual's message system. Messages bubble up through the widget tree.
Component emits message
│
▼
Parent panel receives and may re-emit
│
▼
App handles and coordinates response
│
▼
App calls controller methods
│
▼
Controller updates state, may emit messages
│
▼
UI updates reactively
Example: File selection
# In FilesView (component)
class FileSelected(Message):
def __init__(self, path: Path):
self.path = path
super().__init__()
def on_tree_node_selected(self, event):
if event.node.data.is_file:
self.post_message(self.FileSelected(event.node.data.path))
# In SidebarPanel (panel)
def on_files_view_file_selected(self, event: FilesView.FileSelected):
# Re-emit for app to handle
self.post_message(self.FileSelected(event.path))
# In ClideApp (app)
def on_sidebar_panel_file_selected(self, event: SidebarPanel.FileSelected):
self.editor_controller.open_file(event.path)
self.show_workspace("editor")
Reactive Properties
State that affects UI uses Textual's reactive system:
class ClideApp(App):
# Reactive state
workspace_visible: reactive[bool] = reactive(False)
problem_count: reactive[int] = reactive(0)
current_branch: reactive[str] = reactive("main")
def watch_workspace_visible(self, visible: bool) -> None:
"""React to workspace visibility changes."""
workspace = self.query_one("#panel-workspace")
workspace.display = visible
claude = self.query_one("#panel-claude")
claude.styles.height = "40%" if visible else "100%"
Background Tasks
Long-running operations use the @work decorator to avoid blocking the UI:
from textual import work
class ClideApp(App):
@work(thread=True)
def refresh_git_status(self) -> None:
"""Refresh git status in background."""
status = self.git_controller.get_status_sync()
self.call_from_thread(self._update_git_ui, status)
def _update_git_ui(self, status: GitStatus) -> None:
"""Update UI with git status (runs on main thread)."""
sidebar = self.query_one(SidebarPanel)
sidebar.update_git_status(status.staged, status.unstaged)
Extension System
Clide uses Pluggy for extensibility. Extensions implement hooks defined in hookspecs.py.
# clide/extensions/hookspecs.py
class ClideHookSpec:
@hookspec
def clide_startup(self, app: App) -> None:
"""Called when app starts."""
@hookspec
def clide_on_file_changed(self, event: FileEvent) -> None:
"""Called when a file changes."""
# User extension
class MyExtension:
@hookimpl
def clide_on_file_changed(self, event: FileEvent) -> None:
if event.path.suffix == ".py":
# Custom logic for Python files
...
Available hooks:
clide_startup— App initializationclide_shutdown— App cleanupclide_on_file_changed— File system changesclide_on_file_saved— File saved in editor
Skills System
Clide integrates with Claude Code skills for git operations. Skills are installed to the project's .claude/skills/ directory.
# clide/services/skill_installer.py
class SkillInstaller:
def install(self, skill_name: str, scope: Literal["user", "project"] = "project"):
"""Install a skill from bundled templates."""
template_dir = TEMPLATES_DIR / skill_name
target_dir = self.project_skills_dir / skill_name
shutil.copytree(template_dir, target_dir)
Bundled skills (clide/templates/skills/):
commit— Git commit workflowstash— Git stash operationspull— Git pull with rebasepush— Git push to remotebranch— Branch management
When a git action button is clicked, Clide ensures the skill is installed before sending the command to Claude.
Testing
Tests mirror the source structure:
tests/
├── unit/
│ ├── test_models.py
│ ├── test_services.py
│ ├── test_controllers.py
│ ├── test_widgets.py
│ └── test_app.py
├── integration/
│ └── test_files_view.py
└── snapshots/
└── test_app_snapshots.py
Unit tests verify individual components in isolation. Integration tests verify component interactions. Snapshot tests catch visual regressions using pytest-textual-snapshot.
Configuration
Application Settings
ClideSettings in clide/models/config.py defines app configuration loaded from environment or .config/settings.toml.
User Settings
UserSettings persisted to ~/.clide/settings.json stores user preferences:
- Theme selection
- Panel visibility defaults
- Compact mode preference
- Jira integration settings