Return (fields, body). Tolerant YAML-ish parser; no external dep. Supports: - scalar fields (`name: foo`) - inline lists (`tools: [a, b]`) and block lists (key: \\n - a\\n - b) - YAML block scalar indicators `>` and `|` (folded/literal multi-line strings) - 2-space continuatio
(content: str)
| 34 | |
| 35 | |
| 36 | def parse_frontmatter(content: str) -> tuple[dict, str]: |
| 37 | """Return (fields, body). Tolerant YAML-ish parser; no external dep. |
| 38 | |
| 39 | Supports: |
| 40 | - scalar fields (`name: foo`) |
| 41 | - inline lists (`tools: [a, b]`) and block lists (key: \\n - a\\n - b) |
| 42 | - YAML block scalar indicators `>` and `|` (folded/literal multi-line strings) |
| 43 | - 2-space continuation of scalar values |
| 44 | """ |
| 45 | fields: dict = {} |
| 46 | if not content.startswith("---"): |
| 47 | return fields, content |
| 48 | |
| 49 | end = content.find("\n---", 3) |
| 50 | if end == -1: |
| 51 | return fields, content |
| 52 | |
| 53 | block = content[3:end].strip() |
| 54 | body = content[end + 4 :].lstrip("\n") |
| 55 | |
| 56 | current_key = None |
| 57 | in_list = False |
| 58 | in_block_scalar = False # True while inside `key: >` or `key: |` continuation |
| 59 | for line in block.splitlines(): |
| 60 | m = re.match(r"^(\w[\w-]*):\s*(.*)", line) |
| 61 | if m: |
| 62 | current_key = m.group(1) |
| 63 | val = m.group(2).strip() |
| 64 | # Handle YAML block scalar indicators (>, >-, |, |-) — value continues on |
| 65 | # following indented lines; we collapse to a single space-joined string. |
| 66 | if val in (">", ">-", "|", "|-"): |
| 67 | fields[current_key] = "" |
| 68 | in_block_scalar = True |
| 69 | in_list = False |
| 70 | continue |
| 71 | in_block_scalar = False |
| 72 | # Inline list syntax: `tools: [a, b, c]` (single-line, balanced brackets). |
| 73 | if val.startswith("[") and val.endswith("]"): |
| 74 | inner = val[1:-1].strip() |
| 75 | if not inner: |
| 76 | fields[current_key] = [] |
| 77 | else: |
| 78 | fields[current_key] = _split_inline_list(inner) |
| 79 | in_list = False |
| 80 | continue |
| 81 | val = val.strip('"') |
| 82 | if val == "[": |
| 83 | fields[current_key] = [] |
| 84 | in_list = True |
| 85 | elif val == "": |
| 86 | # Empty scalar OR start of a block list (`tools:\n - item`). Initialize |
| 87 | # as empty STRING so downstream string consumers don't crash; if a `- item` |
| 88 | # continuation appears next, the list branch will replace the value. |
| 89 | fields[current_key] = "" |
| 90 | in_list = True |
| 91 | else: |
| 92 | fields[current_key] = val |
| 93 | in_list = False |