feat: scenarios for curator/memo-inbox/pi-grok, deploy and backup tooling
Scenarios - memo-inbox: mirrored by copying; the live directory was not moved or modified and the service was not restarted. All four tracked files match byte for byte (pi-diff.sh reports SAME). Marked deploy = "mirror" so deploy-scenario.sh refuses --apply: applying a mirror would invert the direction of truth and could change a service in daily use. - curator: target configuration, not yet deployed. .pi/SYSTEM.md replaces pi's coding-assistant prompt; durable role text is in .pi/APPEND_SYSTEM.md; profile.toml is the single source of truth for the launch contract. - pi-grok: registered only. It is genuinely a coding agent, so the isolation baseline does not apply in full. Corrections to the documentation, found by testing rather than by reading - AGENTS.override.md does NOT block parent-directory context files; it only shadows its own directory. Verified: with an override file in the workspace, a marker in /tmp/AGENTS.md still reached the system prompt. The only effective switch is --no-context-files, so durable role text must live in .pi/APPEND_SYSTEM.md, which is a system-prompt file and unaffected by -nc. Verified end state: no coding-assistant framing, no pi-docs block, own identity and role text present, no parent pollution, only own skills/tools. - PI_CODING_AGENT_DIR isolates settings/models/auth/trust/extensions/skills/ prompts/themes under the agent directory -- stronger than the --no-* flags because it also repoints credentials -- but does NOT cover ~/.agents/skills. Measured: find-skills, modsearch and summarize still leak. So it complements --no-skills rather than replacing it. - --append-system-prompt accepts a file path, which pi-grok relies on. - cwd is what anchors .pi discovery: a probe that forgot cwd silently lost .pi/SYSTEM.md and kept the coding-assistant persona. Tooling (all dry-run by default; none of them restarts a service) - pi-diff.sh: compares tracked config against the live install in both directions, with a key-redacted comparison for models.json - deploy-scenario.sh: installs a workspace and renders profile.toml into .pi/launch.json, then checks that every referenced path exists - deploy-runtime.sh: renders models.json from its template, refusing placeholder or missing keys. Verified byte-identical to the live file - pi-backup.sh / pi-restore.sh: archives outside the repo, sha256 manifest verified before any restore, live paths preserved rather than overwritten Fixed while testing: pi-backup.sh compared the destination against the repo root literally, so a relative --dest ./backups wrote credential archives into the work tree. Now canonicalised with realpath; ./backups, an absolute in-repo path and ./docs/../backups are all refused.
This commit is contained in:
Executable
+81
@@ -0,0 +1,81 @@
|
||||
#!/usr/bin/env bash
|
||||
# Shared helpers for the pi-agent-config scripts.
|
||||
# shellcheck shell=bash
|
||||
|
||||
set -uo pipefail
|
||||
|
||||
REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null)" || {
|
||||
echo "error: not inside the pi-agent-config git work tree" >&2
|
||||
exit 1
|
||||
}
|
||||
export REPO_ROOT
|
||||
|
||||
if [ -t 1 ]; then
|
||||
C_RED=$'\033[31m'; C_GREEN=$'\033[32m'; C_YELLOW=$'\033[33m'
|
||||
C_BOLD=$'\033[1m'; C_DIM=$'\033[2m'; C_OFF=$'\033[0m'
|
||||
else
|
||||
C_RED=''; C_GREEN=''; C_YELLOW=''; C_BOLD=''; C_DIM=''; C_OFF=''
|
||||
fi
|
||||
|
||||
info() { printf '%s\n' "$*"; }
|
||||
ok() { printf '%s %s%s\n' "${C_GREEN}OK${C_OFF}" "$*" ''; }
|
||||
warn() { printf '%s %s\n' "${C_YELLOW}WARN${C_OFF}" "$*" >&2; }
|
||||
die() { printf '%s %s\n' "${C_RED}ERROR${C_OFF}" "$*" >&2; exit 1; }
|
||||
head1() { printf '\n%s%s%s\n' "$C_BOLD" "$*" "$C_OFF"; }
|
||||
|
||||
# List the scenarios that exist, excluding the template.
|
||||
list_scenarios() {
|
||||
find "$REPO_ROOT/scenarios" -mindepth 1 -maxdepth 1 -type d \
|
||||
-not -name '_*' -printf '%f\n' | sort
|
||||
}
|
||||
|
||||
require_scenario() {
|
||||
local name="${1:-}"
|
||||
[ -n "$name" ] || die "a scenario name is required. Available: $(list_scenarios | tr '\n' ' ')"
|
||||
[ -d "$REPO_ROOT/scenarios/$name" ] \
|
||||
|| die "unknown scenario '$name'. Available: $(list_scenarios | tr '\n' ' ')"
|
||||
printf '%s' "$REPO_ROOT/scenarios/$name"
|
||||
}
|
||||
|
||||
# Read a flat "key = value" entry from a TOML section.
|
||||
# Intentionally minimal: these profiles are hand-written and flat. Anything more
|
||||
# structured should be read by the Python consumer, not by shell.
|
||||
toml_get() {
|
||||
local file="$1" section="$2" key="$3"
|
||||
awk -v want_section="$section" -v want_key="$key" '
|
||||
/^[[:space:]]*\[/ {
|
||||
s = $0; sub(/^[[:space:]]*\[/, "", s); sub(/\][[:space:]]*$/, "", s)
|
||||
current = s; next
|
||||
}
|
||||
{
|
||||
line = $0
|
||||
sub(/[[:space:]]*#.*$/, "", line)
|
||||
if (current != want_section) next
|
||||
if (line !~ /=/) next
|
||||
k = line; sub(/=.*$/, "", k); gsub(/[[:space:]]/, "", k)
|
||||
if (k != want_key) next
|
||||
v = line; sub(/^[^=]*=[[:space:]]*/, "", v)
|
||||
gsub(/^"|"$/, "", v)
|
||||
gsub(/[[:space:]]+$/, "", v)
|
||||
print v; exit
|
||||
}
|
||||
' "$file"
|
||||
}
|
||||
|
||||
# Print a unified diff between a tracked file and its live counterpart.
|
||||
# Returns 0 when identical, 1 when different or missing.
|
||||
diff_file() {
|
||||
local tracked="$1" live="$2" label="$3"
|
||||
if [ ! -f "$live" ]; then
|
||||
printf '%s %s %s(live file absent)%s\n' "${C_YELLOW}DIFF${C_OFF}" "$label" "$C_DIM" "$C_OFF"
|
||||
return 1
|
||||
fi
|
||||
if cmp -s "$tracked" "$live"; then
|
||||
printf '%s %s\n' "${C_GREEN}SAME${C_OFF}" "$label"
|
||||
return 0
|
||||
fi
|
||||
printf '%s %s\n' "${C_YELLOW}DIFF${C_OFF}" "$label"
|
||||
diff -u --label "repo/$label" "$tracked" --label "live/$label" "$live" \
|
||||
| sed 's/^/ /' || true
|
||||
return 1
|
||||
}
|
||||
Reference in New Issue
Block a user