k-cli-for-devs 1.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.
- k_cli/__init__.py +77 -0
- k_cli/agents/__init__.py +0 -0
- k_cli/agents/adversarial_swarm.py +338 -0
- k_cli/agents/agent_core.py +255 -0
- k_cli/agents/background_daemon.py +141 -0
- k_cli/agents/orchestrator.py +376 -0
- k_cli/agents/persona.py +649 -0
- k_cli/agents/scaffold_engine.py +121 -0
- k_cli/agents/strands_agent.py +832 -0
- k_cli/agents/subagents.py +1496 -0
- k_cli/cli.py +3297 -0
- k_cli/core/__init__.py +0 -0
- k_cli/core/airgap.py +95 -0
- k_cli/core/credentials.py +548 -0
- k_cli/core/intent_sensor.py +177 -0
- k_cli/core/llm_driver.py +1028 -0
- k_cli/core/model_manager.py +1109 -0
- k_cli/core/models_hub.py +913 -0
- k_cli/core/prompting.py +41 -0
- k_cli/core/sdk.py +322 -0
- k_cli/core/session.py +826 -0
- k_cli/core/smart_router.py +230 -0
- k_cli/core/storage_manager.py +176 -0
- k_cli/core/viewport_engine.py +117 -0
- k_cli/demo/demo_runner.py +579 -0
- k_cli/git/__init__.py +0 -0
- k_cli/git/ai_bisect.py +208 -0
- k_cli/git/conflict_resolver.py +1039 -0
- k_cli/git/git_guard.py +417 -0
- k_cli/git/patcher.py +1175 -0
- k_cli/git/repo_map.py +1780 -0
- k_cli/git/smart_git.py +928 -0
- k_cli/git/verifier.py +969 -0
- k_cli/github/__init__.py +0 -0
- k_cli/github/dedup_engine.py +787 -0
- k_cli/github/github_client.py +1702 -0
- k_cli/github/github_engine.py +641 -0
- k_cli/github/local_hub.py +209 -0
- k_cli/github/pr_watcher.py +129 -0
- k_cli/github/trending.py +205 -0
- k_cli/tools/__init__.py +0 -0
- k_cli/tools/audit.py +79 -0
- k_cli/tools/chaos_immunity.py +377 -0
- k_cli/tools/codebase_qa.py +106 -0
- k_cli/tools/command_runner.py +256 -0
- k_cli/tools/diagram_generator.py +547 -0
- k_cli/tools/doc_retriever.py +1332 -0
- k_cli/tools/feature.py +105 -0
- k_cli/tools/ghost_daemon.py +122 -0
- k_cli/tools/incident_triage.py +1365 -0
- k_cli/tools/mcp_client.py +1846 -0
- k_cli/tools/repo_gardener.py +142 -0
- k_cli/tools/rules.py +109 -0
- k_cli/tools/security.py +52 -0
- k_cli/tools/security_healer.py +999 -0
- k_cli/tools/synapse_graph.py +155 -0
- k_cli/tui/__init__.py +0 -0
- k_cli/tui/diff_viewer.py +223 -0
- k_cli/tui/tui.py +1145 -0
- k_cli/tui/tui_animations.py +648 -0
- k_cli/tui/tui_app.py +2788 -0
- k_cli/ui/__init__.py +10 -0
- k_cli/ui/simple_repl.py +315 -0
- k_cli/web/__init__.py +7 -0
- k_cli/web/server.py +624 -0
- k_cli/web/static/app.js +830 -0
- k_cli/web/static/index.html +495 -0
- k_cli/web/static/monitor.html +189 -0
- k_cli/web/static/style.css +838 -0
- k_cli_for_devs-1.0.0.dist-info/METADATA +461 -0
- k_cli_for_devs-1.0.0.dist-info/RECORD +75 -0
- k_cli_for_devs-1.0.0.dist-info/WHEEL +5 -0
- k_cli_for_devs-1.0.0.dist-info/entry_points.txt +2 -0
- k_cli_for_devs-1.0.0.dist-info/licenses/LICENSE +21 -0
- k_cli_for_devs-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
"""
|
|
2
|
+
command_runner.py - Local Machine Command Execution Engine for K-CLI
|
|
3
|
+
Project Bankai v1.0.0 — Built for AWS "Agents for Humans" Hackathon
|
|
4
|
+
Empowers K-CLI with Google Antigravity-grade local shell execution capabilities.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import asyncio
|
|
10
|
+
import logging
|
|
11
|
+
import os
|
|
12
|
+
import shlex
|
|
13
|
+
import subprocess
|
|
14
|
+
import sys
|
|
15
|
+
import time
|
|
16
|
+
from dataclasses import dataclass, field
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
from typing import Any, AsyncIterator, Dict, List, Optional
|
|
19
|
+
|
|
20
|
+
logger = logging.getLogger("k_cli.tools.command_runner")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dataclass
|
|
24
|
+
class CommandExecutionResult:
|
|
25
|
+
"""Represents the outcome of a locally executed command."""
|
|
26
|
+
command: str
|
|
27
|
+
exit_code: int
|
|
28
|
+
stdout: str
|
|
29
|
+
stderr: str
|
|
30
|
+
duration_sec: float
|
|
31
|
+
cwd: str
|
|
32
|
+
success: bool = field(init=False)
|
|
33
|
+
|
|
34
|
+
def __post_init__(self):
|
|
35
|
+
self.success = (self.exit_code == 0)
|
|
36
|
+
|
|
37
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
38
|
+
return {
|
|
39
|
+
"command": self.command,
|
|
40
|
+
"success": self.success,
|
|
41
|
+
"exit_code": self.exit_code,
|
|
42
|
+
"stdout": self.stdout,
|
|
43
|
+
"stderr": self.stderr,
|
|
44
|
+
"duration_sec": round(self.duration_sec, 3),
|
|
45
|
+
"cwd": self.cwd,
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
def summary(self) -> str:
|
|
49
|
+
status_symbol = "✔" if self.success else "✖"
|
|
50
|
+
lines = [
|
|
51
|
+
f"{status_symbol} Command: `{self.command}` (Exit Code: {self.exit_code}, Duration: {self.duration_sec:.2f}s)",
|
|
52
|
+
]
|
|
53
|
+
if self.stdout.strip():
|
|
54
|
+
lines.append(f"--- STDOUT ---\n{self.stdout.strip()}")
|
|
55
|
+
if self.stderr.strip():
|
|
56
|
+
lines.append(f"--- STDERR ---\n{self.stderr.strip()}")
|
|
57
|
+
return "\n".join(lines)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class LocalCommandExecutor:
|
|
61
|
+
"""
|
|
62
|
+
Google Antigravity-grade local shell command executor.
|
|
63
|
+
Executes commands on the developer's local machine with working directory management,
|
|
64
|
+
stdout/stderr capture, and timeout safeguards.
|
|
65
|
+
"""
|
|
66
|
+
|
|
67
|
+
def __init__(self, default_cwd: Optional[str] = None):
|
|
68
|
+
self.default_cwd = str(Path(default_cwd or os.getcwd()).resolve())
|
|
69
|
+
|
|
70
|
+
def _prepare_env(self, env: Optional[Dict[str, str]] = None) -> Dict[str, str]:
|
|
71
|
+
exec_env = os.environ.copy()
|
|
72
|
+
venv_bin = os.path.join(sys.prefix, "bin")
|
|
73
|
+
if os.path.exists(venv_bin):
|
|
74
|
+
current_path = exec_env.get("PATH", "")
|
|
75
|
+
if venv_bin not in current_path:
|
|
76
|
+
exec_env["PATH"] = f"{venv_bin}:{current_path}"
|
|
77
|
+
exec_env["PYTHONPATH"] = "/home/k/K-Cli-for-Devs"
|
|
78
|
+
if env:
|
|
79
|
+
exec_env.update(env)
|
|
80
|
+
return exec_env
|
|
81
|
+
|
|
82
|
+
def execute(
|
|
83
|
+
self,
|
|
84
|
+
command: str,
|
|
85
|
+
cwd: Optional[str] = None,
|
|
86
|
+
timeout: int = 60,
|
|
87
|
+
env: Optional[Dict[str, str]] = None,
|
|
88
|
+
) -> CommandExecutionResult:
|
|
89
|
+
"""
|
|
90
|
+
Executes a shell command synchronously on the local machine.
|
|
91
|
+
|
|
92
|
+
Args:
|
|
93
|
+
command: The command line string to execute.
|
|
94
|
+
cwd: Working directory (defaults to executor default_cwd).
|
|
95
|
+
timeout: Maximum execution time in seconds (default 60).
|
|
96
|
+
env: Custom environment variables dict.
|
|
97
|
+
|
|
98
|
+
Returns:
|
|
99
|
+
CommandExecutionResult containing exit code, stdout, stderr, and duration.
|
|
100
|
+
"""
|
|
101
|
+
target_cwd = str(Path(cwd or self.default_cwd).resolve())
|
|
102
|
+
exec_env = self._prepare_env(env)
|
|
103
|
+
|
|
104
|
+
start_time = time.time()
|
|
105
|
+
logger.info(f"Executing local command: {command} in {target_cwd}")
|
|
106
|
+
|
|
107
|
+
try:
|
|
108
|
+
# Use bash on POSIX systems or cmd on Windows
|
|
109
|
+
shell_executable = "/bin/bash" if os.name == "posix" and os.path.exists("/bin/bash") else None
|
|
110
|
+
proc = subprocess.run(
|
|
111
|
+
command,
|
|
112
|
+
shell=True,
|
|
113
|
+
cwd=target_cwd,
|
|
114
|
+
env=exec_env,
|
|
115
|
+
executable=shell_executable,
|
|
116
|
+
stdout=subprocess.PIPE,
|
|
117
|
+
stderr=subprocess.PIPE,
|
|
118
|
+
text=True,
|
|
119
|
+
errors="replace",
|
|
120
|
+
timeout=timeout,
|
|
121
|
+
)
|
|
122
|
+
duration = time.time() - start_time
|
|
123
|
+
return CommandExecutionResult(
|
|
124
|
+
command=command,
|
|
125
|
+
exit_code=proc.returncode,
|
|
126
|
+
stdout=proc.stdout or "",
|
|
127
|
+
stderr=proc.stderr or "",
|
|
128
|
+
duration_sec=duration,
|
|
129
|
+
cwd=target_cwd,
|
|
130
|
+
)
|
|
131
|
+
except subprocess.TimeoutExpired as te:
|
|
132
|
+
duration = time.time() - start_time
|
|
133
|
+
stdout = te.stdout if isinstance(te.stdout, str) else (te.stdout.decode("utf-8", "replace") if te.stdout else "")
|
|
134
|
+
stderr = te.stderr if isinstance(te.stderr, str) else (te.stderr.decode("utf-8", "replace") if te.stderr else "")
|
|
135
|
+
return CommandExecutionResult(
|
|
136
|
+
command=command,
|
|
137
|
+
exit_code=-1,
|
|
138
|
+
stdout=stdout,
|
|
139
|
+
stderr=f"{stderr}\n[Error] Command timed out after {timeout} seconds.",
|
|
140
|
+
duration_sec=duration,
|
|
141
|
+
cwd=target_cwd,
|
|
142
|
+
)
|
|
143
|
+
except Exception as exc:
|
|
144
|
+
duration = time.time() - start_time
|
|
145
|
+
return CommandExecutionResult(
|
|
146
|
+
command=command,
|
|
147
|
+
exit_code=1,
|
|
148
|
+
stdout="",
|
|
149
|
+
stderr=f"[Error] Failed to launch command: {exc}",
|
|
150
|
+
duration_sec=duration,
|
|
151
|
+
cwd=target_cwd,
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
async def execute_async(
|
|
155
|
+
self,
|
|
156
|
+
command: str,
|
|
157
|
+
cwd: Optional[str] = None,
|
|
158
|
+
timeout: int = 60,
|
|
159
|
+
env: Optional[Dict[str, str]] = None,
|
|
160
|
+
) -> CommandExecutionResult:
|
|
161
|
+
"""
|
|
162
|
+
Asynchronously executes a shell command using asyncio.subprocess.
|
|
163
|
+
"""
|
|
164
|
+
target_cwd = str(Path(cwd or self.default_cwd).resolve())
|
|
165
|
+
exec_env = self._prepare_env(env)
|
|
166
|
+
|
|
167
|
+
start_time = time.time()
|
|
168
|
+
logger.info(f"Executing async local command: {command} in {target_cwd}")
|
|
169
|
+
|
|
170
|
+
try:
|
|
171
|
+
proc = await asyncio.create_subprocess_shell(
|
|
172
|
+
command,
|
|
173
|
+
cwd=target_cwd,
|
|
174
|
+
env=exec_env,
|
|
175
|
+
stdout=asyncio.subprocess.PIPE,
|
|
176
|
+
stderr=asyncio.subprocess.PIPE,
|
|
177
|
+
executable="/bin/bash" if os.name == "posix" and os.path.exists("/bin/bash") else None,
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
try:
|
|
181
|
+
stdout_data, stderr_data = await asyncio.wait_for(
|
|
182
|
+
proc.communicate(),
|
|
183
|
+
timeout=float(timeout)
|
|
184
|
+
)
|
|
185
|
+
duration = time.time() - start_time
|
|
186
|
+
stdout_str = stdout_data.decode("utf-8", "replace") if stdout_data else ""
|
|
187
|
+
stderr_str = stderr_data.decode("utf-8", "replace") if stderr_data else ""
|
|
188
|
+
return CommandExecutionResult(
|
|
189
|
+
command=command,
|
|
190
|
+
exit_code=proc.returncode if proc.returncode is not None else 0,
|
|
191
|
+
stdout=stdout_str,
|
|
192
|
+
stderr=stderr_str,
|
|
193
|
+
duration_sec=duration,
|
|
194
|
+
cwd=target_cwd,
|
|
195
|
+
)
|
|
196
|
+
except asyncio.TimeoutError:
|
|
197
|
+
duration = time.time() - start_time
|
|
198
|
+
try:
|
|
199
|
+
proc.kill()
|
|
200
|
+
await proc.wait()
|
|
201
|
+
except Exception:
|
|
202
|
+
pass
|
|
203
|
+
return CommandExecutionResult(
|
|
204
|
+
command=command,
|
|
205
|
+
exit_code=-1,
|
|
206
|
+
stdout="",
|
|
207
|
+
stderr=f"[Error] Command timed out after {timeout} seconds.",
|
|
208
|
+
duration_sec=duration,
|
|
209
|
+
cwd=target_cwd,
|
|
210
|
+
)
|
|
211
|
+
except Exception as exc:
|
|
212
|
+
duration = time.time() - start_time
|
|
213
|
+
return CommandExecutionResult(
|
|
214
|
+
command=command,
|
|
215
|
+
exit_code=1,
|
|
216
|
+
stdout="",
|
|
217
|
+
stderr=f"[Error] Failed to execute async command: {exc}",
|
|
218
|
+
duration_sec=duration,
|
|
219
|
+
cwd=target_cwd,
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
async def stream_output(
|
|
223
|
+
self,
|
|
224
|
+
command: str,
|
|
225
|
+
cwd: Optional[str] = None,
|
|
226
|
+
env: Optional[Dict[str, str]] = None,
|
|
227
|
+
) -> AsyncIterator[str]:
|
|
228
|
+
"""
|
|
229
|
+
Streams command stdout/stderr line-by-line as it executes.
|
|
230
|
+
"""
|
|
231
|
+
target_cwd = str(Path(cwd or self.default_cwd).resolve())
|
|
232
|
+
exec_env = os.environ.copy()
|
|
233
|
+
if env:
|
|
234
|
+
exec_env.update(env)
|
|
235
|
+
|
|
236
|
+
proc = await asyncio.create_subprocess_shell(
|
|
237
|
+
command,
|
|
238
|
+
cwd=target_cwd,
|
|
239
|
+
env=exec_env,
|
|
240
|
+
stdout=asyncio.subprocess.PIPE,
|
|
241
|
+
stderr=asyncio.subprocess.STDOUT,
|
|
242
|
+
executable="/bin/bash" if os.name == "posix" and os.path.exists("/bin/bash") else None,
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
if proc.stdout:
|
|
246
|
+
while True:
|
|
247
|
+
line = await proc.stdout.readline()
|
|
248
|
+
if not line:
|
|
249
|
+
break
|
|
250
|
+
yield line.decode("utf-8", "replace")
|
|
251
|
+
|
|
252
|
+
await proc.wait()
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
# Global default executor singleton
|
|
256
|
+
global_command_executor = LocalCommandExecutor()
|