|
#!/usr/bin/env python3 |
|
import argparse |
|
import re |
|
import sys |
|
from pathlib import Path |
|
|
|
# Platzhalter-Syntax: {{ include:... }} |
|
PLACEHOLDER_RE = re.compile(r'{{\s*include\s*:\s*([^}]+?)\s*}}') |
|
|
|
def hydrate(prompt_path: Path, output_path: Path, src_dir_name: str) -> None: |
|
base_dir = prompt_path.parent |
|
src_dir = (base_dir / src_dir_name).resolve() |
|
text = prompt_path.read_text(encoding="utf-8") |
|
|
|
def resolve_include(rel_name: str) -> Path: |
|
rel_name = rel_name.strip() |
|
path = Path(rel_name) |
|
|
|
# 1. Absoluter Pfad? |
|
if path.is_absolute(): |
|
return path |
|
|
|
# 2. Pfad enthält bereits einen Slash → relativ zum Prompt-Verzeichnis |
|
if "/" in rel_name or "\\" in rel_name: |
|
return (base_dir / rel_name).resolve() |
|
|
|
# 3. Sonst: Datei im src_dir |
|
return (src_dir / rel_name).resolve() |
|
|
|
def replace(match: re.Match) -> str: |
|
rel_name = match.group(1).strip() |
|
file_path = resolve_include(rel_name) |
|
|
|
if not file_path.exists(): |
|
print( |
|
f"[WARN] Included file not found: {file_path} (from '{rel_name}')", |
|
file=sys.stderr, |
|
) |
|
return match.group(0) |
|
|
|
try: |
|
content = file_path.read_text(encoding="utf-8") |
|
except UnicodeDecodeError: |
|
print( |
|
f"[WARN] Could not read {file_path} as UTF-8. " |
|
"Leaving placeholder unchanged.", |
|
file=sys.stderr, |
|
) |
|
return match.group(0) |
|
|
|
# Für die Kommentare einen hübschen, relativen Pfad erzeugen |
|
try: |
|
rel_display = file_path.relative_to(base_dir) |
|
except ValueError: |
|
rel_display = file_path |
|
|
|
return ( |
|
f"\n<!-- BEGIN include:{rel_display} -->\n" |
|
f"{content.rstrip()}\n" |
|
f"<!-- END include:{rel_display} -->\n" |
|
) |
|
|
|
hydrated = PLACEHOLDER_RE.sub(replace, text) |
|
output_path.write_text(hydrated, encoding="utf-8") |
|
|
|
|
|
def main(): |
|
parser = argparse.ArgumentParser( |
|
description="Hydrate a prompt.md by inlining external Markdown snippets." |
|
) |
|
parser.add_argument( |
|
"prompt", |
|
nargs="?", |
|
default="prompt.md", |
|
help="Base prompt file (default: prompt.md)", |
|
) |
|
parser.add_argument( |
|
"-o", |
|
"--output", |
|
default=None, |
|
help="Output file (default: <prompt>.hydrated.md)", |
|
) |
|
parser.add_argument( |
|
"--src-dir", |
|
default="0000-source", |
|
help="Directory where included files live (default: 0000-source)", |
|
) |
|
|
|
args = parser.parse_args() |
|
prompt_path = Path(args.prompt).resolve() |
|
|
|
if not prompt_path.exists(): |
|
print(f"[ERROR] Prompt file not found: {prompt_path}", file=sys.stderr) |
|
raise SystemExit(1) |
|
|
|
if args.output: |
|
output_path = Path(args.output).resolve() |
|
else: |
|
# prompt.md → prompt.md.hydrated.md |
|
output_path = prompt_path.with_suffix(prompt_path.suffix + ".hydrated.md") |
|
|
|
hydrate(prompt_path, output_path, args.src_dir) |
|
|
|
print( |
|
f"[OK] Wrote hydrated prompt to: {output_path} " |
|
f"(src-dir: {args.src_dir})", |
|
file=sys.stderr, |
|
) |
|
|
|
|
|
if __name__ == "__main__": |
|
main() |