asher-cli 0.0.5__tar.gz → 0.1.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 (75) hide show
  1. asher_cli-0.1.1/.github/pull_request_template.md +24 -0
  2. {asher_cli-0.0.5 → asher_cli-0.1.1}/.github/workflows/ci.yml +2 -2
  3. {asher_cli-0.0.5 → asher_cli-0.1.1}/.github/workflows/claude-code-review.yml +1 -1
  4. {asher_cli-0.0.5 → asher_cli-0.1.1}/.github/workflows/claude.yml +1 -1
  5. {asher_cli-0.0.5 → asher_cli-0.1.1}/.github/workflows/coverage.yml +1 -1
  6. {asher_cli-0.0.5 → asher_cli-0.1.1}/.github/workflows/release.yml +2 -2
  7. {asher_cli-0.0.5 → asher_cli-0.1.1}/.gitignore +3 -0
  8. {asher_cli-0.0.5 → asher_cli-0.1.1}/CLAUDE.md +15 -1
  9. asher_cli-0.1.1/CODEOWNERS +1 -0
  10. {asher_cli-0.0.5 → asher_cli-0.1.1}/PKG-INFO +6 -3
  11. {asher_cli-0.0.5 → asher_cli-0.1.1}/README.md +3 -0
  12. {asher_cli-0.0.5 → asher_cli-0.1.1}/ROADMAP.md +356 -232
  13. asher_cli-0.1.1/asher/activity_labels.py +83 -0
  14. {asher_cli-0.0.5 → asher_cli-0.1.1}/asher/app.py +5 -0
  15. {asher_cli-0.0.5 → asher_cli-0.1.1}/asher/commands/__init__.py +479 -48
  16. asher_cli-0.1.1/asher/connection/__init__.py +259 -0
  17. {asher_cli-0.0.5 → asher_cli-0.1.1}/asher/constants.py +3 -0
  18. asher_cli-0.1.1/asher/faults.py +146 -0
  19. {asher_cli-0.0.5 → asher_cli-0.1.1}/asher/monitoring/__init__.py +135 -3
  20. {asher_cli-0.0.5 → asher_cli-0.1.1}/asher/robot_adapters.py +60 -0
  21. {asher_cli-0.0.5 → asher_cli-0.1.1}/asher/robot_protocol.py +17 -1
  22. {asher_cli-0.0.5 → asher_cli-0.1.1}/asher/ui/__init__.py +12 -0
  23. {asher_cli-0.0.5 → asher_cli-0.1.1}/asher/ui/style.tcss +23 -0
  24. {asher_cli-0.0.5 → asher_cli-0.1.1}/pyproject.toml +4 -4
  25. {asher_cli-0.0.5 → asher_cli-0.1.1}/tests/conftest.py +15 -2
  26. asher_cli-0.1.1/tests/test_activity_labels.py +133 -0
  27. {asher_cli-0.0.5 → asher_cli-0.1.1}/tests/test_app_pilot.py +3 -0
  28. asher_cli-0.1.1/tests/test_connection.py +194 -0
  29. asher_cli-0.1.1/tests/test_faults.py +186 -0
  30. asher_cli-0.1.1/tests/test_lr5_commands.py +264 -0
  31. asher_cli-0.1.1/tests/test_missing_robot_commands.py +534 -0
  32. {asher_cli-0.0.5 → asher_cli-0.1.1}/tests/test_monitoring.py +120 -0
  33. {asher_cli-0.0.5 → asher_cli-0.1.1}/tests/test_new_commands_pilot.py +12 -5
  34. {asher_cli-0.0.5 → asher_cli-0.1.1}/tests/test_robot_adapters.py +188 -0
  35. {asher_cli-0.0.5 → asher_cli-0.1.1}/tests/test_ui.py +12 -0
  36. asher_cli-0.1.1/tests/test_version_command.py +87 -0
  37. {asher_cli-0.0.5 → asher_cli-0.1.1}/uv.lock +10 -10
  38. asher_cli-0.0.5/asher/connection/__init__.py +0 -180
  39. asher_cli-0.0.5/tests/test_connection.py +0 -96
  40. {asher_cli-0.0.5 → asher_cli-0.1.1}/.claude/hooks/block-env.ps1 +0 -0
  41. {asher_cli-0.0.5 → asher_cli-0.1.1}/.claude/settings.json +0 -0
  42. {asher_cli-0.0.5 → asher_cli-0.1.1}/.claude/skills/pylitterbot-ref/SKILL.md +0 -0
  43. {asher_cli-0.0.5 → asher_cli-0.1.1}/.claude/skills/release/SKILL.md +0 -0
  44. {asher_cli-0.0.5 → asher_cli-0.1.1}/.claude/skills/textual/SKILL.md +0 -0
  45. {asher_cli-0.0.5 → asher_cli-0.1.1}/.env.example +0 -0
  46. {asher_cli-0.0.5 → asher_cli-0.1.1}/.githooks/pre-push +0 -0
  47. {asher_cli-0.0.5 → asher_cli-0.1.1}/.vscode/launch.json +0 -0
  48. {asher_cli-0.0.5 → asher_cli-0.1.1}/.vscode/settings.json +0 -0
  49. {asher_cli-0.0.5 → asher_cli-0.1.1}/.vscode/tasks.json +0 -0
  50. {asher_cli-0.0.5 → asher_cli-0.1.1}/LICENSE +0 -0
  51. {asher_cli-0.0.5 → asher_cli-0.1.1}/app.py +0 -0
  52. {asher_cli-0.0.5 → asher_cli-0.1.1}/asher/__init__.py +0 -0
  53. {asher_cli-0.0.5 → asher_cli-0.1.1}/asher/__main__.py +0 -0
  54. {asher_cli-0.0.5 → asher_cli-0.1.1}/asher/auth.py +0 -0
  55. {asher_cli-0.0.5 → asher_cli-0.1.1}/asher/cats.py +0 -0
  56. {asher_cli-0.0.5 → asher_cli-0.1.1}/asher/commands/base.py +0 -0
  57. {asher_cli-0.0.5 → asher_cli-0.1.1}/asher/helpers.py +0 -0
  58. {asher_cli-0.0.5 → asher_cli-0.1.1}/asher/login_flow.py +0 -0
  59. {asher_cli-0.0.5 → asher_cli-0.1.1}/asher/mcp_bridge.py +0 -0
  60. {asher_cli-0.0.5 → asher_cli-0.1.1}/asher/mcp_config.py +0 -0
  61. {asher_cli-0.0.5 → asher_cli-0.1.1}/asher/slash-commands/__init__.py +0 -0
  62. {asher_cli-0.0.5 → asher_cli-0.1.1}/renovate.json +0 -0
  63. {asher_cli-0.0.5 → asher_cli-0.1.1}/requirements.txt +0 -0
  64. {asher_cli-0.0.5 → asher_cli-0.1.1}/test.py +0 -0
  65. {asher_cli-0.0.5 → asher_cli-0.1.1}/tests/__init__.py +0 -0
  66. {asher_cli-0.0.5 → asher_cli-0.1.1}/tests/test_auth.py +0 -0
  67. {asher_cli-0.0.5 → asher_cli-0.1.1}/tests/test_auth_pilot.py +0 -0
  68. {asher_cli-0.0.5 → asher_cli-0.1.1}/tests/test_cats.py +0 -0
  69. {asher_cli-0.0.5 → asher_cli-0.1.1}/tests/test_commands_pilot.py +0 -0
  70. {asher_cli-0.0.5 → asher_cli-0.1.1}/tests/test_connection_mixin.py +0 -0
  71. {asher_cli-0.0.5 → asher_cli-0.1.1}/tests/test_mcp_bridge.py +0 -0
  72. {asher_cli-0.0.5 → asher_cli-0.1.1}/tests/test_mcp_command.py +0 -0
  73. {asher_cli-0.0.5 → asher_cli-0.1.1}/tests/test_mcp_config.py +0 -0
  74. {asher_cli-0.0.5 → asher_cli-0.1.1}/tests/testhelpers.py +0 -0
  75. {asher_cli-0.0.5 → asher_cli-0.1.1}/watchrun.py +0 -0
@@ -0,0 +1,24 @@
1
+ ## Summary
2
+
3
+ <!-- What does this PR change, and why? -->
4
+
5
+ ## Related issue
6
+
7
+ Closes #
8
+
9
+ ## Changes
10
+
11
+ -
12
+
13
+ ## Testing
14
+
15
+ - [ ] `pytest` passes locally
16
+ - [ ] Added/updated tests for new or changed behavior
17
+ - [ ] Manually ran the CLI to verify the change (`asher ...`)
18
+
19
+ ## Checklist
20
+
21
+ - [ ] Code is linted/formatted per project conventions
22
+ - [ ] README / CLI help text updated if flags or commands changed
23
+ - [ ] No secrets, tokens, or local paths committed
24
+ - [ ] Version bump considered if this affects the published PyPI package
@@ -13,7 +13,7 @@ jobs:
13
13
  runs-on: ubuntu-latest
14
14
  steps:
15
15
  - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
16
- - uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
16
+ - uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
17
17
  - run: uv sync --dev
18
18
  - run: uv run ruff check .
19
19
  - run: uv run ruff format --check .
@@ -29,6 +29,6 @@ jobs:
29
29
  runs-on: ${{ matrix.os }}
30
30
  steps:
31
31
  - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
32
- - uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
32
+ - uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
33
33
  - run: uv sync --dev
34
34
  - run: uv run pytest tests/ -v --tb=short
@@ -33,7 +33,7 @@ jobs:
33
33
 
34
34
  - name: Run Claude Code Review
35
35
  id: claude-review
36
- uses: anthropics/claude-code-action@428971d2ecd6e3a7cb0ee0da2a3a8b33fdb3678d # v1
36
+ uses: anthropics/claude-code-action@af0559ee4f514d1ef21826982bed13f7edc3c35e # v1
37
37
  with:
38
38
  claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
39
39
  plugin_marketplaces: 'https://github.com/anthropics/claude-code.git'
@@ -32,7 +32,7 @@ jobs:
32
32
 
33
33
  - name: Run Claude Code
34
34
  id: claude
35
- uses: anthropics/claude-code-action@428971d2ecd6e3a7cb0ee0da2a3a8b33fdb3678d # v1
35
+ uses: anthropics/claude-code-action@af0559ee4f514d1ef21826982bed13f7edc3c35e # v1
36
36
  with:
37
37
  claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
38
38
 
@@ -13,7 +13,7 @@ jobs:
13
13
  runs-on: ubuntu-latest
14
14
  steps:
15
15
  - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
16
- - uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
16
+ - uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
17
17
  - run: uv sync --dev
18
18
  - name: Run tests with coverage
19
19
  run: uv run pytest tests/ --cov=asher --cov-report=lcov --cov-report=term-missing
@@ -20,7 +20,7 @@ jobs:
20
20
  - name: Extract version from branch name
21
21
  id: version
22
22
  run: echo "version=${GITHUB_REF_NAME#release/}" >> "$GITHUB_OUTPUT"
23
- - uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
23
+ - uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
24
24
  - run: uv build
25
25
  - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
26
26
  with:
@@ -37,7 +37,7 @@ jobs:
37
37
  with:
38
38
  name: dist
39
39
  path: dist/
40
- - uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # release/v1
40
+ - uses: pypa/gh-action-pypi-publish@ba38be9e461d3875417946c167d0b5f3d385a247 # release/v1
41
41
 
42
42
  github-release:
43
43
  name: Create GitHub Release
@@ -216,3 +216,6 @@ __marimo__/
216
216
 
217
217
  # Streamlit
218
218
  .streamlit/secrets.toml
219
+
220
+ # ZCode editor state
221
+ .zcode/
@@ -43,6 +43,7 @@ asher/
43
43
  robot_adapters.py RobotAdapter ABC + LR3/LR4/LR5 subclasses + make_adapter() factory
44
44
  mcp_config.py Claude Desktop config read/write for the /mcp slash command
45
45
  mcp_bridge.py asher-mcp-launch console script — keyring-backed pylitterbot MCP launcher
46
+ faults.py check_faults(robot) — model-scoped safety/component fault detection (status enum + per-model attr allowlist; hopper never a fault)
46
47
  __main__.py main() entry point
47
48
  commands/
48
49
  base.py Command ABC, SlashCommand, CommandRegistry
@@ -66,6 +67,7 @@ tests/
66
67
  test_mcp_config.py Claude Desktop config read/write
67
68
  test_mcp_bridge.py mcp_bridge launcher credential/subprocess handling
68
69
  test_mcp_command.py /mcp slash command dispatch
70
+ test_faults.py check_faults() — safety statuses, attribute faults, graceful degradation
69
71
 
70
72
  .github/workflows/
71
73
  ci.yml ruff + mypy + pytest on every push/PR
@@ -123,6 +125,11 @@ AsherApp (textual.App)
123
125
  ├── #main-area
124
126
  │ ├── #log RichLog — scrollable event/command output
125
127
  │ └── #cat-panel animated ASCII cat sidebar
128
+ │ ├── #cat-fx animated FX strip
129
+ │ ├── #cat-art the ASCII cat
130
+ │ ├── #cat-label mode label (connected / cycling… / fault!)
131
+ │ ├── #cat-status complementary badges (status, power, cycles, wait time) — no lock/night-light (those are top-row only)
132
+ │ └── #fault-banner hidden unless check_faults() returns active faults; `d` dismisses
126
133
  └── #bottom-dock bottom dock
127
134
  ├── #input-bar / #input-row command prompt ("> " label + CmdInput)
128
135
  └── #hint-bar shortcut hints / login flow prompts
@@ -135,7 +142,10 @@ LoginScreen (ModalScreen) — available in auth.py but not the primary auth path
135
142
  | Method | Purpose |
136
143
  |---|---|
137
144
  | `_connect_worker()` | `@work` — resolve credentials (keyring → .env → inline login), authenticate |
138
- | `_refresh_status()` | update all header widgets from robot state |
145
+ | `_refresh_status()` | update all header widgets + cat panel + fault banner from robot state |
146
+ | `_update_cat_panel(robot)` | render `#cat-label` + `#cat-status` (complementary: status, power, cycles, wait); called from `_refresh_status` |
147
+ | `_refresh_faults(robot)` | run `check_faults()`, render `#fault-banner`, log transitions; sets cat mode to `error` while faults active |
148
+ | `_cycling_chip()` / `_start_cycle_timer()` / `_stop_cycle_timer()` / `_tick_cycle()` | `⟳ Cycling M:SS` chip + lazy 1s elapsed timer |
139
149
  | `_poll_status_interval()` | `@work` — poll fallback every 300s (5 min); WebSocket is primary |
140
150
  | `_tick_cat()` | advances multi-frame cat animation every 0.4s |
141
151
  | `_dispatch_command(command, args)` | `@work` — calls `command.run(app, args)` from the registry |
@@ -159,6 +169,10 @@ pylitterbot auto-detects robot type. Commands that differ per model are handled
159
169
  - `robot.set_night_light_brightness(int)` or `robot.set_night_light_mode(NightLightMode)`
160
170
  - `robot.get_activity_history(limit=int)` → list of `Activity` objects with `.timestamp` and `.action` (`LitterBoxStatus` enum)
161
171
 
172
+ ## Code comments
173
+
174
+ Don't add comments above functions or inline unless the WHY is genuinely non-obvious (a hidden constraint, a subtle invariant, a workaround for a specific bug). Well-named identifiers should make the WHAT self-evident. Before reaching for a comment, check whether the explanation can instead be expressed through abstraction or encapsulation — e.g. domain logic buried in a mixin or command handler should move to a self-commenting, domain-named method rather than being explained in a comment. Favor human-readable, domain-driven names and logical flow over prose explanations, while keeping code legible to agents working in this repo.
175
+
162
176
  ## Development notes
163
177
 
164
178
  - Textual and pylitterbot are both asyncio-native — compose cleanly with `@work` tasks
@@ -0,0 +1 @@
1
+ * @karanshukla
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: asher-cli
3
- Version: 0.0.5
3
+ Version: 0.1.1
4
4
  Summary: Terminal dashboard for Litter Robot (LR3/LR4/LR5) via the Whisker cloud API
5
5
  License: MIT License
6
6
 
@@ -35,13 +35,13 @@ Classifier: Programming Language :: Python :: 3.12
35
35
  Classifier: Topic :: Home Automation
36
36
  Requires-Python: >=3.10
37
37
  Requires-Dist: keyring>=24.0.0
38
- Requires-Dist: pylitterbot==2025.5.0
38
+ Requires-Dist: pylitterbot==2025.6.2
39
39
  Requires-Dist: python-dotenv>=1.0.0
40
40
  Requires-Dist: rich>=13.0.0
41
41
  Requires-Dist: textual>=0.47.0
42
42
  Requires-Dist: tzlocal>=5.4
43
43
  Provides-Extra: mcp
44
- Requires-Dist: pylitterbot[mcp]==2025.5.0; extra == 'mcp'
44
+ Requires-Dist: pylitterbot[mcp]==2025.6.2; extra == 'mcp'
45
45
  Description-Content-Type: text/markdown
46
46
 
47
47
  # Asher CLI
@@ -62,6 +62,9 @@ A Claude Code-style terminal dashboard for monitoring and controlling Litter Rob
62
62
 
63
63
  - Live status bar — unit name, online/offline, drawer fill level, last activity, cat weight
64
64
  - Human-readable robot status — translates raw API states into plain English (`Ready`, `Cleaning`, `Cat Detected`, `Drawer Full`, etc.)
65
+ - Real-time cycling indicator with elapsed time (`⟳ Cycling M:SS`)
66
+ - Fault & safety monitoring — model-scoped in-panel alerts for cat detected, pinch, motor/position/gas faults (LR5: bonnet/laser/drawer); press `d` to dismiss
67
+ - Cat panel with mode label + status badges (status chip, lock, night light, sleep, wait time) under the art
65
68
  - Scrollable activity log with timestamps
66
69
  - Commands: `clean`, `status`, `lock`, `unlock`, `sleep`, `wake`, `night-light on|off|auto`, `night-light-brightness`, `history`, `export [days|month]`, `help`, `quit`
67
70
  - Slash commands for app management: `/login`, `/logout`, `/robots`, `/robot <index|name>`, `/pets`, `/pet <index|name>`, `/cat on|off|color <hex>`, `/refresh [seconds|off]`, `/config`, `/exit`
@@ -16,6 +16,9 @@ A Claude Code-style terminal dashboard for monitoring and controlling Litter Rob
16
16
 
17
17
  - Live status bar — unit name, online/offline, drawer fill level, last activity, cat weight
18
18
  - Human-readable robot status — translates raw API states into plain English (`Ready`, `Cleaning`, `Cat Detected`, `Drawer Full`, etc.)
19
+ - Real-time cycling indicator with elapsed time (`⟳ Cycling M:SS`)
20
+ - Fault & safety monitoring — model-scoped in-panel alerts for cat detected, pinch, motor/position/gas faults (LR5: bonnet/laser/drawer); press `d` to dismiss
21
+ - Cat panel with mode label + status badges (status chip, lock, night light, sleep, wait time) under the art
19
22
  - Scrollable activity log with timestamps
20
23
  - Commands: `clean`, `status`, `lock`, `unlock`, `sleep`, `wake`, `night-light on|off|auto`, `night-light-brightness`, `history`, `export [days|month]`, `help`, `quit`
21
24
  - Slash commands for app management: `/login`, `/logout`, `/robots`, `/robot <index|name>`, `/pets`, `/pet <index|name>`, `/cat on|off|color <hex>`, `/refresh [seconds|off]`, `/config`, `/exit`