136 lines
4.2 KiB
Python
136 lines
4.2 KiB
Python
#!/usr/bin/env python3
|
|
"""Bootstrap or repair an ECHO vault from the bundled scaffold."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import os
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
SCRIPT_DIR = Path(__file__).resolve().parent
|
|
SKILL_DIR = SCRIPT_DIR.parent
|
|
SCAFFOLD = SKILL_DIR / "scaffold"
|
|
sys.path.insert(0, str(SCRIPT_DIR))
|
|
|
|
import echo # noqa: E402
|
|
|
|
|
|
LEAVES = [
|
|
"inbox/captures",
|
|
"inbox/imports",
|
|
"inbox/processing-log",
|
|
"journal/daily",
|
|
"journal/weekly",
|
|
"journal/monthly",
|
|
"journal/quarterly",
|
|
"journal/annual",
|
|
"journal/templates",
|
|
"projects/active",
|
|
"projects/incubating",
|
|
"projects/on-hold",
|
|
"projects/archived",
|
|
"areas/business",
|
|
"areas/personal",
|
|
"areas/learning",
|
|
"areas/systems",
|
|
"resources/concepts",
|
|
"resources/references",
|
|
"resources/people",
|
|
"resources/companies",
|
|
"resources/meetings",
|
|
"decisions/by-date",
|
|
"_agent/context",
|
|
"_agent/memory/working",
|
|
"_agent/memory/episodic",
|
|
"_agent/memory/semantic",
|
|
"_agent/sessions",
|
|
"_agent/health",
|
|
"_agent/templates",
|
|
"_agent/heartbeat",
|
|
"_agent/skills/active",
|
|
"_agent/skills/archived",
|
|
"_agent/locks",
|
|
]
|
|
|
|
|
|
def exists(path: str) -> bool:
|
|
status, _ = echo.request("GET", echo.vault_url(path))
|
|
return status == 200
|
|
|
|
|
|
def put_text(path: str, text: str) -> None:
|
|
status, body = echo.request(
|
|
"PUT",
|
|
echo.vault_url(path),
|
|
data=text.encode(),
|
|
headers={"Content-Type": "text/markdown"},
|
|
)
|
|
echo.check(status, body, f"bootstrap put {path}")
|
|
|
|
|
|
def seed(path: str, source: Path, dry_run: bool) -> None:
|
|
if exists(path):
|
|
print(f"bootstrap: skip (exists) {path}")
|
|
return
|
|
if dry_run:
|
|
print(f"bootstrap: would seed {path} <- {source.relative_to(SKILL_DIR)}")
|
|
return
|
|
text = source.read_text(encoding="utf-8").replace("{{DATE}}", echo.today())
|
|
put_text(path, text)
|
|
print(f"bootstrap: seeded {path}")
|
|
|
|
|
|
def leaf_readme(folder: str, dry_run: bool) -> None:
|
|
path = f"{folder}/README.md"
|
|
if exists(path):
|
|
return
|
|
if dry_run:
|
|
print(f"bootstrap: would readme {path}")
|
|
return
|
|
name = folder.rsplit("/", 1)[-1]
|
|
put_text(path, f"# {name}\n\nMemory vault folder. See the echo-memory plugin for conventions.\n")
|
|
print(f"bootstrap: readme {path}")
|
|
|
|
|
|
def main(argv: list[str] | None = None) -> int:
|
|
parser = argparse.ArgumentParser(description="Bootstrap or repair an ECHO vault")
|
|
parser.add_argument("--dry-run", "-n", action="store_true")
|
|
args = parser.parse_args(argv)
|
|
|
|
if not SCAFFOLD.is_dir():
|
|
print(f"bootstrap: scaffold not found at {SCAFFOLD}", file=sys.stderr)
|
|
return 1
|
|
|
|
marker = exists("_agent/echo-vault.md")
|
|
if marker:
|
|
try:
|
|
status, body = echo.request("GET", echo.vault_url("_agent/echo-vault.md"))
|
|
echo.check(status, body, "bootstrap marker")
|
|
version = next((line.split(":", 1)[1].strip() for line in body.decode().splitlines() if line.startswith("schema_version:")), "unknown")
|
|
except Exception:
|
|
version = "unknown"
|
|
print(f"bootstrap: marker present (schema_version={version}). Running repair pass.")
|
|
|
|
for folder in LEAVES:
|
|
leaf_readme(folder, args.dry_run)
|
|
|
|
templates = SCAFFOLD / "templates"
|
|
if templates.is_dir():
|
|
for source in sorted(templates.rglob("*.md")):
|
|
seed(source.relative_to(templates).as_posix(), source, args.dry_run)
|
|
|
|
seed("_agent/memory/semantic/operator-preferences.md", SCAFFOLD / "anchors" / "operator-preferences.seed.md", args.dry_run)
|
|
seed("_agent/context/current-context.md", SCAFFOLD / "anchors" / "current-context.seed.md", args.dry_run)
|
|
seed("inbox/captures/inbox.md", SCAFFOLD / "anchors" / "inbox.seed.md", args.dry_run)
|
|
seed("README.md", SCAFFOLD / "README.vault.md", args.dry_run)
|
|
seed("_agent/echo-vault.md", SCAFFOLD / "echo-vault.md", args.dry_run)
|
|
|
|
print(f"bootstrap: done ({'DRY-RUN ' if args.dry_run else ''}{echo.today()}).")
|
|
print("bootstrap: next: create today's daily note, bootstrap session log, and heartbeat if this is a first run.")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|