robot-md 0.1.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
robot_md/__init__.py ADDED
@@ -0,0 +1,3 @@
1
+ """robot-md — parse, validate, and render ROBOT.md files."""
2
+
3
+ __version__ = "0.1.3"
robot_md/__main__.py ADDED
@@ -0,0 +1,133 @@
1
+ """robot-md CLI entry point — typer-based."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import sys
6
+ from pathlib import Path
7
+
8
+ import typer
9
+ from rich.console import Console
10
+
11
+ from robot_md import __version__
12
+ from robot_md.autodetect import emit_draft, scan_system
13
+ from robot_md.context import emit_context
14
+ from robot_md.parser import ParseError, parse_file
15
+ from robot_md.render import render_yaml
16
+ from robot_md.validate import (
17
+ FILE_ERROR,
18
+ MISSING_BODY_SECTION,
19
+ RCAN_CONFORMANCE_VIOLATION,
20
+ SCHEMA_VIOLATION,
21
+ VALID,
22
+ )
23
+ from robot_md.validate import (
24
+ validate as validate_parsed,
25
+ )
26
+
27
+ app = typer.Typer(
28
+ name="robot-md",
29
+ help="Parse, validate, and render ROBOT.md files.",
30
+ no_args_is_help=True,
31
+ add_completion=False,
32
+ )
33
+ err_console = Console(stderr=True)
34
+ out_console = Console()
35
+
36
+
37
+ def _version_callback(value: bool) -> None:
38
+ if value:
39
+ typer.echo(f"robot-md {__version__}")
40
+ raise typer.Exit()
41
+
42
+
43
+ @app.callback()
44
+ def main(
45
+ version: bool = typer.Option(
46
+ False,
47
+ "--version",
48
+ callback=_version_callback,
49
+ is_eager=True,
50
+ help="Show the version and exit.",
51
+ ),
52
+ ) -> None:
53
+ pass
54
+
55
+
56
+ @app.command()
57
+ def validate(path: Path = typer.Argument(..., help="Path to a ROBOT.md file.")) -> None:
58
+ """Validate a ROBOT.md file against schema + body requirements."""
59
+ try:
60
+ parsed = parse_file(path)
61
+ except ParseError as e:
62
+ err_console.print(f"[red]✗[/red] {e}")
63
+ raise typer.Exit(code=FILE_ERROR) from None
64
+
65
+ result = validate_parsed(parsed)
66
+ if result.code == VALID:
67
+ out_console.print(f"[green]✓[/green] {result.summary}")
68
+ raise typer.Exit(code=VALID)
69
+
70
+ severity = {
71
+ SCHEMA_VIOLATION: ("schema violation", "red"),
72
+ RCAN_CONFORMANCE_VIOLATION: ("RCAN conformance violation", "red"),
73
+ MISSING_BODY_SECTION: ("missing body section", "yellow"),
74
+ }.get(result.code, (f"error (code {result.code})", "red"))
75
+
76
+ err_console.print(f"[{severity[1]}]✗ {severity[0]}[/{severity[1]}]")
77
+ for msg in result.errors:
78
+ err_console.print(f" - {msg}")
79
+ raise typer.Exit(code=result.code)
80
+
81
+
82
+ @app.command()
83
+ def render(path: Path = typer.Argument(..., help="Path to a ROBOT.md file.")) -> None:
84
+ """Strip the prose body and emit the frontmatter as pure YAML to stdout."""
85
+ try:
86
+ parsed = parse_file(path)
87
+ except ParseError as e:
88
+ err_console.print(f"[red]✗[/red] {e}")
89
+ raise typer.Exit(code=FILE_ERROR) from None
90
+ sys.stdout.write(render_yaml(parsed))
91
+
92
+
93
+ @app.command()
94
+ def context(path: Path = typer.Argument(..., help="Path to a ROBOT.md file.")) -> None:
95
+ """Emit a Claude-ready context block (markdown) to stdout."""
96
+ try:
97
+ parsed = parse_file(path)
98
+ except ParseError as e:
99
+ err_console.print(f"[red]✗[/red] {e}")
100
+ raise typer.Exit(code=FILE_ERROR) from None
101
+ sys.stdout.write(emit_context(parsed))
102
+
103
+
104
+ @app.command()
105
+ def autodetect(
106
+ write: Path | None = typer.Option(
107
+ None,
108
+ "--write",
109
+ "-w",
110
+ help="Write draft to this path (refuses to overwrite). If omitted, prints to stdout.",
111
+ ),
112
+ ) -> None:
113
+ """Scan visible hardware and emit a draft ROBOT.md.
114
+
115
+ Linux-only. Covers PCI (via lspci), USB (via lsusb), /dev/tty[ACM|USB]*,
116
+ and runtime info. Emitted draft has TODO markers for identity fields —
117
+ review before committing. Always run `robot-md validate` afterwards.
118
+ """
119
+ scan = scan_system()
120
+ draft = emit_draft(scan)
121
+ if write is None:
122
+ sys.stdout.write(draft)
123
+ return
124
+ if write.exists():
125
+ err_console.print(f"[red]✗[/red] {write} already exists — refusing to overwrite")
126
+ raise typer.Exit(code=FILE_ERROR)
127
+ write.write_text(draft)
128
+ out_console.print(f"[green]✓[/green] wrote draft to {write}")
129
+ out_console.print(f" next: edit the TODOs, then `robot-md validate {write}`")
130
+
131
+
132
+ if __name__ == "__main__":
133
+ app()