Skip to content

Commit 1523d57

Browse files
committed
opencode: drop OPENCODE_CONFIG_DIR; run fully from workdir
1 parent 3f6506f commit 1523d57

5 files changed

Lines changed: 46 additions & 53 deletions

File tree

‎AGENT.md‎

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,11 @@ This repo contains an Oh-My-Zsh plugin that turns a natural-language request int
3131
- Configurable: `agent` (default `shell_cmd_generator`), optional `variant`, `title` (default `zsh shell assistant`), log level mapping.
3232
- Disposable sessions: optional `DELETE /session/<id>` after collecting output (requires backend URL).
3333

34-
- `OPENCODE_CONFIG_DIR` is set for the opencode subprocess (default: `${plugin_dir}/opencode`).
35-
- Default agent is `shell_cmd_generator` (loaded from `OPENCODE_CONFIG_DIR/agents/shell_cmd_generator.md`).
34+
- In cold mode, the plugin ensures its bundled agents exist in `${Z_OC_TAB_OPENCODE_WORKDIR}/.opencode/agents/`.
3635

3736
### Cold Start vs Attach Mode
3837

39-
- Default (safe): do not attach; each TAB request starts opencode as needed and uses `OPENCODE_CONFIG_DIR=${plugin_dir}/opencode`, so the bundled agent works out of the box.
38+
- Default (safe): do not attach; each TAB request starts opencode as needed.
4039
- Optional (fast): attach to a running opencode server to avoid warmup overhead.
4140
- Current upstream limitation: `opencode run --attach ... --agent ...` is broken upstream, so attach mode cannot reliably select an agent until that PR lands.
4241
- Track: https://github.com/anomalyco/opencode/pull/11812

‎README.md‎

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -89,9 +89,8 @@ Still safe: it never runs anything for you.
8989
- shows the Knight Rider spinner while the worker runs
9090
- updates `BUFFER` with the generated result on success
9191
- Worker (`src/opencode_generate_command.py`):
92-
- sets `OPENCODE_CONFIG_DIR` for the opencode subprocess (default: `${plugin_dir}/opencode`)
9392
- runs `opencode run --format json` and parses NDJSON events
94-
- returns `sessionID<US>text` (US = ASCII Unit Separator, 0x1f) so the controller can split it; `sessionID` may be empty
93+
- returns `sessionID<US>repro_cmd<US>agent_reply` (US = ASCII Unit Separator, 0x1f) so the controller can split it
9594
- Spinner (`src/spinner.zsh`): rendering-only; draws via `BUFFER` + `region_highlight`.
9695
</details>
9796

@@ -303,9 +302,11 @@ The plugin reads these environment variables at load time:
303302
- `Z_OC_TAB_OPENCODE_BACKEND_URL` (default: empty)
304303
- URL of your opencode server.
305304
- Used for attach mode and for deleting sessions.
306-
- `Z_OC_TAB_OPENCODE_WORKDIR` (default: `${TMPDIR:-/tmp}/zsh-opencode-tab`)
305+
- `Z_OC_TAB_OPENCODE_WORKDIR` (default: `$XDG_DATA_HOME/zsh-opencode-tab`)
307306
- Working directory used for the `opencode` subprocess.
308-
- This defaults to a temp directory so sessions are created in the global workspace (not inside whatever git repo you happen to be in).
307+
- If `XDG_DATA_HOME` is empty, it falls back to `${TMPDIR:-/tmp}/zsh-opencode-tab`.
308+
- The plugin keeps sessions in the global workspace (not inside whatever git repo you happen to be in).
309+
- The plugin writes its two bundled agents into `${Z_OC_TAB_OPENCODE_WORKDIR}/.opencode/agents/`.
309310
- `Z_OC_TAB_OPENCODE_RUN_MODE` (default: `cold`)
310311
- `cold`: run `opencode run` locally (no server attach).
311312
- `attach`: run `opencode run --attach <backend_url>`.
@@ -335,10 +336,6 @@ The plugin reads these environment variables at load time:
335336
- If set to `1`, deletes the created session via the server API after receiving the answer.
336337
- Requires `Z_OC_TAB_OPENCODE_BACKEND_URL` to be set (otherwise you'll see a warning and sessions will be kept).
337338

338-
- `Z_OC_TAB_OPENCODE_CONFIG_DIR` (default: `${plugin_dir}/opencode`)
339-
- Where this plugin looks for its agent/prompt files when it runs `opencode` in cold-start mode.
340-
- This setting is ignored in warm-start mode, when you attach to a running opencode server.
341-
- If on your system you already configured opencode official environment variable `OPENCODE_CONFIG_DIR`, a sensible configuration is `export Z_OC_TAB_OPENCODE_CONFIG_DIR=OPENCODE_CONFIG_DIR` to rely this variable. The name is kept separate so that you more flexibility in principle to choose different directories for each of them; though likely you will not want/need to.
342339
- `Z_OC_TAB_OPENCODE_GNU` (`0` or `1`; default: `1`)
343340
- Passed to the agent whether to prefer GNU tools over macOS/freeBSD.
344341

@@ -354,15 +351,15 @@ This plugin bundles two agent definitions (prompt files) for opencode:
354351
- Default generator agent: `shell_cmd_generator` (definition: `opencode/agents/shell_cmd_generator.md`).
355352
- Default explainer agent: `shell_cmd_explainer` (definition: `opencode/agents/shell_cmd_explainer.md`).
356353
- Custom agents: set `Z_OC_TAB_OPENCODE_AGENT_GENERATOR` and/or `Z_OC_TAB_OPENCODE_AGENT_EXPLAINER`.
357-
- Custom prompts (cold start): point `Z_OC_TAB_OPENCODE_CONFIG_DIR` at your own opencode config directory and provide `agents/<agent>.md` with your preferred instruction set.
354+
- Custom prompts: copy the agent file under `${Z_OC_TAB_OPENCODE_WORKDIR}/.opencode/agents/` to a new filename and point the plugin at it using `Z_OC_TAB_OPENCODE_AGENT_GENERATOR` and `Z_OC_TAB_OPENCODE_AGENT_EXPLAINER`. Note that the plugin overwrites its own bundled filenames `shell_cmd_generator.md` and `shell_cmd_explainer.md` to keep upgrades deterministic; thus, avoid modifying the bundled agents.
358355

359356
Tip: when you are iterating on the agent prompt, use cold start (`Z_OC_TAB_OPENCODE_RUN_MODE=cold`). It's the least confusing setup: you edit a file, reload your shell, and the next TAB uses it.
360357

361358
## Cold Start vs Attach Mode
362359

363360
- Cold start (default): simplest and most reliable.
364361
- You do not run/attach to any server.
365-
- Each TAB request starts `opencode` with this plugin's bundled config (`OPENCODE_CONFIG_DIR=${plugin_dir}/opencode`).
362+
- Each TAB request runs `opencode` from `Z_OC_TAB_OPENCODE_WORKDIR`, which keeps sessions in the global workspace.
366363

367364
- Attach mode (optional): faster if you already run an opencode backend server.
368365
- Important mental model: the server decides which agents exist. This plugin cannot "upload" an agent to a running server.

‎src/controller.zsh‎

Lines changed: 24 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,14 @@ _zsh_opencode_tab.run_with_spinner() {
163163
{
164164
local script="${_zsh_opencode_tab[dir]}/src/opencode_generate_command.py"
165165
local run_mode=${_zsh_opencode_tab[opencode.run_mode]}
166+
local workdir=${_zsh_opencode_tab[opencode.workdir]}
167+
168+
# Ensure the bundled agents exist in the workdir.
169+
# We overwrite them to make plugin upgrades deterministic.
170+
local agents_dst="${workdir}/.opencode/agents"
171+
command mkdir -p -- "$agents_dst"
172+
command cp -f -- "${_zsh_opencode_tab[dir]}/opencode/agents/shell_cmd_generator.md" "$agents_dst/shell_cmd_generator.md"
173+
command cp -f -- "${_zsh_opencode_tab[dir]}/opencode/agents/shell_cmd_explainer.md" "$agents_dst/shell_cmd_explainer.md"
166174

167175
# Select agent/model based on request kind.
168176
# - command/persist: generator
@@ -183,17 +191,13 @@ _zsh_opencode_tab.run_with_spinner() {
183191
--gnu "${_zsh_opencode_tab[opencode.gnu]}" \
184192
--kind "$kind" \
185193
--echo-prompt "$echo_prompt" \
186-
--workdir "${_zsh_opencode_tab[opencode.workdir]}" \
194+
--workdir "$workdir" \
187195
--backend-url "${_zsh_opencode_tab[opencode.backend_url]}" \
188196
--run-mode "$run_mode" \
189197
--title "${_zsh_opencode_tab[opencode.title]}" \
190198
--agent "$agent_to_use"
191199
)
192200

193-
# Only cold-start uses OPENCODE_CONFIG_DIR; in attach mode the server owns
194-
# config/agents.
195-
[[ "$run_mode" == "cold" ]] && cmd+=(--config-dir "${_zsh_opencode_tab[opencode.config_dir]}")
196-
197201
[[ -n $model_to_use ]] && cmd+=(--model "$model_to_use")
198202
[[ -n ${_zsh_opencode_tab[opencode.variant]} ]] && cmd+=(--variant "${_zsh_opencode_tab[opencode.variant]}")
199203
[[ -n ${_zsh_opencode_tab[opencode.log_level]} ]] && cmd+=(--log-level "${_zsh_opencode_tab[opencode.log_level]}")
@@ -341,20 +345,21 @@ _zsh_opencode_tab.run_with_spinner() {
341345
repro_cmd=${rest%%"$US"*}
342346
agent_reply=${rest#*"$US"}
343347

344-
if (( ${_zsh_opencode_tab[debug]} )); then
345-
local dbg_file=${_zsh_opencode_tab[debug_log]}
346-
{
347-
print -r -- "----- zsh-opencode-tab worker -----"
348-
print -r -- "timestamp=$(date)"
349-
print -r -- "kind=$kind"
350-
print -r -- "session_id=$session_id"
351-
print -r -- "repro_cmd:"
352-
print -r -- "$repro_cmd"
353-
print -r -- "agent_reply:"
354-
print -r -- "$agent_reply"
355-
print -r -- "-----"
356-
} >>| "$dbg_file" 2>/dev/null
357-
fi
348+
if (( ${_zsh_opencode_tab[debug]} )); then
349+
local dbg_file=${_zsh_opencode_tab[debug_log]}
350+
{
351+
print -r -- "----- zsh-opencode-tab worker -----"
352+
print -r -- "timestamp=$(date)"
353+
print -r -- "kind=$kind"
354+
print -r -- "workdir=${_zsh_opencode_tab[opencode.workdir]}"
355+
print -r -- "session_id=$session_id"
356+
print -r -- "repro_cmd:"
357+
print -r -- "$repro_cmd"
358+
print -r -- "agent_reply:"
359+
print -r -- "$agent_reply"
360+
print -r -- "-----"
361+
} >>| "$dbg_file" 2>/dev/null
362+
fi
358363

359364
# Empty output means we have nothing meaningful to insert.
360365
if [[ -z ${agent_reply//[[:space:]]/} ]]; then

‎src/opencode_generate_command.py‎

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -45,17 +45,13 @@ def _zsh_dollar_quote(s: str) -> str:
4545
return "$'" + "".join(out) + "'"
4646

4747

48-
def _build_repro_cmd(cmd: list[str], env: dict[str, str], cwd: str) -> str:
48+
def _build_repro_cmd(cmd: list[str], cwd: str) -> str:
4949
"""Build a copy/pasteable single-line command for debugging."""
5050

5151
prefix = ""
5252
if cwd:
5353
prefix += f"cd {sh_quote(cwd)} && "
5454

55-
cfg = env.get("OPENCODE_CONFIG_DIR", "")
56-
if cfg:
57-
prefix += f"OPENCODE_CONFIG_DIR={sh_quote(cfg)} "
58-
5955
parts: list[str] = []
6056
for i, a in enumerate(cmd):
6157
# The final arg is the prompt payload (multi-line). Use $'..' quoting.
@@ -105,8 +101,7 @@ def _parse_json_events(text: str) -> tuple[str, str]:
105101

106102

107103
def _render_prompt(user_request: str, ostype: str, gnu: str, echo_prompt: str) -> str:
108-
# Keep this intentionally small and explicit. The agent definition lives in
109-
# OPENCODE_CONFIG_DIR/agents/<agent>.md, loaded by opencode.
104+
# Keep this intentionally small and explicit.
110105
echo_prompt = (echo_prompt or "").strip() or "0"
111106
return (
112107
"<user>\n"
@@ -145,7 +140,6 @@ def main() -> int:
145140
ap.add_argument("--gnu", default="1")
146141
ap.add_argument("--kind", default="command")
147142
ap.add_argument("--echo-prompt", default="0")
148-
ap.add_argument("--config-dir", default="")
149143
ap.add_argument("--workdir", default="")
150144
ap.add_argument("--model", default="")
151145
ap.add_argument("--backend-url", default="")
@@ -206,12 +200,10 @@ def main() -> int:
206200
cmd.append(prompt)
207201

208202
env = dict(os.environ)
209-
if args.config_dir and run_mode != "attach":
210-
env["OPENCODE_CONFIG_DIR"] = args.config_dir
211203

212-
# Debug-only: return the exact opencode invocation (including OPENCODE_CONFIG_DIR
213-
# if set) so users can copy/paste and reproduce what the plugin ran.
214-
repro_cmd = _build_repro_cmd(cmd, env, workdir)
204+
# Debug-only: return the exact opencode invocation so users can copy/paste and
205+
# reproduce what the plugin ran.
206+
repro_cmd = _build_repro_cmd(cmd, workdir)
215207

216208
if args.debug_dummy:
217209
text = (args.debug_dummy_text or "").strip()

‎zsh-opencode-tab.plugin.zsh‎

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,12 @@ _zsh_opencode_tab[persist.default]=${Z_OC_TAB_PERSIST_DEFAULT:-1}
113113
# Working directory for the `opencode` subprocess.
114114
# We default to a temp directory so opencode sessions are created in the global
115115
# workspace (not project-scoped when the user happens to be inside a git repo).
116-
local default_workdir="${TMPDIR:-/tmp}/zsh-opencode-tab"
116+
local default_workdir
117+
if [[ -n ${XDG_DATA_HOME:-} ]]; then
118+
default_workdir="${XDG_DATA_HOME}/zsh-opencode-tab"
119+
else
120+
default_workdir="${TMPDIR:-/tmp}/zsh-opencode-tab"
121+
fi
117122
_zsh_opencode_tab[opencode.workdir]="${Z_OC_TAB_OPENCODE_WORKDIR:-$default_workdir}"
118123

119124
# How to run opencode:
@@ -134,17 +139,12 @@ _zsh_opencode_tab[persist.default]=${Z_OC_TAB_PERSIST_DEFAULT:-1}
134139
[[ -n ${Z_OC_TAB_OPENCODE_MODEL_EXPLAINER:-''} ]] && _zsh_opencode_tab[opencode.model.explainer]=${Z_OC_TAB_OPENCODE_MODEL_EXPLAINER}
135140

136141
# Agent selection: generator and explainer are distinct agents.
137-
# Agent name is resolved by opencode from OPENCODE_CONFIG_DIR/agents.
142+
# In cold mode, this plugin ensures its bundled agents exist in:
143+
# ${Z_OC_TAB_OPENCODE_WORKDIR:-$default_workdir}/.opencode/agents/
138144
_zsh_opencode_tab[opencode.agent.generator]=${Z_OC_TAB_OPENCODE_AGENT_GENERATOR:-'shell_cmd_generator'}
139145
_zsh_opencode_tab[opencode.agent.explainer]=${Z_OC_TAB_OPENCODE_AGENT_EXPLAINER:-'shell_cmd_explainer'}
140146
_zsh_opencode_tab[opencode.variant]=${Z_OC_TAB_OPENCODE_VARIANT:-''}
141147

142-
# Configure opencode's config directory (contains agents/, etc).
143-
# Intentionally named differently than OPENCODE_CONFIG_DIR so users must opt-in
144-
# explicitly if they want to point this plugin at their own config.
145-
local default_config_dir="${_zsh_opencode_tab[dir]}/opencode"
146-
_zsh_opencode_tab[opencode.config_dir]="${Z_OC_TAB_OPENCODE_CONFIG_DIR:-$default_config_dir}"
147-
148148
# "GNU" is passed as opaque config to the agent (no validation/clamping here).
149149
# Valid values are conventionally 0/1, but we trust user configuration.
150150
_zsh_opencode_tab[opencode.gnu]=${Z_OC_TAB_OPENCODE_GNU:-1}

0 commit comments

Comments
 (0)