← Files Sparkore CoreARCHIVED FILE
skills/kb-bootstrap/scripts/bootstrap_kb.py
6.58 KB · Oct 2, 2026 · 00:35 UTC
#!/usr/bin/env python3
"""Preview, create or check a small shared KB. Existing files are never replaced."""
from __future__ import annotations
import argparse
import datetime as dt
import json
import re
import sys
from pathlib import Path
ASSETS = Path(__file__).resolve().parents[1] / "assets" / "vault"
MARKER = ".kb-bootstrap.json"
SCHEMA = 1
EXTRA_DIRS = ("00_System/Decisions/Records", "00_System/Work/Archive", "99_Archive")
TOKEN = re.compile(r"\{\{([A-Z_]+)\}\}")
def label(value):
if not value.strip() or any(ord(char) < 32 or ord(char) == 127 for char in value):
raise argparse.ArgumentTypeError("use a nonempty single-line value")
return value.strip()
def markdown(value):
return re.sub(r"([\\`*_{}\[\]<>#!|])", r"\\\1", value)
def templates(project, owner, objective):
today = dt.date.today().isoformat()
values = {"PROJECT": markdown(project), "PROJECT_YAML": json.dumps(project),
"OWNER": markdown(owner), "OWNER_YAML": json.dumps(owner),
"OBJECTIVE": markdown(objective), "DATE": today}
result = {}
for asset in sorted(ASSETS.rglob("*.tmpl")):
relative = asset.relative_to(ASSETS).as_posix()[:-5]
result[relative] = TOKEN.sub(lambda match: values[match[1]], asset.read_text(encoding="utf-8"))
if not result:
raise ValueError("bundled KB templates are missing")
return result
def safe_target(root, relative, directory=False):
target = root / relative
for part in (target, *target.parents):
if part == root.parent:
break
if part.is_symlink():
raise ValueError(f"refusing symlink in managed path: {relative}")
if part != target and part.exists() and not part.is_dir():
raise ValueError(f"parent is not a directory: {relative}")
if target.exists() and (target.is_dir() != directory or (not directory and not target.is_file())):
raise ValueError(f"unexpected file type at managed path: {relative}")
return target
def inspect_root(root, project):
if root.is_symlink() or (root.exists() and not root.is_dir()):
raise ValueError("KB root must be a directory, not a file or symlink")
marker = safe_target(root, MARKER)
if marker.exists():
data = json.loads(marker.read_text(encoding="utf-8"))
if not isinstance(data, dict) or data.get("schema") != SCHEMA or data.get("project") != project:
raise ValueError("bootstrap marker has a different schema/project; map the existing KB instead")
elif root.exists() and any(path.name != ".DS_Store" for path in root.iterdir()):
raise ValueError("nonempty KB without a matching bootstrap marker; use the existing-KB adoption workflow")
return marker
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("root", help="Explicit target KB directory")
parser.add_argument("--project", required=True, type=label)
parser.add_argument("--owner", default="Unassigned", type=label)
parser.add_argument("--objective", default="Not supplied", type=label,
help="Supplied first work objective; recorded before later enrichment")
modes = parser.add_mutually_exclusive_group()
modes.add_argument("--apply", action="store_true", help="Create missing starter files; never overwrite")
modes.add_argument("--check", action="store_true", help="Read-only starter structure check")
args = parser.parse_args()
root = Path(args.root).expanduser().absolute()
created = []
try:
contents = templates(args.project, args.owner, args.objective)
marker = inspect_root(root, args.project)
paths = {name: safe_target(root, name) for name in contents}
directories = {safe_target(root, name, directory=True) for name in EXTRA_DIRS}
directories.update(path.parent for path in paths.values())
# Preflight all parents before any write so layout conflicts cannot half-create a KB.
for directory in directories:
safe_target(root, directory.relative_to(root), directory=True)
missing = [name for name, path in paths.items() if not path.exists()]
preserved = [name for name in paths if name not in missing]
missing_dirs = sorted(path.relative_to(root).as_posix() for path in directories if not path.exists())
if args.check:
problems = [f"missing file: {name}" for name in missing]
problems.extend(f"missing directory: {name}" for name in missing_dirs)
if not marker.exists():
problems.append("missing bootstrap marker")
for name in preserved:
text = paths[name].read_text(encoding="utf-8")
if not text.strip() or TOKEN.search(text):
problems.append(f"empty or unrendered starter: {name}")
if "CLAUDE.md" in preserved and not re.search(
r"^@AGENTS\.md\s*$", paths["CLAUDE.md"].read_text(encoding="utf-8"), re.MULTILINE):
problems.append("Claude adapter no longer imports the shared AGENTS.md")
print(json.dumps({"root": str(root), "mode": "check", "structure_complete": not problems,
"problems": problems, "host_behavior": "not_tested"}, indent=2))
return 1 if problems else 0
if args.apply:
root.mkdir(parents=True, exist_ok=True)
if not marker.exists():
with marker.open("x", encoding="utf-8") as stream:
json.dump({"schema": SCHEMA, "project": args.project}, stream, indent=2)
stream.write("\n")
created.append(MARKER)
for directory in sorted(directories, key=lambda path: len(path.parts)):
directory.mkdir(parents=True, exist_ok=True)
for name in missing:
# Exclusive creation also preserves a file created after the preflight.
with paths[name].open("x", encoding="utf-8") as stream:
stream.write(contents[name])
created.append(name)
print(json.dumps({"root": str(root), "mode": "apply" if args.apply else "preview",
"create": missing, "create_directories": missing_dirs,
"created": created, "preserved": preserved,
"next": "Read and adapt project facts; run structure check, KB lint and host handoff checks."}, indent=2))
return 0
except (OSError, ValueError, KeyError) as error:
print(json.dumps({"root": str(root), "error": str(error), "created": created}, indent=2))
return 2
if __name__ == "__main__":
sys.exit(main())
SHA-256: bdcc858588d6d291cba76e821dec2371de99d0996d1472571b66887cca4d4343