mirror of
https://github.com/dtzp555-max/memory-continuity.git
synced 2026-07-21 21:15:07 +00:00
feat: agent detection + selective install (v2.4.0)
- post-install.sh: reads openclaw.json `list` array via python3, presents a numbered menu (all/specific/quit), installs SKILL.md to <workspace>/skills/memory-continuity/SKILL.md for chosen agents; falls back to manual path prompt if config absent - post-install.sh: remove gateway restart step - verify.sh: add --all-agents flag that runs the 3-layer check (SKILL.md, continuity_doctor.py, CURRENT_STATE.md) across every detected agent workspace and reports pass/fail per agent - openclaw.plugin.json: bump version to 2.4.0 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -2,7 +2,7 @@
|
|||||||
"id": "memory-continuity",
|
"id": "memory-continuity",
|
||||||
"name": "Memory Continuity",
|
"name": "Memory Continuity",
|
||||||
"description": "Preserves working state across /new, reset, compaction, and gateway restarts via a simple markdown checkpoint file.",
|
"description": "Preserves working state across /new, reset, compaction, and gateway restarts via a simple markdown checkpoint file.",
|
||||||
"version": "2.3.0",
|
"version": "2.4.0",
|
||||||
"source": "https://github.com/dtzp555-max/memory-continuity",
|
"source": "https://github.com/dtzp555-max/memory-continuity",
|
||||||
"configSchema": {
|
"configSchema": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
|
|||||||
+129
-14
@@ -4,7 +4,7 @@
|
|||||||
# This script:
|
# This script:
|
||||||
# 1. Copies the plugin to ~/.openclaw/extensions/memory-continuity/
|
# 1. Copies the plugin to ~/.openclaw/extensions/memory-continuity/
|
||||||
# 2. Adds the plugin entry to openclaw.json (if not present)
|
# 2. Adds the plugin entry to openclaw.json (if not present)
|
||||||
# 3. Restarts the gateway
|
# 3. Detects OpenClaw agents and installs SKILL.md to selected workspaces
|
||||||
#
|
#
|
||||||
# Usage:
|
# Usage:
|
||||||
# bash scripts/post-install.sh
|
# bash scripts/post-install.sh
|
||||||
@@ -50,16 +50,13 @@ if [[ ! -f "$CONFIG_FILE" ]]; then
|
|||||||
echo " WARNING: $CONFIG_FILE not found. Skipping config update."
|
echo " WARNING: $CONFIG_FILE not found. Skipping config update."
|
||||||
echo " You'll need to manually add the plugin entry."
|
echo " You'll need to manually add the plugin entry."
|
||||||
else
|
else
|
||||||
# Check if memory-continuity entry already exists
|
python3 -c "
|
||||||
# Always ensure allow list, install record, and entry are present (idempotent)
|
|
||||||
python3 -c "
|
|
||||||
import json
|
import json
|
||||||
path = '$CONFIG_FILE'
|
path = '$CONFIG_FILE'
|
||||||
with open(path) as f:
|
with open(path) as f:
|
||||||
data = json.load(f)
|
data = json.load(f)
|
||||||
if 'plugins' not in data:
|
if 'plugins' not in data:
|
||||||
data['plugins'] = {}
|
data['plugins'] = {}
|
||||||
# Add to plugins.allow so OpenClaw trusts this plugin (no provenance warning)
|
|
||||||
allow_list = data['plugins'].get('allow', [])
|
allow_list = data['plugins'].get('allow', [])
|
||||||
if 'memory-continuity' not in allow_list:
|
if 'memory-continuity' not in allow_list:
|
||||||
allow_list.append('memory-continuity')
|
allow_list.append('memory-continuity')
|
||||||
@@ -77,7 +74,6 @@ data['plugins']['entries']['memory-continuity'] = {
|
|||||||
'autoExtract': True
|
'autoExtract': True
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
# Add install record for provenance tracking (eliminates untracked-code warning)
|
|
||||||
if 'installs' not in data['plugins']:
|
if 'installs' not in data['plugins']:
|
||||||
data['plugins']['installs'] = {}
|
data['plugins']['installs'] = {}
|
||||||
data['plugins']['installs']['memory-continuity'] = {
|
data['plugins']['installs']['memory-continuity'] = {
|
||||||
@@ -91,23 +87,142 @@ print(' Added plugin entry, trust config, and install record')
|
|||||||
" 2>/dev/null || echo " WARNING: Could not update config automatically. Add manually."
|
" 2>/dev/null || echo " WARNING: Could not update config automatically. Add manually."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Step 3: Restart gateway
|
# ---------------------------------------------------------------------------
|
||||||
echo "[3/3] Restarting gateway ..."
|
# Step 3: Detect agents and install SKILL.md to selected workspaces
|
||||||
if command -v openclaw &>/dev/null; then
|
# ---------------------------------------------------------------------------
|
||||||
if openclaw gateway status 2>&1 | grep -q "running"; then
|
echo "[3/3] Detecting OpenClaw agents ..."
|
||||||
openclaw gateway restart 2>/dev/null && echo " Gateway restarted" || echo " Gateway restart failed — try: openclaw gateway restart"
|
|
||||||
|
# detect_agents outputs lines of: INDEX|ID|DISPLAY_NAME|WORKSPACE_PATH
|
||||||
|
# Uses python3 to parse openclaw.json; falls back gracefully if unavailable.
|
||||||
|
detect_agents() {
|
||||||
|
if [[ ! -f "$CONFIG_FILE" ]]; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
python3 - "$CONFIG_FILE" "$OPENCLAW_DIR" <<'PYEOF'
|
||||||
|
import json, sys, os
|
||||||
|
|
||||||
|
config_file = sys.argv[1]
|
||||||
|
openclaw_dir = sys.argv[2]
|
||||||
|
|
||||||
|
with open(config_file) as f:
|
||||||
|
data = json.load(f)
|
||||||
|
|
||||||
|
default_ws = data.get('defaults', {}).get('workspace', os.path.join(openclaw_dir, 'workspace', 'main'))
|
||||||
|
|
||||||
|
agents = data.get('list', [])
|
||||||
|
seen = set()
|
||||||
|
idx = 1
|
||||||
|
|
||||||
|
for agent in agents:
|
||||||
|
agent_id = agent.get('id', '')
|
||||||
|
if not agent_id or agent_id in seen:
|
||||||
|
continue
|
||||||
|
seen.add(agent_id)
|
||||||
|
|
||||||
|
name = agent.get('name', agent_id)
|
||||||
|
workspace = agent.get('workspace', default_ws if agent_id == 'main' else None)
|
||||||
|
|
||||||
|
# Skip agents without a resolvable workspace
|
||||||
|
if not workspace:
|
||||||
|
workspace = os.path.join(openclaw_dir, 'workspaces', agent_id)
|
||||||
|
|
||||||
|
# Expand ~ in path
|
||||||
|
workspace = os.path.expanduser(workspace)
|
||||||
|
|
||||||
|
print('{}|{}|{}|{}'.format(idx, agent_id, name, workspace))
|
||||||
|
idx += 1
|
||||||
|
|
||||||
|
PYEOF
|
||||||
|
}
|
||||||
|
|
||||||
|
# Collect detected agents into arrays
|
||||||
|
AGENT_IDS=()
|
||||||
|
AGENT_NAMES=()
|
||||||
|
AGENT_WORKSPACES=()
|
||||||
|
|
||||||
|
# Read detection output
|
||||||
|
if command -v python3 &>/dev/null && [[ -f "$CONFIG_FILE" ]]; then
|
||||||
|
while IFS='|' read -r idx agent_id agent_name workspace; do
|
||||||
|
AGENT_IDS+=("$agent_id")
|
||||||
|
AGENT_NAMES+=("$agent_name")
|
||||||
|
AGENT_WORKSPACES+=("$workspace")
|
||||||
|
done < <(detect_agents 2>/dev/null || true)
|
||||||
|
fi
|
||||||
|
|
||||||
|
install_skill_to_workspace() {
|
||||||
|
local workspace="$1"
|
||||||
|
local skill_dest="${workspace}/skills/memory-continuity"
|
||||||
|
mkdir -p "$skill_dest"
|
||||||
|
cp "$REPO_DIR/SKILL.md" "$skill_dest/SKILL.md"
|
||||||
|
echo " Installed → ${skill_dest}/SKILL.md"
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ ${#AGENT_IDS[@]} -eq 0 ]]; then
|
||||||
|
# Fallback: no agents detected — ask user for a workspace path
|
||||||
|
echo " No agents detected in openclaw.json (file missing or parse error)."
|
||||||
|
echo ""
|
||||||
|
printf " Enter workspace path to install SKILL.md (or press Enter to skip): "
|
||||||
|
read -r FALLBACK_WS
|
||||||
|
if [[ -n "$FALLBACK_WS" ]]; then
|
||||||
|
FALLBACK_WS="${FALLBACK_WS/#\~/$HOME}"
|
||||||
|
install_skill_to_workspace "$FALLBACK_WS"
|
||||||
else
|
else
|
||||||
echo " Gateway not running — start it with: openclaw gateway start"
|
echo " Skipped SKILL.md installation."
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
echo " openclaw command not found — install OpenClaw first"
|
# Show numbered list
|
||||||
|
echo ""
|
||||||
|
echo " Found ${#AGENT_IDS[@]} agent(s):"
|
||||||
|
for i in "${!AGENT_IDS[@]}"; do
|
||||||
|
num=$((i + 1))
|
||||||
|
printf " [%d] %s (%s)\n" "$num" "${AGENT_NAMES[$i]}" "${AGENT_WORKSPACES[$i]}"
|
||||||
|
done
|
||||||
|
echo ""
|
||||||
|
echo " Install to: [A]ll agents / [1,2,...] specific (comma-separated) / [Q]uit"
|
||||||
|
printf " > "
|
||||||
|
read -r SELECTION
|
||||||
|
|
||||||
|
case "${SELECTION^^}" in
|
||||||
|
A|ALL)
|
||||||
|
for i in "${!AGENT_IDS[@]}"; do
|
||||||
|
install_skill_to_workspace "${AGENT_WORKSPACES[$i]}"
|
||||||
|
done
|
||||||
|
;;
|
||||||
|
Q|QUIT)
|
||||||
|
echo " Skipped SKILL.md installation."
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
# Parse comma-separated numbers
|
||||||
|
IFS=',' read -ra PICKS <<< "$SELECTION"
|
||||||
|
INSTALLED=0
|
||||||
|
for pick in "${PICKS[@]}"; do
|
||||||
|
pick="${pick// /}" # trim spaces
|
||||||
|
if [[ "$pick" =~ ^[0-9]+$ ]]; then
|
||||||
|
idx=$((pick - 1))
|
||||||
|
if [[ $idx -ge 0 && $idx -lt ${#AGENT_IDS[@]} ]]; then
|
||||||
|
install_skill_to_workspace "${AGENT_WORKSPACES[$idx]}"
|
||||||
|
INSTALLED=$((INSTALLED + 1))
|
||||||
|
else
|
||||||
|
echo " WARNING: No agent at index $pick — skipped."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo " WARNING: Invalid selection '$pick' — skipped."
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if [[ $INSTALLED -eq 0 ]]; then
|
||||||
|
echo " No valid selections — SKILL.md not installed."
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "=== Installation complete ==="
|
echo "=== Installation complete ==="
|
||||||
echo ""
|
echo ""
|
||||||
echo "Verify with:"
|
echo "Verify with:"
|
||||||
echo " openclaw gateway restart 2>&1 | grep memory-continuity"
|
echo " bash scripts/verify.sh"
|
||||||
|
echo " bash scripts/verify.sh --all-agents"
|
||||||
echo ""
|
echo ""
|
||||||
echo "Test with:"
|
echo "Test with:"
|
||||||
echo " 1. Tell your agent something memorable"
|
echo " 1. Tell your agent something memorable"
|
||||||
|
|||||||
+114
-1
@@ -2,11 +2,12 @@
|
|||||||
# verify.sh — Verify memory-continuity installation (3-layer check)
|
# verify.sh — Verify memory-continuity installation (3-layer check)
|
||||||
#
|
#
|
||||||
# Usage:
|
# Usage:
|
||||||
# bash scripts/verify.sh [--workspace PATH] [--sample]
|
# bash scripts/verify.sh [--workspace PATH] [--sample] [--all-agents]
|
||||||
#
|
#
|
||||||
# Options:
|
# Options:
|
||||||
# --workspace PATH Override default workspace (~/.openclaw/workspace/main)
|
# --workspace PATH Override default workspace (~/.openclaw/workspace/main)
|
||||||
# --sample Show a sample high-importance CURRENT_STATE.md
|
# --sample Show a sample high-importance CURRENT_STATE.md
|
||||||
|
# --all-agents Run 3-layer check across all detected agent workspaces
|
||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
@@ -31,6 +32,7 @@ header() { echo -e "\n${BOLD}$*${RESET}"; }
|
|||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
WORKSPACE="${HOME}/.openclaw/workspace/main"
|
WORKSPACE="${HOME}/.openclaw/workspace/main"
|
||||||
SHOW_SAMPLE=false
|
SHOW_SAMPLE=false
|
||||||
|
ALL_AGENTS=false
|
||||||
|
|
||||||
while [[ $# -gt 0 ]]; do
|
while [[ $# -gt 0 ]]; do
|
||||||
case "$1" in
|
case "$1" in
|
||||||
@@ -38,6 +40,8 @@ while [[ $# -gt 0 ]]; do
|
|||||||
WORKSPACE="$2"; shift 2 ;;
|
WORKSPACE="$2"; shift 2 ;;
|
||||||
--sample)
|
--sample)
|
||||||
SHOW_SAMPLE=true; shift ;;
|
SHOW_SAMPLE=true; shift ;;
|
||||||
|
--all-agents)
|
||||||
|
ALL_AGENTS=true; shift ;;
|
||||||
*)
|
*)
|
||||||
echo "Unknown argument: $1" >&2; exit 1 ;;
|
echo "Unknown argument: $1" >&2; exit 1 ;;
|
||||||
esac
|
esac
|
||||||
@@ -85,6 +89,115 @@ EOF
|
|||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# --all-agents: detect all agent workspaces and run checks for each
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
OPENCLAW_DIR="${OPENCLAW_STATE_DIR:-$HOME/.openclaw}"
|
||||||
|
CONFIG_FILE="$OPENCLAW_DIR/openclaw.json"
|
||||||
|
PLUGIN_DIR_SELF="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
|
||||||
|
detect_all_agent_workspaces() {
|
||||||
|
if [[ ! -f "$CONFIG_FILE" ]] || ! command -v python3 &>/dev/null; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
python3 - "$CONFIG_FILE" "$OPENCLAW_DIR" <<'PYEOF'
|
||||||
|
import json, sys, os
|
||||||
|
config_file = sys.argv[1]
|
||||||
|
openclaw_dir = sys.argv[2]
|
||||||
|
with open(config_file) as f:
|
||||||
|
data = json.load(f)
|
||||||
|
default_ws = data.get('defaults', {}).get('workspace', os.path.join(openclaw_dir, 'workspace', 'main'))
|
||||||
|
seen = set()
|
||||||
|
for agent in data.get('list', []):
|
||||||
|
agent_id = agent.get('id', '')
|
||||||
|
if not agent_id or agent_id in seen:
|
||||||
|
continue
|
||||||
|
seen.add(agent_id)
|
||||||
|
name = agent.get('name', agent_id)
|
||||||
|
workspace = agent.get('workspace', default_ws if agent_id == 'main' else os.path.join(openclaw_dir, 'workspaces', agent_id))
|
||||||
|
workspace = os.path.expanduser(workspace)
|
||||||
|
print('{}|{}|{}'.format(agent_id, name, workspace))
|
||||||
|
PYEOF
|
||||||
|
}
|
||||||
|
|
||||||
|
if $ALL_AGENTS; then
|
||||||
|
echo -e "\n${BOLD}memory-continuity — Multi-Agent Verifier${RESET}"
|
||||||
|
echo "────────────────────────────────────────────"
|
||||||
|
|
||||||
|
AGENT_LINES=()
|
||||||
|
while IFS= read -r line; do
|
||||||
|
AGENT_LINES+=("$line")
|
||||||
|
done < <(detect_all_agent_workspaces 2>/dev/null || true)
|
||||||
|
|
||||||
|
if [[ ${#AGENT_LINES[@]} -eq 0 ]]; then
|
||||||
|
echo -e "${RED}No agents detected. Is openclaw.json present and python3 available?${RESET}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
TOTAL_PASS=0
|
||||||
|
TOTAL_FAIL=0
|
||||||
|
|
||||||
|
for line in "${AGENT_LINES[@]}"; do
|
||||||
|
IFS='|' read -r agent_id agent_name agent_ws <<< "$line"
|
||||||
|
echo -e "\n${BOLD}Agent: ${agent_name} (${agent_id})${RESET}"
|
||||||
|
echo -e " Workspace: ${agent_ws}"
|
||||||
|
|
||||||
|
AGENT_ERRORS=0
|
||||||
|
AGENT_WARNINGS=0
|
||||||
|
|
||||||
|
# Layer 1: SKILL.md in workspace
|
||||||
|
SKILL_PATH="${agent_ws}/skills/memory-continuity/SKILL.md"
|
||||||
|
if [[ -f "$SKILL_PATH" ]]; then
|
||||||
|
ok "SKILL.md installed"
|
||||||
|
else
|
||||||
|
fail "SKILL.md missing at ${SKILL_PATH}"
|
||||||
|
AGENT_ERRORS=$((AGENT_ERRORS + 1))
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Layer 2: continuity_doctor.py
|
||||||
|
DOCTOR="${PLUGIN_DIR_SELF}/scripts/continuity_doctor.py"
|
||||||
|
if command -v python3 &>/dev/null && [[ -f "$DOCTOR" ]]; then
|
||||||
|
if python3 "$DOCTOR" --workspace "$agent_ws" &>/dev/null; then
|
||||||
|
ok "continuity_doctor.py passed"
|
||||||
|
else
|
||||||
|
warn "continuity_doctor.py exited non-zero"
|
||||||
|
AGENT_WARNINGS=$((AGENT_WARNINGS + 1))
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
warn "python3 or continuity_doctor.py unavailable — skipped"
|
||||||
|
AGENT_WARNINGS=$((AGENT_WARNINGS + 1))
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Layer 3: CURRENT_STATE.md
|
||||||
|
STATE_FILE="${agent_ws}/memory/CURRENT_STATE.md"
|
||||||
|
if [[ ! -f "$STATE_FILE" ]]; then
|
||||||
|
warn "CURRENT_STATE.md not found (normal for new installs)"
|
||||||
|
AGENT_WARNINGS=$((AGENT_WARNINGS + 1))
|
||||||
|
else
|
||||||
|
LINE_COUNT=$(grep -c '[^[:space:]]' "$STATE_FILE" || true)
|
||||||
|
if [[ $LINE_COUNT -gt 2 ]]; then
|
||||||
|
ok "CURRENT_STATE.md present (${LINE_COUNT} non-blank lines)"
|
||||||
|
else
|
||||||
|
warn "CURRENT_STATE.md appears empty or placeholder"
|
||||||
|
AGENT_WARNINGS=$((AGENT_WARNINGS + 1))
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $AGENT_ERRORS -eq 0 ]]; then
|
||||||
|
echo -e " ${GREEN}→ PASS${RESET} (${AGENT_WARNINGS} warning(s))"
|
||||||
|
TOTAL_PASS=$((TOTAL_PASS + 1))
|
||||||
|
else
|
||||||
|
echo -e " ${RED}→ FAIL${RESET} (${AGENT_ERRORS} error(s), ${AGENT_WARNINGS} warning(s))"
|
||||||
|
TOTAL_FAIL=$((TOTAL_FAIL + 1))
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "────────────────────────────────────────────"
|
||||||
|
echo -e "${BOLD}Summary: ${TOTAL_PASS} passed, ${TOTAL_FAIL} failed (of ${#AGENT_LINES[@]} agents)${RESET}"
|
||||||
|
[[ $TOTAL_FAIL -gt 0 ]] && exit 1 || exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Header
|
# Header
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|||||||
Reference in New Issue
Block a user