---
name: workspace-cli-bridge
version: "1.0.0"
updated: 2026-06-02
description: Use the company WorkSpace `ws` CLI reliably as a delegated coding agent from Codex. Trigger when the user wants Codex to command `ws`, WorkSpace CLI, or the company opencode-derived coding tool to generate code, inspect a repo, run a bounded implementation task, or use a requested WorkSpace model while Codex reviews the output.
---

# Workspace CLI Bridge

## Overview

Use `ws` as a secondary implementation agent while Codex remains the reviewer and integrator. The reliable path is to attach to a local WorkSpace server, not to cold-start `ws run`.

On this machine, prefer a terminal-managed `ws serve` for Codex delegation when it has the company `workspace` provider loaded. If a terminal-managed server has an empty provider list, quickly inspect the App-managed server before declaring `ws` unusable. The App-managed server can accept no-tool prompts and may have the live company provider even when terminal-managed `ws serve` does not; however, it can still hang on bash/file writes, so treat it as write-capable only after a tiny bash/write preflight.

The local `node` shim only controls whether the `ws` command starts. Repointing `$HOME/.local/bin/node` to the Workspace App bundle node fixes a silent CLI startup failure, but it does not make an App-managed server bash-safe and it does not by itself create the old bash-proxy failure. Server choice is the stability boundary.

## Known Good Path

First discover the currently listening local WorkSpace server port. Do not hard-code the observed port because WorkSpace may start `ws serve` with `--port=0`, which can change after a restart.

Prefer terminal-managed servers over App-managed servers when the terminal-managed server exposes the requested provider/model. App-managed servers usually include `--cors vscode-file://vscode-app` in their command line. If the terminal-managed server provider list is empty but the App-managed server exposes `workspace` models, use the App-managed server for quick no-tool validation and only proceed to shell/edit work after the preflight checks in `Fast Provider Triage`.

```bash
discover_workspace_attach_url() {
  for pid in $(pgrep -f '[w]s serve'); do
    cmd="$(ps -p "$pid" -o command= 2>/dev/null)"
    case "$cmd" in
      *"--cors vscode-file://vscode-app"*) continue ;;
    esac

    url="$(
      lsof -Pan -p "$pid" -iTCP -sTCP:LISTEN 2>/dev/null |
        awk '$9 ~ /^127\.0\.0\.1:[0-9]+$/ { print "http://" $9; exit }'
    )"
    if [ -n "$url" ]; then
      printf '%s\n' "$url"
      return 0
    fi
  done

  return 1
}

WORKSPACE_ATTACH_URL="$(discover_workspace_attach_url)"
if [ -z "$WORKSPACE_ATTACH_URL" ]; then
  echo "No terminal-managed WorkSpace ws server is listening on 127.0.0.1. Start one with: ws serve --hostname=127.0.0.1 --port=0" >&2
  exit 1
fi
WORKSPACE_MODEL="${WORKSPACE_MODEL:-workspace/gpt-5.4}"
```

Then attach to the discovered URL:

```bash
ws run \
  --attach "$WORKSPACE_ATTACH_URL" \
  --dir "<target-repo-or-workdir>" \
  --agent build \
  --model "$WORKSPACE_MODEL" \
  --format json \
  --title "<short-task-title>" \
  "<bounded task prompt>"
```

Default to `workspace/gpt-5.4` when that model is listed by the attached server. If the user explicitly asks for another model, set `WORKSPACE_MODEL` to that exact `provider/model` ID for the whole delegation batch, for example:

```bash
WORKSPACE_MODEL="workspace/claude-sonnet-4.6"
```

Run the no-tool provider preflight with the requested model before real work. If the requested model is unavailable, report the exact model error and do not silently fall back to `workspace/gpt-5.4` unless the user asks you to.

Use `--agent build` for code edits. Use `--agent plan` for planning-only work. Do not use `general` or `explore` for `ws run`; they are subagents in this CLI and may fall back to the default agent.

Always pass `--dir`. Do not let `ws` default to `$HOME` or the current chat workspace when the task targets a specific repo.

Prefer a directory already covered by the attached WorkSpace server. Passing an unrelated external directory such as `/tmp/...` can hang before JSON events because the server may wait on external-directory permission or project switching. If a no-op connectivity check is needed, use the active workspace directory rather than an arbitrary temp directory.

Creating or editing files outside the active workspace, for example under `$HOME/Desktop`, can also hang after the model starts because the file write needs external-directory permission. Treat this as a permission-boundary test, not as proof that the selected `$WORKSPACE_MODEL` is unavailable.

## Fast Provider Triage

Use this quick path when `ws run` reports `Model not found`, `Provider not found: workspace`, `no providers found`, or exits with no JSON events:

```bash
discover_app_workspace_attach_url() {
  for pid in $(pgrep -f '[w]s serve'); do
    cmd="$(ps -p "$pid" -o command= 2>/dev/null)"
    case "$cmd" in
      *"--cors vscode-file://vscode-app"*) ;;
      *) continue ;;
    esac

    url="$(
      lsof -Pan -p "$pid" -iTCP -sTCP:LISTEN 2>/dev/null |
        awk '$9 ~ /^127\.0\.0\.1:[0-9]+$/ { print "http://" $9; exit }'
    )"
    if [ -n "$url" ]; then
      printf '%s\n' "$url"
      return 0
    fi
  done

  return 1
}

inspect_workspace_models() {
  local url="$1"
  curl -s "$url/config/providers" |
    jq -r '.providers[]? | select(.id=="workspace") | .models | to_entries[] | [.key, .value.name, .value.status] | @tsv'
}
```

Expected App-managed result on this machine can include:

```text
gpt-5.4        GPT-5.4        active
gpt-5.2-codex  GPT-5.2-Codex  active
claude-sonnet-4.6  Claude-4.6-Sonnet  active
qwen3.6-plus   Qwen3.6-Plus   active
```

Important distinction:

- UI labels such as `GPT_5.4` / `GPT-5.4` are display names.
- The `ws run --model` value must use the provider/id form, for example `workspace/gpt-5.4`.
- A terminal-managed server can still return `ProviderModelNotFoundError` for `workspace/gpt-5.4` if its `/provider` output is empty; in that case the model may still work through the App-managed server.

Fast validation sequence:

```bash
APP_ATTACH_URL="$(discover_app_workspace_attach_url)"
WORKSPACE_MODEL="${WORKSPACE_MODEL:-workspace/gpt-5.4}"

curl -s "$APP_ATTACH_URL/provider" | jq '{providers: [.all[]?.id], defaults: .default}'
inspect_workspace_models "$APP_ATTACH_URL" | sed -n '1,20p'

ws run \
  --attach "$APP_ATTACH_URL" \
  --dir "<target-dir>" \
  --agent build \
  --model "$WORKSPACE_MODEL" \
  --format json \
  --title app-provider-no-tool-check \
  "Do not read or modify files and do not run commands. Reply only: OK."
```

Treat a JSON `text` event containing `OK` as proof that the App-managed server can call the selected model. This validates model connectivity only; it does not prove bash or file writes are safe.

For tasks that need edits through an App-managed server, run a minimal write preflight inside the target repo before delegating real work:

```bash
ws run \
  --attach "$APP_ATTACH_URL" \
  --dir "<target-dir>" \
  --agent build \
  --model "$WORKSPACE_MODEL" \
  --format json \
  --title app-write-check \
  "Use bash exactly once to create, read, and remove .ws_write_check.tmp in the current directory. Do not touch any other file. Reply with the command output and exit code."
```

If the App-managed preflight hangs after bash/tool approval, stop only that `ws run` process and do not use the App-managed server for edits. If it completes, proceed with a bounded prompt, keep stdout small, and ask `ws` to write files directly rather than streaming large logs.

## Preflight

Check that the local server is listening and capture its attach URL:

```bash
WORKSPACE_ATTACH_URL="$(discover_workspace_attach_url)"
WORKSPACE_MODEL="${WORKSPACE_MODEL:-workspace/gpt-5.4}"
printf '%s\n' "$WORKSPACE_ATTACH_URL"
printf '%s\n' "$WORKSPACE_MODEL"
```

Expected server shape:

```text
http://127.0.0.1:<port>
workspace/gpt-5.4
```

## WorkSpace Skill Discovery

WorkSpace `ws` does not automatically read Codex's skill registry from `~/.codex/skills`, and it does not use `~/.config/opencode/opencode.json` as its active config in this local build.

Observed `ws skill list --print-logs` loads config from:

```text
$HOME/.local/share/workspace-code-prd/workspace-cli/config.json
$HOME/.local/share/workspace-code-prd/workspace-cli/opencode.json
$HOME/.local/share/workspace-code-prd/workspace-cli/opencode.jsonc
```

If those files do not define `skills.paths`, `ws skill list` only shows the built-in skills. To expose Codex local skills to `ws`, keep this file:

```json
{
  "skills": {
    "paths": [
      "$HOME/.codex/skills"
    ]
  }
}
```

at:

```text
$HOME/.local/share/workspace-code-prd/workspace-cli/opencode.json
```

This was verified locally: before the file existed, `ws skill list` showed 5 built-ins; after adding `$HOME/.codex/skills`, it showed 25 skills including `midea-recall-diagnose` and `workspace-cli-bridge`.

The TUI `/skills` command palette is not authoritative for full discovery. It shows a filtered, scrollable suggestion list based on the current input. If a known local skill such as `midea-recall-diagnose` is not visible while typing `/skills`, verify with `ws skill list` or type a narrower prefix such as `/midea`.

Project-local skills also work without global config when placed under:

```text
<project>/.agents/skills/<skill-name>/SKILL.md
```

Use project-local skills for repo-specific instructions. Use the global WorkSpace config above only when the goal is to let `ws` see the same local skill pool Codex uses.

## Server Lifecycle

Do not start and stop `ws serve` for every single `ws run`. Treat terminal-managed `ws serve` as a short-lived local backend for a task batch:

1. At the start of a `ws` delegation batch, discover an existing terminal-managed server and reuse it.
2. If none exists and the user wants Codex to drive `ws` now, start one terminal-managed `ws serve`.
3. After starting or discovering a server, run the no-tool provider preflight below before any real work.
4. For shell/edit/test tasks, run the bash preflight below before any real work.
5. Within the same task, keep using the same attach URL and prefer `--session <session-id>` for follow-up turns.
6. When the task batch is done and no near-term `ws` work is expected, close the terminal-managed server started for that batch.

This keeps context available across follow-ups without making every interaction pay startup cost. Do not close the App-managed server; leave it for the GUI.

If nothing is listening, start a terminal-managed server only when the user wants Codex to drive `ws` now:

```bash
ws serve --hostname=127.0.0.1 --port=0
```

Keep the server running, capture the printed `http://127.0.0.1:<port>` URL, and attach to that URL. The server prints a warning if `OPENCODE_SERVER_PASSWORD` is unset; this is acceptable for local-only `127.0.0.1` testing but should not be exposed beyond localhost.

If a freshly started terminal-managed server returns `ProviderModelNotFoundError` for `$WORKSPACE_MODEL`, inspect its `/provider` output before retrying. If `/provider` is empty and logs contain `unknown certificate verification error Failed to fetch user config`, the terminal server did not load the company provider; do not waste time retrying the same terminal command. Use `Fast Provider Triage` to check the App-managed server, and only use it for edits after the write preflight.

For a low-cost live check:

```bash
ws run \
  --attach "$WORKSPACE_ATTACH_URL" \
  --dir "<target-dir>" \
  --agent build \
  --model "$WORKSPACE_MODEL" \
  --format json \
  --title connectivity-check \
  "Do not read or modify files and do not run commands. Reply only: OK."
```

Treat a JSON `text` event containing `OK` as proof that the selected `$WORKSPACE_MODEL` path is usable.

For any task that may use shell tools, also run a bash preflight:

```bash
ws run \
  --attach "$WORKSPACE_ATTACH_URL" \
  --dir "<target-dir>" \
  --agent build \
  --model "$WORKSPACE_MODEL" \
  --format json \
  --title bash-check \
  "Use the bash tool exactly once to run: git status --short. Do not modify files. Reply with stdout and exit code."
```

The expected result is a completed `tool_use` for `bash` with exit code `0`. If logs show `permission=bash ... action=allow` followed by `bash.proxy.execute.requested` but no completion, stop only that `ws run` and switch to a terminal-managed server.

## Avoid These Paths

Do not rely on cold-start model discovery:

```bash
ws models workspace
ws run --model "$WORKSPACE_MODEL" ...
```

Known terminal-managed failure modes:

```text
Provider not found: workspace
Model not found: workspace/gpt-5.4
ProviderModelNotFoundError
no providers found
unknown certificate verification error Failed to fetch user config
```

This can happen because `ws run` checks the requested model before the company `workspace` provider is injected, or because terminal-managed `ws serve` failed to fetch user config and therefore exposes an empty provider list. Retrying the same command without `--attach` is not a reliable fix. Compare terminal `/provider` with the App-managed `/config/providers` before concluding the model is unavailable.

Never pass `--dangerously-skip-permissions`.

Avoid `ws debug agent ...` for routine checks. It can initialize checkpoints and update local Git hooks.

## Local Shim Repair

If `ws --version` or `ws --env=prd --version` exits immediately with no output, or an interactive shell prints `zsh: killed     ws`, verify the shim node:

```bash
readlink "$HOME/.local/bin/node"
$HOME/.local/share/workspace-code-prd/bin/node/darwin-arm64/node --version
/Applications/Workspace\ CLI-PRD.app/Contents/Resources/app/extensions/midea-workspace.workspace-code/bin/node/darwin-arm64/node --version
```

If the cache node fails but the App bundle node works, use the durable repair: keep a backup of the bad cache node, replace the cache node with a symlink to the App bundle node, then allow the normal `~/.local/bin/node -> cache node` shim to remain in place.

```bash
ts="$(date +%Y%m%d%H%M%S)"
cache_node="$HOME/.local/share/workspace-code-prd/bin/node/darwin-arm64/node"
app_node="/Applications/Workspace CLI-PRD.app/Contents/Resources/app/extensions/midea-workspace.workspace-code/bin/node/darwin-arm64/node"
mv "$cache_node" "${cache_node}.bad.${ts}"
ln -s "$app_node" "$cache_node"
ln -sfn "$cache_node" "$HOME/.local/bin/node"
ws --version
```

This only repairs the local `ws` wrapper runtime; it does not change project files.

The shim can be regenerated by WorkSpace App restart and point back to the cache node. That is expected after the durable repair: `~/.local/bin/node` may point to the cache node, while the cache node itself points to the App bundle node.

## Prompt Contract

Make every prompt bounded and reviewable:

```text
Goal:
- <one concrete outcome>

Scope:
- Work only in <paths/modules>.
- Do not touch <forbidden files/areas>.

Implementation constraints:
- Follow existing project style.
- Keep the patch minimal.
- Do not commit, push, reset, or delete files.
- Do not read secrets or .env files unless explicitly required.

Verification:
- Run <safe test/lint command> if appropriate.
- If the command is not safe to run, say why and leave it for Codex/user.

Output:
- Summarize changed files.
- Report commands run and failures.
```

For Java services, keep the existing user convention: do not start `mvn` or launch the service unless the user explicitly asks. Provide the command or ask the user to start it.

## Review Loop

After `ws` returns, Codex must review the result locally before accepting it:

```bash
git status --short
git diff --stat
git diff
```

Run the narrowest relevant verification command when safe. If verification is blocked by environment, service startup, credentials, or high cost, report the exact blocker.

Default to reusing the existing `ws` session for follow-up edits, retries, or refinements on the same target task or files. This is the common case when the user says things like "continue", "重新改下", "让他再改", or gives a correction to the previous `ws` result.

Start a new `ws` session only when the user explicitly asks for a fresh session, independent second opinion, or separate context, or when the task clearly changes to a different repo/module/objective. If reuse could bias an independent review, ask briefly before choosing.

When continuing a successful `ws` conversation, reuse its session:

```bash
ws run \
  --attach "$WORKSPACE_ATTACH_URL" \
  --dir "<target-dir>" \
  --session "<session-id>" \
  --agent build \
  --model "$WORKSPACE_MODEL" \
  --format json \
  "<follow-up prompt>"
```

Prefer explicit `--session` over `--continue` when reproducibility matters.

## Operational Notes

`ws run` may update `.git/hooks/post-commit` through the WorkSpace checkpoint/AI stats integration. Git will not show this in `git status` because hooks live under `.git/`. If hook behavior matters for the repo, inspect `.git/hooks/post-commit` and ask before removing or disabling it.

If a `ws run` emits an error event but leaves a process running, terminate only the `ws run` process you started. Do not kill the long-running WorkSpace server unless the user asks.

If a `ws run --attach ... --dir <external-dir>` produces no JSON events and no model error for 20-30 seconds, suspect an external-directory/project-switch wait. Stop that `ws run` process and retry with the active workspace directory or a repo already opened by WorkSpace.

If `ws` starts responding but then hangs while trying to write a file outside the active workspace, stop that `ws run` process and report the external-directory permission boundary. Do not bypass it with `--dangerously-skip-permissions`.

Keep internal provider URLs, tokens, and `auth.json` contents out of prompts, commits, and skill files. Refer to the selected model as `$WORKSPACE_MODEL`, which defaults to `workspace/gpt-5.4`, unless a task specifically requires deeper local diagnostics.
