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,190 @@
1
+ """Synchronous IPC client for PowerShell adapter."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import asyncio
7
+ import json
8
+ import shlex
9
+ import sys
10
+ from pathlib import Path
11
+
12
+ # Ensure project root on path when invoked as a script
13
+ _ROOT = Path(__file__).resolve().parents[1]
14
+ if str(_ROOT) not in sys.path:
15
+ sys.path.insert(0, str(_ROOT))
16
+
17
+ from shellmate.ipc.client import ShellMateClient
18
+
19
+ # Common subcommand / flag completions for offline / fast path (no daemon required)
20
+ _SUBCOMMANDS = {
21
+ "git": [
22
+ "status", "push", "pull", "commit", "checkout", "branch", "log", "diff",
23
+ "add", "merge", "rebase", "reset", "fetch", "clone", "stash", "remote",
24
+ ],
25
+ "docker": ["ps", "images", "run", "build", "exec", "stop", "rm", "compose", "logs"],
26
+ "kubectl": ["get", "apply", "delete", "describe", "logs", "exec", "port-forward"],
27
+ "npm": ["install", "run", "test", "build", "start", "ci"],
28
+ }
29
+
30
+ _FLAGS = {
31
+ "git": ["--force", "--all", "--verbose", "--help", "--amend", "--no-verify"],
32
+ "docker": ["--help", "--version", "--verbose", "--rm", "-it"],
33
+ "kubectl": ["--namespace", "--context", "--kubeconfig", "--help"],
34
+ }
35
+
36
+
37
+ def _derive_completions(partial: str, program: str, result: dict) -> list:
38
+ """Derive completions from analysis output for partial commands."""
39
+ completions: list[str] = []
40
+ risk = result.get("risk", "")
41
+ if risk in ("HIGH", "CRITICAL"):
42
+ rem = result.get("remediation") or {}
43
+ if isinstance(rem, dict):
44
+ alts = rem.get("alternatives") or []
45
+ completions.extend([a for a in alts if isinstance(a, str)][:3])
46
+ guide = result.get("command_guide") or {}
47
+ if isinstance(guide, dict):
48
+ completions.extend(
49
+ [s for s in (guide.get("suggestions") or []) if isinstance(s, str)][:2]
50
+ )
51
+ elif isinstance(guide, list):
52
+ completions.extend([s for s in guide if isinstance(s, str)][:5])
53
+ return completions[:5]
54
+
55
+
56
+ def _local_completions(partial: str) -> list[str]:
57
+ """Fast local completions for ghost-text / Tab (works without daemon)."""
58
+ partial = (partial or "").strip()
59
+ if not partial:
60
+ return []
61
+
62
+ try:
63
+ tokens = shlex.split(partial)
64
+ except ValueError:
65
+ tokens = partial.split()
66
+
67
+ if not tokens:
68
+ return []
69
+
70
+ program = tokens[0].lower()
71
+ # Trailing space => user finished a token; suggest next word from empty prefix
72
+ ends_with_space = partial.endswith(" ")
73
+
74
+ if len(tokens) == 1 and not ends_with_space:
75
+ # Completing the program name itself — leave to shell builtins
76
+ return []
77
+
78
+ # Flag completion
79
+ current = "" if ends_with_space else tokens[-1]
80
+ if current.startswith("-"):
81
+ flags = _FLAGS.get(program, [])
82
+ return [f for f in flags if f.startswith(current)][:5]
83
+
84
+ # Subcommand completion (e.g. "git pu" -> "git push")
85
+ if program in _SUBCOMMANDS:
86
+ if len(tokens) == 1 and ends_with_space:
87
+ prefix = ""
88
+ base = program + " "
89
+ elif len(tokens) == 2 and not ends_with_space:
90
+ prefix = tokens[1]
91
+ base = program + " "
92
+ else:
93
+ # Deeper args: try importing bash flag helper / return empty
94
+ prefix = current
95
+ base = " ".join(tokens[:-1]) + " " if tokens else ""
96
+ try:
97
+ from shellmate.completion.bash_adapter import get_flag_completions
98
+ flags = get_flag_completions(program, prefix if prefix.startswith("-") else "-")
99
+ if prefix.startswith("-"):
100
+ return flags[:5]
101
+ except Exception:
102
+ pass
103
+ if len(tokens) >= 2 and not ends_with_space and tokens[1] in _SUBCOMMANDS.get(program, []):
104
+ return []
105
+ # Still allow subcommand match when second token is partial
106
+ if len(tokens) == 2:
107
+ matches = [s for s in _SUBCOMMANDS[program] if s.startswith(prefix)]
108
+ return [f"{program} {m}" for m in matches][:5]
109
+ return []
110
+
111
+ matches = [s for s in _SUBCOMMANDS[program] if s.startswith(prefix)]
112
+ return [f"{base}{m}".rstrip() if base.endswith(" ") else f"{program} {m}" for m in matches][:5]
113
+
114
+ return []
115
+
116
+
117
+ class PSReadLineSyncClient:
118
+ """Blocking wrapper around async Python client for PowerShell."""
119
+
120
+ def send_command_partial_sync(self, command: str, timeout: float = 5.0) -> dict:
121
+ """Blocking call to /analyze-partial endpoint."""
122
+ loop = asyncio.new_event_loop()
123
+ asyncio.set_event_loop(loop)
124
+ try:
125
+ client = ShellMateClient()
126
+ result = loop.run_until_complete(
127
+ asyncio.wait_for(client.send_command_partial(command), timeout=timeout)
128
+ )
129
+ return result
130
+ except Exception as e:
131
+ return {"status": "error", "error": str(e)}
132
+ finally:
133
+ loop.close()
134
+
135
+ def instant_check_sync(self, command: str) -> dict:
136
+ """Local catastrophic match (no daemon required)."""
137
+ from shellmate.rules.catastrophic_matcher import match_catastrophic
138
+ return match_catastrophic(command)
139
+
140
+ def complete_sync(self, command: str, timeout: float = 0.2) -> dict:
141
+ """Return completion suggestions for ghost text / Tab.
142
+
143
+ Prefers local completions (fast). Merges daemon-derived suggestions when available.
144
+ """
145
+ local = _local_completions(command)
146
+ daemon_completions: list[str] = []
147
+ try:
148
+ result = self.send_command_partial_sync(command, timeout=timeout)
149
+ guide = result.get("command_guide") or result.get("annotation", "")
150
+ if isinstance(guide, list):
151
+ daemon_completions = [g for g in guide if isinstance(g, str)][:5]
152
+ elif result.get("status") == "ok" or result.get("annotation") or result.get("risk"):
153
+ program = ""
154
+ parsed = result.get("parsed") or {}
155
+ if isinstance(parsed, dict):
156
+ program = parsed.get("program") or ""
157
+ if not program:
158
+ try:
159
+ program = shlex.split(command)[0] if command.strip() else ""
160
+ except ValueError:
161
+ program = command.split()[0] if command.split() else ""
162
+ daemon_completions = _derive_completions(command, program, result)
163
+ except Exception:
164
+ pass
165
+
166
+ # Dedupe preserving order; local first for latency/stability
167
+ seen = set()
168
+ merged: list[str] = []
169
+ for item in local + daemon_completions:
170
+ if item and item not in seen:
171
+ seen.add(item)
172
+ merged.append(item)
173
+ return {"completions": merged[:5]}
174
+
175
+
176
+ if __name__ == "__main__":
177
+ parser = argparse.ArgumentParser(description="ShellMate PowerShell IPC client")
178
+ parser.add_argument("command", nargs="?", default="")
179
+ parser.add_argument("--complete", action="store_true", help="Return completions instead of analysis")
180
+ parser.add_argument("--instant", action="store_true", help="Local catastrophic check only")
181
+ args = parser.parse_args()
182
+
183
+ client = PSReadLineSyncClient()
184
+ if args.instant:
185
+ result = client.instant_check_sync(args.command)
186
+ elif args.complete:
187
+ result = client.complete_sync(args.command)
188
+ else:
189
+ result = client.send_command_partial_sync(args.command)
190
+ print(json.dumps(result))
@@ -0,0 +1,3 @@
1
+ from shellmate.orchestration.analysis_graph import AnalysisGraph, get_analysis_graph, AnalysisState
2
+
3
+ __all__ = ["AnalysisGraph", "get_analysis_graph", "AnalysisState"]