From 5f9302abc55e8a5494ca7dbcca3f31806eb21e62 Mon Sep 17 00:00:00 2001 From: Jaco Bezuidenhout Date: Thu, 2 Jul 2026 14:48:49 +0000 Subject: [PATCH] feat: add agent-os bootstrap bundle --- .gitignore | 6 + bootstrap/README.md | 32 ++++ bootstrap/bundle-manifest.json | 138 ++++++++++++++++ bootstrap/export-rules.json | 108 +++++++++++++ bootstrap/generate-export.py | 147 ++++++++++++++++++ bootstrap/templates/generic/.gitignore | 23 +++ bootstrap/templates/generic/CLAUDE.md | 36 +++++ bootstrap/templates/generic/README.md | 31 ++++ bootstrap/templates/generic/brain.md | 37 +++++ bootstrap/templates/generic/context/.gitkeep | 0 bootstrap/templates/generic/identity.md | 29 ++++ .../generic/memory/active-projects.md | 7 + .../templates/generic/memory/constraints.md | 19 +++ .../generic/memory/notes-from-last-run.md | 7 + .../templates/generic/memory/persistent.md | 19 +++ .../generic/memory/recent-decisions.md | 7 + .../skills/_template/context/handoff.md | 21 +++ .../generic/skills/_template/eval.json | 17 ++ .../generic/skills/_template/learnings.md | 5 + .../generic/skills/_template/skill.md | 26 ++++ 20 files changed, 715 insertions(+) create mode 100644 bootstrap/README.md create mode 100644 bootstrap/bundle-manifest.json create mode 100644 bootstrap/export-rules.json create mode 100755 bootstrap/generate-export.py create mode 100644 bootstrap/templates/generic/.gitignore create mode 100644 bootstrap/templates/generic/CLAUDE.md create mode 100644 bootstrap/templates/generic/README.md create mode 100644 bootstrap/templates/generic/brain.md create mode 100644 bootstrap/templates/generic/context/.gitkeep create mode 100644 bootstrap/templates/generic/identity.md create mode 100644 bootstrap/templates/generic/memory/active-projects.md create mode 100644 bootstrap/templates/generic/memory/constraints.md create mode 100644 bootstrap/templates/generic/memory/notes-from-last-run.md create mode 100644 bootstrap/templates/generic/memory/persistent.md create mode 100644 bootstrap/templates/generic/memory/recent-decisions.md create mode 100644 bootstrap/templates/generic/skills/_template/context/handoff.md create mode 100644 bootstrap/templates/generic/skills/_template/eval.json create mode 100644 bootstrap/templates/generic/skills/_template/learnings.md create mode 100644 bootstrap/templates/generic/skills/_template/skill.md diff --git a/.gitignore b/.gitignore index 2b4a279..b0c0178 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,14 @@ # Runtime output is generated on the server and must not become source history. logs/ +# Bootstrap exports are generated bundles. Source lives in bootstrap/templates/. +bootstrap/exports/ + # Claude Code compatibility memory is generated/symlinked runtime state. claude-code/ # Operational tools are deployed from the nxm-stacks repository. tools/ +# Python cache generated by local validation. +__pycache__/ +*.pyc diff --git a/bootstrap/README.md b/bootstrap/README.md new file mode 100644 index 0000000..fe65cb8 --- /dev/null +++ b/bootstrap/README.md @@ -0,0 +1,32 @@ +# Agent OS Bootstrap + +This directory owns the source for the generic Agent OS bootstrap bundle. + +The Linux Server design docs are the source specification: + +- `/home/nxm/Documents/NxM Linux Server/Services/Agents.md` defines the Agent OS foundation, the `/opt/agent-os` runtime root, and the agent output contract. +- `/home/nxm/Documents/NxM Linux Server/Operations/Git Workflow.md` defines the Gitea source-of-truth flow and says generated runtime state must stay out of commits. +- `/home/nxm/Documents/NxM Linux Server/Current Work.md` records the clone synchronization baseline for Agent OS. + +## Files + +- `bundle-manifest.json` lists the canonical files and directories in the generic bundle. +- `export-rules.json` defines which metadata is exported and which paths must verify. +- `templates/generic/` holds the canonical bootstrap templates. +- `generate-export.py` creates `bootstrap/exports/generic` from the manifest and rules. + +## Generate + +From a checked-out Agent OS clone: + +```bash +python3 bootstrap/generate-export.py +``` + +To generate the live runtime export requested by the server workflow: + +```bash +sudo -n python3 bootstrap/generate-export.py --output /opt/agent-os/bootstrap/exports/generic +``` + +Generated exports are intentionally ignored by Git. diff --git a/bootstrap/bundle-manifest.json b/bootstrap/bundle-manifest.json new file mode 100644 index 0000000..cbfbb37 --- /dev/null +++ b/bootstrap/bundle-manifest.json @@ -0,0 +1,138 @@ +{ + "schema_version": 1, + "bundle": { + "id": "agent-os-generic-bootstrap", + "name": "Agent OS Generic Bootstrap", + "version": "2026.07.02", + "description": "Canonical starter bundle for a plain-files Agent OS runtime.", + "runtime_root": "/opt/agent-os", + "export_id": "generic", + "export_root": "bootstrap/exports/generic" + }, + "source_spec": [ + { + "path": "/home/nxm/Documents/NxM Linux Server/Services/Agents.md", + "sections": [ + "Agent Output Convention", + "Static Utility Pages", + "Agent OS Foundation" + ] + }, + { + "path": "/home/nxm/Documents/NxM Linux Server/Operations/Git Workflow.md", + "sections": [ + "Source-to-Deployment Flow", + "Repository Layout", + "Files That Must Not Be Committed", + "Minimum Ignore Pattern" + ] + }, + { + "path": "/home/nxm/Documents/NxM Linux Server/Current Work.md", + "sections": [ + "Current Baseline", + "Automated Continuity Controls" + ] + } + ], + "directories": [ + { + "path": "context", + "keep": true + }, + { + "path": "logs", + "keep": true, + "runtime_generated": true + }, + { + "path": "memory", + "keep": false + }, + { + "path": "skills", + "keep": false + }, + { + "path": "skills/_template/context", + "keep": true + } + ], + "templates": [ + { + "source": ".gitignore", + "target": ".gitignore", + "mode": "0644" + }, + { + "source": "README.md", + "target": "README.md", + "mode": "0644" + }, + { + "source": "CLAUDE.md", + "target": "CLAUDE.md", + "mode": "0644" + }, + { + "source": "identity.md", + "target": "identity.md", + "mode": "0644" + }, + { + "source": "brain.md", + "target": "brain.md", + "mode": "0644" + }, + { + "source": "context/.gitkeep", + "target": "context/.gitkeep", + "mode": "0644" + }, + { + "source": "memory/active-projects.md", + "target": "memory/active-projects.md", + "mode": "0644" + }, + { + "source": "memory/constraints.md", + "target": "memory/constraints.md", + "mode": "0644" + }, + { + "source": "memory/notes-from-last-run.md", + "target": "memory/notes-from-last-run.md", + "mode": "0644" + }, + { + "source": "memory/persistent.md", + "target": "memory/persistent.md", + "mode": "0644" + }, + { + "source": "memory/recent-decisions.md", + "target": "memory/recent-decisions.md", + "mode": "0644" + }, + { + "source": "skills/_template/skill.md", + "target": "skills/_template/skill.md", + "mode": "0644" + }, + { + "source": "skills/_template/learnings.md", + "target": "skills/_template/learnings.md", + "mode": "0644" + }, + { + "source": "skills/_template/eval.json", + "target": "skills/_template/eval.json", + "mode": "0644" + }, + { + "source": "skills/_template/context/handoff.md", + "target": "skills/_template/context/handoff.md", + "mode": "0644" + } + ] +} diff --git a/bootstrap/export-rules.json b/bootstrap/export-rules.json new file mode 100644 index 0000000..5cdd9f4 --- /dev/null +++ b/bootstrap/export-rules.json @@ -0,0 +1,108 @@ +{ + "schema_version": 1, + "export_id": "generic", + "description": "Build a self-contained generic bootstrap bundle from canonical templates.", + "metadata": [ + { + "source": "bundle-manifest.json", + "target": "bundle-manifest.json", + "mode": "0644" + }, + { + "source": "export-rules.json", + "target": "export-rules.json", + "mode": "0644" + } + ], + "template_root": "templates/generic", + "ignored_source_paths": [ + "exports/", + "__pycache__/", + ".DS_Store" + ], + "verify": { + "required": [ + { + "path": "bundle-manifest.json", + "type": "file" + }, + { + "path": "export-rules.json", + "type": "file" + }, + { + "path": ".gitignore", + "type": "file" + }, + { + "path": "README.md", + "type": "file" + }, + { + "path": "CLAUDE.md", + "type": "file" + }, + { + "path": "identity.md", + "type": "file" + }, + { + "path": "brain.md", + "type": "file" + }, + { + "path": "context", + "type": "directory" + }, + { + "path": "logs", + "type": "directory" + }, + { + "path": "memory/active-projects.md", + "type": "file" + }, + { + "path": "memory/constraints.md", + "type": "file" + }, + { + "path": "memory/notes-from-last-run.md", + "type": "file" + }, + { + "path": "memory/persistent.md", + "type": "file" + }, + { + "path": "memory/recent-decisions.md", + "type": "file" + }, + { + "path": "skills/_template/skill.md", + "type": "file" + }, + { + "path": "skills/_template/learnings.md", + "type": "file" + }, + { + "path": "skills/_template/eval.json", + "type": "file" + }, + { + "path": "skills/_template/context/handoff.md", + "type": "file" + } + ], + "forbidden": [ + ".env", + ".env.*", + "*.pem", + "*.key", + "*.sqlite", + "*.sqlite3", + "*.db" + ] + } +} diff --git a/bootstrap/generate-export.py b/bootstrap/generate-export.py new file mode 100755 index 0000000..c82dc90 --- /dev/null +++ b/bootstrap/generate-export.py @@ -0,0 +1,147 @@ +#!/usr/bin/env python3 +"""Generate and verify Agent OS bootstrap exports.""" + +from __future__ import annotations + +import argparse +import fnmatch +import json +import shutil +import stat +import sys +from pathlib import Path + + +BOOTSTRAP_ROOT = Path(__file__).resolve().parent +DEFAULT_OUTPUT = BOOTSTRAP_ROOT / "exports" / "generic" + + +def load_json(path: Path) -> dict: + with path.open("r", encoding="utf-8") as handle: + return json.load(handle) + + +def parse_mode(mode: str) -> int: + return int(mode, 8) + + +def ensure_safe_output(output: Path) -> Path: + resolved = output.resolve() + if resolved.parts[-3:] != ("bootstrap", "exports", "generic"): + raise SystemExit( + "refusing to write outside a bootstrap/exports/generic directory: " + f"{resolved}" + ) + return resolved + + +def copy_file(source: Path, target: Path, mode: str) -> None: + if not source.is_file(): + raise FileNotFoundError(f"missing source file: {source}") + target.parent.mkdir(parents=True, exist_ok=True) + shutil.copyfile(source, target) + target.chmod(parse_mode(mode)) + + +def clean_output(output: Path) -> None: + ensure_safe_output(output) + if output.exists(): + shutil.rmtree(output) + output.mkdir(parents=True, exist_ok=True) + + +def create_directories(output: Path, manifest: dict) -> None: + for directory in manifest["directories"]: + target = output / directory["path"] + target.mkdir(parents=True, exist_ok=True) + if directory.get("keep"): + keep_file = target / ".gitkeep" + keep_file.touch() + keep_file.chmod(stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH) + + +def export_bundle(output: Path, manifest: dict, rules: dict) -> None: + clean_output(output) + create_directories(output, manifest) + + for item in rules["metadata"]: + copy_file( + BOOTSTRAP_ROOT / item["source"], + output / item["target"], + item.get("mode", "0644"), + ) + + template_root = BOOTSTRAP_ROOT / rules["template_root"] + for item in manifest["templates"]: + copy_file( + template_root / item["source"], + output / item["target"], + item.get("mode", "0644"), + ) + + +def verify_required(output: Path, rules: dict) -> list[str]: + failures: list[str] = [] + for item in rules["verify"]["required"]: + target = output / item["path"] + expected_type = item["type"] + if expected_type == "file" and not target.is_file(): + failures.append(f"missing file: {item['path']}") + elif expected_type == "directory" and not target.is_dir(): + failures.append(f"missing directory: {item['path']}") + return failures + + +def verify_forbidden(output: Path, rules: dict) -> list[str]: + failures: list[str] = [] + for path in output.rglob("*"): + rel = path.relative_to(output).as_posix() + for pattern in rules["verify"].get("forbidden", []): + if fnmatch.fnmatch(rel, pattern): + failures.append(f"forbidden path exported: {rel}") + return failures + + +def verify_export(output: Path, rules: dict) -> None: + failures = verify_required(output, rules) + verify_forbidden(output, rules) + if failures: + for failure in failures: + print(f"FAIL {failure}", file=sys.stderr) + raise SystemExit(1) + + +def count_files(output: Path) -> int: + return sum(1 for path in output.rglob("*") if path.is_file()) + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument( + "--output", + type=Path, + default=DEFAULT_OUTPUT, + help="Export directory. Must end in bootstrap/exports/generic.", + ) + parser.add_argument( + "--verify-only", + action="store_true", + help="Verify an existing export without regenerating it.", + ) + args = parser.parse_args() + + output = ensure_safe_output(args.output) + manifest = load_json(BOOTSTRAP_ROOT / "bundle-manifest.json") + rules = load_json(BOOTSTRAP_ROOT / "export-rules.json") + + if not args.verify_only: + export_bundle(output, manifest, rules) + + verify_export(output, rules) + print(f"OK {manifest['bundle']['id']} -> {output}") + print(f"OK verified {len(rules['verify']['required'])} required paths") + print(f"OK exported {count_files(output)} files") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/bootstrap/templates/generic/.gitignore b/bootstrap/templates/generic/.gitignore new file mode 100644 index 0000000..5623309 --- /dev/null +++ b/bootstrap/templates/generic/.gitignore @@ -0,0 +1,23 @@ +# Runtime output is generated on the server and must not become source history. +logs/ + +# Bootstrap exports are generated bundles. Source lives in bootstrap/templates/. +bootstrap/exports/ + +# Claude Code compatibility memory is generated/symlinked runtime state. +claude-code/ + +# Local secrets and deployment state must never be committed. +.env +.env.* +!.env.example +*.log +__pycache__/ +*.pyc +.cache/ +data/ +uploads/ +backups/ +*.db +*.sqlite +*.sqlite3 diff --git a/bootstrap/templates/generic/CLAUDE.md b/bootstrap/templates/generic/CLAUDE.md new file mode 100644 index 0000000..b4235b0 --- /dev/null +++ b/bootstrap/templates/generic/CLAUDE.md @@ -0,0 +1,36 @@ +# Agent OS Project Brief + +## What This Project Is + +Agent OS is a tool-agnostic, plain-files operating layer for LLM agents. Markdown files are the shared memory, operating contract, and handoff surface. + +## Runtime + +- Server runtime root: `/opt/agent-os/` +- Scheduled jobs: local scheduler or cron on the server +- Agent web output: `/opt/sites//` +- Agent status logs: `/opt/agent-os/logs//last-run.json` + +## Required Output Contract + +Every scheduled or autonomous agent should write: + +- `last-run.json` at `/opt/agent-os/logs//last-run.json` +- `last-output.md` at `/opt/sites//last-output.md` + +Keep `last-output.md` concise: heading, timestamp, and useful summary. + +## Repository Rules + +- Gitea or the configured remote is the recoverable source of truth. +- Runtime state, logs, caches, credentials, databases, uploads, and generated exports stay out of commits. +- Review and validate changes before deployment. +- Copy successful live fixes back to the edit clone before committing. + +## First-Run Checklist + +1. Fill in `identity.md`. +2. Fill in stable infrastructure facts in `brain.md`. +3. Record non-negotiable constraints in `memory/constraints.md`. +4. Replace this project brief with environment-specific status. +5. Add real skills under `skills//`. diff --git a/bootstrap/templates/generic/README.md b/bootstrap/templates/generic/README.md new file mode 100644 index 0000000..cf677f6 --- /dev/null +++ b/bootstrap/templates/generic/README.md @@ -0,0 +1,31 @@ +# Agent OS + +A plain-files operating layer for agents and LLM tools. + +## First Read + +1. Read `identity.md` for owner preferences, hard limits, and communication style. +2. Read `brain.md` for stable infrastructure and project facts. +3. Read `memory/active-projects.md` for current work. +4. Read `memory/constraints.md` for non-negotiable safety rules. +5. Read `CLAUDE.md` for runtime conventions and project status. + +## Runtime Contract + +- Runtime root: `/opt/agent-os/` +- Machine-readable agent status: `/opt/agent-os/logs//last-run.json` +- Human-readable agent output: `/opt/sites//last-output.md` +- Credentials: reference the approved secret store only; do not write secret values into this repo. + +## Directory Layout + +| Path | Purpose | +|---|---| +| `identity.md` | Owner profile, communication preferences, hard limits | +| `brain.md` | Stable facts, services, hosts, ports, standing decisions | +| `memory/` | Persistent and recent working memory | +| `context/` | Dated task-specific working notes | +| `skills/` | Repeatable workflows and skill learnings | +| `logs/` | Generated runtime status output, ignored by Git | + +This bundle is generic. Replace placeholders before trusting an agent to act. diff --git a/bootstrap/templates/generic/brain.md b/bootstrap/templates/generic/brain.md new file mode 100644 index 0000000..04347c9 --- /dev/null +++ b/bootstrap/templates/generic/brain.md @@ -0,0 +1,37 @@ +# Brain + +Stable facts that every agent may need. + +## Infrastructure + +| Name | Address | Purpose | Notes | +|---|---|---|---| +| | | | | + +## Services + +| Service | URL | Port | Owner | Notes | +|---|---|---|---|---| +| | | | | | + +## Agents + +| Short Name | Full Name | Role | Status | Output | +|---|---|---|---|---| +| | | | | | + +## Repositories + +| Repository | Remote | Runtime Path | Notes | +|---|---|---|---| +| | | | | + +## Standing Decisions + +- Keep generated runtime output out of Git. +- Keep credentials in the approved secret store only. +- Prefer reviewed edit-clone changes over live-only edits. + +## Open Questions + +- diff --git a/bootstrap/templates/generic/context/.gitkeep b/bootstrap/templates/generic/context/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/bootstrap/templates/generic/identity.md b/bootstrap/templates/generic/identity.md new file mode 100644 index 0000000..6a07279 --- /dev/null +++ b/bootstrap/templates/generic/identity.md @@ -0,0 +1,29 @@ +# Identity + +## Owner + +- Name: +- Organization: +- Role: +- Primary timezone: +- Preferred working hours: + +## Communication Preferences + +- Preferred tone: +- Detail level: +- Escalation style: +- Decision-making preferences: + +## Hard Limits + +- Do not expose credentials, private keys, or customer data. +- Do not perform destructive or high-impact actions without explicit approval. +- Do not restart or redeploy production services unless the requested change requires it and validation is ready. + +## Useful Context + +- Important people: +- Important systems: +- Standing priorities: +- Things to avoid: diff --git a/bootstrap/templates/generic/memory/active-projects.md b/bootstrap/templates/generic/memory/active-projects.md new file mode 100644 index 0000000..c4cf772 --- /dev/null +++ b/bootstrap/templates/generic/memory/active-projects.md @@ -0,0 +1,7 @@ +# Active Projects + +Update this file when work starts, pauses, completes, or changes owner. + +| Project | Status | Owner | Next Action | +|---|---|---|---| +| | | | | diff --git a/bootstrap/templates/generic/memory/constraints.md b/bootstrap/templates/generic/memory/constraints.md new file mode 100644 index 0000000..2112f0e --- /dev/null +++ b/bootstrap/templates/generic/memory/constraints.md @@ -0,0 +1,19 @@ +# Constraints + +Rules in this file override convenience. + +## Security + +- Never write credential values into code, logs, docs, generated reports, or chat output. +- Use placeholder examples for configuration shape. +- Treat customer data and private infrastructure details as sensitive. + +## Operations + +- Confirm target path, branch, remote, and worktree status before deployment. +- Keep live fixes synchronized back to the edit clone. +- Validate syntax and intended behavior before service reloads or restarts. + +## Environment-Specific Limits + +- diff --git a/bootstrap/templates/generic/memory/notes-from-last-run.md b/bootstrap/templates/generic/memory/notes-from-last-run.md new file mode 100644 index 0000000..4bc5127 --- /dev/null +++ b/bootstrap/templates/generic/memory/notes-from-last-run.md @@ -0,0 +1,7 @@ +# Notes From Last Run + +Clear or roll up this file at the start of each new session. + +## Notes + +- diff --git a/bootstrap/templates/generic/memory/persistent.md b/bootstrap/templates/generic/memory/persistent.md new file mode 100644 index 0000000..bfabce7 --- /dev/null +++ b/bootstrap/templates/generic/memory/persistent.md @@ -0,0 +1,19 @@ +# Persistent Memory + +Facts that are expected to remain true for a long time. + +## People + +- + +## Systems + +- + +## Preferences + +- + +## Lessons + +- diff --git a/bootstrap/templates/generic/memory/recent-decisions.md b/bootstrap/templates/generic/memory/recent-decisions.md new file mode 100644 index 0000000..b56079b --- /dev/null +++ b/bootstrap/templates/generic/memory/recent-decisions.md @@ -0,0 +1,7 @@ +# Recent Decisions + +Record decisions from the last 30 days or the current work cycle. + +| Date | Decision | Reason | Follow-Up | +|---|---|---|---| +| | | | | diff --git a/bootstrap/templates/generic/skills/_template/context/handoff.md b/bootstrap/templates/generic/skills/_template/context/handoff.md new file mode 100644 index 0000000..7a536c1 --- /dev/null +++ b/bootstrap/templates/generic/skills/_template/context/handoff.md @@ -0,0 +1,21 @@ +# Handoff + +## Objective + +- + +## Completed + +- + +## Validation + +- + +## Risks + +- + +## Next Action + +- diff --git a/bootstrap/templates/generic/skills/_template/eval.json b/bootstrap/templates/generic/skills/_template/eval.json new file mode 100644 index 0000000..5b57963 --- /dev/null +++ b/bootstrap/templates/generic/skills/_template/eval.json @@ -0,0 +1,17 @@ +{ + "schema_version": 1, + "criteria": [ + { + "name": "correctness", + "description": "The skill produces the requested result without inventing facts." + }, + { + "name": "safety", + "description": "The skill respects credentials, approvals, and operational limits." + }, + { + "name": "handoff_quality", + "description": "The skill writes clear output and next steps for humans or other agents." + } + ] +} diff --git a/bootstrap/templates/generic/skills/_template/learnings.md b/bootstrap/templates/generic/skills/_template/learnings.md new file mode 100644 index 0000000..7a5372b --- /dev/null +++ b/bootstrap/templates/generic/skills/_template/learnings.md @@ -0,0 +1,5 @@ +# Learnings + +Append stable lessons from real runs. Keep this concise and actionable. + +- diff --git a/bootstrap/templates/generic/skills/_template/skill.md b/bootstrap/templates/generic/skills/_template/skill.md new file mode 100644 index 0000000..9397079 --- /dev/null +++ b/bootstrap/templates/generic/skills/_template/skill.md @@ -0,0 +1,26 @@ +# Skill Name + +## Purpose + +Describe what this skill does and when an agent should use it. + +## Inputs + +- + +## Workflow + +1. Read the relevant context. +2. Perform the smallest safe action that satisfies the request. +3. Write outputs to the documented runtime paths. +4. Record useful learnings in `learnings.md`. + +## Outputs + +- `/opt/agent-os/logs//last-run.json` +- `/opt/sites//last-output.md` + +## Safety + +- Do not expose secrets. +- Ask for approval before destructive or high-impact actions.