shellmate-cli 3.0.1__tar.gz → 3.0.2__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 (115) hide show
  1. {shellmate_cli-3.0.1/shellmate_cli.egg-info → shellmate_cli-3.0.2}/PKG-INFO +16 -16
  2. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/README.md +15 -15
  3. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/pyproject.toml +1 -1
  4. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/adapters/ble_adapter.sh +7 -3
  5. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/adapters/powershell_adapter.ps1 +7 -3
  6. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/adapters/powershell_predictor.ps1 +3 -0
  7. shellmate_cli-3.0.2/shellmate/cli/version.py +1 -0
  8. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/ui/annotations.py +24 -5
  9. shellmate_cli-3.0.2/shellmate/ui/colors.py +109 -0
  10. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2/shellmate_cli.egg-info}/PKG-INFO +16 -16
  11. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate_cli.egg-info/SOURCES.txt +3 -1
  12. shellmate_cli-3.0.2/tests/test_colors.py +69 -0
  13. shellmate_cli-3.0.1/shellmate/cli/version.py +0 -1
  14. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/LICENSE +0 -0
  15. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/MANIFEST.in +0 -0
  16. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/setup.cfg +0 -0
  17. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/__init__.py +0 -0
  18. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/adapters/__init__.py +0 -0
  19. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/adapters/bash_adapter.sh +0 -0
  20. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/adapters/zsh_adapter.zsh +0 -0
  21. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/agents/__init__.py +0 -0
  22. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/agents/base.py +0 -0
  23. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/agents/chain_patterns.yaml +0 -0
  24. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/agents/command_chain.py +0 -0
  25. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/agents/context_gatherer.py +0 -0
  26. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/agents/environment.py +0 -0
  27. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/agents/librarian.py +0 -0
  28. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/agents/query.py +0 -0
  29. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/agents/repomix_backend.py +0 -0
  30. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/agents/watcher.py +0 -0
  31. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/cache/__init__.py +0 -0
  32. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/cache/semantic_cache.py +0 -0
  33. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/cli/__init__.py +0 -0
  34. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/cli/commands/__init__.py +0 -0
  35. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/cli/commands/analyze.py +0 -0
  36. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/cli/commands/config.py +0 -0
  37. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/cli/commands/configure.py +0 -0
  38. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/cli/commands/disable.py +0 -0
  39. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/cli/commands/enable.py +0 -0
  40. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/cli/commands/init.py +0 -0
  41. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/cli/commands/install.py +0 -0
  42. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/cli/commands/migrate.py +0 -0
  43. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/cli/commands/status.py +0 -0
  44. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/cli/commands/uninstall.py +0 -0
  45. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/cli/main.py +0 -0
  46. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/cli/parser.py +0 -0
  47. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/completion/__init__.py +0 -0
  48. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/completion/bash_adapter.py +0 -0
  49. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/config/__init__.py +0 -0
  50. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/config/credentials.py +0 -0
  51. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/config/defaults.py +0 -0
  52. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/config/manager.py +0 -0
  53. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/config/paths.py +0 -0
  54. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/config/secrets.py +0 -0
  55. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/daemon/__init__.py +0 -0
  56. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/daemon/lifecycle.py +0 -0
  57. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/daemon/observability.py +0 -0
  58. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/daemon/pipeline.py +0 -0
  59. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/daemon/server.py +0 -0
  60. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/daemon/session.py +0 -0
  61. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/daemon/startup_ui.py +0 -0
  62. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/environment/__init__.py +0 -0
  63. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/environment/detector.py +0 -0
  64. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/ipc/__init__.py +0 -0
  65. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/ipc/client.py +0 -0
  66. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/ipc/ps_client.py +0 -0
  67. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/orchestration/__init__.py +0 -0
  68. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/orchestration/analysis_graph.py +0 -0
  69. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/orchestration/state.py +0 -0
  70. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/parser/__init__.py +0 -0
  71. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/parser/command_parser.py +0 -0
  72. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/plugins/__init__.py +0 -0
  73. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/plugins/base.py +0 -0
  74. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/plugins/loader.py +0 -0
  75. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/providers/__init__.py +0 -0
  76. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/providers/base.py +0 -0
  77. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/providers/groq_command_guide.py +0 -0
  78. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/providers/groq_provider.py +0 -0
  79. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/providers/groq_supervisor.py +0 -0
  80. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/providers/openrouter_provider.py +0 -0
  81. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/providers/router.py +0 -0
  82. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/providers/validate_keys.py +0 -0
  83. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/providers/xai_provider.py +0 -0
  84. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/rules/__init__.py +0 -0
  85. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/rules/catastrophic.yaml +0 -0
  86. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/rules/catastrophic_matcher.py +0 -0
  87. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/rules/definitions/__init__.py +0 -0
  88. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/rules/definitions/file_deletion.yaml +0 -0
  89. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/rules/definitions/filesystem.yaml +0 -0
  90. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/rules/definitions/network_execution.yaml +0 -0
  91. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/rules/definitions/permissions.yaml +0 -0
  92. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/rules/definitions/process_control.yaml +0 -0
  93. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/rules/definitions/system_destruction.yaml +0 -0
  94. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/rules/engine.py +0 -0
  95. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/sanitizer/__init__.py +0 -0
  96. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/sanitizer/typo_detector.py +0 -0
  97. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/ui/__init__.py +0 -0
  98. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/ui/analysis.py +0 -0
  99. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/ui/banner.py +0 -0
  100. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/ui/progress.py +0 -0
  101. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/ui/runtime/__init__.py +0 -0
  102. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/ui/runtime/console_factory.py +0 -0
  103. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/ui/runtime/live_scene.py +0 -0
  104. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/ui/runtime/motion_policy.py +0 -0
  105. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/ui/runtime/tokens.py +0 -0
  106. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/ui/scenes/__init__.py +0 -0
  107. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/ui/scenes/enable.py +0 -0
  108. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/ui/scenes/help.py +0 -0
  109. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/ui/scenes/stages.py +0 -0
  110. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/ui/scenes/status.py +0 -0
  111. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate/ui/status.py +0 -0
  112. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate_cli.egg-info/dependency_links.txt +0 -0
  113. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate_cli.egg-info/entry_points.txt +0 -0
  114. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate_cli.egg-info/requires.txt +0 -0
  115. {shellmate_cli-3.0.1 → shellmate_cli-3.0.2}/shellmate_cli.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: shellmate-cli
3
- Version: 3.0.1
3
+ Version: 3.0.2
4
4
  Summary: Intelligent shell autocomplete with consequence preview
5
5
  Author: ShellMate Contributors
6
6
  License: MIT
@@ -39,7 +39,7 @@ Requires-Dist: pywinpty>=2.0.0; sys_platform == "win32" and extra == "windows-te
39
39
  Requires-Dist: pyte>=0.8.1; extra == "windows-test"
40
40
  Dynamic: license-file
41
41
 
42
- # 🐢 ShellMate
42
+ # ShellMate
43
43
 
44
44
  **A friendly safety net for your terminal.**
45
45
 
@@ -67,22 +67,22 @@ $ git push --force
67
67
 
68
68
  ---
69
69
 
70
- ## ✨ Why you'll like it
70
+ ## Why you'll like it
71
71
 
72
- - 🛑 **Stops the scary stuff.** Genuinely dangerous commands (like `rm -rf /`) are
72
+ - **Stops the scary stuff.** Genuinely dangerous commands (like `rm -rf /`) are
73
73
  caught instantly and ask you to confirm before they run.
74
- - 💬 **Explains commands in plain English.** No more guessing what a command does —
74
+ - **Explains commands in plain English.** No more guessing what a command does —
75
75
  ShellMate tells you what will happen and whether you can undo it.
76
- - 🧭 **Suggests safer alternatives.** When there's a gentler way to do the same thing,
76
+ - **Suggests safer alternatives.** When there's a gentler way to do the same thing,
77
77
  it offers it.
78
- - ⌨️ **Autocomplete that helps you learn.** Press Tab for suggestions as you type.
79
- - 🙌 **Stays out of your way.** Safe commands run normally — no pop-ups, no nagging.
80
- - 🔒 **You stay in control.** ShellMate only *shows* you things. It never executes
78
+ - **Autocomplete that helps you learn.** Press Tab for suggestions as you type.
79
+ - **Stays out of your way.** Safe commands run normally — no pop-ups, no nagging.
80
+ - **You stay in control.** ShellMate only *shows* you things. It never executes
81
81
  commands on its own.
82
82
 
83
83
  ---
84
84
 
85
- ## 🚀 Get started in 3 steps
85
+ ## Get started in 3 steps
86
86
 
87
87
  ### 1. Install it
88
88
 
@@ -112,7 +112,7 @@ sm enable # starts the helper in the background
112
112
  ```
113
113
 
114
114
  Now just **use your terminal normally.** Little notes will appear under your
115
- commands as you type. 🎉
115
+ commands as you type.
116
116
 
117
117
  > **What you need:** Python 3.10+, and either **Windows PowerShell** or, on
118
118
  > **Linux/WSL**, [ble.sh](https://github.com/akinomyoga/ble.sh). ShellMate sets up
@@ -120,7 +120,7 @@ commands as you type. 🎉
120
120
 
121
121
  ---
122
122
 
123
- ## 👀 What it feels like to use
123
+ ## What it feels like to use
124
124
 
125
125
  As you type, ShellMate shows a tiny note under your command:
126
126
 
@@ -139,7 +139,7 @@ many people from a bad day.
139
139
 
140
140
  ---
141
141
 
142
- ## 🧰 Everyday commands
142
+ ## Everyday commands
143
143
 
144
144
  These are the only commands you need day to day:
145
145
 
@@ -154,7 +154,7 @@ These are the only commands you need day to day:
154
154
 
155
155
  ---
156
156
 
157
- ## 🔒 A note on your privacy
157
+ ## A note on your privacy
158
158
 
159
159
  Being honest about how it works:
160
160
 
@@ -168,7 +168,7 @@ Being honest about how it works:
168
168
 
169
169
  ---
170
170
 
171
- ## 🆘 Troubleshooting
171
+ ## Troubleshooting
172
172
 
173
173
  - **"I don't see any notes."** Run `sm status` to confirm the daemon is running, and
174
174
  make sure you ran `sm enable`. On a brand-new install, open a fresh terminal so the
@@ -182,7 +182,7 @@ Being honest about how it works:
182
182
  ---
183
183
 
184
184
  <details>
185
- <summary>🔧 <b>Under the hood</b> (for the curious / power users)</summary>
185
+ <summary><b>Under the hood</b> (for the curious / power users)</summary>
186
186
 
187
187
  ### How it works
188
188
 
@@ -1,4 +1,4 @@
1
- # 🐢 ShellMate
1
+ # ShellMate
2
2
 
3
3
  **A friendly safety net for your terminal.**
4
4
 
@@ -26,22 +26,22 @@ $ git push --force
26
26
 
27
27
  ---
28
28
 
29
- ## ✨ Why you'll like it
29
+ ## Why you'll like it
30
30
 
31
- - 🛑 **Stops the scary stuff.** Genuinely dangerous commands (like `rm -rf /`) are
31
+ - **Stops the scary stuff.** Genuinely dangerous commands (like `rm -rf /`) are
32
32
  caught instantly and ask you to confirm before they run.
33
- - 💬 **Explains commands in plain English.** No more guessing what a command does —
33
+ - **Explains commands in plain English.** No more guessing what a command does —
34
34
  ShellMate tells you what will happen and whether you can undo it.
35
- - 🧭 **Suggests safer alternatives.** When there's a gentler way to do the same thing,
35
+ - **Suggests safer alternatives.** When there's a gentler way to do the same thing,
36
36
  it offers it.
37
- - ⌨️ **Autocomplete that helps you learn.** Press Tab for suggestions as you type.
38
- - 🙌 **Stays out of your way.** Safe commands run normally — no pop-ups, no nagging.
39
- - 🔒 **You stay in control.** ShellMate only *shows* you things. It never executes
37
+ - **Autocomplete that helps you learn.** Press Tab for suggestions as you type.
38
+ - **Stays out of your way.** Safe commands run normally — no pop-ups, no nagging.
39
+ - **You stay in control.** ShellMate only *shows* you things. It never executes
40
40
  commands on its own.
41
41
 
42
42
  ---
43
43
 
44
- ## 🚀 Get started in 3 steps
44
+ ## Get started in 3 steps
45
45
 
46
46
  ### 1. Install it
47
47
 
@@ -71,7 +71,7 @@ sm enable # starts the helper in the background
71
71
  ```
72
72
 
73
73
  Now just **use your terminal normally.** Little notes will appear under your
74
- commands as you type. 🎉
74
+ commands as you type.
75
75
 
76
76
  > **What you need:** Python 3.10+, and either **Windows PowerShell** or, on
77
77
  > **Linux/WSL**, [ble.sh](https://github.com/akinomyoga/ble.sh). ShellMate sets up
@@ -79,7 +79,7 @@ commands as you type. 🎉
79
79
 
80
80
  ---
81
81
 
82
- ## 👀 What it feels like to use
82
+ ## What it feels like to use
83
83
 
84
84
  As you type, ShellMate shows a tiny note under your command:
85
85
 
@@ -98,7 +98,7 @@ many people from a bad day.
98
98
 
99
99
  ---
100
100
 
101
- ## 🧰 Everyday commands
101
+ ## Everyday commands
102
102
 
103
103
  These are the only commands you need day to day:
104
104
 
@@ -113,7 +113,7 @@ These are the only commands you need day to day:
113
113
 
114
114
  ---
115
115
 
116
- ## 🔒 A note on your privacy
116
+ ## A note on your privacy
117
117
 
118
118
  Being honest about how it works:
119
119
 
@@ -127,7 +127,7 @@ Being honest about how it works:
127
127
 
128
128
  ---
129
129
 
130
- ## 🆘 Troubleshooting
130
+ ## Troubleshooting
131
131
 
132
132
  - **"I don't see any notes."** Run `sm status` to confirm the daemon is running, and
133
133
  make sure you ran `sm enable`. On a brand-new install, open a fresh terminal so the
@@ -141,7 +141,7 @@ Being honest about how it works:
141
141
  ---
142
142
 
143
143
  <details>
144
- <summary>🔧 <b>Under the hood</b> (for the curious / power users)</summary>
144
+ <summary><b>Under the hood</b> (for the curious / power users)</summary>
145
145
 
146
146
  ### How it works
147
147
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "shellmate-cli"
7
- version = "3.0.1"
7
+ version = "3.0.2"
8
8
  description = "Intelligent shell autocomplete with consequence preview"
9
9
  readme = "README.md"
10
10
  license = {text = "MIT"}
@@ -107,6 +107,8 @@ print(json.dumps({
107
107
  suggested_fix=$(python3 -c "import sys,json; d=json.loads(sys.argv[1]); print(d.get('suggested_fix',''))" "$raw" 2>/dev/null)
108
108
  severity=$(python3 -c "import sys,json; d=json.loads(sys.argv[1]); print(d.get('severity','MEDIUM'))" "$raw" 2>/dev/null)
109
109
 
110
+ # Color scheme mirrors shellmate/ui/colors.py: caution=91 red, warning=93
111
+ # yellow, suggestion/safe=92 green.
110
112
  local color="\e[93m"
111
113
  [[ "$severity" == "HIGH" || "$severity" == "CRITICAL" || "$severity" == "blocking" ]] && color="\e[91m"
112
114
  [[ "$severity" == "SAFE" || "$severity" == "LOW" ]] && color="\e[92m"
@@ -114,7 +116,8 @@ print(json.dumps({
114
116
  local line1 line2 line3=""
115
117
  line1="${color} → ${what_happens}\e[m"
116
118
  line2="${color} → Reversible: ${reversible} | ${scope}\e[m"
117
- [[ -n "$suggested_fix" ]] && line3="\e[90m → Safe alt: ${suggested_fix}\e[m"
119
+ # A suggested fix is a suggestion -> green (92), not dim.
120
+ [[ -n "$suggested_fix" ]] && line3="\e[92m → Safe alt: ${suggested_fix}\e[m"
118
121
 
119
122
  # Gate HIGH/CRITICAL on second Enter
120
123
  if [[ "$severity" == "HIGH" || "$severity" == "CRITICAL" || "$severity" == "blocking" ]]; then
@@ -328,10 +331,11 @@ print('\n'.join(get_completions(sys.argv[1])))
328
331
  _SHELLMATE_TAB_CMD="$first"
329
332
  _SHELLMATE_TAB_STATE="suggested"
330
333
  local suffix="${first#$line}"
334
+ # Autocomplete ghost-text is a suggestion -> green (92), per shellmate/ui/colors.py.
331
335
  if [[ -n "$suffix" ]] && type ble/canvas/put.draw &>/dev/null; then
332
- ble/canvas/put.draw "\e[90m${suffix}\e[m"
336
+ ble/canvas/put.draw "\e[92m${suffix}\e[m"
333
337
  elif [[ -n "$suffix" ]]; then
334
- printf '\e[90m%s\e[m' "$suffix" >/dev/tty
338
+ printf '\e[92m%s\e[m' "$suffix" >/dev/tty
335
339
  printf '\e[%dD' "${#suffix}" >/dev/tty
336
340
  fi
337
341
  fi
@@ -70,11 +70,13 @@ function Show-ShellMateConsequence {
70
70
  elseif ($Severity -in @("SAFE","LOW")) { "${ESC}[92m" } `
71
71
  else { "${ESC}[93m" }
72
72
  $reset = "${ESC}[0m"
73
- $dim = "${ESC}[90m"
73
+ # Color scheme mirrors shellmate/ui/colors.py: caution=91 red, warning=93
74
+ # yellow, suggestion/safe=92 green. A suggested fix is a suggestion -> green.
75
+ $green = "${ESC}[92m"
74
76
 
75
77
  $line1 = "${color} -> ${WhatHappens}${reset}"
76
78
  $line2 = "${color} -> Reversible: ${Reversible} | ${Scope}${reset}"
77
- $line3 = if ($SuggestedFix) { "${dim} -> Safe alt: ${SuggestedFix}${reset}" } else { "" }
79
+ $line3 = if ($SuggestedFix) { "${green} -> Safe alt: ${SuggestedFix}${reset}" } else { "" }
78
80
 
79
81
  # Save cursor, write 3 lines below prompt, restore
80
82
  [Console]::Write("${ESC}[s")
@@ -368,7 +370,9 @@ if (-not $script:ShellMatePredictorRegistered) {
368
370
  $ESC = [char]27
369
371
  $suffix = ([string]$suggestion).Substring([Math]::Min($prefix.Length, ([string]$suggestion).Length))
370
372
  if ($suffix) {
371
- [Console]::Write("${ESC}[90m${suffix}${ESC}[0m")
373
+ # Autocomplete ghost-text is a suggestion -> green (92),
374
+ # matching shellmate/ui/colors.py.
375
+ [Console]::Write("${ESC}[92m${suffix}${ESC}[0m")
372
376
  [Console]::Write("${ESC}[$($suffix.Length)D")
373
377
  }
374
378
  return
@@ -89,6 +89,9 @@ if ($py -and (Test-Path $client)) {
89
89
  $predictor
90
90
  )
91
91
  Set-PSReadLineOption -PredictionSource Plugin -PredictionViewStyle InlineView -ErrorAction SilentlyContinue
92
+ # Render predictions green — they are suggestions (mirrors shellmate/ui/colors.py).
93
+ $smEsc = [char]27
94
+ Set-PSReadLineOption -Colors @{ InlinePrediction = "${smEsc}[92m" } -ErrorAction SilentlyContinue
92
95
  $script:ShellMatePredictorRegistered = $true
93
96
  } catch {
94
97
  Write-Verbose "ShellMate: predictor registration failed: $_"
@@ -0,0 +1 @@
1
+ VERSION = "3.0.2"
@@ -4,6 +4,8 @@ from __future__ import annotations
4
4
 
5
5
  from typing import Any, Dict, Optional
6
6
 
7
+ from shellmate.ui import colors
8
+
7
9
  # Emoji mappings for risk levels (short inline / Unix terminals)
8
10
  RISK_ICONS = {
9
11
  "SAFE": "✅",
@@ -105,6 +107,7 @@ def format_consequence_annotation(
105
107
  risk: str = "MEDIUM",
106
108
  *,
107
109
  ascii_safe: bool = False,
110
+ color: bool = False,
108
111
  ) -> str:
109
112
  """Format annotation using structured consequence preview + badges."""
110
113
  env_context = env_context or {}
@@ -137,7 +140,12 @@ def format_consequence_annotation(
137
140
  if not consequence.get("reversible", True):
138
141
  parts.append("[IRREVERSIBLE]")
139
142
 
140
- return " ".join(parts) if len(parts) > 1 else ""
143
+ if len(parts) <= 1:
144
+ return ""
145
+ text = " ".join(parts)
146
+ # Consequence severity ("blocking"/"warning"/"info") or risk level both map
147
+ # through SEVERITY_ROLE -> caution=red / warning=yellow / safe=green.
148
+ return colors.by_severity(text, severity if severity in colors.SEVERITY_ROLE else risk) if color else text
141
149
 
142
150
 
143
151
  def format_legacy_pipeline_annotation(
@@ -146,6 +154,7 @@ def format_legacy_pipeline_annotation(
146
154
  chain_patterns: Optional[Dict[str, Any]] = None,
147
155
  *,
148
156
  ascii_safe: bool = False,
157
+ color: bool = False,
149
158
  ) -> str:
150
159
  """Format annotation from pipeline result without consequence_preview."""
151
160
  env_context = env_context or {}
@@ -186,7 +195,10 @@ def format_legacy_pipeline_annotation(
186
195
  msg = msg[:37] + "..."
187
196
  parts.append(msg)
188
197
 
189
- return " ".join(parts) if len(parts) > 1 else ""
198
+ if len(parts) <= 1:
199
+ return ""
200
+ text = " ".join(parts)
201
+ return colors.by_severity(text, risk) if color else text
190
202
 
191
203
 
192
204
  def format_pipeline_annotation(
@@ -195,6 +207,7 @@ def format_pipeline_annotation(
195
207
  chain_patterns: Optional[Dict[str, Any]] = None,
196
208
  *,
197
209
  ascii_safe: bool = False,
210
+ color: bool = False,
198
211
  ) -> str:
199
212
  """Prefer consequence_preview; fall back to legacy pipeline formatting."""
200
213
  consequence = risk_result.get("consequence_preview") or {}
@@ -205,13 +218,16 @@ def format_pipeline_annotation(
205
218
  chain_patterns,
206
219
  risk_result.get("risk", "MEDIUM"),
207
220
  ascii_safe=ascii_safe,
221
+ color=color,
208
222
  )
209
223
  return format_legacy_pipeline_annotation(
210
- risk_result, env_context, chain_patterns, ascii_safe=ascii_safe
224
+ risk_result, env_context, chain_patterns, ascii_safe=ascii_safe, color=color
211
225
  )
212
226
 
213
227
 
214
- def format_cached_annotation(cached_result: Dict[str, Any], *, ascii_safe: bool = False) -> str:
228
+ def format_cached_annotation(
229
+ cached_result: Dict[str, Any], *, ascii_safe: bool = False, color: bool = False
230
+ ) -> str:
215
231
  """Format a simple annotation from a cache hit payload."""
216
232
  msg = cached_result.get("explanation", "") or ""
217
233
  risk = cached_result.get("risk", "MEDIUM")
@@ -229,4 +245,7 @@ def format_cached_annotation(cached_result: Dict[str, Any], *, ascii_safe: bool
229
245
 
230
246
  if len(msg) > 50:
231
247
  msg = msg[:47] + "..."
232
- return f"{icon} {msg}" if msg else ""
248
+ if not msg:
249
+ return ""
250
+ text = f"{icon} {msg}"
251
+ return colors.by_severity(text, risk) if color else text
@@ -0,0 +1,109 @@
1
+ """Semantic terminal colors for suggestions and cautions.
2
+
3
+ Single source of truth for ShellMate's inline color scheme so every surface
4
+ agrees on what red / yellow / green mean:
5
+
6
+ caution -> red (dangerous: HIGH / CRITICAL / blocking)
7
+ warning -> yellow (proceed with care: MEDIUM)
8
+ suggestion -> green (a safer alternative / "Safe alt")
9
+ safe -> green (SAFE / LOW — nothing to worry about)
10
+ info -> cyan (neutral information)
11
+ dim -> grey (de-emphasized)
12
+
13
+ The shell adapters (``powershell_adapter.ps1`` / ``ble_adapter.sh``) mirror these
14
+ SGR codes directly since they render ANSI themselves; keep them in sync with
15
+ ``ROLE_SGR`` below. Python-side rendering should use this module.
16
+
17
+ Honors the https://no-color.org convention: if the ``NO_COLOR`` environment
18
+ variable is set (to anything), colorizing becomes a no-op.
19
+ """
20
+
21
+ from __future__ import annotations
22
+
23
+ import os
24
+ from typing import Optional
25
+
26
+ ESC = "\033"
27
+ RESET = f"{ESC}[0m"
28
+
29
+ # Role -> SGR (Select Graphic Rendition) code. Bright variants read well on both
30
+ # dark and light terminals. Mirror these in the shell adapters.
31
+ ROLE_SGR = {
32
+ "caution": 91, # bright red
33
+ "warning": 93, # bright yellow
34
+ "suggestion": 92, # bright green
35
+ "safe": 92, # bright green
36
+ "info": 96, # bright cyan
37
+ "dim": 90, # grey
38
+ }
39
+
40
+ # Role -> rich style name, for Console/Text rendering in the CLI scenes.
41
+ ROLE_RICH = {
42
+ "caution": "bold red",
43
+ "warning": "yellow",
44
+ "suggestion": "green",
45
+ "safe": "green",
46
+ "info": "cyan",
47
+ "dim": "bright_black",
48
+ }
49
+
50
+ # Risk/severity label -> semantic role.
51
+ SEVERITY_ROLE = {
52
+ "CRITICAL": "caution",
53
+ "HIGH": "caution",
54
+ "blocking": "caution",
55
+ "MEDIUM": "warning",
56
+ "warning": "warning",
57
+ "LOW": "safe",
58
+ "SAFE": "safe",
59
+ "info": "info",
60
+ }
61
+
62
+
63
+ def color_enabled(no_color: Optional[bool] = None) -> bool:
64
+ """Whether color should be emitted.
65
+
66
+ Explicit ``no_color`` wins; otherwise honor the ``NO_COLOR`` env var.
67
+ """
68
+ if no_color is not None:
69
+ return not no_color
70
+ return os.environ.get("NO_COLOR") is None
71
+
72
+
73
+ def role_for_severity(severity: Optional[str]) -> str:
74
+ """Map a risk/severity label to a semantic role (defaults to ``warning``)."""
75
+ return SEVERITY_ROLE.get(severity or "", "warning")
76
+
77
+
78
+ def colorize(text: str, role: str, *, no_color: Optional[bool] = None) -> str:
79
+ """Wrap ``text`` in the ANSI color for ``role``.
80
+
81
+ Returns ``text`` unchanged when color is disabled, the text is empty, or the
82
+ role is unknown — so it is always safe to call.
83
+ """
84
+ if not text or not color_enabled(no_color):
85
+ return text
86
+ sgr = ROLE_SGR.get(role)
87
+ if sgr is None:
88
+ return text
89
+ return f"{ESC}[{sgr}m{text}{RESET}"
90
+
91
+
92
+ def caution(text: str, *, no_color: Optional[bool] = None) -> str:
93
+ """Red — a dangerous command or consequence."""
94
+ return colorize(text, "caution", no_color=no_color)
95
+
96
+
97
+ def warning(text: str, *, no_color: Optional[bool] = None) -> str:
98
+ """Yellow — proceed with care."""
99
+ return colorize(text, "warning", no_color=no_color)
100
+
101
+
102
+ def suggestion(text: str, *, no_color: Optional[bool] = None) -> str:
103
+ """Green — a safer alternative to offer."""
104
+ return colorize(text, "suggestion", no_color=no_color)
105
+
106
+
107
+ def by_severity(text: str, severity: Optional[str], *, no_color: Optional[bool] = None) -> str:
108
+ """Colorize ``text`` using the role implied by a risk/severity label."""
109
+ return colorize(text, role_for_severity(severity), no_color=no_color)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: shellmate-cli
3
- Version: 3.0.1
3
+ Version: 3.0.2
4
4
  Summary: Intelligent shell autocomplete with consequence preview
5
5
  Author: ShellMate Contributors
6
6
  License: MIT
@@ -39,7 +39,7 @@ Requires-Dist: pywinpty>=2.0.0; sys_platform == "win32" and extra == "windows-te
39
39
  Requires-Dist: pyte>=0.8.1; extra == "windows-test"
40
40
  Dynamic: license-file
41
41
 
42
- # 🐢 ShellMate
42
+ # ShellMate
43
43
 
44
44
  **A friendly safety net for your terminal.**
45
45
 
@@ -67,22 +67,22 @@ $ git push --force
67
67
 
68
68
  ---
69
69
 
70
- ## ✨ Why you'll like it
70
+ ## Why you'll like it
71
71
 
72
- - 🛑 **Stops the scary stuff.** Genuinely dangerous commands (like `rm -rf /`) are
72
+ - **Stops the scary stuff.** Genuinely dangerous commands (like `rm -rf /`) are
73
73
  caught instantly and ask you to confirm before they run.
74
- - 💬 **Explains commands in plain English.** No more guessing what a command does —
74
+ - **Explains commands in plain English.** No more guessing what a command does —
75
75
  ShellMate tells you what will happen and whether you can undo it.
76
- - 🧭 **Suggests safer alternatives.** When there's a gentler way to do the same thing,
76
+ - **Suggests safer alternatives.** When there's a gentler way to do the same thing,
77
77
  it offers it.
78
- - ⌨️ **Autocomplete that helps you learn.** Press Tab for suggestions as you type.
79
- - 🙌 **Stays out of your way.** Safe commands run normally — no pop-ups, no nagging.
80
- - 🔒 **You stay in control.** ShellMate only *shows* you things. It never executes
78
+ - **Autocomplete that helps you learn.** Press Tab for suggestions as you type.
79
+ - **Stays out of your way.** Safe commands run normally — no pop-ups, no nagging.
80
+ - **You stay in control.** ShellMate only *shows* you things. It never executes
81
81
  commands on its own.
82
82
 
83
83
  ---
84
84
 
85
- ## 🚀 Get started in 3 steps
85
+ ## Get started in 3 steps
86
86
 
87
87
  ### 1. Install it
88
88
 
@@ -112,7 +112,7 @@ sm enable # starts the helper in the background
112
112
  ```
113
113
 
114
114
  Now just **use your terminal normally.** Little notes will appear under your
115
- commands as you type. 🎉
115
+ commands as you type.
116
116
 
117
117
  > **What you need:** Python 3.10+, and either **Windows PowerShell** or, on
118
118
  > **Linux/WSL**, [ble.sh](https://github.com/akinomyoga/ble.sh). ShellMate sets up
@@ -120,7 +120,7 @@ commands as you type. 🎉
120
120
 
121
121
  ---
122
122
 
123
- ## 👀 What it feels like to use
123
+ ## What it feels like to use
124
124
 
125
125
  As you type, ShellMate shows a tiny note under your command:
126
126
 
@@ -139,7 +139,7 @@ many people from a bad day.
139
139
 
140
140
  ---
141
141
 
142
- ## 🧰 Everyday commands
142
+ ## Everyday commands
143
143
 
144
144
  These are the only commands you need day to day:
145
145
 
@@ -154,7 +154,7 @@ These are the only commands you need day to day:
154
154
 
155
155
  ---
156
156
 
157
- ## 🔒 A note on your privacy
157
+ ## A note on your privacy
158
158
 
159
159
  Being honest about how it works:
160
160
 
@@ -168,7 +168,7 @@ Being honest about how it works:
168
168
 
169
169
  ---
170
170
 
171
- ## 🆘 Troubleshooting
171
+ ## Troubleshooting
172
172
 
173
173
  - **"I don't see any notes."** Run `sm status` to confirm the daemon is running, and
174
174
  make sure you ran `sm enable`. On a brand-new install, open a fresh terminal so the
@@ -182,7 +182,7 @@ Being honest about how it works:
182
182
  ---
183
183
 
184
184
  <details>
185
- <summary>🔧 <b>Under the hood</b> (for the curious / power users)</summary>
185
+ <summary><b>Under the hood</b> (for the curious / power users)</summary>
186
186
 
187
187
  ### How it works
188
188
 
@@ -90,6 +90,7 @@ shellmate/ui/__init__.py
90
90
  shellmate/ui/analysis.py
91
91
  shellmate/ui/annotations.py
92
92
  shellmate/ui/banner.py
93
+ shellmate/ui/colors.py
93
94
  shellmate/ui/progress.py
94
95
  shellmate/ui/status.py
95
96
  shellmate/ui/runtime/__init__.py
@@ -107,4 +108,5 @@ shellmate_cli.egg-info/SOURCES.txt
107
108
  shellmate_cli.egg-info/dependency_links.txt
108
109
  shellmate_cli.egg-info/entry_points.txt
109
110
  shellmate_cli.egg-info/requires.txt
110
- shellmate_cli.egg-info/top_level.txt
111
+ shellmate_cli.egg-info/top_level.txt
112
+ tests/test_colors.py
@@ -0,0 +1,69 @@
1
+ """Tests for the semantic color module and its annotation integration."""
2
+
3
+ import importlib
4
+
5
+ import pytest
6
+
7
+ from shellmate.ui import colors
8
+ from shellmate.ui import annotations
9
+
10
+
11
+ def test_roles_map_to_expected_ansi():
12
+ assert colors.ROLE_SGR["caution"] == 91 # red
13
+ assert colors.ROLE_SGR["warning"] == 93 # yellow
14
+ assert colors.ROLE_SGR["suggestion"] == 92 # green
15
+ assert colors.ROLE_SGR["safe"] == 92 # green
16
+
17
+
18
+ @pytest.mark.parametrize("sev,role", [
19
+ ("CRITICAL", "caution"), ("HIGH", "caution"), ("blocking", "caution"),
20
+ ("MEDIUM", "warning"), ("LOW", "safe"), ("SAFE", "safe"),
21
+ ("", "warning"), (None, "warning"),
22
+ ])
23
+ def test_severity_role_mapping(sev, role):
24
+ assert colors.role_for_severity(sev) == role
25
+
26
+
27
+ def test_colorize_wraps_and_resets():
28
+ out = colors.suggestion("Safe alt: git push --force-with-lease", no_color=False)
29
+ assert out.startswith("\033[92m")
30
+ assert out.endswith("\033[0m")
31
+ assert "Safe alt" in out
32
+
33
+
34
+ def test_caution_is_red_warning_is_yellow():
35
+ assert colors.caution("boom", no_color=False).startswith("\033[91m")
36
+ assert colors.warning("careful", no_color=False).startswith("\033[91m") is False
37
+ assert colors.warning("careful", no_color=False).startswith("\033[93m")
38
+
39
+
40
+ def test_no_color_param_disables():
41
+ assert colors.caution("x", no_color=True) == "x"
42
+
43
+
44
+ def test_empty_and_unknown_role_passthrough():
45
+ assert colors.colorize("", "caution", no_color=False) == ""
46
+ assert colors.colorize("hi", "no-such-role", no_color=False) == "hi"
47
+
48
+
49
+ def test_no_color_env(monkeypatch):
50
+ monkeypatch.setenv("NO_COLOR", "1")
51
+ assert colors.color_enabled() is False
52
+ assert colors.suggestion("x") == "x"
53
+ monkeypatch.delenv("NO_COLOR", raising=False)
54
+ assert colors.color_enabled() is True
55
+
56
+
57
+ def test_annotation_color_flag_colorizes_suggestion_path_green():
58
+ # A low/safe consequence should carry the green (92) role when color=True.
59
+ consequence = {"what_happens": "Lists files", "reversible": True, "severity": "info"}
60
+ plain = annotations.format_consequence_annotation(consequence, risk="SAFE")
61
+ colored = annotations.format_consequence_annotation(consequence, risk="SAFE", color=True)
62
+ assert "\033[" not in plain # default path unchanged (adapters color)
63
+ assert colored.startswith("\033[") and colored.endswith("\033[0m")
64
+
65
+
66
+ def test_annotation_default_is_plain_for_adapters():
67
+ # Adapters add their own ANSI, so the default (color=False) must stay plain.
68
+ result = {"risk": "HIGH", "risk_analysis": {"reason": "rewrites history"}}
69
+ assert "\033[" not in annotations.format_pipeline_annotation(result)
@@ -1 +0,0 @@
1
- VERSION = "3.0.1"
File without changes
File without changes
File without changes