subagent_generate: rewrite as standalone Python; fix spawn header
- Eliminates inline Python-in-shell; same logic, directly inspectable. - Fix stale Output comment in subagent_spawn header.
This commit is contained in:
parent
36eeb38a06
commit
a5efaa623f
|
|
@ -1,64 +1,98 @@
|
|||
#!/usr/bin/env bash
|
||||
# description: Generate a JIT subagent config. No args prints the JSON template. With JSON on stdin, creates the config and prints the agent name.
|
||||
#!/usr/bin/env python3
|
||||
# description: Generate a JIT subagent config. No args prints the JSON template. With JSON as first arg or on stdin, creates the config and prints the agent name.
|
||||
# Args: [json] — JSON config as first arg, or piped via stdin
|
||||
# Stdin: JSON with structured fields (role, roster, shared_task, protocol, constraints) or legacy "prompt"
|
||||
# Output: template (no stdin) or agent name
|
||||
set -euo pipefail
|
||||
# Output: template (no input) or agent name
|
||||
|
||||
OLLIE="${OLLIE:-$HOME/mnt/ollie}"
|
||||
import json
|
||||
import os
|
||||
import secrets
|
||||
import sys
|
||||
|
||||
die() { printf 'agent_generate: %s\n' "$*" >&2; exit 1; }
|
||||
OLLIE = os.environ.get("OLLIE", os.path.expanduser("~/mnt/ollie"))
|
||||
AGENTS_DIR = f"{OLLIE}/a"
|
||||
BASE_CONFIG = f"{AGENTS_DIR}/default.json"
|
||||
|
||||
if [ -t 0 ] && [ $# -eq 0 ]; then
|
||||
printf '{\n "name": "",\n "role": "",\n "roster": "",\n "shared_task": "",\n "protocol": "",\n "constraints": [],\n "prompt": ""\n}\n'
|
||||
exit 0
|
||||
fi
|
||||
TEMPLATE = {
|
||||
"name": "",
|
||||
"role": "",
|
||||
"roster": "",
|
||||
"shared_task": "",
|
||||
"protocol": "",
|
||||
"constraints": [],
|
||||
"prompt": "",
|
||||
}
|
||||
|
||||
if [ $# -gt 0 ]; then
|
||||
cfg="$1"
|
||||
else
|
||||
cfg=$(cat) || die "failed to read stdin"
|
||||
fi
|
||||
[ -z "$cfg" ] && die "empty input"
|
||||
|
||||
name=$(printf '%s' "$cfg" | python3 -c "
|
||||
import json, sys, secrets
|
||||
|
||||
raw = json.load(sys.stdin)
|
||||
name = raw.get('name', '') or 'jit-' + secrets.token_hex(8)
|
||||
|
||||
role = raw.get('role', '')
|
||||
roster = raw.get('roster', '')
|
||||
shared_task = raw.get('shared_task', '')
|
||||
protocol = raw.get('protocol', '')
|
||||
constraints = raw.get('constraints', [])
|
||||
legacy = raw.get('prompt', '')
|
||||
|
||||
if not role and not legacy:
|
||||
print('error: role or prompt required', file=sys.stderr)
|
||||
def die(msg):
|
||||
print(f"subagent_generate: {msg}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
if role:
|
||||
parts = ['## Role', role]
|
||||
if roster: parts += ['## Roster', roster]
|
||||
if shared_task: parts += ['## Shared Task', shared_task]
|
||||
if protocol: parts += ['## Coordination Protocol', protocol]
|
||||
if constraints: parts += ['## Constraints', '\n'.join('- ' + c for c in constraints)]
|
||||
identity = '\n\n'.join(parts)
|
||||
else:
|
||||
identity = legacy
|
||||
|
||||
base = sys.argv[1]
|
||||
with open(base) as f:
|
||||
cfg = json.load(f)
|
||||
def main():
|
||||
# No input: print template.
|
||||
if len(sys.argv) < 2 and sys.stdin.isatty():
|
||||
json.dump(TEMPLATE, sys.stdout, indent=2)
|
||||
print()
|
||||
return
|
||||
|
||||
cfg['prompt'] = ['\$OLLIE/x/prime SYSTEM_PROMPT', 'echo ' + json.dumps(identity)]
|
||||
# Read config from arg or stdin.
|
||||
if len(sys.argv) > 1:
|
||||
text = sys.argv[1]
|
||||
else:
|
||||
text = sys.stdin.read()
|
||||
if not text.strip():
|
||||
die("empty input")
|
||||
|
||||
out = sys.argv[2] + '/' + name + '.json'
|
||||
with open(out, 'w') as f:
|
||||
json.dump(cfg, f, indent=2)
|
||||
try:
|
||||
raw = json.loads(text)
|
||||
except json.JSONDecodeError as e:
|
||||
die(f"invalid JSON: {e}")
|
||||
|
||||
print(name)
|
||||
" "$OLLIE/a/default.json" "$OLLIE/a") || die "failed to generate agent config"
|
||||
name = raw.get("name", "") or f"jit-{secrets.token_hex(8)}"
|
||||
role = raw.get("role", "")
|
||||
roster = raw.get("roster", "")
|
||||
shared_task = raw.get("shared_task", "")
|
||||
protocol = raw.get("protocol", "")
|
||||
constraints = raw.get("constraints", [])
|
||||
legacy = raw.get("prompt", "")
|
||||
|
||||
printf '%s\n' "$name"
|
||||
if not role and not legacy:
|
||||
die("role or prompt required")
|
||||
|
||||
# Build identity text.
|
||||
if role:
|
||||
parts = ["## Role", role]
|
||||
if roster:
|
||||
parts += ["## Roster", roster]
|
||||
if shared_task:
|
||||
parts += ["## Shared Task", shared_task]
|
||||
if protocol:
|
||||
parts += ["## Coordination Protocol", protocol]
|
||||
if constraints:
|
||||
parts += ["## Constraints", "\n".join(f"- {c}" for c in constraints)]
|
||||
identity = "\n\n".join(parts)
|
||||
else:
|
||||
identity = legacy
|
||||
|
||||
# Load base config.
|
||||
try:
|
||||
with open(BASE_CONFIG) as f:
|
||||
cfg = json.load(f)
|
||||
except (OSError, json.JSONDecodeError) as e:
|
||||
die(f"failed to read base config: {e}")
|
||||
|
||||
cfg["prompt"] = ["$OLLIE/x/prime SYSTEM_PROMPT", f"echo {json.dumps(identity)}"]
|
||||
|
||||
# Write JIT config.
|
||||
out_path = f"{AGENTS_DIR}/{name}.json"
|
||||
try:
|
||||
with open(out_path, "w") as f:
|
||||
json.dump(cfg, f, indent=2)
|
||||
except OSError as e:
|
||||
die(f"failed to write config: {e}")
|
||||
|
||||
print(name)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/env bash
|
||||
# description: Spawn a subagent session, submit a prompt, and return immediately. The subagent runs independently; use prompt files for coordination.
|
||||
# Args: [-name NAME] [-agent A] [-backend B] [-model M] [-cwd DIR] <prompt...>
|
||||
# Output: session path (e.g. s/my-session)
|
||||
# Output: structured KV (session, path, backend, model)
|
||||
set -euo pipefail
|
||||
|
||||
OLLIE="${OLLIE:-$HOME/mnt/ollie}"
|
||||
|
|
|
|||
Loading…
Reference in New Issue