info-tech-canon/src/info_tech_canon/cli.py
tegwick b081d39da1
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 3s
Implement canon conformance and maintenance optimizations
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a06e82-3e08-7042-a79d-438ac6eed8db
2026-09-05 00:50:09 +02:00

279 lines
9 KiB
Python

from __future__ import annotations
import argparse
import json
import sys
from collections.abc import Callable
from pathlib import Path
from typing import Any
from .api import serve
from .service import (
CanonServiceError,
alignment_template,
artifact_graph,
generate_agent_briefs,
generate_indexes,
generate_tree,
inspect_canon,
list_artifacts,
list_models,
list_standards,
list_views,
profile_graph,
profile_inspect,
profile_validate,
read_view,
review_capability_record,
review_kit,
validate_canon,
write_validation_report,
)
Command = Callable[[argparse.Namespace], dict[str, Any]]
def build_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(prog="info-tech-canon")
parser.add_argument(
"--root",
default="",
help="Infospace root. Defaults to ./infospace from the repository root.",
)
sub = parser.add_subparsers(dest="command", required=True)
inspect = sub.add_parser("inspect", help="Inspect the canon infospace")
inspect.set_defaults(handler=_inspect)
artifacts = sub.add_parser("artifacts", help="List canon artifacts")
artifacts.add_argument("--kind", default="")
artifacts.set_defaults(handler=_artifacts)
models = sub.add_parser("models", help="List canon model artifacts")
models.set_defaults(handler=_models)
standards = sub.add_parser("standards", help="List canon standard artifacts")
standards.set_defaults(handler=_standards)
review_kit_cmd = sub.add_parser(
"review-kit",
help="Read the consumer repository alignment review kit",
)
review_kit_cmd.set_defaults(handler=_review_kit)
alignment_template_cmd = sub.add_parser(
"alignment-template",
help="Read the consumer alignment workplan template",
)
alignment_template_cmd.set_defaults(handler=_alignment_template)
validate = sub.add_parser("validate", help="Validate the canon infospace")
validate.add_argument(
"--write",
default="",
help="Write the JSON validation payload to this path.",
)
validate.set_defaults(handler=_validate)
index = sub.add_parser("index", help="Refresh generated indexes and views")
index.set_defaults(handler=_index)
tree = sub.add_parser("tree", help="Refresh the generated infospace tree")
tree.set_defaults(handler=_tree)
agent_briefs = sub.add_parser("agent-briefs", help="Refresh generated agent briefs")
agent_briefs.set_defaults(handler=_agent_briefs)
views = sub.add_parser("views", help="List or read generated views")
views.add_argument("name", nargs="?", default="")
views.set_defaults(handler=_views)
graph = sub.add_parser("graph", help="Export the canon artifact graph")
graph.add_argument("--format", choices=["json", "mermaid"], default="json")
graph.set_defaults(handler=_graph)
profile = sub.add_parser("profile", help="Inspect canon profiles")
profile_sub = profile.add_subparsers(dest="profile_command", required=True)
profile_inspect_cmd = profile_sub.add_parser("inspect", help="Inspect a profile")
profile_inspect_cmd.add_argument("profile")
profile_inspect_cmd.set_defaults(handler=_profile_inspect)
profile_validate_cmd = profile_sub.add_parser("validate", help="Validate a profile")
profile_validate_cmd.add_argument("profile")
profile_validate_cmd.set_defaults(handler=_profile_validate)
profile_graph_cmd = profile_sub.add_parser("graph", help="Export a profile graph")
profile_graph_cmd.add_argument("profile")
profile_graph_cmd.add_argument("--format", choices=["json", "mermaid"], default="json")
profile_graph_cmd.set_defaults(handler=_profile_graph)
capability_review = sub.add_parser(
"capability-review",
help="Review a consumer capability record against the live catalog",
)
capability_review.add_argument("record")
capability_review.set_defaults(handler=_capability_review)
emission = sub.add_parser("emission-review", help="Validate a source-owned YAML/JSON cadence declaration")
emission.add_argument("record")
emission.set_defaults(handler=_emission_review)
coverage = sub.add_parser("validation-coverage", help="List implemented checks and explicit limits")
coverage.set_defaults(handler=_coverage)
freshness = sub.add_parser("check-generated", help="Check projections without changing source files")
freshness.set_defaults(handler=_check_generated)
inventory = sub.add_parser("scope-inventory", help="Derive scope counts from the artifact registry")
inventory.set_defaults(handler=_scope_inventory)
bundle = sub.add_parser("export-emission-contract", help="Export a content-addressed contract tar")
bundle.add_argument("destination")
bundle.set_defaults(handler=_export_emission)
benchmark = sub.add_parser("benchmark-reads", help="Measure ten uncached inspect calls")
benchmark.set_defaults(handler=_benchmark_reads)
api = sub.add_parser("api", help="Run the read-only local API")
api.add_argument("--host", default="127.0.0.1")
api.add_argument("--port", type=int, default=8765)
api.set_defaults(handler=_api)
return parser
def main(argv: list[str] | None = None) -> int:
parser = build_parser()
args = parser.parse_args(argv)
handler: Command = args.handler
try:
result = handler(args)
except CanonServiceError as exc:
_print_json(exc.to_dict())
return 2
except Exception as exc:
_print_json(
{
"ok": False,
"error": {
"code": "unhandled_error",
"message": str(exc),
"details": {},
},
}
)
return 1
if result:
_print_json(result)
return 0 if result.get("ok", False) else 1
def _root(args: argparse.Namespace) -> Path | None:
return Path(args.root) if args.root else None
def _inspect(args: argparse.Namespace) -> dict[str, Any]:
return inspect_canon(_root(args))
def _artifacts(args: argparse.Namespace) -> dict[str, Any]:
return list_artifacts(_root(args), kind=args.kind or None)
def _models(args: argparse.Namespace) -> dict[str, Any]:
return list_models(_root(args))
def _standards(args: argparse.Namespace) -> dict[str, Any]:
return list_standards(_root(args))
def _review_kit(args: argparse.Namespace) -> dict[str, Any]:
return review_kit(_root(args))
def _alignment_template(args: argparse.Namespace) -> dict[str, Any]:
return alignment_template(_root(args))
def _validate(args: argparse.Namespace) -> dict[str, Any]:
if args.write:
return write_validation_report(args.write, _root(args))
return validate_canon(_root(args))
def _index(args: argparse.Namespace) -> dict[str, Any]:
return generate_indexes(_root(args))
def _tree(args: argparse.Namespace) -> dict[str, Any]:
return generate_tree(_root(args))
def _agent_briefs(args: argparse.Namespace) -> dict[str, Any]:
return generate_agent_briefs(_root(args))
def _views(args: argparse.Namespace) -> dict[str, Any]:
if args.name:
return read_view(args.name, _root(args))
return list_views(_root(args))
def _graph(args: argparse.Namespace) -> dict[str, Any]:
return artifact_graph(_root(args), output_format=args.format)
def _capability_review(args: argparse.Namespace) -> dict[str, Any]:
return review_capability_record(args.record, _root(args))
def _emission_review(args):
from .contracts import review_emission
return review_emission(args.record, _root(args))
def _coverage(args):
from .contracts import coverage
from .service import load_context
return coverage(load_context(_root(args)))
def _check_generated(args):
from .maintenance import check_generated
from .service import load_context
return check_generated(load_context(_root(args)))
def _scope_inventory(args):
from .maintenance import scope_inventory
from .service import load_context
return dict(scope_inventory(load_context(_root(args))), ok=True)
def _export_emission(args):
from .maintenance import export_emission_bundle
from .paths import infospace_root
return export_emission_bundle(infospace_root(_root(args)), Path(args.destination))
def _benchmark_reads(args):
from .maintenance import benchmark_reads
from .paths import infospace_root
return benchmark_reads(infospace_root(_root(args)))
def _profile_inspect(args: argparse.Namespace) -> dict[str, Any]:
return profile_inspect(args.profile, _root(args))
def _profile_validate(args: argparse.Namespace) -> dict[str, Any]:
return profile_validate(args.profile, _root(args))
def _profile_graph(args: argparse.Namespace) -> dict[str, Any]:
return profile_graph(args.profile, _root(args), output_format=args.format)
def _api(args: argparse.Namespace) -> dict[str, Any]:
serve(host=args.host, port=args.port, root=_root(args))
return {}
def _print_json(data: dict[str, Any]) -> None:
json.dump(data, sys.stdout, indent=2, sort_keys=True)
sys.stdout.write("\n")