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.
Files changed (264) hide show
  1. raise_cli/__init__.py +38 -0
  2. raise_cli/__main__.py +30 -0
  3. raise_cli/adapters/__init__.py +91 -0
  4. raise_cli/adapters/declarative/__init__.py +26 -0
  5. raise_cli/adapters/declarative/adapter.py +267 -0
  6. raise_cli/adapters/declarative/discovery.py +94 -0
  7. raise_cli/adapters/declarative/expressions.py +150 -0
  8. raise_cli/adapters/declarative/reference/__init__.py +1 -0
  9. raise_cli/adapters/declarative/reference/github.yaml +143 -0
  10. raise_cli/adapters/declarative/schema.py +98 -0
  11. raise_cli/adapters/filesystem.py +299 -0
  12. raise_cli/adapters/mcp_bridge.py +10 -0
  13. raise_cli/adapters/mcp_confluence.py +246 -0
  14. raise_cli/adapters/mcp_jira.py +405 -0
  15. raise_cli/adapters/models.py +205 -0
  16. raise_cli/adapters/protocols.py +180 -0
  17. raise_cli/adapters/registry.py +90 -0
  18. raise_cli/adapters/sync.py +149 -0
  19. raise_cli/agents/__init__.py +14 -0
  20. raise_cli/agents/antigravity.yaml +8 -0
  21. raise_cli/agents/claude.yaml +8 -0
  22. raise_cli/agents/copilot.yaml +8 -0
  23. raise_cli/agents/copilot_plugin.py +124 -0
  24. raise_cli/agents/cursor.yaml +7 -0
  25. raise_cli/agents/roo.yaml +8 -0
  26. raise_cli/agents/windsurf.yaml +8 -0
  27. raise_cli/artifacts/__init__.py +30 -0
  28. raise_cli/artifacts/models.py +43 -0
  29. raise_cli/artifacts/reader.py +55 -0
  30. raise_cli/artifacts/renderer.py +104 -0
  31. raise_cli/artifacts/story_design.py +69 -0
  32. raise_cli/artifacts/writer.py +45 -0
  33. raise_cli/backlog/__init__.py +1 -0
  34. raise_cli/backlog/sync.py +115 -0
  35. raise_cli/cli/__init__.py +3 -0
  36. raise_cli/cli/commands/__init__.py +3 -0
  37. raise_cli/cli/commands/_resolve.py +153 -0
  38. raise_cli/cli/commands/adapters.py +362 -0
  39. raise_cli/cli/commands/artifact.py +137 -0
  40. raise_cli/cli/commands/backlog.py +333 -0
  41. raise_cli/cli/commands/base.py +31 -0
  42. raise_cli/cli/commands/discover.py +551 -0
  43. raise_cli/cli/commands/docs.py +130 -0
  44. raise_cli/cli/commands/doctor.py +177 -0
  45. raise_cli/cli/commands/gate.py +223 -0
  46. raise_cli/cli/commands/graph.py +1086 -0
  47. raise_cli/cli/commands/info.py +81 -0
  48. raise_cli/cli/commands/init.py +746 -0
  49. raise_cli/cli/commands/journal.py +167 -0
  50. raise_cli/cli/commands/mcp.py +524 -0
  51. raise_cli/cli/commands/memory.py +467 -0
  52. raise_cli/cli/commands/pattern.py +348 -0
  53. raise_cli/cli/commands/profile.py +59 -0
  54. raise_cli/cli/commands/publish.py +80 -0
  55. raise_cli/cli/commands/release.py +338 -0
  56. raise_cli/cli/commands/session.py +528 -0
  57. raise_cli/cli/commands/signal.py +410 -0
  58. raise_cli/cli/commands/skill.py +350 -0
  59. raise_cli/cli/commands/skill_set.py +145 -0
  60. raise_cli/cli/error_handler.py +158 -0
  61. raise_cli/cli/main.py +163 -0
  62. raise_cli/compat.py +66 -0
  63. raise_cli/config/__init__.py +41 -0
  64. raise_cli/config/agent_plugin.py +105 -0
  65. raise_cli/config/agent_registry.py +233 -0
  66. raise_cli/config/agents.py +120 -0
  67. raise_cli/config/ide.py +32 -0
  68. raise_cli/config/paths.py +379 -0
  69. raise_cli/config/settings.py +180 -0
  70. raise_cli/context/__init__.py +42 -0
  71. raise_cli/context/analyzers/__init__.py +16 -0
  72. raise_cli/context/analyzers/models.py +36 -0
  73. raise_cli/context/analyzers/protocol.py +43 -0
  74. raise_cli/context/analyzers/python.py +292 -0
  75. raise_cli/context/builder.py +1569 -0
  76. raise_cli/context/diff.py +213 -0
  77. raise_cli/context/extractors/__init__.py +13 -0
  78. raise_cli/context/extractors/skills.py +121 -0
  79. raise_cli/core/__init__.py +37 -0
  80. raise_cli/core/files.py +66 -0
  81. raise_cli/core/text.py +174 -0
  82. raise_cli/core/tools.py +441 -0
  83. raise_cli/discovery/__init__.py +50 -0
  84. raise_cli/discovery/analyzer.py +691 -0
  85. raise_cli/discovery/drift.py +355 -0
  86. raise_cli/discovery/scanner.py +1687 -0
  87. raise_cli/doctor/__init__.py +4 -0
  88. raise_cli/doctor/checks/__init__.py +1 -0
  89. raise_cli/doctor/checks/environment.py +110 -0
  90. raise_cli/doctor/checks/project.py +238 -0
  91. raise_cli/doctor/fix.py +80 -0
  92. raise_cli/doctor/models.py +56 -0
  93. raise_cli/doctor/protocol.py +43 -0
  94. raise_cli/doctor/registry.py +100 -0
  95. raise_cli/doctor/report.py +141 -0
  96. raise_cli/doctor/runner.py +95 -0
  97. raise_cli/engines/__init__.py +3 -0
  98. raise_cli/exceptions.py +215 -0
  99. raise_cli/gates/__init__.py +19 -0
  100. raise_cli/gates/builtin/__init__.py +1 -0
  101. raise_cli/gates/builtin/coverage.py +52 -0
  102. raise_cli/gates/builtin/lint.py +48 -0
  103. raise_cli/gates/builtin/tests.py +48 -0
  104. raise_cli/gates/builtin/types.py +48 -0
  105. raise_cli/gates/models.py +40 -0
  106. raise_cli/gates/protocol.py +41 -0
  107. raise_cli/gates/registry.py +141 -0
  108. raise_cli/governance/__init__.py +11 -0
  109. raise_cli/governance/extractor.py +412 -0
  110. raise_cli/governance/models.py +134 -0
  111. raise_cli/governance/parsers/__init__.py +35 -0
  112. raise_cli/governance/parsers/_convert.py +38 -0
  113. raise_cli/governance/parsers/adr.py +274 -0
  114. raise_cli/governance/parsers/backlog.py +356 -0
  115. raise_cli/governance/parsers/constitution.py +119 -0
  116. raise_cli/governance/parsers/epic.py +323 -0
  117. raise_cli/governance/parsers/glossary.py +316 -0
  118. raise_cli/governance/parsers/guardrails.py +345 -0
  119. raise_cli/governance/parsers/prd.py +112 -0
  120. raise_cli/governance/parsers/roadmap.py +118 -0
  121. raise_cli/governance/parsers/vision.py +116 -0
  122. raise_cli/graph/__init__.py +1 -0
  123. raise_cli/graph/backends/__init__.py +57 -0
  124. raise_cli/graph/backends/api.py +137 -0
  125. raise_cli/graph/backends/dual.py +139 -0
  126. raise_cli/graph/backends/pending.py +84 -0
  127. raise_cli/handlers/__init__.py +3 -0
  128. raise_cli/hooks/__init__.py +54 -0
  129. raise_cli/hooks/builtin/__init__.py +1 -0
  130. raise_cli/hooks/builtin/backlog.py +216 -0
  131. raise_cli/hooks/builtin/gate_bridge.py +83 -0
  132. raise_cli/hooks/builtin/jira_sync.py +127 -0
  133. raise_cli/hooks/builtin/memory.py +117 -0
  134. raise_cli/hooks/builtin/telemetry.py +72 -0
  135. raise_cli/hooks/emitter.py +184 -0
  136. raise_cli/hooks/events.py +262 -0
  137. raise_cli/hooks/protocol.py +38 -0
  138. raise_cli/hooks/registry.py +117 -0
  139. raise_cli/mcp/__init__.py +33 -0
  140. raise_cli/mcp/bridge.py +218 -0
  141. raise_cli/mcp/models.py +43 -0
  142. raise_cli/mcp/registry.py +77 -0
  143. raise_cli/mcp/schema.py +41 -0
  144. raise_cli/memory/__init__.py +58 -0
  145. raise_cli/memory/loader.py +247 -0
  146. raise_cli/memory/migration.py +241 -0
  147. raise_cli/memory/models.py +169 -0
  148. raise_cli/memory/writer.py +598 -0
  149. raise_cli/onboarding/__init__.py +103 -0
  150. raise_cli/onboarding/bootstrap.py +324 -0
  151. raise_cli/onboarding/claudemd.py +17 -0
  152. raise_cli/onboarding/conventions.py +742 -0
  153. raise_cli/onboarding/detection.py +374 -0
  154. raise_cli/onboarding/governance.py +443 -0
  155. raise_cli/onboarding/instructions.py +672 -0
  156. raise_cli/onboarding/manifest.py +201 -0
  157. raise_cli/onboarding/memory_md.py +399 -0
  158. raise_cli/onboarding/migration.py +207 -0
  159. raise_cli/onboarding/profile.py +624 -0
  160. raise_cli/onboarding/skill_conflict.py +100 -0
  161. raise_cli/onboarding/skill_manifest.py +176 -0
  162. raise_cli/onboarding/skills.py +437 -0
  163. raise_cli/onboarding/workflows.py +101 -0
  164. raise_cli/output/__init__.py +28 -0
  165. raise_cli/output/console.py +394 -0
  166. raise_cli/output/formatters/__init__.py +9 -0
  167. raise_cli/output/formatters/adapters.py +135 -0
  168. raise_cli/output/formatters/discover.py +439 -0
  169. raise_cli/output/formatters/skill.py +298 -0
  170. raise_cli/publish/__init__.py +3 -0
  171. raise_cli/publish/changelog.py +80 -0
  172. raise_cli/publish/check.py +179 -0
  173. raise_cli/publish/version.py +172 -0
  174. raise_cli/rai_base/__init__.py +22 -0
  175. raise_cli/rai_base/framework/__init__.py +7 -0
  176. raise_cli/rai_base/framework/methodology.yaml +233 -0
  177. raise_cli/rai_base/governance/__init__.py +1 -0
  178. raise_cli/rai_base/governance/architecture/__init__.py +1 -0
  179. raise_cli/rai_base/governance/architecture/domain-model.md +20 -0
  180. raise_cli/rai_base/governance/architecture/system-context.md +34 -0
  181. raise_cli/rai_base/governance/architecture/system-design.md +24 -0
  182. raise_cli/rai_base/governance/backlog.md +8 -0
  183. raise_cli/rai_base/governance/guardrails.md +17 -0
  184. raise_cli/rai_base/governance/prd.md +25 -0
  185. raise_cli/rai_base/governance/vision.md +16 -0
  186. raise_cli/rai_base/identity/__init__.py +8 -0
  187. raise_cli/rai_base/identity/core.md +119 -0
  188. raise_cli/rai_base/identity/perspective.md +119 -0
  189. raise_cli/rai_base/memory/__init__.py +7 -0
  190. raise_cli/rai_base/memory/patterns-base.jsonl +55 -0
  191. raise_cli/schemas/__init__.py +3 -0
  192. raise_cli/schemas/journal.py +49 -0
  193. raise_cli/schemas/session_state.py +117 -0
  194. raise_cli/session/__init__.py +5 -0
  195. raise_cli/session/bundle.py +820 -0
  196. raise_cli/session/close.py +268 -0
  197. raise_cli/session/journal.py +119 -0
  198. raise_cli/session/resolver.py +126 -0
  199. raise_cli/session/state.py +187 -0
  200. raise_cli/skills/__init__.py +44 -0
  201. raise_cli/skills/locator.py +141 -0
  202. raise_cli/skills/name_checker.py +199 -0
  203. raise_cli/skills/parser.py +145 -0
  204. raise_cli/skills/scaffold.py +212 -0
  205. raise_cli/skills/schema.py +132 -0
  206. raise_cli/skills/skillsets.py +195 -0
  207. raise_cli/skills/validator.py +197 -0
  208. raise_cli/skills_base/__init__.py +80 -0
  209. raise_cli/skills_base/contract-template.md +60 -0
  210. raise_cli/skills_base/preamble.md +37 -0
  211. raise_cli/skills_base/rai-architecture-review/SKILL.md +137 -0
  212. raise_cli/skills_base/rai-debug/SKILL.md +171 -0
  213. raise_cli/skills_base/rai-discover/SKILL.md +167 -0
  214. raise_cli/skills_base/rai-discover-document/SKILL.md +128 -0
  215. raise_cli/skills_base/rai-discover-scan/SKILL.md +147 -0
  216. raise_cli/skills_base/rai-discover-start/SKILL.md +145 -0
  217. raise_cli/skills_base/rai-discover-validate/SKILL.md +142 -0
  218. raise_cli/skills_base/rai-docs-update/SKILL.md +142 -0
  219. raise_cli/skills_base/rai-doctor/SKILL.md +120 -0
  220. raise_cli/skills_base/rai-epic-close/SKILL.md +165 -0
  221. raise_cli/skills_base/rai-epic-close/templates/retrospective.md +68 -0
  222. raise_cli/skills_base/rai-epic-design/SKILL.md +146 -0
  223. raise_cli/skills_base/rai-epic-design/templates/design.md +24 -0
  224. raise_cli/skills_base/rai-epic-design/templates/scope.md +76 -0
  225. raise_cli/skills_base/rai-epic-plan/SKILL.md +153 -0
  226. raise_cli/skills_base/rai-epic-plan/_references/sequencing-strategies.md +67 -0
  227. raise_cli/skills_base/rai-epic-plan/templates/plan-section.md +49 -0
  228. raise_cli/skills_base/rai-epic-run/SKILL.md +208 -0
  229. raise_cli/skills_base/rai-epic-start/SKILL.md +136 -0
  230. raise_cli/skills_base/rai-epic-start/templates/brief.md +34 -0
  231. raise_cli/skills_base/rai-mcp-add/SKILL.md +176 -0
  232. raise_cli/skills_base/rai-mcp-remove/SKILL.md +120 -0
  233. raise_cli/skills_base/rai-mcp-status/SKILL.md +147 -0
  234. raise_cli/skills_base/rai-problem-shape/SKILL.md +138 -0
  235. raise_cli/skills_base/rai-project-create/SKILL.md +144 -0
  236. raise_cli/skills_base/rai-project-onboard/SKILL.md +162 -0
  237. raise_cli/skills_base/rai-quality-review/SKILL.md +189 -0
  238. raise_cli/skills_base/rai-research/SKILL.md +143 -0
  239. raise_cli/skills_base/rai-research/references/research-prompt-template.md +317 -0
  240. raise_cli/skills_base/rai-session-close/SKILL.md +176 -0
  241. raise_cli/skills_base/rai-session-start/SKILL.md +110 -0
  242. raise_cli/skills_base/rai-story-close/SKILL.md +198 -0
  243. raise_cli/skills_base/rai-story-design/SKILL.md +203 -0
  244. raise_cli/skills_base/rai-story-design/references/tech-design-story-v2.md +293 -0
  245. raise_cli/skills_base/rai-story-implement/SKILL.md +115 -0
  246. raise_cli/skills_base/rai-story-plan/SKILL.md +135 -0
  247. raise_cli/skills_base/rai-story-review/SKILL.md +178 -0
  248. raise_cli/skills_base/rai-story-run/SKILL.md +282 -0
  249. raise_cli/skills_base/rai-story-start/SKILL.md +166 -0
  250. raise_cli/skills_base/rai-story-start/templates/story.md +38 -0
  251. raise_cli/skills_base/rai-welcome/SKILL.md +134 -0
  252. raise_cli/telemetry/__init__.py +42 -0
  253. raise_cli/telemetry/schemas.py +285 -0
  254. raise_cli/telemetry/writer.py +217 -0
  255. raise_cli/tier/__init__.py +0 -0
  256. raise_cli/tier/context.py +134 -0
  257. raise_cli/viz/__init__.py +7 -0
  258. raise_cli/viz/generator.py +406 -0
  259. raise_cli-2.2.1.dist-info/METADATA +433 -0
  260. raise_cli-2.2.1.dist-info/RECORD +264 -0
  261. raise_cli-2.2.1.dist-info/WHEEL +4 -0
  262. raise_cli-2.2.1.dist-info/entry_points.txt +40 -0
  263. raise_cli-2.2.1.dist-info/licenses/LICENSE +190 -0
  264. raise_cli-2.2.1.dist-info/licenses/NOTICE +4 -0
@@ -0,0 +1,333 @@
1
+ """CLI commands for backlog management via ProjectManagementAdapter.
2
+
3
+ Provides the ``rai backlog`` command group. All commands delegate to a
4
+ ProjectManagementAdapter discovered via entry points (Pattern B, D2).
5
+ The adapter is resolved automatically when exactly one is registered,
6
+ or selected explicitly via ``--adapter NAME`` (D3).
7
+
8
+ Query format in ``search`` is adapter-specific: JQL for Jira, etc. (AR5).
9
+
10
+ Architecture: E301 (Agent Tool Abstraction), ADR-033 (PM Adapter)
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ from pathlib import Path
16
+ from typing import Annotated, Any
17
+
18
+ import typer
19
+ from rich.console import Console
20
+
21
+ from raise_cli.adapters.models import IssueSpec
22
+ from raise_cli.backlog.sync import sync_backlog
23
+ from raise_cli.cli.commands._resolve import resolve_adapter
24
+
25
+ backlog_app = typer.Typer(
26
+ name="backlog",
27
+ help="Manage backlog items via ProjectManagementAdapter",
28
+ no_args_is_help=True,
29
+ )
30
+
31
+ console = Console()
32
+
33
+ _VALID_FORMATS = ("human", "agent")
34
+
35
+ # Common option for adapter override (D3)
36
+ AdapterOption = Annotated[
37
+ str | None,
38
+ typer.Option(
39
+ "--adapter", "-a", help="Adapter name override (auto-detect if omitted)"
40
+ ),
41
+ ]
42
+
43
+ # Output format option (S325.3: ACI)
44
+ FormatOption = Annotated[
45
+ str,
46
+ typer.Option("--format", "-f", help="Output format (human or agent)"),
47
+ ]
48
+
49
+
50
+ def _sanitize_pipe(value: str) -> str:
51
+ """Replace pipe characters in value to preserve agent format field boundaries."""
52
+ return value.replace("|", "¦")
53
+
54
+
55
+ def _validate_format(format: str) -> None:
56
+ """Validate format option, exit with error if invalid."""
57
+ if format not in _VALID_FORMATS:
58
+ console.print(f"[red]Error:[/red] Invalid format: {format}")
59
+ console.print(f"Valid formats: {', '.join(_VALID_FORMATS)}")
60
+ raise typer.Exit(1)
61
+
62
+
63
+ @backlog_app.command()
64
+ def create(
65
+ summary: Annotated[str, typer.Argument(help="Issue title")],
66
+ project: Annotated[
67
+ str, typer.Option("--project", "-p", help="Project key (e.g., RAISE)")
68
+ ],
69
+ issue_type: Annotated[
70
+ str, typer.Option("--type", "-t", help="Issue type")
71
+ ] = "Task",
72
+ labels: Annotated[
73
+ str | None, typer.Option("--labels", "-l", help="Comma-separated labels")
74
+ ] = None,
75
+ parent: Annotated[
76
+ str | None, typer.Option("--parent", help="Parent issue key")
77
+ ] = None,
78
+ description: Annotated[
79
+ str | None,
80
+ typer.Option("--description", "-d", help="Issue description (markdown)"),
81
+ ] = None,
82
+ adapter: AdapterOption = None,
83
+ format: FormatOption = "human",
84
+ ) -> None:
85
+ """Create a new backlog item."""
86
+ _validate_format(format)
87
+ pm = resolve_adapter(adapter)
88
+ spec = IssueSpec(
89
+ summary=summary,
90
+ issue_type=issue_type,
91
+ description=description or "",
92
+ labels=labels.split(",") if labels else [],
93
+ metadata={"parent": parent} if parent else {},
94
+ )
95
+ ref = pm.create_issue(project, spec)
96
+ if format == "agent":
97
+ print(ref.key)
98
+ else:
99
+ console.print(f"Created: {ref.key}")
100
+
101
+
102
+ @backlog_app.command()
103
+ def transition(
104
+ key: Annotated[str, typer.Argument(help="Issue key (e.g., RAISE-123)")],
105
+ status: Annotated[str, typer.Argument(help="Target status")],
106
+ adapter: AdapterOption = None,
107
+ ) -> None:
108
+ """Transition a backlog item to a new status."""
109
+ pm = resolve_adapter(adapter)
110
+ ref = pm.transition_issue(key, status)
111
+ console.print(f"{ref.key}: transitioned \u2192 {status}")
112
+
113
+
114
+ @backlog_app.command()
115
+ def update(
116
+ key: Annotated[str, typer.Argument(help="Issue key (e.g., RAISE-123)")],
117
+ summary: Annotated[
118
+ str | None, typer.Option("--summary", "-s", help="New summary")
119
+ ] = None,
120
+ labels: Annotated[
121
+ str | None, typer.Option("--labels", "-l", help="Comma-separated labels")
122
+ ] = None,
123
+ priority: Annotated[
124
+ str | None, typer.Option("--priority", help="Priority name")
125
+ ] = None,
126
+ assignee: Annotated[
127
+ str | None, typer.Option("--assignee", help="Assignee identifier")
128
+ ] = None,
129
+ adapter: AdapterOption = None,
130
+ ) -> None:
131
+ """Update fields on a backlog item."""
132
+ pm = resolve_adapter(adapter)
133
+ fields: dict[str, Any] = {}
134
+ if summary is not None:
135
+ fields["summary"] = summary
136
+ if labels is not None:
137
+ fields["labels"] = labels.split(",")
138
+ if priority is not None:
139
+ fields["priority"] = priority
140
+ if assignee is not None:
141
+ fields["assignee"] = assignee
142
+
143
+ if not fields:
144
+ console.print("[yellow]Warning:[/yellow] No fields to update.")
145
+ raise typer.Exit(0)
146
+
147
+ ref = pm.update_issue(key, fields)
148
+ console.print(f"{ref.key}: updated")
149
+
150
+
151
+ @backlog_app.command()
152
+ def link(
153
+ source: Annotated[str, typer.Argument(help="Source issue key")],
154
+ target: Annotated[str, typer.Argument(help="Target issue key")],
155
+ link_type: Annotated[
156
+ str, typer.Argument(help="Link type (e.g., 'blocks', 'relates')")
157
+ ],
158
+ adapter: AdapterOption = None,
159
+ ) -> None:
160
+ """Link two backlog items (AR4: uses link_issues only)."""
161
+ pm = resolve_adapter(adapter)
162
+ pm.link_issues(source, target, link_type)
163
+ console.print(f"{source} \u2192 {link_type} \u2192 {target}: linked")
164
+
165
+
166
+ @backlog_app.command()
167
+ def comment(
168
+ key: Annotated[str, typer.Argument(help="Issue key (e.g., RAISE-123)")],
169
+ body: Annotated[str, typer.Argument(help="Comment text (markdown)")],
170
+ adapter: AdapterOption = None,
171
+ ) -> None:
172
+ """Add a comment to a backlog item."""
173
+ pm = resolve_adapter(adapter)
174
+ ref = pm.add_comment(key, body)
175
+ console.print(f"{key}: comment added ({ref.id})")
176
+
177
+
178
+ @backlog_app.command()
179
+ def get(
180
+ key: Annotated[str, typer.Argument(help="Issue key (e.g., RAISE-123)")],
181
+ adapter: AdapterOption = None,
182
+ ) -> None:
183
+ """Retrieve details for a single backlog item."""
184
+ pm = resolve_adapter(adapter)
185
+ try:
186
+ detail = pm.get_issue(key)
187
+ except Exception as exc:
188
+ console.print(f"[red]Error:[/red] {exc}")
189
+ raise typer.Exit(1) from exc
190
+
191
+ # Header: key, status, type
192
+ console.print(f"{detail.key} {detail.status} {detail.issue_type}")
193
+ console.print(detail.summary)
194
+
195
+ # Optional fields — only show when non-empty
196
+ if detail.assignee:
197
+ console.print(f"Assignee: {detail.assignee}")
198
+ if detail.labels:
199
+ console.print(f"Labels: {', '.join(detail.labels)}")
200
+ if detail.parent_key:
201
+ console.print(f"Parent: {detail.parent_key}")
202
+ if detail.priority:
203
+ console.print(f"Priority: {detail.priority}")
204
+ if detail.created:
205
+ console.print(f"Created: {detail.created}")
206
+
207
+ # Description
208
+ if detail.description:
209
+ console.print()
210
+ desc = detail.description
211
+ if len(desc) > 500:
212
+ desc = desc[:500] + "..."
213
+ console.print(desc)
214
+
215
+
216
+ @backlog_app.command("get-comments")
217
+ def get_comments(
218
+ key: Annotated[str, typer.Argument(help="Issue key (e.g., RAISE-123)")],
219
+ limit: Annotated[int, typer.Option("--limit", "-n", help="Max comments")] = 10,
220
+ adapter: AdapterOption = None,
221
+ ) -> None:
222
+ """Retrieve comments for a backlog item."""
223
+ pm = resolve_adapter(adapter)
224
+ try:
225
+ comments = pm.get_comments(key, limit=limit)
226
+ except Exception as exc:
227
+ console.print(f"[red]Error:[/red] {exc}")
228
+ raise typer.Exit(1) from exc
229
+
230
+ if not comments:
231
+ console.print("No comments.")
232
+ return
233
+
234
+ for c in comments:
235
+ # Truncate timestamp to date+time (drop timezone for compactness)
236
+ ts = c.created[:19].replace("T", " ") if c.created else ""
237
+ console.print(f"[{ts}] {c.author}:")
238
+ # Indent comment body
239
+ for line in c.body.splitlines():
240
+ console.print(f" {line}")
241
+ console.print()
242
+
243
+
244
+ @backlog_app.command()
245
+ def search(
246
+ query: Annotated[
247
+ str,
248
+ typer.Argument(
249
+ help="Search query (format depends on adapter, e.g., JQL for Jira)"
250
+ ),
251
+ ],
252
+ limit: Annotated[int, typer.Option("--limit", "-n", help="Max results")] = 50,
253
+ adapter: AdapterOption = None,
254
+ format: FormatOption = "human",
255
+ ) -> None:
256
+ """Search backlog items. Query format is adapter-specific (AR5)."""
257
+ _validate_format(format)
258
+ pm = resolve_adapter(adapter)
259
+ results = pm.search(query, limit=limit)
260
+ if not results:
261
+ if format != "agent":
262
+ console.print("No results.")
263
+ return
264
+ if format == "agent":
265
+ for issue in results:
266
+ print(
267
+ f"{issue.key}|{_sanitize_pipe(issue.status)}|{_sanitize_pipe(issue.summary)}"
268
+ )
269
+ else:
270
+ for issue in results:
271
+ console.print(f"{issue.key} {issue.status:<12} {issue.summary}")
272
+
273
+
274
+ @backlog_app.command("batch-transition")
275
+ def batch_transition(
276
+ keys: Annotated[
277
+ str, typer.Argument(help="Comma-separated issue keys (e.g., RAISE-1,RAISE-2)")
278
+ ],
279
+ status: Annotated[str, typer.Argument(help="Target status")],
280
+ adapter: AdapterOption = None,
281
+ ) -> None:
282
+ """Transition multiple backlog items at once."""
283
+ pm = resolve_adapter(adapter)
284
+ key_list = [k.strip() for k in keys.split(",") if k.strip()]
285
+ if not key_list:
286
+ console.print("[red]Error:[/red] No valid keys provided.")
287
+ raise typer.Exit(1)
288
+
289
+ result = pm.batch_transition(key_list, status)
290
+ succeeded = len(result.succeeded)
291
+ failed = len(result.failed)
292
+ total = succeeded + failed
293
+
294
+ console.print(f"{succeeded}/{total} transitioned \u2192 {status}")
295
+ for failure in result.failed:
296
+ console.print(f" [red]\u2717[/red] {failure.key}: {failure.error}")
297
+
298
+
299
+ @backlog_app.command()
300
+ def sync(
301
+ project: Annotated[
302
+ str | None,
303
+ typer.Option("--project", "-p", help="Project key filter (e.g., RAISE)"),
304
+ ] = None,
305
+ adapter: AdapterOption = None,
306
+ ) -> None:
307
+ """Regenerate governance/backlog.md from a remote adapter."""
308
+ pm = resolve_adapter(adapter)
309
+
310
+ # Derive adapter name for display
311
+ adapter_name = adapter or type(pm).__name__.lower().replace("pmadapter", "").replace("adapter", "") or "unknown"
312
+
313
+ output_path = Path.cwd() / "governance" / "backlog.md"
314
+
315
+ try:
316
+ result = sync_backlog(
317
+ pm,
318
+ adapter_name,
319
+ project_filter=project,
320
+ output_path=output_path,
321
+ )
322
+ except ValueError as exc:
323
+ # Filesystem adapter no-op
324
+ console.print(f"[yellow]{exc}[/yellow]")
325
+ raise typer.Exit(0) from exc
326
+ except RuntimeError as exc:
327
+ console.print(f"[red]Error:[/red] {exc}")
328
+ raise typer.Exit(1) from exc
329
+
330
+ console.print(
331
+ f"Synced {result.output_path} from {result.adapter_name} "
332
+ f"({result.epic_count} epics, {result.timestamp})"
333
+ )
@@ -0,0 +1,31 @@
1
+ """Backward-compatible alias for the info command.
2
+
3
+ The `base show` command has been moved to `rai info` (RAISE-247/S5).
4
+
5
+ This alias prints a deprecation warning and delegates to the canonical command.
6
+ It will be removed in a future release (RAISE-247/S9).
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import typer
12
+ from rich.console import Console
13
+
14
+ base_app = typer.Typer(
15
+ name="base",
16
+ help="View base Rai package info",
17
+ no_args_is_help=True,
18
+ )
19
+
20
+ _stderr_console = Console(stderr=True)
21
+
22
+
23
+ @base_app.command()
24
+ def show() -> None:
25
+ """Deprecated: use 'rai info'."""
26
+ _stderr_console.print(
27
+ "[yellow]DEPRECATED:[/yellow] 'rai base show' → use 'rai info' instead",
28
+ )
29
+ from raise_cli.cli.commands.info import info_command
30
+
31
+ info_command()