import os import re import sys import json from pathlib import Path import shutil filenames = [ "docs/changes.md", "docs/overview.md", "docs/lexical_structure.md", "docs/primitive_types.md", "docs/basic_operations.md", "docs/functions.md", "docs/control_flow.md", "docs/data_structures.md", "docs/type_system.md", "docs/trait_system.md", "docs/generic_programming.md", "docs/advanced_topics.md", "docs/standard_library.md", "docs/complete_trait_reference.md", "docs/complete_operator_reference.md", "docs/grammar_summary.md", "docs/module_system.md", "docs/memory_management.md", "docs/examples_and_tutorials.md", "docs/complete_type_reference.md", ] def remove_front_matter(content): """Remove YAML front matter (--- ... ---) from markdown content.""" pattern = r'^---\n.*?\n---\n' return re.sub(pattern, '', content, flags=re.DOTALL) def extract_front_matter(content): """Extract YAML front matter from markdown content.""" match = re.match(r'^---\n(.*?)\n---\n', content, flags=re.DOTALL) return match.group(1).strip() if match else None def read_file_list(input_arg): """Return list of files either from arguments or a text file of paths.""" p = Path(input_arg) if p.is_file() and p.suffix == '.txt': with p.open(encoding='utf-8') as f: return [line.strip() for line in f if line.strip()] return [input_arg] def combine_markdown(file_inputs, output_combined, output_meta_json): """Combine multiple markdown files into one, skipping front matter.""" files = [] for f in file_inputs: files.extend(read_file_list(f)) combined_parts = [] meta_info = {"order": [], "files": {}} for file in files: file_path = Path(file) if not file_path.exists(): print(f"Skipping missing file: {file}") continue text = file_path.read_text(encoding='utf-8') front_matter = extract_front_matter(text) if front_matter: meta_info["files"][file_path.name] = front_matter cleaned = remove_front_matter(text).strip() combined_parts.append(cleaned) meta_info["order"].append(file_path.name) Path(output_combined).write_text("\n\n".join(combined_parts) + "\n", encoding='utf-8') Path(output_meta_json).write_text(json.dumps(meta_info, indent=2), encoding='utf-8') print(f"Combined file saved as: {output_combined}") print(f"Metadata JSON saved as: {output_meta_json}") def write_with_safety(path, content, force=False, backup=False): """Write a file safely with force/backup options and diff-aware prompt.""" if path.exists(): current = path.read_text(encoding='utf-8') if current == content: print(f"No changes for: {path}") return if backup and not force: backup_path = path.with_suffix(path.suffix + ".bak") shutil.copy2(path, backup_path) print(f"Backup created: {backup_path}") elif not force: while True: choice = input(f"{path} has changed. [y] overwrite, [b] backup+overwrite, [n] skip? ").strip().lower() if choice in ("y", "yes"): break elif choice in ("b", "backup"): backup_path = path.with_suffix(path.suffix + ".bak") shutil.copy2(path, backup_path) print(f"Backup created: {backup_path}") break elif choice in ("n", "no", ""): print(f"Skipped: {path}") return else: print("Please choose [y], [b], or [n].") path.write_text(content, encoding='utf-8') print(f"Wrote: {path}") def split_with_front_matter(input_combined, output_dir, metadata_file, force=False, backup=False): """Split a combined markdown file back into original files, restoring front matter.""" combined_text = Path(input_combined).read_text(encoding='utf-8') if not Path(metadata_file).exists(): print(f"Metadata file not found: {metadata_file}") sys.exit(1) meta_info = json.loads(Path(metadata_file).read_text(encoding='utf-8')) os.makedirs(output_dir, exist_ok=True) order = meta_info.get("order", []) frontmatters = meta_info.get("files", {}) # Split by H1 or H2 headings — each original file should start with an H1 or H2 heading. sections = re.split(r'(?=^(?:#{1,2})\s)', combined_text, flags=re.MULTILINE) sections = [s.strip() for s in sections if s.strip()] if len(sections) != len(order): raise Exception(f"{len(sections)} sections found but {len(order)} files listed.") else: chunks = sections for i, filename in enumerate(order): output_path = Path(output_dir, filename) body = chunks[i].strip() if i < len(chunks) else "" # Restore front matter if available front_matter = frontmatters.get(filename) if front_matter: content = f"---\n{front_matter}\n---\n\n{body}\n" else: content = body + "\n" write_with_safety(output_path, content, force=force, backup=backup) print(f"Split complete. {len(order)} files processed.") if __name__ == "__main__": if len(sys.argv) < 2: print("Usage:") print(" Combine: python manage_docs.py combine") print(" Split: python manage_docs.py split [--force|--backup]") sys.exit(1) command = sys.argv[1].lower() force = "--force" in sys.argv backup = "--backup" in sys.argv if command == "combine": combine_markdown(filenames, "stack_lang_spec.md", "metadata.json") elif command == "split": split_with_front_matter("stack_lang_spec.md", "docs", "metadata.json", force=force, backup=backup) else: print("Unknown command. Use 'combine' or 'split'.")