What
ward scan now renders a clean, colored table in a terminal (via cortexward.cli.console), but ward threat-model still only prints raw JSON. Give it the same treatment.
Suggested scope
- When output is an interactive terminal (reuse
cortexward.cli.console.should_decorate()), render the STRIDE threats as a readable table — e.g. columns for STRIDE category, CWE, location, reachable-from-entrypoint, crosses-trust-boundary.
- Keep the machine contract: JSON stays the default when piped or written with
--output, exactly like ward scan's --format auto behavior. Don't break ward threat-model . > model.json.
- Add a
console.py render helper + tests (both the decorated and plain paths), matching the 100% coverage bar.
Why it's a good first issue
Self-contained, follows an existing pattern you can copy from scan, and touches only the CLI layer.
Pointers
- Command:
threat_model() in packages/cortexward-cli/src/cortexward/cli/main.py
- Styling helpers to reuse:
packages/cortexward-cli/src/cortexward/cli/console.py
- The model shape:
cortexward.domain.threat_model (ThreatModel, Threat)
What
ward scannow renders a clean, colored table in a terminal (viacortexward.cli.console), butward threat-modelstill only prints raw JSON. Give it the same treatment.Suggested scope
cortexward.cli.console.should_decorate()), render the STRIDE threats as a readable table — e.g. columns for STRIDE category, CWE, location, reachable-from-entrypoint, crosses-trust-boundary.--output, exactly likeward scan's--format autobehavior. Don't breakward threat-model . > model.json.console.pyrender helper + tests (both the decorated and plain paths), matching the 100% coverage bar.Why it's a good first issue
Self-contained, follows an existing pattern you can copy from
scan, and touches only the CLI layer.Pointers
threat_model()inpackages/cortexward-cli/src/cortexward/cli/main.pypackages/cortexward-cli/src/cortexward/cli/console.pycortexward.domain.threat_model(ThreatModel,Threat)