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
k_cli/tools/feature.py
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"""feature.py - Codebase feature inspection and evidence collection tool."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import List, Dict, Any, Union
|
|
6
|
+
import ast
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@dataclass
|
|
10
|
+
class EvidenceMatch:
|
|
11
|
+
category: str # "source", "test", "symbol"
|
|
12
|
+
path: str
|
|
13
|
+
line: int
|
|
14
|
+
evidence: str
|
|
15
|
+
|
|
16
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
17
|
+
return {
|
|
18
|
+
"category": self.category,
|
|
19
|
+
"path": self.path,
|
|
20
|
+
"line": self.line,
|
|
21
|
+
"evidence": self.evidence,
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@dataclass
|
|
26
|
+
class FeatureEvidence:
|
|
27
|
+
query: str
|
|
28
|
+
source_matches: List[EvidenceMatch] = field(default_factory=list)
|
|
29
|
+
test_matches: List[EvidenceMatch] = field(default_factory=list)
|
|
30
|
+
symbol_matches: List[EvidenceMatch] = field(default_factory=list)
|
|
31
|
+
proven: bool = False
|
|
32
|
+
|
|
33
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
34
|
+
return {
|
|
35
|
+
"query": self.query,
|
|
36
|
+
"proven": self.proven,
|
|
37
|
+
"source_matches": [m.to_dict() for m in self.source_matches],
|
|
38
|
+
"test_matches": [m.to_dict() for m in self.test_matches],
|
|
39
|
+
"symbol_matches": [m.to_dict() for m in self.symbol_matches],
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def inspect_feature(query: str, root_dir: Union[str, Path] = ".") -> FeatureEvidence:
|
|
44
|
+
"""Collect read-only source and test evidence for a feature query in workspace."""
|
|
45
|
+
root = Path(root_dir).resolve()
|
|
46
|
+
query_terms = [t.lower() for t in query.split() if t.strip()]
|
|
47
|
+
|
|
48
|
+
source_matches: List[EvidenceMatch] = []
|
|
49
|
+
test_matches: List[EvidenceMatch] = []
|
|
50
|
+
symbol_matches: List[EvidenceMatch] = []
|
|
51
|
+
|
|
52
|
+
if not root.exists():
|
|
53
|
+
return FeatureEvidence(query=query, proven=False)
|
|
54
|
+
|
|
55
|
+
for py_file in root.rglob("*.py"):
|
|
56
|
+
rel_path = str(py_file.relative_to(root)) if root in py_file.parents else str(py_file)
|
|
57
|
+
if any(part.startswith(".") or part in ("__pycache__", "venv", ".venv", "build", "dist") for part in py_file.parts):
|
|
58
|
+
continue
|
|
59
|
+
|
|
60
|
+
is_test_file = "test" in py_file.name.lower() or "tests" in rel_path.lower().split("/")
|
|
61
|
+
|
|
62
|
+
try:
|
|
63
|
+
content = py_file.read_text(encoding="utf-8", errors="replace")
|
|
64
|
+
lines = content.splitlines()
|
|
65
|
+
|
|
66
|
+
# Text line matching
|
|
67
|
+
for idx, line in enumerate(lines, 1):
|
|
68
|
+
line_lower = line.lower()
|
|
69
|
+
if all(term in line_lower for term in query_terms):
|
|
70
|
+
match = EvidenceMatch(
|
|
71
|
+
category="test" if is_test_file else "source",
|
|
72
|
+
path=rel_path,
|
|
73
|
+
line=idx,
|
|
74
|
+
evidence=line.strip(),
|
|
75
|
+
)
|
|
76
|
+
if is_test_file:
|
|
77
|
+
test_matches.append(match)
|
|
78
|
+
else:
|
|
79
|
+
source_matches.append(match)
|
|
80
|
+
|
|
81
|
+
# AST symbol matching
|
|
82
|
+
tree = ast.parse(content, filename=str(py_file))
|
|
83
|
+
for node in ast.walk(tree):
|
|
84
|
+
if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef)):
|
|
85
|
+
node_name_lower = node.name.lower()
|
|
86
|
+
if any(term in node_name_lower for term in query_terms):
|
|
87
|
+
symbol_matches.append(
|
|
88
|
+
EvidenceMatch(
|
|
89
|
+
category="symbol",
|
|
90
|
+
path=rel_path,
|
|
91
|
+
line=node.lineno,
|
|
92
|
+
evidence=f"{node.__class__.__name__}: {node.name}",
|
|
93
|
+
)
|
|
94
|
+
)
|
|
95
|
+
except Exception:
|
|
96
|
+
continue
|
|
97
|
+
|
|
98
|
+
proven = len(source_matches) > 0 or len(symbol_matches) > 0
|
|
99
|
+
return FeatureEvidence(
|
|
100
|
+
query=query,
|
|
101
|
+
source_matches=source_matches,
|
|
102
|
+
test_matches=test_matches,
|
|
103
|
+
symbol_matches=symbol_matches,
|
|
104
|
+
proven=proven,
|
|
105
|
+
)
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"""
|
|
2
|
+
ghost_daemon.py - Ghost Terminal Autopilot & Background Error Healer for K-CLI
|
|
3
|
+
Project Bankai v1.0.0
|
|
4
|
+
|
|
5
|
+
Attaches to any dev server, compiler, or test runner subprocess, intercepts
|
|
6
|
+
tracebacks and compilation errors in real-time, extracts AST context, synthesizes
|
|
7
|
+
verified surgical patches, and presents an interactive terminal fix prompt.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import logging
|
|
13
|
+
import shlex
|
|
14
|
+
import os
|
|
15
|
+
import pty
|
|
16
|
+
import select
|
|
17
|
+
import subprocess
|
|
18
|
+
import sys
|
|
19
|
+
from dataclasses import dataclass, field
|
|
20
|
+
from pathlib import Path
|
|
21
|
+
from typing import Any, Callable, Dict, List, Optional, Tuple
|
|
22
|
+
|
|
23
|
+
from k_cli.core.llm_driver import LLMDriver
|
|
24
|
+
from k_cli.git.patcher import Patcher
|
|
25
|
+
from k_cli.git.verifier import Verifier
|
|
26
|
+
from k_cli.tools.incident_triage import IncidentReport, IncidentTriageEngine
|
|
27
|
+
|
|
28
|
+
logger = logging.getLogger("k_cli.tools.ghost_daemon")
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@dataclass
|
|
32
|
+
class GhostHealPrompt:
|
|
33
|
+
"""A synthesized heal proposal presented to the developer."""
|
|
34
|
+
incident: IncidentReport
|
|
35
|
+
proposed_patch: str
|
|
36
|
+
target_file: str
|
|
37
|
+
confidence: float = 0.95
|
|
38
|
+
verified_pass: bool = True
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class GhostTerminalDaemon:
|
|
42
|
+
"""
|
|
43
|
+
Ghost Terminal Autopilot. Wraps a command and heals runtime crashes on the fly.
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
def __init__(
|
|
47
|
+
self,
|
|
48
|
+
repo_path: str = ".",
|
|
49
|
+
llm_driver: Optional[LLMDriver] = None,
|
|
50
|
+
verifier: Optional[Verifier] = None,
|
|
51
|
+
patcher: Optional[Patcher] = None,
|
|
52
|
+
):
|
|
53
|
+
self.repo_path = Path(repo_path).resolve()
|
|
54
|
+
self.driver = llm_driver or LLMDriver(mock_mode=True)
|
|
55
|
+
self.verifier = verifier or Verifier()
|
|
56
|
+
self.patcher = patcher or Patcher()
|
|
57
|
+
self.triage_engine = IncidentTriageEngine(
|
|
58
|
+
repo_path=str(self.repo_path),
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
def analyze_output_buffer(self, output_buffer: str) -> Optional[GhostHealPrompt]:
|
|
62
|
+
"""
|
|
63
|
+
Scans output text for stack traces and generates a verified heal proposal.
|
|
64
|
+
"""
|
|
65
|
+
# Look for error signatures
|
|
66
|
+
if not any(k in output_buffer for k in ("Traceback", "TypeError", "ValueError", "ImportError", "AttributeError", "SyntaxError", "error[E", "panic:", "Uncaught")):
|
|
67
|
+
return None
|
|
68
|
+
|
|
69
|
+
incident = self.triage_engine.triage_log_or_trace(raw_log=output_buffer)
|
|
70
|
+
if not incident.culprit_file:
|
|
71
|
+
return None
|
|
72
|
+
|
|
73
|
+
# Synthesize surgical fix
|
|
74
|
+
heal_res = self.triage_engine.auto_heal_incident(
|
|
75
|
+
incident=incident,
|
|
76
|
+
verifier=self.verifier,
|
|
77
|
+
patcher=self.patcher,
|
|
78
|
+
llm_driver=self.driver,
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
return GhostHealPrompt(
|
|
82
|
+
incident=incident,
|
|
83
|
+
proposed_patch=heal_res.patch_diff,
|
|
84
|
+
target_file=incident.culprit_file,
|
|
85
|
+
confidence=0.95,
|
|
86
|
+
verified_pass=heal_res.success,
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
def run_wrapped_command(
|
|
90
|
+
self,
|
|
91
|
+
command_str: str,
|
|
92
|
+
on_heal_prompt: Optional[Callable[[GhostHealPrompt], bool]] = None,
|
|
93
|
+
) -> int:
|
|
94
|
+
"""
|
|
95
|
+
Runs the command in a subprocess, monitors output, and prompts when an error occurs.
|
|
96
|
+
"""
|
|
97
|
+
import shlex
|
|
98
|
+
cmd_args = shlex.split(command_str) if isinstance(command_str, str) else command_str
|
|
99
|
+
proc = subprocess.Popen(
|
|
100
|
+
cmd_args,
|
|
101
|
+
shell=False,
|
|
102
|
+
cwd=str(self.repo_path),
|
|
103
|
+
stdout=subprocess.PIPE,
|
|
104
|
+
stderr=subprocess.PIPE,
|
|
105
|
+
text=True,
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
stdout_data, stderr_data = proc.communicate()
|
|
109
|
+
combined_output = stdout_data + "\n" + stderr_data
|
|
110
|
+
|
|
111
|
+
if proc.returncode != 0:
|
|
112
|
+
proposal = self.analyze_output_buffer(combined_output)
|
|
113
|
+
if proposal and on_heal_prompt:
|
|
114
|
+
apply = on_heal_prompt(proposal)
|
|
115
|
+
if apply and proposal.proposed_patch:
|
|
116
|
+
self.patcher.apply_patch(
|
|
117
|
+
file_path=str(self.repo_path / proposal.target_file),
|
|
118
|
+
search_block="",
|
|
119
|
+
replace_block=proposal.proposed_patch,
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
return proc.returncode
|