Create spec for better process management
This commit is contained in:
164
openspec/changes/improved-opencode-process-management/design.md
Normal file
164
openspec/changes/improved-opencode-process-management/design.md
Normal file
@@ -0,0 +1,164 @@
|
||||
## 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.
|
||||
Reference in New Issue
Block a user