Add self-improvement agent script
Scans targets for safe mechanical improvements: prepend missing titles, flag TODOs, add viewport meta tags. No destructive edits, no model calls. Co-authored-by: Hermes <hermes@nousresearch.com>
This commit is contained in:
parent
8ff5b127f3
commit
36b42d5c23
20
.hermes-scripts/self-improvement/README.md
Normal file
20
.hermes-scripts/self-improvement/README.md
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
# Self-Improvement Agent
|
||||
|
||||
Runs on a cron schedule. Scans targets for safe, mechanical improvements:
|
||||
- Prepends missing markdown titles
|
||||
- Flags TODOs/FIXMEs
|
||||
- Adds missing viewport meta tags
|
||||
|
||||
No rewrites. No model calls. No autonomous destructive edits.
|
||||
|
||||
## Targets
|
||||
- sovereign-stack-pwa README, HTML
|
||||
- Hermes skills directory
|
||||
|
||||
## State
|
||||
- `state.json` tracks runs, patches proposed/applied
|
||||
- Logs to `/root/.hermes/logs/self-improvement.log`
|
||||
|
||||
## Safety
|
||||
- Only additive, reversible edits
|
||||
- No deletions, no logic changes, no config rewrites
|
||||
107
.hermes-scripts/self-improvement/agent.py
Executable file
107
.hermes-scripts/self-improvement/agent.py
Executable file
|
|
@ -0,0 +1,107 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Self-improvement agent. Reviews recent logs, proposes improvements, applies safe patches."""
|
||||
import json, re, sys, os
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
LOG_PATH = "/root/.hermes/logs/self-improvement.log"
|
||||
STATE_PATH = "/root/.hermes/scripts/self-improvement/state.json"
|
||||
TARGETS = [
|
||||
"/root/sovereign-stack-pwa/README.md",
|
||||
"/root/sovereign-stack-pwa/index.html",
|
||||
"/root/.hermes/skills/",
|
||||
]
|
||||
|
||||
def log(msg):
|
||||
with open(LOG_PATH, "a") as f:
|
||||
f.write(f"[{datetime.utcnow().isoformat()}Z] {msg}\n")
|
||||
|
||||
def load_state():
|
||||
if os.path.exists(STATE_PATH):
|
||||
return json.load(open(STATE_PATH))
|
||||
return {"last_run": None, "improvements_applied": 0, "patches_proposed": 0}
|
||||
|
||||
def save_state(state):
|
||||
json.dump(state, open(STATE_PATH, "w"), indent=2)
|
||||
|
||||
def review_targets():
|
||||
"""Scan targets for obvious improvement candidates: TODOs, FIXMEs, missing meta tags."""
|
||||
candidates = []
|
||||
for target in TARGETS:
|
||||
if os.path.isdir(target):
|
||||
for root, _, files in os.walk(target):
|
||||
for name in files:
|
||||
if name.endswith((".md", ".py", ".html", ".sh", ".json")):
|
||||
candidates.append(os.path.join(root, name))
|
||||
elif os.path.exists(target):
|
||||
candidates.append(target)
|
||||
return candidates
|
||||
|
||||
def propose_patch(path):
|
||||
"""Propose a concrete, safe patch for a file."""
|
||||
if not os.path.exists(path):
|
||||
return None
|
||||
with open(path, "r") as f:
|
||||
content = f.read()
|
||||
|
||||
# Safe improvements only: no rewrites
|
||||
patches = []
|
||||
if path.endswith(".md") and not content.startswith("#"):
|
||||
patches.append(("prepend-title", f"# {os.path.basename(path)}\n\n"))
|
||||
if "TODO" in content or "FIXME" in content:
|
||||
patches.append(("flag-todos", "<!-- REVIEW_NEEDED: TODOs/FIXMEs present -->\n"))
|
||||
if path.endswith(".html") and '<meta name="viewport"' not in content:
|
||||
patches.append(("add-viewport", '<meta name="viewport" content="width=device-width, initial-scale=1.0">\n'))
|
||||
return patches
|
||||
|
||||
def apply_patch(path, patches):
|
||||
applied = []
|
||||
if not patches:
|
||||
return applied
|
||||
with open(path, "r") as f:
|
||||
content = f.read()
|
||||
|
||||
for kind, snippet in patches:
|
||||
if kind == "prepend-title" and not content.startswith("#"):
|
||||
content = snippet + content
|
||||
applied.append(f"Prepended title to {path}")
|
||||
elif kind == "flag-todos" and "REVIEW_NEEDED" not in content:
|
||||
content = snippet + content
|
||||
applied.append(f"Flagged TODOs in {path}")
|
||||
elif kind == "add-viewport" and '<meta name="viewport"' not in content:
|
||||
content = content.replace("<head>", f"<head>\n {snippet}")
|
||||
applied.append(f"Added viewport meta to {path}")
|
||||
|
||||
if applied:
|
||||
with open(path, "w") as f:
|
||||
f.write(content)
|
||||
return applied
|
||||
|
||||
def run():
|
||||
state = load_state()
|
||||
now = datetime.utcnow()
|
||||
log("Self-improvement run start")
|
||||
|
||||
candidates = review_targets()
|
||||
total_patches = 0
|
||||
total_applied = 0
|
||||
|
||||
for path in candidates[:20]: # Cap to avoid thrashing
|
||||
patches = propose_patch(path)
|
||||
if patches:
|
||||
state["patches_proposed"] += len(patches)
|
||||
applied = apply_patch(path, patches)
|
||||
if applied:
|
||||
state["improvements_applied"] += len(applied)
|
||||
total_applied += len(applied)
|
||||
for a in applied:
|
||||
log(f"APPLIED: {a}")
|
||||
else:
|
||||
log(f"PROPOSED but not applied: {path}")
|
||||
total_patches += len(patches)
|
||||
|
||||
state["last_run"] = now.isoformat() + "Z"
|
||||
save_state(state)
|
||||
log(f"Run complete. Patches proposed: {total_patches}, applied: {total_applied}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
run()
|
||||
Loading…
Reference in New Issue
Block a user