Files
zeekayandHanzo Dev 0671d5aad8 chore: rename the last live symbols, and let the rule see them
Four internal names, none of them on a wire:

  is_hog_identifier_kind    pub(crate) in the InsightsQL parser
  runHogliListUnits/DevApply unexported, in the phrocs TUI
  "Hogli's db:restore-..."   a CI comment
  ".TH HOGLI 1"              the generated man page's title

And the reason all four survived a rule that exists to refuse exactly
this: `HOGLI_` only matches when an underscore follows. The man page
says `HOGLI 1`, the TUI says `Hogli`, and `tools/insightscli/AGENTS.md`
wrote the heading as `HOGLI\_\*` -- markdown-escaped, so the backslash
sat between the letters and the underscore the rule was anchored on.
Its own body already said `INSIGHTSCLI_` throughout; only the heading
was stale.

So the rule takes the escaped form, the bare word, and the capitalized
one. Checked against `INSIGHTSCLI_DEBUG` and `hogline`, which must stay
unmatched.

Rust and Go renames are whole-word, with definitions and call sites
counted and no leftovers; neither toolchain is installed here, so that
count is the verification, not a compile.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-13 20:58:01 -07:00

131 lines
3.9 KiB
Python

#!/usr/bin/env python3
"""Generate the insightscli(1) man page from live CLI help output."""
from __future__ import annotations
import re
import sys
import argparse
from pathlib import Path
import click
from insightscli.cli import cli
ANSI_RE = re.compile(r"\x1B\[[0-9;]*[A-Za-z]")
TERMINAL_WIDTH = 120
def _parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(description="Generate insightscli(1) from the current CLI.")
parser.add_argument(
"--output",
type=Path,
help="Write the generated man page to this path. If omitted, print to stdout.",
)
return parser.parse_args()
def _strip_ansi(text: str) -> str:
return ANSI_RE.sub("", text)
def _escape_roff(line: str) -> str:
escaped = line.replace("\\", r"\\")
if escaped.startswith(".") or escaped.startswith("'"):
escaped = rf"\&{escaped}"
return escaped
def _collect_help_text() -> str:
ctx = click.Context(cli, info_name="insightscli", terminal_width=TERMINAL_WIDTH, color=False)
return _strip_ansi(cli.get_help(ctx)).strip()
def _collect_command_help() -> list[tuple[str, str]]:
root_ctx = click.Context(cli, info_name="insightscli", terminal_width=TERMINAL_WIDTH, color=False)
command_help_sections: list[tuple[str, str]] = []
for command_name in cli.list_commands(root_ctx):
command = cli.get_command(root_ctx, command_name)
if command is None or command.hidden:
continue
sub_ctx = click.Context(
command,
info_name=f"insightscli {command_name}",
parent=root_ctx,
terminal_width=TERMINAL_WIDTH,
color=False,
)
command_help_sections.append((command_name, _strip_ansi(command.get_help(sub_ctx)).strip()))
return command_help_sections
def _render(help_text: str, command_help_sections: list[tuple[str, str]]) -> str:
escaped_help_lines = [_escape_roff(line) for line in help_text.splitlines()]
lines = [
'.TH INSIGHTSCLI 1 "Auto-generated" "Insights" "User Commands"',
".SH NAME",
"insightscli \\- Insights developer CLI",
".SH SYNOPSIS",
".B insightscli",
"[\\fIOPTIONS\\fR] \\fICOMMAND\\fR [\\fIARGS\\fR]...",
".SH DESCRIPTION",
"\\fBinsightscli\\fR is the unified command line interface for Insights development workflows.",
"This page is generated from the current CLI help output.",
".SH HELP OUTPUT",
".nf",
*escaped_help_lines,
".fi",
]
if command_help_sections:
lines.append(".SH COMMAND HELP OUTPUT")
for command_name, command_help in command_help_sections:
lines.extend(
[
".SS \\fB" + _escape_roff(command_name) + "\\fR",
".nf",
*[_escape_roff(line) for line in command_help.splitlines()],
".fi",
]
)
lines.extend(
[
".SH FILES",
".TP",
"\\fIinsightscli.yaml\\fR",
"Command definitions and metadata.",
".TP",
"\\fItools/insightscli/src/insightscli/cli.py\\fR",
"CLI group and command registration.",
".TP",
"\\fItools/insightscli-commands/insightscli_commands/\\fR",
"Insights-specific lazy Click command modules.",
"",
]
)
return "\n".join(lines)
def _write(output_path: Path, content: str) -> None:
output_path.parent.mkdir(parents=True, exist_ok=True)
output_path.write_text(content, encoding="utf-8")
def main() -> int:
args = _parse_args()
content = _render(_collect_help_text(), _collect_command_help())
if not args.output:
sys.stdout.write(content)
return 0
_write(args.output, content)
return 0
if __name__ == "__main__":
raise SystemExit(main())