raise-cli 2.2.1__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.
- raise_cli/__init__.py +38 -0
- raise_cli/__main__.py +30 -0
- raise_cli/adapters/__init__.py +91 -0
- raise_cli/adapters/declarative/__init__.py +26 -0
- raise_cli/adapters/declarative/adapter.py +267 -0
- raise_cli/adapters/declarative/discovery.py +94 -0
- raise_cli/adapters/declarative/expressions.py +150 -0
- raise_cli/adapters/declarative/reference/__init__.py +1 -0
- raise_cli/adapters/declarative/reference/github.yaml +143 -0
- raise_cli/adapters/declarative/schema.py +98 -0
- raise_cli/adapters/filesystem.py +299 -0
- raise_cli/adapters/mcp_bridge.py +10 -0
- raise_cli/adapters/mcp_confluence.py +246 -0
- raise_cli/adapters/mcp_jira.py +405 -0
- raise_cli/adapters/models.py +205 -0
- raise_cli/adapters/protocols.py +180 -0
- raise_cli/adapters/registry.py +90 -0
- raise_cli/adapters/sync.py +149 -0
- raise_cli/agents/__init__.py +14 -0
- raise_cli/agents/antigravity.yaml +8 -0
- raise_cli/agents/claude.yaml +8 -0
- raise_cli/agents/copilot.yaml +8 -0
- raise_cli/agents/copilot_plugin.py +124 -0
- raise_cli/agents/cursor.yaml +7 -0
- raise_cli/agents/roo.yaml +8 -0
- raise_cli/agents/windsurf.yaml +8 -0
- raise_cli/artifacts/__init__.py +30 -0
- raise_cli/artifacts/models.py +43 -0
- raise_cli/artifacts/reader.py +55 -0
- raise_cli/artifacts/renderer.py +104 -0
- raise_cli/artifacts/story_design.py +69 -0
- raise_cli/artifacts/writer.py +45 -0
- raise_cli/backlog/__init__.py +1 -0
- raise_cli/backlog/sync.py +115 -0
- raise_cli/cli/__init__.py +3 -0
- raise_cli/cli/commands/__init__.py +3 -0
- raise_cli/cli/commands/_resolve.py +153 -0
- raise_cli/cli/commands/adapters.py +362 -0
- raise_cli/cli/commands/artifact.py +137 -0
- raise_cli/cli/commands/backlog.py +333 -0
- raise_cli/cli/commands/base.py +31 -0
- raise_cli/cli/commands/discover.py +551 -0
- raise_cli/cli/commands/docs.py +130 -0
- raise_cli/cli/commands/doctor.py +177 -0
- raise_cli/cli/commands/gate.py +223 -0
- raise_cli/cli/commands/graph.py +1086 -0
- raise_cli/cli/commands/info.py +81 -0
- raise_cli/cli/commands/init.py +746 -0
- raise_cli/cli/commands/journal.py +167 -0
- raise_cli/cli/commands/mcp.py +524 -0
- raise_cli/cli/commands/memory.py +467 -0
- raise_cli/cli/commands/pattern.py +348 -0
- raise_cli/cli/commands/profile.py +59 -0
- raise_cli/cli/commands/publish.py +80 -0
- raise_cli/cli/commands/release.py +338 -0
- raise_cli/cli/commands/session.py +528 -0
- raise_cli/cli/commands/signal.py +410 -0
- raise_cli/cli/commands/skill.py +350 -0
- raise_cli/cli/commands/skill_set.py +145 -0
- raise_cli/cli/error_handler.py +158 -0
- raise_cli/cli/main.py +163 -0
- raise_cli/compat.py +66 -0
- raise_cli/config/__init__.py +41 -0
- raise_cli/config/agent_plugin.py +105 -0
- raise_cli/config/agent_registry.py +233 -0
- raise_cli/config/agents.py +120 -0
- raise_cli/config/ide.py +32 -0
- raise_cli/config/paths.py +379 -0
- raise_cli/config/settings.py +180 -0
- raise_cli/context/__init__.py +42 -0
- raise_cli/context/analyzers/__init__.py +16 -0
- raise_cli/context/analyzers/models.py +36 -0
- raise_cli/context/analyzers/protocol.py +43 -0
- raise_cli/context/analyzers/python.py +292 -0
- raise_cli/context/builder.py +1569 -0
- raise_cli/context/diff.py +213 -0
- raise_cli/context/extractors/__init__.py +13 -0
- raise_cli/context/extractors/skills.py +121 -0
- raise_cli/core/__init__.py +37 -0
- raise_cli/core/files.py +66 -0
- raise_cli/core/text.py +174 -0
- raise_cli/core/tools.py +441 -0
- raise_cli/discovery/__init__.py +50 -0
- raise_cli/discovery/analyzer.py +691 -0
- raise_cli/discovery/drift.py +355 -0
- raise_cli/discovery/scanner.py +1687 -0
- raise_cli/doctor/__init__.py +4 -0
- raise_cli/doctor/checks/__init__.py +1 -0
- raise_cli/doctor/checks/environment.py +110 -0
- raise_cli/doctor/checks/project.py +238 -0
- raise_cli/doctor/fix.py +80 -0
- raise_cli/doctor/models.py +56 -0
- raise_cli/doctor/protocol.py +43 -0
- raise_cli/doctor/registry.py +100 -0
- raise_cli/doctor/report.py +141 -0
- raise_cli/doctor/runner.py +95 -0
- raise_cli/engines/__init__.py +3 -0
- raise_cli/exceptions.py +215 -0
- raise_cli/gates/__init__.py +19 -0
- raise_cli/gates/builtin/__init__.py +1 -0
- raise_cli/gates/builtin/coverage.py +52 -0
- raise_cli/gates/builtin/lint.py +48 -0
- raise_cli/gates/builtin/tests.py +48 -0
- raise_cli/gates/builtin/types.py +48 -0
- raise_cli/gates/models.py +40 -0
- raise_cli/gates/protocol.py +41 -0
- raise_cli/gates/registry.py +141 -0
- raise_cli/governance/__init__.py +11 -0
- raise_cli/governance/extractor.py +412 -0
- raise_cli/governance/models.py +134 -0
- raise_cli/governance/parsers/__init__.py +35 -0
- raise_cli/governance/parsers/_convert.py +38 -0
- raise_cli/governance/parsers/adr.py +274 -0
- raise_cli/governance/parsers/backlog.py +356 -0
- raise_cli/governance/parsers/constitution.py +119 -0
- raise_cli/governance/parsers/epic.py +323 -0
- raise_cli/governance/parsers/glossary.py +316 -0
- raise_cli/governance/parsers/guardrails.py +345 -0
- raise_cli/governance/parsers/prd.py +112 -0
- raise_cli/governance/parsers/roadmap.py +118 -0
- raise_cli/governance/parsers/vision.py +116 -0
- raise_cli/graph/__init__.py +1 -0
- raise_cli/graph/backends/__init__.py +57 -0
- raise_cli/graph/backends/api.py +137 -0
- raise_cli/graph/backends/dual.py +139 -0
- raise_cli/graph/backends/pending.py +84 -0
- raise_cli/handlers/__init__.py +3 -0
- raise_cli/hooks/__init__.py +54 -0
- raise_cli/hooks/builtin/__init__.py +1 -0
- raise_cli/hooks/builtin/backlog.py +216 -0
- raise_cli/hooks/builtin/gate_bridge.py +83 -0
- raise_cli/hooks/builtin/jira_sync.py +127 -0
- raise_cli/hooks/builtin/memory.py +117 -0
- raise_cli/hooks/builtin/telemetry.py +72 -0
- raise_cli/hooks/emitter.py +184 -0
- raise_cli/hooks/events.py +262 -0
- raise_cli/hooks/protocol.py +38 -0
- raise_cli/hooks/registry.py +117 -0
- raise_cli/mcp/__init__.py +33 -0
- raise_cli/mcp/bridge.py +218 -0
- raise_cli/mcp/models.py +43 -0
- raise_cli/mcp/registry.py +77 -0
- raise_cli/mcp/schema.py +41 -0
- raise_cli/memory/__init__.py +58 -0
- raise_cli/memory/loader.py +247 -0
- raise_cli/memory/migration.py +241 -0
- raise_cli/memory/models.py +169 -0
- raise_cli/memory/writer.py +598 -0
- raise_cli/onboarding/__init__.py +103 -0
- raise_cli/onboarding/bootstrap.py +324 -0
- raise_cli/onboarding/claudemd.py +17 -0
- raise_cli/onboarding/conventions.py +742 -0
- raise_cli/onboarding/detection.py +374 -0
- raise_cli/onboarding/governance.py +443 -0
- raise_cli/onboarding/instructions.py +672 -0
- raise_cli/onboarding/manifest.py +201 -0
- raise_cli/onboarding/memory_md.py +399 -0
- raise_cli/onboarding/migration.py +207 -0
- raise_cli/onboarding/profile.py +624 -0
- raise_cli/onboarding/skill_conflict.py +100 -0
- raise_cli/onboarding/skill_manifest.py +176 -0
- raise_cli/onboarding/skills.py +437 -0
- raise_cli/onboarding/workflows.py +101 -0
- raise_cli/output/__init__.py +28 -0
- raise_cli/output/console.py +394 -0
- raise_cli/output/formatters/__init__.py +9 -0
- raise_cli/output/formatters/adapters.py +135 -0
- raise_cli/output/formatters/discover.py +439 -0
- raise_cli/output/formatters/skill.py +298 -0
- raise_cli/publish/__init__.py +3 -0
- raise_cli/publish/changelog.py +80 -0
- raise_cli/publish/check.py +179 -0
- raise_cli/publish/version.py +172 -0
- raise_cli/rai_base/__init__.py +22 -0
- raise_cli/rai_base/framework/__init__.py +7 -0
- raise_cli/rai_base/framework/methodology.yaml +233 -0
- raise_cli/rai_base/governance/__init__.py +1 -0
- raise_cli/rai_base/governance/architecture/__init__.py +1 -0
- raise_cli/rai_base/governance/architecture/domain-model.md +20 -0
- raise_cli/rai_base/governance/architecture/system-context.md +34 -0
- raise_cli/rai_base/governance/architecture/system-design.md +24 -0
- raise_cli/rai_base/governance/backlog.md +8 -0
- raise_cli/rai_base/governance/guardrails.md +17 -0
- raise_cli/rai_base/governance/prd.md +25 -0
- raise_cli/rai_base/governance/vision.md +16 -0
- raise_cli/rai_base/identity/__init__.py +8 -0
- raise_cli/rai_base/identity/core.md +119 -0
- raise_cli/rai_base/identity/perspective.md +119 -0
- raise_cli/rai_base/memory/__init__.py +7 -0
- raise_cli/rai_base/memory/patterns-base.jsonl +55 -0
- raise_cli/schemas/__init__.py +3 -0
- raise_cli/schemas/journal.py +49 -0
- raise_cli/schemas/session_state.py +117 -0
- raise_cli/session/__init__.py +5 -0
- raise_cli/session/bundle.py +820 -0
- raise_cli/session/close.py +268 -0
- raise_cli/session/journal.py +119 -0
- raise_cli/session/resolver.py +126 -0
- raise_cli/session/state.py +187 -0
- raise_cli/skills/__init__.py +44 -0
- raise_cli/skills/locator.py +141 -0
- raise_cli/skills/name_checker.py +199 -0
- raise_cli/skills/parser.py +145 -0
- raise_cli/skills/scaffold.py +212 -0
- raise_cli/skills/schema.py +132 -0
- raise_cli/skills/skillsets.py +195 -0
- raise_cli/skills/validator.py +197 -0
- raise_cli/skills_base/__init__.py +80 -0
- raise_cli/skills_base/contract-template.md +60 -0
- raise_cli/skills_base/preamble.md +37 -0
- raise_cli/skills_base/rai-architecture-review/SKILL.md +137 -0
- raise_cli/skills_base/rai-debug/SKILL.md +171 -0
- raise_cli/skills_base/rai-discover/SKILL.md +167 -0
- raise_cli/skills_base/rai-discover-document/SKILL.md +128 -0
- raise_cli/skills_base/rai-discover-scan/SKILL.md +147 -0
- raise_cli/skills_base/rai-discover-start/SKILL.md +145 -0
- raise_cli/skills_base/rai-discover-validate/SKILL.md +142 -0
- raise_cli/skills_base/rai-docs-update/SKILL.md +142 -0
- raise_cli/skills_base/rai-doctor/SKILL.md +120 -0
- raise_cli/skills_base/rai-epic-close/SKILL.md +165 -0
- raise_cli/skills_base/rai-epic-close/templates/retrospective.md +68 -0
- raise_cli/skills_base/rai-epic-design/SKILL.md +146 -0
- raise_cli/skills_base/rai-epic-design/templates/design.md +24 -0
- raise_cli/skills_base/rai-epic-design/templates/scope.md +76 -0
- raise_cli/skills_base/rai-epic-plan/SKILL.md +153 -0
- raise_cli/skills_base/rai-epic-plan/_references/sequencing-strategies.md +67 -0
- raise_cli/skills_base/rai-epic-plan/templates/plan-section.md +49 -0
- raise_cli/skills_base/rai-epic-run/SKILL.md +208 -0
- raise_cli/skills_base/rai-epic-start/SKILL.md +136 -0
- raise_cli/skills_base/rai-epic-start/templates/brief.md +34 -0
- raise_cli/skills_base/rai-mcp-add/SKILL.md +176 -0
- raise_cli/skills_base/rai-mcp-remove/SKILL.md +120 -0
- raise_cli/skills_base/rai-mcp-status/SKILL.md +147 -0
- raise_cli/skills_base/rai-problem-shape/SKILL.md +138 -0
- raise_cli/skills_base/rai-project-create/SKILL.md +144 -0
- raise_cli/skills_base/rai-project-onboard/SKILL.md +162 -0
- raise_cli/skills_base/rai-quality-review/SKILL.md +189 -0
- raise_cli/skills_base/rai-research/SKILL.md +143 -0
- raise_cli/skills_base/rai-research/references/research-prompt-template.md +317 -0
- raise_cli/skills_base/rai-session-close/SKILL.md +176 -0
- raise_cli/skills_base/rai-session-start/SKILL.md +110 -0
- raise_cli/skills_base/rai-story-close/SKILL.md +198 -0
- raise_cli/skills_base/rai-story-design/SKILL.md +203 -0
- raise_cli/skills_base/rai-story-design/references/tech-design-story-v2.md +293 -0
- raise_cli/skills_base/rai-story-implement/SKILL.md +115 -0
- raise_cli/skills_base/rai-story-plan/SKILL.md +135 -0
- raise_cli/skills_base/rai-story-review/SKILL.md +178 -0
- raise_cli/skills_base/rai-story-run/SKILL.md +282 -0
- raise_cli/skills_base/rai-story-start/SKILL.md +166 -0
- raise_cli/skills_base/rai-story-start/templates/story.md +38 -0
- raise_cli/skills_base/rai-welcome/SKILL.md +134 -0
- raise_cli/telemetry/__init__.py +42 -0
- raise_cli/telemetry/schemas.py +285 -0
- raise_cli/telemetry/writer.py +217 -0
- raise_cli/tier/__init__.py +0 -0
- raise_cli/tier/context.py +134 -0
- raise_cli/viz/__init__.py +7 -0
- raise_cli/viz/generator.py +406 -0
- raise_cli-2.2.1.dist-info/METADATA +433 -0
- raise_cli-2.2.1.dist-info/RECORD +264 -0
- raise_cli-2.2.1.dist-info/WHEEL +4 -0
- raise_cli-2.2.1.dist-info/entry_points.txt +40 -0
- raise_cli-2.2.1.dist-info/licenses/LICENSE +190 -0
- raise_cli-2.2.1.dist-info/licenses/NOTICE +4 -0
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
"""Output console with format-aware printing.
|
|
2
|
+
|
|
3
|
+
This module provides a unified output interface that respects the --format flag,
|
|
4
|
+
supporting human-readable (Rich), JSON, and table formats.
|
|
5
|
+
|
|
6
|
+
Example:
|
|
7
|
+
>>> from raise_cli.output import get_console
|
|
8
|
+
>>>
|
|
9
|
+
>>> console = get_console()
|
|
10
|
+
>>> console.print_message("Processing...")
|
|
11
|
+
>>> console.print_success("Done!", details={"duration": "2.3s"})
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
import json
|
|
17
|
+
from typing import TYPE_CHECKING, Any, Literal, cast
|
|
18
|
+
|
|
19
|
+
from rich.console import Console
|
|
20
|
+
from rich.table import Table
|
|
21
|
+
from rich.tree import Tree
|
|
22
|
+
|
|
23
|
+
if TYPE_CHECKING:
|
|
24
|
+
from collections.abc import Sequence
|
|
25
|
+
|
|
26
|
+
# Output format type
|
|
27
|
+
OutputFormat = Literal["human", "json", "table"]
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class OutputConsole:
|
|
31
|
+
"""Output abstraction that respects --format flag.
|
|
32
|
+
|
|
33
|
+
Provides consistent output formatting across human-readable, JSON,
|
|
34
|
+
and table formats for CLI commands.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
format: Output format ("human", "json", or "table").
|
|
38
|
+
verbosity: Verbosity level (-1=quiet, 0=normal, 1-3=verbose).
|
|
39
|
+
color: Whether to use colors in human output.
|
|
40
|
+
|
|
41
|
+
Example:
|
|
42
|
+
>>> console = OutputConsole(format="human")
|
|
43
|
+
>>> console.print_message("Hello")
|
|
44
|
+
Hello
|
|
45
|
+
>>> console = OutputConsole(format="json")
|
|
46
|
+
>>> console.print_message("Hello")
|
|
47
|
+
{"message": "Hello"}
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
def __init__(
|
|
51
|
+
self,
|
|
52
|
+
format: OutputFormat = "human",
|
|
53
|
+
verbosity: int = 0,
|
|
54
|
+
color: bool = True,
|
|
55
|
+
) -> None:
|
|
56
|
+
"""Initialize OutputConsole.
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
format: Output format ("human", "json", or "table").
|
|
60
|
+
verbosity: Verbosity level (-1=quiet, 0=normal, 1-3=verbose).
|
|
61
|
+
color: Whether to use colors in human output.
|
|
62
|
+
"""
|
|
63
|
+
self.format = format
|
|
64
|
+
self.verbosity = verbosity
|
|
65
|
+
self.color = color
|
|
66
|
+
self._console = Console(force_terminal=color, no_color=not color)
|
|
67
|
+
|
|
68
|
+
def _is_quiet(self) -> bool:
|
|
69
|
+
"""Check if output should be suppressed (quiet mode)."""
|
|
70
|
+
return self.verbosity < 0
|
|
71
|
+
|
|
72
|
+
def print_message(self, message: str, *, style: str | None = None) -> None:
|
|
73
|
+
"""Print a simple message.
|
|
74
|
+
|
|
75
|
+
Args:
|
|
76
|
+
message: The message to print.
|
|
77
|
+
style: Optional Rich style for human format (e.g., "bold", "dim").
|
|
78
|
+
|
|
79
|
+
Example:
|
|
80
|
+
>>> console.print_message("Processing kata...")
|
|
81
|
+
Processing kata...
|
|
82
|
+
"""
|
|
83
|
+
if self._is_quiet():
|
|
84
|
+
return
|
|
85
|
+
|
|
86
|
+
if self.format == "json":
|
|
87
|
+
self._print_json({"message": message})
|
|
88
|
+
else:
|
|
89
|
+
# Both human and table use plain text for messages
|
|
90
|
+
if style and self.color:
|
|
91
|
+
self._console.print(f"[{style}]{message}[/]")
|
|
92
|
+
else:
|
|
93
|
+
self._console.print(message)
|
|
94
|
+
|
|
95
|
+
def print_success(
|
|
96
|
+
self, message: str, *, details: dict[str, Any] | None = None
|
|
97
|
+
) -> None:
|
|
98
|
+
"""Print a success message with optional details.
|
|
99
|
+
|
|
100
|
+
Args:
|
|
101
|
+
message: The success message.
|
|
102
|
+
details: Optional key-value details to display.
|
|
103
|
+
|
|
104
|
+
Example:
|
|
105
|
+
>>> console.print_success("Kata completed", details={"duration": "2.3s"})
|
|
106
|
+
✓ Kata completed (duration: 2.3s)
|
|
107
|
+
"""
|
|
108
|
+
if self._is_quiet():
|
|
109
|
+
return
|
|
110
|
+
|
|
111
|
+
if self.format == "json":
|
|
112
|
+
output: dict[str, Any] = {"status": "success", "message": message}
|
|
113
|
+
if details:
|
|
114
|
+
output["details"] = details
|
|
115
|
+
self._print_json(output)
|
|
116
|
+
else:
|
|
117
|
+
# Human and table format
|
|
118
|
+
detail_str = ""
|
|
119
|
+
if details:
|
|
120
|
+
detail_str = (
|
|
121
|
+
" (" + ", ".join(f"{k}: {v}" for k, v in details.items()) + ")"
|
|
122
|
+
)
|
|
123
|
+
if self.color:
|
|
124
|
+
self._console.print(f"[green]✓[/] {message}{detail_str}")
|
|
125
|
+
else:
|
|
126
|
+
self._console.print(f"✓ {message}{detail_str}")
|
|
127
|
+
|
|
128
|
+
def print_warning(
|
|
129
|
+
self, message: str, *, details: dict[str, Any] | None = None
|
|
130
|
+
) -> None:
|
|
131
|
+
"""Print a warning message.
|
|
132
|
+
|
|
133
|
+
Args:
|
|
134
|
+
message: The warning message.
|
|
135
|
+
details: Optional key-value details to display.
|
|
136
|
+
|
|
137
|
+
Example:
|
|
138
|
+
>>> console.print_warning("Config not found, using defaults")
|
|
139
|
+
⚠ Config not found, using defaults
|
|
140
|
+
"""
|
|
141
|
+
if self._is_quiet():
|
|
142
|
+
return
|
|
143
|
+
|
|
144
|
+
if self.format == "json":
|
|
145
|
+
output: dict[str, Any] = {"status": "warning", "message": message}
|
|
146
|
+
if details:
|
|
147
|
+
output["details"] = details
|
|
148
|
+
self._print_json(output)
|
|
149
|
+
else:
|
|
150
|
+
# Human and table format
|
|
151
|
+
detail_str = ""
|
|
152
|
+
if details:
|
|
153
|
+
detail_str = (
|
|
154
|
+
" (" + ", ".join(f"{k}: {v}" for k, v in details.items()) + ")"
|
|
155
|
+
)
|
|
156
|
+
if self.color:
|
|
157
|
+
self._console.print(f"[yellow]⚠[/] {message}{detail_str}")
|
|
158
|
+
else:
|
|
159
|
+
self._console.print(f"⚠ {message}{detail_str}")
|
|
160
|
+
|
|
161
|
+
def print_data(self, data: dict[str, Any], *, title: str | None = None) -> None:
|
|
162
|
+
"""Print a data structure.
|
|
163
|
+
|
|
164
|
+
Renders as:
|
|
165
|
+
- Human: Rich Tree for nested, key-value list for flat
|
|
166
|
+
- JSON: Raw dict
|
|
167
|
+
- Table: Key-value table
|
|
168
|
+
|
|
169
|
+
Args:
|
|
170
|
+
data: Dictionary to display.
|
|
171
|
+
title: Optional title for the output.
|
|
172
|
+
|
|
173
|
+
Example:
|
|
174
|
+
>>> console.print_data({"name": "discovery", "steps": 5})
|
|
175
|
+
"""
|
|
176
|
+
if self._is_quiet():
|
|
177
|
+
return
|
|
178
|
+
|
|
179
|
+
if self.format == "json":
|
|
180
|
+
self._print_json(data)
|
|
181
|
+
elif self.format == "table":
|
|
182
|
+
self._print_data_as_table(data, title=title)
|
|
183
|
+
else:
|
|
184
|
+
# Human format - use tree for nested, simple for flat
|
|
185
|
+
if self._has_nested_values(data):
|
|
186
|
+
self._print_data_as_tree(data, title=title)
|
|
187
|
+
else:
|
|
188
|
+
self._print_data_as_kv(data, title=title)
|
|
189
|
+
|
|
190
|
+
def print_list(
|
|
191
|
+
self,
|
|
192
|
+
items: Sequence[dict[str, Any]],
|
|
193
|
+
*,
|
|
194
|
+
columns: list[str] | None = None,
|
|
195
|
+
title: str | None = None,
|
|
196
|
+
) -> None:
|
|
197
|
+
"""Print a list of items.
|
|
198
|
+
|
|
199
|
+
Renders as:
|
|
200
|
+
- Human: Bullet list
|
|
201
|
+
- JSON: Array
|
|
202
|
+
- Table: Rich Table with columns
|
|
203
|
+
|
|
204
|
+
Args:
|
|
205
|
+
items: List of dictionaries to display.
|
|
206
|
+
columns: Column names to display (default: all keys from first item).
|
|
207
|
+
title: Optional title for the output.
|
|
208
|
+
|
|
209
|
+
Example:
|
|
210
|
+
>>> console.print_list([
|
|
211
|
+
... {"id": "kata/discovery", "name": "Discovery"},
|
|
212
|
+
... {"id": "kata/design", "name": "Design"},
|
|
213
|
+
... ])
|
|
214
|
+
"""
|
|
215
|
+
if self._is_quiet():
|
|
216
|
+
return
|
|
217
|
+
|
|
218
|
+
if not items:
|
|
219
|
+
if self.format == "json":
|
|
220
|
+
self._print_json([])
|
|
221
|
+
else:
|
|
222
|
+
self._console.print("(no items)")
|
|
223
|
+
return
|
|
224
|
+
|
|
225
|
+
if self.format == "json":
|
|
226
|
+
self._print_json(list(items))
|
|
227
|
+
elif self.format == "table":
|
|
228
|
+
self._print_list_as_table(items, columns=columns, title=title)
|
|
229
|
+
else:
|
|
230
|
+
# Human format - bullet list
|
|
231
|
+
self._print_list_as_bullets(items, title=title)
|
|
232
|
+
|
|
233
|
+
# --- Private helpers ---
|
|
234
|
+
|
|
235
|
+
def _print_json(self, data: Any) -> None:
|
|
236
|
+
"""Output data as JSON to stdout."""
|
|
237
|
+
print(json.dumps(data, indent=2, default=str))
|
|
238
|
+
|
|
239
|
+
def _has_nested_values(self, data: dict[str, Any]) -> bool:
|
|
240
|
+
"""Check if dict has nested dict/list values."""
|
|
241
|
+
return any(isinstance(v, (dict, list)) for v in data.values())
|
|
242
|
+
|
|
243
|
+
def _print_data_as_tree(
|
|
244
|
+
self, data: dict[str, Any], *, title: str | None = None
|
|
245
|
+
) -> None:
|
|
246
|
+
"""Render nested dict as Rich Tree."""
|
|
247
|
+
tree = Tree(title or "Data")
|
|
248
|
+
self._add_dict_to_tree(tree, data)
|
|
249
|
+
self._console.print(tree)
|
|
250
|
+
|
|
251
|
+
def _add_dict_to_tree(self, tree: Tree, data: dict[str, Any]) -> None:
|
|
252
|
+
"""Recursively add dict entries to tree."""
|
|
253
|
+
for key, value in data.items():
|
|
254
|
+
if isinstance(value, dict):
|
|
255
|
+
branch = tree.add(f"[bold]{key}[/]")
|
|
256
|
+
nested = cast(dict[str, Any], value)
|
|
257
|
+
self._add_dict_to_tree(branch, nested)
|
|
258
|
+
elif isinstance(value, list):
|
|
259
|
+
branch = tree.add(f"[bold]{key}[/]")
|
|
260
|
+
items = cast(list[Any], value)
|
|
261
|
+
for i, item in enumerate(items):
|
|
262
|
+
if isinstance(item, dict):
|
|
263
|
+
sub = branch.add(f"[dim][{i}][/]")
|
|
264
|
+
nested_item = cast(dict[str, Any], item)
|
|
265
|
+
self._add_dict_to_tree(sub, nested_item)
|
|
266
|
+
else:
|
|
267
|
+
branch.add(str(item))
|
|
268
|
+
else:
|
|
269
|
+
tree.add(f"[bold]{key}:[/] {value}")
|
|
270
|
+
|
|
271
|
+
def _print_data_as_kv(
|
|
272
|
+
self, data: dict[str, Any], *, title: str | None = None
|
|
273
|
+
) -> None:
|
|
274
|
+
"""Render flat dict as key-value pairs."""
|
|
275
|
+
if title:
|
|
276
|
+
self._console.print(f"[bold]{title}[/]")
|
|
277
|
+
for key, value in data.items():
|
|
278
|
+
self._console.print(f" [bold]{key}:[/] {value}")
|
|
279
|
+
|
|
280
|
+
def _print_data_as_table(
|
|
281
|
+
self, data: dict[str, Any], *, title: str | None = None
|
|
282
|
+
) -> None:
|
|
283
|
+
"""Render dict as two-column table."""
|
|
284
|
+
table = Table(title=title, show_header=True)
|
|
285
|
+
table.add_column("Key", style="bold")
|
|
286
|
+
table.add_column("Value")
|
|
287
|
+
for key, value in data.items():
|
|
288
|
+
table.add_row(key, str(value))
|
|
289
|
+
self._console.print(table)
|
|
290
|
+
|
|
291
|
+
def _print_list_as_bullets(
|
|
292
|
+
self, items: Sequence[dict[str, Any]], *, title: str | None = None
|
|
293
|
+
) -> None:
|
|
294
|
+
"""Render list as bullet points."""
|
|
295
|
+
if title:
|
|
296
|
+
self._console.print(f"{title}:")
|
|
297
|
+
for item in items:
|
|
298
|
+
# Create a summary from the first 2-3 values
|
|
299
|
+
values = list(item.values())[:3]
|
|
300
|
+
summary = " - ".join(str(v) for v in values)
|
|
301
|
+
self._console.print(f" • {summary}")
|
|
302
|
+
|
|
303
|
+
def _print_list_as_table(
|
|
304
|
+
self,
|
|
305
|
+
items: Sequence[dict[str, Any]],
|
|
306
|
+
*,
|
|
307
|
+
columns: list[str] | None = None,
|
|
308
|
+
title: str | None = None,
|
|
309
|
+
) -> None:
|
|
310
|
+
"""Render list as Rich Table."""
|
|
311
|
+
# Determine columns from first item if not specified
|
|
312
|
+
if columns is None:
|
|
313
|
+
columns = list(items[0].keys()) if items else []
|
|
314
|
+
|
|
315
|
+
table = Table(title=title, show_header=True)
|
|
316
|
+
for col in columns:
|
|
317
|
+
table.add_column(col.upper())
|
|
318
|
+
|
|
319
|
+
for item in items:
|
|
320
|
+
row = [str(item.get(col, "")) for col in columns]
|
|
321
|
+
table.add_row(*row)
|
|
322
|
+
|
|
323
|
+
self._console.print(table)
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
# --- Module-level singleton ---
|
|
327
|
+
|
|
328
|
+
_console: OutputConsole | None = None
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
def get_console() -> OutputConsole:
|
|
332
|
+
"""Get or create the output console singleton.
|
|
333
|
+
|
|
334
|
+
Returns:
|
|
335
|
+
The global OutputConsole instance.
|
|
336
|
+
|
|
337
|
+
Example:
|
|
338
|
+
>>> console = get_console()
|
|
339
|
+
>>> console.print_message("Hello")
|
|
340
|
+
"""
|
|
341
|
+
global _console # noqa: PLW0603
|
|
342
|
+
if _console is None:
|
|
343
|
+
_console = OutputConsole()
|
|
344
|
+
return _console
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
def set_console(console: OutputConsole | None) -> None:
|
|
348
|
+
"""Set the output console (for testing or reconfiguration).
|
|
349
|
+
|
|
350
|
+
Args:
|
|
351
|
+
console: Console to use, or None to reset to default.
|
|
352
|
+
|
|
353
|
+
Example:
|
|
354
|
+
>>> set_console(OutputConsole(format="json"))
|
|
355
|
+
>>> # ... use console ...
|
|
356
|
+
>>> set_console(None) # Reset
|
|
357
|
+
"""
|
|
358
|
+
global _console # noqa: PLW0603
|
|
359
|
+
_console = console
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
def configure_console(
|
|
363
|
+
format: OutputFormat = "human",
|
|
364
|
+
verbosity: int = 0,
|
|
365
|
+
color: bool = True,
|
|
366
|
+
) -> OutputConsole:
|
|
367
|
+
"""Configure and return the global console.
|
|
368
|
+
|
|
369
|
+
Creates a new OutputConsole with the given settings and sets it
|
|
370
|
+
as the global singleton.
|
|
371
|
+
|
|
372
|
+
Args:
|
|
373
|
+
format: Output format ("human", "json", or "table").
|
|
374
|
+
verbosity: Verbosity level (-1=quiet, 0=normal, 1-3=verbose).
|
|
375
|
+
color: Whether to use colors in human output.
|
|
376
|
+
|
|
377
|
+
Returns:
|
|
378
|
+
The newly configured OutputConsole.
|
|
379
|
+
|
|
380
|
+
Example:
|
|
381
|
+
>>> console = configure_console(format="json", verbosity=1)
|
|
382
|
+
"""
|
|
383
|
+
global _console # noqa: PLW0603
|
|
384
|
+
_console = OutputConsole(format=format, verbosity=verbosity, color=color)
|
|
385
|
+
return _console
|
|
386
|
+
|
|
387
|
+
|
|
388
|
+
__all__ = [
|
|
389
|
+
"OutputConsole",
|
|
390
|
+
"OutputFormat",
|
|
391
|
+
"get_console",
|
|
392
|
+
"set_console",
|
|
393
|
+
"configure_console",
|
|
394
|
+
]
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"""Output formatters for adapter commands."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
from typing import TYPE_CHECKING, Any
|
|
7
|
+
|
|
8
|
+
from rich.console import Console
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from raise_cli.adapters.declarative.schema import DeclarativeAdapterConfig
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def format_list_human(
|
|
15
|
+
tier: str,
|
|
16
|
+
groups: list[dict[str, Any]],
|
|
17
|
+
console: Console,
|
|
18
|
+
) -> None:
|
|
19
|
+
"""Display adapter list grouped by entry point group.
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
tier: Current tier level.
|
|
23
|
+
groups: List of dicts with group, protocol_name, adapters keys.
|
|
24
|
+
console: Rich console for output.
|
|
25
|
+
"""
|
|
26
|
+
console.print(f"Tier: {tier}\n")
|
|
27
|
+
|
|
28
|
+
for group_info in groups:
|
|
29
|
+
header = f"{group_info['group']} ({group_info['protocol_name']})"
|
|
30
|
+
console.print(f"[bold]{header}[/bold]")
|
|
31
|
+
|
|
32
|
+
adapters = group_info["adapters"]
|
|
33
|
+
if not adapters:
|
|
34
|
+
console.print(" [dim](none)[/dim]")
|
|
35
|
+
else:
|
|
36
|
+
for adapter in adapters:
|
|
37
|
+
console.print(f" {adapter['name']:<20s}{adapter['package']}")
|
|
38
|
+
|
|
39
|
+
console.print()
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def format_list_json(tier: str, groups: list[dict[str, Any]]) -> str:
|
|
43
|
+
"""Format adapter list as JSON.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
tier: Current tier level.
|
|
47
|
+
groups: List of dicts with group, protocol_name, adapters keys.
|
|
48
|
+
|
|
49
|
+
Returns:
|
|
50
|
+
JSON string.
|
|
51
|
+
"""
|
|
52
|
+
return json.dumps({"tier": tier, "groups": groups}, indent=2)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def format_check_human(
|
|
56
|
+
results: list[dict[str, Any]],
|
|
57
|
+
console: Console,
|
|
58
|
+
) -> None:
|
|
59
|
+
"""Display adapter check results with pass/fail indicators.
|
|
60
|
+
|
|
61
|
+
Args:
|
|
62
|
+
results: List of dicts with group, name, protocol_name, compliant, error keys.
|
|
63
|
+
console: Rich console for output.
|
|
64
|
+
"""
|
|
65
|
+
if not results:
|
|
66
|
+
console.print("No adapters registered.")
|
|
67
|
+
return
|
|
68
|
+
|
|
69
|
+
console.print("Checking adapters...\n")
|
|
70
|
+
|
|
71
|
+
current_group = ""
|
|
72
|
+
for r in results:
|
|
73
|
+
if r["group"] != current_group:
|
|
74
|
+
current_group = r["group"]
|
|
75
|
+
console.print(f"[bold]{current_group}[/bold]")
|
|
76
|
+
|
|
77
|
+
if r["compliant"]:
|
|
78
|
+
console.print(
|
|
79
|
+
f" [green]\u2713[/green] {r['name']:<20s}{r['protocol_name']} compliant"
|
|
80
|
+
)
|
|
81
|
+
else:
|
|
82
|
+
console.print(f" [red]\u2717[/red] {r['name']:<20s}{r['error']}")
|
|
83
|
+
|
|
84
|
+
passed = sum(1 for r in results if r["compliant"])
|
|
85
|
+
total = len(results)
|
|
86
|
+
console.print()
|
|
87
|
+
if passed == total:
|
|
88
|
+
console.print(f"[green]All {total} adapters passed.[/green]")
|
|
89
|
+
else:
|
|
90
|
+
failed = total - passed
|
|
91
|
+
console.print(f"[red]{failed} of {total} adapters failed.[/red]")
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def format_check_json(results: list[dict[str, Any]]) -> str:
|
|
95
|
+
"""Format adapter check results as JSON.
|
|
96
|
+
|
|
97
|
+
Args:
|
|
98
|
+
results: List of dicts with group, name, protocol_name, compliant, error keys.
|
|
99
|
+
|
|
100
|
+
Returns:
|
|
101
|
+
JSON string.
|
|
102
|
+
"""
|
|
103
|
+
passed = sum(1 for r in results if r["compliant"])
|
|
104
|
+
return json.dumps(
|
|
105
|
+
{
|
|
106
|
+
"results": results,
|
|
107
|
+
"total": len(results),
|
|
108
|
+
"passed": passed,
|
|
109
|
+
"all_passed": passed == len(results),
|
|
110
|
+
},
|
|
111
|
+
indent=2,
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def format_validate_human(
|
|
116
|
+
config: DeclarativeAdapterConfig,
|
|
117
|
+
console: Console,
|
|
118
|
+
) -> None:
|
|
119
|
+
"""Display validation success for a declarative adapter config.
|
|
120
|
+
|
|
121
|
+
Args:
|
|
122
|
+
config: Validated adapter configuration.
|
|
123
|
+
console: Rich console for output.
|
|
124
|
+
"""
|
|
125
|
+
mapped = sum(1 for v in config.methods.values() if v is not None)
|
|
126
|
+
unsupported = sum(1 for v in config.methods.values() if v is None)
|
|
127
|
+
server_cmd = f"{config.server.command} {' '.join(config.server.args)}".strip()
|
|
128
|
+
|
|
129
|
+
console.print("[green]✓ Valid adapter config[/green]")
|
|
130
|
+
console.print(f" Name: {config.adapter.name}")
|
|
131
|
+
console.print(f" Protocol: {config.adapter.protocol}")
|
|
132
|
+
if config.adapter.description:
|
|
133
|
+
console.print(f" Description: {config.adapter.description}")
|
|
134
|
+
console.print(f" Methods: {mapped} mapped, {unsupported} unsupported")
|
|
135
|
+
console.print(f" Server: {server_cmd}")
|