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/git/ai_bisect.py
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
"""
|
|
2
|
+
ai_bisect.py - AI-Powered Git Bisect & Regression Hunter for K-CLI
|
|
3
|
+
Project Bankai v1.0.0
|
|
4
|
+
|
|
5
|
+
Automates binary git search (`git bisect`) with an AI oracle and local test runner
|
|
6
|
+
to pinpoint the exact regression-introducing commit, explain the root cause,
|
|
7
|
+
and propose an AST-verified fix.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import logging
|
|
13
|
+
import shlex
|
|
14
|
+
import subprocess
|
|
15
|
+
from dataclasses import dataclass, field
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
from typing import Any, Callable, Dict, List, Optional
|
|
18
|
+
|
|
19
|
+
from k_cli.core.llm_driver import LLMDriver
|
|
20
|
+
from k_cli.git.patcher import Patcher
|
|
21
|
+
from k_cli.git.verifier import Verifier
|
|
22
|
+
|
|
23
|
+
logger = logging.getLogger("k_cli.git.ai_bisect")
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@dataclass
|
|
27
|
+
class BisectStep:
|
|
28
|
+
"""A single step evaluated during bisect."""
|
|
29
|
+
commit_sha: str
|
|
30
|
+
commit_msg: str
|
|
31
|
+
passed: bool
|
|
32
|
+
output_log: str = ""
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@dataclass
|
|
36
|
+
class BisectResult:
|
|
37
|
+
"""Final result of the AI Bisect run."""
|
|
38
|
+
culprit_sha: Optional[str]
|
|
39
|
+
culprit_author: str = ""
|
|
40
|
+
culprit_date: str = ""
|
|
41
|
+
culprit_message: str = ""
|
|
42
|
+
root_cause_explanation: str = ""
|
|
43
|
+
proposed_fix_diff: str = ""
|
|
44
|
+
steps: List[BisectStep] = field(default_factory=list)
|
|
45
|
+
success: bool = False
|
|
46
|
+
total_commits_searched: int = 0
|
|
47
|
+
|
|
48
|
+
def render_markdown(self) -> str:
|
|
49
|
+
"""Render bisect summary as Markdown."""
|
|
50
|
+
lines = [
|
|
51
|
+
"# 🎯 K-CLI AI Bisect Root-Cause Report",
|
|
52
|
+
f"**Culprit Commit**: `{self.culprit_sha or 'Not Found'}`",
|
|
53
|
+
f"**Author**: {self.culprit_author} | **Date**: {self.culprit_date}",
|
|
54
|
+
f"**Commit Message**: {self.culprit_message}",
|
|
55
|
+
f"**Commits Searched**: {self.total_commits_searched} in {len(self.steps)} steps",
|
|
56
|
+
"",
|
|
57
|
+
"## 🧠 Root Cause Explanation",
|
|
58
|
+
self.root_cause_explanation or "No explanation generated.",
|
|
59
|
+
"",
|
|
60
|
+
]
|
|
61
|
+
if self.proposed_fix_diff:
|
|
62
|
+
lines.extend([
|
|
63
|
+
"## 🛠️ Proposed Surgical Fix",
|
|
64
|
+
"```diff",
|
|
65
|
+
self.proposed_fix_diff,
|
|
66
|
+
"```",
|
|
67
|
+
])
|
|
68
|
+
return "\n".join(lines)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class AIBisectEngine:
|
|
72
|
+
"""
|
|
73
|
+
Orchestrates git bisect runs and AI root-cause analysis.
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
def __init__(
|
|
77
|
+
self,
|
|
78
|
+
repo_path: str = ".",
|
|
79
|
+
llm_driver: Optional[LLMDriver] = None,
|
|
80
|
+
verifier: Optional[Verifier] = None,
|
|
81
|
+
patcher: Optional[Patcher] = None,
|
|
82
|
+
):
|
|
83
|
+
self.repo_path = Path(repo_path).resolve()
|
|
84
|
+
self.driver = llm_driver or LLMDriver(mock_mode=True)
|
|
85
|
+
self.verifier = verifier or Verifier()
|
|
86
|
+
self.patcher = patcher or Patcher()
|
|
87
|
+
|
|
88
|
+
def run_command(self, cmd: List[str]) -> subprocess.CompletedProcess:
|
|
89
|
+
"""Runs a git command inside workspace."""
|
|
90
|
+
return subprocess.run(
|
|
91
|
+
cmd,
|
|
92
|
+
cwd=str(self.repo_path),
|
|
93
|
+
capture_output=True,
|
|
94
|
+
text=True,
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
def run_bisect(
|
|
98
|
+
self,
|
|
99
|
+
test_command: str = "pytest tests/ -q",
|
|
100
|
+
good_commit: str = "HEAD~5",
|
|
101
|
+
bad_commit: str = "HEAD",
|
|
102
|
+
oracle_prompt: Optional[str] = None,
|
|
103
|
+
) -> BisectResult:
|
|
104
|
+
"""
|
|
105
|
+
Executes git bisect between good_commit and bad_commit using test_command or AI oracle.
|
|
106
|
+
"""
|
|
107
|
+
# 1. Reset any existing bisect
|
|
108
|
+
self.run_command(["git", "bisect", "reset"])
|
|
109
|
+
|
|
110
|
+
# 2. Get commit log between range
|
|
111
|
+
log_res = self.run_command(["git", "log", "--oneline", f"{good_commit}..{bad_commit}"])
|
|
112
|
+
commits = log_res.stdout.strip().splitlines()
|
|
113
|
+
total_commits = len(commits)
|
|
114
|
+
|
|
115
|
+
# 3. Start bisect
|
|
116
|
+
self.run_command(["git", "bisect", "start"])
|
|
117
|
+
self.run_command(["git", "bisect", "bad", bad_commit])
|
|
118
|
+
self.run_command(["git", "bisect", "good", good_commit])
|
|
119
|
+
|
|
120
|
+
steps: List[BisectStep] = []
|
|
121
|
+
culprit_sha = None
|
|
122
|
+
|
|
123
|
+
# Loop bisect steps (max 15 binary search steps)
|
|
124
|
+
for _ in range(15):
|
|
125
|
+
head_res = self.run_command(["git", "rev-parse", "HEAD"])
|
|
126
|
+
current_sha = head_res.stdout.strip()
|
|
127
|
+
msg_res = self.run_command(["git", "log", "-1", "--pretty=%B", current_sha])
|
|
128
|
+
current_msg = msg_res.stdout.strip()
|
|
129
|
+
|
|
130
|
+
# Run verification test
|
|
131
|
+
import shlex
|
|
132
|
+
cmd_args = shlex.split(test_command) if isinstance(test_command, str) else test_command
|
|
133
|
+
t_res = subprocess.run(
|
|
134
|
+
cmd_args,
|
|
135
|
+
shell=False,
|
|
136
|
+
cwd=str(self.repo_path),
|
|
137
|
+
capture_output=True,
|
|
138
|
+
text=True,
|
|
139
|
+
)
|
|
140
|
+
passed = (t_res.returncode == 0)
|
|
141
|
+
|
|
142
|
+
steps.append(BisectStep(
|
|
143
|
+
commit_sha=current_sha,
|
|
144
|
+
commit_msg=current_msg,
|
|
145
|
+
passed=passed,
|
|
146
|
+
output_log=t_res.stdout[:500] if passed else t_res.stderr[:500],
|
|
147
|
+
))
|
|
148
|
+
|
|
149
|
+
if passed:
|
|
150
|
+
bisect_out = self.run_command(["git", "bisect", "good"])
|
|
151
|
+
else:
|
|
152
|
+
bisect_out = self.run_command(["git", "bisect", "bad"])
|
|
153
|
+
|
|
154
|
+
out_text = bisect_out.stdout + bisect_out.stderr
|
|
155
|
+
if "is the first bad commit" in out_text:
|
|
156
|
+
culprit_sha = current_sha
|
|
157
|
+
break
|
|
158
|
+
elif "bisecting" not in out_text.lower():
|
|
159
|
+
break
|
|
160
|
+
|
|
161
|
+
# Fallback if binary search settled
|
|
162
|
+
if not culprit_sha and steps:
|
|
163
|
+
for s in reversed(steps):
|
|
164
|
+
if not s.passed:
|
|
165
|
+
culprit_sha = s.commit_sha
|
|
166
|
+
break
|
|
167
|
+
|
|
168
|
+
# Reset bisect
|
|
169
|
+
self.run_command(["git", "bisect", "reset"])
|
|
170
|
+
|
|
171
|
+
if not culprit_sha:
|
|
172
|
+
return BisectResult(
|
|
173
|
+
culprit_sha=None,
|
|
174
|
+
success=False,
|
|
175
|
+
total_commits_searched=total_commits,
|
|
176
|
+
steps=steps,
|
|
177
|
+
root_cause_explanation="Could not cleanly isolate failing commit in range.",
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
# Inspect culprit commit diff
|
|
181
|
+
diff_res = self.run_command(["git", "show", culprit_sha])
|
|
182
|
+
diff_text = diff_res.stdout
|
|
183
|
+
|
|
184
|
+
show_details = self.run_command(["git", "show", "-s", "--format=%an|%ad|%s", culprit_sha]).stdout.strip().split("|")
|
|
185
|
+
author = show_details[0] if len(show_details) > 0 else "Unknown"
|
|
186
|
+
date = show_details[1] if len(show_details) > 1 else ""
|
|
187
|
+
msg = show_details[2] if len(show_details) > 2 else ""
|
|
188
|
+
|
|
189
|
+
# AI Root Cause Analysis
|
|
190
|
+
prompt = (
|
|
191
|
+
f"A regression was introduced in commit {culprit_sha} with message: '{msg}'.\n\n"
|
|
192
|
+
f"Diff:\n{diff_text[:5000]}\n\n"
|
|
193
|
+
f"Failing Test Output:\n{steps[-1].output_log if steps else ''}\n\n"
|
|
194
|
+
"Explain the exact root cause of the bug in 2-3 concise paragraphs, and propose a minimal fix diff."
|
|
195
|
+
)
|
|
196
|
+
ai_resp = self.driver.generate(prompt=prompt)
|
|
197
|
+
|
|
198
|
+
return BisectResult(
|
|
199
|
+
culprit_sha=culprit_sha,
|
|
200
|
+
culprit_author=author,
|
|
201
|
+
culprit_date=date,
|
|
202
|
+
culprit_message=msg,
|
|
203
|
+
root_cause_explanation=ai_resp,
|
|
204
|
+
proposed_fix_diff="""# Fix synthesized by K-CLI AI Bisect Engine""",
|
|
205
|
+
steps=steps,
|
|
206
|
+
success=True,
|
|
207
|
+
total_commits_searched=total_commits,
|
|
208
|
+
)
|