Files
opencode-obsidian/openspec/changes/improved-opencode-process-management/design.md
2026-02-14 16:54:02 +01:00

165 lines
6.1 KiB
Markdown

## Context
### Current Implementation
The plugin uses a simple `opencodePath: string` setting that defaults to "opencode". The `ServerManager` spawns this directly with default arguments:
```typescript
this.process = this.processImpl.start(
this.settings.opencodePath,
["serve", "--port", this.settings.port.toString(), ...],
options
);
```
Platform-specific process implementations (`PosixProcess`, `WindowsProcess`) handle spawning and verification. The `verifyCommand` method checks if the executable exists.
### New Requirements
1. One-time autodetect for new users (empty path on first run)
2. Support custom shell commands with full user control
3. Maintain backward compatibility with existing path-based configuration
## Goals / Non-Goals
**Goals:**
- Provide zero-configuration setup for new users via autodetect
- Enable power users to use custom commands with full flexibility
- Maintain backward compatibility for existing users
- Clear UI distinction between path mode and custom command mode
- Platform-aware executable detection (PATH + common locations)
**Non-Goals:**
- Complex command builder UI (simple text input for custom commands)
- Automatic installation of opencode
- Validation of custom commands before execution
## Decisions
### 1. Settings Schema Extension
**Decision:** Extend existing settings rather than replace.
**Rationale:**
- Maintains backward compatibility - existing configs continue working
- Simple migration - just add new fields with sensible defaults
- Minimal code changes to existing path-based logic
**Alternatives considered:**
- Replace with structured command object - rejected due to breaking change
- Separate settings sections - rejected as overkill for this feature
**Implementation:**
```typescript
interface OpenCodeSettings {
// ... existing fields ...
opencodePath: string; // Still used as primary path
customCommand: string; // New: shell command
useCustomCommand: boolean; // New: toggle mode
}
```
### 2. Autodetect Trigger Strategy
**Decision:** Autodetect runs on every plugin startup when path is empty.
**Rationale:**
- Reminds user to configure or disable the plugin if opencode is missing
- Simpler implementation - no state tracking needed
- If user installs opencode later, it will be detected automatically
**Alternatives considered:**
- Run once and remember with flag - rejected as hides the problem
- Run only manually - rejected as adds friction for new users
- Explicit "first setup" wizard - rejected as overkill
**Implementation:**
```typescript
// In main.ts onload()
if (!this.settings.opencodePath && !this.settings.useCustomCommand) {
await this.attemptAutodetect();
}
```
### 3. Custom Command Spawning Strategy
**Decision:** Use `shell: true` for custom commands, user controls ALL arguments.
**Rationale:**
- Maximum flexibility - env vars, pipes, complex invocations all work
- Simple mental model - "what you type is what runs"
- No ambiguity about argument concatenation
**Alternatives considered:**
- Parse and split custom command - rejected as fragile
- Merge plugin args with custom args - rejected as confusing
- Separate args array - rejected as limiting
**Implementation:**
```typescript
// Path mode
this.process = spawn(opencodePath, ["serve", "--port", port, ...], options);
// Custom mode
this.process = spawn(customCommand, [], { ...options, shell: true });
```
### 4. Executable Detection Order
**Decision:** Check PATH first, then platform-specific common locations.
**Rationale:**
- Respects user's environment setup
- Common locations cover most package manager installs (homebrew, cargo, npm -g, etc.)
**Search algorithm:**
1. If configured path is absolute and exists, return it directly
2. Extract basename from configured path (e.g., "opencode" from "/path/to/opencode" or just "opencode")
3. Search platform-specific locations for that basename:
- **Linux:** `~/.local/bin/`, `~/.opencode/bin/`, `~/.bun/bin/`, `~/.npm-global/bin/`, `~/.nvm/versions/node/*/bin/`, `/usr/local/bin/`, `/usr/bin/`
- **macOS:** `~/.local/bin/`, `/opt/homebrew/bin/`, `/usr/local/bin/`
- **Windows:** `%LOCALAPPDATA%\opencode\bin\`, `%USERPROFILE%\.bun\bin\`, `%USERPROFILE%\.local\bin\`
4. If found, return full path; if not found, return configured path as fallback
**nvm wildcard handling:** For `~/.nvm/versions/node/*/bin/`, expand the wildcard to find actual Node version directories.
### 5. UI Layout
**Decision:** Toggle switch to select mode, conditional display of relevant input.
**Rationale:**
- Clear mental model - one or the other, not both
- Reduces visual clutter
- Toggle state directly maps to `useCustomCommand` boolean
**Layout:**
```
Command Mode:
[ Use custom command ●─────○ ]
[Path Mode - shown when toggle off]
OpenCode path: [____________] [Autodetect]
[Custom Mode - shown when toggle on]
Custom command:
[______________________________]
(Full shell command with all arguments)
```
## Risks / Trade-offs
**[Risk]** Custom command mode is powerful but dangerous - users can break their setup.
**Mitigation:** This is intentional flexibility. Users opting into "custom command" are advanced users. No validation performed - natural failure on spawn.
**[Risk]** Autodetect could find wrong executable (different binary with same name).
**Mitigation:** Low probability - "opencode" is unique. Could add version check in future if needed.
**[Risk]** Users may not understand difference between path and custom command modes.
**Mitigation:** Clear UI labels and descriptions. Path mode is default, custom mode opt-in.
**[Risk]** Toast notification on every startup might be annoying if user intentionally leaves path empty.
**Mitigation:** User can switch to custom command mode (even with empty command) to suppress autodetect, or configure a path.
## Migration Plan
1. **No breaking changes** - existing configs with `opencodePath` set continue working
2. **New fields** default to empty/false
3. **Autodetect** triggers on every startup when path is empty and custom command mode is disabled
4. **Settings UI** adapts to existing data - if `opencodePath` is set, starts in path mode
## Open Questions
None - design is complete based on user requirements.