shellmate-cli 3.0.0__py3-none-any.whl → 3.0.2__py3-none-any.whl
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.
- shellmate/adapters/ble_adapter.sh +7 -3
- shellmate/adapters/powershell_adapter.ps1 +7 -3
- shellmate/adapters/powershell_predictor.ps1 +3 -0
- shellmate/cli/version.py +1 -1
- shellmate/ui/annotations.py +24 -5
- shellmate/ui/colors.py +109 -0
- shellmate_cli-3.0.2.dist-info/METADATA +326 -0
- {shellmate_cli-3.0.0.dist-info → shellmate_cli-3.0.2.dist-info}/RECORD +12 -11
- shellmate_cli-3.0.0.dist-info/METADATA +0 -352
- {shellmate_cli-3.0.0.dist-info → shellmate_cli-3.0.2.dist-info}/WHEEL +0 -0
- {shellmate_cli-3.0.0.dist-info → shellmate_cli-3.0.2.dist-info}/entry_points.txt +0 -0
- {shellmate_cli-3.0.0.dist-info → shellmate_cli-3.0.2.dist-info}/licenses/LICENSE +0 -0
- {shellmate_cli-3.0.0.dist-info → shellmate_cli-3.0.2.dist-info}/top_level.txt +0 -0
|
@@ -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
|
-
|
|
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[
|
|
336
|
+
ble/canvas/put.draw "\e[92m${suffix}\e[m"
|
|
333
337
|
elif [[ -n "$suffix" ]]; then
|
|
334
|
-
printf '\e[
|
|
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
|
-
|
|
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) { "${
|
|
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
|
-
|
|
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: $_"
|
shellmate/cli/version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
VERSION = "3.0.
|
|
1
|
+
VERSION = "3.0.2"
|
shellmate/ui/annotations.py
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
248
|
+
if not msg:
|
|
249
|
+
return ""
|
|
250
|
+
text = f"{icon} {msg}"
|
|
251
|
+
return colors.by_severity(text, risk) if color else text
|
shellmate/ui/colors.py
ADDED
|
@@ -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)
|
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: shellmate-cli
|
|
3
|
+
Version: 3.0.2
|
|
4
|
+
Summary: Intelligent shell autocomplete with consequence preview
|
|
5
|
+
Author: ShellMate Contributors
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/sayan-does/shellmate
|
|
8
|
+
Project-URL: Repository, https://github.com/sayan-does/shellmate
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Environment :: Console
|
|
17
|
+
Classifier: Topic :: Utilities
|
|
18
|
+
Requires-Python: >=3.10
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
License-File: LICENSE
|
|
21
|
+
Requires-Dist: click>=8.0
|
|
22
|
+
Requires-Dist: pyyaml>=6.0
|
|
23
|
+
Requires-Dist: aiohttp>=3.9.0
|
|
24
|
+
Requires-Dist: groq>=0.4.0
|
|
25
|
+
Requires-Dist: openai>=1.0.0
|
|
26
|
+
Requires-Dist: numpy>=1.24.0
|
|
27
|
+
Requires-Dist: langgraph>=1.2.0
|
|
28
|
+
Requires-Dist: langchain-core>=1.4.0
|
|
29
|
+
Requires-Dist: rich>=13.0.0
|
|
30
|
+
Requires-Dist: psutil>=5.9.0
|
|
31
|
+
Requires-Dist: watchdog>=4.0.0
|
|
32
|
+
Provides-Extra: dev
|
|
33
|
+
Requires-Dist: build; extra == "dev"
|
|
34
|
+
Requires-Dist: twine; extra == "dev"
|
|
35
|
+
Requires-Dist: pytest; extra == "dev"
|
|
36
|
+
Requires-Dist: pytest-asyncio; extra == "dev"
|
|
37
|
+
Provides-Extra: windows-test
|
|
38
|
+
Requires-Dist: pywinpty>=2.0.0; sys_platform == "win32" and extra == "windows-test"
|
|
39
|
+
Requires-Dist: pyte>=0.8.1; extra == "windows-test"
|
|
40
|
+
Dynamic: license-file
|
|
41
|
+
|
|
42
|
+
# ShellMate
|
|
43
|
+
|
|
44
|
+
**A friendly safety net for your terminal.**
|
|
45
|
+
|
|
46
|
+
[](https://pypi.org/project/shellmate-cli/)
|
|
47
|
+
[](https://pypi.org/project/shellmate-cli/)
|
|
48
|
+
[](LICENSE)
|
|
49
|
+
|
|
50
|
+
The terminal is powerful — and a little scary when you're starting out. One wrong
|
|
51
|
+
command can delete files, wipe a branch, or break something you didn't mean to touch.
|
|
52
|
+
|
|
53
|
+
ShellMate is like having an experienced friend looking over your shoulder. As you
|
|
54
|
+
type, it quietly checks your command and, **before you press Enter**, tells you in
|
|
55
|
+
plain English what it will do, whether it's risky, and if there's a safer way. It
|
|
56
|
+
never runs anything for you — **you're always the one in control.**
|
|
57
|
+
|
|
58
|
+
```text
|
|
59
|
+
$ rm -rf /
|
|
60
|
+
[!!] BLOCKING — this would erase everything. Press Enter again only if you're sure.
|
|
61
|
+
|
|
62
|
+
$ git push --force
|
|
63
|
+
→ This rewrites history on the remote branch (others may lose work).
|
|
64
|
+
→ Reversible: No | target: origin/main
|
|
65
|
+
→ Safer option: git push --force-with-lease
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## Why you'll like it
|
|
71
|
+
|
|
72
|
+
- **Stops the scary stuff.** Genuinely dangerous commands (like `rm -rf /`) are
|
|
73
|
+
caught instantly and ask you to confirm before they run.
|
|
74
|
+
- **Explains commands in plain English.** No more guessing what a command does —
|
|
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,
|
|
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
|
|
81
|
+
commands on its own.
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## Get started in 3 steps
|
|
86
|
+
|
|
87
|
+
### 1. Install it
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
pip install shellmate-cli
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### 2. Give it a brain (a free API key)
|
|
94
|
+
|
|
95
|
+
ShellMate uses an AI service to explain commands. The easiest free option is **Groq**:
|
|
96
|
+
|
|
97
|
+
1. Grab a free key at **[console.groq.com](https://console.groq.com)** (takes a minute).
|
|
98
|
+
2. Tell ShellMate about it:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
sm config
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
It'll ask you to pick a provider and paste your key. That's it — the key is
|
|
105
|
+
stored privately on your computer, not in your command history.
|
|
106
|
+
|
|
107
|
+
### 3. Turn it on
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
sm install # connects ShellMate to your shell (one time)
|
|
111
|
+
sm enable # starts the helper in the background
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Now just **use your terminal normally.** Little notes will appear under your
|
|
115
|
+
commands as you type.
|
|
116
|
+
|
|
117
|
+
> **What you need:** Python 3.10+, and either **Windows PowerShell** or, on
|
|
118
|
+
> **Linux/WSL**, [ble.sh](https://github.com/akinomyoga/ble.sh). ShellMate sets up
|
|
119
|
+
> the rest for you.
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## What it feels like to use
|
|
124
|
+
|
|
125
|
+
As you type, ShellMate shows a tiny note under your command:
|
|
126
|
+
|
|
127
|
+
| You type | You see | What it means |
|
|
128
|
+
|----------|---------|---------------|
|
|
129
|
+
| `ls` | *(nothing)* | Totally safe — ShellMate stays quiet. |
|
|
130
|
+
| `git push --force` | `[!] rewrites history [PROD]` | Heads up: this is risky and you're on production. |
|
|
131
|
+
| `rm -rf /` | `[!!] BLOCKING — Enter again to confirm` | Dangerous. It won't run until you really mean it. |
|
|
132
|
+
|
|
133
|
+
**Dangerous commands don't run on the first Enter.** You'll get a clear warning, and
|
|
134
|
+
only a *second* Enter actually runs them — a gentle "are you sure?" that has saved
|
|
135
|
+
many people from a bad day.
|
|
136
|
+
|
|
137
|
+
**Want to understand a command?** Press **Tab** for a suggestion, and a short
|
|
138
|
+
"here's what this will do" preview appears before you commit to it.
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## Everyday commands
|
|
143
|
+
|
|
144
|
+
These are the only commands you need day to day:
|
|
145
|
+
|
|
146
|
+
| Command | What it does |
|
|
147
|
+
|---------|--------------|
|
|
148
|
+
| `sm enable` | Turn ShellMate on. |
|
|
149
|
+
| `sm disable` | Turn it off. |
|
|
150
|
+
| `sm status` | Check if it's running and healthy. |
|
|
151
|
+
| `sm config` | Set or update your API key. |
|
|
152
|
+
| `sm uninstall` | Remove ShellMate from your shell (keeps your settings). |
|
|
153
|
+
| `sm --help` | See everything. |
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
## A note on your privacy
|
|
158
|
+
|
|
159
|
+
Being honest about how it works:
|
|
160
|
+
|
|
161
|
+
- The **instant danger checks** (like catching `rm -rf /`) run **entirely on your
|
|
162
|
+
computer** — no internet needed.
|
|
163
|
+
- When ShellMate needs AI help to explain a trickier command, the **command text you
|
|
164
|
+
typed** is sent to the AI provider you chose (e.g. Groq) so it can analyze it.
|
|
165
|
+
- Your API key is stored locally in a private config file, never in your shell history.
|
|
166
|
+
- As with any tool that talks to a cloud service, **avoid typing passwords or secret
|
|
167
|
+
keys directly on the command line.**
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## Troubleshooting
|
|
172
|
+
|
|
173
|
+
- **"I don't see any notes."** Run `sm status` to confirm the daemon is running, and
|
|
174
|
+
make sure you ran `sm enable`. On a brand-new install, open a fresh terminal so the
|
|
175
|
+
shell hook loads.
|
|
176
|
+
- **"It says my AI model isn't available."** Some free accounts can't use every model.
|
|
177
|
+
ShellMate will warn you at startup and `sm status` lists the models your key *can*
|
|
178
|
+
use — set one with `sm config`.
|
|
179
|
+
- **"Is it slowing me down?"** Safe commands get an instant local check (under 10ms).
|
|
180
|
+
Only genuinely unclear commands ever wait on the AI.
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
|
|
184
|
+
<details>
|
|
185
|
+
<summary><b>Under the hood</b> (for the curious / power users)</summary>
|
|
186
|
+
|
|
187
|
+
### How it works
|
|
188
|
+
|
|
189
|
+
```
|
|
190
|
+
You type in the shell
|
|
191
|
+
→ ble.sh (Linux/WSL) or PSReadLine (Windows) adapter
|
|
192
|
+
→ IPC to a background daemon (TCP 127.0.0.1:9823)
|
|
193
|
+
→ LangGraph analysis pipeline
|
|
194
|
+
→ Inline annotation / confirmation gate
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
Three tiers, fastest first:
|
|
198
|
+
|
|
199
|
+
1. **Instant catastrophic check** — local YAML rules (`shellmate/rules/catastrophic.yaml`), no API, <10ms.
|
|
200
|
+
2. **Debounced partial analysis** — ~300ms after you pause typing (`/analyze-partial`).
|
|
201
|
+
3. **Full analysis** — rules → exact-match cache → AI + context agents when risk is unclear.
|
|
202
|
+
|
|
203
|
+
### Install from source
|
|
204
|
+
|
|
205
|
+
```bash
|
|
206
|
+
git clone https://github.com/sayan-does/shellmate.git
|
|
207
|
+
cd shellmate
|
|
208
|
+
pip install -e ".[dev]"
|
|
209
|
+
python -m build
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### Annotation badges
|
|
213
|
+
|
|
214
|
+
| Kind | Examples |
|
|
215
|
+
|------|----------|
|
|
216
|
+
| Risk | `[OK]` `[i]` `[!]` `[!!]` `[X]` |
|
|
217
|
+
| Environment | `[PROD]` `[STAGE]` `[DEV]` `[CI]` |
|
|
218
|
+
| Time | `BH` (business hours) · `AH` (after hours) |
|
|
219
|
+
| Chain / consequence | `[BROKEN]` `[ESCALATION]` `[IRREVERSIBLE]` |
|
|
220
|
+
|
|
221
|
+
### Bracket syntax (typed inline; adapters strip and honor them)
|
|
222
|
+
|
|
223
|
+
| Syntax | Purpose | Example |
|
|
224
|
+
|--------|---------|---------|
|
|
225
|
+
| `[?…]` | Ask a question about the command | `docker rm $(docker ps -aq) [?why dangerous]` |
|
|
226
|
+
| `[!…]` | Directive to the analyzer | `rm -rf /tmp [!force]` |
|
|
227
|
+
| `[@key=value]` | Override context | `git reset --hard [@env=production]` |
|
|
228
|
+
|
|
229
|
+
### Full CLI
|
|
230
|
+
|
|
231
|
+
| Command | Description |
|
|
232
|
+
|---------|-------------|
|
|
233
|
+
| `sm install` | Install shell hooks (bash/zsh → ble adapter, PowerShell → PS adapter) |
|
|
234
|
+
| `sm uninstall` | Remove shell hooks (keeps config) |
|
|
235
|
+
| `sm config` | Configure API keys and settings |
|
|
236
|
+
| `sm enable` | Start daemon (autocomplete mode) |
|
|
237
|
+
| `sm disable` | Stop daemon and clean session |
|
|
238
|
+
| `sm status` | Daemon, agents, providers, credentials |
|
|
239
|
+
| `sm --help` / `sm --version` | Help / version |
|
|
240
|
+
| `sm --no-color <cmd>` | Plain-text output |
|
|
241
|
+
|
|
242
|
+
```bash
|
|
243
|
+
sm install --dry-run # preview changes without touching files
|
|
244
|
+
sm config --show # masked credentials
|
|
245
|
+
sm config provider risk_analysis xai grok-3-mini # set a provider role
|
|
246
|
+
sm config cache.max_entries 200 # YAML override (dotted key)
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
Exit codes: `0` success · `2` usage error · `3` temporary error · `4` internal error.
|
|
250
|
+
|
|
251
|
+
### Analysis pipeline
|
|
252
|
+
|
|
253
|
+
Defined in `shellmate/orchestration/analysis_graph.py` (LangGraph `StateGraph`):
|
|
254
|
+
|
|
255
|
+
```mermaid
|
|
256
|
+
flowchart TD
|
|
257
|
+
START([start]) --> sanitize --> parse --> command_chain --> rules --> cache
|
|
258
|
+
cache -->|early exit| format
|
|
259
|
+
cache -->|needs AI| supervisor
|
|
260
|
+
supervisor -->|gather context| context_worker
|
|
261
|
+
supervisor -->|env only| env_context
|
|
262
|
+
context_worker --> risk_analyzer
|
|
263
|
+
env_context --> risk_analyzer
|
|
264
|
+
risk_analyzer --> consequence_builder --> remediation --> command_guide --> format
|
|
265
|
+
format --> query
|
|
266
|
+
query --> END([end])
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
**Early exits** (skip AI): sanitizer correction, high-confidence rule match, cache hit.
|
|
270
|
+
|
|
271
|
+
### Daemon agents (started by `sm enable`)
|
|
272
|
+
|
|
273
|
+
| Agent | Purpose |
|
|
274
|
+
|-------|---------|
|
|
275
|
+
| Watcher | OS, shell, git, tech-stack snapshot |
|
|
276
|
+
| Librarian | Project file index + search |
|
|
277
|
+
| Environment | Environment / staging signals |
|
|
278
|
+
| CommandChain | Chain-pattern analysis |
|
|
279
|
+
| Query | Answers `[?…]` queries |
|
|
280
|
+
|
|
281
|
+
### Providers
|
|
282
|
+
|
|
283
|
+
| Stage | Default | Fallback |
|
|
284
|
+
|-------|---------|----------|
|
|
285
|
+
| Supervisor / risk / guide | Groq | Grok (xAI) / heuristic |
|
|
286
|
+
| Remediation | OpenRouter | Grok (xAI) / Groq guide / generic text |
|
|
287
|
+
|
|
288
|
+
Configure **at least one** of Groq, OpenRouter, or Grok (xAI). Instant catastrophic
|
|
289
|
+
gating always uses local rules, with or without a key.
|
|
290
|
+
|
|
291
|
+
### Where things live
|
|
292
|
+
|
|
293
|
+
| Purpose | Linux / macOS | Windows |
|
|
294
|
+
|---------|---------------|---------|
|
|
295
|
+
| Config + `.env` | `~/.config/shellmate` | `%APPDATA%\ShellMate` |
|
|
296
|
+
| State / sessions / logs | `~/.local/state/shellmate` | `%LOCALAPPDATA%\ShellMate` |
|
|
297
|
+
| Cache | `~/.cache/shellmate` | `%LOCALAPPDATA%\ShellMate\cache` |
|
|
298
|
+
| Installed adapters | `~/.shellmate/adapters/` | `%USERPROFILE%\.shellmate\adapters\` |
|
|
299
|
+
|
|
300
|
+
### Environment variables
|
|
301
|
+
|
|
302
|
+
| Variable | Required | Purpose |
|
|
303
|
+
|----------|----------|---------|
|
|
304
|
+
| `GROQ_API_KEY` | one of the three | Risk analysis, supervisor, command guide |
|
|
305
|
+
| `OPENROUTER_API_KEY` | one of the three | Remediation alternatives |
|
|
306
|
+
| `XAI_API_KEY` | one of the three | Risk + remediation via Grok |
|
|
307
|
+
| `CONTEXT7_API_KEY` | no | Optional docs lookup |
|
|
308
|
+
| `GROQ_SUPERVISOR_MODEL` | no | Override supervisor model (default `qwen/qwen3.8-27b`) |
|
|
309
|
+
| `XAI_MODEL` | no | Override Grok model (default `grok-3-mini`) |
|
|
310
|
+
|
|
311
|
+
Optional YAML overrides live in `<config_dir>/config.yaml`.
|
|
312
|
+
|
|
313
|
+
### Dependencies (authoritative: `pyproject.toml`)
|
|
314
|
+
|
|
315
|
+
`click`, `rich`, `pyyaml` (CLI/UI) · `langgraph`, `langchain-core` (orchestration) ·
|
|
316
|
+
`groq`, `openai`, `aiohttp` (providers) · `psutil`, `watchdog` (runtime) · `numpy`.
|
|
317
|
+
|
|
318
|
+
CI builds and smoke-tests the wheel on Python 3.10–3.12. Publishing runs on `v*` tags
|
|
319
|
+
via Trusted Publishing (`.github/workflows/publish.yml`).
|
|
320
|
+
|
|
321
|
+
</details>
|
|
322
|
+
|
|
323
|
+
---
|
|
324
|
+
|
|
325
|
+
Made to make the terminal a little less scary. **MIT licensed** — contributions welcome at
|
|
326
|
+
[github.com/sayan-does/shellmate](https://github.com/sayan-does/shellmate).
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
shellmate/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
2
|
shellmate/adapters/__init__.py,sha256=4gnk5hxRGFfKS9NM0oHVFPczZe7wK-4pu7pVHDlhqWc,2292
|
|
3
3
|
shellmate/adapters/bash_adapter.sh,sha256=8n7DeBS_vy1_dwTmulY3wvsqhUEMYNy8Huz4gUh771M,312
|
|
4
|
-
shellmate/adapters/ble_adapter.sh,sha256=
|
|
5
|
-
shellmate/adapters/powershell_adapter.ps1,sha256=
|
|
6
|
-
shellmate/adapters/powershell_predictor.ps1,sha256=
|
|
4
|
+
shellmate/adapters/ble_adapter.sh,sha256=yAVdAYOV3e7HNiqXtmRCg3nJCt2TkrrHsddPAQY3eDo,11539
|
|
5
|
+
shellmate/adapters/powershell_adapter.ps1,sha256=XfLWswjYOFbSY86rXHKtWLFIn1NM8cTiMxEIbZVWJ9U,16084
|
|
6
|
+
shellmate/adapters/powershell_predictor.ps1,sha256=C52Ds-2seMGd_jWjazOaaoHO6YdhnH3Qk1OzTVKPDHI,4362
|
|
7
7
|
shellmate/adapters/zsh_adapter.zsh,sha256=NBqP4d0JxkjlyAbRt95JqNe99o3YSHcT18R_nX2qeVg,342
|
|
8
8
|
shellmate/agents/__init__.py,sha256=L1p1AT5XqnijplQv23G4OrzyAGlGTGfVX4StsNj_6Cc,515
|
|
9
9
|
shellmate/agents/base.py,sha256=vfwvln6P2_qk054udxf0jGE921mMOxZJh0OjBYCH4JA,8140
|
|
@@ -20,7 +20,7 @@ shellmate/cache/semantic_cache.py,sha256=d9Je0gRFW1ASau8xemGBbbJfxk08C3QkIl_HtCI
|
|
|
20
20
|
shellmate/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
21
21
|
shellmate/cli/main.py,sha256=_gljAQLkDgYgukkyYNVjjow7d-FIOtWbFufnFhsNr7A,3815
|
|
22
22
|
shellmate/cli/parser.py,sha256=Lh3mo6aHwn6ISmXoFKMfROF--tSe7Rpig_3rpjTGgi0,5747
|
|
23
|
-
shellmate/cli/version.py,sha256=
|
|
23
|
+
shellmate/cli/version.py,sha256=obmd3VOGkno3g89HIX3XZWI2xsdprkRqSyf-ntBilNg,18
|
|
24
24
|
shellmate/cli/commands/__init__.py,sha256=MFJrPc6Yrhe6eFxTAJI8dnOnqUdILoh3JnBRCevVOwU,1032
|
|
25
25
|
shellmate/cli/commands/analyze.py,sha256=t5BxxGBrPizJvgsbrvOpsF327DxgunoN7QefaBXIc3k,270
|
|
26
26
|
shellmate/cli/commands/config.py,sha256=DI22jVKEwaFLqQIA3ps-ZFRz6lNML_DYBWBtyh70ISA,10168
|
|
@@ -84,8 +84,9 @@ shellmate/sanitizer/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSu
|
|
|
84
84
|
shellmate/sanitizer/typo_detector.py,sha256=ZBhOzB7ZSv4hctbu60tu_iBMjcBIP4TLjQ20aoCLVVQ,2957
|
|
85
85
|
shellmate/ui/__init__.py,sha256=i8MvY7xFXCg9WRXFJcny3iiNBUnHoNW8U82t4tjqSrs,252
|
|
86
86
|
shellmate/ui/analysis.py,sha256=U5WrGXSrBfR2Q_xOZ_nxi6frouTU6CrmHu-t7afLpmc,5905
|
|
87
|
-
shellmate/ui/annotations.py,sha256=
|
|
87
|
+
shellmate/ui/annotations.py,sha256=1qp2zhD6bmRKG2TtSVvS7KaG5U0ZZ9TMOLvWYoCwAy8,7525
|
|
88
88
|
shellmate/ui/banner.py,sha256=gOT8hYnO6pRqPW_tWx5KPInw9TOcByLthdmKz8uneaI,1752
|
|
89
|
+
shellmate/ui/colors.py,sha256=1qzvODW1rpAlkH_02HgCbh5WY_hGOycJsMQydeQXzx4,3576
|
|
89
90
|
shellmate/ui/progress.py,sha256=5rLtBR4VM9CUs7phTz6VJqVSIou0hGTk9YDRE-E0nvc,4020
|
|
90
91
|
shellmate/ui/status.py,sha256=EmsWCd5R_h-rQ9nApBBUFPhhZurfKoXY3_te4h7LIJo,134
|
|
91
92
|
shellmate/ui/runtime/__init__.py,sha256=G2tRJWagzRJSUYRVmyb0cBVxPr5XKwoXibVmSdXkA4Y,448
|
|
@@ -98,9 +99,9 @@ shellmate/ui/scenes/enable.py,sha256=wrKSyGg_aSU3anvCAMPQOBq9E13KDCtQT3GRDbCSalA
|
|
|
98
99
|
shellmate/ui/scenes/help.py,sha256=ozg_JosfzpA4cNCSWHazETiRuU9gPO_-Y04arYmgUUU,1904
|
|
99
100
|
shellmate/ui/scenes/stages.py,sha256=pK396uMatWxJm_XbWCJTWsvx7W6Sx0s7G95S3ec1ffQ,4048
|
|
100
101
|
shellmate/ui/scenes/status.py,sha256=nC-wAC8cdE3UC2QXeK9zD8fOAMpdLogHK67h74a9Nek,6427
|
|
101
|
-
shellmate_cli-3.0.
|
|
102
|
-
shellmate_cli-3.0.
|
|
103
|
-
shellmate_cli-3.0.
|
|
104
|
-
shellmate_cli-3.0.
|
|
105
|
-
shellmate_cli-3.0.
|
|
106
|
-
shellmate_cli-3.0.
|
|
102
|
+
shellmate_cli-3.0.2.dist-info/licenses/LICENSE,sha256=_eDrSi0nYhgVjipoIJoiUEdbARagURo6nVbTGBzqvD4,1079
|
|
103
|
+
shellmate_cli-3.0.2.dist-info/METADATA,sha256=AXJSeTRRhxe3wCX6cqNS5uc7fJAQl8PKi5Pr86w9BRA,11788
|
|
104
|
+
shellmate_cli-3.0.2.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
|
|
105
|
+
shellmate_cli-3.0.2.dist-info/entry_points.txt,sha256=lBMog9g17_P4iSXr6Akfg_QTVzswLWXUeGnuq66Wwhw,83
|
|
106
|
+
shellmate_cli-3.0.2.dist-info/top_level.txt,sha256=0htuoy1ZSdVSt_0l2mO-mPPvSp2veo6dbr3h0K_AmIw,10
|
|
107
|
+
shellmate_cli-3.0.2.dist-info/RECORD,,
|
|
@@ -1,352 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: shellmate-cli
|
|
3
|
-
Version: 3.0.0
|
|
4
|
-
Summary: Intelligent shell autocomplete with consequence preview
|
|
5
|
-
Author: ShellMate Contributors
|
|
6
|
-
License: MIT
|
|
7
|
-
Project-URL: Homepage, https://github.com/sayan-does/shellmate
|
|
8
|
-
Project-URL: Repository, https://github.com/sayan-does/shellmate
|
|
9
|
-
Classifier: Development Status :: 4 - Beta
|
|
10
|
-
Classifier: Intended Audience :: Developers
|
|
11
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
-
Classifier: Programming Language :: Python :: 3
|
|
13
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
-
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
-
Classifier: Environment :: Console
|
|
17
|
-
Classifier: Topic :: Utilities
|
|
18
|
-
Requires-Python: >=3.10
|
|
19
|
-
Description-Content-Type: text/markdown
|
|
20
|
-
License-File: LICENSE
|
|
21
|
-
Requires-Dist: click>=8.0
|
|
22
|
-
Requires-Dist: pyyaml>=6.0
|
|
23
|
-
Requires-Dist: aiohttp>=3.9.0
|
|
24
|
-
Requires-Dist: groq>=0.4.0
|
|
25
|
-
Requires-Dist: openai>=1.0.0
|
|
26
|
-
Requires-Dist: numpy>=1.24.0
|
|
27
|
-
Requires-Dist: langgraph>=1.2.0
|
|
28
|
-
Requires-Dist: langchain-core>=1.4.0
|
|
29
|
-
Requires-Dist: rich>=13.0.0
|
|
30
|
-
Requires-Dist: psutil>=5.9.0
|
|
31
|
-
Requires-Dist: watchdog>=4.0.0
|
|
32
|
-
Provides-Extra: dev
|
|
33
|
-
Requires-Dist: build; extra == "dev"
|
|
34
|
-
Requires-Dist: twine; extra == "dev"
|
|
35
|
-
Requires-Dist: pytest; extra == "dev"
|
|
36
|
-
Requires-Dist: pytest-asyncio; extra == "dev"
|
|
37
|
-
Provides-Extra: windows-test
|
|
38
|
-
Requires-Dist: pywinpty>=2.0.0; sys_platform == "win32" and extra == "windows-test"
|
|
39
|
-
Requires-Dist: pyte>=0.8.1; extra == "windows-test"
|
|
40
|
-
Dynamic: license-file
|
|
41
|
-
|
|
42
|
-
# ShellMate
|
|
43
|
-
|
|
44
|
-
Intelligent shell autocomplete with consequence preview.
|
|
45
|
-
|
|
46
|
-
**Version:** 3.0.0
|
|
47
|
-
**Package:** [`shellmate-cli`](https://pypi.org/project/shellmate-cli/)
|
|
48
|
-
**License:** MIT
|
|
49
|
-
**Python:** 3.10+
|
|
50
|
-
|
|
51
|
-
ShellMate watches what you type and shows **inline risk annotations** before you press Enter. A background daemon analyzes commands with deterministic rules, an exact-match cache, and a LangGraph pipeline that pulls project context plus Groq / OpenRouter / Grok (xAI) for AI risk and remediation.
|
|
52
|
-
|
|
53
|
-
## How it works
|
|
54
|
-
|
|
55
|
-
```
|
|
56
|
-
You type in the shell
|
|
57
|
-
→ ble.sh (Linux/WSL) or PSReadLine (Windows) adapter
|
|
58
|
-
→ IPC to daemon (TCP 127.0.0.1:9823)
|
|
59
|
-
→ LangGraph analysis pipeline
|
|
60
|
-
→ Inline annotation / confirmation gate
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
1. **Instant catastrophic check** — local YAML rules (`rules/catastrophic.yaml`), no API.
|
|
64
|
-
2. **Debounced partial analysis** — after a short pause while typing.
|
|
65
|
-
3. **Full analysis** — rules → cache → AI + context agents when risk is unclear.
|
|
66
|
-
|
|
67
|
-
## Installation
|
|
68
|
-
|
|
69
|
-
### From PyPI
|
|
70
|
-
|
|
71
|
-
```bash
|
|
72
|
-
pip install shellmate-cli
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
### From source
|
|
76
|
-
|
|
77
|
-
```bash
|
|
78
|
-
git clone https://github.com/sayan-does/shellmate.git
|
|
79
|
-
cd shellmate
|
|
80
|
-
pip install -e .
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
## Quick start
|
|
84
|
-
|
|
85
|
-
### 1. Prerequisites
|
|
86
|
-
|
|
87
|
-
| Platform | Required |
|
|
88
|
-
|----------|----------|
|
|
89
|
-
| Linux / WSL | [ble.sh](https://github.com/akinomyoga/ble.sh) (e.g. under `~/.local/share/blesh/ble.sh`) |
|
|
90
|
-
| Windows | PowerShell with PSReadLine |
|
|
91
|
-
| All | At least one of `GROQ_API_KEY`, `OPENROUTER_API_KEY`, or `XAI_API_KEY` |
|
|
92
|
-
|
|
93
|
-
### 2. Install hooks and configure keys
|
|
94
|
-
|
|
95
|
-
```bash
|
|
96
|
-
sm install # copy adapter → ~/.shellmate/adapters/ and append RC/profile hook
|
|
97
|
-
sm config # select provider (Groq / OpenRouter / Grok xAI), paste API key
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
Dry-run without changing files:
|
|
101
|
-
|
|
102
|
-
```bash
|
|
103
|
-
sm install --dry-run
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
### 3. Start the daemon
|
|
107
|
-
|
|
108
|
-
```bash
|
|
109
|
-
sm enable
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
Starts the daemon for the current directory (Watcher, Librarian, and other agents). Check with `sm status`.
|
|
113
|
-
|
|
114
|
-
### 4. Type normally
|
|
115
|
-
|
|
116
|
-
Inline annotations appear under the line as you type. Catastrophic commands require a second Enter to confirm.
|
|
117
|
-
|
|
118
|
-
### 5. Stop / remove
|
|
119
|
-
|
|
120
|
-
```bash
|
|
121
|
-
sm disable # stop daemon
|
|
122
|
-
sm uninstall # remove shell hooks (keeps config)
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
## Autocomplete features
|
|
126
|
-
|
|
127
|
-
ShellMate’s main UX is **in-shell autocomplete annotations** (not a separate `sm -a` command). Adapters hook ble.sh or PSReadLine and talk to the daemon over IPC.
|
|
128
|
-
|
|
129
|
-
### Two-phase typing feedback
|
|
130
|
-
|
|
131
|
-
| Phase | Latency | What happens |
|
|
132
|
-
|-------|---------|--------------|
|
|
133
|
-
| **1 — Instant** | <10ms, no API | `/instant-check` against `rules/catastrophic.yaml`. Blocking commands get an immediate annotation. |
|
|
134
|
-
| **2 — Debounced** | ~300ms after you pause | `/analyze-partial` runs the analysis pipeline and **replaces** the annotation in place with risk + badges. |
|
|
135
|
-
|
|
136
|
-
```bash
|
|
137
|
-
$ git push --force
|
|
138
|
-
[!] rewrites history [PROD] BH
|
|
139
|
-
|
|
140
|
-
$ rm -rf /
|
|
141
|
-
[!] BLOCKING - Type again to confirm
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
### Consequence preview (Tab × 2)
|
|
145
|
-
|
|
146
|
-
1. **First Tab** — completion / ghost-text suggestion
|
|
147
|
-
2. **Second Tab** — accept suggestion and show a **consequence preview** below the prompt:
|
|
148
|
-
|
|
149
|
-
```text
|
|
150
|
-
→ Force-pushes and rewrites remote history
|
|
151
|
-
→ Reversible: No | remote: origin/main
|
|
152
|
-
→ Safe alt: git push --force-with-lease
|
|
153
|
-
```
|
|
154
|
-
|
|
155
|
-
On HIGH / CRITICAL / blocking severity, Enter is gated until you confirm again.
|
|
156
|
-
|
|
157
|
-
| Platform | Completion surface |
|
|
158
|
-
|----------|-------------------|
|
|
159
|
-
| Linux / WSL (ble.sh) | Tab bound to ShellMate complete widget |
|
|
160
|
-
| PowerShell 7.2+ | PSReadLine predictor (`powershell_predictor.ps1`) when available |
|
|
161
|
-
| PowerShell 5.1 | Tab handler ghost-text fallback |
|
|
162
|
-
|
|
163
|
-
### Double-Enter confirmation
|
|
164
|
-
|
|
165
|
-
Dangerous commands do not run on the first Enter:
|
|
166
|
-
|
|
167
|
-
1. First Enter → blocked; annotation asks you to confirm
|
|
168
|
-
2. Second Enter (same command) → allowed
|
|
169
|
-
|
|
170
|
-
Applies to catastrophic instant matches and to HIGH/CRITICAL consequence previews.
|
|
171
|
-
|
|
172
|
-
### Annotation badges
|
|
173
|
-
|
|
174
|
-
Short inline text under the command (ASCII-safe on all platforms):
|
|
175
|
-
|
|
176
|
-
| Kind | Examples |
|
|
177
|
-
|------|----------|
|
|
178
|
-
| Risk | `[OK]` `[i]` `[!]` `[!!]` `[X]` |
|
|
179
|
-
| Environment | `[PROD]` `[STAGE]` `[DEV]` `[CI]` |
|
|
180
|
-
| Time | `BH` (business hours) · `AH` (after hours) |
|
|
181
|
-
| Chain / consequence | `[BROKEN]` `[ESCALATION]` `[IRREVERSIBLE]` |
|
|
182
|
-
|
|
183
|
-
### Bracket syntax
|
|
184
|
-
|
|
185
|
-
Typed as part of the command line; adapters/daemon strip and honor them:
|
|
186
|
-
|
|
187
|
-
| Syntax | Purpose | Example |
|
|
188
|
-
|--------|---------|---------|
|
|
189
|
-
| `[?…]` | Ask a question about the command | `docker rm $(docker ps -aq) [?why dangerous]` |
|
|
190
|
-
| `[!…]` | Directive to the analyzer | `rm -rf /tmp [!force]` |
|
|
191
|
-
| `[@key=value]` | Override context (e.g. environment) | `git reset --hard [@env=production]` |
|
|
192
|
-
|
|
193
|
-
### Surfaces
|
|
194
|
-
|
|
195
|
-
| Adapter | Shell | Role |
|
|
196
|
-
|---------|-------|------|
|
|
197
|
-
| `ble_adapter.sh` | bash/zsh + ble.sh | Primary Linux/WSL autocomplete |
|
|
198
|
-
| `powershell_adapter.ps1` | PowerShell + PSReadLine | Primary Windows autocomplete |
|
|
199
|
-
| `powershell_predictor.ps1` | PowerShell 7.2+ | Optional ghost-text predictor |
|
|
200
|
-
| `bash_adapter.sh` / `zsh_adapter.zsh` | — | Deprecated stubs |
|
|
201
|
-
|
|
202
|
-
Requires at least one of `GROQ_API_KEY`, `OPENROUTER_API_KEY`, or `XAI_API_KEY` for AI annotations and consequence previews. Instant catastrophic checks still use local rules.
|
|
203
|
-
|
|
204
|
-
## CLI
|
|
205
|
-
|
|
206
|
-
| Command | Description |
|
|
207
|
-
|---------|-------------|
|
|
208
|
-
| `sm install` | Install shell hooks (bash/zsh → ble adapter, PowerShell → PS adapter) |
|
|
209
|
-
| `sm uninstall` | Remove shell hooks |
|
|
210
|
-
| `sm config` | Configure API keys and settings |
|
|
211
|
-
| `sm enable` | Start daemon (autocomplete mode) |
|
|
212
|
-
| `sm disable` | Stop daemon and clean session |
|
|
213
|
-
| `sm status` | Daemon, agents, providers, credentials |
|
|
214
|
-
| `sm --help` | Help |
|
|
215
|
-
| `sm --version` | Version |
|
|
216
|
-
| `sm --no-color <cmd>` | Plain text output |
|
|
217
|
-
|
|
218
|
-
### `sm config` usages
|
|
219
|
-
|
|
220
|
-
```bash
|
|
221
|
-
sm config # validate existing keys; select provider → paste key
|
|
222
|
-
sm config --show # masked credentials
|
|
223
|
-
sm config provider risk_analysis xai grok-3-mini # set provider role
|
|
224
|
-
sm config cache.max_entries 200 # YAML override (dotted key)
|
|
225
|
-
```
|
|
226
|
-
|
|
227
|
-
Interactive setup asks you to pick Groq, OpenRouter, or Grok (xAI), then paste the key. Existing keys are checked against the provider API.
|
|
228
|
-
### Exit codes
|
|
229
|
-
|
|
230
|
-
| Code | Meaning |
|
|
231
|
-
|------|---------|
|
|
232
|
-
| 0 | Success |
|
|
233
|
-
| 2 | Usage error |
|
|
234
|
-
| 3 | Temporary error (e.g. interrupt) |
|
|
235
|
-
| 4 | Internal error |
|
|
236
|
-
|
|
237
|
-
## Shell adapters
|
|
238
|
-
|
|
239
|
-
See [Autocomplete features](#autocomplete-features) for behavior. `sm install` copies the ble or PowerShell adapter into `~/.shellmate/adapters/` and appends a source line to your RC / profile.
|
|
240
|
-
|
|
241
|
-
## Architecture
|
|
242
|
-
|
|
243
|
-
### Analysis pipeline
|
|
244
|
-
|
|
245
|
-
Defined in `shellmate/orchestration/analysis_graph.py` (LangGraph `StateGraph`):
|
|
246
|
-
|
|
247
|
-
```mermaid
|
|
248
|
-
flowchart TD
|
|
249
|
-
START([start]) --> sanitize --> parse --> command_chain --> rules --> cache
|
|
250
|
-
cache -->|early exit| format
|
|
251
|
-
cache -->|needs AI| supervisor
|
|
252
|
-
supervisor -->|gather context| context_worker
|
|
253
|
-
supervisor -->|env only| env_context
|
|
254
|
-
context_worker --> risk_analyzer
|
|
255
|
-
env_context --> risk_analyzer
|
|
256
|
-
risk_analyzer --> consequence_builder --> remediation --> command_guide --> format
|
|
257
|
-
format --> query
|
|
258
|
-
query --> END([end])
|
|
259
|
-
```
|
|
260
|
-
|
|
261
|
-
| Node | Role |
|
|
262
|
-
|------|------|
|
|
263
|
-
| `sanitize` / `parse` | Typo / syntax checks; tokenize |
|
|
264
|
-
| `command_chain` | Multi-step / chain pattern detection |
|
|
265
|
-
| `rules` | YAML rules under `shellmate/rules/definitions/` |
|
|
266
|
-
| `cache` | Exact-match in-memory cache (no FAISS) |
|
|
267
|
-
| `supervisor` | Groq (or heuristic) decides context needs |
|
|
268
|
-
| `context_worker` / `env_context` | Parallel agent queries or env-only path |
|
|
269
|
-
| `risk_analyzer` | Groq risk assessment |
|
|
270
|
-
| `consequence_builder` | Structured consequence preview |
|
|
271
|
-
| `remediation` | OpenRouter safer alternatives |
|
|
272
|
-
| `command_guide` | Groq “what you meant” guide |
|
|
273
|
-
| `format` / `query` | Final payload; optional `[?…]` query handling |
|
|
274
|
-
|
|
275
|
-
**Early exits** (skip AI): sanitizer correction, high-confidence rule match, cache hit.
|
|
276
|
-
|
|
277
|
-
### Agents (daemon)
|
|
278
|
-
|
|
279
|
-
Started with `sm enable`:
|
|
280
|
-
|
|
281
|
-
| Agent | Purpose |
|
|
282
|
-
|-------|---------|
|
|
283
|
-
| Watcher | OS, shell, git, tech-stack snapshot |
|
|
284
|
-
| Librarian | Project file index + search |
|
|
285
|
-
| Environment | Environment / staging signals |
|
|
286
|
-
| CommandChain | Chain-pattern analysis |
|
|
287
|
-
| Query | Answers `[?…]` queries |
|
|
288
|
-
|
|
289
|
-
Repomix-style pack search runs alongside Librarian in the context worker when needed.
|
|
290
|
-
|
|
291
|
-
### Providers
|
|
292
|
-
|
|
293
|
-
| Stage | Default | Fallback |
|
|
294
|
-
|-------|---------|----------|
|
|
295
|
-
| Supervisor / risk / guide | Groq | Grok (xAI) / heuristic |
|
|
296
|
-
| Remediation | OpenRouter | Grok (xAI) / Groq guide / generic text |
|
|
297
|
-
|
|
298
|
-
Configure **at least one** of Groq, OpenRouter, or Grok (xAI). Groq covers risk analysis, supervisor routing, and command guide; OpenRouter covers remediation; xAI can cover both risk and remediation. Instant catastrophic gating still uses local rules either way.
|
|
299
|
-
## Configuration
|
|
300
|
-
|
|
301
|
-
### Directories
|
|
302
|
-
|
|
303
|
-
| Purpose | Linux / macOS | Windows |
|
|
304
|
-
|---------|---------------|---------|
|
|
305
|
-
| Config + `.env` | `~/.config/shellmate` | `%APPDATA%\ShellMate` |
|
|
306
|
-
| State / sessions / logs | `~/.local/state/shellmate` | `%LOCALAPPDATA%\ShellMate` |
|
|
307
|
-
| Cache dir | `~/.cache/shellmate` | `%LOCALAPPDATA%\ShellMate\cache` |
|
|
308
|
-
| Installed adapters | `~/.shellmate/adapters/` | `%USERPROFILE%\.shellmate\adapters\` |
|
|
309
|
-
|
|
310
|
-
Credentials file: `<config_dir>/.env` (permissions restricted on Unix).
|
|
311
|
-
|
|
312
|
-
### Environment variables
|
|
313
|
-
|
|
314
|
-
| Variable | Required | Purpose |
|
|
315
|
-
|----------|----------|---------|
|
|
316
|
-
| `GROQ_API_KEY` | At least one of Groq / OpenRouter / xAI | Risk analysis, supervisor, command guide |
|
|
317
|
-
| `OPENROUTER_API_KEY` | At least one of Groq / OpenRouter / xAI | Remediation alternatives |
|
|
318
|
-
| `XAI_API_KEY` | At least one of Groq / OpenRouter / xAI | Risk analysis and remediation via Grok |
|
|
319
|
-
| `CONTEXT7_API_KEY` | No | Optional docs lookup |
|
|
320
|
-
| `GROQ_SUPERVISOR_MODEL` | No | Override supervisor model (default `qwen/qwen3.8-27b`) |
|
|
321
|
-
| `XAI_MODEL` | No | Override Grok model (default `grok-3-mini`) |
|
|
322
|
-
|
|
323
|
-
Optional YAML overrides live in `<config_dir>/config.yaml`.
|
|
324
|
-
|
|
325
|
-
## Requirements
|
|
326
|
-
|
|
327
|
-
- Python 3.10+
|
|
328
|
-
- ble.sh (Linux/WSL) or PowerShell + PSReadLine (Windows)
|
|
329
|
-
- At least one of `GROQ_API_KEY`, `OPENROUTER_API_KEY`, or `XAI_API_KEY` (Groq + OpenRouter recommended for full risk + remediation)
|
|
330
|
-
|
|
331
|
-
### Dependencies (authoritative: `pyproject.toml`)
|
|
332
|
-
|
|
333
|
-
- `click`, `rich`, `pyyaml` — CLI / UI
|
|
334
|
-
- `langgraph`, `langchain-core` — orchestration
|
|
335
|
-
- `groq`, `openai`, `aiohttp` — providers
|
|
336
|
-
- `psutil`, `watchdog` — runtime monitoring
|
|
337
|
-
- `numpy` — declared runtime dependency
|
|
338
|
-
|
|
339
|
-
## Development
|
|
340
|
-
|
|
341
|
-
```bash
|
|
342
|
-
git clone https://github.com/sayan-does/shellmate.git
|
|
343
|
-
cd shellmate
|
|
344
|
-
pip install -e ".[dev]"
|
|
345
|
-
python -m build
|
|
346
|
-
```
|
|
347
|
-
|
|
348
|
-
CI builds and smoke-tests the wheel on Python 3.10–3.12. Publishing runs on `v*` tags via Trusted Publishing (`.github/workflows/publish.yml`).
|
|
349
|
-
|
|
350
|
-
## License
|
|
351
|
-
|
|
352
|
-
MIT
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|