scaffold.py 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. #!/usr/bin/env python3
  2. """
  3. scaffold.py — Bootstrap a new LLM Wiki directory structure.
  4. Usage:
  5. python3 scaffold.py <wiki-root> "<Topic Title>"
  6. Example:
  7. python3 scaffold.py ~/wikis/ai-research "AI Research"
  8. Creates:
  9. <wiki-root>/
  10. ├── CLAUDE.md (schema template)
  11. ├── log/
  12. │ └── YYYYMMDD.md (first day's log with scaffold entry)
  13. ├── audit/
  14. │ ├── .gitkeep
  15. │ └── resolved/
  16. │ └── .gitkeep
  17. ├── raw/
  18. │ ├── articles/
  19. │ ├── papers/
  20. │ ├── notes/
  21. │ └── refs/
  22. ├── wiki/
  23. │ ├── index.md (category-structured catalog)
  24. │ ├── concepts/
  25. │ ├── entities/
  26. │ └── summaries/
  27. └── outputs/
  28. └── queries/
  29. """
  30. import os
  31. import sys
  32. from datetime import date, datetime
  33. def scaffold(root: str, title: str) -> None:
  34. today = date.today()
  35. today_iso = today.isoformat()
  36. today_compact = today.strftime("%Y%m%d")
  37. now_hm = datetime.now().strftime("%H:%M")
  38. dirs = [
  39. "raw/articles",
  40. "raw/papers",
  41. "raw/notes",
  42. "raw/refs",
  43. "wiki/concepts",
  44. "wiki/entities",
  45. "wiki/summaries",
  46. "outputs/queries",
  47. "log",
  48. "audit",
  49. "audit/resolved",
  50. ]
  51. for d in dirs:
  52. os.makedirs(os.path.join(root, d), exist_ok=True)
  53. print(f"✓ Created directory tree under {root}/")
  54. # .gitkeep for empty audit dirs
  55. _write(root, "audit/.gitkeep", "")
  56. _write(root, "audit/resolved/.gitkeep", "")
  57. # CLAUDE.md
  58. claude_md = f"""# {title} Knowledge Base
  59. > Schema document — read at the start of every session together with `wiki/index.md`.
  60. > Update after every major compile, ingest batch, or structural change.
  61. ## Scope
  62. What this wiki covers:
  63. - <describe the topic area>
  64. What this wiki deliberately excludes:
  65. - <describe out-of-scope areas>
  66. ## Operations
  67. This wiki follows the llm-wiki skill's five operations: `compile`, `ingest`, `query`, `lint`, `audit`.
  68. Every operation appends an entry to `log/YYYYMMDD.md`.
  69. ## Naming conventions
  70. - **Concept pages** (`wiki/concepts/`): Title Case noun phrases.
  71. - **Folder-split concepts** (`wiki/concepts/<topic>/`): used when a topic exceeds ~1200 words. Contains `index.md` + one file per aspect.
  72. - **Entity pages** (`wiki/entities/`): Proper names.
  73. - **Summary pages** (`wiki/summaries/`): kebab-case source slug.
  74. All pages require YAML frontmatter: `title`, `type`, `created`, `updated`, `sources`, `tags`.
  75. ### Diagrams and formulas
  76. - All diagrams are **mermaid**. No ASCII art.
  77. - All formulas are **KaTeX** (inline `$...$` or block `$$...$$`).
  78. ### Raw file policy
  79. - Small text sources → copy into `raw/<subfolder>/`.
  80. - Large binaries → create a pointer file at `raw/refs/<slug>.md` with `kind: ref` and `external_path` fields. Do not copy the binary.
  81. ## Current articles
  82. *None yet — update this list after every compile.*
  83. ### Concepts
  84. *(none)*
  85. ### Entities
  86. *(none)*
  87. ### Summaries
  88. *(none)*
  89. ## Open research questions
  90. - <What do you want to understand better?>
  91. - <What are the key open questions in this domain?>
  92. ## Research gaps
  93. Sources to ingest:
  94. - [ ] <URL or paper title> — why it's relevant
  95. ## Audit backlog
  96. *(none — run `python3 scripts/audit_review.py <wiki-root> --open` to refresh)*
  97. ## Notes for the LLM
  98. - Language: <en | zh | bilingual>
  99. - Tone: <neutral, academic, conversational, ...>
  100. - Depth: <survey-level | deep technical>
  101. - Handling contradictions: state both, cite each, add to Open Research Questions.
  102. """
  103. _write(root, "CLAUDE.md", claude_md)
  104. print("✓ Created CLAUDE.md")
  105. # log/<today>.md
  106. log_md = f"""# {today_iso}
  107. ## [{now_hm}] scaffold | Initialized {title} knowledge base
  108. - Created directory tree (raw/, wiki/, log/, audit/, outputs/)
  109. - Created CLAUDE.md schema template
  110. - Created wiki/index.md category skeleton
  111. """
  112. _write(root, f"log/{today_compact}.md", log_md)
  113. print(f"✓ Created log/{today_compact}.md")
  114. # wiki/index.md
  115. index_md = f"""# Index — {title}
  116. > One-sentence scope of the wiki.
  117. ## 🔖 Navigation
  118. - [[#Concepts]] · [[#Entities]] · [[#Summaries]] · [[#Open Questions]]
  119. ## Concepts
  120. *(none yet)*
  121. ## Entities
  122. *(none yet)*
  123. ## Summaries (chronological)
  124. *(none yet)*
  125. ## Open Questions
  126. - <First research question>
  127. """
  128. _write(root, "wiki/index.md", index_md)
  129. print("✓ Created wiki/index.md")
  130. print(f"""
  131. ✅ Wiki scaffolded at: {root}/
  132. Next steps:
  133. 1. Fill in CLAUDE.md — define scope and naming conventions
  134. 2. Add sources to raw/ (use Obsidian Web Clipper for web articles)
  135. 3. Run ingest: tell your LLM agent "ingest raw/<file>.md"
  136. 4. Ask questions: "what does the wiki say about X?"
  137. 5. Run lint periodically: python3 scripts/lint_wiki.py {root}
  138. 6. Process feedback: python3 scripts/audit_review.py {root} --open
  139. """)
  140. def _write(root: str, path: str, content: str) -> None:
  141. full = os.path.join(root, path)
  142. os.makedirs(os.path.dirname(full) or ".", exist_ok=True)
  143. with open(full, "w", encoding="utf-8") as f:
  144. f.write(content)
  145. if __name__ == "__main__":
  146. if len(sys.argv) < 3:
  147. print(__doc__)
  148. sys.exit(1)
  149. scaffold(sys.argv[1], sys.argv[2])