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.
Files changed (75) hide show
  1. k_cli/__init__.py +77 -0
  2. k_cli/agents/__init__.py +0 -0
  3. k_cli/agents/adversarial_swarm.py +338 -0
  4. k_cli/agents/agent_core.py +255 -0
  5. k_cli/agents/background_daemon.py +141 -0
  6. k_cli/agents/orchestrator.py +376 -0
  7. k_cli/agents/persona.py +649 -0
  8. k_cli/agents/scaffold_engine.py +121 -0
  9. k_cli/agents/strands_agent.py +832 -0
  10. k_cli/agents/subagents.py +1496 -0
  11. k_cli/cli.py +3297 -0
  12. k_cli/core/__init__.py +0 -0
  13. k_cli/core/airgap.py +95 -0
  14. k_cli/core/credentials.py +548 -0
  15. k_cli/core/intent_sensor.py +177 -0
  16. k_cli/core/llm_driver.py +1028 -0
  17. k_cli/core/model_manager.py +1109 -0
  18. k_cli/core/models_hub.py +913 -0
  19. k_cli/core/prompting.py +41 -0
  20. k_cli/core/sdk.py +322 -0
  21. k_cli/core/session.py +826 -0
  22. k_cli/core/smart_router.py +230 -0
  23. k_cli/core/storage_manager.py +176 -0
  24. k_cli/core/viewport_engine.py +117 -0
  25. k_cli/demo/demo_runner.py +579 -0
  26. k_cli/git/__init__.py +0 -0
  27. k_cli/git/ai_bisect.py +208 -0
  28. k_cli/git/conflict_resolver.py +1039 -0
  29. k_cli/git/git_guard.py +417 -0
  30. k_cli/git/patcher.py +1175 -0
  31. k_cli/git/repo_map.py +1780 -0
  32. k_cli/git/smart_git.py +928 -0
  33. k_cli/git/verifier.py +969 -0
  34. k_cli/github/__init__.py +0 -0
  35. k_cli/github/dedup_engine.py +787 -0
  36. k_cli/github/github_client.py +1702 -0
  37. k_cli/github/github_engine.py +641 -0
  38. k_cli/github/local_hub.py +209 -0
  39. k_cli/github/pr_watcher.py +129 -0
  40. k_cli/github/trending.py +205 -0
  41. k_cli/tools/__init__.py +0 -0
  42. k_cli/tools/audit.py +79 -0
  43. k_cli/tools/chaos_immunity.py +377 -0
  44. k_cli/tools/codebase_qa.py +106 -0
  45. k_cli/tools/command_runner.py +256 -0
  46. k_cli/tools/diagram_generator.py +547 -0
  47. k_cli/tools/doc_retriever.py +1332 -0
  48. k_cli/tools/feature.py +105 -0
  49. k_cli/tools/ghost_daemon.py +122 -0
  50. k_cli/tools/incident_triage.py +1365 -0
  51. k_cli/tools/mcp_client.py +1846 -0
  52. k_cli/tools/repo_gardener.py +142 -0
  53. k_cli/tools/rules.py +109 -0
  54. k_cli/tools/security.py +52 -0
  55. k_cli/tools/security_healer.py +999 -0
  56. k_cli/tools/synapse_graph.py +155 -0
  57. k_cli/tui/__init__.py +0 -0
  58. k_cli/tui/diff_viewer.py +223 -0
  59. k_cli/tui/tui.py +1145 -0
  60. k_cli/tui/tui_animations.py +648 -0
  61. k_cli/tui/tui_app.py +2788 -0
  62. k_cli/ui/__init__.py +10 -0
  63. k_cli/ui/simple_repl.py +315 -0
  64. k_cli/web/__init__.py +7 -0
  65. k_cli/web/server.py +624 -0
  66. k_cli/web/static/app.js +830 -0
  67. k_cli/web/static/index.html +495 -0
  68. k_cli/web/static/monitor.html +189 -0
  69. k_cli/web/static/style.css +838 -0
  70. k_cli_for_devs-1.0.0.dist-info/METADATA +461 -0
  71. k_cli_for_devs-1.0.0.dist-info/RECORD +75 -0
  72. k_cli_for_devs-1.0.0.dist-info/WHEEL +5 -0
  73. k_cli_for_devs-1.0.0.dist-info/entry_points.txt +2 -0
  74. k_cli_for_devs-1.0.0.dist-info/licenses/LICENSE +21 -0
  75. 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