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.
- asher_cli-0.1.1/.github/pull_request_template.md +24 -0
- {asher_cli-0.0.5 → asher_cli-0.1.1}/.github/workflows/ci.yml +2 -2
- {asher_cli-0.0.5 → asher_cli-0.1.1}/.github/workflows/claude-code-review.yml +1 -1
- {asher_cli-0.0.5 → asher_cli-0.1.1}/.github/workflows/claude.yml +1 -1
- {asher_cli-0.0.5 → asher_cli-0.1.1}/.github/workflows/coverage.yml +1 -1
- {asher_cli-0.0.5 → asher_cli-0.1.1}/.github/workflows/release.yml +2 -2
- {asher_cli-0.0.5 → asher_cli-0.1.1}/.gitignore +3 -0
- {asher_cli-0.0.5 → asher_cli-0.1.1}/CLAUDE.md +15 -1
- asher_cli-0.1.1/CODEOWNERS +1 -0
- {asher_cli-0.0.5 → asher_cli-0.1.1}/PKG-INFO +6 -3
- {asher_cli-0.0.5 → asher_cli-0.1.1}/README.md +3 -0
- {asher_cli-0.0.5 → asher_cli-0.1.1}/ROADMAP.md +356 -232
- asher_cli-0.1.1/asher/activity_labels.py +83 -0
- {asher_cli-0.0.5 → asher_cli-0.1.1}/asher/app.py +5 -0
- {asher_cli-0.0.5 → asher_cli-0.1.1}/asher/commands/__init__.py +479 -48
- asher_cli-0.1.1/asher/connection/__init__.py +259 -0
- {asher_cli-0.0.5 → asher_cli-0.1.1}/asher/constants.py +3 -0
- asher_cli-0.1.1/asher/faults.py +146 -0
- {asher_cli-0.0.5 → asher_cli-0.1.1}/asher/monitoring/__init__.py +135 -3
- {asher_cli-0.0.5 → asher_cli-0.1.1}/asher/robot_adapters.py +60 -0
- {asher_cli-0.0.5 → asher_cli-0.1.1}/asher/robot_protocol.py +17 -1
- {asher_cli-0.0.5 → asher_cli-0.1.1}/asher/ui/__init__.py +12 -0
- {asher_cli-0.0.5 → asher_cli-0.1.1}/asher/ui/style.tcss +23 -0
- {asher_cli-0.0.5 → asher_cli-0.1.1}/pyproject.toml +4 -4
- {asher_cli-0.0.5 → asher_cli-0.1.1}/tests/conftest.py +15 -2
- asher_cli-0.1.1/tests/test_activity_labels.py +133 -0
- {asher_cli-0.0.5 → asher_cli-0.1.1}/tests/test_app_pilot.py +3 -0
- asher_cli-0.1.1/tests/test_connection.py +194 -0
- asher_cli-0.1.1/tests/test_faults.py +186 -0
- asher_cli-0.1.1/tests/test_lr5_commands.py +264 -0
- asher_cli-0.1.1/tests/test_missing_robot_commands.py +534 -0
- {asher_cli-0.0.5 → asher_cli-0.1.1}/tests/test_monitoring.py +120 -0
- {asher_cli-0.0.5 → asher_cli-0.1.1}/tests/test_new_commands_pilot.py +12 -5
- {asher_cli-0.0.5 → asher_cli-0.1.1}/tests/test_robot_adapters.py +188 -0
- {asher_cli-0.0.5 → asher_cli-0.1.1}/tests/test_ui.py +12 -0
- asher_cli-0.1.1/tests/test_version_command.py +87 -0
- {asher_cli-0.0.5 → asher_cli-0.1.1}/uv.lock +10 -10
- asher_cli-0.0.5/asher/connection/__init__.py +0 -180
- asher_cli-0.0.5/tests/test_connection.py +0 -96
- {asher_cli-0.0.5 → asher_cli-0.1.1}/.claude/hooks/block-env.ps1 +0 -0
- {asher_cli-0.0.5 → asher_cli-0.1.1}/.claude/settings.json +0 -0
- {asher_cli-0.0.5 → asher_cli-0.1.1}/.claude/skills/pylitterbot-ref/SKILL.md +0 -0
- {asher_cli-0.0.5 → asher_cli-0.1.1}/.claude/skills/release/SKILL.md +0 -0
- {asher_cli-0.0.5 → asher_cli-0.1.1}/.claude/skills/textual/SKILL.md +0 -0
- {asher_cli-0.0.5 → asher_cli-0.1.1}/.env.example +0 -0
- {asher_cli-0.0.5 → asher_cli-0.1.1}/.githooks/pre-push +0 -0
- {asher_cli-0.0.5 → asher_cli-0.1.1}/.vscode/launch.json +0 -0
- {asher_cli-0.0.5 → asher_cli-0.1.1}/.vscode/settings.json +0 -0
- {asher_cli-0.0.5 → asher_cli-0.1.1}/.vscode/tasks.json +0 -0
- {asher_cli-0.0.5 → asher_cli-0.1.1}/LICENSE +0 -0
- {asher_cli-0.0.5 → asher_cli-0.1.1}/app.py +0 -0
- {asher_cli-0.0.5 → asher_cli-0.1.1}/asher/__init__.py +0 -0
- {asher_cli-0.0.5 → asher_cli-0.1.1}/asher/__main__.py +0 -0
- {asher_cli-0.0.5 → asher_cli-0.1.1}/asher/auth.py +0 -0
- {asher_cli-0.0.5 → asher_cli-0.1.1}/asher/cats.py +0 -0
- {asher_cli-0.0.5 → asher_cli-0.1.1}/asher/commands/base.py +0 -0
- {asher_cli-0.0.5 → asher_cli-0.1.1}/asher/helpers.py +0 -0
- {asher_cli-0.0.5 → asher_cli-0.1.1}/asher/login_flow.py +0 -0
- {asher_cli-0.0.5 → asher_cli-0.1.1}/asher/mcp_bridge.py +0 -0
- {asher_cli-0.0.5 → asher_cli-0.1.1}/asher/mcp_config.py +0 -0
- {asher_cli-0.0.5 → asher_cli-0.1.1}/asher/slash-commands/__init__.py +0 -0
- {asher_cli-0.0.5 → asher_cli-0.1.1}/renovate.json +0 -0
- {asher_cli-0.0.5 → asher_cli-0.1.1}/requirements.txt +0 -0
- {asher_cli-0.0.5 → asher_cli-0.1.1}/test.py +0 -0
- {asher_cli-0.0.5 → asher_cli-0.1.1}/tests/__init__.py +0 -0
- {asher_cli-0.0.5 → asher_cli-0.1.1}/tests/test_auth.py +0 -0
- {asher_cli-0.0.5 → asher_cli-0.1.1}/tests/test_auth_pilot.py +0 -0
- {asher_cli-0.0.5 → asher_cli-0.1.1}/tests/test_cats.py +0 -0
- {asher_cli-0.0.5 → asher_cli-0.1.1}/tests/test_commands_pilot.py +0 -0
- {asher_cli-0.0.5 → asher_cli-0.1.1}/tests/test_connection_mixin.py +0 -0
- {asher_cli-0.0.5 → asher_cli-0.1.1}/tests/test_mcp_bridge.py +0 -0
- {asher_cli-0.0.5 → asher_cli-0.1.1}/tests/test_mcp_command.py +0 -0
- {asher_cli-0.0.5 → asher_cli-0.1.1}/tests/test_mcp_config.py +0 -0
- {asher_cli-0.0.5 → asher_cli-0.1.1}/tests/testhelpers.py +0 -0
- {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@
|
|
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@
|
|
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@
|
|
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@
|
|
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@
|
|
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@
|
|
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@
|
|
40
|
+
- uses: pypa/gh-action-pypi-publish@ba38be9e461d3875417946c167d0b5f3d385a247 # release/v1
|
|
41
41
|
|
|
42
42
|
github-release:
|
|
43
43
|
name: Create GitHub Release
|
|
@@ -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.
|
|
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.
|
|
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.
|
|
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`
|