| 1 | #!/usr/bin/env python3 |
| 2 | """One-shot: [[melody_root]]+[[slash_route]] -> [[command]] with nested forms.""" |
| 3 | from __future__ import annotations |
| 4 | |
| 5 | import re |
| 6 | from pathlib import Path |
| 7 | |
| 8 | ROOT = Path(__file__).resolve().parents[1] / "IntentMelody" / "intent-catalog.toml" |
| 9 | |
| 10 | |
| 11 | def parse_tables(text: str, name: str) -> list[dict[str, str]]: |
| 12 | blocks: list[dict[str, str]] = [] |
| 13 | current: dict[str, str] | None = None |
| 14 | header = re.compile(rf"^\[\[{re.escape(name)}\]\]\s*$") |
| 15 | for line in text.splitlines(): |
| 16 | if header.match(line.strip()): |
| 17 | if current: |
| 18 | blocks.append(current) |
| 19 | current = {} |
| 20 | continue |
| 21 | if current is None: |
| 22 | continue |
| 23 | m = re.match(r'^([a-z_]+)\s*=\s*"(.*)"\s*$', line.strip()) |
| 24 | if m: |
| 25 | current[m.group(1)] = m.group(2) |
| 26 | elif line.strip().startswith("#") or not line.strip(): |
| 27 | pass |
| 28 | elif line.strip().startswith("["): |
| 29 | if current: |
| 30 | blocks.append(current) |
| 31 | current = None |
| 32 | if current: |
| 33 | blocks.append(current) |
| 34 | return blocks |
| 35 | |
| 36 | |
| 37 | def main() -> None: |
| 38 | text = ROOT.read_text(encoding="utf-8") |
| 39 | tail_section = text.split("# ------------------------------------------------------------------------------", 1)[0] |
| 40 | tail_lines = [] |
| 41 | in_tail = False |
| 42 | for line in text.splitlines(): |
| 43 | if "[[tail_wire_class]]" in line: |
| 44 | in_tail = True |
| 45 | if in_tail and line.startswith("[[melody_root]]"): |
| 46 | break |
| 47 | if in_tail: |
| 48 | tail_lines.append(line) |
| 49 | |
| 50 | melodies = parse_tables(text, "melody_root") |
| 51 | slashes = parse_tables(text, "slash_route") |
| 52 | |
| 53 | by_cmd: dict[str, dict] = {} |
| 54 | for m in melodies: |
| 55 | cid = m.get("command_id", "") |
| 56 | if not cid: |
| 57 | continue |
| 58 | by_cmd.setdefault(cid, {"melody": m, "slashes": []}) |
| 59 | |
| 60 | for s in slashes: |
| 61 | cid = s.get("command_id", "") |
| 62 | if s.get("kind") == "help": |
| 63 | by_cmd.setdefault("__local_help__", {"melody": None, "slashes": []}) |
| 64 | by_cmd["__local_help__"]["slashes"].append(s) |
| 65 | elif cid: |
| 66 | by_cmd.setdefault(cid, {"melody": None, "slashes": []}) |
| 67 | by_cmd[cid]["slashes"].append(s) |
| 68 | else: |
| 69 | by_cmd.setdefault(cid or "__no_cmd__", {"melody": None, "slashes": []}) |
| 70 | by_cmd[cid or "__no_cmd__"]["slashes"].append(s) |
| 71 | |
| 72 | out: list[str] = [ |
| 73 | "# Единый intent-каталог: якорь command_id, формы melody (c:) и slash (/).", |
| 74 | "# Оверлей: IntentMelody/intent-catalog.toml рядом с exe.", |
| 75 | "# ADR 0109, 0119.", |
| 76 | "", |
| 77 | "intent_catalog_schema_version = 1", |
| 78 | "", |
| 79 | ] |
| 80 | out.extend(tail_lines) |
| 81 | out.append("") |
| 82 | |
| 83 | for cid in sorted(by_cmd.keys(), key=lambda x: (x == "__local_help__", x)): |
| 84 | entry = by_cmd[cid] |
| 85 | m = entry.get("melody") |
| 86 | slashes = entry.get("slashes") or [] |
| 87 | if cid == "__local_help__": |
| 88 | out.append("[[command]]") |
| 89 | for s in slashes: |
| 90 | out.extend(emit_slash(s)) |
| 91 | out.append("") |
| 92 | continue |
| 93 | |
| 94 | out.append("[[command]]") |
| 95 | out.append(f'command_id = "{cid}"') |
| 96 | if m: |
| 97 | out.append("") |
| 98 | out.append("[command.melody]") |
| 99 | for key in ( |
| 100 | "slug", |
| 101 | "shape", |
| 102 | "show_usage_hint_if_bare_slug", |
| 103 | "tail_signature", |
| 104 | "wire_class", |
| 105 | "chord_commit", |
| 106 | "palette_hint_slug", |
| 107 | "palette_usage_hint", |
| 108 | "palette_usage_category", |
| 109 | ): |
| 110 | if key in m: |
| 111 | val = m[key] |
| 112 | if key == "show_usage_hint_if_bare_slug": |
| 113 | out.append(f"{key} = {val}") |
| 114 | else: |
| 115 | out.append(f'{key} = "{val}"') |
| 116 | for s in slashes: |
| 117 | out.append("") |
| 118 | out.extend(emit_slash(s)) |
| 119 | out.append("") |
| 120 | |
| 121 | ROOT.write_text("\n".join(out).rstrip() + "\n", encoding="utf-8") |
| 122 | print(f"Wrote {ROOT} ({len(by_cmd)} commands)") |
| 123 | |
| 124 | |
| 125 | def emit_slash(s: dict[str, str]) -> list[str]: |
| 126 | lines = ["[[command.slash]]"] |
| 127 | for key in ("path", "help", "group", "mfd_page", "primary_surface", "kind"): |
| 128 | if key in s: |
| 129 | lines.append(f'{key} = "{s[key]}"') |
| 130 | return lines |
| 131 | |
| 132 | |
| 133 | if __name__ == "__main__": |
| 134 | main() |
| 135 | |