shellmate-cli 3.0.0__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.
Files changed (106) hide show
  1. shellmate/__init__.py +0 -0
  2. shellmate/adapters/__init__.py +67 -0
  3. shellmate/adapters/bash_adapter.sh +7 -0
  4. shellmate/adapters/ble_adapter.sh +350 -0
  5. shellmate/adapters/powershell_adapter.ps1 +417 -0
  6. shellmate/adapters/powershell_predictor.ps1 +97 -0
  7. shellmate/adapters/zsh_adapter.zsh +8 -0
  8. shellmate/agents/__init__.py +23 -0
  9. shellmate/agents/base.py +248 -0
  10. shellmate/agents/chain_patterns.yaml +98 -0
  11. shellmate/agents/command_chain.py +322 -0
  12. shellmate/agents/context_gatherer.py +175 -0
  13. shellmate/agents/environment.py +363 -0
  14. shellmate/agents/librarian.py +502 -0
  15. shellmate/agents/query.py +415 -0
  16. shellmate/agents/repomix_backend.py +171 -0
  17. shellmate/agents/watcher.py +266 -0
  18. shellmate/cache/__init__.py +0 -0
  19. shellmate/cache/semantic_cache.py +41 -0
  20. shellmate/cli/__init__.py +0 -0
  21. shellmate/cli/commands/__init__.py +27 -0
  22. shellmate/cli/commands/analyze.py +5 -0
  23. shellmate/cli/commands/config.py +319 -0
  24. shellmate/cli/commands/configure.py +118 -0
  25. shellmate/cli/commands/disable.py +11 -0
  26. shellmate/cli/commands/enable.py +49 -0
  27. shellmate/cli/commands/init.py +412 -0
  28. shellmate/cli/commands/install.py +242 -0
  29. shellmate/cli/commands/migrate.py +147 -0
  30. shellmate/cli/commands/status.py +31 -0
  31. shellmate/cli/commands/uninstall.py +78 -0
  32. shellmate/cli/main.py +114 -0
  33. shellmate/cli/parser.py +184 -0
  34. shellmate/cli/version.py +1 -0
  35. shellmate/completion/__init__.py +4 -0
  36. shellmate/completion/bash_adapter.py +145 -0
  37. shellmate/config/__init__.py +33 -0
  38. shellmate/config/credentials.py +120 -0
  39. shellmate/config/defaults.py +88 -0
  40. shellmate/config/manager.py +94 -0
  41. shellmate/config/paths.py +77 -0
  42. shellmate/config/secrets.py +66 -0
  43. shellmate/daemon/__init__.py +0 -0
  44. shellmate/daemon/lifecycle.py +308 -0
  45. shellmate/daemon/observability.py +82 -0
  46. shellmate/daemon/pipeline.py +119 -0
  47. shellmate/daemon/server.py +605 -0
  48. shellmate/daemon/session.py +170 -0
  49. shellmate/daemon/startup_ui.py +43 -0
  50. shellmate/environment/__init__.py +0 -0
  51. shellmate/environment/detector.py +130 -0
  52. shellmate/ipc/__init__.py +0 -0
  53. shellmate/ipc/client.py +210 -0
  54. shellmate/ipc/ps_client.py +190 -0
  55. shellmate/orchestration/__init__.py +3 -0
  56. shellmate/orchestration/analysis_graph.py +1274 -0
  57. shellmate/orchestration/state.py +35 -0
  58. shellmate/parser/__init__.py +0 -0
  59. shellmate/parser/command_parser.py +111 -0
  60. shellmate/plugins/__init__.py +0 -0
  61. shellmate/plugins/base.py +80 -0
  62. shellmate/plugins/loader.py +41 -0
  63. shellmate/providers/__init__.py +0 -0
  64. shellmate/providers/base.py +97 -0
  65. shellmate/providers/groq_command_guide.py +108 -0
  66. shellmate/providers/groq_provider.py +127 -0
  67. shellmate/providers/groq_supervisor.py +78 -0
  68. shellmate/providers/openrouter_provider.py +149 -0
  69. shellmate/providers/router.py +272 -0
  70. shellmate/providers/validate_keys.py +98 -0
  71. shellmate/providers/xai_provider.py +142 -0
  72. shellmate/rules/__init__.py +0 -0
  73. shellmate/rules/catastrophic.yaml +96 -0
  74. shellmate/rules/catastrophic_matcher.py +167 -0
  75. shellmate/rules/definitions/__init__.py +0 -0
  76. shellmate/rules/definitions/file_deletion.yaml +54 -0
  77. shellmate/rules/definitions/filesystem.yaml +43 -0
  78. shellmate/rules/definitions/network_execution.yaml +46 -0
  79. shellmate/rules/definitions/permissions.yaml +54 -0
  80. shellmate/rules/definitions/process_control.yaml +37 -0
  81. shellmate/rules/definitions/system_destruction.yaml +67 -0
  82. shellmate/rules/engine.py +200 -0
  83. shellmate/sanitizer/__init__.py +0 -0
  84. shellmate/sanitizer/typo_detector.py +114 -0
  85. shellmate/ui/__init__.py +10 -0
  86. shellmate/ui/analysis.py +164 -0
  87. shellmate/ui/annotations.py +232 -0
  88. shellmate/ui/banner.py +49 -0
  89. shellmate/ui/progress.py +126 -0
  90. shellmate/ui/runtime/__init__.py +15 -0
  91. shellmate/ui/runtime/console_factory.py +65 -0
  92. shellmate/ui/runtime/live_scene.py +116 -0
  93. shellmate/ui/runtime/motion_policy.py +69 -0
  94. shellmate/ui/runtime/tokens.py +47 -0
  95. shellmate/ui/scenes/__init__.py +24 -0
  96. shellmate/ui/scenes/enable.py +316 -0
  97. shellmate/ui/scenes/help.py +53 -0
  98. shellmate/ui/scenes/stages.py +128 -0
  99. shellmate/ui/scenes/status.py +181 -0
  100. shellmate/ui/status.py +3 -0
  101. shellmate_cli-3.0.0.dist-info/METADATA +352 -0
  102. shellmate_cli-3.0.0.dist-info/RECORD +106 -0
  103. shellmate_cli-3.0.0.dist-info/WHEEL +5 -0
  104. shellmate_cli-3.0.0.dist-info/entry_points.txt +3 -0
  105. shellmate_cli-3.0.0.dist-info/licenses/LICENSE +21 -0
  106. shellmate_cli-3.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,181 @@
1
+ """Status scene — quiet assemble, no hardcoded width."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import time
6
+ from typing import Callable, Optional
7
+
8
+ from rich.console import Console, Group
9
+ from rich.panel import Panel
10
+ from rich.table import Table
11
+ from rich.text import Text
12
+
13
+ from shellmate.ui.runtime import tokens
14
+ from shellmate.ui.runtime.console_factory import ConsoleFactory
15
+ from shellmate.ui.runtime.live_scene import LiveScene
16
+ from shellmate.ui.runtime.motion_policy import MotionPolicy
17
+
18
+
19
+ def _status_tag(status: str) -> Text:
20
+ if status in ("running", "ready"):
21
+ return Text(" * ", style="bold green") + Text("Running", style="green")
22
+ if status == "stopped":
23
+ return Text(" o ", style="bold red") + Text("Stopped", style="red")
24
+ if status == "error":
25
+ return Text(" x ", style="bold red") + Text("Error", style="red")
26
+ if status == "starting":
27
+ return Text(" ~ ", style="bold yellow") + Text("Starting", style="yellow")
28
+ return Text(f" {status}", style="dim")
29
+
30
+
31
+ def build_status_renderable(
32
+ daemon_status: dict,
33
+ provider_status: dict,
34
+ credential_status: dict,
35
+ agent_status: dict | None = None,
36
+ config: dict | None = None,
37
+ *,
38
+ console_width: int = 96,
39
+ ) -> Group:
40
+ width = tokens.content_width(console_width)
41
+ daemon_stat = daemon_status.get("status", "stopped")
42
+ status_indicator = _status_tag(daemon_stat)
43
+
44
+ info_lines = [status_indicator]
45
+ if daemon_stat == "running":
46
+ pid = daemon_status.get("pid", "?")
47
+ cwd = daemon_status.get("cwd", "?")
48
+ uptime = daemon_status.get("uptime_seconds", 0)
49
+ last_check = daemon_status.get("last_health_check")
50
+ info_lines.append(Text(f" PID: {pid}", style=tokens.MUTED))
51
+ info_lines.append(Text(f" CWD: {cwd}", style=tokens.MUTED))
52
+ if uptime:
53
+ uptime_str = (
54
+ f"{int(uptime // 3600):02d}:"
55
+ f"{int((uptime % 3600) // 60):02d}:"
56
+ f"{int(uptime % 60):02d}"
57
+ )
58
+ info_lines.append(Text(f" Uptime: {uptime_str}", style=tokens.MUTED))
59
+ if last_check:
60
+ info_lines.append(Text(f" Health: {last_check:.0f}s ago", style=tokens.MUTED))
61
+
62
+ panels: list[Panel] = [
63
+ Panel(
64
+ Group(*info_lines),
65
+ border_style="green" if daemon_stat == "running" else "red",
66
+ title="[bold]Daemon[/]",
67
+ padding=(1, 2),
68
+ width=width,
69
+ expand=False,
70
+ )
71
+ ]
72
+
73
+ if agent_status:
74
+ agent_table = Table(show_header=False, box=None, padding=(0, 2))
75
+ agent_table.add_column("Agent", style=tokens.ACCENT)
76
+ agent_table.add_column("Status")
77
+ for name, st in agent_status.items():
78
+ agent_table.add_row(f" {name}", _status_tag(st))
79
+ panels.append(
80
+ Panel(
81
+ Group(Text("Agent Status", style=tokens.TITLE), Text(""), agent_table),
82
+ border_style=tokens.ACCENT,
83
+ title="[bold]Agents[/]",
84
+ padding=(1, 2),
85
+ width=width,
86
+ expand=False,
87
+ )
88
+ )
89
+
90
+ provider_table = Table(show_header=False, box=None, padding=(0, 2))
91
+ provider_table.add_column("Provider", style=tokens.ACCENT)
92
+ provider_table.add_column("Configured")
93
+ provider_table.add_column("Status")
94
+ for name, info in provider_status.items():
95
+ configured = info.get("configured", False)
96
+ conf_tag = Text("+", style=tokens.SUCCESS) if configured else Text("x", style=tokens.ERROR)
97
+ provider_table.add_row(f" {name}", conf_tag, info.get("status", "unknown"))
98
+ panels.append(
99
+ Panel(
100
+ Group(Text("Providers", style=tokens.TITLE), Text(""), provider_table),
101
+ border_style=tokens.ACCENT,
102
+ title="[bold]Providers[/]",
103
+ padding=(1, 2),
104
+ width=width,
105
+ expand=False,
106
+ )
107
+ )
108
+
109
+ cred_table = Table(show_header=False, box=None, padding=(0, 2))
110
+ cred_table.add_column("Key", style=tokens.ACCENT)
111
+ cred_table.add_column("Configured")
112
+ cred_table.add_column("Label")
113
+ for key, info in credential_status.items():
114
+ configured = info.get("configured", False)
115
+ conf_tag = Text("+", style=tokens.SUCCESS) if configured else Text("x", style=tokens.ERROR)
116
+ cred_table.add_row(f" {key}", conf_tag, info.get("label", ""))
117
+ panels.append(
118
+ Panel(
119
+ Group(Text("Credentials", style=tokens.TITLE), Text(""), cred_table),
120
+ border_style=tokens.ACCENT,
121
+ title="[bold]Credentials[/]",
122
+ padding=(1, 2),
123
+ width=width,
124
+ expand=False,
125
+ )
126
+ )
127
+
128
+ if config:
129
+ latency = config.get("latency_targets", {})
130
+ lat_lines = [Text("Latency Targets", style=tokens.TITLE)]
131
+ for key, value in latency.items():
132
+ lat_lines.append(Text(f" {key}: {value}ms", style=tokens.MUTED))
133
+ panels.append(
134
+ Panel(Group(*lat_lines), border_style=tokens.ACCENT, padding=(1, 2), width=width, expand=False)
135
+ )
136
+
137
+ # Separate with blank lines via Group of panels + empty Text
138
+ parts: list = []
139
+ for i, p in enumerate(panels):
140
+ if i:
141
+ parts.append(Text(""))
142
+ parts.append(p)
143
+ return Group(*parts)
144
+
145
+
146
+ def render_status(
147
+ daemon_status: dict,
148
+ provider_status: dict,
149
+ credential_status: dict,
150
+ agent_status: dict = None,
151
+ config: dict = None,
152
+ *,
153
+ console: Console | None = None,
154
+ plain: bool | None = None,
155
+ sleep_fn: Callable[[float], None] = time.sleep,
156
+ ):
157
+ """Render status with a light assemble animation when interactive."""
158
+ cf = ConsoleFactory(no_color=bool(plain))
159
+ c = console or cf.create()
160
+ is_plain = cf.is_plain() if plain is None else plain
161
+
162
+ full = build_status_renderable(
163
+ daemon_status,
164
+ provider_status,
165
+ credential_status,
166
+ agent_status,
167
+ config,
168
+ console_width=c.width,
169
+ )
170
+
171
+ policy = MotionPolicy(plain=is_plain, min_dwell_ms=60, stagger_ms=20, max_artificial_ms_after_ready=400)
172
+ scene = LiveScene(c, plain=is_plain)
173
+ header = Text(f"ShellMate status", style=f"bold {tokens.ACCENT}")
174
+ try:
175
+ scene.start(header)
176
+ sleep_fn(policy.stagger_seconds())
177
+ scene.update(Group(header, Text(""), full))
178
+ sleep_fn(policy.dwell_seconds())
179
+ except Exception:
180
+ pass
181
+ scene.settle(full)
shellmate/ui/status.py ADDED
@@ -0,0 +1,3 @@
1
+ from shellmate.ui.scenes.status import render_status, build_status_renderable
2
+
3
+ __all__ = ["render_status", "build_status_renderable"]
@@ -0,0 +1,352 @@
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
@@ -0,0 +1,106 @@
1
+ shellmate/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ shellmate/adapters/__init__.py,sha256=4gnk5hxRGFfKS9NM0oHVFPczZe7wK-4pu7pVHDlhqWc,2292
3
+ shellmate/adapters/bash_adapter.sh,sha256=8n7DeBS_vy1_dwTmulY3wvsqhUEMYNy8Huz4gUh771M,312
4
+ shellmate/adapters/ble_adapter.sh,sha256=tBgsJmVDbFfIKtxwB8bx32W0lh1ybWEPxySTr09TLjk,11268
5
+ shellmate/adapters/powershell_adapter.ps1,sha256=4ngc0fFWfa46b5sOB5CPYzX5iPmMoAiect5KtPPmkzE,15782
6
+ shellmate/adapters/powershell_predictor.ps1,sha256=W3NAsqYI0gOQ-qyMAgplfgTz0VxpsWBf4MTNTHh1zWU,4136
7
+ shellmate/adapters/zsh_adapter.zsh,sha256=NBqP4d0JxkjlyAbRt95JqNe99o3YSHcT18R_nX2qeVg,342
8
+ shellmate/agents/__init__.py,sha256=L1p1AT5XqnijplQv23G4OrzyAGlGTGfVX4StsNj_6Cc,515
9
+ shellmate/agents/base.py,sha256=vfwvln6P2_qk054udxf0jGE921mMOxZJh0OjBYCH4JA,8140
10
+ shellmate/agents/chain_patterns.yaml,sha256=Qh_md7liAWdtC-Tk3AbxCGG1pz9xRcxeavVWBH66nRk,2752
11
+ shellmate/agents/command_chain.py,sha256=CW98s7Y1CtjvcOAA6D9yAS7qzLhWWq3ECs96-ST74Yk,10739
12
+ shellmate/agents/context_gatherer.py,sha256=mcdCoo6IspFqmGt4fGIgvA5-u5hoOFw9Mol1w5z9clo,5877
13
+ shellmate/agents/environment.py,sha256=BxKhIpJ_Zj2pFBO-8u-132y05-4y1oC3n8ilGRuvaXs,12510
14
+ shellmate/agents/librarian.py,sha256=SnJOHfuO-groBLKwKickiVNMEp9LUSku82E2l_CAjl0,17322
15
+ shellmate/agents/query.py,sha256=qFVVaVGupS_pd9bqSiMRct_3dnFUoHtLIChxcEuysj0,14752
16
+ shellmate/agents/repomix_backend.py,sha256=sJtYz4BVz_OjZzA88s8E-OWXUlKY020KJo0w32gxUTE,6484
17
+ shellmate/agents/watcher.py,sha256=M5KqrDmGk4WkA6zbZ_ByEq6_LU6GfJYdZbugNjRZZto,9363
18
+ shellmate/cache/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
+ shellmate/cache/semantic_cache.py,sha256=d9Je0gRFW1ASau8xemGBbbJfxk08C3QkIl_HtCIIMWI,937
20
+ shellmate/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
21
+ shellmate/cli/main.py,sha256=_gljAQLkDgYgukkyYNVjjow7d-FIOtWbFufnFhsNr7A,3815
22
+ shellmate/cli/parser.py,sha256=Lh3mo6aHwn6ISmXoFKMfROF--tSe7Rpig_3rpjTGgi0,5747
23
+ shellmate/cli/version.py,sha256=qckxTKowVHHdLYuDeVRWcIaYVKeKZEw0YwwUIm6QtNY,18
24
+ shellmate/cli/commands/__init__.py,sha256=MFJrPc6Yrhe6eFxTAJI8dnOnqUdILoh3JnBRCevVOwU,1032
25
+ shellmate/cli/commands/analyze.py,sha256=t5BxxGBrPizJvgsbrvOpsF327DxgunoN7QefaBXIc3k,270
26
+ shellmate/cli/commands/config.py,sha256=DI22jVKEwaFLqQIA3ps-ZFRz6lNML_DYBWBtyh70ISA,10168
27
+ shellmate/cli/commands/configure.py,sha256=KqHFlA_U1sW5-LhYpATErhDVXZjwV16LRcj_-DHm7sE,4179
28
+ shellmate/cli/commands/disable.py,sha256=-xqpNGERQjKetwx9wERtYrpxdqqXhblLc9cLfGjW32o,268
29
+ shellmate/cli/commands/enable.py,sha256=dNHMdAf-8_GHYoRDe6B8CaIVvAxFSBstADL1L4i38qU,1604
30
+ shellmate/cli/commands/init.py,sha256=lXX9TqnpVDfsHUqKDAzczgPVqgsoPrJWnvzYo5bsZhY,13740
31
+ shellmate/cli/commands/install.py,sha256=4DTTG7xwedETAIVydLSQcR99YHvT82zY0cTC7SLxoM4,9168
32
+ shellmate/cli/commands/migrate.py,sha256=WUjyCjWXuxYCER8IesSPXnVMdlmiYR1nUJm2kUQYC2A,5107
33
+ shellmate/cli/commands/status.py,sha256=m8QHQaKOfx1GJaFeU-6sJQeEojRYdJYF5bbWxNrKHbQ,1008
34
+ shellmate/cli/commands/uninstall.py,sha256=gZ6hyzFa22hjFOaN-OpOcijWgPMIpot1Quc9PGWi_sk,2385
35
+ shellmate/completion/__init__.py,sha256=YIa-Yd7bfwJv5GLDqMFY2nw5kzOfsNSsm5mihhVsQz0,148
36
+ shellmate/completion/bash_adapter.py,sha256=AXYpTcq5UdoPoUgkk-u4poIqw8SFVSPCYJtknbKgLhU,4829
37
+ shellmate/config/__init__.py,sha256=U3cXQSYsPuxDynOHfH5v32zVrWo9MinDXNlQSVIf5i0,900
38
+ shellmate/config/credentials.py,sha256=Nr4HXgG-a8oihSL4uGnrKfjDzE9rC67KFBn6lehdRt4,3634
39
+ shellmate/config/defaults.py,sha256=iMRxqfQndH0m36tW9SDjT_DJi77Nel8FiStaldOWX_o,2932
40
+ shellmate/config/manager.py,sha256=8XY-QEwC6CrFgJ7WnRezOHhl-bT4I7JZ1c_b3Y8Gf1Q,2801
41
+ shellmate/config/paths.py,sha256=EfeCLtTku_SZn4pw22Uks1V06-HoGQfU4KTaeMHc298,2299
42
+ shellmate/config/secrets.py,sha256=GkZnM22SC_0NpTaie0RmbuG_PcZuCciH3MzQk1DHB7Q,2649
43
+ shellmate/daemon/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
44
+ shellmate/daemon/lifecycle.py,sha256=UK6PTuIklIAuC2Iy8ycxHP7ZAtWPiKhMBiu31mw3HWs,10269
45
+ shellmate/daemon/observability.py,sha256=BQs_xob4OHYT_NWIX6E4twi-kvBPvWVT9oG8tRuQTFA,2175
46
+ shellmate/daemon/pipeline.py,sha256=x-mlr01vuqT-cXSzFUq5Fm_e1eOIn8R7NVKXcwC8qFQ,3615
47
+ shellmate/daemon/server.py,sha256=4BMe2mU7upgnf-WyMYLMhfg9RltiHiHZlFAXhItcVTA,24196
48
+ shellmate/daemon/session.py,sha256=xPijQIdoIG6ynsHaUL1sUnVLywqLYtvuQBk1WuBfX8w,4556
49
+ shellmate/daemon/startup_ui.py,sha256=eQwVmIrTRT9FJ3HVgB0o4ToWz4Mx9MbJXxMzAixT3fg,1488
50
+ shellmate/environment/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
51
+ shellmate/environment/detector.py,sha256=EMIKgaqoAcuibDzQSFRM98OpYTDl4tCzPI5uKpT8vGI,3315
52
+ shellmate/ipc/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
53
+ shellmate/ipc/client.py,sha256=4tsEyTCLa2OCfL0dQDXMkcLQeG5Y_rqi9fcKIW5twUM,6914
54
+ shellmate/ipc/ps_client.py,sha256=QpEQV93oWj3e-aOlcHtvmSBIp702H8hw3p7pHDT3pas,7458
55
+ shellmate/orchestration/__init__.py,sha256=14x2HJSxgRBrO7tUI4W6-kFlF87VymM2a0fFNF1l6Gk,168
56
+ shellmate/orchestration/analysis_graph.py,sha256=uLKzKvrIQ72Y-yukCqazA2Vb53I2T8EP5P84O07_ReY,47126
57
+ shellmate/orchestration/state.py,sha256=zNMkdOtvxroYBJ5tpj-oYWpf0OjDXzkyZCnbVymAXLk,1509
58
+ shellmate/parser/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
59
+ shellmate/parser/command_parser.py,sha256=ck3sYbrjHEYedgYjuIoDvJYHuMGPiR9ia3ltiXW5JIM,4344
60
+ shellmate/plugins/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
61
+ shellmate/plugins/base.py,sha256=Fbl-Dyx7JPE0Hj2g9XI9U7jlaGzdqxbTKBk1Q9Vkt64,1785
62
+ shellmate/plugins/loader.py,sha256=jhd6utSiyEgRzXpn8lIZFUMMv_H5fTX-XNOX0-icLm8,1338
63
+ shellmate/providers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
64
+ shellmate/providers/base.py,sha256=FJLwM1Room8BSKo1GCY-aP5wqBPYJ_4vZJldq_nOBKg,3372
65
+ shellmate/providers/groq_command_guide.py,sha256=qrvPE-KCV3mh8RzODW6rodDX3qo4N6IcdnUBzLh9yj0,4067
66
+ shellmate/providers/groq_provider.py,sha256=MSZxZkFfF-8zsniJ2Av6v6eKxy2fiRxbd3HSUMorY4E,4808
67
+ shellmate/providers/groq_supervisor.py,sha256=TVCVB0xEkexIWH9RPKIkXPi8CEmOT8OC1tD0kYfixn0,2527
68
+ shellmate/providers/openrouter_provider.py,sha256=L1_8BtOVtuCV2SFRt08x2UoIKwpGytvVcW8f1blLcIo,5745
69
+ shellmate/providers/router.py,sha256=p3Hntwz5YKnLLn76ykIpJMYRUHYpOdUlMvQVo2W3wIg,9163
70
+ shellmate/providers/validate_keys.py,sha256=TcyJWhjh2S5oCGt7GX4nR8qPrRl4v_sCE115w21qBKE,3335
71
+ shellmate/providers/xai_provider.py,sha256=T6IvRXe_uAIG00LNZCqpa4s4DSTbm3gUama1FvcaZt8,5487
72
+ shellmate/rules/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
73
+ shellmate/rules/catastrophic.yaml,sha256=prnRtEHyNCvAHwbY4lqBJCxRgflKiD3p871JlBMNSaE,2346
74
+ shellmate/rules/catastrophic_matcher.py,sha256=VLbzHjt-eUl-BsbjLpiRM4FBpKstqo6D0WHA3Lh9_vI,6212
75
+ shellmate/rules/engine.py,sha256=grDqUdCtm5K_IcyrJ6GGURoPoSu52EMVWpPzbajasOA,7068
76
+ shellmate/rules/definitions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
77
+ shellmate/rules/definitions/file_deletion.yaml,sha256=TC78X-j_VF9N3jwJ-6XFhQL81uatZPIrCOnOiojjqzI,1397
78
+ shellmate/rules/definitions/filesystem.yaml,sha256=fDKNf4MIBCKb7sXdpT7W0X8Ld_mUq5KTs386NWvG-so,1205
79
+ shellmate/rules/definitions/network_execution.yaml,sha256=34B982dCNW0qMZFXBiZrNk_Bg4ALQUzPnll_bYUHbqo,1357
80
+ shellmate/rules/definitions/permissions.yaml,sha256=4XlweCN9YcXYoAa79t5xg1Kb1JoaJLm1xtg7b1Ahp3M,1364
81
+ shellmate/rules/definitions/process_control.yaml,sha256=LAuqL_mw5O_3F1sBx_vBf2snpmkhfh9CqEdEqTBCWKk,1027
82
+ shellmate/rules/definitions/system_destruction.yaml,sha256=_iSmSwTnbWEmBcteuP6Gw2-gyXPSe3SW2oGnDLEz6X4,1719
83
+ shellmate/sanitizer/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
84
+ shellmate/sanitizer/typo_detector.py,sha256=ZBhOzB7ZSv4hctbu60tu_iBMjcBIP4TLjQ20aoCLVVQ,2957
85
+ shellmate/ui/__init__.py,sha256=i8MvY7xFXCg9WRXFJcny3iiNBUnHoNW8U82t4tjqSrs,252
86
+ shellmate/ui/analysis.py,sha256=U5WrGXSrBfR2Q_xOZ_nxi6frouTU6CrmHu-t7afLpmc,5905
87
+ shellmate/ui/annotations.py,sha256=ztf3PlPcCeainX4Hi7qQW30pJ9T1zY5aaxC6IlyHwoI,6914
88
+ shellmate/ui/banner.py,sha256=gOT8hYnO6pRqPW_tWx5KPInw9TOcByLthdmKz8uneaI,1752
89
+ shellmate/ui/progress.py,sha256=5rLtBR4VM9CUs7phTz6VJqVSIou0hGTk9YDRE-E0nvc,4020
90
+ shellmate/ui/status.py,sha256=EmsWCd5R_h-rQ9nApBBUFPhhZurfKoXY3_te4h7LIJo,134
91
+ shellmate/ui/runtime/__init__.py,sha256=G2tRJWagzRJSUYRVmyb0cBVxPr5XKwoXibVmSdXkA4Y,448
92
+ shellmate/ui/runtime/console_factory.py,sha256=eg0t5Xek599qtDEGuWSk8dvyehbef2KUjs8uhvp-fVI,1745
93
+ shellmate/ui/runtime/live_scene.py,sha256=nzEFAK-RhP5CPrJ8DdBy9PHCKtLaqSpTb1QkaCMw3e0,3674
94
+ shellmate/ui/runtime/motion_policy.py,sha256=t5tvqv72LSLbqxxQd2GARg7GvoRggcjPu6kAvvVpq6o,2254
95
+ shellmate/ui/runtime/tokens.py,sha256=J9C6jq-7uNOn7v3gQLBBtJ4sOEnJJFUoMGEMdIPf7Dk,1127
96
+ shellmate/ui/scenes/__init__.py,sha256=Pn3xBCJgxweVk9TpT7c9J1-b-9wA1eDrDj99l3IyC0s,642
97
+ shellmate/ui/scenes/enable.py,sha256=wrKSyGg_aSU3anvCAMPQOBq9E13KDCtQT3GRDbCSalA,11087
98
+ shellmate/ui/scenes/help.py,sha256=ozg_JosfzpA4cNCSWHazETiRuU9gPO_-Y04arYmgUUU,1904
99
+ shellmate/ui/scenes/stages.py,sha256=pK396uMatWxJm_XbWCJTWsvx7W6Sx0s7G95S3ec1ffQ,4048
100
+ shellmate/ui/scenes/status.py,sha256=nC-wAC8cdE3UC2QXeK9zD8fOAMpdLogHK67h74a9Nek,6427
101
+ shellmate_cli-3.0.0.dist-info/licenses/LICENSE,sha256=_eDrSi0nYhgVjipoIJoiUEdbARagURo6nVbTGBzqvD4,1079
102
+ shellmate_cli-3.0.0.dist-info/METADATA,sha256=MjD_jybwTY6cc6eqjxQAgk-x0Qp4Ocdi0QiK-D72pDg,12187
103
+ shellmate_cli-3.0.0.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
104
+ shellmate_cli-3.0.0.dist-info/entry_points.txt,sha256=lBMog9g17_P4iSXr6Akfg_QTVzswLWXUeGnuq66Wwhw,83
105
+ shellmate_cli-3.0.0.dist-info/top_level.txt,sha256=0htuoy1ZSdVSt_0l2mO-mPPvSp2veo6dbr3h0K_AmIw,10
106
+ shellmate_cli-3.0.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.2.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,3 @@
1
+ [console_scripts]
2
+ shellmate = shellmate.cli.main:main
3
+ sm = shellmate.cli.main:main
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 ShellMate Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ shellmate