gabriel / muse public
domain_info.py python
166 lines 4.9 KB
00373ad0 feat: migrate CLI from typer to argparse (POSIX-compliant, order-independent) Gabriel Cardona <gabriel@tellurstori.com> 1d ago
1 """muse plumbing domain-info — inspect the active domain plugin.
2
3 Reports which domain is active for this repository, which plugin class
4 implements it, what optional capabilities it exposes, and the full structural
5 schema it declares (merge mode, top-level element shape, dimensions).
6
7 Output (JSON, default)::
8
9 {
10 "domain": "midi",
11 "plugin_class": "MidiPlugin",
12 "capabilities": {
13 "structured_merge": true,
14 "crdt": false,
15 "rerere": false
16 },
17 "schema": {
18 "domain": "midi",
19 "description": "...",
20 "merge_mode": "three_way",
21 "schema_version": "0.x.y",
22 "top_level": { ... },
23 "dimensions": [ ... ]
24 },
25 "registered_domains": ["bitcoin", "code", "midi", "scaffold"]
26 }
27
28 Text output (``--format text``)::
29
30 Domain: midi
31 Plugin: MidiPlugin
32 Merge mode: three_way
33 Capabilities: structured_merge
34
35 Plumbing contract
36 -----------------
37
38 - Exit 0: domain resolved and schema emitted.
39 - Exit 1: no repository found; domain not registered; bad ``--format`` value.
40 - Exit 3: plugin raised an unexpected error when computing its schema.
41 """
42
43 from __future__ import annotations
44
45 import argparse
46 import json
47 import logging
48 import sys
49 from typing import TypedDict
50
51 from muse.core.errors import ExitCode
52 from muse.core.repo import require_repo
53 from muse.domain import CRDTPlugin, RererePlugin, StructuredMergePlugin
54 from muse.plugins.registry import read_domain, registered_domains, resolve_plugin
55
56 logger = logging.getLogger(__name__)
57
58 _FORMAT_CHOICES = ("json", "text")
59
60
61 class _CapabilitiesDict(TypedDict):
62 structured_merge: bool
63 crdt: bool
64 rerere: bool
65
66
67 def register(subparsers: "argparse._SubParsersAction[argparse.ArgumentParser]") -> None:
68 """Register the domain-info subcommand."""
69 parser = subparsers.add_parser(
70 "domain-info",
71 help="Inspect active domain plugin capabilities and schema.",
72 description=__doc__,
73 )
74 parser.add_argument(
75 "--format", "-f",
76 dest="fmt",
77 default="json",
78 metavar="FORMAT",
79 help="Output format: json or text. (default: json)",
80 )
81 parser.add_argument(
82 "--all-domains", "-a",
83 action="store_true",
84 dest="all_domains",
85 help="List every registered domain instead of querying the active repo.",
86 )
87 parser.set_defaults(func=run)
88
89
90 def run(args: argparse.Namespace) -> None:
91 """Inspect the domain plugin active for this repository.
92
93 Reports the domain name, plugin class, optional protocol capabilities
94 (``StructuredMergePlugin``, ``CRDTPlugin``, ``RererePlugin``), and the
95 full structural schema declared by the plugin.
96
97 Use ``--all-domains`` to enumerate every domain registered in this Muse
98 installation without requiring an active repository.
99 """
100 fmt: str = args.fmt
101 all_domains: bool = args.all_domains
102
103 if fmt not in _FORMAT_CHOICES:
104 print(
105 json.dumps(
106 {"error": f"Unknown format {fmt!r}. Valid: {', '.join(_FORMAT_CHOICES)}"}
107 )
108 )
109 raise SystemExit(ExitCode.USER_ERROR)
110
111 if all_domains:
112 domains = registered_domains()
113 if fmt == "text":
114 for d in domains:
115 print(d)
116 else:
117 print(json.dumps({"registered_domains": domains}))
118 return
119
120 root = require_repo()
121 domain = read_domain(root)
122
123 try:
124 plugin = resolve_plugin(root)
125 except Exception as exc:
126 print(json.dumps({"error": str(exc)}))
127 raise SystemExit(ExitCode.USER_ERROR)
128
129 plugin_class = type(plugin).__name__
130
131 capabilities: _CapabilitiesDict = {
132 "structured_merge": isinstance(plugin, StructuredMergePlugin),
133 "crdt": isinstance(plugin, CRDTPlugin),
134 "rerere": isinstance(plugin, RererePlugin),
135 }
136
137 try:
138 schema = plugin.schema()
139 except Exception as exc:
140 logger.debug("domain-info: plugin.schema() failed: %s", exc)
141 print(json.dumps({"error": f"Plugin schema error: {exc}"}))
142 raise SystemExit(ExitCode.INTERNAL_ERROR)
143
144 all_domains_list = registered_domains()
145
146 if fmt == "text":
147 print(f"Domain: {domain}")
148 print(f"Plugin: {plugin_class}")
149 print(f"Merge mode: {schema.get('merge_mode', 'unknown')}")
150 active_caps = [k for k, v in capabilities.items() if v]
151 cap_str = ", ".join(active_caps) if active_caps else "none"
152 print(f"Capabilities: {cap_str}")
153 print(f"Registered: {', '.join(all_domains_list)}")
154 return
155
156 print(
157 json.dumps(
158 {
159 "domain": domain,
160 "plugin_class": plugin_class,
161 "capabilities": dict(capabilities),
162 "schema": dict(schema),
163 "registered_domains": all_domains_list,
164 }
165 )
166 )