Status: Accepted
Date: 2026-03-05
Decision Owner: Steffen
Context: PAI-OpenCode v3.0 Migration
When porting PAI from Claude Code to OpenCode, we discovered a fundamental architectural difference in how the Bash tool handles working directories.
In Claude Code, the cd command persists across bash calls within a session. The shell process maintains state.
In OpenCode, each bash() call spawns a NEW shell process with Instance.directory as the default working directory. The cd command has NO persistent effect across tool invocations.
// WRONG — cd has no effect on next command
bash({ command: "cd /path/to/repo" })
bash({ command: "git status" }) // Runs in Instance.directory, NOT /path/to/repo!OpenCode's Bash tool implementation:
const cwd = params.workdir || Instance.directoryThis means Instance.directory is the default for EVERY command. The cd command changes the shell's working directory, but that state is lost when the tool returns.
Use the workdir parameter for all commands that must run in a different directory.
// CORRECT — explicit workdir
bash({
command: "git status",
workdir: "/path/to/repo"
})| Situation | Wrong Approach | Correct Approach |
|---|---|---|
| Git ops in another repo | cd /repo && git status |
bash({ command: "git status", workdir: "/repo" }) |
| File ops in subdirectory | cd subdir && ls |
bash({ command: "ls", workdir: "/path/subdir" }) |
| Build in different project | cd project && bun build |
bash({ command: "bun build", workdir: "/project" }) |
| npm install in package | cd package && npm i |
bash({ command: "npm i", workdir: "/package" }) |
When the PAI Algorithm navigates to work in a different repository:
- OBSERVE: Note the target directory
- BUILD/EXECUTE: Use
workdirparameter for all operations in that directory - VERIFY: Confirm operations executed in correct location
User: "Fix the bug in pai-opencode repo"
OBSERVE:
- Target: /Users/steffen/workspace/github.com/Steffen025/pai-opencode
- Instance.directory: /Users/steffen/workspace/github.com/Steffen025/jeremy-opencode
BUILD:
- bash({ command: "git status", workdir: "/Users/.../pai-opencode" }) ✓
- NOT: bash({ command: "cd /Users/.../pai-opencode && git status" }) ✗
- Explicit and clear: The target directory is visible in every call
- No hidden state: Each command is independent and predictable
- Safer: No risk of commands running in wrong directory
- Better for multi-repo workflows: Clear separation of contexts
- More verbose: Must specify
workdirfor every command - Breaking change: Code that relied on
cdpersistence will fail - Learning curve: Users familiar with Claude Code must adapt
- Documentation: This ADR and the Algorithm documentation explain the pattern
- Plugin validation: WP3 can add workdir validation to catch missing parameters
- Code review: Check for
cdusage in bash calls during review
- ADR-008 created
- Algorithm documentation updated (local PAI)
- Learning documents created
- Add workdir section to Algorithm v3.7.0.md
- Create PLATFORM-DIFFERENCES.md in PAI-OpenCode
- Update README.md for v3.0
- Add workdir validation to plugin
- Detect
cdusage in bash calls - Warn when workdir missing for external paths
- OpenCode Source:
packages/opencode/src/tool/bash.ts - Instance.directory:
packages/opencode/src/project/instance.ts - Local Learning:
~/.opencode/MEMORY/LEARNING/2026-03-05_OpenCode-Bash-workdir-Parameter-Problem.md - Integration Points:
~/.opencode/MEMORY/LEARNING/2026-03-05_OpenCode-Bash-workdir-Parameter-Integration-Points.md
This is a critical platform difference that affects every multi-repository workflow. The PAI Algorithm must be updated to use workdir consistently when working outside Instance.directory.
The Rule: When working OUTSIDE Instance.directory:
- NEVER use
cdexpecting it to persist - ALWAYS use
workdirparameter for the target directory - Each bash call is INDEPENDENT — no state carries over