Give opencode the same agent instructions, rules and MCP servers as Claude Code
opencode already reads skills from ~/.claude/skills and ~/.agents/skills, so those needed nothing. The rest did. The manifest now links agents/AGENTS.md to ~/.config/opencode/AGENTS.md as well, beside the Claude Code and Codex names. opencode keeps its MCP servers in its own JSON config, with no file to point a symlink at, so link-mcp now writes them there too. It builds one entry per row of user/agents/mcp/servers with the same tokens it gives Claude Code, and adds ~/.claude/rules/*.md under instructions, since opencode does not read that directory by itself. Only those two keys change. The file is written private and swapped in whole because it now holds tokens, and a config with JSONC comments is left alone with a warning rather than rewritten without them.
This commit is contained in:
1 parent
f4fca1797f
commit
34bc1663ed
3 files changed
+70
-14
No files matched your search
+64
-9
@@ -18,9 +18,15 @@
|
||||
# This is personal content, so it obeys the same interview decision link-user
|
||||
# does. A machine that never said yes registers nothing.
|
||||
#
|
||||
# Only Claude Code is handled. Codex stores its servers inside config.toml, and
|
||||
# rewriting a section of somebody's live TOML is a worse failure mode than
|
||||
# leaving two lines for them to paste once.
|
||||
# Claude Code and opencode are handled. Codex stores its servers inside
|
||||
# config.toml, and rewriting a section of somebody's live TOML is a worse
|
||||
# failure mode than leaving two lines for them to paste once.
|
||||
#
|
||||
# opencode has no CLI for this either, but its config is plain JSON, so this
|
||||
# stage merges two keys into it with jq: one mcp entry per server row, and the
|
||||
# rules directory under instructions, because opencode reads ~/.claude/CLAUDE.md
|
||||
# and ~/.claude/skills on its own but not ~/.claude/rules. Every other key in
|
||||
# that file is left as it was.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
@@ -44,8 +50,12 @@ if [[ "$decision" != "yes" ]]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if ! command -v claude >/dev/null 2>&1; then
|
||||
log "Claude Code is not installed; nothing to register."
|
||||
has_claude=false
|
||||
has_opencode=false
|
||||
command -v claude >/dev/null 2>&1 && has_claude=true
|
||||
command -v opencode >/dev/null 2>&1 && has_opencode=true
|
||||
if ! $has_claude && ! $has_opencode; then
|
||||
log "Neither Claude Code nor opencode is installed; nothing to register."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
@@ -62,6 +72,8 @@ fi
|
||||
|
||||
registered=0
|
||||
skipped=0
|
||||
# The opencode mcp object, built up one server at a time.
|
||||
opencode_mcp='{}'
|
||||
|
||||
while read -r name transport target token_var _rest; do
|
||||
case "${name:-}" in ''|'#'*) continue ;; esac
|
||||
@@ -103,6 +115,20 @@ while read -r name transport target token_var _rest; do
|
||||
fi
|
||||
fi
|
||||
|
||||
auth=""
|
||||
[[ "${args[0]:-}" == -H ]] && auth="${args[1]#Authorization: }"
|
||||
|
||||
if [[ "$transport" == stdio ]]; then
|
||||
entry="$(jq -cn --arg c "$launcher" --arg t "$token" \
|
||||
'{type: "local", command: [$c]} + (if $t == "" then {} else {environment: {MCP_TOKEN: $t}} end)')"
|
||||
else
|
||||
entry="$(jq -cn --arg u "$url" --arg a "$auth" \
|
||||
'{type: "remote", url: $u} + (if $a == "" then {} else {headers: {Authorization: $a}} end)')"
|
||||
fi
|
||||
opencode_mcp="$(jq -c --arg n "$name" --argjson e "$entry" '.[$n] = $e' <<<"$opencode_mcp")"
|
||||
|
||||
$has_claude || continue
|
||||
|
||||
# A server already registered exactly as this row would register it is left
|
||||
# alone. Removing and re-adding it would throw away the OAuth session of a
|
||||
# server that signs in on its own (bill-tracker, atlassian), so every
|
||||
@@ -114,8 +140,6 @@ while read -r name transport target token_var _rest; do
|
||||
'.mcpServers[$n] // {} | {command, env: (if (.env // {}) == {} then null else .env end)}' \
|
||||
"$HOME/.claude.json" 2>/dev/null || true)"
|
||||
else
|
||||
auth=""
|
||||
[[ "${args[0]:-}" == -H ]] && auth="${args[1]#Authorization: }"
|
||||
want="$(jq -cn --arg u "$url" --arg a "$auth" \
|
||||
'{url: $u, auth: (if $a == "" then null else $a end)}')"
|
||||
have="$(jq -c --arg n "$name" \
|
||||
@@ -146,5 +170,36 @@ while read -r name transport target token_var _rest; do
|
||||
fi
|
||||
done <"$SERVERS"
|
||||
|
||||
log "MCP servers: $registered registered, $skipped skipped."
|
||||
log "Claude Code loads them at start, so restart a running session to pick them up."
|
||||
if $has_claude; then
|
||||
log "MCP servers: $registered registered, $skipped skipped."
|
||||
log "Claude Code loads them at start, so restart a running session to pick them up."
|
||||
fi
|
||||
|
||||
$has_opencode || exit 0
|
||||
|
||||
# opencode reads opencode.jsonc or opencode.json, whichever this machine has.
|
||||
# The file holds tokens once this runs, so it is written private and swapped in
|
||||
# whole, the way ~/.claude.json already holds them for Claude Code.
|
||||
OPENCODE_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/opencode"
|
||||
opencode_config="$OPENCODE_DIR/opencode.jsonc"
|
||||
[[ -e "$opencode_config" ]] || opencode_config="$OPENCODE_DIR/opencode.json"
|
||||
rules="$HOME/.claude/rules/*.md"
|
||||
|
||||
mkdir -p "$OPENCODE_DIR"
|
||||
current='{"$schema": "https://opencode.ai/config.json"}'
|
||||
[[ -e "$opencode_config" ]] && current="$(cat "$opencode_config")"
|
||||
|
||||
# jq cannot read JSONC comments. A file that has them is left alone rather
|
||||
# than rewritten without them.
|
||||
if ! merged="$(jq --argjson mcp "$opencode_mcp" --arg rules "$rules" \
|
||||
'.mcp = ((.mcp // {}) + $mcp) | .instructions = (((.instructions // []) - [$rules]) + [$rules])' \
|
||||
<<<"$current" 2>/dev/null)"; then
|
||||
warn "Could not parse $opencode_config as JSON; opencode MCP servers not written."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
tmp="$(mktemp "$OPENCODE_DIR/.opencode-config.XXXXXX")"
|
||||
chmod 600 "$tmp"
|
||||
printf '%s\n' "$merged" >"$tmp"
|
||||
mv "$tmp" "$opencode_config"
|
||||
log "opencode: $(jq -r 'length' <<<"$opencode_mcp") MCP servers and the rules written to $opencode_config."
|
||||
Reference in new issue
Block a user