feat(scripts): merge-settings.sh — jq merge with preservation of theme/editorMode

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Дмитрий
2026-05-11 00:50:56 +03:00
parent 9557109a17
commit 0ba32c29dc
3 changed files with 122 additions and 0 deletions
+16
View File
@@ -0,0 +1,16 @@
{
"theme": "dark",
"editorMode": "vscode",
"statusLine": "compact",
"enabledPlugins": {
"old-plugin@old-marketplace": true
},
"permissions": {
"allow": ["Bash(old:*)"],
"deny": [],
"ask": []
},
"hooks": {
"SessionStart": []
}
}
+42
View File
@@ -0,0 +1,42 @@
#!/usr/bin/env bash
# Merge brain settings-fragment.json into consumer's settings.json
# Replaces: enabledPlugins, permissions, hooks
# Preserves: theme, editorMode, statusLine, all other keys
#
# Usage: merge-settings.sh <consumer-settings.json> <brain-fragment.json>
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
source "$SCRIPT_DIR/common.sh"
target="$1"
fragment="$2"
require_cmd jq || exit 1
[ -f "$fragment" ] || { log_error "Fragment not found: $fragment"; exit 1; }
# If target doesn't exist, just copy fragment
if [ ! -f "$target" ]; then
log_info "Target settings.json missing, creating from fragment"
cp "$fragment" "$target"
exit 0
fi
# Backup
backup="${target}.bak.$(date +%s)"
cp "$target" "$backup"
# Merge: target.* keys preserved EXCEPT enabledPlugins/permissions/hooks — those are REPLACED
# (not deep-merged) from fragment. Use --slurpfile to load fragment into $frag.
tmp="${target}.tmp"
jq --slurpfile frag "$fragment" \
'.enabledPlugins = $frag[0].enabledPlugins
| .permissions = $frag[0].permissions
| .hooks = $frag[0].hooks' \
"$target" > "$tmp"
# Validate output JSON
jq empty "$tmp" || { log_error "Merge produced invalid JSON"; rm "$tmp"; exit 1; }
mv "$tmp" "$target"
log_info "settings.json merged (backup: $backup)"
+64
View File
@@ -0,0 +1,64 @@
#!/usr/bin/env bash
set -u
SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
source "$SCRIPT_DIR/lib/common.sh"
FAILURES=0
assert_eq() {
local expected="$1" actual="$2" name="$3"
if [ "$expected" = "$actual" ]; then
echo "PASS: $name"
else
echo "FAIL: $name (expected '$expected', got '$actual')"
FAILURES=$((FAILURES + 1))
fi
}
# Setup: temp consumer settings + brain fragment
tmpdir=$(mktemp -d)
cp "$SCRIPT_DIR/fixtures/sample-settings.json" "$tmpdir/settings.json"
cat > "$tmpdir/fragment.json" <<EOF2
{
"enabledPlugins": {
"new-plugin@new-mp": true
},
"permissions": {
"allow": ["Bash(new:*)"],
"deny": ["Bash(rm:*)"],
"ask": []
},
"hooks": {
"SessionStart": [{"hooks": [{"type": "command", "command": "echo new"}]}]
}
}
EOF2
# Run merge
bash "$SCRIPT_DIR/lib/merge-settings.sh" "$tmpdir/settings.json" "$tmpdir/fragment.json"
# Test 1: theme preserved
theme=$(jq -r '.theme' "$tmpdir/settings.json")
assert_eq "dark" "$theme" "theme preserved"
# Test 2: editorMode preserved
em=$(jq -r '.editorMode' "$tmpdir/settings.json")
assert_eq "vscode" "$em" "editorMode preserved"
# Test 3: enabledPlugins replaced (not merged)
plugins=$(jq -r '.enabledPlugins | keys[]' "$tmpdir/settings.json")
assert_eq "new-plugin@new-mp" "$plugins" "enabledPlugins replaced with brain version"
# Test 4: permissions.deny populated from fragment
deny=$(jq -r '.permissions.deny[0]' "$tmpdir/settings.json")
assert_eq "Bash(rm:*)" "$deny" "permissions.deny from fragment"
# Test 5: hooks replaced
hook_cmd=$(jq -r '.hooks.SessionStart[0].hooks[0].command' "$tmpdir/settings.json")
assert_eq "echo new" "$hook_cmd" "hooks replaced with brain version"
# Cleanup
rm -rf "$tmpdir"
echo "---"
echo "Failures: $FAILURES"
exit $FAILURES