roadmap-cli 0.2.0__tar.gz → 0.3.1__tar.gz

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 (121) hide show
  1. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/.gitignore +1 -0
  2. roadmap_cli-0.3.1/CHANGELOG.md +224 -0
  3. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/PKG-INFO +45 -11
  4. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/README.md +38 -3
  5. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/pyproject.toml +27 -23
  6. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/__init__.py +2 -2
  7. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/adapters/inbound/cli/__init__.py +44 -16
  8. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/adapters/inbound/cli/analysis/commands.py +37 -22
  9. roadmap_cli-0.3.1/roadmap/adapters/inbound/cli/cleanup.py +185 -0
  10. roadmap_cli-0.3.1/roadmap/adapters/inbound/cli/cli_command_helpers.py +269 -0
  11. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/adapters/inbound/cli/config/commands.py +26 -0
  12. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/adapters/inbound/cli/data/commands.py +88 -57
  13. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/adapters/inbound/cli/decorators.py +22 -19
  14. roadmap_cli-0.3.1/roadmap/adapters/inbound/cli/exception_handler.py +38 -0
  15. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/adapters/inbound/cli/health/commands.py +127 -48
  16. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/adapters/inbound/cli/init/commands.py +31 -11
  17. roadmap_cli-0.3.1/roadmap/adapters/inbound/cli/inspection.py +42 -0
  18. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/adapters/inbound/cli/issues/archive.py +46 -16
  19. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/adapters/inbound/cli/issues/create.py +29 -8
  20. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/adapters/inbound/cli/issues/list.py +59 -14
  21. roadmap_cli-0.3.1/roadmap/adapters/inbound/cli/issues/query_presenter.py +160 -0
  22. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/adapters/inbound/cli/issues/resolution.py +15 -16
  23. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/adapters/inbound/cli/issues/restore.py +24 -7
  24. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/adapters/inbound/cli/issues/start.py +5 -1
  25. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/adapters/inbound/cli/issues/update.py +44 -13
  26. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/adapters/inbound/cli/issues/view.py +9 -2
  27. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/adapters/inbound/cli/layout.py +6 -3
  28. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/adapters/inbound/cli/migrate.py +45 -25
  29. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/adapters/inbound/cli/milestones/__init__.py +1 -0
  30. roadmap_cli-0.3.1/roadmap/adapters/inbound/cli/milestones/archive.py +81 -0
  31. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/adapters/inbound/cli/milestones/create.py +14 -3
  32. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/adapters/inbound/cli/milestones/recalculate.py +5 -0
  33. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/adapters/inbound/cli/milestones/restore.py +18 -7
  34. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/adapters/inbound/cli/milestones/update.py +7 -1
  35. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/adapters/inbound/cli/milestones/view.py +47 -22
  36. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/adapters/inbound/cli/models/output_models.py +50 -41
  37. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/adapters/inbound/cli/output_formatter.py +28 -34
  38. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/adapters/inbound/cli/planning_resolution.py +1 -15
  39. roadmap_cli-0.3.1/roadmap/adapters/inbound/cli/projects/archive.py +81 -0
  40. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/adapters/inbound/cli/projects/close.py +13 -2
  41. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/adapters/inbound/cli/projects/create.py +23 -4
  42. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/adapters/inbound/cli/projects/list.py +28 -14
  43. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/adapters/inbound/cli/projects/restore.py +18 -7
  44. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/adapters/inbound/cli/projects/update.py +11 -2
  45. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/adapters/inbound/cli/projects/view.py +8 -1
  46. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/adapters/inbound/cli/status.py +62 -64
  47. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/adapters/outbound/git/local.py +6 -2
  48. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/adapters/outbound/persistence/canonical.py +199 -107
  49. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/adapters/outbound/persistence/configuration.py +98 -22
  50. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/adapters/outbound/persistence/diagnostics.py +169 -121
  51. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/adapters/outbound/persistence/documents.py +148 -90
  52. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/adapters/outbound/persistence/initialization.py +5 -3
  53. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/adapters/outbound/persistence/migration.py +179 -130
  54. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/application/contracts.py +13 -0
  55. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/application/ports.py +3 -3
  56. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/application/use_cases/issue_mutations.py +116 -60
  57. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/application/use_cases/issues.py +23 -7
  58. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/application/use_cases/planning.py +215 -97
  59. roadmap_cli-0.3.1/roadmap/application/use_cases/updates.py +9 -0
  60. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/bootstrap/__init__.py +14 -2
  61. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/domain/aggregates.py +30 -16
  62. roadmap_cli-0.2.0/CHANGELOG.md +0 -86
  63. roadmap_cli-0.2.0/roadmap/adapters/inbound/cli/cleanup.py +0 -107
  64. roadmap_cli-0.2.0/roadmap/adapters/inbound/cli/cli_command_helpers.py +0 -106
  65. roadmap_cli-0.2.0/roadmap/adapters/inbound/cli/exception_handler.py +0 -94
  66. roadmap_cli-0.2.0/roadmap/adapters/inbound/cli/issues/query_presenter.py +0 -104
  67. roadmap_cli-0.2.0/roadmap/adapters/inbound/cli/milestones/archive.py +0 -55
  68. roadmap_cli-0.2.0/roadmap/adapters/inbound/cli/projects/archive.py +0 -55
  69. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/LICENSE.md +0 -0
  70. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/adapters/__init__.py +0 -0
  71. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/adapters/inbound/__init__.py +0 -0
  72. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/adapters/inbound/cli/analysis/__init__.py +0 -0
  73. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/adapters/inbound/cli/analysis/presenter.py +0 -0
  74. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/adapters/inbound/cli/column_factory.py +0 -0
  75. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/adapters/inbound/cli/config/__init__.py +0 -0
  76. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/adapters/inbound/cli/console.py +0 -0
  77. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/adapters/inbound/cli/daily_summary.py +0 -0
  78. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/adapters/inbound/cli/data/__init__.py +0 -0
  79. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/adapters/inbound/cli/datetime_parser.py +0 -0
  80. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/adapters/inbound/cli/git/__init__.py +0 -0
  81. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/adapters/inbound/cli/git/commands.py +0 -0
  82. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/adapters/inbound/cli/health/__init__.py +0 -0
  83. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/adapters/inbound/cli/init/__init__.py +0 -0
  84. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/adapters/inbound/cli/issues/__init__.py +0 -0
  85. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/adapters/inbound/cli/issues/block.py +0 -0
  86. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/adapters/inbound/cli/issues/close.py +0 -0
  87. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/adapters/inbound/cli/issues/comment.py +0 -0
  88. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/adapters/inbound/cli/issues/delete.py +0 -0
  89. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/adapters/inbound/cli/issues/deps.py +0 -0
  90. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/adapters/inbound/cli/issues/progress.py +0 -0
  91. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/adapters/inbound/cli/issues/unblock.py +0 -0
  92. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/adapters/inbound/cli/milestones/assign.py +0 -0
  93. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/adapters/inbound/cli/milestones/close.py +0 -0
  94. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/adapters/inbound/cli/milestones/delete.py +0 -0
  95. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/adapters/inbound/cli/milestones/kanban.py +0 -0
  96. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/adapters/inbound/cli/milestones/list.py +0 -0
  97. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/adapters/inbound/cli/models/__init__.py +0 -0
  98. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/adapters/inbound/cli/output_options.py +0 -0
  99. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/adapters/inbound/cli/projects/__init__.py +0 -0
  100. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/adapters/inbound/cli/projects/delete.py +0 -0
  101. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/adapters/inbound/cli/styling.py +0 -0
  102. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/adapters/inbound/cli/today.py +0 -0
  103. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/adapters/outbound/__init__.py +0 -0
  104. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/adapters/outbound/git/__init__.py +0 -0
  105. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/adapters/outbound/persistence/__init__.py +0 -0
  106. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/adapters/outbound/persistence/issue_queries.py +0 -0
  107. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/adapters/outbound/persistence/projection.py +0 -0
  108. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/adapters/outbound/system.py +0 -0
  109. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/application/__init__.py +0 -0
  110. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/application/configuration.py +0 -0
  111. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/application/failures.py +0 -0
  112. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/application/use_cases/__init__.py +0 -0
  113. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/application/use_cases/health.py +0 -0
  114. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/application/use_cases/local_git.py +0 -0
  115. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/application/use_cases/workspace_initialization.py +0 -0
  116. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/application/use_cases/workspace_migration.py +0 -0
  117. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/bootstrap/core.py +0 -0
  118. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/domain/__init__.py +0 -0
  119. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/domain/failures.py +0 -0
  120. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/domain/transitions.py +0 -0
  121. {roadmap_cli-0.2.0 → roadmap_cli-0.3.1}/roadmap/domain/types.py +0 -0
@@ -254,6 +254,7 @@ test_sync_init/*.lock
254
254
  /tmp/test_check_sync/*.tmp
255
255
  /tmp/test_check_sync/*.lock
256
256
  sync_git_state.txt
257
+ /output.txt
257
258
 
258
259
  # design notes, AI prompts, etc.
259
260
 
@@ -0,0 +1,224 @@
1
+ # Changelog
2
+
3
+ All notable changes to Roadmap CLI are recorded here. The format follows
4
+ [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and releases follow
5
+ [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ ## [Unreleased]
8
+
9
+ ## [0.3.1] - 2026-10-08
10
+
11
+ ### Added
12
+
13
+ - Add CI gates for high-confidence dead code and active documentation links,
14
+ with negative fixtures proving rejected inputs.
15
+
16
+ - Add branch-outcome regressions for migration refusals, canonical transaction
17
+ failure/retry, planning idempotency, aggregate invariants and scoped issue-list
18
+ workload output.
19
+
20
+ - Add scoped requirement verification records, fail-closed evidence policy and
21
+ an executable maintainer handover runbook with a current release checklist.
22
+
23
+ - Add explicit workspace selection, JSON entity inspection with issue history,
24
+ project/milestone ID-only creation, nullable-field clearing, issue due dates and
25
+ label edits, project owner/priority controls, and stderr-only operational verbosity.
26
+ - Add a reviewed command/option evidence matrix and a guard against CLI drift.
27
+
28
+ - Add global `--debug` tracebacks for unexpected CLI failures. Configuration
29
+ access errors no longer silently use defaults; command import failures remain
30
+ errors. Log projection refresh exceptions with Python logging and send repair
31
+ warnings to stderr.
32
+ - Show candidate IDs and labels for ambiguous issue/project/milestone lookups.
33
+ - Explain effective configuration values, owning scope, and default/configured
34
+ source with `config explain KEY [--format json]`.
35
+ - Print only the created issue ID with `issue create --print-id`, keeping
36
+ warnings and branch notifications on stderr.
37
+ - Show repair-preview commands in plain health output and document built-in
38
+ shell completion.
39
+
40
+ ### Changed
41
+
42
+ - Replace deprecated Click filesystem isolation in tests with pytest-managed
43
+ temporary workspaces, remove retired coverage exclusions, and enable Bandit's
44
+ try/except/pass rule instead of suppressing it globally.
45
+
46
+ - Raise the statement-coverage floor to 90% after real-storage CLI safety journeys.
47
+
48
+ - Replace the hand-written import graph checker with Import Linter contracts
49
+ and narrow namespace/core dependency guards; raise the coverage floor to 87%.
50
+
51
+ - Standardize type checking on ty and remove Pyright and unused pytest plugins;
52
+ consolidate development dependencies into the dev extra.
53
+ - Run architecture checks in pre-commit, retain CI coverage evidence, and
54
+ require the full CI workflow before a release can publish.
55
+
56
+ ### Fixed
57
+
58
+ - Correct the empty issue-list creation hint to include the required `--title`
59
+ option, with a CLI regression proving the suggested command works.
60
+
61
+ - Refuse migration when canonical directories cannot be enumerated or a personal
62
+ configuration schema version is boolean; protect changed migration previews
63
+ and prove projection failure rollback/retry with regression tests.
64
+ - Distinguish unpublished master behavior from PyPI 0.3.0 and correct the package
65
+ documentation URL to the actual default branch.
66
+
67
+ - Refuse dry-run recovery writes and parent-close guard bypasses; reject explicit
68
+ branch options without branch action before canonical writes.
69
+ - Preserve literal machine output, independent sort directions and empty CSV
70
+ headers; validate output options and export enums even when results are empty.
71
+ - Honor empty-analysis destinations and health summary-only across formats.
72
+ - Require explicit bounded health repair targets. Prefer `--format`, `--yes` and
73
+ `milestone progress`; warn on deprecated spellings through 0.3, retire in 0.4.
74
+ Archive keeps a lifecycle override; its legacy force-as-consent shortcut is
75
+ explicitly deprecated, as is project close's shortcut. Init force is deprecated
76
+ because initialization is idempotent and never overwrites canonical data.
77
+
78
+ - Keep broken-reference health findings visible when filtering by entity, with
79
+ workspace-relative scopes and an accurate unhealthy exit code.
80
+ - Validate manually edited configuration values using the same type rules as
81
+ scoped writes; reject boolean schema versions without rewriting input.
82
+ - Treat duplicate project/milestone names as ambiguous and list candidates.
83
+
84
+ - Preserve successful canonical mutation results when post-commit cleanup or
85
+ projection stale-marker persistence fails; retry journals on reopen.
86
+ - Reject symlinked backup directories/files and anchor cleanup deletion to a
87
+ directory descriptor, including revalidation after confirmation.
88
+ - Fail canonical scans on unreadable directories instead of treating them as
89
+ empty, and block projection repair when enumeration fails.
90
+
91
+ - Refuse recovery preview and projection repair when transaction journals
92
+ cannot be inspected, instead of silently treating them as absent.
93
+ - Validate complete recovery journals and snapshot integrity before replay;
94
+ preserve post-interruption manual edits and reject unsafe snapshot paths.
95
+ - Release workspace locks when recovery or lock-metadata persistence fails.
96
+ - Persist transaction snapshots and preparation/cleanup boundaries; preserve
97
+ rollback intent when an ordinary write failure cannot finish restoration.
98
+ - Rebuild missing projections when retrying a migration interrupted after
99
+ canonical commit.
100
+ - Explicitly select locked development dependencies for CI and pre-commit
101
+ checks and the documented contributor commands.
102
+
103
+ ### Added
104
+
105
+ - Reliability coverage for deletion guards, reciprocal dependency edits,
106
+ populated backup cleanup, malformed canonical input, and repair refusal.
107
+ - A maintainer continuity/inactivity policy and a review closeout record that
108
+ distinguishes local verification from remote CI and independent review.
109
+ - A required CI audit of locked runtime/development dependencies using pinned
110
+ pip-audit, in addition to GitHub's Dependabot monitoring.
111
+ - Reliability tests covering actual process termination, separate writers,
112
+ filesystem replacement failures, recovery conflicts, and installed CLI
113
+ repair, plus a recovery guide and guarantee-to-test evidence map.
114
+
115
+ ## [0.3.0] - 2026-09-02
116
+
117
+ ### Changed
118
+
119
+ - Migrated static type checking from Pyright-only to `ty` running alongside
120
+ Pyright, both enforced in CI and pre-commit; aligned their configured
121
+ Python-version floor with the declared `>=3.12` support range.
122
+ - Reduced cyclomatic complexity across the codebase and enforced a
123
+ xenon B-or-better gate (absolute, per-module, and average) in CI and
124
+ pre-commit.
125
+ - Raised the enforced test coverage floor from 78% to 85%, backed by a
126
+ targeted test-coverage sweep (CLI output formatting and issue query
127
+ presentation) that reached 85.70%.
128
+ - Reduced DRY violations identified during the complexity-reduction pass.
129
+ - Enabled GitHub's native Dependabot vulnerability alerts for the repository.
130
+
131
+ ### Fixed
132
+
133
+ - Removed the stale, inaccurate `.env.production` file (unverifiable "0 known
134
+ CVEs" claim, an obsolete `poetry install` reference, and unused
135
+ environment variables that nothing in the codebase reads).
136
+ - Removed the stale, tracked `output.txt` pytest-output artifact from version
137
+ control.
138
+ - Fixed a broken `SECURITY.md` reference to a nonexistent developer-notes
139
+ file; it now points to the ADR that documents credential handling.
140
+
141
+ ### Removed
142
+
143
+ - Removed `.env.production` and `output.txt` from version control (see
144
+ Fixed, above).
145
+
146
+ ## [0.2.0] - 2026-08-27
147
+
148
+
149
+ ### Added
150
+
151
+ - Added explicit `roadmap migrate` preflight, dry-run, confirmed execution,
152
+ structured output, recovery, and idempotent rerun for supported 0.1.1
153
+ workspaces.
154
+ - Added versioned workspace/document schemas, stable entity IDs, scoped
155
+ configuration, deterministic JSON/CSV output, read-only health diagnosis,
156
+ previewable repair, and explicit local Git status/branch/reference commands.
157
+ - Added architecture, test-quality, package-installation, compatibility,
158
+ failure-injection, concurrency, corruption, migration, offline, and measured
159
+ performance gates.
160
+
161
+ ### Changed
162
+
163
+ - Canonical Markdown/YAML documents are now the sole data authority. SQLite is
164
+ a disposable query projection refreshed or rebuilt only from those files.
165
+ - Projects, milestones, and issues use flat stable-ID paths. Lifecycle state is
166
+ metadata rather than a physical archive-directory contract.
167
+ - New IDs use complete UUID4 values; migration preserves existing IDs and
168
+ user-authored content.
169
+ - The package now has one Domain/Application/Adapters/Bootstrap architecture,
170
+ one configuration path, one persistence path, and one implementation for
171
+ each retained CLI journey.
172
+ - Supported runtimes now include Python 3.12 through 3.14 on macOS and Linux;
173
+ CI verifies the full Python range and installed artifacts on Ubuntu x64 and
174
+ macOS ARM64.
175
+ - Machine-readable stdout is isolated from diagnostics and confirmations on
176
+ stderr.
177
+
178
+ ### Removed
179
+
180
+ - Removed provider-backed synchronization, GitHub entity replication,
181
+ Roadmap-owned credentials, connectivity/setup handlers, provider baselines,
182
+ reconciliation databases, and sync metrics. Ordinary Git transport remains
183
+ outside Roadmap and local file-to-SQLite projection maintenance remains.
184
+ - Removed the legacy `core`, `common`, `infrastructure`, `presentation`, and
185
+ superseded adapter ownership zones, along with duplicate configuration,
186
+ health, export, persistence, and compatibility implementations.
187
+ - Removed claimed features that did not belong to the retained product,
188
+ including hosted dashboards, predictive analytics, OAuth, and generated API
189
+ documentation stacks.
190
+
191
+ ### Migration
192
+
193
+ - Existing 0.1.1 workspaces must run `roadmap migrate --dry-run` and review the
194
+ report before `roadmap migrate --yes`.
195
+ - Migration rewrites canonical entities to stable paths, externalizes user
196
+ preferences, removes legacy credential fields, and rebuilds SQLite without
197
+ treating it as authority. See `docs/user_guide/MIGRATING_TO_0_2.md`.
198
+
199
+ ## [0.1.1] - 2026-08-09
200
+
201
+ ### Fixed
202
+
203
+ - Removed the unrelated `roadmap` PyPI distribution from runtime dependencies;
204
+ it shadowed this project's package and broke the console command.
205
+ - Moved development and documentation tooling out of runtime dependencies.
206
+ - Read the installed version from distribution metadata and added clean wheel
207
+ and source-distribution smoke tests.
208
+ - Corrected supported Python/platform declarations, repository URLs, and
209
+ installation guidance.
210
+
211
+ ## [0.1.0] - 2026-05-18
212
+
213
+ ### Changed
214
+
215
+ - Reset the public version from premature 1.x tags to 0.1.0 so package versions
216
+ accurately communicate pre-1.0 compatibility and maturity.
217
+
218
+ ## Pre-reset 1.x tags
219
+
220
+ Tags numbered 1.0.0 through 1.0.2 predated the maturity reset. Their release
221
+ notes contained aspirational and unverified feature, compatibility, coverage,
222
+ security, and performance claims, so they are not a description of the current
223
+ product contract. Git history remains available for provenance; supported
224
+ behavior begins with the honest 0.1.x line above.
@@ -1,11 +1,11 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: roadmap-cli
3
- Version: 0.2.0
3
+ Version: 0.3.1
4
4
  Summary: Git-native command line tool for local project roadmap management
5
5
  Project-URL: Homepage, https://github.com/shanewilkins/roadmap
6
6
  Project-URL: Repository, https://github.com/shanewilkins/roadmap
7
7
  Project-URL: Issues, https://github.com/shanewilkins/roadmap/issues
8
- Project-URL: Documentation, https://github.com/shanewilkins/roadmap/tree/main/docs
8
+ Project-URL: Documentation, https://github.com/shanewilkins/roadmap/tree/master/docs
9
9
  Author: Shane Wilkins
10
10
  License: MIT
11
11
  License-File: LICENSE.md
@@ -33,17 +33,16 @@ Requires-Dist: pyyaml<7.0.0,>=6.0.3
33
33
  Requires-Dist: rich<16.0.0,>=15.0.0
34
34
  Provides-Extra: dev
35
35
  Requires-Dist: bandit[toml]>=1.9.4; extra == 'dev'
36
+ Requires-Dist: import-linter<3,>=2.11; extra == 'dev'
37
+ Requires-Dist: markdown-it-py<5,>=4.2; extra == 'dev'
36
38
  Requires-Dist: pre-commit>=4.6.0; extra == 'dev'
37
- Requires-Dist: pyright>=1.1.411; extra == 'dev'
38
- Requires-Dist: pytest-aiohttp>=1.1.0; extra == 'dev'
39
- Requires-Dist: pytest-asyncio>=1.3.0; extra == 'dev'
40
- Requires-Dist: pytest-benchmark>=5.2.3; extra == 'dev'
41
39
  Requires-Dist: pytest-cov>=7.1.0; extra == 'dev'
42
- Requires-Dist: pytest-mock>=3.15.1; extra == 'dev'
43
40
  Requires-Dist: pytest-xdist>=3.8.0; extra == 'dev'
44
41
  Requires-Dist: pytest>=9.0.3; extra == 'dev'
45
42
  Requires-Dist: radon<7.0.0,>=6.0.1; extra == 'dev'
46
- Requires-Dist: ruff==0.16.4; extra == 'dev'
43
+ Requires-Dist: ruff>=0.16.10; extra == 'dev'
44
+ Requires-Dist: ty>=0.0.84; extra == 'dev'
45
+ Requires-Dist: vulture<3,>=2.14; extra == 'dev'
47
46
  Requires-Dist: xenon<1.0.0,>=0.9.3; extra == 'dev'
48
47
  Description-Content-Type: text/markdown
49
48
 
@@ -54,9 +53,17 @@ their planning data in reviewable Markdown and YAML files rather than a hosted
54
53
  service. It supports issues, dependencies, comments, projects, milestones,
55
54
  daily views, health checks, and machine-readable exports.
56
55
 
57
- The current release is 0.2.0. Its deliberately contracted behavior is defined
56
+ Version **0.3.1** adds CLI workflow improvements and reliability fixes. Use the
57
+ [versioned documentation](https://github.com/shanewilkins/roadmap/tree/v0.3.1/docs)
58
+ and [0.3.1 changelog](CHANGELOG.md#031---2026-10-08) for this version.
59
+ Source checkouts can precede package publication; check
60
+ [PyPI](https://pypi.org/project/roadmap-cli/) for available package versions.
61
+
62
+ The retained CLI and data behavior is defined
58
63
  by an explicit
59
64
  [public compatibility contract](docs/architecture/public-contract-0.2.md).
65
+ 0.3 is an internal quality-hardening release; the 0.2 CLI and data contract is
66
+ unchanged.
60
67
 
61
68
  ## What it does
62
69
 
@@ -139,7 +146,7 @@ Organize delivery:
139
146
 
140
147
  ```bash
141
148
  roadmap project create --title "Web application"
142
- roadmap milestone create --title "0.2" --due-date 2026-09-30
149
+ roadmap milestone create --title "0.2" --due-date 2026-09-30 --project "Web application"
143
150
  roadmap milestone assign <issue-id> "0.2"
144
151
  roadmap milestone view "0.2"
145
152
  ```
@@ -211,6 +218,7 @@ migration. Do not treat the internal SQLite schema as a public API.
211
218
  - [Workflows](docs/user_guide/WORKFLOWS.md)
212
219
  - [FAQ](docs/user_guide/FAQ.md)
213
220
  - [0.2 migration guide](docs/user_guide/MIGRATING_TO_0_2.md)
221
+ - [Recovery guide](docs/user_guide/RECOVERY.md)
214
222
  - [Architecture decisions](docs/architecture/README.md)
215
223
  - [Refactor case study](docs/architecture/portfolio-case-study.md)
216
224
  - [0.2 release checklist](docs/releases/0.2.0-checklist.md)
@@ -225,7 +233,9 @@ product direction.
225
233
 
226
234
  ## Project status
227
235
 
228
- Roadmap 0.2.0 is the result of thirteen independently verified refactor phases.
236
+ This checkout targets 0.3.1; the workflow shortcuts below describe that version. See the [next-release checklist](docs/releases/NEXT_RELEASE.md)
237
+ for the remaining publication checks. The architecture was established through
238
+ thirteen independently verified refactor phases for 0.2.0.
229
239
  See the
230
240
  [execution plan](docs/architecture/refactor-execution-plan.md) and published
231
241
  [checkpoint reports](docs/architecture/README.md#execution-checkpoints).
@@ -233,3 +243,27 @@ See the
233
243
  ## License
234
244
 
235
245
  [MIT](LICENSE.md)
246
+
247
+
248
+ ### Small workflow shortcuts
249
+
250
+ - Ambiguous issue, project, and milestone lookups list matching IDs and labels.
251
+ - Plain health output offers repair-preview commands; previews never apply repairs.
252
+ - `roadmap config explain output.format` shows the effective value and its source;
253
+ add `--format json` for structured output.
254
+ - Capture a created issue ID without parsing prose:
255
+
256
+ ```sh
257
+ issue_id=$(roadmap issue create --title "Improve recovery guidance" --print-id)
258
+ roadmap issue view "$issue_id"
259
+ ```
260
+
261
+ `--print-id` keeps warnings and branch notifications on stderr. A subsequent
262
+ Git-branch failure still returns nonzero; the printed ID identifies the already
263
+ created issue, so inspect it before retrying creation.
264
+
265
+ Enable [shell completion](docs/user_guide/SHELL_COMPLETION.md) for commands,
266
+ options, and choice values.
267
+
268
+ The reviewed command and option contracts, compatibility guidance, and examples
269
+ are in the [CLI correctness guide](docs/architecture/cli-correctness-closeout.md).
@@ -5,9 +5,17 @@ their planning data in reviewable Markdown and YAML files rather than a hosted
5
5
  service. It supports issues, dependencies, comments, projects, milestones,
6
6
  daily views, health checks, and machine-readable exports.
7
7
 
8
- The current release is 0.2.0. Its deliberately contracted behavior is defined
8
+ Version **0.3.1** adds CLI workflow improvements and reliability fixes. Use the
9
+ [versioned documentation](https://github.com/shanewilkins/roadmap/tree/v0.3.1/docs)
10
+ and [0.3.1 changelog](CHANGELOG.md#031---2026-10-08) for this version.
11
+ Source checkouts can precede package publication; check
12
+ [PyPI](https://pypi.org/project/roadmap-cli/) for available package versions.
13
+
14
+ The retained CLI and data behavior is defined
9
15
  by an explicit
10
16
  [public compatibility contract](docs/architecture/public-contract-0.2.md).
17
+ 0.3 is an internal quality-hardening release; the 0.2 CLI and data contract is
18
+ unchanged.
11
19
 
12
20
  ## What it does
13
21
 
@@ -90,7 +98,7 @@ Organize delivery:
90
98
 
91
99
  ```bash
92
100
  roadmap project create --title "Web application"
93
- roadmap milestone create --title "0.2" --due-date 2026-09-30
101
+ roadmap milestone create --title "0.2" --due-date 2026-09-30 --project "Web application"
94
102
  roadmap milestone assign <issue-id> "0.2"
95
103
  roadmap milestone view "0.2"
96
104
  ```
@@ -162,6 +170,7 @@ migration. Do not treat the internal SQLite schema as a public API.
162
170
  - [Workflows](docs/user_guide/WORKFLOWS.md)
163
171
  - [FAQ](docs/user_guide/FAQ.md)
164
172
  - [0.2 migration guide](docs/user_guide/MIGRATING_TO_0_2.md)
173
+ - [Recovery guide](docs/user_guide/RECOVERY.md)
165
174
  - [Architecture decisions](docs/architecture/README.md)
166
175
  - [Refactor case study](docs/architecture/portfolio-case-study.md)
167
176
  - [0.2 release checklist](docs/releases/0.2.0-checklist.md)
@@ -176,7 +185,9 @@ product direction.
176
185
 
177
186
  ## Project status
178
187
 
179
- Roadmap 0.2.0 is the result of thirteen independently verified refactor phases.
188
+ This checkout targets 0.3.1; the workflow shortcuts below describe that version. See the [next-release checklist](docs/releases/NEXT_RELEASE.md)
189
+ for the remaining publication checks. The architecture was established through
190
+ thirteen independently verified refactor phases for 0.2.0.
180
191
  See the
181
192
  [execution plan](docs/architecture/refactor-execution-plan.md) and published
182
193
  [checkpoint reports](docs/architecture/README.md#execution-checkpoints).
@@ -184,3 +195,27 @@ See the
184
195
  ## License
185
196
 
186
197
  [MIT](LICENSE.md)
198
+
199
+
200
+ ### Small workflow shortcuts
201
+
202
+ - Ambiguous issue, project, and milestone lookups list matching IDs and labels.
203
+ - Plain health output offers repair-preview commands; previews never apply repairs.
204
+ - `roadmap config explain output.format` shows the effective value and its source;
205
+ add `--format json` for structured output.
206
+ - Capture a created issue ID without parsing prose:
207
+
208
+ ```sh
209
+ issue_id=$(roadmap issue create --title "Improve recovery guidance" --print-id)
210
+ roadmap issue view "$issue_id"
211
+ ```
212
+
213
+ `--print-id` keeps warnings and branch notifications on stderr. A subsequent
214
+ Git-branch failure still returns nonzero; the printed ID identifies the already
215
+ created issue, so inspect it before retrying creation.
216
+
217
+ Enable [shell completion](docs/user_guide/SHELL_COMPLETION.md) for commands,
218
+ options, and choice values.
219
+
220
+ The reviewed command and option contracts, compatibility guidance, and examples
221
+ are in the [CLI correctness guide](docs/architecture/cli-correctness-closeout.md).
@@ -13,7 +13,7 @@ build-backend = "hatchling.build"
13
13
  # Installs the locked runtime and development dependency sets.
14
14
  # ============================================================================
15
15
  name = "roadmap-cli"
16
- version = "0.2.0"
16
+ version = "0.3.1"
17
17
  description = "Git-native command line tool for local project roadmap management"
18
18
  readme = "README.md"
19
19
  requires-python = ">=3.12,<4.0"
@@ -59,24 +59,23 @@ dependencies = [
59
59
  dev = [
60
60
  "pytest>=9.0.3",
61
61
  "pytest-cov>=7.1.0",
62
- "pytest-asyncio>=1.3.0",
63
- "pytest-aiohttp>=1.1.0",
64
- "ruff==0.16.4",
62
+ "ruff>=0.16.10",
65
63
  "pre-commit>=4.6.0",
66
64
  "bandit[toml]>=1.9.4",
67
65
  "radon>=6.0.1,<7.0.0",
68
66
  "xenon>=0.9.3,<1.0.0",
69
67
  "pytest-xdist>=3.8.0",
70
- "pytest-benchmark>=5.2.3",
71
- "pyright>=1.1.411",
72
- "pytest-mock>=3.15.1",
68
+ "ty>=0.0.84",
69
+ "import-linter>=2.11,<3",
70
+ "markdown-it-py>=4.2,<5",
71
+ "vulture>=2.14,<3",
73
72
  ]
74
73
 
75
74
  [project.urls]
76
75
  Homepage = "https://github.com/shanewilkins/roadmap"
77
76
  Repository = "https://github.com/shanewilkins/roadmap"
78
77
  Issues = "https://github.com/shanewilkins/roadmap/issues"
79
- Documentation = "https://github.com/shanewilkins/roadmap/tree/main/docs"
78
+ Documentation = "https://github.com/shanewilkins/roadmap/tree/master/docs"
80
79
 
81
80
  [project.scripts]
82
81
  roadmap = "roadmap.bootstrap:main"
@@ -112,10 +111,27 @@ include = [
112
111
  # - ruff.toml: Linting and formatting rules
113
112
  # - bandit.toml: Security scanning configuration
114
113
  # - .coveragerc: Test coverage thresholds and exclusions
115
- # - pyrightconfig.json: Type checking configuration
114
+ # - tool.ty below: Type checking configuration
116
115
 
117
- # Type checking is now handled by Pyright
118
- # Configuration is in pyrightconfig.json
116
+ # ty is the sole type checker, enforced in CI and pre-commit.
117
+ [tool.ty.src]
118
+ include = ["roadmap", "tests"]
119
+ # These fixture trees are synthetic "roadmap" packages used only for the
120
+ # architecture checker's AST scan; they are never actually imported, so
121
+ # ty's import resolution has nothing real to check them against.
122
+ exclude = ["tests/policy/fixtures/architecture"]
123
+
124
+ [tool.ty.environment]
125
+ python-version = "3.12"
126
+
127
+ [tool.ty.rules]
128
+ unresolved-import = "error"
129
+ unresolved-reference = "error"
130
+ invalid-argument-type = "error"
131
+ invalid-assignment = "error"
132
+ invalid-return-type = "error"
133
+ possibly-missing-attribute = "error"
134
+ unresolved-attribute = "error"
119
135
 
120
136
  [tool.pytest.ini_options]
121
137
  testpaths = ["tests"]
@@ -131,17 +147,5 @@ markers = [
131
147
  "mock_scanning: Tests with mocked repository scanning operations",
132
148
  "no_xdist: Tests that should not run in parallel (disables xdist parallelization)"
133
149
  ]
134
- filterwarnings = [
135
- "ignore::pytest_benchmark.logger.PytestBenchmarkWarning"
136
- ]
137
-
138
- [dependency-groups]
139
- dev = [
140
- "pytest-asyncio>=1.4.0",
141
- "pytest-benchmark>=5.2.3",
142
- "pytest-cov>=7.1.0",
143
- "pytest-mock>=3.15.1",
144
- "pytest-xdist>=3.8.0",
145
- ]
146
150
 
147
151
  # Coverage configuration moved to config/.coveragerc
@@ -4,10 +4,10 @@ from importlib.metadata import PackageNotFoundError, version
4
4
 
5
5
  # Installed artifacts read their distribution metadata; source checkouts use the
6
6
  # matching fallback without importing the executable adapter as a side effect.
7
- __version__ = "0.2.0"
7
+ __version__ = "0.3.1"
8
8
  try:
9
9
  __version__ = version("roadmap-cli")
10
10
  except PackageNotFoundError:
11
- __version__ = "0.2.0"
11
+ __version__ = "0.3.1"
12
12
 
13
13
  __all__ = ["__version__"]
@@ -1,6 +1,7 @@
1
1
  """Lazy Click adapter assembled by :mod:`roadmap.bootstrap`."""
2
2
 
3
3
  import importlib
4
+ import stat
4
5
  from collections.abc import Callable, Mapping
5
6
  from dataclasses import dataclass
6
7
  from pathlib import Path
@@ -12,7 +13,9 @@ import click
12
13
  def _handle_cli_exception(ctx: click.Context, error: Exception) -> Any:
13
14
  from roadmap.adapters.inbound.cli.exception_handler import handle_cli_exception
14
15
 
15
- return handle_cli_exception(ctx, error, show_traceback=False)
16
+ return handle_cli_exception(
17
+ ctx, error, show_traceback=bool(ctx.find_root().params.get("debug", False))
18
+ )
16
19
 
17
20
 
18
21
  CommandLocation = tuple[str, str, str]
@@ -67,6 +70,7 @@ class CliRuntime:
67
70
  migration_factory: Callable[[], Any]
68
71
  initialization_factory: Callable[[str], Any]
69
72
  version: str
73
+ workspace_factories: Callable[[Path], dict[str, Any]] | None = None
70
74
 
71
75
 
72
76
  class RoadmapClickGroup(click.Group):
@@ -92,27 +96,21 @@ class RoadmapClickGroup(click.Group):
92
96
  """Render root help from static metadata without importing features."""
93
97
  names = sorted(self._command_registry)
94
98
  limit = formatter.width - 6 - max(map(len, names), default=0)
95
- rows = [(name, click.utils.make_default_short_help(self._command_registry[name][2], limit)) for name in names] # fmt: skip
99
+ rows = [(name, click.Command(name, help=self._command_registry[name][2]).get_short_help_str(limit)) for name in names] # fmt: skip
96
100
  with formatter.section("Commands"):
97
101
  formatter.write_dl(rows)
98
102
 
99
- def get_command(self, ctx: click.Context, name: str) -> click.Command | None:
103
+ def get_command(self, ctx: click.Context, cmd_name: str) -> click.Command | None:
100
104
  """Load one registered command only when Click requests it."""
101
- if (attached := super().get_command(ctx, name)) is not None:
105
+ if (attached := super().get_command(ctx, cmd_name)) is not None:
102
106
  return attached
103
- if name in self._command_cache:
104
- return self._command_cache[name]
105
- location = self._command_registry.get(name)
107
+ if cmd_name in self._command_cache:
108
+ return self._command_cache[cmd_name]
109
+ location = self._command_registry.get(cmd_name)
106
110
  if location is None:
107
111
  return None
108
112
  module_path, attribute, _help = location
109
- try:
110
- self._command_cache[name] = command = getattr(importlib.import_module(module_path), attribute) # fmt: skip
111
- except Exception as error:
112
- self._console_factory().print(
113
- f"⚠️ Failed to load command '{name}': {error}", style="yellow"
114
- )
115
- return None
113
+ self._command_cache[cmd_name] = command = getattr(importlib.import_module(module_path), attribute) # fmt: skip
116
114
  return command
117
115
 
118
116
  def invoke(self, ctx: click.Context) -> Any:
@@ -138,8 +136,16 @@ def create_cli(
138
136
  console_factory=runtime.console_factory,
139
137
  )
140
138
  @click.version_option(version=runtime.version)
139
+ @click.option(
140
+ "--debug", is_flag=True, help="Show tracebacks for unexpected errors."
141
+ )
142
+ @click.option(
143
+ "--workspace",
144
+ type=click.Path(path_type=Path),
145
+ help="Canonical workspace directory; overrides the current-directory default.",
146
+ )
141
147
  @click.pass_context
142
- def cli(ctx: click.Context) -> None:
148
+ def cli(ctx: click.Context, debug: bool, workspace: Path | None) -> None: # noqa: ARG001
143
149
  """Roadmap CLI - A command line tool for creating and managing roadmaps."""
144
150
  ctx.ensure_object(dict)
145
151
  ctx.obj.setdefault("core_factory", runtime.core_factory)
@@ -147,12 +153,34 @@ def create_cli(
147
153
  ctx.obj.setdefault("console_factory", runtime.console_factory)
148
154
  ctx.obj.setdefault("migration_factory", runtime.migration_factory)
149
155
  ctx.obj.setdefault("initialization_factory", runtime.initialization_factory)
156
+ if workspace is not None:
157
+ if runtime.workspace_factories is None:
158
+ raise click.ClickException(
159
+ "Explicit workspace selection requires Bootstrap"
160
+ )
161
+ selected = workspace.expanduser().absolute()
162
+ ctx.obj.update(runtime.workspace_factories(selected))
163
+ ctx.obj["workspace"] = selected
164
+ ctx.obj.pop("core", None)
165
+ if ctx.invoked_subcommand not in {None, "init", "migrate"}:
166
+ try:
167
+ valid = stat.S_ISDIR(selected.stat().st_mode) and stat.S_ISREG(
168
+ (selected / "config.yaml").stat().st_mode
169
+ )
170
+ except OSError as error:
171
+ raise click.ClickException(
172
+ f"Cannot open explicit workspace {selected}: {error}"
173
+ ) from error
174
+ if not valid:
175
+ raise click.ClickException(
176
+ f"Explicit workspace is not initialized: {selected}"
177
+ )
150
178
  if (
151
179
  ctx.invoked_subcommand not in {None, "init", "migrate"}
152
180
  and "core" not in ctx.obj
153
181
  ):
154
182
  try:
155
- ctx.obj["core"] = runtime.core_factory(".roadmap")
183
+ ctx.obj["core"] = ctx.obj["core_factory"](".roadmap")
156
184
  except Exception as error:
157
185
  _handle_cli_exception(ctx, error)
158
186