@anastops/cli 1.0.0 → 1.2.0

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 (74) hide show
  1. package/dist/commands/doctor.d.ts.map +1 -1
  2. package/dist/commands/doctor.js +8 -6
  3. package/dist/commands/doctor.js.map +1 -1
  4. package/dist/commands/init.d.ts +17 -0
  5. package/dist/commands/init.d.ts.map +1 -1
  6. package/dist/commands/init.js +315 -17
  7. package/dist/commands/init.js.map +1 -1
  8. package/dist/commands/ranger.d.ts +7 -2
  9. package/dist/commands/ranger.d.ts.map +1 -1
  10. package/dist/commands/ranger.js +99 -21
  11. package/dist/commands/ranger.js.map +1 -1
  12. package/dist/commands/uninstall.d.ts +16 -0
  13. package/dist/commands/uninstall.d.ts.map +1 -0
  14. package/dist/commands/uninstall.js +206 -0
  15. package/dist/commands/uninstall.js.map +1 -0
  16. package/dist/index.js +11 -0
  17. package/dist/index.js.map +1 -1
  18. package/package.json +8 -6
  19. package/ranger-tui/pyproject.toml +51 -0
  20. package/ranger-tui/ranger_tui/__init__.py +5 -0
  21. package/ranger-tui/ranger_tui/__main__.py +16 -0
  22. package/ranger-tui/ranger_tui/__pycache__/__init__.cpython-314.pyc +0 -0
  23. package/ranger-tui/ranger_tui/__pycache__/__main__.cpython-314.pyc +0 -0
  24. package/ranger-tui/ranger_tui/__pycache__/accessibility.cpython-314.pyc +0 -0
  25. package/ranger-tui/ranger_tui/__pycache__/app.cpython-314.pyc +0 -0
  26. package/ranger-tui/ranger_tui/__pycache__/config.cpython-314.pyc +0 -0
  27. package/ranger-tui/ranger_tui/__pycache__/theme.cpython-314.pyc +0 -0
  28. package/ranger-tui/ranger_tui/accessibility.py +499 -0
  29. package/ranger-tui/ranger_tui/actions/__init__.py +13 -0
  30. package/ranger-tui/ranger_tui/actions/agent_actions.py +74 -0
  31. package/ranger-tui/ranger_tui/actions/session_actions.py +110 -0
  32. package/ranger-tui/ranger_tui/actions/task_actions.py +107 -0
  33. package/ranger-tui/ranger_tui/app.py +93 -0
  34. package/ranger-tui/ranger_tui/assets/ranger_head.png +0 -0
  35. package/ranger-tui/ranger_tui/config.py +100 -0
  36. package/ranger-tui/ranger_tui/data/__init__.py +16 -0
  37. package/ranger-tui/ranger_tui/data/__pycache__/__init__.cpython-314.pyc +0 -0
  38. package/ranger-tui/ranger_tui/data/__pycache__/client.cpython-314.pyc +0 -0
  39. package/ranger-tui/ranger_tui/data/__pycache__/models.cpython-314.pyc +0 -0
  40. package/ranger-tui/ranger_tui/data/client.py +858 -0
  41. package/ranger-tui/ranger_tui/data/models.py +151 -0
  42. package/ranger-tui/ranger_tui/screens/__init__.py +16 -0
  43. package/ranger-tui/ranger_tui/screens/__pycache__/__init__.cpython-314.pyc +0 -0
  44. package/ranger-tui/ranger_tui/screens/__pycache__/dashboard.cpython-314.pyc +0 -0
  45. package/ranger-tui/ranger_tui/screens/__pycache__/modals.cpython-314.pyc +0 -0
  46. package/ranger-tui/ranger_tui/screens/__pycache__/session.cpython-314.pyc +0 -0
  47. package/ranger-tui/ranger_tui/screens/__pycache__/task.cpython-314.pyc +0 -0
  48. package/ranger-tui/ranger_tui/screens/command_palette.py +357 -0
  49. package/ranger-tui/ranger_tui/screens/dashboard.py +232 -0
  50. package/ranger-tui/ranger_tui/screens/help.py +103 -0
  51. package/ranger-tui/ranger_tui/screens/modals.py +95 -0
  52. package/ranger-tui/ranger_tui/screens/session.py +289 -0
  53. package/ranger-tui/ranger_tui/screens/task.py +187 -0
  54. package/ranger-tui/ranger_tui/styles/ranger.tcss +254 -0
  55. package/ranger-tui/ranger_tui/theme.py +93 -0
  56. package/ranger-tui/ranger_tui/widgets/__init__.py +23 -0
  57. package/ranger-tui/ranger_tui/widgets/__pycache__/__init__.cpython-314.pyc +0 -0
  58. package/ranger-tui/ranger_tui/widgets/__pycache__/accessible.cpython-314.pyc +0 -0
  59. package/ranger-tui/ranger_tui/widgets/__pycache__/logo.cpython-314.pyc +0 -0
  60. package/ranger-tui/ranger_tui/widgets/__pycache__/logo_assets.cpython-314.pyc +0 -0
  61. package/ranger-tui/ranger_tui/widgets/__pycache__/ranger_image.cpython-314.pyc +0 -0
  62. package/ranger-tui/ranger_tui/widgets/__pycache__/sidebar.cpython-314.pyc +0 -0
  63. package/ranger-tui/ranger_tui/widgets/__pycache__/topbar.cpython-314.pyc +0 -0
  64. package/ranger-tui/ranger_tui/widgets/accessible.py +176 -0
  65. package/ranger-tui/ranger_tui/widgets/agents_table.py +151 -0
  66. package/ranger-tui/ranger_tui/widgets/header.py +141 -0
  67. package/ranger-tui/ranger_tui/widgets/logo.py +258 -0
  68. package/ranger-tui/ranger_tui/widgets/logo_assets.py +62 -0
  69. package/ranger-tui/ranger_tui/widgets/metrics_panel.py +121 -0
  70. package/ranger-tui/ranger_tui/widgets/ranger_image.py +91 -0
  71. package/ranger-tui/ranger_tui/widgets/sessions_table.py +191 -0
  72. package/ranger-tui/ranger_tui/widgets/sidebar.py +91 -0
  73. package/ranger-tui/ranger_tui/widgets/tasks_table.py +189 -0
  74. package/ranger-tui/ranger_tui/widgets/topbar.py +168 -0
@@ -0,0 +1,254 @@
1
+ /* Ranger TUI - Theme-aware responsive design */
2
+
3
+ /* ============ Global ============ */
4
+
5
+ Screen {
6
+ background: $background;
7
+ layout: vertical;
8
+ }
9
+
10
+ /* ============ Main Layout ============ */
11
+
12
+ #main {
13
+ width: 100%;
14
+ height: 1fr;
15
+ layout: horizontal;
16
+ padding: 0 1;
17
+ }
18
+
19
+ /* ============ Tables ============ */
20
+
21
+ DataTable {
22
+ width: 1fr;
23
+ height: 100%;
24
+ background: $surface;
25
+ border: round $secondary-darken-2;
26
+ overflow-x: hidden;
27
+ }
28
+
29
+ DataTable > .datatable--header {
30
+ text-style: bold;
31
+ color: $primary;
32
+ background: $panel;
33
+ }
34
+
35
+ DataTable > .datatable--cursor {
36
+ background: $primary-darken-3;
37
+ }
38
+
39
+ DataTable:focus > .datatable--cursor {
40
+ background: $primary-darken-1;
41
+ }
42
+
43
+ DataTable > .datatable--even-row {
44
+ background: $surface;
45
+ }
46
+
47
+ DataTable > .datatable--odd-row {
48
+ background: $background;
49
+ }
50
+
51
+ /* ============ Tabs ============ */
52
+
53
+ TabbedContent {
54
+ width: 1fr;
55
+ height: 100%;
56
+ }
57
+
58
+ ContentSwitcher {
59
+ height: 1fr;
60
+ }
61
+
62
+ Tabs {
63
+ background: $panel;
64
+ dock: top;
65
+ height: 3;
66
+ }
67
+
68
+ Tab {
69
+ color: $foreground-muted;
70
+ padding: 0 2;
71
+ }
72
+
73
+ Tab:hover {
74
+ color: $foreground;
75
+ background: $surface;
76
+ }
77
+
78
+ Tab.-active {
79
+ color: $primary;
80
+ text-style: bold;
81
+ background: $surface;
82
+ }
83
+
84
+ TabPane {
85
+ height: 1fr;
86
+ padding: 0;
87
+ }
88
+
89
+ /* ============ RichLog ============ */
90
+
91
+ RichLog {
92
+ width: 100%;
93
+ height: 100%;
94
+ background: $surface;
95
+ border: round $secondary-darken-2;
96
+ padding: 1;
97
+ scrollbar-color: $secondary;
98
+ scrollbar-color-hover: $primary;
99
+ }
100
+
101
+ /* ============ Modals ============ */
102
+
103
+ ModalScreen {
104
+ align: center middle;
105
+ }
106
+
107
+ #modal, #dialog {
108
+ width: 60;
109
+ height: auto;
110
+ background: $surface;
111
+ border: round $primary;
112
+ padding: 1 2;
113
+ }
114
+
115
+ #modal-title, #title {
116
+ text-style: bold;
117
+ color: $primary;
118
+ text-align: center;
119
+ margin-bottom: 1;
120
+ }
121
+
122
+ #modal Label, #dialog Label {
123
+ color: $foreground-muted;
124
+ margin-top: 1;
125
+ }
126
+
127
+ #modal Input, #dialog Input {
128
+ margin-bottom: 1;
129
+ }
130
+
131
+ #modal Select, #dialog Select {
132
+ margin-bottom: 1;
133
+ }
134
+
135
+ #modal-buttons, #buttons {
136
+ margin-top: 1;
137
+ align: center middle;
138
+ layout: horizontal;
139
+ }
140
+
141
+ #modal-buttons Button, #buttons Button {
142
+ margin: 0 1;
143
+ min-width: 12;
144
+ }
145
+
146
+ /* ============ Inputs ============ */
147
+
148
+ Input {
149
+ background: $background;
150
+ border: tall $secondary-darken-2;
151
+ color: $foreground;
152
+ }
153
+
154
+ Input:focus {
155
+ border: tall $primary;
156
+ }
157
+
158
+ Input.-invalid {
159
+ border: tall $error;
160
+ }
161
+
162
+ /* ============ Selects ============ */
163
+
164
+ Select {
165
+ background: $background;
166
+ border: tall $secondary-darken-2;
167
+ }
168
+
169
+ Select:focus {
170
+ border: tall $primary;
171
+ }
172
+
173
+ SelectCurrent {
174
+ background: $background;
175
+ }
176
+
177
+ SelectOverlay {
178
+ background: $surface;
179
+ border: round $secondary-darken-2;
180
+ }
181
+
182
+ /* ============ Buttons ============ */
183
+
184
+ Button {
185
+ background: $secondary;
186
+ color: $foreground;
187
+ border: none;
188
+ }
189
+
190
+ Button:hover {
191
+ background: $secondary-lighten-1;
192
+ }
193
+
194
+ Button.-primary {
195
+ background: $primary;
196
+ color: $background;
197
+ }
198
+
199
+ Button.-primary:hover {
200
+ background: $primary-lighten-1;
201
+ }
202
+
203
+ /* ============ Footer ============ */
204
+
205
+ Footer {
206
+ background: $panel;
207
+ }
208
+
209
+ Footer > .footer--key {
210
+ background: $secondary;
211
+ color: $primary;
212
+ }
213
+
214
+ Footer > .footer--description {
215
+ color: $foreground-muted;
216
+ }
217
+
218
+ /* ============ Scrollbars ============ */
219
+
220
+ * {
221
+ scrollbar-color: $secondary-darken-2;
222
+ scrollbar-color-hover: $secondary;
223
+ scrollbar-color-active: $primary;
224
+ }
225
+
226
+ /* ============ Status Classes ============ */
227
+
228
+ .status-active {
229
+ color: $accent;
230
+ }
231
+
232
+ .status-success {
233
+ color: $success;
234
+ }
235
+
236
+ .status-error {
237
+ color: $error;
238
+ }
239
+
240
+ .status-warning {
241
+ color: $warning;
242
+ }
243
+
244
+ .status-muted {
245
+ color: $foreground-muted;
246
+ }
247
+
248
+ /* ============ Notifications ============ */
249
+
250
+ Toast {
251
+ background: $surface;
252
+ border: round $secondary-darken-2;
253
+ color: $foreground;
254
+ }
@@ -0,0 +1,93 @@
1
+ """
2
+ Ranger TUI theme definition.
3
+ """
4
+
5
+ from textual.theme import Theme
6
+
7
+ # Custom theme based on Tokyo Night with orchestration-focused colors
8
+ RANGER_THEME = Theme(
9
+ name="ranger",
10
+ primary="#7aa2f7", # Blue - primary actions
11
+ secondary="#565f89", # Muted - secondary elements
12
+ accent="#bb9af7", # Purple - highlights
13
+ foreground="#a9b1d6", # Light text
14
+ background="#1a1b26", # Dark background
15
+ success="#9ece6a", # Green - completed
16
+ warning="#e0af68", # Orange - warnings
17
+ error="#f7768e", # Red - errors
18
+ surface="#24283b", # Elevated surfaces
19
+ panel="#16161e", # Sidebar/panels
20
+ dark=True,
21
+ )
22
+
23
+ # Status symbols for consistent display
24
+ STATUS_SYMBOLS = {
25
+ # Session status
26
+ "active": "●",
27
+ "completed": "✓",
28
+ "archived": "◌",
29
+
30
+ # Task status
31
+ "pending": "○",
32
+ "queued": "◐",
33
+ "running": "●",
34
+ "failed": "✗",
35
+ "cancelled": "⊘",
36
+
37
+ # Agent status
38
+ "idle": "○",
39
+ "working": "●",
40
+ }
41
+
42
+
43
+ def format_status(status: str, include_label: bool = True) -> str:
44
+ """Format status with symbol and optional label."""
45
+ symbol = STATUS_SYMBOLS.get(status, "?")
46
+ if include_label:
47
+ return f"{symbol} {status.capitalize()}"
48
+ return symbol
49
+
50
+
51
+ def get_status_class(status: str) -> str:
52
+ """Get CSS class for status styling."""
53
+ status_classes = {
54
+ "active": "status-active",
55
+ "running": "status-active",
56
+ "working": "status-active",
57
+ "completed": "status-success",
58
+ "archived": "status-muted",
59
+ "pending": "status-muted",
60
+ "idle": "status-muted",
61
+ "queued": "status-warning",
62
+ "failed": "status-error",
63
+ "cancelled": "status-error",
64
+ }
65
+ return status_classes.get(status, "")
66
+
67
+
68
+ # Model display names with versions
69
+ MODEL_DISPLAY_NAMES = {
70
+ # Claude models
71
+ "claude-opus": "Opus 4",
72
+ "claude-sonnet": "Sonnet 4",
73
+ "claude-haiku": "Haiku 3.5",
74
+ # Gemini models
75
+ "gemini-default": "Gemini 2.5",
76
+ "gemini-2.5-pro": "Gemini 2.5 Pro",
77
+ "gemini-2.5-flash": "Gemini 2.5 Flash",
78
+ "gemini-3-pro": "Gemini 3 Pro",
79
+ # OpenAI models
80
+ "gpt-5": "GPT-5",
81
+ "gpt-5-codex": "GPT-5 Codex",
82
+ "gpt-5.2": "GPT-5.2",
83
+ # Cursor models
84
+ "sonnet-4.5": "Sonnet 4.5",
85
+ "opus-4.5": "Opus 4.5",
86
+ # Grok
87
+ "grok-default": "Grok 3",
88
+ }
89
+
90
+
91
+ def format_model(model: str) -> str:
92
+ """Format model name with version for display."""
93
+ return MODEL_DISPLAY_NAMES.get(model, model)
@@ -0,0 +1,23 @@
1
+ """
2
+ Custom widgets for Ranger TUI.
3
+ """
4
+
5
+ from ranger_tui.widgets.topbar import TopBar
6
+ from ranger_tui.widgets.sidebar import Sidebar
7
+ from ranger_tui.widgets.logo import Logo, LogoBanner, LogoSplash
8
+ from ranger_tui.widgets.accessible import (
9
+ AccessibleWidget,
10
+ AccessibleDataTable,
11
+ AccessibilityChangedMessage,
12
+ )
13
+
14
+ __all__ = [
15
+ "TopBar",
16
+ "Sidebar",
17
+ "Logo",
18
+ "LogoBanner",
19
+ "LogoSplash",
20
+ "AccessibleWidget",
21
+ "AccessibleDataTable",
22
+ "AccessibilityChangedMessage",
23
+ ]
@@ -0,0 +1,176 @@
1
+ """
2
+ Accessible widget mixin for enhanced accessibility support.
3
+
4
+ Provides accessibility features to widgets:
5
+ - ARIA-like metadata attachment
6
+ - Screen reader announcements
7
+ - Keyboard help generation
8
+ - Status announcements for dynamic updates
9
+ """
10
+
11
+ from typing import Optional, Any, Dict
12
+ from textual.widget import Widget
13
+ from textual.message import Message
14
+
15
+ from ranger_tui.accessibility import (
16
+ AccessibilityMetadata,
17
+ AccessibilityHelper,
18
+ get_widget_metadata,
19
+ )
20
+
21
+
22
+ class AccessibilityChangedMessage(Message):
23
+ """Message for accessibility state changes."""
24
+
25
+ def __init__(self, announcement: str) -> None:
26
+ super().__init__()
27
+ self.announcement = announcement
28
+
29
+
30
+ class AccessibleWidget(Widget):
31
+ """Mixin for accessible widgets with ARIA-like metadata."""
32
+
33
+ _accessibility_metadata: Optional[AccessibilityMetadata] = None
34
+ _previous_announcement: Optional[str] = None
35
+
36
+ def __init__(self, **kwargs: Any) -> None:
37
+ super().__init__(**kwargs)
38
+ # Load metadata on initialization if available
39
+ if self.id:
40
+ self._accessibility_metadata = get_widget_metadata(self.id)
41
+
42
+ def set_accessibility_metadata(
43
+ self,
44
+ metadata: AccessibilityMetadata,
45
+ ) -> None:
46
+ """Set accessibility metadata for this widget."""
47
+ self._accessibility_metadata = metadata
48
+
49
+ def get_accessibility_metadata(self) -> Optional[AccessibilityMetadata]:
50
+ """Get accessibility metadata for this widget."""
51
+ return self._accessibility_metadata
52
+
53
+ def get_accessibility_label(self) -> str:
54
+ """Get the accessibility label for this widget."""
55
+ if self._accessibility_metadata:
56
+ return self._accessibility_metadata.label
57
+ return self.id or self.__class__.__name__
58
+
59
+ def get_accessibility_announcement(self) -> str:
60
+ """Get a complete accessibility announcement for this widget."""
61
+ if not self._accessibility_metadata:
62
+ return ""
63
+ return self._accessibility_metadata.to_announcement()
64
+
65
+ def get_keyboard_help(self) -> str:
66
+ """Get keyboard help for this widget."""
67
+ if not self._accessibility_metadata:
68
+ return ""
69
+ return AccessibilityHelper.announce_keyboard_help(self.id or "")
70
+
71
+ def announce_status_change(
72
+ self,
73
+ action: str,
74
+ details: Optional[Dict[str, Any]] = None,
75
+ ) -> None:
76
+ """Announce a status change for accessibility."""
77
+ from ranger_tui.accessibility import format_accessibility_announcement
78
+
79
+ announcement = format_accessibility_announcement(action, details)
80
+ self.post_message(AccessibilityChangedMessage(announcement))
81
+ self._previous_announcement = announcement
82
+
83
+ def get_role(self) -> str:
84
+ """Get the ARIA-like role for this widget."""
85
+ if self._accessibility_metadata:
86
+ return self._accessibility_metadata.role.value
87
+ return "widget"
88
+
89
+ def get_help_text(self) -> str:
90
+ """Get the help text for this widget."""
91
+ if self._accessibility_metadata and self._accessibility_metadata.help_text:
92
+ return self._accessibility_metadata.help_text
93
+ return ""
94
+
95
+ def is_landmark(self) -> bool:
96
+ """Check if this widget is a landmark region."""
97
+ if self._accessibility_metadata:
98
+ return self._accessibility_metadata.landmark is not None
99
+ return False
100
+
101
+ def get_landmark_name(self) -> Optional[str]:
102
+ """Get the landmark name if this is a landmark region."""
103
+ if self._accessibility_metadata:
104
+ return self._accessibility_metadata.landmark
105
+ return None
106
+
107
+ def should_be_hidden_from_screen_readers(self) -> bool:
108
+ """Check if this widget should be hidden from screen readers."""
109
+ if self._accessibility_metadata:
110
+ return self._accessibility_metadata.aria_hidden
111
+ return False
112
+
113
+ def is_disabled(self) -> bool:
114
+ """Check if this widget is disabled."""
115
+ if self._accessibility_metadata:
116
+ return self._accessibility_metadata.aria_disabled
117
+ return False
118
+
119
+ def is_loading(self) -> bool:
120
+ """Check if this widget is loading or processing."""
121
+ if self._accessibility_metadata:
122
+ return self._accessibility_metadata.aria_busy
123
+ return False
124
+
125
+ def get_live_region_politeness(self) -> str:
126
+ """Get the ARIA live region politeness level."""
127
+ if self._accessibility_metadata and self._accessibility_metadata.aria_live:
128
+ return self._accessibility_metadata.aria_live
129
+ return "off"
130
+
131
+ def should_announce_all_changes(self) -> bool:
132
+ """Check if all widget changes should be announced."""
133
+ if self._accessibility_metadata:
134
+ return self._accessibility_metadata.aria_atomic
135
+ return False
136
+
137
+
138
+ class AccessibleDataTable(AccessibleWidget):
139
+ """Accessible data table widget with ARIA-like metadata."""
140
+
141
+ def get_table_description(self) -> str:
142
+ """Get description of the entire table."""
143
+ if self._accessibility_metadata:
144
+ return self._accessibility_metadata.description or ""
145
+ return ""
146
+
147
+ def get_column_headers_announcement(self) -> str:
148
+ """Get announcement of all column headers."""
149
+ # This should be implemented by subclasses
150
+ return ""
151
+
152
+ def get_current_row_announcement(self, row_index: int) -> str:
153
+ """Get announcement for the current row."""
154
+ # This should be implemented by subclasses
155
+ return ""
156
+
157
+ def announce_row_selection(self, row_index: int) -> None:
158
+ """Announce row selection."""
159
+ announcement = f"Row {row_index + 1} selected. {self.get_current_row_announcement(row_index)}"
160
+ self.post_message(AccessibilityChangedMessage(announcement))
161
+
162
+ def announce_navigation(self, direction: str, row_index: int) -> None:
163
+ """Announce navigation within the table."""
164
+ direction_text = "down" if direction == "down" else "up"
165
+ announcement = (
166
+ f"Moved {direction_text} to row {row_index + 1}. "
167
+ f"{self.get_current_row_announcement(row_index)}"
168
+ )
169
+ self.post_message(AccessibilityChangedMessage(announcement))
170
+
171
+
172
+ __all__ = [
173
+ "AccessibleWidget",
174
+ "AccessibleDataTable",
175
+ "AccessibilityChangedMessage",
176
+ ]