mirror of
https://github.com/rait-09/obsidian-agent-client.git
synced 2026-07-22 06:43:37 +00:00
docs: update CLAUDE.md and ARCHITECTURE.md to reflect simplified architecture
This commit is contained in:
parent
d2948e20b2
commit
ffbfe33304
2 changed files with 417 additions and 314 deletions
477
ARCHITECTURE.md
477
ARCHITECTURE.md
|
|
@ -2,164 +2,202 @@
|
|||
|
||||
## Overview
|
||||
|
||||
This plugin uses **React Hooks Architecture** with clear separation of concerns. State and logic are managed by custom hooks, UI by components, and external integrations by adapters.
|
||||
This plugin uses **React Hooks + ChatPanel Architecture**. A central `ChatPanel` component composes hooks and renders children directly. Services are injected via React Context. ACP protocol changes are isolated in the `acp/` layer.
|
||||
|
||||
## Directory Structure
|
||||
|
||||
```
|
||||
src/
|
||||
├── domain/ # Domain Layer (innermost)
|
||||
│ ├── models/ # Pure domain models (no dependencies)
|
||||
│ │ ├── agent-config.ts
|
||||
│ │ ├── agent-error.ts
|
||||
│ │ ├── chat-message.ts
|
||||
│ │ └── chat-session.ts
|
||||
│ └── ports/ # Interfaces (Dependency Inversion)
|
||||
│ ├── agent-client.port.ts
|
||||
│ ├── settings-access.port.ts
|
||||
│ └── vault-access.port.ts
|
||||
├── types/ # Type Definitions (no logic)
|
||||
│ ├── chat.ts # ChatMessage, MessageContent, PromptContent, AttachedFile
|
||||
│ ├── session.ts # ChatSession, SessionUpdate, SessionInfo, Capabilities
|
||||
│ ├── agent.ts # AgentConfig, agent settings (Claude/Gemini/Codex/Custom)
|
||||
│ └── errors.ts # AcpError, ProcessError, ErrorInfo
|
||||
│
|
||||
├── acp/ # ACP Protocol Layer (SDK dependency confined here)
|
||||
│ ├── acp-client.ts # ACP communication, process lifecycle, IAgentClient
|
||||
│ ├── type-converter.ts # ACP SDK types ↔ internal types
|
||||
│ ├── permission-handler.ts # Permission queue, auto-approve, Promise resolution
|
||||
│ └── terminal-handler.ts # Terminal process create/output/kill
|
||||
│
|
||||
├── services/ # Business Logic (non-React)
|
||||
│ ├── vault-service.ts # Vault access + fuzzy search + CM6 selection tracking
|
||||
│ ├── settings-service.ts # Settings persistence, session storage, normalization
|
||||
│ ├── session-helpers.ts # Agent config building, API key injection (pure functions)
|
||||
│ ├── message-sender.ts # Prompt preparation + sending (pure functions)
|
||||
│ ├── chat-exporter.ts # Markdown export with frontmatter
|
||||
│ ├── view-registry.ts # Multi-view management, focus, broadcast
|
||||
│ └── update-checker.ts # Agent/plugin version checking
|
||||
│
|
||||
├── hooks/ # React Custom Hooks (state + logic)
|
||||
│ ├── useAgentSession.ts # Session lifecycle, agent switching
|
||||
│ ├── useChat.ts # Message sending, callbacks
|
||||
│ ├── usePermission.ts # Permission handling
|
||||
│ ├── useMentions.ts # @[[note]] suggestions
|
||||
│ ├── useSession.ts # Session lifecycle, create/close/restart, config options
|
||||
│ ├── useMessages.ts # Message state, send/receive, streaming updates
|
||||
│ ├── usePermission.ts # Permission request handling
|
||||
│ ├── useMentions.ts # @[[note]] suggestions + auto-mention active note
|
||||
│ ├── useSlashCommands.ts # /command suggestions
|
||||
│ ├── useAutoMention.ts # Auto-mention active note
|
||||
│ ├── useAutoExport.ts # Auto-export on new/close
|
||||
│ └── useSettings.ts # Settings subscription
|
||||
│ ├── useSessionHistory.ts # Session list/load/resume/fork
|
||||
│ └── useSettings.ts # Settings subscription (useSyncExternalStore)
|
||||
│
|
||||
├── adapters/ # Interface Adapters
|
||||
│ ├── acp/ # Agent Client Protocol
|
||||
│ │ ├── acp.adapter.ts # Implements IAgentClient port
|
||||
│ │ └── acp-type-converter.ts # Converts ACP types to domain
|
||||
│ └── obsidian/ # Obsidian platform
|
||||
│ ├── vault.adapter.ts # Implements IVaultAccess port
|
||||
│ ├── settings-store.adapter.ts # Implements ISettingsAccess port
|
||||
│ └── mention-service.ts # File indexing, fuzzy search
|
||||
├── ui/ # React Components
|
||||
│ ├── ChatContext.ts # React Context (plugin, acpClient, vaultService)
|
||||
│ ├── ChatPanel.tsx # Hook aggregation + rendering (replaces useChatController)
|
||||
│ ├── ChatView.tsx # Sidebar view (ItemView + Context Provider)
|
||||
│ ├── FloatingChatView.tsx # Floating window (position/drag/resize + Context Provider)
|
||||
│ ├── FloatingButton.tsx # Draggable launch button
|
||||
│ ├── ChatHeader.tsx # Header (sidebar + floating variants)
|
||||
│ ├── MessageList.tsx # Message list with auto-scroll
|
||||
│ ├── MessageBubble.tsx # Single message (content type dispatch)
|
||||
│ ├── ToolCallBlock.tsx # Tool call display + diff
|
||||
│ ├── TerminalBlock.tsx # Terminal output polling
|
||||
│ ├── InputArea.tsx # Textarea, attachments, mentions, history
|
||||
│ ├── InputToolbar.tsx # Mode/model selectors, usage, send button
|
||||
│ ├── SuggestionPopup.tsx # Mention/command dropdown
|
||||
│ ├── PermissionBanner.tsx # Permission request buttons
|
||||
│ ├── ErrorBanner.tsx # Error/notification overlay
|
||||
│ ├── SessionHistoryModal.tsx # Session history modal (list + confirm delete)
|
||||
│ ├── SettingsTab.ts # Plugin settings UI
|
||||
│ ├── types.ts # IChatViewHost interface
|
||||
│ └── shared/
|
||||
│ ├── IconButton.tsx # Icon button + Lucide icon wrapper
|
||||
│ ├── MarkdownRenderer.tsx # Obsidian markdown rendering
|
||||
│ └── AttachmentStrip.tsx # Attachment preview strip
|
||||
│
|
||||
├── components/ # UI Components
|
||||
│ ├── chat/ # Chat UI (14 components)
|
||||
│ │ ├── ChatView.tsx # Main view, hook composition
|
||||
│ │ ├── ChatHeader.tsx
|
||||
│ │ ├── ChatMessages.tsx
|
||||
│ │ ├── ChatInput.tsx
|
||||
│ │ ├── MessageRenderer.tsx
|
||||
│ │ ├── MessageContentRenderer.tsx
|
||||
│ │ ├── ToolCallRenderer.tsx
|
||||
│ │ ├── TerminalRenderer.tsx
|
||||
│ │ ├── PermissionRequestSection.tsx
|
||||
│ │ ├── SuggestionDropdown.tsx
|
||||
│ │ ├── CollapsibleThought.tsx
|
||||
│ │ ├── MarkdownTextRenderer.tsx
|
||||
│ │ ├── TextWithMentions.tsx
|
||||
│ │ └── HeaderButton.tsx
|
||||
│ └── settings/
|
||||
│ └── AgentClientSettingTab.ts
|
||||
├── utils/ # Shared Utilities (pure functions)
|
||||
│ ├── platform.ts # Shell, WSL, Windows env, command building
|
||||
│ ├── paths.ts # Path resolution, file:// URI
|
||||
│ ├── error-utils.ts # ACP error conversion
|
||||
│ ├── mention-parser.ts # @[[note]] detection/extraction
|
||||
│ └── logger.ts # Debug-mode logger
|
||||
│
|
||||
├── shared/ # Utilities
|
||||
│ ├── message-service.ts # prepareMessage, sendPreparedMessage
|
||||
│ ├── terminal-manager.ts # Process spawn, stdout/stderr
|
||||
│ ├── chat-exporter.ts # Markdown export
|
||||
│ ├── mention-utils.ts # Mention parsing
|
||||
│ ├── settings-utils.ts # Settings validation
|
||||
│ ├── logger.ts # Debug logging
|
||||
│ ├── path-utils.ts # Path resolution
|
||||
│ └── wsl-utils.ts # WSL support
|
||||
│
|
||||
├── plugin.ts # Obsidian plugin entry point
|
||||
└── main.ts # Re-exports plugin
|
||||
├── plugin.ts # Obsidian plugin lifecycle, commands, view management
|
||||
└── main.ts # Entry point (re-exports plugin)
|
||||
```
|
||||
|
||||
## Architectural Layers
|
||||
|
||||
### 1. Domain Layer (`src/domain/`)
|
||||
### 1. Types Layer (`src/types/`)
|
||||
|
||||
**Purpose**: Pure types and interfaces, zero external dependencies.
|
||||
**Purpose**: Pure type definitions. No logic, no dependencies.
|
||||
|
||||
#### Models (`src/domain/models/`)
|
||||
- `ChatMessage`: Message structure with content types
|
||||
- `ChatSession`: Session state, available commands
|
||||
- `AgentError`: Error types
|
||||
- `AgentConfig`: Agent configuration
|
||||
|
||||
#### Ports (`src/domain/ports/`)
|
||||
- `IAgentClient`: Agent communication interface
|
||||
- `IVaultAccess`: File system access interface
|
||||
- `ISettingsAccess`: Settings management interface
|
||||
|
||||
**Dependency Rule**: Zero dependencies on other layers.
|
||||
| File | Contents |
|
||||
|------|----------|
|
||||
| `chat.ts` | ChatMessage, MessageContent (9-type union), Role, ToolCallStatus, ToolKind, AttachedFile, PromptContent |
|
||||
| `session.ts` | ChatSession, SessionState, SessionUpdate (11-type union), SessionConfigOption, Capabilities, SessionInfo |
|
||||
| `agent.ts` | AgentEnvVar, BaseAgentSettings, ClaudeAgentSettings, GeminiAgentSettings, CodexAgentSettings |
|
||||
| `errors.ts` | AcpErrorCode, AcpError, ProcessError, ErrorInfo |
|
||||
|
||||
---
|
||||
|
||||
### 2. Hooks Layer (`src/hooks/`)
|
||||
### 2. ACP Layer (`src/acp/`)
|
||||
|
||||
**Purpose**: State management and business logic using React hooks.
|
||||
|
||||
| Hook | Responsibility |
|
||||
|------|---------------|
|
||||
| `useAgentSession` | Session lifecycle, create/close/restart, agent switching |
|
||||
| `useChat` | Messages state, send/receive, callbacks for AcpAdapter |
|
||||
| `usePermission` | Permission request handling, auto-approve logic |
|
||||
| `useMentions` | @[[note]] dropdown state, selection |
|
||||
| `useSlashCommands` | /command dropdown state, filtering |
|
||||
| `useAutoMention` | Auto-prepend active note to messages |
|
||||
| `useAutoExport` | Export chat on new/close |
|
||||
| `useSettings` | Settings subscription with useSyncExternalStore |
|
||||
|
||||
**Dependency Rule**: Hooks depend on Domain (ports/models) and Shared utilities.
|
||||
|
||||
---
|
||||
|
||||
### 3. Adapters Layer (`src/adapters/`)
|
||||
|
||||
**Purpose**: Implement ports, bridge external systems to domain.
|
||||
|
||||
#### ACP Adapters (`src/adapters/acp/`)
|
||||
- `acp.adapter.ts`: Implements `IAgentClient`, manages agent process
|
||||
- `acp-type-converter.ts`: Converts ACP protocol types to domain types
|
||||
|
||||
#### Obsidian Adapters (`src/adapters/obsidian/`)
|
||||
- `vault.adapter.ts`: Implements `IVaultAccess`
|
||||
- `settings-store.adapter.ts`: Implements `ISettingsAccess` with observer pattern
|
||||
- `mention-service.ts`: File indexing, fuzzy search
|
||||
|
||||
**Dependency Rule**: Adapters depend on Domain only.
|
||||
|
||||
---
|
||||
|
||||
### 4. Components Layer (`src/components/`)
|
||||
|
||||
**Purpose**: UI rendering, receives state from hooks.
|
||||
|
||||
#### ChatView.tsx
|
||||
- **Hook Composition**: Combines all hooks
|
||||
- **Adapter Instantiation**: Creates AcpAdapter, VaultAdapter via useMemo
|
||||
- **Rendering**: Delegates to child components
|
||||
|
||||
#### Child Components
|
||||
- Receive data via props
|
||||
- Call callbacks from hooks
|
||||
- No direct business logic
|
||||
|
||||
**Dependency Rule**: Components depend on Hooks and Adapters.
|
||||
|
||||
---
|
||||
|
||||
### 5. Shared Layer (`src/shared/`)
|
||||
|
||||
**Purpose**: Cross-cutting utilities.
|
||||
**Purpose**: Isolate ACP protocol dependency. All `@agentclientprotocol/sdk` imports are confined here.
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `message-service.ts` | Pure functions: prepareMessage, sendPreparedMessage |
|
||||
| `terminal-manager.ts` | Process spawn, stdout/stderr capture |
|
||||
| `chat-exporter.ts` | Export chat to markdown |
|
||||
| `mention-utils.ts` | Parse @[[note]] syntax |
|
||||
| `settings-utils.ts` | Validate and normalize settings |
|
||||
| `logger.ts` | Debug logging (respects debugMode) |
|
||||
| `acp-client.ts` | Process spawn/kill, JSON-RPC communication, session management. Defines `IAgentClient` and `ITerminalClient` interfaces. |
|
||||
| `type-converter.ts` | Converts ACP SDK types to internal types (change buffer for protocol updates) |
|
||||
| `permission-handler.ts` | Permission request queue, auto-approve, Promise-based resolution |
|
||||
| `terminal-handler.ts` | Terminal process create/output/kill, stdout/stderr buffering |
|
||||
|
||||
**Dependency Rule**: Can be used by any layer.
|
||||
**Key design**: When the ACP protocol changes, only files in this directory need updating.
|
||||
|
||||
---
|
||||
|
||||
### 3. Services Layer (`src/services/`)
|
||||
|
||||
**Purpose**: Non-React business logic. Classes and pure functions.
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `vault-service.ts` | `VaultService` class — vault note access, fuzzy search, CM6 selection tracking. Exports `IVaultAccess`, `NoteMetadata`, `EditorPosition`. |
|
||||
| `settings-service.ts` | `SettingsService` class — settings persistence (data.json), session storage (sessions/*.json), observer pattern. Exports `ISettingsAccess`. |
|
||||
| `session-helpers.ts` | Pure functions — agent config building, API key injection, agent settings resolution |
|
||||
| `message-sender.ts` | Pure functions — prompt preparation (mention expansion, context building), sending with auth retry |
|
||||
| `chat-exporter.ts` | `ChatExporter` class — markdown export with frontmatter, image handling |
|
||||
| `view-registry.ts` | `ChatViewRegistry` class — multi-view focus tracking, broadcast commands. Exports `IChatViewContainer`. |
|
||||
| `update-checker.ts` | Agent version checking via npm registry |
|
||||
|
||||
---
|
||||
|
||||
### 4. Hooks Layer (`src/hooks/`)
|
||||
|
||||
**Purpose**: React state management. Each hook owns a specific domain of state.
|
||||
|
||||
| Hook | Responsibility |
|
||||
|------|---------------|
|
||||
| `useSession` | Session lifecycle (create/close/restart), mode/model/configOption, optimistic updates |
|
||||
| `useMessages` | Message state, streaming updates (agent_message_chunk, tool_call, etc.), send/receive |
|
||||
| `usePermission` | Permission request detection, approve/reject |
|
||||
| `useMentions` | @[[note]] dropdown + auto-mention active note tracking (merged) |
|
||||
| `useSlashCommands` | /command dropdown filtering and selection |
|
||||
| `useSessionHistory` | Session list/load/resume/fork, local session storage, 5-min cache |
|
||||
| `useSettings` | Settings subscription via useSyncExternalStore |
|
||||
|
||||
**Dependency Rule**: Hooks import from `types/`, `acp/`, `services/`, `utils/`. Never from `ui/`.
|
||||
|
||||
---
|
||||
|
||||
### 5. UI Layer (`src/ui/`)
|
||||
|
||||
**Purpose**: React components. Rendering and user interaction.
|
||||
|
||||
#### Core Architecture
|
||||
|
||||
**ChatContext** provides shared services to the component tree:
|
||||
```typescript
|
||||
interface ChatContextValue {
|
||||
plugin: AgentClientPlugin;
|
||||
acpClient: AcpAdapter;
|
||||
vaultService: VaultService;
|
||||
settingsService: SettingsService;
|
||||
}
|
||||
```
|
||||
|
||||
**ChatPanel** is the central orchestrator:
|
||||
- Calls all hooks (useSession, useMessages, usePermission, useMentions, useSlashCommands, useSessionHistory, useSettings)
|
||||
- Manages session update routing (ACP → hooks)
|
||||
- Handles workspace events, message persistence, cleanup
|
||||
- Renders ChatHeader, MessageList, InputArea directly
|
||||
|
||||
**ChatView** (sidebar) and **FloatingChatView** (floating window) are thin wrappers:
|
||||
- Create services (AcpClient, VaultService) in lifecycle methods
|
||||
- Provide ChatContext
|
||||
- Render ChatPanel with `variant` prop
|
||||
- Implement IChatViewContainer for broadcast commands
|
||||
|
||||
#### Component Tree
|
||||
|
||||
```
|
||||
ChatView / FloatingChatView
|
||||
└── ChatContextProvider
|
||||
└── ChatPanel (variant="sidebar" | "floating")
|
||||
├── ChatHeader (variant-based rendering)
|
||||
├── MessageList
|
||||
│ └── MessageBubble (per message)
|
||||
│ ├── ToolCallBlock → PermissionBanner
|
||||
│ ├── TerminalBlock
|
||||
│ └── MarkdownRenderer
|
||||
├── InputArea
|
||||
│ ├── SuggestionPopup (mentions / commands)
|
||||
│ ├── ErrorBanner
|
||||
│ ├── AttachmentStrip
|
||||
│ └── InputToolbar (mode/model/usage/send)
|
||||
└── SessionHistoryModal (imperative, via ref)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 6. Utils Layer (`src/utils/`)
|
||||
|
||||
**Purpose**: Pure utility functions. No React, no Obsidian dependencies (except `platform.ts`).
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `platform.ts` | Shell detection, WSL path conversion, Windows PATH from registry, platform-specific command preparation |
|
||||
| `paths.ts` | Path resolution (which/where), file:// URI building, relative path conversion |
|
||||
| `error-utils.ts` | ACP error code → user-friendly title/suggestion conversion |
|
||||
| `mention-parser.ts` | @[[note]] detection, replacement, extraction from text |
|
||||
| `logger.ts` | Singleton logger respecting debugMode setting |
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -167,92 +205,96 @@ src/
|
|||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ Components Layer │
|
||||
│ ┌────────────────────────────────────────────────────────┐ │
|
||||
│ │ ChatView.tsx (hook composition + rendering) │ │
|
||||
│ │ ├─ ChatHeader, ChatMessages, ChatInput │ │
|
||||
│ │ └─ MessageRenderer, ToolCallRenderer, etc. │ │
|
||||
│ └────────────────────────────────────────────────────────┘ │
|
||||
│ UI Layer │
|
||||
│ │
|
||||
│ ChatView / FloatingChatView (Context Providers) │
|
||||
│ └── ChatPanel (hook composition + rendering) │
|
||||
│ ├── ChatHeader, MessageList, InputArea │
|
||||
│ └── MessageBubble, ToolCallBlock, etc. │
|
||||
└─────────────────────────────┬───────────────────────────────┘
|
||||
↓ uses
|
||||
↓ calls hooks
|
||||
┌─────────────────────────────┴───────────────────────────────┐
|
||||
│ Hooks Layer │
|
||||
│ ┌──────────────────┐ ┌──────────────────┐ │
|
||||
│ │ useAgentSession │ │ useChat │ │
|
||||
│ │ usePermission │ │ useMentions │ │
|
||||
│ │ useSlashCommands │ │ useAutoExport │ │
|
||||
│ └──────────────────┘ └──────────────────┘ │
|
||||
└─────────────────────────────┬───────────────────────────────┘
|
||||
↓ uses ports
|
||||
┌─────────────────────────────┴───────────────────────────────┐
|
||||
│ Ports (Interfaces) │
|
||||
│ ┌─────────────────┐ ┌──────────────────┐ │
|
||||
│ │ IAgentClient │ │ IVaultAccess │ │
|
||||
│ │ ISettingsAccess │ └──────────────────┘ │
|
||||
│ └─────────────────┘ │
|
||||
└─────────────────────────────┬───────────────────────────────┘
|
||||
↑ implements
|
||||
┌─────────────────────────────┴───────────────────────────────┐
|
||||
│ Adapters (Implementations) │
|
||||
│ ┌─────────────────┐ ┌──────────────────┐ │
|
||||
│ │ AcpAdapter │ │ VaultAdapter │ │
|
||||
│ │ SettingsStore │ │ MentionService │ │
|
||||
│ └─────────────────┘ └──────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
↑
|
||||
┌─────────────────────────────┴───────────────────────────────┐
|
||||
│ Domain Models │
|
||||
│ ┌─────────────────┐ ┌──────────────────┐ │
|
||||
│ │ ChatMessage │ │ ChatSession │ │
|
||||
│ │ AgentError │ │ AgentConfig │ │
|
||||
│ └─────────────────┘ └──────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
│ useSession, useMessages, usePermission, useMentions, │
|
||||
│ useSlashCommands, useSessionHistory, useSettings │
|
||||
└───────────┬─────────────────────────────┬───────────────────┘
|
||||
↓ calls ↓ reads types
|
||||
┌───────────┴───────────┐ ┌─────────────┴───────────────────┐
|
||||
│ Services Layer │ │ Types Layer │
|
||||
│ VaultService │ │ chat.ts, session.ts, │
|
||||
│ SettingsService │ │ agent.ts, errors.ts │
|
||||
│ session-helpers │ └─────────────────────────────────┘
|
||||
│ message-sender │
|
||||
│ chat-exporter │
|
||||
│ view-registry │
|
||||
└───────────┬───────────┘
|
||||
↓ communicates
|
||||
┌───────────┴───────────┐
|
||||
│ ACP Layer │
|
||||
│ acp-client.ts │
|
||||
│ type-converter.ts │
|
||||
│ permission-handler │
|
||||
│ terminal-handler │
|
||||
└───────────────────────┘
|
||||
↑
|
||||
@agentclientprotocol/sdk
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Design Patterns
|
||||
|
||||
### 1. Custom Hooks Pattern
|
||||
- State and logic encapsulated in hooks
|
||||
- Composable and reusable
|
||||
- Testable in isolation
|
||||
### 1. ChatPanel Pattern (Component-as-Orchestrator)
|
||||
- Central component calls all hooks and renders children directly
|
||||
- Replaces the "god hook" pattern (useChatController)
|
||||
- No massive return object — props flow directly to JSX
|
||||
|
||||
### 2. Dependency Inversion (Ports & Adapters)
|
||||
- Domain defines interfaces (ports)
|
||||
- Adapters implement those interfaces
|
||||
- ACP protocol changes isolated to adapters
|
||||
### 2. React Context for Services
|
||||
- `ChatContext` provides plugin, acpClient, vaultService, settingsService
|
||||
- Value is stable (service instances don't change)
|
||||
- Eliminates prop drilling for shared dependencies
|
||||
|
||||
### 3. Observer Pattern
|
||||
- `SettingsStore` notifies subscribers on change
|
||||
### 3. Custom Hooks for State
|
||||
- Each hook owns a specific state domain
|
||||
- Composable and independently testable
|
||||
- No inter-hook dependencies (communication via ChatPanel)
|
||||
|
||||
### 4. ACP Isolation
|
||||
- All `@agentclientprotocol/sdk` imports confined to `acp/`
|
||||
- `type-converter.ts` is the change buffer for protocol updates
|
||||
- Interface types (`IAgentClient`) defined alongside implementation
|
||||
|
||||
### 5. Observer Pattern
|
||||
- `SettingsService` notifies subscribers on change
|
||||
- React components use `useSyncExternalStore`
|
||||
|
||||
### 4. Pure Functions in Shared
|
||||
- `message-service.ts`: prepareMessage, sendPreparedMessage
|
||||
- No React dependencies, easy to test
|
||||
### 6. Ref Pattern for Callbacks
|
||||
- IChatViewContainer callbacks use refs for latest values
|
||||
- Prevents stale closures in broadcast command handlers
|
||||
- Unmount cleanup uses refs to access latest state
|
||||
|
||||
---
|
||||
|
||||
## Key Benefits
|
||||
|
||||
### 1. React-idiomatic
|
||||
- Hooks for state management
|
||||
- No ViewModel classes
|
||||
- Standard React patterns
|
||||
### 1. Flat and Readable
|
||||
- 4 layers (types → acp/services → hooks → ui) instead of 5
|
||||
- No port/adapter indirection
|
||||
- File names reflect functionality (MessageBubble, ToolCallBlock, ErrorBanner)
|
||||
|
||||
### 2. ACP Change Resistance
|
||||
- `IAgentClient` interface isolates protocol
|
||||
- Only `adapters/acp/` needs changes for protocol updates
|
||||
- Only `acp/` directory needs changes for protocol updates
|
||||
- `type-converter.ts` localizes type mapping changes
|
||||
|
||||
### 3. Testability
|
||||
- Hooks can be tested with React Testing Library
|
||||
- Pure functions in shared/ easily unit tested
|
||||
- Adapters mockable via ports
|
||||
### 3. Easy Feature Addition
|
||||
- New hook: create in `hooks/`, call in `ChatPanel`, pass to child
|
||||
- New message type: add to `types/session.ts`, handle in `useMessages`, render in `MessageBubble`
|
||||
- New agent: add settings in `plugin.ts`, configure in `SettingsTab`
|
||||
|
||||
### 4. Maintainability
|
||||
- Clear file locations by responsibility
|
||||
- Single Responsibility Principle
|
||||
- ~9,100 lines across 45 files
|
||||
- ~20,400 lines across 50 files
|
||||
- Services testable without React
|
||||
- Clear dependency direction (no circular dependencies)
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -260,11 +302,12 @@ src/
|
|||
|
||||
| Pattern | Example |
|
||||
|---------|---------|
|
||||
| Ports | `*.port.ts` |
|
||||
| Adapters | `*.adapter.ts` |
|
||||
| Hooks | `use*.ts` |
|
||||
| Components | `PascalCase.tsx` |
|
||||
| Utilities | `kebab-case.ts` |
|
||||
| Types | `kebab-case.ts` in `types/` |
|
||||
| ACP | `kebab-case.ts` in `acp/` |
|
||||
| Services | `kebab-case.ts` in `services/` |
|
||||
| Hooks | `use*.ts` in `hooks/` |
|
||||
| Components | `PascalCase.tsx` in `ui/` |
|
||||
| Utilities | `kebab-case.ts` in `utils/` |
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -273,23 +316,31 @@ src/
|
|||
### Adding a New Hook
|
||||
1. Create `hooks/use[Feature].ts`
|
||||
2. Define state with useState/useReducer
|
||||
3. Export state and functions
|
||||
4. Compose in ChatView.tsx
|
||||
3. Call the hook in `ui/ChatPanel.tsx`
|
||||
4. Pass state/callbacks to child components as props
|
||||
|
||||
### Adding a New Agent
|
||||
1. Implement `IAgentClient` in `adapters/[agent]/`
|
||||
2. Add settings to `plugin.ts`
|
||||
3. Update AgentClientSettingTab
|
||||
### Adding a New Session Update Type
|
||||
1. Add interface to `types/session.ts`, add to `SessionUpdate` union
|
||||
2. Handle in `hooks/useMessages.ts` `handleSessionUpdate()` (for message-level) or `hooks/useSession.ts` (for session-level)
|
||||
3. Convert from ACP type in `acp/type-converter.ts`
|
||||
4. Render in `ui/MessageBubble.tsx` if needed
|
||||
|
||||
**No changes needed** in hooks or components!
|
||||
### Adding a New Agent Type
|
||||
1. Add settings type to `types/agent.ts`
|
||||
2. Add config in `plugin.ts` settings
|
||||
3. Add API key injection in `services/session-helpers.ts`
|
||||
4. Update `ui/SettingsTab.ts` for configuration UI
|
||||
|
||||
---
|
||||
|
||||
## Migration Notes
|
||||
|
||||
Refactored from Clean Architecture (MVVM) to React Hooks Architecture in November 2025:
|
||||
### March 2026: Simplified Architecture Refactoring
|
||||
|
||||
- **Removed**: `core/use-cases/`, `adapters/view-models/`, `infrastructure/`
|
||||
- **Added**: `hooks/` layer with 8 custom hooks
|
||||
- **Moved**: `plugin.ts` to src root
|
||||
- **Result**: Simpler, more React-idiomatic codebase
|
||||
Refactored from Port/Adapter Architecture to simplified layered architecture:
|
||||
|
||||
- **Removed**: `domain/models/` (9 files → `types/` 4 files), `domain/ports/` (5 files → interfaces moved to implementation files), `adapters/` directory, `components/` directory, `shared/` directory
|
||||
- **Added**: `types/`, `acp/`, `services/`, `ui/`, `utils/` flat directories, `ChatPanel` + `ChatContext`
|
||||
- **Merged**: VaultAdapter + MentionService → VaultService, useMentions + useAutoMention → useMentions
|
||||
- **Removed**: useChatController (god hook → ChatPanel component), Port files (no implementation swapping planned)
|
||||
- **Result**: 76 → 50 files, 5 → 4 layers, flat directory structure
|
||||
|
|
|
|||
254
CLAUDE.md
254
CLAUDE.md
|
|
@ -1,7 +1,7 @@
|
|||
# Agent Client Plugin - LLM Developer Guide
|
||||
|
||||
## Overview
|
||||
Obsidian plugin for AI agent interaction (Claude Code, Gemini CLI, custom agents). **React Hooks Architecture**.
|
||||
Obsidian plugin for AI agent interaction (Claude Code, Codex, Gemini CLI, custom agents). **ChatPanel + React Hooks Architecture**.
|
||||
|
||||
**Tech**: React 19, TypeScript, Obsidian API, Agent Client Protocol (ACP)
|
||||
|
||||
|
|
@ -9,127 +9,164 @@ Obsidian plugin for AI agent interaction (Claude Code, Gemini CLI, custom agents
|
|||
|
||||
```
|
||||
src/
|
||||
├── domain/ # Pure domain models + ports (interfaces)
|
||||
│ ├── models/ # agent-config, agent-error, chat-message, chat-session, session-update
|
||||
│ └── ports/ # IAgentClient, ISettingsAccess, IVaultAccess
|
||||
├── adapters/ # Interface implementations
|
||||
│ ├── acp/ # ACP protocol (acp.adapter.ts, acp-type-converter.ts)
|
||||
│ └── obsidian/ # Platform adapters (vault, settings, mention-service)
|
||||
├── types/ # Type definitions (no logic, no dependencies)
|
||||
│ ├── chat.ts # ChatMessage, MessageContent, PromptContent, AttachedFile
|
||||
│ ├── session.ts # ChatSession, SessionUpdate, SessionInfo, Capabilities
|
||||
│ ├── agent.ts # AgentConfig, agent settings (Claude/Codex/Gemini/Custom)
|
||||
│ └── errors.ts # AcpError, ProcessError, ErrorInfo
|
||||
├── acp/ # ACP protocol (SDK dependency confined here)
|
||||
│ ├── acp-client.ts # Process lifecycle, JSON-RPC, IAgentClient + ITerminalClient
|
||||
│ ├── type-converter.ts # ACP SDK ↔ internal type conversion
|
||||
│ ├── permission-handler.ts # Permission queue, auto-approve, Promise resolution
|
||||
│ └── terminal-handler.ts # Terminal process create/output/kill
|
||||
├── services/ # Business logic (non-React)
|
||||
│ ├── vault-service.ts # Vault access + fuzzy search + CM6 selection tracking
|
||||
│ ├── settings-service.ts # Settings persistence, session storage, normalization
|
||||
│ ├── session-helpers.ts # Agent config building, API key injection (pure functions)
|
||||
│ ├── message-sender.ts # Prompt preparation + sending (pure functions)
|
||||
│ ├── chat-exporter.ts # Markdown export with frontmatter
|
||||
│ ├── view-registry.ts # Multi-view management, focus, broadcast
|
||||
│ └── update-checker.ts # Agent/plugin version checking
|
||||
├── hooks/ # React custom hooks (state + logic)
|
||||
│ ├── useAgentSession.ts # Session lifecycle, agent switching
|
||||
│ ├── useChat.ts # Message sending, session update handling
|
||||
│ ├── useSession.ts # Session lifecycle, config options, optimistic updates
|
||||
│ ├── useMessages.ts # Message state, streaming updates, send/receive
|
||||
│ ├── usePermission.ts # Permission handling
|
||||
│ ├── useMentions.ts # @[[note]] suggestions
|
||||
│ ├── useMentions.ts # @[[note]] suggestions + auto-mention active note
|
||||
│ ├── useSlashCommands.ts # /command suggestions
|
||||
│ ├── useAutoMention.ts # Auto-mention active note
|
||||
│ ├── useAutoExport.ts # Auto-export on new/close
|
||||
│ ├── useSessionHistory.ts # Session list/load/resume/fork
|
||||
│ └── useSettings.ts # Settings subscription
|
||||
├── components/ # UI components
|
||||
│ ├── chat/ # ChatView, ChatHeader, ChatMessages, ChatInput, etc.
|
||||
│ └── settings/ # AgentClientSettingTab
|
||||
├── shared/ # Utilities
|
||||
│ ├── message-service.ts # prepareMessage, sendPreparedMessage (pure functions)
|
||||
│ ├── terminal-manager.ts # Process spawn, stdout/stderr capture
|
||||
│ ├── logger.ts, chat-exporter.ts, mention-utils.ts, etc.
|
||||
├── ui/ # React components
|
||||
│ ├── ChatContext.ts # React Context (plugin, acpClient, vaultService)
|
||||
│ ├── ChatPanel.tsx # Hook aggregation + rendering (core orchestrator)
|
||||
│ ├── ChatView.tsx # Sidebar view (ItemView wrapper)
|
||||
│ ├── FloatingChatView.tsx # Floating window (position/drag/resize)
|
||||
│ ├── ChatHeader.tsx # Header (sidebar + floating variants)
|
||||
│ ├── MessageList.tsx # Message list with auto-scroll
|
||||
│ ├── MessageBubble.tsx # Single message rendering (content dispatch)
|
||||
│ ├── ToolCallBlock.tsx # Tool call + diff display
|
||||
│ ├── TerminalBlock.tsx # Terminal output polling
|
||||
│ ├── InputArea.tsx # Textarea, attachments, mentions, history
|
||||
│ ├── InputToolbar.tsx # Mode/model selectors, usage, send button
|
||||
│ └── ... # SuggestionPopup, PermissionBanner, ErrorBanner, etc.
|
||||
├── utils/ # Shared utilities
|
||||
│ ├── platform.ts # Shell, WSL, Windows env, command building
|
||||
│ ├── paths.ts # Path resolution, file:// URI
|
||||
│ ├── error-utils.ts # ACP error conversion
|
||||
│ ├── mention-parser.ts # @[[note]] detection/extraction
|
||||
│ └── logger.ts # Debug-mode logger
|
||||
├── plugin.ts # Obsidian plugin lifecycle, settings persistence
|
||||
└── main.ts # Entry point
|
||||
```
|
||||
|
||||
## Key Components
|
||||
|
||||
### ChatView (`components/chat/ChatView.tsx`)
|
||||
- **Hook Composition**: Combines all hooks (useAgentSession, useChat, usePermission, etc.)
|
||||
- **Adapter Instantiation**: Creates AcpAdapter, VaultAdapter, MentionService via useMemo
|
||||
- **Callback Registration**: Registers `onSessionUpdate` for unified event handling
|
||||
- **Rendering**: Delegates to ChatHeader, ChatMessages, ChatInput
|
||||
### ChatPanel (`ui/ChatPanel.tsx`)
|
||||
Central orchestrator component. Replaces the former `useChatController` hook.
|
||||
- **Hook Composition**: Calls all hooks (useSession, useMessages, usePermission, etc.)
|
||||
- **Session Update Routing**: Routes ACP updates to useMessages (message-level) or useSession (session-level)
|
||||
- **Callback Registration**: Provides IChatViewContainer callbacks via ref pattern
|
||||
- **Workspace Events**: Handles toggle-auto-mention, new-chat-requested, approve/reject-permission, cancel, export
|
||||
- **Rendering**: Renders ChatHeader, MessageList, InputArea directly (no prop bag)
|
||||
|
||||
### ChatView / FloatingChatView (`ui/ChatView.tsx`, `ui/FloatingChatView.tsx`)
|
||||
Thin wrappers that:
|
||||
- Create services (AcpClient, VaultService) in lifecycle methods
|
||||
- Provide ChatContext (plugin, acpClient, vaultService, settingsService)
|
||||
- Render `<ChatPanel variant="sidebar" | "floating" />`
|
||||
- Implement IChatViewContainer for broadcast commands
|
||||
|
||||
### Hooks (`hooks/`)
|
||||
|
||||
**useAgentSession**: Session lifecycle
|
||||
- `createSession()`: Load config, inject API keys, initialize + newSession
|
||||
- `switchAgent()`: Change active agent, restart session
|
||||
**useSession**: Session lifecycle
|
||||
- `createSession()`: Build config, inject API keys, initialize + newSession
|
||||
- `restartSession()`: Change active agent, restart session
|
||||
- `closeSession()`: Cancel session, disconnect
|
||||
- `updateAvailableCommands()`: Handle slash command updates
|
||||
- `updateCurrentMode()`: Handle mode change updates
|
||||
- `setConfigOption()`: Optimistic update + rollback on error
|
||||
- `handleSessionUpdate()`: Handle session-level updates (commands, config, usage)
|
||||
|
||||
**useChat**: Messaging and session update handling
|
||||
- `sendMessage()`: Prepare (auto-mention, path conversion) → send via IAgentClient
|
||||
- `handleNewChat()`: Export if enabled, restart session
|
||||
- `handleSessionUpdate()`: Unified handler for all session updates (agent_message_chunk, tool_call, etc.)
|
||||
**useMessages**: Messaging and streaming updates
|
||||
- `sendMessage()`: Prepare (auto-mention, path conversion) → send via AcpClient
|
||||
- `handleSessionUpdate()`: Handle message-level updates (agent_message_chunk, tool_call, etc.)
|
||||
- `upsertToolCall()`: Create or update tool call in single `setMessages` callback (avoids race conditions)
|
||||
- `updateLastMessage()`: Append text/thought chunks to last assistant message
|
||||
- `updateMessage()`: Update specific message by tool call ID
|
||||
|
||||
**usePermission**: Permission handling
|
||||
- `handlePermissionResponse()`: Respond with selected option
|
||||
- `approvePermission()` / `rejectPermission()`: Respond with selected option
|
||||
- Auto-approve logic based on settings
|
||||
|
||||
**useMentions / useSlashCommands**: Input suggestions
|
||||
- Dropdown state management
|
||||
- Selection handlers
|
||||
**useMentions**: @mention + auto-mention (merged)
|
||||
- Dropdown state management + selection
|
||||
- Active note tracking + toggle for slash commands
|
||||
|
||||
### AcpAdapter (`adapters/acp/acp.adapter.ts`)
|
||||
Implements IAgentClient + IAcpClient (terminal ops)
|
||||
**useSessionHistory**: Session persistence
|
||||
- `restoreSession()`: Load/resume with local message fallback
|
||||
- `forkSession()`: Create new branch from existing session
|
||||
|
||||
### ACP Client (`acp/acp-client.ts`)
|
||||
Implements IAgentClient + ITerminalClient
|
||||
|
||||
- **Process**: spawn() with login shell (macOS/Linux -l, Windows shell:true)
|
||||
- **Protocol**: JSON-RPC over stdin/stdout via ndJsonStream
|
||||
- **Flow**: initialize() → newSession() → sendMessage() → sessionUpdate via `onSessionUpdate`
|
||||
- **Updates**: agent_message_chunk, agent_thought_chunk, tool_call, tool_call_update, plan, available_commands_update, current_mode_update
|
||||
- **Unified Callback**: Single `onSessionUpdate(callback)` replaces legacy `onMessage`, `onError`, `onPermissionRequest`
|
||||
- **Permissions**: Promise-based Map<requestId, resolver>
|
||||
- **Terminal**: createTerminal, terminalOutput, killTerminal, releaseTerminal
|
||||
- **Flow**: initialize() → newSession() → sendPrompt() → sessionUpdate via `onSessionUpdate`
|
||||
- **Updates**: agent_message_chunk, agent_thought_chunk, user_message_chunk, tool_call, tool_call_update, plan, available_commands_update, current_mode_update, session_info_update, usage_update, config_option_update
|
||||
- **Unified Callback**: Single `onSessionUpdate(callback)` for all agent events
|
||||
- **Permissions**: Promise-based via PermissionHandler
|
||||
- **Terminal**: TerminalHandler manages create/output/kill/release
|
||||
|
||||
### Obsidian Adapters (`adapters/obsidian/`)
|
||||
### Services (`services/`)
|
||||
|
||||
**VaultAdapter**: IVaultAccess - searchNotes (fuzzy), getActiveNote, readNote
|
||||
**SettingsStore**: ISettingsAccess - Observer pattern, getSnapshot(), subscribe()
|
||||
**MentionService**: File index, fuzzy search (basename, path, aliases)
|
||||
**VaultService**: Vault access + file index + fuzzy search + CM6 selection tracking
|
||||
**SettingsService**: Observer pattern settings store, session storage (data.json + sessions/*.json)
|
||||
**session-helpers**: Pure functions — buildAgentConfigWithApiKey, findAgentSettings, getAvailableAgents
|
||||
**message-sender**: Pure functions — preparePrompt (auto-mention, context), sendPreparedPrompt (auth retry)
|
||||
|
||||
### Message Service (`shared/message-service.ts`)
|
||||
Pure functions (non-React):
|
||||
- `prepareMessage()`: Auto-mention, convert @[[note]] → paths
|
||||
- `sendPreparedMessage()`: Send via IAgentClient, auth retry
|
||||
## Types
|
||||
|
||||
## Domain Models
|
||||
|
||||
### SessionUpdate (`domain/models/session-update.ts`)
|
||||
### SessionUpdate (`types/session.ts`)
|
||||
Union type for all session update events from the agent:
|
||||
|
||||
```typescript
|
||||
type SessionUpdate =
|
||||
| AgentMessageChunkUpdate // Text chunk from agent's response
|
||||
| AgentThoughtChunkUpdate // Text chunk from agent's reasoning
|
||||
| ToolCallUpdate // New tool call event
|
||||
| ToolCallUpdateUpdate // Update to existing tool call
|
||||
| PlanUpdate // Agent's task plan
|
||||
| AvailableCommandsUpdate // Slash commands changed
|
||||
| CurrentModeUpdate // Mode changed
|
||||
| ErrorUpdate; // Error from agent operations
|
||||
| AgentMessageChunk // Text chunk from agent's response
|
||||
| AgentThoughtChunk // Text chunk from agent's reasoning
|
||||
| UserMessageChunk // Text chunk from user message (session/load)
|
||||
| ToolCall // New tool call event
|
||||
| ToolCallUpdate // Update to existing tool call
|
||||
| Plan // Agent's task plan
|
||||
| AvailableCommandsUpdate // Slash commands changed
|
||||
| CurrentModeUpdate // Mode changed
|
||||
| SessionInfoUpdate // Session metadata changed
|
||||
| UsageUpdate // Context window usage
|
||||
| ConfigOptionUpdate; // Config options changed
|
||||
```
|
||||
|
||||
This domain type abstracts ACP's `SessionNotification.update.sessionUpdate` values, allowing the application layer to handle events without depending on ACP protocol specifics.
|
||||
### Key Interfaces
|
||||
|
||||
## Ports (Interfaces)
|
||||
Interfaces are defined in their implementation files (no separate port files):
|
||||
|
||||
```typescript
|
||||
// acp/acp-client.ts
|
||||
interface IAgentClient {
|
||||
initialize(config: AgentConfig): Promise<InitializeResult>;
|
||||
newSession(workingDirectory: string): Promise<NewSessionResult>;
|
||||
newSession(workingDirectory: string): Promise<SessionResult>;
|
||||
authenticate(methodId: string): Promise<boolean>;
|
||||
sendMessage(sessionId: string, message: string): Promise<void>;
|
||||
sendPrompt(sessionId: string, content: PromptContent[]): Promise<void>;
|
||||
cancel(sessionId: string): Promise<void>;
|
||||
disconnect(): Promise<void>;
|
||||
|
||||
// Unified callback for all session updates
|
||||
onSessionUpdate(callback: (update: SessionUpdate) => void): void;
|
||||
|
||||
onError(callback: (error: ProcessError) => void): void;
|
||||
respondToPermission(requestId: string, optionId: string): Promise<void>;
|
||||
isInitialized(): boolean;
|
||||
getCurrentAgentId(): string | null;
|
||||
setSessionMode(sessionId: string, modeId: string): Promise<void>;
|
||||
setSessionModel(sessionId: string, modelId: string): Promise<void>;
|
||||
setSessionConfigOption(sessionId: string, configId: string, value: string): Promise<SessionConfigOption[]>;
|
||||
listSessions(cwd?: string, cursor?: string): Promise<ListSessionsResult>;
|
||||
loadSession(sessionId: string, cwd: string): Promise<SessionResult>;
|
||||
resumeSession(sessionId: string, cwd: string): Promise<SessionResult>;
|
||||
forkSession(sessionId: string, cwd: string): Promise<SessionResult>;
|
||||
}
|
||||
|
||||
// services/vault-service.ts
|
||||
interface IVaultAccess {
|
||||
readNote(path: string): Promise<string>;
|
||||
searchNotes(query: string): Promise<NoteMetadata[]>;
|
||||
|
|
@ -137,21 +174,29 @@ interface IVaultAccess {
|
|||
listNotes(): Promise<NoteMetadata[]>;
|
||||
}
|
||||
|
||||
// services/settings-service.ts
|
||||
interface ISettingsAccess {
|
||||
getSnapshot(): AgentClientPluginSettings;
|
||||
updateSettings(updates: Partial<AgentClientPluginSettings>): Promise<void>;
|
||||
subscribe(listener: () => void): () => void;
|
||||
saveSession(info: SavedSessionInfo): Promise<void>;
|
||||
getSavedSessions(agentId?: string, cwd?: string): SavedSessionInfo[];
|
||||
deleteSession(sessionId: string): Promise<void>;
|
||||
saveSessionMessages(sessionId: string, agentId: string, messages: ChatMessage[]): Promise<void>;
|
||||
loadSessionMessages(sessionId: string): Promise<ChatMessage[] | null>;
|
||||
deleteSessionMessages(sessionId: string): Promise<void>;
|
||||
}
|
||||
```
|
||||
|
||||
## Development Rules
|
||||
|
||||
### Architecture
|
||||
1. **Hooks for state + logic**: No ViewModel, no Use Cases classes
|
||||
2. **Pure functions in shared/**: Non-React business logic
|
||||
3. **Ports for ACP resistance**: IAgentClient interface isolates protocol changes
|
||||
4. **Domain has zero deps**: No `obsidian`, `@agentclientprotocol/sdk`
|
||||
5. **Unified callbacks**: Use `onSessionUpdate` for all agent events (not multiple callbacks)
|
||||
1. **ChatPanel as orchestrator**: All hooks called in ChatPanel, renders children directly
|
||||
2. **Services for non-React logic**: Pure functions and classes in `services/`
|
||||
3. **ACP isolation**: All `@agentclientprotocol/sdk` imports confined to `acp/`
|
||||
4. **Types have zero deps**: No `obsidian`, no SDK, no React in `types/`
|
||||
5. **Unified callbacks**: Use `onSessionUpdate` for all agent events
|
||||
6. **Context for services**: plugin, acpClient, vaultService via ChatContext
|
||||
|
||||
### Obsidian Plugin Review (CRITICAL)
|
||||
1. No innerHTML/outerHTML - use createEl/createDiv/createSpan
|
||||
|
|
@ -161,19 +206,22 @@ interface ISettingsAccess {
|
|||
5. Minimize `any` - use proper types
|
||||
|
||||
### Naming Conventions
|
||||
- Ports: `*.port.ts`
|
||||
- Adapters: `*.adapter.ts`
|
||||
- Hooks: `use*.ts`
|
||||
- Components: `PascalCase.tsx`
|
||||
- Utils/Models: `kebab-case.ts`
|
||||
- Types: `kebab-case.ts` in `types/`
|
||||
- ACP: `kebab-case.ts` in `acp/`
|
||||
- Services: `kebab-case.ts` in `services/`
|
||||
- Hooks: `use*.ts` in `hooks/`
|
||||
- Components: `PascalCase.tsx` in `ui/`
|
||||
- Utils: `kebab-case.ts` in `utils/`
|
||||
|
||||
### Code Patterns
|
||||
1. React hooks for state management
|
||||
2. useCallback/useMemo for performance
|
||||
3. useRef for cleanup function access
|
||||
3. useRef for cleanup function access and stale closure prevention
|
||||
4. Error handling: try-catch async ops
|
||||
5. Logging: Logger class (respects debugMode)
|
||||
6. **Upsert pattern**: Use `setMessages` functional updates to avoid race conditions with tool_call updates
|
||||
7. **Ref pattern for callbacks**: IChatViewContainer callbacks use refs for latest values
|
||||
8. **Context value stability**: ChatContext value created once (service instances), wrapped in useMemo
|
||||
|
||||
## Common Tasks
|
||||
|
||||
|
|
@ -181,46 +229,50 @@ interface ISettingsAccess {
|
|||
1. Create `hooks/use[Feature].ts`
|
||||
2. Define state with useState/useReducer
|
||||
3. Export functions and state
|
||||
4. Compose in ChatView.tsx
|
||||
4. Call the hook in `ui/ChatPanel.tsx`
|
||||
5. Pass state/callbacks to child components as props
|
||||
|
||||
### Add Agent Type
|
||||
1. **Optional**: Define config in `domain/models/agent-config.ts`
|
||||
2. **Adapter**: Implement IAgentClient in `adapters/[agent]/[agent].adapter.ts`
|
||||
3. **Settings**: Add to AgentClientPluginSettings in plugin.ts
|
||||
4. **UI**: Update AgentClientSettingTab
|
||||
1. Add settings type in `types/agent.ts`
|
||||
2. Add config and defaults in `plugin.ts`
|
||||
3. Add API key injection in `services/session-helpers.ts`
|
||||
4. Update `ui/SettingsTab.ts` for configuration UI
|
||||
|
||||
### Modify Message Types
|
||||
1. Update `ChatMessage`/`MessageContent` in `domain/models/chat-message.ts`
|
||||
1. Update `ChatMessage`/`MessageContent` in `types/chat.ts`
|
||||
2. If adding new session update type:
|
||||
- Add to `SessionUpdate` union in `domain/models/session-update.ts`
|
||||
- Handle in `useChat.handleSessionUpdate()`
|
||||
3. Update `AcpAdapter.sessionUpdate()` to emit the new type
|
||||
4. Update `MessageContentRenderer` to render new type
|
||||
- Add to `SessionUpdate` union in `types/session.ts`
|
||||
- Handle in `hooks/useMessages.ts` `handleSessionUpdate()`
|
||||
3. Update `acp/acp-client.ts` `sessionUpdate()` to emit the new type
|
||||
4. Update `ui/MessageBubble.tsx` `ContentBlock` to render new type
|
||||
|
||||
### Add New Session Update Type
|
||||
1. Define interface in `domain/models/session-update.ts`
|
||||
1. Define interface in `types/session.ts`
|
||||
2. Add to `SessionUpdate` union type
|
||||
3. Handle in `useChat.handleSessionUpdate()` (for message-level updates)
|
||||
4. Or handle in `ChatView` (for session-level updates like `available_commands_update`)
|
||||
3. Handle in `hooks/useMessages.ts` `handleSessionUpdate()` (for message-level)
|
||||
4. Or handle in `hooks/useSession.ts` `handleSessionUpdate()` (for session-level)
|
||||
5. Route in `ui/ChatPanel.tsx` session update effect
|
||||
|
||||
### Debug
|
||||
1. Settings → Developer Settings → Debug Mode ON
|
||||
2. Open DevTools (Cmd+Option+I / Ctrl+Shift+I)
|
||||
3. Filter logs: `[AcpAdapter]`, `[useChat]`, `[NoteMentionService]`
|
||||
3. Filter logs: `[AcpAdapter]`, `[useMessages]`, `[VaultService]`, `[ChatPanel]`
|
||||
|
||||
## ACP Protocol
|
||||
|
||||
**Communication**: JSON-RPC 2.0 over stdin/stdout
|
||||
|
||||
**Methods**: initialize, newSession, authenticate, prompt, cancel, setSessionMode, setSessionModel
|
||||
**Notifications**: session/update (agent_message_chunk, agent_thought_chunk, tool_call, tool_call_update, plan, available_commands_update, current_mode_update)
|
||||
**Methods**: initialize, newSession, authenticate, prompt, cancel, setSessionConfigOption
|
||||
**Notifications**: session/update (agent_message_chunk, agent_thought_chunk, user_message_chunk, tool_call, tool_call_update, plan, available_commands_update, current_mode_update, session_info_update, usage_update, config_option_update)
|
||||
**Requests**: requestPermission
|
||||
**Session Management** (unstable): session/list, session/load, session/resume, session/fork
|
||||
|
||||
**Agents**:
|
||||
- Claude Code: `@zed-industries/claude-agent-acp` (ANTHROPIC_API_KEY)
|
||||
- Gemini CLI: `@anthropics/gemini-cli-acp` (GOOGLE_API_KEY)
|
||||
- Codex: `@zed-industries/codex-acp` (OPENAI_API_KEY)
|
||||
- Gemini CLI: `@anthropics/gemini-cli-acp` (GEMINI_API_KEY)
|
||||
- Custom: Any ACP-compatible agent
|
||||
|
||||
---
|
||||
|
||||
**Last Updated**: December 2025 | **Architecture**: React Hooks | **Version**: 0.4.0
|
||||
**Last Updated**: March 2026 | **Architecture**: ChatPanel + React Hooks | **Version**: 0.9.1
|
||||
|
|
|
|||
Loading…
Reference in a new issue