@mseep/obsidian-agent-client 0.10.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/hooks/gh-setup.sh +49 -0
- package/.claude/settings.json +15 -0
- package/.claude/skills/release-notes/SKILL.md +331 -0
- package/.editorconfig +10 -0
- package/.github/FUNDING.yml +2 -0
- package/.github/ISSUE_TEMPLATE/bug_report.yml +90 -0
- package/.github/ISSUE_TEMPLATE/config.yml +11 -0
- package/.github/ISSUE_TEMPLATE/feature_request.yml +59 -0
- package/.github/copilot-instructions.md +45 -0
- package/.github/pull_request_template.md +32 -0
- package/.github/workflows/ci.yaml +25 -0
- package/.github/workflows/docs.yml +58 -0
- package/.github/workflows/relay_to_openclaw.yml +59 -0
- package/.github/workflows/release.yaml +45 -0
- package/.prettierignore +10 -0
- package/.prettierrc +13 -0
- package/.vscode/extensions.json +7 -0
- package/.vscode/settings.json +37 -0
- package/.zed/settings.json +42 -0
- package/AGENTS.md +330 -0
- package/ARCHITECTURE.md +390 -0
- package/CONTRIBUTING.md +216 -0
- package/LICENSE +202 -0
- package/NOTICE +2 -0
- package/README.ja.md +121 -0
- package/README.md +125 -0
- package/docs/.vitepress/config.mts +124 -0
- package/docs/.vitepress/theme/custom.css +111 -0
- package/docs/.vitepress/theme/index.ts +4 -0
- package/docs/agent-setup/claude-code.md +84 -0
- package/docs/agent-setup/codex.md +76 -0
- package/docs/agent-setup/custom-agents.md +67 -0
- package/docs/agent-setup/gemini-cli.md +99 -0
- package/docs/agent-setup/index.md +34 -0
- package/docs/announcements/gemini-cli-deprecation.md +73 -0
- package/docs/getting-started/index.md +78 -0
- package/docs/getting-started/quick-start.md +38 -0
- package/docs/help/faq.md +181 -0
- package/docs/help/troubleshooting.md +221 -0
- package/docs/index.md +63 -0
- package/docs/public/apple-touch-icon.png +0 -0
- package/docs/public/demo.mp4 +0 -0
- package/docs/public/favicon-16x16.png +0 -0
- package/docs/public/favicon-32x32.png +0 -0
- package/docs/public/favicon.ico +0 -0
- package/docs/public/images/editing.webp +0 -0
- package/docs/public/images/export.webp +0 -0
- package/docs/public/images/floating-chat-button.webp +0 -0
- package/docs/public/images/floating-chat-instance-menu.webp +0 -0
- package/docs/public/images/floating-chat-view.webp +0 -0
- package/docs/public/images/mode-selection.webp +0 -0
- package/docs/public/images/model-selection.webp +0 -0
- package/docs/public/images/multi-session.webp +0 -0
- package/docs/public/images/remove-image.webp +0 -0
- package/docs/public/images/ribbon-icon.webp +0 -0
- package/docs/public/images/selection-context.gif +0 -0
- package/docs/public/images/sending-images.webp +0 -0
- package/docs/public/images/sending-messages.webp +0 -0
- package/docs/public/images/session-history-button.webp +0 -0
- package/docs/public/images/slash-commands-1.webp +0 -0
- package/docs/public/images/slash-commands-2.webp +0 -0
- package/docs/public/images/switch-agent.webp +0 -0
- package/docs/public/images/switch-default-agent.webp +0 -0
- package/docs/public/images/temporary-disable.gif +0 -0
- package/docs/reference/acp-support.md +110 -0
- package/docs/usage/chat-export.md +80 -0
- package/docs/usage/commands.md +51 -0
- package/docs/usage/context-files.md +57 -0
- package/docs/usage/editing.md +69 -0
- package/docs/usage/floating-chat.md +84 -0
- package/docs/usage/index.md +97 -0
- package/docs/usage/mcp-tools.md +33 -0
- package/docs/usage/mentions.md +70 -0
- package/docs/usage/mode-selection.md +28 -0
- package/docs/usage/model-selection.md +32 -0
- package/docs/usage/multi-session.md +68 -0
- package/docs/usage/sending-images.md +64 -0
- package/docs/usage/session-history.md +91 -0
- package/docs/usage/slash-commands.md +44 -0
- package/esbuild.config.mjs +49 -0
- package/eslint.config.mjs +25 -0
- package/main.js +228 -0
- package/manifest.json +11 -0
- package/package.json +52 -0
- package/src/acp/acp-client.ts +921 -0
- package/src/acp/acp-handler.ts +252 -0
- package/src/acp/permission-handler.ts +282 -0
- package/src/acp/terminal-handler.ts +264 -0
- package/src/acp/type-converter.ts +272 -0
- package/src/hooks/useAgent.ts +250 -0
- package/src/hooks/useAgentMessages.ts +470 -0
- package/src/hooks/useAgentSession.ts +544 -0
- package/src/hooks/useChatActions.ts +400 -0
- package/src/hooks/useHistoryModal.ts +219 -0
- package/src/hooks/useSessionHistory.ts +863 -0
- package/src/hooks/useSettings.ts +19 -0
- package/src/hooks/useSuggestions.ts +342 -0
- package/src/main.ts +9 -0
- package/src/plugin.ts +1126 -0
- package/src/services/chat-exporter.ts +552 -0
- package/src/services/message-sender.ts +755 -0
- package/src/services/message-state.ts +375 -0
- package/src/services/session-helpers.ts +211 -0
- package/src/services/session-state.ts +130 -0
- package/src/services/session-storage.ts +267 -0
- package/src/services/settings-normalizer.ts +255 -0
- package/src/services/settings-service.ts +285 -0
- package/src/services/update-checker.ts +128 -0
- package/src/services/vault-service.ts +558 -0
- package/src/services/view-registry.ts +345 -0
- package/src/types/agent.ts +92 -0
- package/src/types/chat.ts +351 -0
- package/src/types/errors.ts +136 -0
- package/src/types/obsidian-internals.d.ts +14 -0
- package/src/types/session.ts +731 -0
- package/src/ui/ChangeDirectoryModal.ts +137 -0
- package/src/ui/ChatContext.ts +25 -0
- package/src/ui/ChatHeader.tsx +295 -0
- package/src/ui/ChatPanel.tsx +1162 -0
- package/src/ui/ChatView.tsx +348 -0
- package/src/ui/ErrorBanner.tsx +104 -0
- package/src/ui/FloatingButton.tsx +351 -0
- package/src/ui/FloatingChatView.tsx +531 -0
- package/src/ui/InputArea.tsx +1107 -0
- package/src/ui/InputToolbar.tsx +371 -0
- package/src/ui/MessageBubble.tsx +442 -0
- package/src/ui/MessageList.tsx +265 -0
- package/src/ui/PermissionBanner.tsx +61 -0
- package/src/ui/SessionHistoryModal.tsx +821 -0
- package/src/ui/SettingsTab.ts +1337 -0
- package/src/ui/SuggestionPopup.tsx +138 -0
- package/src/ui/TerminalBlock.tsx +107 -0
- package/src/ui/ToolCallBlock.tsx +456 -0
- package/src/ui/shared/AttachmentStrip.tsx +57 -0
- package/src/ui/shared/IconButton.tsx +55 -0
- package/src/ui/shared/MarkdownRenderer.tsx +103 -0
- package/src/ui/view-host.ts +56 -0
- package/src/utils/error-utils.ts +274 -0
- package/src/utils/logger.ts +44 -0
- package/src/utils/mention-parser.ts +129 -0
- package/src/utils/paths.ts +246 -0
- package/src/utils/platform.ts +425 -0
- package/styles.css +2322 -0
- package/tsconfig.json +18 -0
- package/version-bump.mjs +18 -0
- package/versions.json +3 -0
package/ARCHITECTURE.md
ADDED
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
# Architecture Documentation
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
Obsidian plugin for AI agent interaction via ACP. `useAgent` facade hook composes sub-hooks (`useAgentSession` + `useAgentMessages`) and subscribes to a single `onSessionUpdate` channel. `ChatPanel` orchestrates hooks and renders children directly. Services are injected via React Context. ACP protocol details are isolated in the `acp/` layer.
|
|
6
|
+
|
|
7
|
+
## Directory Structure
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
src/
|
|
11
|
+
├── types/ # Type Definitions (no logic, no dependencies)
|
|
12
|
+
│ ├── chat.ts # ChatMessage, MessageContent, PromptContent, AttachedFile, ActivePermission
|
|
13
|
+
│ ├── session.ts # ChatSession, SessionUpdate (12-type union), SessionInfo, Capabilities
|
|
14
|
+
│ ├── agent.ts # AgentConfig, agent settings (Claude/Gemini/Codex/Custom)
|
|
15
|
+
│ └── errors.ts # AcpError, ProcessError, ErrorInfo
|
|
16
|
+
│
|
|
17
|
+
├── acp/ # ACP Protocol Layer (SDK dependency confined here)
|
|
18
|
+
│ ├── acp-client.ts # Process lifecycle, UI-facing API (AcpClient class)
|
|
19
|
+
│ ├── acp-handler.ts # SDK event handler + sessionId filter + listener broadcast
|
|
20
|
+
│ ├── type-converter.ts # ACP SDK types ↔ internal types
|
|
21
|
+
│ ├── permission-handler.ts # Permission queue, auto-approve, Promise resolution
|
|
22
|
+
│ └── terminal-handler.ts # Terminal process create/output/kill
|
|
23
|
+
│
|
|
24
|
+
├── services/ # Business Logic (non-React, no React imports)
|
|
25
|
+
│ ├── vault-service.ts # Vault access + fuzzy search + CM6 selection tracking
|
|
26
|
+
│ ├── settings-service.ts # Reactive settings store (observer pattern only)
|
|
27
|
+
│ ├── session-storage.ts # Session metadata + message file I/O (sessions/*.json)
|
|
28
|
+
│ ├── settings-normalizer.ts # Settings validation helpers (str, bool, num, enumVal, etc.)
|
|
29
|
+
│ ├── session-helpers.ts # Agent config building, API key injection (pure functions)
|
|
30
|
+
│ ├── session-state.ts # Session state updates (legacy mode/model, config restore)
|
|
31
|
+
│ ├── message-state.ts # Message array transforms (upsert, merge, streaming apply)
|
|
32
|
+
│ ├── message-sender.ts # Prompt preparation + sending (pure functions)
|
|
33
|
+
│ ├── chat-exporter.ts # Markdown export with frontmatter
|
|
34
|
+
│ ├── view-registry.ts # Multi-view management, focus, broadcast
|
|
35
|
+
│ └── update-checker.ts # Agent/plugin version checking
|
|
36
|
+
│
|
|
37
|
+
├── hooks/ # React Custom Hooks (state + logic)
|
|
38
|
+
│ ├── useAgent.ts # Facade: composes useAgentSession + useAgentMessages
|
|
39
|
+
│ ├── useAgentSession.ts # Session lifecycle, config options, optimistic updates
|
|
40
|
+
│ ├── useAgentMessages.ts # Message state, streaming (RAF batch), permissions
|
|
41
|
+
│ ├── useSuggestions.ts # @[[note]] mentions + /command suggestions (unified)
|
|
42
|
+
│ ├── useSessionHistory.ts # Session list/load/resume/fork, 5-min cache
|
|
43
|
+
│ ├── useChatActions.ts # Business callbacks (send, newChat, export, restart, etc.)
|
|
44
|
+
│ ├── useHistoryModal.ts # Session history modal lifecycle
|
|
45
|
+
│ └── useSettings.ts # Settings subscription (useSyncExternalStore)
|
|
46
|
+
│
|
|
47
|
+
├── ui/ # React Components
|
|
48
|
+
│ ├── ChatContext.ts # React Context (plugin, acpClient, vaultService, settingsService)
|
|
49
|
+
│ ├── ChatPanel.tsx # Orchestrator: calls hooks, workspace events, rendering
|
|
50
|
+
│ ├── ChatView.tsx # Sidebar view (ItemView + Context Provider)
|
|
51
|
+
│ ├── FloatingChatView.tsx # Floating window (position/drag/resize + Context Provider)
|
|
52
|
+
│ ├── FloatingButton.tsx # Draggable launch button
|
|
53
|
+
│ ├── ChatHeader.tsx # Header (sidebar + floating variants)
|
|
54
|
+
│ ├── MessageList.tsx # Virtualized message list (@tanstack/react-virtual)
|
|
55
|
+
│ ├── MessageBubble.tsx # Single message (content dispatch, copy button)
|
|
56
|
+
│ ├── ToolCallBlock.tsx # Tool call display + diff (word-level highlighting)
|
|
57
|
+
│ ├── TerminalBlock.tsx # Terminal output polling
|
|
58
|
+
│ ├── InputArea.tsx # Textarea, attachments, mentions, history
|
|
59
|
+
│ ├── InputToolbar.tsx # Config/mode/model selectors, usage, send button
|
|
60
|
+
│ ├── SuggestionPopup.tsx # Mention/command dropdown
|
|
61
|
+
│ ├── PermissionBanner.tsx # Permission request buttons
|
|
62
|
+
│ ├── ErrorBanner.tsx # Error/notification overlay
|
|
63
|
+
│ ├── SessionHistoryModal.tsx # Session history modal (list + confirm delete)
|
|
64
|
+
│ ├── SettingsTab.ts # Plugin settings UI
|
|
65
|
+
│ ├── view-host.ts # IChatViewHost interface
|
|
66
|
+
│ └── shared/
|
|
67
|
+
│ ├── IconButton.tsx # Icon button + Lucide icon wrapper
|
|
68
|
+
│ ├── MarkdownRenderer.tsx # Obsidian markdown rendering
|
|
69
|
+
│ └── AttachmentStrip.tsx # Attachment preview strip
|
|
70
|
+
│
|
|
71
|
+
├── utils/ # Shared Utilities (pure functions)
|
|
72
|
+
│ ├── platform.ts # Shell, WSL, Windows env, command building
|
|
73
|
+
│ ├── paths.ts # Path resolution, file:// URI
|
|
74
|
+
│ ├── error-utils.ts # ACP error conversion
|
|
75
|
+
│ ├── mention-parser.ts # @[[note]] detection/extraction
|
|
76
|
+
│ └── logger.ts # Debug-mode logger
|
|
77
|
+
│
|
|
78
|
+
├── plugin.ts # Obsidian plugin lifecycle, commands, view management
|
|
79
|
+
└── main.ts # Entry point (re-exports plugin)
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Architectural Layers
|
|
83
|
+
|
|
84
|
+
### 1. Types Layer (`src/types/`)
|
|
85
|
+
|
|
86
|
+
**Purpose**: Pure type definitions. No logic, no dependencies.
|
|
87
|
+
|
|
88
|
+
| File | Contents |
|
|
89
|
+
|------|----------|
|
|
90
|
+
| `chat.ts` | ChatMessage, MessageContent (8+ type union), Role, ToolCallStatus, ToolKind, AttachedFile, ActivePermission, PromptContent |
|
|
91
|
+
| `session.ts` | ChatSession, SessionState, SessionUpdate (12-type union incl. ProcessErrorUpdate), SessionConfigOption, Capabilities, SessionInfo |
|
|
92
|
+
| `agent.ts` | AgentEnvVar, BaseAgentSettings, ClaudeAgentSettings, GeminiAgentSettings, CodexAgentSettings |
|
|
93
|
+
| `errors.ts` | AcpErrorCode, AcpError, ProcessError, ErrorInfo |
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
### 2. ACP Layer (`src/acp/`)
|
|
98
|
+
|
|
99
|
+
**Purpose**: Isolate ACP protocol dependency. All `@agentclientprotocol/sdk` imports are confined here.
|
|
100
|
+
|
|
101
|
+
| File | Purpose |
|
|
102
|
+
|------|---------|
|
|
103
|
+
| `acp-client.ts` | UI-facing API: process spawn/kill, JSON-RPC communication, session management. Owns AcpHandler + managers. Single exit point: `onSessionUpdate` (multiple listeners via Set). |
|
|
104
|
+
| `acp-handler.ts` | SDK-facing: receives sessionUpdate, requestPermission, terminal ops. Filters by `currentSessionId`. Broadcasts to all listeners. |
|
|
105
|
+
| `type-converter.ts` | Converts ACP SDK types to internal types (change buffer for protocol updates) |
|
|
106
|
+
| `permission-handler.ts` | Permission request queue, auto-approve, Promise-based resolution. All UI updates via `onSessionUpdate` (no separate callback path). |
|
|
107
|
+
| `terminal-handler.ts` | Terminal process create/output/kill, stdout/stderr buffering |
|
|
108
|
+
|
|
109
|
+
**Key design**: All agent events (messages, session updates, permissions, errors) flow through a single `onSessionUpdate` channel. No special paths.
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
### 3. Services Layer (`src/services/`)
|
|
114
|
+
|
|
115
|
+
**Purpose**: Non-React business logic. Classes and pure functions. **No React imports.**
|
|
116
|
+
|
|
117
|
+
| File | Purpose |
|
|
118
|
+
|------|---------|
|
|
119
|
+
| `vault-service.ts` | `VaultService` class — vault note access, fuzzy search, CM6 selection tracking. Exports `IVaultAccess`, `NoteMetadata`. |
|
|
120
|
+
| `settings-service.ts` | `SettingsService` class — reactive settings store (observer pattern). Delegates session storage to `SessionStorage`. Exports `ISettingsAccess`. |
|
|
121
|
+
| `session-storage.ts` | `SessionStorage` class — session metadata CRUD (in plugin settings) + message file I/O (sessions/*.json). |
|
|
122
|
+
| `settings-normalizer.ts` | Pure functions — settings validation helpers (`str`, `bool`, `num`, `enumVal`, `obj`, `strRecord`, `xyPoint`), `toAgentConfig`, `parseChatFontSize`. |
|
|
123
|
+
| `session-helpers.ts` | Pure functions — agent config building, API key injection, agent settings resolution |
|
|
124
|
+
| `session-state.ts` | Pure functions — legacy mode/model application, config option restoration |
|
|
125
|
+
| `message-state.ts` | Pure functions — message array transforms (streaming apply, tool call upsert with O(1) index, permission scanning) |
|
|
126
|
+
| `message-sender.ts` | Pure functions — prompt preparation (embedded context vs XML text, shared helpers), sending with auth retry |
|
|
127
|
+
| `chat-exporter.ts` | `ChatExporter` class — markdown export with frontmatter, image handling |
|
|
128
|
+
| `view-registry.ts` | `ChatViewRegistry` class — multi-view focus tracking, broadcast commands. Exports `IChatViewContainer`. |
|
|
129
|
+
| `update-checker.ts` | Agent version checking via npm registry |
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
### 4. Hooks Layer (`src/hooks/`)
|
|
134
|
+
|
|
135
|
+
**Purpose**: React state management. Hook composition via useAgent facade.
|
|
136
|
+
|
|
137
|
+
| Hook | Responsibility |
|
|
138
|
+
|------|---------------|
|
|
139
|
+
| `useAgent` | Facade: composes useAgentSession + useAgentMessages. Single `onSessionUpdate` subscription. Return is `useMemo`-wrapped. |
|
|
140
|
+
| `useAgentSession` | Session lifecycle (create/close/restart), mode/model/configOption with optimistic updates. Uses `sessionRef` pattern. |
|
|
141
|
+
| `useAgentMessages` | Message state, RAF-batched streaming, permissions (activePermission derivation, approve/reject) |
|
|
142
|
+
| `useSuggestions` | @[[note]] mentions + /command suggestions (unified). Return is `useMemo`-wrapped. |
|
|
143
|
+
| `useSessionHistory` | Session list/load/resume/fork, local session storage, 5-min cache. Return is `useMemo`-wrapped. |
|
|
144
|
+
| `useChatActions` | Business callbacks (send, newChat, export, restart, config changes). Individual method deps for stability. |
|
|
145
|
+
| `useHistoryModal` | Session history modal lifecycle (lazy creation, props sync) |
|
|
146
|
+
| `useSettings` | Settings subscription via useSyncExternalStore |
|
|
147
|
+
|
|
148
|
+
**Dependency Rule**: Hooks import from `types/`, `acp/`, `services/`, `utils/`. Never from `ui/`.
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
### 5. UI Layer (`src/ui/`)
|
|
153
|
+
|
|
154
|
+
**Purpose**: React components. Rendering and user interaction.
|
|
155
|
+
|
|
156
|
+
#### Core Architecture
|
|
157
|
+
|
|
158
|
+
**ChatContext** provides shared services to the component tree:
|
|
159
|
+
```typescript
|
|
160
|
+
interface ChatContextValue {
|
|
161
|
+
plugin: AgentClientPlugin;
|
|
162
|
+
acpClient: AcpClient;
|
|
163
|
+
vaultService: VaultService;
|
|
164
|
+
settingsService: SettingsService;
|
|
165
|
+
}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
**ChatPanel** is the central orchestrator:
|
|
169
|
+
- Calls hooks: useAgent, useSuggestions, useSessionHistory, useChatActions, useHistoryModal, useSettings
|
|
170
|
+
- Does NOT route session updates (useAgent handles that internally)
|
|
171
|
+
- Handles workspace events via ref pattern (stable event registration)
|
|
172
|
+
- Renders ChatHeader, MessageList, InputArea directly
|
|
173
|
+
|
|
174
|
+
**ChatView** (sidebar) and **FloatingChatView** (floating window) are thin wrappers:
|
|
175
|
+
- Create services (AcpClient, VaultService) in lifecycle methods
|
|
176
|
+
- Provide ChatContext
|
|
177
|
+
- Render ChatPanel with `variant` prop
|
|
178
|
+
- Implement IChatViewContainer for broadcast commands
|
|
179
|
+
|
|
180
|
+
#### Component Tree
|
|
181
|
+
|
|
182
|
+
```
|
|
183
|
+
ChatView / FloatingChatView
|
|
184
|
+
└── ChatContextProvider
|
|
185
|
+
└── ChatPanel (variant="sidebar" | "floating")
|
|
186
|
+
├── ChatHeader (variant-based rendering)
|
|
187
|
+
├── MessageList (virtualized via @tanstack/react-virtual)
|
|
188
|
+
│ └── MessageBubble (per message, React.memo)
|
|
189
|
+
│ ├── ToolCallBlock (React.memo) → PermissionBanner
|
|
190
|
+
│ ├── TerminalBlock (React.memo)
|
|
191
|
+
│ └── MarkdownRenderer
|
|
192
|
+
├── InputArea
|
|
193
|
+
│ ├── SuggestionPopup (mentions / commands)
|
|
194
|
+
│ ├── ErrorBanner
|
|
195
|
+
│ ├── AttachmentStrip
|
|
196
|
+
│ └── InputToolbar (config/mode/model/usage/send)
|
|
197
|
+
└── SessionHistoryModal (imperative, via useHistoryModal)
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
---
|
|
201
|
+
|
|
202
|
+
### 6. Utils Layer (`src/utils/`)
|
|
203
|
+
|
|
204
|
+
**Purpose**: Pure utility functions. No React, no Obsidian dependencies (except `platform.ts`).
|
|
205
|
+
|
|
206
|
+
| File | Purpose |
|
|
207
|
+
|------|---------|
|
|
208
|
+
| `platform.ts` | Shell detection, WSL path conversion, Windows PATH from registry, platform-specific command preparation |
|
|
209
|
+
| `paths.ts` | Path resolution (which/where), file:// URI building, relative path conversion |
|
|
210
|
+
| `error-utils.ts` | ACP error code → user-friendly title/suggestion conversion |
|
|
211
|
+
| `mention-parser.ts` | @[[note]] detection, replacement, extraction from text |
|
|
212
|
+
| `logger.ts` | Singleton logger respecting debugMode setting |
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
## Dependency Flow
|
|
217
|
+
|
|
218
|
+
```
|
|
219
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
220
|
+
│ UI Layer │
|
|
221
|
+
│ │
|
|
222
|
+
│ ChatView / FloatingChatView (Context Providers) │
|
|
223
|
+
│ └── ChatPanel (hook composition + rendering) │
|
|
224
|
+
│ ├── ChatHeader, MessageList, InputArea │
|
|
225
|
+
│ └── MessageBubble, ToolCallBlock, etc. │
|
|
226
|
+
└─────────────────────────────┬───────────────────────────────┘
|
|
227
|
+
↓ calls hooks
|
|
228
|
+
┌─────────────────────────────┴───────────────────────────────┐
|
|
229
|
+
│ Hooks Layer │
|
|
230
|
+
│ useAgent (facade) → useAgentSession + useAgentMessages │
|
|
231
|
+
│ useSuggestions, useSessionHistory, useChatActions, │
|
|
232
|
+
│ useHistoryModal, useSettings │
|
|
233
|
+
└───────────┬─────────────────────────────┬───────────────────┘
|
|
234
|
+
↓ calls ↓ reads types
|
|
235
|
+
┌───────────┴───────────┐ ┌─────────────┴───────────────────┐
|
|
236
|
+
│ Services Layer │ │ Types Layer │
|
|
237
|
+
│ VaultService │ │ chat.ts, session.ts, │
|
|
238
|
+
│ SettingsService │ │ agent.ts, errors.ts │
|
|
239
|
+
│ SessionStorage │ └─────────────────────────────────┘
|
|
240
|
+
│ settings-normalizer │
|
|
241
|
+
│ session-helpers │
|
|
242
|
+
│ session-state │
|
|
243
|
+
│ message-state │
|
|
244
|
+
│ message-sender │
|
|
245
|
+
│ chat-exporter │
|
|
246
|
+
│ view-registry │
|
|
247
|
+
└───────────┬───────────┘
|
|
248
|
+
↓ communicates
|
|
249
|
+
┌───────────┴───────────┐
|
|
250
|
+
│ ACP Layer │
|
|
251
|
+
│ acp-client.ts │
|
|
252
|
+
│ acp-handler.ts │
|
|
253
|
+
│ type-converter.ts │
|
|
254
|
+
│ permission-handler │
|
|
255
|
+
│ terminal-handler │
|
|
256
|
+
└───────────────────────┘
|
|
257
|
+
↑
|
|
258
|
+
@agentclientprotocol/sdk
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
---
|
|
262
|
+
|
|
263
|
+
## Design Patterns
|
|
264
|
+
|
|
265
|
+
### 1. useAgent Facade Pattern
|
|
266
|
+
- `useAgent` composes `useAgentSession` + `useAgentMessages`
|
|
267
|
+
- Single `onSessionUpdate` subscription, dispatches to both sub-hooks
|
|
268
|
+
- ChatPanel calls useAgent, not sub-hooks directly
|
|
269
|
+
- Return is `useMemo`-wrapped for referential stability
|
|
270
|
+
|
|
271
|
+
### 2. React Context for Services
|
|
272
|
+
- `ChatContext` provides plugin, acpClient, vaultService, settingsService
|
|
273
|
+
- Value is stable (service instances don't change)
|
|
274
|
+
- Eliminates prop drilling for shared dependencies
|
|
275
|
+
|
|
276
|
+
### 3. Single Event Channel
|
|
277
|
+
- All agent events flow through `onSessionUpdate` (messages, session updates, permissions, errors)
|
|
278
|
+
- No special callback paths (onUpdateMessage, onError removed)
|
|
279
|
+
- AcpHandler filters by `currentSessionId` before broadcasting
|
|
280
|
+
|
|
281
|
+
### 4. ACP Isolation
|
|
282
|
+
- All `@agentclientprotocol/sdk` imports confined to `acp/`
|
|
283
|
+
- `AcpClient` (UI-facing) and `AcpHandler` (SDK-facing) separate concerns
|
|
284
|
+
- `type-converter.ts` is the change buffer for protocol updates
|
|
285
|
+
|
|
286
|
+
### 5. Performance Patterns
|
|
287
|
+
- **useMemo for return stability**: useAgent, useSuggestions, useSessionHistory wrap returns in useMemo
|
|
288
|
+
- **sessionRef pattern**: useAgentSession stores session in useRef, reads in callbacks without adding to deps
|
|
289
|
+
- **Individual method deps**: useChatActions uses `agent.sendMessage` not `agent` object in deps
|
|
290
|
+
- **Workspace event refs**: ChatPanel stores handler callbacks in refs, keeping useEffect deps minimal
|
|
291
|
+
- **RAF batching**: useAgentMessages batches streaming updates per animation frame
|
|
292
|
+
- **React.memo**: MessageBubble, ToolCallBlock, TerminalBlock for skip-render optimization
|
|
293
|
+
- **Virtual scroll**: MessageList uses @tanstack/react-virtual
|
|
294
|
+
- **O(1) tool call index**: Map<string, number> for tool call upsert
|
|
295
|
+
|
|
296
|
+
### 6. Observer Pattern
|
|
297
|
+
- `SettingsService` notifies subscribers on change
|
|
298
|
+
- React components use `useSyncExternalStore`
|
|
299
|
+
|
|
300
|
+
### 7. Ref Pattern for Callbacks
|
|
301
|
+
- IChatViewContainer callbacks use refs for latest values
|
|
302
|
+
- Workspace event handlers use refs to avoid re-registration
|
|
303
|
+
- Unmount cleanup uses refs to access latest state
|
|
304
|
+
|
|
305
|
+
---
|
|
306
|
+
|
|
307
|
+
## Key Benefits
|
|
308
|
+
|
|
309
|
+
### 1. Flat and Readable
|
|
310
|
+
- 4 layers (types → acp/services → hooks → ui)
|
|
311
|
+
- No port/adapter indirection
|
|
312
|
+
- File names reflect functionality
|
|
313
|
+
|
|
314
|
+
### 2. ACP Change Resistance
|
|
315
|
+
- Only `acp/` directory needs changes for protocol updates
|
|
316
|
+
- `type-converter.ts` localizes type mapping changes
|
|
317
|
+
|
|
318
|
+
### 3. Easy Feature Addition
|
|
319
|
+
- New hook: create in `hooks/`, call in `ChatPanel`, wrap return in `useMemo`
|
|
320
|
+
- New message type: add to `types/session.ts`, handle in `useAgentMessages` or `message-state.ts`, render in `MessageBubble`
|
|
321
|
+
- New agent: add settings in `plugin.ts`, configure in `SettingsTab`
|
|
322
|
+
|
|
323
|
+
### 4. Maintainability
|
|
324
|
+
- ~19,800 lines across 56 files
|
|
325
|
+
- Services testable without React (zero React imports)
|
|
326
|
+
- Clear dependency direction (no circular dependencies)
|
|
327
|
+
|
|
328
|
+
---
|
|
329
|
+
|
|
330
|
+
## File Naming Conventions
|
|
331
|
+
|
|
332
|
+
| Pattern | Example |
|
|
333
|
+
|---------|---------|
|
|
334
|
+
| Types | `kebab-case.ts` in `types/` |
|
|
335
|
+
| ACP | `kebab-case.ts` in `acp/` |
|
|
336
|
+
| Services | `kebab-case.ts` in `services/` |
|
|
337
|
+
| Hooks | `use*.ts` in `hooks/` |
|
|
338
|
+
| Components | `PascalCase.tsx` in `ui/` |
|
|
339
|
+
| Utilities | `kebab-case.ts` in `utils/` |
|
|
340
|
+
|
|
341
|
+
---
|
|
342
|
+
|
|
343
|
+
## Adding New Features
|
|
344
|
+
|
|
345
|
+
### Adding a New Hook
|
|
346
|
+
1. Create `hooks/use[Feature].ts`
|
|
347
|
+
2. Define state with useState/useReducer
|
|
348
|
+
3. Call the hook in `ui/ChatPanel.tsx`
|
|
349
|
+
4. Pass state/callbacks to child components as props
|
|
350
|
+
5. Wrap return object in `useMemo` if passed as dependency to other hooks
|
|
351
|
+
|
|
352
|
+
### Adding a New Session Update Type
|
|
353
|
+
1. Add interface to `types/session.ts`, add to `SessionUpdate` union
|
|
354
|
+
2. Handle in `acp/acp-handler.ts` `sessionUpdate()` switch
|
|
355
|
+
3. Convert from ACP type in `acp/type-converter.ts` if needed
|
|
356
|
+
4. Handle in `hooks/useAgentSession.ts` `handleSessionUpdate()` (for session-level)
|
|
357
|
+
5. Or handle via `applySingleUpdate()` in `services/message-state.ts` (for message-level)
|
|
358
|
+
6. No routing needed in ChatPanel — useAgent handles dispatch internally
|
|
359
|
+
|
|
360
|
+
### Adding a New Agent Type
|
|
361
|
+
1. Add settings type to `types/agent.ts`
|
|
362
|
+
2. Add config in `plugin.ts` settings
|
|
363
|
+
3. Add API key injection in `services/session-helpers.ts`
|
|
364
|
+
4. Update `ui/SettingsTab.ts` for configuration UI
|
|
365
|
+
|
|
366
|
+
---
|
|
367
|
+
|
|
368
|
+
## Migration Notes
|
|
369
|
+
|
|
370
|
+
### March 2026: Simplified Architecture Refactoring
|
|
371
|
+
|
|
372
|
+
Refactored from Port/Adapter Architecture to simplified layered architecture:
|
|
373
|
+
|
|
374
|
+
- **Removed**: `domain/models/` (9 files → `types/` 4 files), `domain/ports/` (5 files → interfaces moved to implementation files), `adapters/` directory, `components/` directory, `shared/` directory
|
|
375
|
+
- **Added**: `types/`, `acp/`, `services/`, `ui/`, `utils/` flat directories, `ChatPanel` + `ChatContext`
|
|
376
|
+
- **Merged**: VaultAdapter + MentionService → VaultService, useMentions + useAutoMention → useMentions
|
|
377
|
+
- **Removed**: useChatController (god hook → ChatPanel component), Port files (no implementation swapping planned)
|
|
378
|
+
- **Result**: 76 → 50 files, 5 → 4 layers, flat directory structure
|
|
379
|
+
|
|
380
|
+
### April 2026: Simplification & Performance Refactoring
|
|
381
|
+
|
|
382
|
+
Refactored data flow, hooks, services, and performance:
|
|
383
|
+
|
|
384
|
+
- **ACP wiring**: 3 exit points (onSessionUpdate, onError, setUpdateMessageCallback) → 1 (onSessionUpdate only). Multiple listeners via Set. SessionId filter in AcpHandler.
|
|
385
|
+
- **Hook consolidation**: 7 hooks → 4 public hooks. useSession + useMessages + usePermission → useAgent (facade) + useAgentSession + useAgentMessages. useMentions + useSlashCommands → useSuggestions. New: useChatActions, useHistoryModal.
|
|
386
|
+
- **ChatPanel slimmed**: 1,483 → 936 lines. Session update routing removed (moved to useAgent). Business callbacks extracted to useChatActions. History modal extracted to useHistoryModal. Workspace events stabilized with refs.
|
|
387
|
+
- **Services split**: settings-service.ts (722 lines) → settings-service (285) + session-storage (267) + settings-normalizer (264). Pure functions extracted: message-state.ts, session-state.ts.
|
|
388
|
+
- **plugin.ts cleaned**: loadSettings compressed with helper functions (370 → 120 lines). Legacy floatingChatInstances removed. Double-save fixed.
|
|
389
|
+
- **Performance**: useMemo on hook returns (useAgent, useSuggestions, useSessionHistory). sessionRef pattern in useAgentSession. Individual method deps in useChatActions. Workspace event handler refs in ChatPanel.
|
|
390
|
+
- **Result**: 50 → 56 files, ~19,800 lines. Single event channel. All hooks stabilized.
|
package/CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
# Contributing to Agent Client Plugin
|
|
2
|
+
|
|
3
|
+
Thank you for your interest in contributing to the Agent Client plugin!
|
|
4
|
+
|
|
5
|
+
## Before You Start
|
|
6
|
+
|
|
7
|
+
### Please Open an Issue First
|
|
8
|
+
|
|
9
|
+
**For significant changes, please open an issue before writing code:**
|
|
10
|
+
|
|
11
|
+
- New features
|
|
12
|
+
- Architecture changes
|
|
13
|
+
- Adding or modifying external dependencies
|
|
14
|
+
- Implementing draft/experimental ACP specifications
|
|
15
|
+
|
|
16
|
+
This helps ensure alignment with the project direction and saves time for both contributors and maintainers.
|
|
17
|
+
|
|
18
|
+
**You can submit a PR directly for:**
|
|
19
|
+
|
|
20
|
+
- Obvious bug fixes (typos, crashes, etc.)
|
|
21
|
+
- Fixes for existing issues
|
|
22
|
+
- Documentation improvements
|
|
23
|
+
|
|
24
|
+
### Project Scope
|
|
25
|
+
|
|
26
|
+
This plugin focuses on **ACP client implementation** + **features that make ACP convenient to use in Obsidian**.
|
|
27
|
+
|
|
28
|
+
**In scope:**
|
|
29
|
+
|
|
30
|
+
- ACP protocol implementation
|
|
31
|
+
- Note mentions (`@[[note]]` to pass note content to agents)
|
|
32
|
+
- Obsidian-specific UI integration
|
|
33
|
+
|
|
34
|
+
**Out of scope:**
|
|
35
|
+
|
|
36
|
+
- Features achievable via standard protocols like MCP (these should be provided as MCP servers for a consistent experience across all agents)
|
|
37
|
+
- Agent-specific features (these should be handled via agent-specific config files, e.g., `.claude/` directory)
|
|
38
|
+
|
|
39
|
+
## Development Setup
|
|
40
|
+
|
|
41
|
+
### Prerequisites
|
|
42
|
+
|
|
43
|
+
- Node.js 18.x or later
|
|
44
|
+
- npm
|
|
45
|
+
|
|
46
|
+
### Setup Steps
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
# Navigate to your vault's plugins directory
|
|
50
|
+
cd /path/to/your/vault/.obsidian/plugins
|
|
51
|
+
|
|
52
|
+
# Clone the repository as "agent-client"
|
|
53
|
+
# The directory name must match the id in manifest.json
|
|
54
|
+
git clone https://github.com/RAIT-09/obsidian-agent-client.git agent-client
|
|
55
|
+
cd agent-client
|
|
56
|
+
|
|
57
|
+
# Install dependencies
|
|
58
|
+
npm install
|
|
59
|
+
|
|
60
|
+
# Start development build (watch mode)
|
|
61
|
+
npm run dev
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Testing in Obsidian
|
|
65
|
+
|
|
66
|
+
1. After cloning to `.obsidian/plugins/agent-client`, run `npm run dev`
|
|
67
|
+
2. Enable the plugin in Obsidian Settings → Community Plugins
|
|
68
|
+
3. Code changes trigger automatic rebuilds, but you need to reload the plugin (toggle it off/on in Community Plugins) to see changes
|
|
69
|
+
|
|
70
|
+
## Available Commands
|
|
71
|
+
|
|
72
|
+
| Command | Description |
|
|
73
|
+
| ------------------- | ------------------------------------------------ |
|
|
74
|
+
| `npm run dev` | Development build (watch mode) |
|
|
75
|
+
| `npm run build` | Production build (includes TypeScript type check)|
|
|
76
|
+
| `npm run lint` | Run ESLint |
|
|
77
|
+
| `npm run lint:fix` | Run ESLint with auto-fix |
|
|
78
|
+
| `npm run format` | Format code with Prettier |
|
|
79
|
+
| `npm run format:check` | Check formatting (used in CI) |
|
|
80
|
+
|
|
81
|
+
## Code Style
|
|
82
|
+
|
|
83
|
+
### Prettier Configuration
|
|
84
|
+
|
|
85
|
+
| Setting | Value |
|
|
86
|
+
| -------------- | ------------- |
|
|
87
|
+
| Indentation | Tabs (width 4)|
|
|
88
|
+
| Semicolons | Yes |
|
|
89
|
+
| Quotes | Double |
|
|
90
|
+
| Trailing comma | All |
|
|
91
|
+
| Print width | 80 |
|
|
92
|
+
| End of line | LF |
|
|
93
|
+
|
|
94
|
+
### ESLint
|
|
95
|
+
|
|
96
|
+
We use `eslint-plugin-obsidianmd` for Obsidian-specific rules and `typescript-eslint` for TypeScript.
|
|
97
|
+
|
|
98
|
+
### Obsidian Plugin Guidelines
|
|
99
|
+
|
|
100
|
+
1. **No innerHTML/outerHTML** — Use `createEl`, `createDiv`, `createSpan`
|
|
101
|
+
2. **Don't detach leaves in onunload** — This is an anti-pattern
|
|
102
|
+
3. **Styles in CSS only** — No JS style manipulation
|
|
103
|
+
4. **Use Platform API** — Don't use `process.platform`
|
|
104
|
+
5. **Minimize `any`** — Use proper types
|
|
105
|
+
|
|
106
|
+
### File Naming Conventions
|
|
107
|
+
|
|
108
|
+
- **Types**: `kebab-case.ts` in `types/`
|
|
109
|
+
- **ACP**: `kebab-case.ts` in `acp/`
|
|
110
|
+
- **Services**: `kebab-case.ts` in `services/`
|
|
111
|
+
- **Hooks**: `use*.ts` in `hooks/`
|
|
112
|
+
- **Components**: `PascalCase.tsx` in `ui/`
|
|
113
|
+
- **Utilities**: `kebab-case.ts` in `utils/`
|
|
114
|
+
|
|
115
|
+
## Branch Naming
|
|
116
|
+
|
|
117
|
+
```
|
|
118
|
+
{username}/{type}/{description}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
**Types:**
|
|
122
|
+
|
|
123
|
+
- `feature/` — New feature
|
|
124
|
+
- `fix/` — Bug fix
|
|
125
|
+
- `refactor/` — Refactoring
|
|
126
|
+
- `docs/` — Documentation
|
|
127
|
+
- `hotfix/` — Urgent fix
|
|
128
|
+
|
|
129
|
+
**Examples:**
|
|
130
|
+
|
|
131
|
+
- `yourname/feature/add-export`
|
|
132
|
+
- `yourname/fix/message-rendering`
|
|
133
|
+
|
|
134
|
+
## Commit Messages
|
|
135
|
+
|
|
136
|
+
We recommend [Conventional Commits](https://www.conventionalcommits.org/) style:
|
|
137
|
+
|
|
138
|
+
```
|
|
139
|
+
<type>: <description>
|
|
140
|
+
|
|
141
|
+
<optional body>
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
**Types:**
|
|
145
|
+
|
|
146
|
+
- `feat:` — New feature
|
|
147
|
+
- `fix:` — Bug fix
|
|
148
|
+
- `refactor:` — Refactoring
|
|
149
|
+
- `docs:` — Documentation
|
|
150
|
+
- `chore:` — Build/dependencies
|
|
151
|
+
- `style:` — Formatting (no functional changes)
|
|
152
|
+
|
|
153
|
+
## Pull Request Process
|
|
154
|
+
|
|
155
|
+
### Workflow
|
|
156
|
+
|
|
157
|
+
1. Create a branch from `master`
|
|
158
|
+
- `master` is the stable branch, `dev` is for development
|
|
159
|
+
- Feature PRs typically target `dev`, hotfixes target `master`
|
|
160
|
+
2. Make your changes and commit
|
|
161
|
+
3. Create a pull request
|
|
162
|
+
4. Ensure CI passes (lint, build)
|
|
163
|
+
5. Wait for review
|
|
164
|
+
|
|
165
|
+
### PR Checklist
|
|
166
|
+
|
|
167
|
+
Before submitting, please verify:
|
|
168
|
+
|
|
169
|
+
- [ ] `npm run lint` passes
|
|
170
|
+
- [ ] `npm run build` passes
|
|
171
|
+
- [ ] Tested in Obsidian
|
|
172
|
+
- [ ] Existing functionality still works
|
|
173
|
+
- [ ] Documentation updated if needed
|
|
174
|
+
|
|
175
|
+
### CI
|
|
176
|
+
|
|
177
|
+
Pull requests automatically run:
|
|
178
|
+
|
|
179
|
+
- ESLint (`npx eslint src/`)
|
|
180
|
+
- Build (`npm run build`)
|
|
181
|
+
|
|
182
|
+
Please ensure these pass locally before submitting.
|
|
183
|
+
|
|
184
|
+
**Note:** "Use sentence case for UI text" lint errors are acceptable for brand names and proper nouns (e.g., "Claude Code", "Gemini CLI").
|
|
185
|
+
|
|
186
|
+
## Architecture Overview
|
|
187
|
+
|
|
188
|
+
```
|
|
189
|
+
src/
|
|
190
|
+
├── types/ # Pure type definitions (no logic, no dependencies)
|
|
191
|
+
├── acp/ # ACP protocol layer (SDK confined here)
|
|
192
|
+
├── services/ # Non-React business logic + pure functions
|
|
193
|
+
├── hooks/ # React custom hooks (useAgent facade + sub-hooks)
|
|
194
|
+
├── ui/ # React components (ChatPanel orchestrator)
|
|
195
|
+
└── utils/ # Shared utility functions
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### Architecture Principles
|
|
199
|
+
|
|
200
|
+
1. **useAgent as facade** — Composes useAgentSession + useAgentMessages. Single `onSessionUpdate` subscription.
|
|
201
|
+
2. **Services have zero React imports** — Pure functions and classes in `services/`
|
|
202
|
+
3. **ACP isolation** — All `@agentclientprotocol/sdk` imports confined to `acp/`
|
|
203
|
+
4. **Types have zero deps** — No `obsidian`, no SDK, no React in `types/`
|
|
204
|
+
5. **Single event channel** — All agent events flow through `onSessionUpdate`. No special callback paths.
|
|
205
|
+
|
|
206
|
+
For more details, see `ARCHITECTURE.md`.
|
|
207
|
+
|
|
208
|
+
## ACP Notes
|
|
209
|
+
|
|
210
|
+
- Prioritize implementations that conform to the official (stable) ACP specification
|
|
211
|
+
- If implementing draft/experimental specs, please discuss in an issue first
|
|
212
|
+
- Implementations should work with official ACP-compatible agents (e.g., `@agentclientprotocol/claude-agent-acp`)
|
|
213
|
+
|
|
214
|
+
## Questions?
|
|
215
|
+
|
|
216
|
+
Open an issue if you have any questions!
|