refactor: remove Anthropic API integration, CLI agent only (ADR-0028)

Remove AIChatPanel, useAIChat hook, Rust ai_chat command, and
anthropic_key from settings. AI is now exclusively via Claude CLI
subprocess (AiPanel). Simplifies codebase and eliminates API key
management for users. ADR-0027 superseded.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Test
2026-03-29 15:05:56 +02:00
parent 85b545a0bc
commit d9254ffaf5
25 changed files with 67 additions and 1275 deletions

View File

@@ -523,7 +523,6 @@ App-level settings persisted at `~/.config/com.laputa.app/settings.json`:
```typescript
interface Settings {
anthropic_key: string | null
openai_key: string | null
google_key: string | null
github_token: string | null

View File

@@ -21,7 +21,7 @@ When deciding where to persist a piece of data, ask: **"Would the user want this
| Follows the vault | Stays with the installation |
|-------------------|-----------------------------|
| Type icon, type color | Editor zoom level |
| Pinned properties per type | API keys (Anthropic, OpenAI) |
| Pinned properties per type | API keys (OpenAI, Google) |
| Sidebar label overrides | GitHub token |
| Property display order | Window size / position |
| Any user-visible customization of how content is organized or displayed | Any machine-specific or credential-type setting |
@@ -31,7 +31,6 @@ When deciding where to persist a piece of data, ask: **"Would the user want this
Examples:
- ✅ Vault: `_pinned_properties` in a Type note (every device should show the same pinned properties)
- ✅ Vault: `_icon: shapes` in a Type note (icon is part of the type's identity)
- ✅ App settings: `anthropic_key` (credential, not vault data)
- ✅ App settings: `zoom: 1.3` (machine-specific preference)
### No hardcoded exceptions
@@ -97,7 +96,6 @@ flowchart LR
| Build | Vite | 7.3.1 |
| Backend language | Rust (edition 2021) | 1.77.2 |
| Frontmatter parsing | gray_matter | 0.2 |
| AI (in-app chat) | Anthropic Claude API (Haiku 3.5 default) | - |
| AI (agent panel) | Claude CLI subprocess (streaming NDJSON) | - |
| Search | Keyword (walkdir-based file scan) | - |
| MCP | @modelcontextprotocol/sdk | 1.0 |
@@ -116,14 +114,13 @@ flowchart TD
NL["NoteList / PulseView\n(filtered list / activity)"]
ED["Editor\n(BlockNote + diff + raw)"]
IN["Inspector\n(metadata + relationships)"]
AIC["AIChatPanel\n(API-based chat)"]
AIP["AiPanel\n(Claude CLI agent + tools)"]
SP["SearchPanel\n(keyword search)"]
ST["StatusBar\n(vault picker + sync + version)"]
CP["CommandPalette\n(Cmd+K launcher)"]
App --> WS & SB & NL & ED & SP & ST & CP
ED --> IN & AIC & AIP
ED --> IN & AIP
end
subgraph RB["Rust Backend"]
@@ -138,7 +135,6 @@ flowchart TD
end
subgraph EXT["External Services"]
ANTH["Anthropic API\n(Claude chat)"]
CCLI["Claude CLI\n(agent subprocess)"]
MCP["MCP Server\n(ws://9710, 9711)"]
GHAPI["GitHub API\n(OAuth, repos, clone)"]
@@ -179,7 +175,7 @@ flowchart TD
- **Sidebar** (150-400px, resizable): Top-level filters (All Notes, Changes, Pulse) and collapsible type-based section groups. Each type can have a custom icon, color, sort, and visibility set via its type document in `type/`.
- **Note List / Pulse View** (200-500px, resizable): When a section group or filter is selected, shows filtered notes with snippets, modified dates, and status indicators. When Pulse filter is active, shows `PulseView` — a chronological git activity feed grouped by day.
- **Editor** (flex, fills remaining space): Single note open at a time (no tabs — see ADR-0003). Breadcrumb bar with word count, BlockNote rich text editor with wikilink support. Can toggle to diff view (modified files) or raw CodeMirror view. Decomposed into `Editor` (orchestrator), `EditorContent`, `EditorRightPanel`, `SingleEditorView`, with hooks `useDiffMode`, `useEditorFocus`, `useEditorSave`, `useRawMode`. Navigation history (Cmd+[/]) replaces tabs.
- **Inspector / AI Chat / AI Agent** (200-500px or 40px collapsed): Toggles between Inspector (frontmatter, relationships, instances, backlinks, git history), AI Chat panel (API-based), and AI Agent panel (Claude CLI subprocess with tool execution). The Sparkle icon in the breadcrumb bar toggles between them. When viewing a Type note, the Inspector shows an **Instances** section listing all notes of that type (sorted by modified_at desc, capped at 50).
- **Inspector / AI Agent** (200-500px or 40px collapsed): Toggles between Inspector (frontmatter, relationships, instances, backlinks, git history) and AI Agent panel (Claude CLI subprocess with tool execution). The Sparkle icon in the breadcrumb bar toggles between them. When viewing a Type note, the Inspector shows an **Instances** section listing all notes of that type (sorted by modified_at desc, capped at 50).
Panels are separated by `ResizeHandle` components that support drag-to-resize.
@@ -203,16 +199,6 @@ Notes can be opened in separate Tauri windows for focused editing. Secondary win
## AI System
Laputa has two AI interfaces with distinct architectures:
### AI Chat (AIChatPanel)
Simple chat mode — no tool execution, streaming text responses.
1. **Frontend** (`AIChatPanel` + `useAIChat` hook) — UI and state management
2. **API Proxy** (Vite middleware in dev, Rust `ai_chat` command in Tauri) — routes to Anthropic
3. **Context picker** — selected notes sent as system context with token estimation
### AI Agent (AiPanel)
Full agent mode — spawns Claude CLI as a subprocess with tool access and MCP vault integration.
@@ -263,7 +249,7 @@ When the agent writes or edits vault files, `useAiAgent` detects this from tool
### Context Building
Both AI modes use context from the active note and linked entries. The agent panel (`ai-context.ts`) builds a structured JSON snapshot:
The agent panel (`ai-context.ts`) builds a structured JSON snapshot from the active note and linked entries:
```json
{
@@ -277,19 +263,9 @@ Both AI modes use context from the active note and linked entries. The agent pan
Token budget: 60% of 180k context limit (~108k tokens max). Active note gets priority, then linked notes, then truncation.
### Models (Chat mode)
### Authentication
| Model | ID | Use case |
|-------|----|----------|
| Haiku 3.5 | `claude-3-5-haiku-20241022` | Fast, cheap — default |
| Sonnet 4 | `claude-sonnet-4-20250514` | Balanced |
| Opus 4 | `claude-opus-4-20250514` | Most capable |
### API Key Management
- Stored in app settings (`~/.config/com.laputa.app/settings.json`) under `anthropic_key`
- Configurable via Settings panel (also supports `openai_key`, `google_key`)
- Claude CLI (agent mode) uses its own authentication — no API key needed
Claude CLI (agent mode) uses its own authentication — no API key configuration needed in Laputa.
## MCP Server
@@ -589,7 +565,6 @@ The vault backend (`src-tauri/src/vault/`) is split into focused submodules:
| `github/` | GitHub OAuth + API (`auth.rs`, `api.rs`, `clone.rs`) |
| `search.rs` | Keyword search — walkdir-based vault file scan |
| `claude_cli.rs` | Claude CLI subprocess spawning + NDJSON stream parsing |
| `ai_chat.rs` | Direct Anthropic API client (non-streaming, for Tauri builds) |
| `mcp.rs` | MCP server spawning + config registration |
| `commands/` | Tauri command handlers (split into submodules) |
| `settings.rs` | App settings persistence |
@@ -674,7 +649,6 @@ The vault backend (`src-tauri/src/vault/`) is split into focused submodules:
| Command | Description |
|---------|-------------|
| `ai_chat` | Direct Anthropic API call (non-streaming) |
| `stream_claude_chat` | Claude CLI chat mode (streaming) |
| `stream_claude_agent` | Claude CLI agent mode (streaming + tools) |
| `check_claude_cli` | Check if Claude CLI is available |
@@ -726,7 +700,6 @@ No Redux or global context. State lives in the root `App.tsx` and custom hooks:
| `useTabManagement` | Navigation history, note switching | Note navigation lifecycle |
| `useVaultSwitcher` | `vaultPath`, `extraVaults` | Vault switching |
| `useTheme` | Editor theme CSS vars | Editor typography theme |
| `useAIChat` | `messages`, `isStreaming` | AI chat conversation |
| `useAiAgent` | `messages`, `status`, tool actions | AI agent conversation |
| `useAutoSync` | Sync interval, pull/push state | Git auto-sync |
| `useUnifiedSearch` | Query, results, loading state | Keyword search |

View File

@@ -53,7 +53,6 @@ laputa-app/
│ │ ├── RawEditorView.tsx # CodeMirror raw editor
│ │ ├── Inspector.tsx # Fourth panel: metadata + relationships
│ │ ├── DynamicPropertiesPanel.tsx # Editable frontmatter properties
│ │ ├── AIChatPanel.tsx # AI chat (API-based)
│ │ ├── AiPanel.tsx # AI agent (Claude CLI subprocess)
│ │ ├── AiMessage.tsx # Agent message display
│ │ ├── AiActionCard.tsx # Agent tool action cards
@@ -86,7 +85,6 @@ laputa-app/
│ │ ├── useNoteActions.ts # Composes creation + rename + frontmatter
│ │ ├── useNoteCreation.ts # Note/type/daily-note creation
│ │ ├── useNoteRename.ts # Note renaming + wikilink updates
│ │ ├── useAIChat.ts # AI chat state
│ │ ├── useAiAgent.ts # AI agent state + tool tracking
│ │ ├── useAiActivity.ts # MCP UI bridge listener
│ │ ├── useAutoSync.ts # Auto git pull/push
@@ -110,7 +108,7 @@ laputa-app/
│ │ ├── wikilinks.ts # Wikilink preprocessing pipeline
│ │ ├── frontmatter.ts # TypeScript YAML parser
│ │ ├── ai-agent.ts # Agent stream utilities
│ │ ├── ai-chat.ts # Chat API client + token estimation
│ │ ├── ai-chat.ts # Token estimation utilities
│ │ ├── ai-context.ts # Context snapshot builder
│ │ ├── noteListHelpers.ts # Sorting, filtering, date formatting
│ │ ├── wikilink.ts # Wikilink resolution
@@ -155,7 +153,6 @@ laputa-app/
│ │ ├── telemetry.rs # Sentry init + path scrubber
│ │ ├── search.rs # Keyword search (walkdir-based)
│ │ ├── claude_cli.rs # Claude CLI subprocess management
│ │ ├── ai_chat.rs # Direct Anthropic API client
│ │ ├── mcp.rs # MCP server lifecycle + registration
│ │ ├── settings.rs # App settings persistence
│ │ ├── vault_config.rs # Per-vault UI config
@@ -231,7 +228,6 @@ laputa-app/
| File | Why it matters |
|------|---------------|
| `src/components/AiPanel.tsx` | AI agent panel — Claude CLI with tool execution, reasoning, actions. |
| `src/components/AIChatPanel.tsx` | AI chat panel — API-based chat without tools. |
| `src/hooks/useAiAgent.ts` | Agent state: messages, streaming, tool tracking, file detection. |
| `src/utils/ai-context.ts` | Context snapshot builder for AI conversations. |

View File

@@ -30,7 +30,7 @@ Laputa can be ported to iPad using Tauri v2 iOS (beta) with **minimal code chang
|---------|---------|---------------------|
| Git operations | No `git` binary on iOS | **Option B (Working Copy)** for prototype; **Option A (isomorphic-git)** for production |
| GitHub clone/push/pull | Depends on git CLI | Same as above |
| Claude CLI streaming | No `claude` binary on iOS | Use Anthropic API directly (already available via `ai_chat`) |
| Claude CLI streaming | No `claude` binary on iOS | Use Anthropic API directly (requires new implementation) |
| MCP server / WS bridge | Spawns Node.js child process | Skip for mobile; explore in-process MCP later |
| macOS menu bar | Desktop-only API | Touch-native navigation (already handled by React) |
| Updater plugin | Desktop-only | Use TestFlight for updates |

View File

@@ -2,7 +2,8 @@
type: ADR
id: "0027"
title: "Dual AI architecture (API chat + CLI agent)"
status: active
status: superseded
superseded_by: "0028"
date: 2026-03-01
---

View File

@@ -0,0 +1,40 @@
---
type: ADR
id: "0028"
title: "CLI agent only — no direct Anthropic API key"
status: active
date: 2026-03-29
supersedes: "0027"
---
## Context
ADR-0027 introduced a dual AI architecture: a lightweight API-based chat (AIChatPanel) using the Anthropic API directly, and a full CLI agent (AiPanel) spawning Claude CLI as a subprocess with MCP tool access. In practice, the API chat was never shipped to users — the CLI agent covered all use cases and provided a superior experience through tool access and MCP integration. Maintaining two codepaths added complexity, and requiring users to manage an Anthropic API key created friction.
## Decision
**Remove the direct Anthropic API integration entirely. AI is available exclusively via CLI agent subprocesses (Claude Code, and in the future Codex or other CLI agents).** No API key field in settings. The CLI agent authenticates via its own mechanism (e.g. `claude` CLI login).
Removed:
- `AIChatPanel` component, `useAIChat` hook
- Rust `ai_chat` command and `ai_chat.rs` module
- `anthropic_key` field from Settings (Rust and TypeScript)
- Vite dev-server Anthropic API proxy (`aiChatProxyPlugin`, `aiAgentProxyPlugin`)
Kept:
- `AiPanel` + `useAiAgent` — Claude CLI subprocess with MCP vault integration
- Shared utilities in `ai-chat.ts` (`trimHistory`, `formatMessageWithHistory`, `streamClaudeChat`, etc.)
- `Cmd+I` keyboard shortcut and menu item for toggling the AI panel
## Options considered
- **Option A** (chosen): Remove API chat, keep CLI agent only. Simplifies codebase, removes API key management, single codepath.
- **Option B**: Keep both but hide API chat behind feature flag. Adds dead code weight without benefit.
- **Option C**: Replace CLI agent with API chat + manual tool calling. Loses MCP integration and Claude CLI features.
## Consequences
- Users no longer need to obtain or manage an Anthropic API key
- Existing saved API keys are silently ignored (the field no longer exists in the Settings struct; serde skips unknown fields on deserialization)
- Future CLI agents (Codex, etc.) can plug into the same `AiPanel` architecture
- If a lightweight chat mode is needed later, it should be built as a CLI agent mode, not a separate API integration