Generate opencode's config from a tracked template, with matching commands, agents and pwswitch
This commit is contained in:
1 parent
68c7d6a3ea
commit
a987cbab66
16 files changed
+531
-25
No files matched your search
+59
-21
@@ -23,10 +23,13 @@
|
||||
# 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.
|
||||
# stage writes it whole from user/agents/opencode/config.json -- the tracked
|
||||
# template carrying everything that is not a secret -- plus the MCP block built
|
||||
# here from the server rows. The whole file is generated rather than merged into
|
||||
# because the point of the template is that every machine ends up with the same
|
||||
# opencode experience, and a merged file drifts the moment one machine is edited
|
||||
# by hand. The output says so at the top, and anything worth changing goes in
|
||||
# the template, where it reaches the other machines too.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
@@ -37,6 +40,8 @@ PANAMA_PATH="${PANAMA_PATH:-$HOME/.local/share/Panama}"
|
||||
MCP_DIR="$PANAMA_PATH/user/agents/mcp"
|
||||
SERVERS="$MCP_DIR/servers"
|
||||
ENV_FILE="$MCP_DIR/env"
|
||||
TEMPLATE="$PANAMA_PATH/user/agents/opencode/config.json"
|
||||
PANAMA_OLD="$PANAMA_PATH/config/old"
|
||||
STATE_DIR="${XDG_STATE_HOME:-$HOME/.local/state}/panama"
|
||||
DECISION="$STATE_DIR/user-content"
|
||||
|
||||
@@ -177,29 +182,62 @@ 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 reads opencode.jsonc. 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."
|
||||
[[ -r "$TEMPLATE" ]] || { warn "No $TEMPLATE; opencode config not written."; exit 0; }
|
||||
if ! jq -e . "$TEMPLATE" >/dev/null 2>&1; then
|
||||
warn "$TEMPLATE is not valid JSON; opencode config not written."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Nothing at the destination is destroyed, the promise link-user and link-skills
|
||||
# make. A file this stage did not write is somebody's own work and goes to
|
||||
# config/old/ under a name that says where it came from. One carrying the marker
|
||||
# was written by an earlier run and is replaced in place, so re-running never
|
||||
# buries its own output and config/old/ does not grow on every upgrade.
|
||||
MARKER='generated by setup/scripts/link-mcp'
|
||||
displace() {
|
||||
local destination="$1" backup
|
||||
[[ -e "$destination" || -L "$destination" ]] || return 0
|
||||
if [[ -L "$destination" ]]; then
|
||||
rm -f "$destination"
|
||||
return 0
|
||||
fi
|
||||
grep -qF "$MARKER" "$destination" 2>/dev/null && return 0
|
||||
backup="$PANAMA_OLD/mcp-$(printf '%s' "${destination#"$HOME"/}" | tr '/' '-')"
|
||||
[[ -e "$backup" ]] && backup="$backup.$(date +%s)"
|
||||
mv "$destination" "$backup"
|
||||
log "Moved existing $destination to $backup"
|
||||
}
|
||||
|
||||
mkdir -p "$OPENCODE_DIR" "$PANAMA_OLD"
|
||||
# Two names for one file would leave opencode choosing between them, and the one
|
||||
# this stage does not write would win or lose on discovery order.
|
||||
displace "${opencode_config%.jsonc}.json"
|
||||
displace "$opencode_config"
|
||||
|
||||
# The template is portable prose: it names the rules directory as ~/ so it does
|
||||
# not carry this machine's login. opencode resolves a leading ~/ on its own, but
|
||||
# expanding it here keeps the generated file readable the way a person would
|
||||
# have written it by hand when they went looking for where the path came from.
|
||||
merged="$(jq --argjson mcp "$opencode_mcp" --arg home "$HOME" \
|
||||
'.mcp = $mcp
|
||||
| .instructions = [(.instructions // [])[]
|
||||
| if startswith("~/") then $home + .[1:] else . end]' \
|
||||
<<<"$(cat "$TEMPLATE")")"
|
||||
|
||||
tmp="$(mktemp "$OPENCODE_DIR/.opencode-config.XXXXXX")"
|
||||
{
|
||||
printf '// %s\n' "$MARKER"
|
||||
printf '// Edit user/agents/opencode/config.json in the Panama checkout instead.\n'
|
||||
printf '// This file is replaced whenever ./install or panama update runs.\n'
|
||||
printf '%s\n' "$merged"
|
||||
} >"$tmp"
|
||||
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."
|
||||
|
||||
log "opencode: $(jq -r 'length' <<<"$opencode_mcp") MCP servers and the template written to $opencode_config."
|
||||
Reference in new issue
Block a user