Skip to content

Commit ac71769

Browse files
author
picoclaw
committed
feat(skills): add self-config skill
safe config editing with secret redaction - config.sh redacted|summary - config-patch.sh start|'... jq patch..'|commit|diff|sort|status|show|rollback|reset|backup - service.sh restart|restart-auto-rollback|confirm
1 parent 25f26f3 commit ac71769

4 files changed

Lines changed: 549 additions & 0 deletions

File tree

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
---
2+
name: self-config
3+
description: Allows the agent to safely update its own configuration files
4+
---
5+
6+
# Self-Config Skill Capabilities
7+
8+
1. Presentation of the `config.json` with secrets redacted.
9+
2. Staging of a `config.new.json` with utilities for patching, sorting, diffing, and validation.
10+
3. Safe service restarts with auto-rollback timers.
11+
12+
## Tools used
13+
- `scripts/config.sh`: Base utility for presenting redacted JSON files.
14+
- `scripts/config-patch.sh`: Tools for iterative patching of a staged `config.new.json` file.
15+
- `scripts/service.sh`: Manages service restarts with auto-rollback.
16+
- `jq`: Used internally for JSON manipulation.
17+
18+
## View Current Configuration
19+
20+
### Redacted (Full)
21+
```bash
22+
scripts/config.sh redacted
23+
```
24+
25+
### Summary (Filtered)
26+
Shows only configured models, agents, enabled tools, devices, and heartbeat settings.
27+
```bash
28+
scripts/config.sh summary
29+
```
30+
31+
---
32+
33+
## Workflow: Safe Configuration Update
34+
35+
### 1. Start a Session
36+
Initialize the staging environment. This redacts sensitive keys (tokens, passwords) into a separate hidden file so they aren't exposed in plain text during editing.
37+
```bash
38+
scripts/config-patch.sh start
39+
```
40+
41+
### 2. Apply Patches
42+
Apply `jq` filters to the **staged** file. You can run this multiple times.
43+
```bash
44+
scripts/config-patch.sh '.path.to.key = "new_value"'
45+
```
46+
47+
### 3. Review Changes
48+
Review the diff between the original and the staged (redacted) version.
49+
```bash
50+
scripts/config-patch.sh diff
51+
```
52+
53+
### 4. View Staged Config
54+
```bash
55+
scripts/config-patch.sh config
56+
# OR
57+
scripts/config-patch.sh summary
58+
```
59+
60+
### 5. Sort Keys (Optional)
61+
Sort the keys in the staged file alphabetically.
62+
```bash
63+
scripts/config-patch.sh sort
64+
```
65+
66+
### 6. Reset (Abort)
67+
If you make a mistake *before* switching, clear the staging files.
68+
```bash
69+
scripts/config-patch.sh reset
70+
```
71+
72+
### 7. Switch & Test (The "Hot" Update)
73+
Commit the patch, restart the service, and create a timer that will rollback
74+
unless 'confirm' action is performed. Default is 120 seconds.
75+
76+
```bash
77+
TIMEOUT=300 scripts/service.sh restart-auto-rollback
78+
```
79+
80+
### 8. Confirm
81+
If the agent is still working and the changes are correct, confirm the update to remove the rollback marker.
82+
```bash
83+
scripts/service.sh confirm
84+
```
85+
86+
## Safety Rules
87+
1. **Always** use `start` before applying patches.
88+
2. **Never** manually edit the `.secrets.json` file.
89+
3. **Always** confirm your changes within the time limit after a `switch`.
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
#!/bin/bash
2+
3+
# config-patch: A tool for iterative JSON patching with high-security secret handling.
4+
5+
# Determine script directory for relocatable operation
6+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
7+
SKILL_ROOT="$(dirname "$SCRIPT_DIR")"
8+
9+
# Source shared config and functions
10+
source "$SCRIPT_DIR/config.sh"
11+
12+
COMMAND="${1:-help}"
13+
14+
case "$COMMAND" in
15+
16+
start|sort)
17+
18+
[ -f "$SECRETS_FILE" ] && { echo "Error: Session active. Commit or reset first."; exit 1; }
19+
[ ! -f "$PICOCLAW_CONFIG" ] && { echo "Error: $PICOCLAW_CONFIG not found."; exit 1; }
20+
21+
redact_secrets || exit 1
22+
23+
# On success - Move results final locations (trap will clean if script crashes before this)
24+
# Clear <file>_TMP so trap doesn't try to delete final files
25+
26+
mv "$CONFIG_TMP" "$STAGING_FILE"
27+
CONFIG_TMP=""
28+
mv "$SECRETS_TMP" "$SECRETS_FILE"
29+
SECRETS_TMP=""
30+
31+
echo "Start: Redacted staging file created. Secrets mapped to $SECRETS_FILE"
32+
33+
;;&
34+
35+
start)
36+
exit 0
37+
;;
38+
39+
sort)
40+
41+
{ rm "$STAGING_FILE" ; jq -S . > "$STAGING_FILE"; } < "$STAGING_FILE"
42+
43+
echo "Sorted: keys in $STAGING_FILE"
44+
exit 0
45+
;;
46+
47+
commit)
48+
[ ! -f "$STAGING_FILE" ] && { echo "Error: No staged changes."; exit 1; }
49+
[ ! -f "$SECRETS_FILE" ] && { echo "Error: No secrets file found."; exit 1; }
50+
51+
# 3. Restoration & Validation
52+
FINAL_FILE=$(mktemp)
53+
54+
# Restore secrets: replace path placeholders with stored values
55+
# Keys in secrets file are the paths (e.g., "SECRET:agents.defaults.model")
56+
jq --slurpfile secrets "$SECRETS_FILE" 'walk(if type == "string" and $secrets[0][.] != null then $secrets[0][.] else . end)' "$STAGING_FILE" > "$FINAL_FILE"
57+
58+
# Ensure no placeholders survived (means they weren't in the map)
59+
# Check if any string key in secrets map still exists in output
60+
while IFS= read -r key; do
61+
if grep -q "\"$key\"" "$FINAL_FILE"; then
62+
echo "Error: Placeholder '$key' was not restored. Check for typos in secrets map."
63+
exit 1
64+
fi
65+
done < <(jq -r 'keys[]' "$SECRETS_FILE")
66+
67+
# 4. JSON Integrity Check
68+
jq . "$FINAL_FILE" > /dev/null 2>&1 || { echo "Error: Invalid JSON output. Aborting."; exit 1; }
69+
70+
# 5. Backup & Swap (unredacted config)
71+
mkdir -p "$BACKUP_DIR"
72+
cp "$PICOCLAW_CONFIG" "$BACKUP_DIR/${BASE}_${TIMESTAMP}.json"
73+
74+
mv "$FINAL_FILE" "$PICOCLAW_CONFIG"
75+
rm -f "$STAGING_FILE" "$SECRETS_FILE"
76+
echo "Committed successfully. Backup: $BACKUP_DIR/${BASE}_${TIMESTAMP}.json"
77+
exit 0
78+
;;
79+
80+
status)
81+
echo "Target: $PICOCLAW_CONFIG"
82+
[ -f "$STAGING_FILE" ] && echo "Staging: Active ($STAGING_FILE)" || echo "Staging: None"
83+
[ -f "$SECRETS_FILE" ] && echo "Secrets: $(jq 'keys | length' "$SECRETS_FILE") tracked" || echo "Secrets: None"
84+
;;
85+
86+
summary)
87+
# Output a summary of the staged config with only configured models, agents, enabled tools, devices, and heartbeat
88+
89+
summarize "$STAGING_FILE"
90+
91+
exit 0
92+
;;
93+
94+
show|redacted|config)
95+
# Output the full staged config
96+
97+
cat "$STAGING_FILE"
98+
;;
99+
100+
diff)
101+
[ ! -f "$STAGING_FILE" ] && { echo "Error: No staged changes to diff."; exit 1; }
102+
103+
redact_secrets
104+
diff -u "$CONFIG_TMP" "$STAGING_FILE"
105+
exit 0
106+
;;
107+
108+
rollback)
109+
# Restore the most recent backup
110+
LATEST=$(ls -t "$BACKUP_DIR"/${BASE}_*.json 2>/dev/null | head -n 1)
111+
if [ -z "$LATEST" ]; then
112+
echo "Error: No backups found for $PICOCLAW_CONFIG"
113+
exit 1
114+
fi
115+
116+
LATEST_BASE=$(basename "$LATEST" .json)
117+
118+
cp "$LATEST" "$PICOCLAW_CONFIG"
119+
120+
echo "Rollback: Restored $PICOCLAW_CONFIG from $LATEST"
121+
exit 0
122+
;;
123+
124+
reset)
125+
rm -f "$STAGING_FILE" "$SECRETS_FILE"
126+
echo "Session cleared."
127+
exit 0
128+
;;
129+
130+
help)
131+
echo "Usage: $0 <command> [config_file]"
132+
echo "Commands:"
133+
echo " start - Create staging file with redacted secrets"
134+
echo " sort - Sort JSON keys alphabetically"
135+
echo " diff - Show staged changes"
136+
echo " commit - Apply staged changes to config"
137+
echo " reset - Clear staging (discard changes)"
138+
echo " rollback - Restore from last backup"
139+
echo " status - Show current state"
140+
echo " summary - Show summary of the staged config with unused models/chatconfigs filtered"
141+
echo " config - Show the staged config"
142+
echo " <jq expr> - Apply inline jq patch (e.g. '.agents.model=\"gpt-4\"')"
143+
exit 0
144+
;;
145+
146+
*)
147+
# Generic Patching
148+
redact_secrets
149+
if [ -f "$STAGING_FILE" ]; then
150+
SOURCE="$STAGING_FILE"
151+
else
152+
SOURCE="$PICOCLAW_CONFIG"
153+
fi
154+
TMP=$(mktemp)
155+
if jq "$COMMAND" "$SOURCE" > "$TMP"; then
156+
mv "$TMP" "$STAGING_FILE"
157+
echo "Applied patch to $STAGING_FILE"
158+
else
159+
rm -f "$TMP"; exit 1
160+
fi
161+
;;
162+
esac

0 commit comments

Comments
 (0)