T030: seed solution agent-scaffold
This commit is contained in:
parent
83cda680d2
commit
362fd2e5b3
5 changed files with 312 additions and 0 deletions
26
solutions/agent-scaffold/listing.json
Normal file
26
solutions/agent-scaffold/listing.json
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
{
|
||||||
|
"schema_version": "m2.listing.v1",
|
||||||
|
"listing_id": "lst_agent-scaffold",
|
||||||
|
"solution_id": "sol_agent-scaffold",
|
||||||
|
"solution_version": "1.0.0",
|
||||||
|
"inventory_type": "solution",
|
||||||
|
"name": "Agent Scaffold Generator",
|
||||||
|
"summary": "Scaffold OpenClaw/Hermes agent workspaces (PRD.md + SOUL.md + MEMORY.md) from m2-memory context.",
|
||||||
|
"category": "agent-tooling",
|
||||||
|
"keywords": ["agent", "scaffold", "openclaw", "hermes", "prd", "soul", "memory"],
|
||||||
|
"price": {
|
||||||
|
"amount": 60,
|
||||||
|
"currency": "m2cr",
|
||||||
|
"model": "fixed"
|
||||||
|
},
|
||||||
|
"seller": "sdjs-operator",
|
||||||
|
"evidence_summary": "Skill in production use on the m2 host since 2026-04-20, extracted from a real workflow: four PRD.md/SOUL.md agent workspaces at /home/m2/gmi-clinic/agents/ (GMI Clinic fleet deployment) created within a minute of SKILL.md's own mtime, plus three more SOUL.md digital-twin identities at /home/m2/agent-restore-harness/agents/ from 2026-04-22/23 confirming continued production use. Requires buyer-side memory-api access for full function.",
|
||||||
|
"tenant_visibility": ["*"],
|
||||||
|
"stats": {
|
||||||
|
"installs": 0,
|
||||||
|
"proposals_shown": 0,
|
||||||
|
"proposals_accepted": 0
|
||||||
|
},
|
||||||
|
"status": "draft",
|
||||||
|
"install_ref": "lst_agent-scaffold-v1.0.0"
|
||||||
|
}
|
||||||
41
solutions/agent-scaffold/payload/SKILL.md
Normal file
41
solutions/agent-scaffold/payload/SKILL.md
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
---
|
||||||
|
name: agent-scaffold
|
||||||
|
description: Scaffolds OpenClaw/Hermes agent workspaces from m2-memory. Query memory for context, generate PRD.md + SOUL.md for a new agent, and optionally wire into the fleet.
|
||||||
|
---
|
||||||
|
|
||||||
|
# agent-scaffold
|
||||||
|
|
||||||
|
Generate a complete agent workspace (PRD + SOUL + MEMORY seed) from context in m2-memory.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```
|
||||||
|
/agent-scaffold <agent-name> "<what this agent does>"
|
||||||
|
```
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
```
|
||||||
|
/agent-scaffold gesy-rates-fetcher "Fetches and monitors GESY reimbursement rate updates for GMI Clinic"
|
||||||
|
/agent-scaffold platform-unifier "Maps and unifies the 3-5 GMI Clinic software platforms into Machine.Machine"
|
||||||
|
/agent-scaffold nasr-research-runner "Runs parallel deep research tasks for Nasr's healthcare domain work"
|
||||||
|
```
|
||||||
|
|
||||||
|
## What it produces
|
||||||
|
|
||||||
|
For each agent, the skill:
|
||||||
|
|
||||||
|
1. **Queries m2-memory** for relevant context (entity/project history, related agents, decisions)
|
||||||
|
2. **Generates `PRD.md`** — problem, personas, functional requirements, success metrics, open items
|
||||||
|
3. **Generates `SOUL.md`** — agent identity, values, communication style, scope boundaries
|
||||||
|
4. **Generates `MEMORY.md`** — seed memories pre-loaded from m2-memory search results
|
||||||
|
5. **Prints a deploy snippet** — `docker run` or Coolify env vars to spawn the agent
|
||||||
|
|
||||||
|
## Output location
|
||||||
|
|
||||||
|
`~/agents/<agent-name>/` — commit to `git.machinemachine.ai/machine.machine/specs/` when ready.
|
||||||
|
|
||||||
|
## Design principle
|
||||||
|
|
||||||
|
The goal is reproducibility: if an agent's context is lost (like Nasr's Apr 8 research agents),
|
||||||
|
this skill can reconstruct the workspace from the vector store and hand it to OpenClaw or Hermes
|
||||||
|
to re-execute without starting from scratch.
|
||||||
162
solutions/agent-scaffold/payload/scripts/scaffold.py
Executable file
162
solutions/agent-scaffold/payload/scripts/scaffold.py
Executable file
|
|
@ -0,0 +1,162 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
agent-scaffold — generate PRD.md + SOUL.md + MEMORY.md for a new agent
|
||||||
|
from m2-memory context.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
python3 scaffold.py <agent-name> "<description>"
|
||||||
|
python3 scaffold.py gesy-rates-fetcher "Monitors GESY rates for GMI Clinic DRG billing"
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import urllib.request
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
MEMORY_API = os.environ.get("M2_MEMORY_API_URL", "http://172.18.0.20:8000")
|
||||||
|
AGENT_ID = os.environ.get("M2_MEMORY_AGENT_ID", "m2")
|
||||||
|
API_KEY = os.environ.get("M2_MEMORY_API_KEY")
|
||||||
|
OUT_BASE = Path.home() / "agents"
|
||||||
|
|
||||||
|
|
||||||
|
def search_memory(query: str, limit: int = 10) -> list[dict]:
|
||||||
|
url = f"{MEMORY_API}/memory/search"
|
||||||
|
body = json.dumps({
|
||||||
|
"query": query,
|
||||||
|
"agent_id": AGENT_ID,
|
||||||
|
"routing_strategy": "deep",
|
||||||
|
"limit": limit,
|
||||||
|
}).encode()
|
||||||
|
headers = {"Content-Type": "application/json"}
|
||||||
|
if API_KEY:
|
||||||
|
headers["X-API-Key"] = API_KEY
|
||||||
|
req = urllib.request.Request(url, data=body, headers=headers, method="POST")
|
||||||
|
with urllib.request.urlopen(req, timeout=30) as r:
|
||||||
|
return json.loads(r.read()).get("results", [])
|
||||||
|
|
||||||
|
|
||||||
|
def fmt_memory_block(results: list[dict]) -> str:
|
||||||
|
lines = []
|
||||||
|
for r in results:
|
||||||
|
ts = (r.get("timestamp") or "")[:10]
|
||||||
|
score = r.get("score", 0)
|
||||||
|
content = (r.get("content") or "").replace("\n", " ")[:200]
|
||||||
|
lines.append(f"- [{ts} score={score:.2f}] {content}")
|
||||||
|
return "\n".join(lines)
|
||||||
|
|
||||||
|
|
||||||
|
def generate_prd(name: str, description: str, memories: list[dict]) -> str:
|
||||||
|
mem_block = fmt_memory_block(memories)
|
||||||
|
return f"""# {name.replace("-", " ").title()} — PRD
|
||||||
|
|
||||||
|
**Agent ID:** {name}
|
||||||
|
**Description:** {description}
|
||||||
|
**Generated from:** m2-memory search ({len(memories)} relevant memories)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Problem
|
||||||
|
|
||||||
|
> TODO: Refine based on memory context below
|
||||||
|
|
||||||
|
{description}
|
||||||
|
|
||||||
|
## Relevant Memory Context
|
||||||
|
|
||||||
|
{mem_block}
|
||||||
|
|
||||||
|
## Personas
|
||||||
|
|
||||||
|
| Persona | Goal |
|
||||||
|
|---------|------|
|
||||||
|
| TODO | TODO |
|
||||||
|
|
||||||
|
## Functional Requirements
|
||||||
|
|
||||||
|
1. TODO — derived from memory context above
|
||||||
|
2.
|
||||||
|
3.
|
||||||
|
|
||||||
|
## Success Metrics
|
||||||
|
|
||||||
|
- TODO
|
||||||
|
|
||||||
|
## Open Items
|
||||||
|
|
||||||
|
- TODO — check memory for unresolved gaps
|
||||||
|
|
||||||
|
## Technical Notes
|
||||||
|
|
||||||
|
- Agent runtime: OpenClaw / Hermes
|
||||||
|
- Memory: m2-memory (agent_id={name})
|
||||||
|
- Deploy: Coolify stack on Machine.Machine fleet
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def generate_soul(name: str, description: str) -> str:
|
||||||
|
title = name.replace("-", " ").title()
|
||||||
|
return f"""# {title} — Agent Identity
|
||||||
|
|
||||||
|
You are **{title}**, part of the Machine.Machine agent fleet.
|
||||||
|
|
||||||
|
{description}
|
||||||
|
|
||||||
|
## Values
|
||||||
|
- TODO: define 3 core values for this agent
|
||||||
|
|
||||||
|
## Communication style
|
||||||
|
- TODO: define tone and output format
|
||||||
|
|
||||||
|
## Scope boundaries
|
||||||
|
- You do not TODO
|
||||||
|
- You do not TODO
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def generate_memory_seed(memories: list[dict]) -> str:
|
||||||
|
lines = ["# Seed Memories\n",
|
||||||
|
"These memories are pre-loaded from m2-memory at agent creation time.\n"]
|
||||||
|
for r in memories[:10]:
|
||||||
|
ts = (r.get("timestamp") or "")[:19]
|
||||||
|
mtype = r.get("memory_type", "semantic")
|
||||||
|
content = (r.get("content") or "").strip()[:500]
|
||||||
|
lines.append(f"## [{mtype} {ts}]\n{content}\n")
|
||||||
|
return "\n".join(lines)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
p = argparse.ArgumentParser()
|
||||||
|
p.add_argument("name", help="agent slug (e.g. gesy-rates-fetcher)")
|
||||||
|
p.add_argument("description", help="one-line description of what the agent does")
|
||||||
|
p.add_argument("--out", default=None, help="output directory (default: ~/agents/<name>)")
|
||||||
|
p.add_argument("--limit", type=int, default=10, help="memory search limit")
|
||||||
|
args = p.parse_args()
|
||||||
|
|
||||||
|
out_dir = Path(args.out) if args.out else OUT_BASE / args.name
|
||||||
|
out_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
print(f"Searching m2-memory for: {args.description}", file=sys.stderr)
|
||||||
|
try:
|
||||||
|
memories = search_memory(args.description, args.limit)
|
||||||
|
print(f" Found {len(memories)} relevant memories", file=sys.stderr)
|
||||||
|
except Exception as e:
|
||||||
|
print(f" Memory search failed ({e}) — generating without context", file=sys.stderr)
|
||||||
|
memories = []
|
||||||
|
|
||||||
|
(out_dir / "PRD.md").write_text(generate_prd(args.name, args.description, memories))
|
||||||
|
(out_dir / "SOUL.md").write_text(generate_soul(args.name, args.description))
|
||||||
|
(out_dir / "MEMORY.md").write_text(generate_memory_seed(memories))
|
||||||
|
|
||||||
|
print(f"\nScaffolded: {out_dir}")
|
||||||
|
print(f" PRD.md — {(out_dir / 'PRD.md').stat().st_size} bytes")
|
||||||
|
print(f" SOUL.md — {(out_dir / 'SOUL.md').stat().st_size} bytes")
|
||||||
|
print(f" MEMORY.md — {(out_dir / 'MEMORY.md').stat().st_size} bytes")
|
||||||
|
print(f"\nNext: review + fill TODOs, then commit to git.machinemachine.ai/machine.machine/specs/")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
13
solutions/agent-scaffold/recipe.yaml
Normal file
13
solutions/agent-scaffold/recipe.yaml
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
# agent-scaffold install recipe (apply-adapter.md bundle layout, local adapter v1)
|
||||||
|
# Places the agent-scaffold skill into the buyer's Claude Code skills directory.
|
||||||
|
targets:
|
||||||
|
- src: SKILL.md
|
||||||
|
dest: ~/.claude/skills/agent-scaffold/SKILL.md
|
||||||
|
- src: scripts/scaffold.py
|
||||||
|
dest: ~/.claude/skills/agent-scaffold/scripts/scaffold.py
|
||||||
|
checks:
|
||||||
|
- cmd: test -f ~/.claude/skills/agent-scaffold/SKILL.md
|
||||||
|
expect_exit: 0
|
||||||
|
- cmd: test -x ~/.claude/skills/agent-scaffold/scripts/scaffold.py
|
||||||
|
expect_exit: 0
|
||||||
|
entrypoint: "Ask your agent to use the agent-scaffold skill, or run: python3 ~/.claude/skills/agent-scaffold/scripts/scaffold.py <agent-name> \"<description>\""
|
||||||
70
solutions/agent-scaffold/solution.json
Normal file
70
solutions/agent-scaffold/solution.json
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
{
|
||||||
|
"schema_version": "m2.solution.v1",
|
||||||
|
"solution_id": "sol_agent-scaffold",
|
||||||
|
"name": "Agent Scaffold Generator",
|
||||||
|
"summary": "Scaffold OpenClaw/Hermes agent workspaces (PRD.md + SOUL.md + MEMORY.md) from m2-memory context.",
|
||||||
|
"description": "Installs the agent-scaffold skill: given an agent name and a one-line description, it queries m2-memory (agent.memory.system / memory-api) for relevant context and generates a complete agent workspace — PRD.md (problem, personas, functional requirements, success metrics), SOUL.md (identity, values, communication style, scope boundaries), and MEMORY.md (seed memories pre-loaded from the search results) — plus a deploy snippet for OpenClaw or Hermes. Ships two artifacts: SKILL.md and scripts/scaffold.py (a single-file Python script using only the stdlib, talking HTTP to memory-api). Requirement: the buyer machine needs memory-api (m2-memory) network access for full function — scripts/scaffold.py calls M2_MEMORY_API_URL (default http://172.18.0.20:8000, override via env) at /memory/search. Without reachable memory-api the script still runs and produces PRD.md/SOUL.md, but with an empty 'Relevant Memory Context' section and no MEMORY.md content — degraded, not blocked. content_hash is computed over payload/ + recipe.yaml only (not solution.json itself, to avoid the self-referential hash problem of hashing a file that contains its own hash): sha256 of `tar --sort=name --mtime='@0' --owner=0 --group=0 --numeric-owner -czf - payload recipe.yaml` run from this bundle's root.",
|
||||||
|
"intent": "Give an operator a reproducible way to stand up (or reconstruct) a new agent's working context — PRD + identity + seed memory — from what the fleet's memory system already knows, instead of starting from a blank page.",
|
||||||
|
"behavior": {
|
||||||
|
"skill_ref": "payload/SKILL.md"
|
||||||
|
},
|
||||||
|
"tools": [
|
||||||
|
{ "name": "memory-api", "kind": "http-api", "required": true },
|
||||||
|
{ "name": "python3", "kind": "cli", "required": true }
|
||||||
|
],
|
||||||
|
"runtime": {
|
||||||
|
"surfaces": ["claude-code-skill"]
|
||||||
|
},
|
||||||
|
"permissions": [
|
||||||
|
{ "action": "write", "target": "~/.claude/skills/agent-scaffold/" },
|
||||||
|
{ "action": "write", "target": "~/agents/<agent-name>/ (PRD.md, SOUL.md, MEMORY.md — output of running the skill, not part of the install)" },
|
||||||
|
{ "action": "net", "target": "HTTP POST to the configured M2_MEMORY_API_URL /memory/search endpoint" }
|
||||||
|
],
|
||||||
|
"deployment": {
|
||||||
|
"recipe_ref": "recipe.yaml",
|
||||||
|
"entrypoint": "Ask your agent to use the agent-scaffold skill, or run: python3 ~/.claude/skills/agent-scaffold/scripts/scaffold.py <agent-name> \"<description>\"",
|
||||||
|
"verify_command": "test -f ~/.claude/skills/agent-scaffold/SKILL.md"
|
||||||
|
},
|
||||||
|
"applicability": {
|
||||||
|
"image_classes": ["primus", "agent-latest"],
|
||||||
|
"roles": ["operator", "desktop-agent"],
|
||||||
|
"tool_requirements": ["python3", "memory-api"]
|
||||||
|
},
|
||||||
|
"tenant_scope": "m2-core",
|
||||||
|
"evidence": [
|
||||||
|
{
|
||||||
|
"source": "/home/m2/.claude/skills/agent-scaffold/ (SKILL.md, scripts/scaffold.py) — the proven skill this bundle packages, present on the m2 host since 2026-04-20 (SKILL.md mtime 2026-04-20T22:48:00+02:00, scripts/scaffold.py mtime 2026-04-28T00:33:10+02:00 — a later revision of the same script)",
|
||||||
|
"machine": "m2",
|
||||||
|
"excerpt": "SKILL.md frontmatter: 'Scaffolds OpenClaw/Hermes agent workspaces from m2-memory. Query memory for context, generate PRD.md + SOUL.md for a new agent, and optionally wire into the fleet.'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "/home/m2/gmi-clinic/agents/{admin-data-bridge,clinical-entity-extraction,drg-los-optimizer}/{PRD.md,SOUL.md} — three real PRD+SOUL agent-workspace pairs on disk, all created 2026-04-20 22:46:27–22:47:19 (mtimes), roughly one minute before this skill's own SKILL.md mtime (22:48:00) — the exact PRD.md/SOUL.md workspace shape this skill formalizes and automates, produced for the GMI Clinic AI fleet deployment immediately prior to the skill being written",
|
||||||
|
"machine": "m2",
|
||||||
|
"excerpt": "admin-data-bridge/SOUL.md: 'You are the Admin Data Bridge for GMI Clinic... ## Values ## Communication style ## Scope boundaries' — same section structure generate_soul() in scaffold.py emits"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "/home/m2/gmi-clinic/agents/gesy-research/PRD.md — a fourth real workspace, mtime 2026-04-20T22:47:48+02:00, whose own text names the exact incident SKILL.md's 'Design principle' section cites as the skill's reason for existing: 'On 2026-04-08, Nasr ran 4 parallel research agents on his machine... The output was intended to be stored in m2-memory, Planka, and Forgejo but the storage step failed (m2 errored). This PRD reconstructs the agent's purpose and gaps so the research can be completed and properly stored.'",
|
||||||
|
"machine": "m2",
|
||||||
|
"excerpt": "SKILL.md: 'The goal is reproducibility: if an agent's context is lost (like Nasr's Apr 8 research agents), this skill can reconstruct the workspace from the vector store and hand it to OpenClaw or Hermes to re-execute without starting from scratch.'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "/home/m2/agent-restore-harness/agents/{nasr,parlo,peter}/SOUL.md — three further real SOUL.md agent-identity files for actual named fleet agents (nasr-m2o, parlobyg-m2o per /home/m2/CLAUDE.md), mtimes 2026-04-22T23:41–2026-04-23T00:48, confirming the PRD+SOUL workspace pattern stayed in production use after the skill was written, not a one-off",
|
||||||
|
"machine": "m2",
|
||||||
|
"excerpt": "nasr/SOUL.md: 'You are Nasr's digital twin — his AI counterpart in the Machine.Machine fleet... ## Core Capabilities'"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"content_hash": "sha256:202aaff5519e774e2669bd2ca6220fc67284a506775a90ff7710fb2361cc58ee",
|
||||||
|
"price": {
|
||||||
|
"amount": 60,
|
||||||
|
"currency": "m2cr",
|
||||||
|
"model": "fixed"
|
||||||
|
},
|
||||||
|
"seller": "sdjs-operator",
|
||||||
|
"license": {
|
||||||
|
"terms": "Perpetual use on operator-owned machines for the buyer's own agent-workspace generation; no redistribution of the skill files as a standalone product.",
|
||||||
|
"major_version_coverage": true
|
||||||
|
},
|
||||||
|
"revenue_split": {
|
||||||
|
"platform_pct": 10
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue