← back to Knowledge Layer Skill
knowledge-layer/scripts/kl_init.py
120 lines
#!/usr/bin/env python3
"""
kl_init.py — scaffold a new Knowledge Layer instance.
A Knowledge Layer is a plain-directory, LLM-compiled wiki (the Karpathy /
gBrain pattern): raw sources go in `sources/`, the LLM compiles them into
typed entry folders, and agents read the wiki FIRST before any task. No RAG,
no vector DB — the structure IS the index.
Usage:
kl_init.py [<path>] [--force] [--folders people,companies,concepts,...]
Defaults:
<path> ./knowledge-layer
folders people,companies,concepts,ideas,projects,operations,media
Creates:
<path>/
SCHEMA.md INDEX.md CONTRADICTIONS.md SOURCES.md
.kl/manifest.json (source content-hash state; do not edit by hand)
sources/ (drop raw material here)
<each typed folder>/_about.md
Exit codes: 0 ok, 1 refused (exists, no --force), 2 bad args.
"""
import argparse
import json
import shutil
import sys
from datetime import date
from pathlib import Path
DEFAULT_FOLDERS = ["people", "companies", "concepts", "ideas",
"projects", "operations", "media"]
FOLDER_ABOUT = {
"people": "One entry per person. Aliases, role, affiliations, quotes, decisions they made.",
"companies": "One entry per organization/vendor. What they do, our relationship, key facts.",
"concepts": "One entry per idea/term/framework worth a durable definition.",
"ideas": "Proposals and hypotheses (not yet decided). Link to the project once acted on.",
"projects": "One entry per initiative. Goal, status, decisions, open questions.",
"operations": "How-we-do-it: workflows, playbooks, standing rules, gotchas.",
"media": "Notes on assets/links/videos/threads — the durable takeaway, not the raw file.",
}
def template_dir() -> Path:
return Path(__file__).resolve().parent.parent / "assets" / "template"
def main() -> int:
ap = argparse.ArgumentParser(description="Scaffold a Knowledge Layer instance.")
ap.add_argument("path", nargs="?", default="knowledge-layer",
help="target directory (default: ./knowledge-layer)")
ap.add_argument("--force", action="store_true",
help="proceed even if the directory already exists")
ap.add_argument("--folders", default=",".join(DEFAULT_FOLDERS),
help="comma-separated typed folder names")
args = ap.parse_args()
root = Path(args.path).resolve()
folders = [f.strip() for f in args.folders.split(",") if f.strip()]
if root.exists() and any(root.iterdir()) and not args.force:
print(f"REFUSED: {root} exists and is not empty. Re-run with --force to add "
f"missing scaffolding without overwriting your entries.")
return 1
tdir = template_dir()
if not tdir.is_dir():
print(f"ERROR: template dir missing at {tdir}", file=sys.stderr)
return 2
root.mkdir(parents=True, exist_ok=True)
(root / ".kl").mkdir(exist_ok=True)
(root / "sources").mkdir(exist_ok=True)
today = date.today().isoformat()
# Seed the four root docs (never clobber an existing one).
for name in ("SCHEMA.md", "INDEX.md", "CONTRADICTIONS.md", "SOURCES.md"):
dst = root / name
if dst.exists():
print(f"kept {name} (already present)")
continue
text = (tdir / name).read_text()
text = text.replace("{{DATE}}", today).replace(
"{{FOLDERS}}", ", ".join(folders))
dst.write_text(text)
print(f"create {name}")
# Typed folders, each with an _about.md so git tracks the empty folder and
# the compile agent knows what belongs where.
for f in folders:
d = root / f
d.mkdir(exist_ok=True)
about = d / "_about.md"
if not about.exists():
desc = FOLDER_ABOUT.get(f, "One entry per item in this category.")
about.write_text(f"# {f}/\n\n{desc}\n\n"
f"Entry files here are `<slug>.md` with YAML "
f"frontmatter (see ../SCHEMA.md).\n")
print(f"folder {f}/")
# Fresh manifest if none.
manifest = root / ".kl" / "manifest.json"
if not manifest.exists():
manifest.write_text(json.dumps(
{"version": 1, "compiled_at": None, "sources": {}}, indent=2) + "\n")
print("create .kl/manifest.json")
print(f"\nKnowledge Layer ready at: {root}")
print("Next: drop raw material into sources/, then run kl_scan.py to see the "
"compile work-list, then follow references/compile-protocol.md.")
return 0
if __name__ == "__main__":
sys.exit(main())