asher-cli 0.1.1__tar.gz → 1.0.0__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 (95) hide show
  1. {asher_cli-0.1.1 → asher_cli-1.0.0}/.claude/settings.json +6 -0
  2. {asher_cli-0.1.1 → asher_cli-1.0.0}/.github/workflows/ci.yml +4 -4
  3. {asher_cli-0.1.1 → asher_cli-1.0.0}/.github/workflows/claude-code-review.yml +2 -2
  4. {asher_cli-0.1.1 → asher_cli-1.0.0}/.github/workflows/claude.yml +2 -2
  5. {asher_cli-0.1.1 → asher_cli-1.0.0}/.github/workflows/coverage.yml +2 -2
  6. {asher_cli-0.1.1 → asher_cli-1.0.0}/.github/workflows/release.yml +19 -4
  7. asher_cli-1.0.0/CHANGELOG.md +87 -0
  8. {asher_cli-0.1.1 → asher_cli-1.0.0}/CLAUDE.md +22 -5
  9. {asher_cli-0.1.1 → asher_cli-1.0.0}/PKG-INFO +99 -15
  10. {asher_cli-0.1.1 → asher_cli-1.0.0}/README.md +93 -14
  11. asher_cli-1.0.0/asher/__main__.py +58 -0
  12. {asher_cli-0.1.1 → asher_cli-1.0.0}/asher/app.py +16 -5
  13. {asher_cli-0.1.1 → asher_cli-1.0.0}/asher/commands/__init__.py +284 -106
  14. asher_cli-1.0.0/asher/completion.py +113 -0
  15. asher_cli-1.0.0/asher/config.py +66 -0
  16. {asher_cli-0.1.1 → asher_cli-1.0.0}/asher/connection/__init__.py +63 -0
  17. asher_cli-1.0.0/asher/export.py +236 -0
  18. {asher_cli-0.1.1 → asher_cli-1.0.0}/asher/faults.py +6 -0
  19. asher_cli-1.0.0/asher/history_view.py +228 -0
  20. {asher_cli-0.1.1 → asher_cli-1.0.0}/asher/monitoring/__init__.py +19 -1
  21. asher_cli-1.0.0/asher/notifications.py +48 -0
  22. {asher_cli-0.1.1 → asher_cli-1.0.0}/asher/robot_adapters.py +47 -1
  23. {asher_cli-0.1.1 → asher_cli-1.0.0}/asher/robot_protocol.py +5 -1
  24. asher_cli-1.0.0/asher/slash-commands/__init__.py +44 -0
  25. {asher_cli-0.1.1 → asher_cli-1.0.0}/asher/ui/__init__.py +40 -2
  26. {asher_cli-0.1.1 → asher_cli-1.0.0}/asher/ui/style.tcss +17 -0
  27. asher_cli-1.0.0/cliff.toml +102 -0
  28. {asher_cli-0.1.1 → asher_cli-1.0.0/docs}/ROADMAP.md +140 -820
  29. asher_cli-1.0.0/docs/roadmap-archive/README.md +16 -0
  30. asher_cli-1.0.0/docs/roadmap-archive/cat-panel-badges.md +100 -0
  31. asher_cli-1.0.0/docs/roadmap-archive/config-persistence.md +42 -0
  32. asher_cli-1.0.0/docs/roadmap-archive/desktop-notifications.md +123 -0
  33. asher_cli-1.0.0/docs/roadmap-archive/fault-monitoring.md +131 -0
  34. asher_cli-1.0.0/docs/roadmap-archive/headless-export.md +180 -0
  35. asher_cli-1.0.0/docs/roadmap-archive/history-export.md +117 -0
  36. asher_cli-1.0.0/docs/roadmap-archive/tab-completion.md +90 -0
  37. {asher_cli-0.1.1 → asher_cli-1.0.0}/pyproject.toml +11 -2
  38. {asher_cli-0.1.1 → asher_cli-1.0.0}/tests/conftest.py +2 -0
  39. {asher_cli-0.1.1 → asher_cli-1.0.0}/tests/test_commands_pilot.py +2 -2
  40. asher_cli-1.0.0/tests/test_completion.py +571 -0
  41. asher_cli-1.0.0/tests/test_config.py +357 -0
  42. asher_cli-1.0.0/tests/test_export.py +416 -0
  43. {asher_cli-0.1.1 → asher_cli-1.0.0}/tests/test_faults.py +29 -0
  44. asher_cli-1.0.0/tests/test_history_view.py +276 -0
  45. {asher_cli-0.1.1 → asher_cli-1.0.0}/tests/test_lr5_commands.py +63 -0
  46. {asher_cli-0.1.1 → asher_cli-1.0.0}/tests/test_monitoring.py +83 -1
  47. {asher_cli-0.1.1 → asher_cli-1.0.0}/tests/test_new_commands_pilot.py +17 -1
  48. asher_cli-1.0.0/tests/test_notifications.py +82 -0
  49. {asher_cli-0.1.1 → asher_cli-1.0.0}/tests/test_robot_adapters.py +88 -0
  50. {asher_cli-0.1.1 → asher_cli-1.0.0}/uv.lock +38 -5
  51. asher_cli-0.1.1/asher/__main__.py +0 -11
  52. asher_cli-0.1.1/asher/slash-commands/__init__.py +0 -19
  53. {asher_cli-0.1.1 → asher_cli-1.0.0}/.claude/hooks/block-env.ps1 +0 -0
  54. {asher_cli-0.1.1 → asher_cli-1.0.0}/.claude/skills/pylitterbot-ref/SKILL.md +0 -0
  55. {asher_cli-0.1.1 → asher_cli-1.0.0}/.claude/skills/release/SKILL.md +0 -0
  56. {asher_cli-0.1.1 → asher_cli-1.0.0}/.claude/skills/textual/SKILL.md +0 -0
  57. {asher_cli-0.1.1 → asher_cli-1.0.0}/.env.example +0 -0
  58. {asher_cli-0.1.1 → asher_cli-1.0.0}/.githooks/pre-push +0 -0
  59. {asher_cli-0.1.1 → asher_cli-1.0.0}/.github/pull_request_template.md +0 -0
  60. {asher_cli-0.1.1 → asher_cli-1.0.0}/.gitignore +0 -0
  61. {asher_cli-0.1.1 → asher_cli-1.0.0}/.vscode/launch.json +0 -0
  62. {asher_cli-0.1.1 → asher_cli-1.0.0}/.vscode/settings.json +0 -0
  63. {asher_cli-0.1.1 → asher_cli-1.0.0}/.vscode/tasks.json +0 -0
  64. {asher_cli-0.1.1 → asher_cli-1.0.0}/CODEOWNERS +0 -0
  65. {asher_cli-0.1.1 → asher_cli-1.0.0}/LICENSE +0 -0
  66. {asher_cli-0.1.1 → asher_cli-1.0.0}/app.py +0 -0
  67. {asher_cli-0.1.1 → asher_cli-1.0.0}/asher/__init__.py +0 -0
  68. {asher_cli-0.1.1 → asher_cli-1.0.0}/asher/activity_labels.py +0 -0
  69. {asher_cli-0.1.1 → asher_cli-1.0.0}/asher/auth.py +0 -0
  70. {asher_cli-0.1.1 → asher_cli-1.0.0}/asher/cats.py +0 -0
  71. {asher_cli-0.1.1 → asher_cli-1.0.0}/asher/commands/base.py +0 -0
  72. {asher_cli-0.1.1 → asher_cli-1.0.0}/asher/constants.py +0 -0
  73. {asher_cli-0.1.1 → asher_cli-1.0.0}/asher/helpers.py +0 -0
  74. {asher_cli-0.1.1 → asher_cli-1.0.0}/asher/login_flow.py +0 -0
  75. {asher_cli-0.1.1 → asher_cli-1.0.0}/asher/mcp_bridge.py +0 -0
  76. {asher_cli-0.1.1 → asher_cli-1.0.0}/asher/mcp_config.py +0 -0
  77. {asher_cli-0.1.1 → asher_cli-1.0.0}/renovate.json +0 -0
  78. {asher_cli-0.1.1 → asher_cli-1.0.0}/requirements.txt +0 -0
  79. {asher_cli-0.1.1 → asher_cli-1.0.0}/test.py +0 -0
  80. {asher_cli-0.1.1 → asher_cli-1.0.0}/tests/__init__.py +0 -0
  81. {asher_cli-0.1.1 → asher_cli-1.0.0}/tests/test_activity_labels.py +0 -0
  82. {asher_cli-0.1.1 → asher_cli-1.0.0}/tests/test_app_pilot.py +0 -0
  83. {asher_cli-0.1.1 → asher_cli-1.0.0}/tests/test_auth.py +0 -0
  84. {asher_cli-0.1.1 → asher_cli-1.0.0}/tests/test_auth_pilot.py +0 -0
  85. {asher_cli-0.1.1 → asher_cli-1.0.0}/tests/test_cats.py +0 -0
  86. {asher_cli-0.1.1 → asher_cli-1.0.0}/tests/test_connection.py +0 -0
  87. {asher_cli-0.1.1 → asher_cli-1.0.0}/tests/test_connection_mixin.py +0 -0
  88. {asher_cli-0.1.1 → asher_cli-1.0.0}/tests/test_mcp_bridge.py +0 -0
  89. {asher_cli-0.1.1 → asher_cli-1.0.0}/tests/test_mcp_command.py +0 -0
  90. {asher_cli-0.1.1 → asher_cli-1.0.0}/tests/test_mcp_config.py +0 -0
  91. {asher_cli-0.1.1 → asher_cli-1.0.0}/tests/test_missing_robot_commands.py +0 -0
  92. {asher_cli-0.1.1 → asher_cli-1.0.0}/tests/test_ui.py +0 -0
  93. {asher_cli-0.1.1 → asher_cli-1.0.0}/tests/test_version_command.py +0 -0
  94. {asher_cli-0.1.1 → asher_cli-1.0.0}/tests/testhelpers.py +0 -0
  95. {asher_cli-0.1.1 → asher_cli-1.0.0}/watchrun.py +0 -0
@@ -1,4 +1,10 @@
1
1
  {
2
+ "enabledPlugins": {
3
+ "context7@claude-plugins-official": true,
4
+ "github@claude-plugins-official": true,
5
+ "security-guidance@claude-plugins-official": true,
6
+ "claude-md-management@claude-plugins-official": true
7
+ },
2
8
  "permissions": {
3
9
  "allow": [
4
10
  "Bash(uv run *)",
@@ -12,8 +12,8 @@ jobs:
12
12
  lint:
13
13
  runs-on: ubuntu-latest
14
14
  steps:
15
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
16
- - uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
15
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
16
+ - uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
17
17
  - run: uv sync --dev
18
18
  - run: uv run ruff check .
19
19
  - run: uv run ruff format --check .
@@ -28,7 +28,7 @@ jobs:
28
28
  os: [ubuntu-latest, windows-latest, macos-latest]
29
29
  runs-on: ${{ matrix.os }}
30
30
  steps:
31
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
32
- - uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
31
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
32
+ - uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
33
33
  - run: uv sync --dev
34
34
  - run: uv run pytest tests/ -v --tb=short
@@ -27,13 +27,13 @@ jobs:
27
27
 
28
28
  steps:
29
29
  - name: Checkout repository
30
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
30
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
31
31
  with:
32
32
  fetch-depth: 1
33
33
 
34
34
  - name: Run Claude Code Review
35
35
  id: claude-review
36
- uses: anthropics/claude-code-action@af0559ee4f514d1ef21826982bed13f7edc3c35e # v1
36
+ uses: anthropics/claude-code-action@be7b93b1907a4abad570368f3c74b6fe3807510b # 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'
@@ -26,13 +26,13 @@ jobs:
26
26
  actions: read # Required for Claude to read CI results on PRs
27
27
  steps:
28
28
  - name: Checkout repository
29
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
29
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
30
30
  with:
31
31
  fetch-depth: 1
32
32
 
33
33
  - name: Run Claude Code
34
34
  id: claude
35
- uses: anthropics/claude-code-action@af0559ee4f514d1ef21826982bed13f7edc3c35e # v1
35
+ uses: anthropics/claude-code-action@be7b93b1907a4abad570368f3c74b6fe3807510b # v1
36
36
  with:
37
37
  claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
38
38
 
@@ -12,8 +12,8 @@ jobs:
12
12
  coverage:
13
13
  runs-on: ubuntu-latest
14
14
  steps:
15
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
16
- - uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
15
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
16
+ - uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
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
@@ -16,11 +16,11 @@ jobs:
16
16
  outputs:
17
17
  version: ${{ steps.version.outputs.version }}
18
18
  steps:
19
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
19
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
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@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
23
+ - uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
24
24
  - run: uv build
25
25
  - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
26
26
  with:
@@ -44,11 +44,26 @@ jobs:
44
44
  needs: [build, publish]
45
45
  runs-on: ubuntu-latest
46
46
  steps:
47
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
47
+ # Full history (fetch-depth: 0) so git-cliff can walk commits/tags.
48
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
49
+ with:
50
+ fetch-depth: 0
48
51
  - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
49
52
  with:
50
53
  name: dist
51
54
  path: dist/
55
+ - uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
56
+ - name: Install git-cliff
57
+ run: uv tool install git-cliff
58
+ - name: Generate release notes with git-cliff
59
+ env:
60
+ VERSION: ${{ needs.build.outputs.version }}
61
+ run: |
62
+ # Explicit -c cliff.toml avoids auto-discovering pyproject.toml.
63
+ # Pin the just-released version so git-cliff groups its commits under
64
+ # the right heading even before the tag is fetched, then emit only
65
+ # that section (--latest) with the header stripped.
66
+ git cliff -c cliff.toml --tag "v${VERSION}" --latest --strip header -o RELEASE_NOTES.md
52
67
  - name: Create release
53
68
  env:
54
69
  GH_TOKEN: ${{ github.token }}
@@ -56,5 +71,5 @@ jobs:
56
71
  run: |
57
72
  gh release create "v${VERSION}" dist/* \
58
73
  --title "v${VERSION}" \
59
- --generate-notes \
74
+ --notes-file RELEASE_NOTES.md \
60
75
  --verify-tag=false
@@ -0,0 +1,87 @@
1
+ # Changelog
2
+
3
+ All notable changes to asher-cli are documented here.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ Generated by [git-cliff](https://git-cliff.org) from conventional commits.
9
+
10
+ ## [Unreleased]
11
+
12
+ ### 🚀 Features
13
+
14
+ - *(robot)* Complete LR3/LR4 non-destructive support
15
+ - *(notify)* Desktop toast notifications for fault events + /notify command
16
+ - *(commands)* Accept /cat colour spelling (color as alias)
17
+ - *(config)* Persist runtime settings to ~/.asher-cli/config.json
18
+
19
+ ### 📚 Documentation
20
+
21
+ - *(roadmap)* Archive completed sections to docs/roadmap-archive/
22
+
23
+ ### 🔧 Miscellaneous
24
+
25
+ - *(release)* Move ROADMAP to docs/, add git-cliff CHANGELOG automation
26
+
27
+ ## [0.2.0] - 2026-07-27
28
+
29
+ ### 🚀 Features
30
+
31
+ - *(export)* Headless --export for cron/automation (ROADMAP §25)
32
+ - *(completion)* Inline ghost-text suggestions for bare commands
33
+ - *(completion)* Claude Code-style slash-command tab completion
34
+ - *(history)* Aligned timestamps + rich pager header/footer
35
+ - *(history)* Scrollable pager sub-view for activity history
36
+
37
+ ### 📚 Documentation
38
+
39
+ - *(changelog)* Cut v0.2.0
40
+ - *(commands)* Sync stale command lists in CLAUDE.md and slash-commands docstring
41
+ - *(readme)* Sync command tables with code, add changelog
42
+
43
+ ### 🔧 Miscellaneous
44
+
45
+ - Declare repo-level Claude Code plugins
46
+
47
+ ## [0.1.1] - 2026-07-21
48
+
49
+ ### 🚀 Features
50
+
51
+ - *(commands)* Add /version slash command and sleep-schedule viewer
52
+ - *(connection)* Persist OAuth session token in keyring
53
+ - *(ui)* Cat panel badges, fault banner, cycling elapsed timer
54
+
55
+ ### 🐛 Bug Fixes
56
+
57
+ - *(faults,panel)* Model-scoped detection; aligned complementary badges
58
+ - *(faults)* Enum-aware detection; restore top rows; banner in cat panel
59
+
60
+ ### 📚 Documentation
61
+
62
+ - *(roadmap)* Mark token persistence, /version, sleep-schedule done
63
+
64
+ ## [0.1.0] - 2026-07-20
65
+
66
+ ### 🚀 Features
67
+
68
+ - *(lr5)* Wire up privacy, volume, camera-audio, drawer-reset
69
+ - *(commands)* Split status/info, add info; fix flaky export test
70
+ - *(commands)* Add wait-time, power, rename, insight
71
+ - *(history)* Render readable, colour-coded event labels
72
+
73
+ ### 🐛 Bug Fixes
74
+
75
+ - *(status)* Render readable status text, not enum repr
76
+
77
+ ### 📚 Documentation
78
+
79
+ - *(roadmap)* Mark testing/coverage/versioning/CI as done
80
+ - *(roadmap)* Mark LR5 extras (privacy/volume/camera-audio/drawer-reset) done
81
+ - *(roadmap)* Mark wait-time/power/rename/insight + status/info split done
82
+
83
+ ### 🔧 Miscellaneous
84
+
85
+ - Add CODEOWNERS to auto-request review on PRs
86
+
87
+ <!-- generated by git-cliff -->
@@ -37,18 +37,23 @@ asher/
37
37
  auth.py LoginScreen modal (ModalScreen[tuple[str,str]]) — available, not primary flow
38
38
  helpers.py fmt_ago(), drawer_bar(), ts(), robot_model() (pure, testable)
39
39
  constants.py STATUS_COLORS, ROBOT_MODELS
40
+ config.py runtime settings persistence — load()/save()/update() over ~/.asher-cli/config.json; holds poll interval, cat-panel visibility/colour, active pet index, notification settings (non-secret UI prefs only; credentials stay in keyring)
41
+ notifications.py desktop toast + audible alert façade over plyer (fire/beep, always-safe no-op on failure/headless)
40
42
  cats.py CATS dict (ASCII art)
41
43
  login_flow.py LoginFlow state machine — inline email/password prompt in command bar
42
44
  robot_protocol.py RobotProtocol structural Protocol for pylitterbot robot objects
43
45
  robot_adapters.py RobotAdapter ABC + LR3/LR4/LR5 subclasses + make_adapter() factory
44
46
  mcp_config.py Claude Desktop config read/write for the /mcp slash command
45
47
  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)
48
+ faults.py check_faults(robot) — model-scoped safety/component fault detection (status enum + per-model attr allowlist incl. LR4 USB power fault; hopper never a fault)
49
+ history_view.py HistoryScreen (ModalScreen) + format_history_rows() — scrollable activity-history pager pushed by the `history` command
50
+ export.py shared activity-history CSV core + headless export path: build_history_csv(), resolve_dest(), resolve_robot(), _run_headless_export(), ExportError — no Textual imports; both the TUI `export` command and `asher --export` call build_history_csv()
51
+ completion.py pure helpers for command completion: slash popup (slash_matches, enter_completes, render_completion) + inline ghost text (CommandSuggester) — fed by _registry, no Textual imports except the Suggester base class
47
52
  __main__.py main() entry point
48
53
  commands/
49
54
  base.py Command ABC, SlashCommand, CommandRegistry
50
55
  __init__.py CommandsMixin — all command classes + registry + dispatch
51
- connection/ ConnectionMixin — keyring auth, _connect_worker, keyring helpers
56
+ connection/ ConnectionMixin — keyring auth, _connect_worker, keyring helpers, _connect_headless() (no-UI auth for `asher --export`)
52
57
  monitoring/ MonitoringMixin — _poll_status_interval, _refresh_status
53
58
  ui/ UIMixin — CSS, compose(), log helpers, cat helpers
54
59
  slash-commands/ Convention doc
@@ -65,9 +70,14 @@ tests/
65
70
  test_monitoring.py MonitoringMixin async methods
66
71
  test_ui.py UIMixin constants, CSS, helper existence
67
72
  test_mcp_config.py Claude Desktop config read/write
73
+ test_config.py runtime settings persistence — load/save/update over defaults
74
+ test_notifications.py plyer toast + beep façade — always-safe no-op paths
68
75
  test_mcp_bridge.py mcp_bridge launcher credential/subprocess handling
69
76
  test_mcp_command.py /mcp slash command dispatch
70
77
  test_faults.py check_faults() — safety statuses, attribute faults, graceful degradation
78
+ test_history_view.py format_history_rows() + HistoryScreen structure + Pilot push/dismiss
79
+ test_export.py build_history_csv/resolve_dest/resolve_robot/parse_days (pure) + headless _run_headless_export (no Pilot, mocks _connect_headless)
80
+ test_completion.py slash_matches/enter_completes/render_completion (pure) + Pilot overlay visibility/navigation/accept
71
81
 
72
82
  .github/workflows/
73
83
  ci.yml ruff + mypy + pytest on every push/PR
@@ -103,10 +113,14 @@ pylitterbot ships an optional MCP server (`pip install pylitterbot[mcp]`, run vi
103
113
  ## Command convention
104
114
 
105
115
  **Normal commands** (no prefix) — robot actions only:
106
- `clean`, `status`, `lock`, `unlock`, `sleep`, `wake`, `night-light on|off|auto`, `night-light-brightness <level>`, `history`, `clear`, `help`
116
+ `clean`, `status`, `info`, `lock`, `unlock`, `sleep`, `wake`, `night-light on|off|auto`, `night-light-brightness <level>`, `panel-brightness <low|medium|high>`, `wait-time <minutes>`, `power on|off`, `rename <name>`, `insight [days|month]`, `sleep-schedule`, `privacy on|off`, `volume <0-100>`, `camera-audio on|off`, `drawer-reset`, `history [count|all]`, `export [days|month]`, `clear`, `help`, `quit`
107
117
 
108
118
  **Slash commands** (`/` prefix) — app management only:
109
- `/login`, `/logout`, `/exit`, `/robots`, `/robot <index|name>`, `/pets`, `/pet <index|name>`, `/cat on|off|color <hex>`, `/refresh <seconds|off>`, `/config`, `/mcp on|off|status`
119
+ `/login`, `/logout`, `/robots`, `/robot <index|name>`, `/pets`, `/pet <index|name>`, `/cat on|off|colour <hex>`, `/refresh [seconds|off]`, `/config`, `/notify on|off|sound on|off|test`, `/version`, `/mcp on|off|status`, `/exit`
120
+
121
+ `/refresh`, `/cat`, `/pet`, and `/notify` persist their settings to `~/.asher-cli/config.json` (via `asher.config.update()`), so they survive restarts. Credentials and the preferred-robot serial stay in the OS keyring; the config file holds only non-secret UI preferences.
122
+
123
+ > The authoritative list is the `_registry` in `asher/commands/__init__.py`; `/help` renders it at runtime. If you add a command, update the tables in `README.md` and the list in `asher/slash-commands/__init__.py`.
110
124
 
111
125
  **Special cases** (accepted both with and without `/`):
112
126
  `exit`, `quit`, `q` — exit the app
@@ -124,6 +138,7 @@ AsherApp (textual.App)
124
138
  ├── #status-bar top dock — two rows (top: name/online/night-light/lock; bottom: drawer/litter/weight/visit)
125
139
  ├── #main-area
126
140
  │ ├── #log RichLog — scrollable event/command output
141
+ │ ├── #completion-overlay Static — floating slash-command completion list (overlay: screen; hidden unless typing /); does not reserve layout space
127
142
  │ └── #cat-panel animated ASCII cat sidebar
128
143
  │ ├── #cat-fx animated FX strip
129
144
  │ ├── #cat-art the ASCII cat
@@ -149,7 +164,9 @@ LoginScreen (ModalScreen) — available in auth.py but not the primary auth path
149
164
  | `_poll_status_interval()` | `@work` — poll fallback every 300s (5 min); WebSocket is primary |
150
165
  | `_tick_cat()` | advances multi-frame cat animation every 0.4s |
151
166
  | `_dispatch_command(command, args)` | `@work` — calls `command.run(app, args)` from the registry |
152
- | `on_input_submitted()` | routes input to login flow or `_dispatch_command` via `CommandRegistry` |
167
+ | `on_input_submitted()` | routes input to login flow or `_dispatch_command` via `CommandRegistry`; Enter on a partial `/cmd` completes it instead of submitting |
168
+ | `on_input_changed()` | live-filters the `#completion-overlay` as the user types (slash commands only; hides once a space is present); also clears ghost text during the login flow |
169
+ | `on_key()` | `↑`/`↓` history nav, plus completion nav (arrows cycle the slash popup, `Tab`/`Enter` accept, `Esc` dismisses); `Tab` also accepts the inline ghost-text suggestion when the popup is closed |
153
170
  | `_start_login_flow()` | begin inline email/password prompt in command bar |
154
171
  | `_cmd_logout()` | delete creds from keyring, disconnect |
155
172
  | `make_adapter(robot)` | factory in `robot_adapters.py` — returns correct `RobotAdapter` subclass |
@@ -1,7 +1,11 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: asher-cli
3
- Version: 0.1.1
3
+ Version: 1.0.0
4
4
  Summary: Terminal dashboard for Litter Robot (LR3/LR4/LR5) via the Whisker cloud API
5
+ Project-URL: Homepage, https://github.com/karanshukla/asher-cli
6
+ Project-URL: Repository, https://github.com/karanshukla/asher-cli
7
+ Project-URL: Issues, https://github.com/karanshukla/asher-cli/issues
8
+ Project-URL: Changelog, https://github.com/karanshukla/asher-cli/blob/main/CHANGELOG.md
5
9
  License: MIT License
6
10
 
7
11
  Copyright (c) 2026 Karz
@@ -35,6 +39,7 @@ Classifier: Programming Language :: Python :: 3.12
35
39
  Classifier: Topic :: Home Automation
36
40
  Requires-Python: >=3.10
37
41
  Requires-Dist: keyring>=24.0.0
42
+ Requires-Dist: plyer>=2.1.0
38
43
  Requires-Dist: pylitterbot==2025.6.2
39
44
  Requires-Dist: python-dotenv>=1.0.0
40
45
  Requires-Dist: rich>=13.0.0
@@ -65,9 +70,12 @@ A Claude Code-style terminal dashboard for monitoring and controlling Litter Rob
65
70
  - Real-time cycling indicator with elapsed time (`⟳ Cycling M:SS`)
66
71
  - 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
72
  - Cat panel with mode label + status badges (status chip, lock, night light, sleep, wait time) under the art
68
- - Scrollable activity log with timestamps
69
- - Commands: `clean`, `status`, `lock`, `unlock`, `sleep`, `wake`, `night-light on|off|auto`, `night-light-brightness`, `history`, `export [days|month]`, `help`, `quit`
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`
73
+ - Scrollable activity-history pager — `history [count|all]` opens a full-screen, paginated view (arrow keys, `Page Up`/`Page Down`, `Home`/`End`); `q`/`Esc`/`Enter` to close
74
+ - Commands: `clean`, `status`, `info`, `lock`, `unlock`, `sleep`, `wake`, `night-light on|off|auto`, `night-light-brightness`, `panel-brightness <low|medium|high>` (LR4/LR5), `wait-time`, `power on|off`, `rename`, `insight`, `sleep-schedule`, plus LR5 extras (`privacy`, `volume`, `camera-audio`, `drawer-reset`), `history [count|all]`, `export [days|month]`, `help`, `quit`
75
+ - Slash commands for app management: `/login`, `/logout`, `/robots`, `/robot <index|name>`, `/pets`, `/pet <index|name>`, `/cat on|off|colour <hex>`, `/refresh [seconds|off]`, `/config`, `/notify on|off|sound on|off|test`, `/version`, `/mcp on|off|status`, `/exit`
76
+ - Slash-command tab completion — type `/` and a Claude Code-style overlay lists matching commands; `↑`/`↓` to move, `Tab` or `Enter` to accept, `Esc` to dismiss
77
+ - Inline ghost-text completion for bare commands — type a prefix (`cle`) and the rest (`an`) appears greyed; `Tab` or `→` to accept → `clean`
78
+ - Headless export — `asher --export 7` writes activity history to CSV from cron / Task Scheduler / SSH without launching the TUI
71
79
  - Cat animation panel that reacts to robot state
72
80
  - Command history (↑/↓ arrows)
73
81
  - Real-time updates via WebSocket; 5-minute poll fallback
@@ -132,7 +140,17 @@ LITTER_ROBOT_PASSWORD=yourpassword
132
140
  | `sleep` / `wake` | Toggle sleep mode |
133
141
  | `night-light on\|off\|auto` | Set night light mode |
134
142
  | `night-light-brightness <level>` | Set brightness (LR5: 0-100; LR4: 25/50/100) |
135
- | `history` | Show last 25 activity events |
143
+ | `panel-brightness <low\|medium\|high>` | Set control-panel brightness (LR4/LR5 only; shows current if omitted) |
144
+ | `wait-time <minutes>` | Set clean-cycle wait time (shows valid values / current if omitted) |
145
+ | `power on\|off` | Hard-power the unit on or off |
146
+ | `rename <new name>` | Rename the unit in the Whisker cloud |
147
+ | `insight [days\|month]` | Show cycle-usage statistics (default: 30 days) |
148
+ | `sleep-schedule` | Show the per-day sleep schedule (read-only) |
149
+ | `privacy on\|off` | Toggle LR5 privacy mode |
150
+ | `volume <0-100>` | Set LR5 sound volume |
151
+ | `camera-audio on\|off` | Toggle LR5 camera audio |
152
+ | `drawer-reset` | Reset the LR5 waste drawer level indicator |
153
+ | `history [count\|all]` | Show recent activity in a scrollable pager (default: 50 events) |
136
154
  | `export [days\|month]` | Export activity history to CSV in `~/Downloads` (default: 30 days) |
137
155
  | `clear` | Clear the log |
138
156
  | `help` | Show command list |
@@ -149,28 +167,89 @@ LITTER_ROBOT_PASSWORD=yourpassword
149
167
  | `/pets` | List all pets on the account |
150
168
  | `/pet <index\|name>` | Switch which pet's name/weight shows in the status bar |
151
169
  | `/cat on\|off` | Show or hide the cat animation panel |
152
- | `/cat color <hex>` | Change the cat art colour (e.g. `/cat color #ff79c6`); `/cat reset` to revert |
170
+ | `/cat colour <hex>` | Change the cat art colour (e.g. `/cat colour #ff79c6`); `color` also accepted; `/cat reset` to revert |
153
171
  | `/refresh [seconds\|off]` | Change the auto-poll interval or disable it (`/refresh 60`, `/refresh off`) |
154
- | `/config` | Show current runtime settings (robot, refresh rate, cat panel, active pet) |
172
+ | `/config` | Show current runtime settings (robot, refresh rate, cat panel, active pet, notifications) |
173
+ | `/notify on\|off\|sound on\|off\|test` | Toggle desktop toast notifications for fault events (cat detected, pinch, motor fault, drawer full); `test` fires a sample toast |
174
+ | `/version` | Show version info (asher-cli, Python, pylitterbot, textual) |
175
+ | `/mcp on\|off\|status` | Toggle the Litter-Robot MCP server entry in Claude Desktop |
155
176
  | `/exit` | Exit Asher CLI |
156
177
 
157
- **Keyboard shortcuts:** `Ctrl+L` clears the log, `Ctrl+C` quits.
178
+ **Keyboard shortcuts:** `Ctrl+L` clears the log, `Ctrl+C` quits. While typing a `/` slash command, `↑`/`↓` move through completions, `Tab` or `Enter` accepts, `Esc` dismisses. While typing a bare command, a greyed ghost suggestion appears — `Tab` or `→` accepts it.
179
+
180
+ ### Headless export (cron / Task Scheduler / SSH)
181
+
182
+ `--export` writes the same CSV as the `export` command **without launching the TUI** — so you can script activity-history exports from cron, Windows Task Scheduler, or a server over SSH. No flags launches the interactive dashboard as before.
183
+
184
+ ```bash
185
+ asher --export 7 export last 7 days to ~/Downloads
186
+ asher --export 7 --output ~/hist.csv explicit output path
187
+ asher --export month --robot "Asher 2" 30 days (Whisker ceiling) for a specific robot
188
+ ```
189
+
190
+ `--robot` accepts an index or a partial, case-insensitive name (defaults to your saved preferred robot, else the first). Credentials use the same keyring → `.env` priority as the TUI, but with **no interactive login prompt** — a scheduled task can't type a password, so sign in once with `/login` first.
191
+
192
+ Exit codes for scripting:
193
+
194
+ | Code | Meaning |
195
+ |---|---|
196
+ | `0` | export succeeded |
197
+ | `1` | no credentials found (keyring or `.env`) |
198
+ | `2` | connection or API failure |
199
+ | `3` | failed to write the CSV (permissions, disk full) |
200
+ | `4` | `--robot` matched no robot on the account |
201
+
202
+ ```bash
203
+ # crontab — nightly export at 03:00
204
+ 0 3 * * * /usr/bin/env asher --export 7 --output /home/me/litter-history.csv >> /var/log/asher-export.log 2>&1
205
+ ```
206
+
207
+ ```powershell
208
+ # Windows Task Scheduler action
209
+ asher.exe --export 7 --output C:\Users\me\litter-history.csv
210
+ ```
211
+
212
+ ## Configuration
213
+
214
+ Runtime settings persist across restarts in `~/.asher-cli/config.json`, so you don't have to re-apply `/refresh 60`, `/cat colour #ff79c6`, or `/pet 1` every launch. The file is auto-created on first change and holds six non-secret UI preferences:
215
+
216
+ | Setting | Slash command | Default |
217
+ |---|---|---|
218
+ | `poll_interval_seconds` | `/refresh <seconds\|off>` | `300` |
219
+ | `cat_panel_visible` | `/cat on\|off` | `true` |
220
+ | `cat_panel_color` | `/cat colour <hex>\|reset` | `null` (default palette) |
221
+ | `active_pet_index` | `/pet <index\|name>` | `0` |
222
+ | `notifications` | `/notify on\|off` | `true` |
223
+ | `notification_sound` | `/notify sound on\|off` | `false` |
224
+
225
+ Credentials and the preferred-robot serial stay in the OS keyring; `.env` vars stay as env vars. You generally don't need to edit the file by hand — just use the slash commands — but it's plain JSON and safe to inspect or delete (deleting it restores defaults).
158
226
 
159
227
  ## Releasing
160
228
 
161
229
  ```bash
162
- # bump version, commit, and tag in one step, then push with tags
163
- uv run bump-my-version bump patch # 0.0.1 → 0.0.2
164
- uv run bump-my-version bump minor # 0.0.2 → 0.1.0
165
- uv run bump-my-version bump major # 0.1.0 → 1.0.0
230
+ # 1. regenerate CHANGELOG.md from conventional commits (idempotent), then commit it
231
+ uv run poe changelog
232
+ git add CHANGELOG.md
233
+ git commit -m "docs(changelog): update for next release"
234
+
235
+ # 2. bump version, commit, and tag in one step, then push with tags
236
+ uv run bump-my-version bump patch # 0.2.0 → 0.2.1
237
+ uv run bump-my-version bump minor # 0.2.0 → 0.3.0
238
+ uv run bump-my-version bump major # 0.2.0 → 1.0.0
166
239
 
167
240
  git push && git push --tags
168
241
 
169
- # then push the release branch to trigger PyPI publish with the new semver
170
- git checkout -b release/0.0.2
171
- git push origin release/0.0.2
242
+ # 3. push the release branch to trigger PyPI publish (OIDC) + GitHub Release
243
+ # (release notes auto-generated by git-cliff from the same cliff.toml)
244
+ git checkout -b release/X.Y.Z
245
+ git push origin release/X.Y.Z
172
246
  ```
173
247
 
248
+ `CHANGELOG.md` is generated by [git-cliff](https://git-cliff.org) from
249
+ conventional commits — see [`cliff.toml`](cliff.toml). Commit messages must use
250
+ `feat:`/`fix:`/`docs:`/etc. prefixes (optionally scoped, e.g. `feat(robot):`)
251
+ to appear in the changelog.
252
+
174
253
  ## Troubleshooting
175
254
 
176
255
  **`asher` not found after `pip install asher-cli`**
@@ -262,6 +341,11 @@ uv run poe check # run all of the above + tests (same as CI)
262
341
 
263
342
  CI runs on Python 3.10 / 3.11 / 3.12 across Ubuntu, Windows, and macOS on every push.
264
343
 
344
+ ## Changelog
345
+
346
+ The full, maintained release history lives in [CHANGELOG.md](CHANGELOG.md),
347
+ auto-generated from conventional commits by [git-cliff](https://git-cliff.org).
348
+
265
349
  ## Notes
266
350
 
267
351
  - Uses the unofficial [pylitterbot](https://github.com/natekspencer/pylitterbot) library — supports LR3, LR4, LR5, and other Whisker robots
@@ -19,9 +19,12 @@ A Claude Code-style terminal dashboard for monitoring and controlling Litter Rob
19
19
  - Real-time cycling indicator with elapsed time (`⟳ Cycling M:SS`)
20
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
21
  - Cat panel with mode label + status badges (status chip, lock, night light, sleep, wait time) under the art
22
- - Scrollable activity log with timestamps
23
- - Commands: `clean`, `status`, `lock`, `unlock`, `sleep`, `wake`, `night-light on|off|auto`, `night-light-brightness`, `history`, `export [days|month]`, `help`, `quit`
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`
22
+ - Scrollable activity-history pager — `history [count|all]` opens a full-screen, paginated view (arrow keys, `Page Up`/`Page Down`, `Home`/`End`); `q`/`Esc`/`Enter` to close
23
+ - Commands: `clean`, `status`, `info`, `lock`, `unlock`, `sleep`, `wake`, `night-light on|off|auto`, `night-light-brightness`, `panel-brightness <low|medium|high>` (LR4/LR5), `wait-time`, `power on|off`, `rename`, `insight`, `sleep-schedule`, plus LR5 extras (`privacy`, `volume`, `camera-audio`, `drawer-reset`), `history [count|all]`, `export [days|month]`, `help`, `quit`
24
+ - Slash commands for app management: `/login`, `/logout`, `/robots`, `/robot <index|name>`, `/pets`, `/pet <index|name>`, `/cat on|off|colour <hex>`, `/refresh [seconds|off]`, `/config`, `/notify on|off|sound on|off|test`, `/version`, `/mcp on|off|status`, `/exit`
25
+ - Slash-command tab completion — type `/` and a Claude Code-style overlay lists matching commands; `↑`/`↓` to move, `Tab` or `Enter` to accept, `Esc` to dismiss
26
+ - Inline ghost-text completion for bare commands — type a prefix (`cle`) and the rest (`an`) appears greyed; `Tab` or `→` to accept → `clean`
27
+ - Headless export — `asher --export 7` writes activity history to CSV from cron / Task Scheduler / SSH without launching the TUI
25
28
  - Cat animation panel that reacts to robot state
26
29
  - Command history (↑/↓ arrows)
27
30
  - Real-time updates via WebSocket; 5-minute poll fallback
@@ -86,7 +89,17 @@ LITTER_ROBOT_PASSWORD=yourpassword
86
89
  | `sleep` / `wake` | Toggle sleep mode |
87
90
  | `night-light on\|off\|auto` | Set night light mode |
88
91
  | `night-light-brightness <level>` | Set brightness (LR5: 0-100; LR4: 25/50/100) |
89
- | `history` | Show last 25 activity events |
92
+ | `panel-brightness <low\|medium\|high>` | Set control-panel brightness (LR4/LR5 only; shows current if omitted) |
93
+ | `wait-time <minutes>` | Set clean-cycle wait time (shows valid values / current if omitted) |
94
+ | `power on\|off` | Hard-power the unit on or off |
95
+ | `rename <new name>` | Rename the unit in the Whisker cloud |
96
+ | `insight [days\|month]` | Show cycle-usage statistics (default: 30 days) |
97
+ | `sleep-schedule` | Show the per-day sleep schedule (read-only) |
98
+ | `privacy on\|off` | Toggle LR5 privacy mode |
99
+ | `volume <0-100>` | Set LR5 sound volume |
100
+ | `camera-audio on\|off` | Toggle LR5 camera audio |
101
+ | `drawer-reset` | Reset the LR5 waste drawer level indicator |
102
+ | `history [count\|all]` | Show recent activity in a scrollable pager (default: 50 events) |
90
103
  | `export [days\|month]` | Export activity history to CSV in `~/Downloads` (default: 30 days) |
91
104
  | `clear` | Clear the log |
92
105
  | `help` | Show command list |
@@ -103,28 +116,89 @@ LITTER_ROBOT_PASSWORD=yourpassword
103
116
  | `/pets` | List all pets on the account |
104
117
  | `/pet <index\|name>` | Switch which pet's name/weight shows in the status bar |
105
118
  | `/cat on\|off` | Show or hide the cat animation panel |
106
- | `/cat color <hex>` | Change the cat art colour (e.g. `/cat color #ff79c6`); `/cat reset` to revert |
119
+ | `/cat colour <hex>` | Change the cat art colour (e.g. `/cat colour #ff79c6`); `color` also accepted; `/cat reset` to revert |
107
120
  | `/refresh [seconds\|off]` | Change the auto-poll interval or disable it (`/refresh 60`, `/refresh off`) |
108
- | `/config` | Show current runtime settings (robot, refresh rate, cat panel, active pet) |
121
+ | `/config` | Show current runtime settings (robot, refresh rate, cat panel, active pet, notifications) |
122
+ | `/notify on\|off\|sound on\|off\|test` | Toggle desktop toast notifications for fault events (cat detected, pinch, motor fault, drawer full); `test` fires a sample toast |
123
+ | `/version` | Show version info (asher-cli, Python, pylitterbot, textual) |
124
+ | `/mcp on\|off\|status` | Toggle the Litter-Robot MCP server entry in Claude Desktop |
109
125
  | `/exit` | Exit Asher CLI |
110
126
 
111
- **Keyboard shortcuts:** `Ctrl+L` clears the log, `Ctrl+C` quits.
127
+ **Keyboard shortcuts:** `Ctrl+L` clears the log, `Ctrl+C` quits. While typing a `/` slash command, `↑`/`↓` move through completions, `Tab` or `Enter` accepts, `Esc` dismisses. While typing a bare command, a greyed ghost suggestion appears — `Tab` or `→` accepts it.
128
+
129
+ ### Headless export (cron / Task Scheduler / SSH)
130
+
131
+ `--export` writes the same CSV as the `export` command **without launching the TUI** — so you can script activity-history exports from cron, Windows Task Scheduler, or a server over SSH. No flags launches the interactive dashboard as before.
132
+
133
+ ```bash
134
+ asher --export 7 export last 7 days to ~/Downloads
135
+ asher --export 7 --output ~/hist.csv explicit output path
136
+ asher --export month --robot "Asher 2" 30 days (Whisker ceiling) for a specific robot
137
+ ```
138
+
139
+ `--robot` accepts an index or a partial, case-insensitive name (defaults to your saved preferred robot, else the first). Credentials use the same keyring → `.env` priority as the TUI, but with **no interactive login prompt** — a scheduled task can't type a password, so sign in once with `/login` first.
140
+
141
+ Exit codes for scripting:
142
+
143
+ | Code | Meaning |
144
+ |---|---|
145
+ | `0` | export succeeded |
146
+ | `1` | no credentials found (keyring or `.env`) |
147
+ | `2` | connection or API failure |
148
+ | `3` | failed to write the CSV (permissions, disk full) |
149
+ | `4` | `--robot` matched no robot on the account |
150
+
151
+ ```bash
152
+ # crontab — nightly export at 03:00
153
+ 0 3 * * * /usr/bin/env asher --export 7 --output /home/me/litter-history.csv >> /var/log/asher-export.log 2>&1
154
+ ```
155
+
156
+ ```powershell
157
+ # Windows Task Scheduler action
158
+ asher.exe --export 7 --output C:\Users\me\litter-history.csv
159
+ ```
160
+
161
+ ## Configuration
162
+
163
+ Runtime settings persist across restarts in `~/.asher-cli/config.json`, so you don't have to re-apply `/refresh 60`, `/cat colour #ff79c6`, or `/pet 1` every launch. The file is auto-created on first change and holds six non-secret UI preferences:
164
+
165
+ | Setting | Slash command | Default |
166
+ |---|---|---|
167
+ | `poll_interval_seconds` | `/refresh <seconds\|off>` | `300` |
168
+ | `cat_panel_visible` | `/cat on\|off` | `true` |
169
+ | `cat_panel_color` | `/cat colour <hex>\|reset` | `null` (default palette) |
170
+ | `active_pet_index` | `/pet <index\|name>` | `0` |
171
+ | `notifications` | `/notify on\|off` | `true` |
172
+ | `notification_sound` | `/notify sound on\|off` | `false` |
173
+
174
+ Credentials and the preferred-robot serial stay in the OS keyring; `.env` vars stay as env vars. You generally don't need to edit the file by hand — just use the slash commands — but it's plain JSON and safe to inspect or delete (deleting it restores defaults).
112
175
 
113
176
  ## Releasing
114
177
 
115
178
  ```bash
116
- # bump version, commit, and tag in one step, then push with tags
117
- uv run bump-my-version bump patch # 0.0.1 → 0.0.2
118
- uv run bump-my-version bump minor # 0.0.2 → 0.1.0
119
- uv run bump-my-version bump major # 0.1.0 → 1.0.0
179
+ # 1. regenerate CHANGELOG.md from conventional commits (idempotent), then commit it
180
+ uv run poe changelog
181
+ git add CHANGELOG.md
182
+ git commit -m "docs(changelog): update for next release"
183
+
184
+ # 2. bump version, commit, and tag in one step, then push with tags
185
+ uv run bump-my-version bump patch # 0.2.0 → 0.2.1
186
+ uv run bump-my-version bump minor # 0.2.0 → 0.3.0
187
+ uv run bump-my-version bump major # 0.2.0 → 1.0.0
120
188
 
121
189
  git push && git push --tags
122
190
 
123
- # then push the release branch to trigger PyPI publish with the new semver
124
- git checkout -b release/0.0.2
125
- git push origin release/0.0.2
191
+ # 3. push the release branch to trigger PyPI publish (OIDC) + GitHub Release
192
+ # (release notes auto-generated by git-cliff from the same cliff.toml)
193
+ git checkout -b release/X.Y.Z
194
+ git push origin release/X.Y.Z
126
195
  ```
127
196
 
197
+ `CHANGELOG.md` is generated by [git-cliff](https://git-cliff.org) from
198
+ conventional commits — see [`cliff.toml`](cliff.toml). Commit messages must use
199
+ `feat:`/`fix:`/`docs:`/etc. prefixes (optionally scoped, e.g. `feat(robot):`)
200
+ to appear in the changelog.
201
+
128
202
  ## Troubleshooting
129
203
 
130
204
  **`asher` not found after `pip install asher-cli`**
@@ -216,6 +290,11 @@ uv run poe check # run all of the above + tests (same as CI)
216
290
 
217
291
  CI runs on Python 3.10 / 3.11 / 3.12 across Ubuntu, Windows, and macOS on every push.
218
292
 
293
+ ## Changelog
294
+
295
+ The full, maintained release history lives in [CHANGELOG.md](CHANGELOG.md),
296
+ auto-generated from conventional commits by [git-cliff](https://git-cliff.org).
297
+
219
298
  ## Notes
220
299
 
221
300
  - Uses the unofficial [pylitterbot](https://github.com/natekspencer/pylitterbot) library — supports LR3, LR4, LR5, and other Whisker robots