Add MVP specs

This commit is contained in:
Mateusz Tymek
2026-01-04 21:43:49 +00:00
parent 6a24431c1b
commit a6926aeab3
4 changed files with 280 additions and 0 deletions

View File

@@ -0,0 +1,120 @@
## Context
This plugin embeds the OpenCode AI assistant into Obsidian by spawning a local server process and displaying its web UI in an iframe within the Obsidian sidebar. The design prioritizes simplicity and reliability for the MVP.
**Constraints:**
- Desktop-only (requires Node.js child_process APIs)
- Must work within Obsidian's plugin sandbox
- OpenCode CLI must be installed separately by the user
## Goals / Non-Goals
**Goals:**
- Seamless embedding of OpenCode UI in Obsidian sidebar
- Reliable server process lifecycle management
- Configurable server and project settings
- Quick access via ribbon icon and keyboard shortcut
**Non-Goals:**
- Mobile support (not possible due to Node.js dependencies)
- Native UI rendering (OpenCode provides its own web UI)
- Multiple simultaneous OpenCode instances
- Deep Obsidian integration (file linking, note references)
## Decisions
### 1. Iframe Embedding vs Native Rendering
**Decision:** Embed OpenCode's web UI via iframe rather than building native Obsidian UI.
**Rationale:**
- OpenCode already provides a full-featured web interface
- Reduces maintenance burden - UI updates come from OpenCode automatically
- Allows feature parity with standalone OpenCode web experience
**Trade-offs:**
- Limited ability to customize UI appearance
- Requires CORS configuration (`--cors app://obsidian.md`)
- Iframe isolation limits some interactions
### 2. Process State Machine
**Decision:** Use a 4-state machine: `stopped``starting``running` | `error`
**Rationale:**
- Clear, predictable state transitions
- Enables reactive UI that shows appropriate content for each state
- Prevents race conditions (can't start while starting)
**States:**
- `stopped`: Server not running, show start button
- `starting`: Spawn initiated, polling health endpoint, show spinner
- `running`: Health check passed, show iframe
- `error`: Spawn failed or health check timeout, show retry option
### 3. Lazy Start Strategy
**Decision:** Start server when view opens, not when plugin loads.
**Rationale:**
- Faster Obsidian startup (server spawn is ~2-5 seconds)
- User may not need OpenCode every session
- Auto-start available as opt-in setting for users who prefer it
**Implementation:** `OpenCodeView.onOpen()` calls `plugin.startServer()` if state is `stopped`.
### 4. Health Check Polling
**Decision:** Poll `/global/health` endpoint every 500ms during startup, with 15-second timeout.
**Rationale:**
- Server needs time to initialize before accepting connections
- Polling is simple and reliable
- 15 seconds accommodates slow systems without waiting too long
**Trade-offs:**
- Slight delay before iframe loads (typically 1-3 seconds)
- Could use server stdout parsing instead, but health endpoint is more reliable
### 5. Graceful Shutdown
**Decision:** SIGTERM first, SIGKILL after 2-second timeout.
**Rationale:**
- Allows server to clean up resources gracefully
- SIGKILL fallback ensures process doesn't hang
- 2 seconds is sufficient for typical cleanup
### 6. Project Directory Encoding
**Decision:** Encode project directory as base64 in URL path (e.g., `http://127.0.0.1:14096/{base64-path}`).
**Rationale:**
- OpenCode server supports multiple projects via URL routing
- Base64 handles special characters in paths safely
- Allows future support for switching projects without server restart
### 7. Callback-Based State Subscriptions
**Decision:** Use callback array pattern for state change notifications.
**Rationale:**
- Simpler than event emitters or observables for this scale
- View subscribes to plugin state changes in `onOpen()`
- Immediate notification ensures UI stays in sync
**Implementation:**
```typescript
plugin.onProcessStateChange((state) => {
this.currentState = state;
this.updateView();
});
```
## Risks / Trade-offs
| Risk | Mitigation |
|------|------------|
| OpenCode CLI not installed | Clear error message with settings link |
| Port already in use | Health check detects existing server, reuses it |
| Server crash during use | View shows error state, user can retry |
| Slow server startup | 15-second timeout with spinner feedback |
## Open Questions
- Should we support multiple project directories (tabs/switcher)?
- Should settings changes hot-reload the iframe without full restart?
- Should we add connection status indicator in header?

View File

@@ -0,0 +1,16 @@
# Change: Document MVP Capabilities
## Why
The Obsidian OpenCode plugin MVP has been implemented but lacks formal specification. This change retroactively documents the existing functionality to establish a baseline specification that can be expanded as the MVP is polished.
## What Changes
- **ADDED** `001-mvp-opencode-embed` capability spec covering:
- Server process management (spawn, health check, shutdown, state machine)
- Sidebar view with iframe embedding and state-reactive UI
- Plugin settings with validation
- Commands and ribbon icon integration
## Impact
- Affected specs: None (new spec)
- Affected code: None (documentation only)
- This is a retroactive documentation effort with no code changes

View File

@@ -0,0 +1,139 @@
## ADDED Requirements
### Requirement: Server Process Spawning
The plugin SHALL spawn the OpenCode server process using the configured executable path, port, and hostname when the user initiates server start.
#### Scenario: Successful server spawn
- **WHEN** the user starts the server
- **THEN** the plugin spawns `opencode serve --port <port> --hostname <hostname> --cors app://obsidian.md`
- **AND** the process runs with the vault directory as the working directory
### Requirement: Server Health Checking
The plugin SHALL verify server availability by polling a health endpoint during startup.
#### Scenario: Health check during startup
- **WHEN** the server process is spawned
- **THEN** the plugin polls `GET /global/health` every 500ms
- **AND** transitions to running state when the endpoint returns HTTP 200
- **AND** transitions to error state if 15 seconds elapse without success
#### Scenario: Existing server detected
- **WHEN** the user starts the server
- **AND** a server is already running on the configured port
- **THEN** the plugin reuses the existing server
- **AND** transitions directly to running state
### Requirement: Server Shutdown
The plugin SHALL gracefully terminate the server process when stopping.
#### Scenario: Graceful shutdown
- **WHEN** the user stops the server
- **THEN** the plugin sends SIGTERM to the process
- **AND** sends SIGKILL after 2 seconds if the process is still running
### Requirement: Process State Management
The plugin SHALL maintain a state machine with states: stopped, starting, running, and error.
#### Scenario: State transitions
- **WHEN** the server is not running
- **THEN** the state is `stopped`
- **WHEN** spawn is initiated
- **THEN** the state transitions to `starting`
- **WHEN** health check succeeds
- **THEN** the state transitions to `running`
- **WHEN** spawn fails or health check times out
- **THEN** the state transitions to `error`
### Requirement: Sidebar View Registration
The plugin SHALL register an ItemView that displays in the Obsidian sidebar.
#### Scenario: View activation
- **WHEN** the user clicks the ribbon icon or runs the toggle command
- **THEN** the OpenCode view opens in the right sidebar
- **AND** if the view already exists, it is revealed
### Requirement: View State Rendering
The plugin SHALL render different UI content based on the current process state.
#### Scenario: Stopped state UI
- **WHEN** the process state is `stopped`
- **THEN** the view displays a "Start OpenCode" button
#### Scenario: Starting state UI
- **WHEN** the process state is `starting`
- **THEN** the view displays a loading spinner with "Starting OpenCode..." message
#### Scenario: Running state UI
- **WHEN** the process state is `running`
- **THEN** the view displays a header with controls and an iframe loading the server URL
#### Scenario: Error state UI
- **WHEN** the process state is `error`
- **THEN** the view displays an error message with "Retry" and "Open Settings" buttons
### Requirement: Iframe Controls
The plugin SHALL provide controls in the view header when the server is running.
#### Scenario: Header controls available
- **WHEN** the server is running
- **THEN** the header displays reload, open-in-browser, and stop buttons
- **AND** clicking reload refreshes the iframe
- **AND** clicking open-in-browser opens the server URL in system browser
- **AND** clicking stop terminates the server
### Requirement: Lazy Server Start
The plugin SHALL start the server automatically when the view is opened if not already running.
#### Scenario: Auto-start on view open
- **WHEN** the user opens the OpenCode view
- **AND** the server is in `stopped` state
- **THEN** the plugin initiates server start
### Requirement: Settings Configuration
The plugin SHALL provide configurable settings for server port, hostname, executable path, project directory, and auto-start behavior.
#### Scenario: Settings persistence
- **WHEN** the user modifies settings
- **THEN** changes are persisted to plugin data
- **AND** the process manager is updated with new settings
### Requirement: Project Directory Validation
The plugin SHALL validate the project directory setting and support tilde expansion.
#### Scenario: Valid absolute path
- **WHEN** the user enters an absolute path or path starting with ~
- **AND** the path exists and is a directory
- **THEN** the setting is saved with the expanded path
#### Scenario: Invalid path rejection
- **WHEN** the user enters a relative path or non-existent path
- **THEN** a notice is displayed explaining the error
- **AND** the setting is not saved
### Requirement: Project Directory Auto-Restart
The plugin SHALL restart the server when the project directory setting changes while running.
#### Scenario: Restart on directory change
- **WHEN** the user changes the project directory
- **AND** the server is currently running
- **THEN** the plugin stops and restarts the server with the new directory
### Requirement: Commands Registration
The plugin SHALL register commands for toggling the view and controlling the server.
#### Scenario: Toggle command
- **WHEN** the user runs "Toggle OpenCode panel" command or presses Mod+Shift+O
- **THEN** the view opens if closed, or closes if open
#### Scenario: Start and stop commands
- **WHEN** the user runs "Start OpenCode server" command
- **THEN** the server starts
- **WHEN** the user runs "Stop OpenCode server" command
- **THEN** the server stops
### Requirement: Ribbon Icon
The plugin SHALL add a ribbon icon that activates the OpenCode view.
#### Scenario: Ribbon icon click
- **WHEN** the user clicks the OpenCode ribbon icon
- **THEN** the OpenCode view is activated in the right sidebar

View File

@@ -0,0 +1,5 @@
## 1. Documentation
- [x] 1.1 Create proposal.md
- [x] 1.2 Create design.md with architectural decisions
- [x] 1.3 Create specs/001-mvp-opencode-embed/spec.md with requirements and scenarios
- [x] 1.4 Validate with `openspec validate document-mvp-capabilities --strict`