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
@@ -0,0 +1,142 @@
1
+ """
2
+ repo_gardener.py - Nightly Autonomous Repo Maintenance & Health Engine for K-CLI
3
+ Project Bankai v1.0.0
4
+
5
+ Scans repository for dead code, unreferenced symbols, outdated dependencies,
6
+ untracked technical debt, and formats actionable cleanup PRs.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import ast
12
+ import os
13
+ import re
14
+ import subprocess
15
+ from dataclasses import dataclass, field
16
+ from pathlib import Path
17
+ from typing import Any, Dict, List, Optional, Set
18
+
19
+ from k_cli.git.repo_map import RepoMap
20
+
21
+
22
+ @dataclass
23
+ class GardenFinding:
24
+ """A single technical debt or hygiene finding."""
25
+ category: str # "dead_code", "outdated_dependency", "stale_branch", "security_hygiene"
26
+ file_path: str
27
+ symbol_or_item: str
28
+ message: str
29
+ severity: str = "MEDIUM" # "HIGH", "MEDIUM", "LOW"
30
+ suggested_action: str = ""
31
+
32
+
33
+ @dataclass
34
+ class GardenReport:
35
+ """Consolidated repo hygiene and maintenance report."""
36
+ total_files_scanned: int
37
+ total_findings: int
38
+ findings: List[GardenFinding] = field(default_factory=list)
39
+ dead_code_count: int = 0
40
+ dependency_issues: int = 0
41
+ health_score: float = 100.0 # 0 to 100
42
+
43
+ def render_markdown(self) -> str:
44
+ """Renders formatted markdown report."""
45
+ lines = [
46
+ f"# 🌿 K-CLI Repo Health & Maintenance Report",
47
+ f"**Overall Health Score**: `{self.health_score:.1f}/100` | **Files Scanned**: {self.total_files_scanned} | **Total Findings**: {self.total_findings}",
48
+ "",
49
+ "## Summary",
50
+ f"- 🧹 **Dead / Unused Functions**: {self.dead_code_count}",
51
+ f"- 📦 **Dependency Issues**: {self.dependency_issues}",
52
+ "",
53
+ "## Detailed Findings",
54
+ ]
55
+ if not self.findings:
56
+ lines.append("✨ Workspace is perfectly pruned! Zero technical debt detected.")
57
+ else:
58
+ for f in self.findings:
59
+ lines.append(f"- **[{f.severity}]** `{f.file_path}`: `{f.symbol_or_item}` — {f.message} *(Fix: {f.suggested_action})*")
60
+ return "\n".join(lines)
61
+
62
+
63
+ class RepoGardener:
64
+ """
65
+ Autonomous Repository Maintenance Gardener.
66
+ """
67
+
68
+ def __init__(self, repo_path: str = "."):
69
+ self.repo_path = Path(repo_path).resolve()
70
+
71
+ def scan_dead_code(self) -> List[GardenFinding]:
72
+ """Scans python codebase for defined functions never referenced across the project."""
73
+ findings: List[GardenFinding] = []
74
+ defined_functions: Dict[str, Tuple[str, int]] = {} # name -> (file_path, lineno)
75
+ ignored_dirs = {".venv", "k_cli_env", ".git", ".pytest_cache", "__pycache__", "build", "dist", "data"}
76
+ py_files = [
77
+ p for p in self.repo_path.rglob("*.py")
78
+ if not any(ig in p.parts for ig in ignored_dirs) and not p.name.startswith("test_")
79
+ ]
80
+
81
+ for p in py_files[:100]:
82
+ try:
83
+ content = p.read_text(encoding="utf-8", errors="ignore")
84
+ all_code_text += "\n" + content
85
+ tree = ast.parse(content)
86
+ for node in ast.walk(tree):
87
+ if isinstance(node, ast.FunctionDef) and not node.name.startswith("_") and node.name not in ("main", "app", "compose", "on_mount"):
88
+ defined_functions[node.name] = (str(p.relative_to(self.repo_path)), node.lineno)
89
+ except Exception:
90
+ pass
91
+
92
+ for func_name, (rel_path, lineno) in defined_functions.items():
93
+ occurrences = len(re.findall(r"\b" + re.escape(func_name) + r"\b", all_code_text))
94
+ if occurrences <= 1: # Only occurs at its own definition
95
+ findings.append(GardenFinding(
96
+ category="dead_code",
97
+ file_path=f"{rel_path}:{lineno}",
98
+ symbol_or_item=func_name,
99
+ message=f"Function `{func_name}` appears unreferenced anywhere else in project.",
100
+ severity="LOW",
101
+ suggested_action=f"Safe to remove or mark private `_{func_name}`",
102
+ ))
103
+
104
+ return findings
105
+
106
+ def scan_dependencies(self) -> List[GardenFinding]:
107
+ """Inspects pyproject.toml or requirements.txt for unpinned or obsolete libraries."""
108
+ findings: List[GardenFinding] = []
109
+ req_file = self.repo_path / "requirements.txt"
110
+ if req_file.exists():
111
+ for line in req_file.read_text(encoding="utf-8").splitlines():
112
+ line = line.strip()
113
+ if line and not line.startswith("#"):
114
+ if "==" not in line and ">=" not in line:
115
+ findings.append(GardenFinding(
116
+ category="outdated_dependency",
117
+ file_path="requirements.txt",
118
+ symbol_or_item=line,
119
+ message=f"Dependency `{line}` lacks version pin.",
120
+ severity="MEDIUM",
121
+ suggested_action=f"Pin to minimum supported version e.g. `{line}>=1.0.0`",
122
+ ))
123
+ return findings
124
+
125
+ def run_garden_sweep(self) -> GardenReport:
126
+ """Executes full repo garden sweep."""
127
+ dead_code = self.scan_dead_code()
128
+ dep_issues = self.scan_dependencies()
129
+
130
+ all_findings = dead_code + dep_issues
131
+ total_files = len(list(self.repo_path.rglob("*.py")))
132
+
133
+ score = max(0.0, 100.0 - (len(dead_code) * 2.5) - (len(dep_issues) * 5.0))
134
+
135
+ return GardenReport(
136
+ total_files_scanned=total_files,
137
+ total_findings=len(all_findings),
138
+ findings=all_findings,
139
+ dead_code_count=len(dead_code),
140
+ dependency_issues=len(dep_issues),
141
+ health_score=score,
142
+ )
k_cli/tools/rules.py ADDED
@@ -0,0 +1,109 @@
1
+ """
2
+ rules.py - Project Rules, Custom Developer Instructions & Workspace Guidance Loader
3
+ Project Bankai v1.0.0
4
+
5
+ Loads and manages developer-specific instructions for the AI from:
6
+ 1. Workspace files: .kclirules, K_RULES.md, .cursorrules, .kcli/rules.md, CLAUDE.md, AGENTS.md
7
+ 2. User-level global instructions: ~/.kcli/rules.md or ~/.kcli/custom_instructions.md
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import os
13
+ from pathlib import Path
14
+ from typing import Union, Optional, List
15
+
16
+ MAX_RULE_BYTES = 65_536
17
+
18
+ DEFAULT_RULES_TEMPLATE = """# K-CLI Custom Developer Instructions (.kclirules)
19
+ # Project Bankai Autonomous AI Engine
20
+
21
+ ## Architecture & Code Standards
22
+ - Write clean, modular, and type-annotated code (Python 3.12+ / Rust / TypeScript).
23
+ - Never remove existing docstrings, tests, or exception handlers unless refactoring.
24
+ - Always include robust error handling with zero crashes on edge cases.
25
+ - Perform strict AST verification and validate all generated functions against test suites.
26
+
27
+ ## Developer Preferences
28
+ - Coding Style: Clean, production-grade, minimal dependencies.
29
+ - Test Framework: pytest (Python) / cargo test (Rust) / vitest (TS).
30
+ - Security Guardrails: Zero raw secrets in code; use environment variables or Credential Vault.
31
+ """
32
+
33
+ RULE_FILE_CANDIDATES = [
34
+ ".kclirules",
35
+ "K_RULES.md",
36
+ ".cursorrules",
37
+ ".kcli/rules.md",
38
+ ".kcli/instructions.md",
39
+ "CLAUDE.md",
40
+ "AGENTS.md",
41
+ ]
42
+
43
+
44
+ def load_project_rules(
45
+ workspace_dir: Union[str, Path] = ".",
46
+ rules_file: Optional[Union[str, Path]] = None,
47
+ ) -> str:
48
+ """
49
+ Load project-level coding rules and custom developer instructions from workspace
50
+ or global user preferences.
51
+ """
52
+ workspace = Path(workspace_dir).resolve()
53
+ target_file: Optional[Path] = None
54
+
55
+ if rules_file is not None:
56
+ rf_path = Path(rules_file)
57
+ if not rf_path.is_absolute():
58
+ rf_path = (workspace / rf_path).resolve()
59
+ else:
60
+ rf_path = rf_path.resolve()
61
+
62
+ try:
63
+ rf_path.relative_to(workspace)
64
+ except ValueError:
65
+ raise ValueError("Rules file must be inside the workspace directory.")
66
+
67
+ target_file = rf_path
68
+ else:
69
+ # Search workspace candidates
70
+ for cand in RULE_FILE_CANDIDATES:
71
+ p = workspace / cand
72
+ if p.exists() and p.is_file():
73
+ target_file = p
74
+ break
75
+
76
+ # If no workspace file found, check global user instructions
77
+ if target_file is None or not target_file.exists():
78
+ global_p = Path.home() / ".kcli" / "rules.md"
79
+ if global_p.exists() and global_p.is_file():
80
+ target_file = global_p
81
+
82
+ if target_file is None or not target_file.exists():
83
+ return ""
84
+
85
+ content_bytes = target_file.read_bytes()
86
+ if len(content_bytes) > MAX_RULE_BYTES:
87
+ raise ValueError(f"Rules file exceeds byte limit ({len(content_bytes)} > {MAX_RULE_BYTES})")
88
+
89
+ content = content_bytes.decode("utf-8", errors="replace")
90
+ return f"### 📋 Custom Developer Instructions & Workspace Rules ({target_file.name}) [untrusted repository context]:\n{content.strip()}\n"
91
+
92
+
93
+ def create_default_rules_file(workspace_dir: Union[str, Path] = ".", force: bool = False) -> Path:
94
+ """Creates a starter .kclirules template in the workspace root."""
95
+ workspace = Path(workspace_dir).resolve()
96
+ target = workspace / ".kclirules"
97
+ if target.exists() and not force:
98
+ return target
99
+ target.write_text(DEFAULT_RULES_TEMPLATE, encoding="utf-8")
100
+ return target
101
+
102
+
103
+ def set_global_rules(instructions: str) -> Path:
104
+ """Sets global custom developer instructions in ~/.kcli/rules.md."""
105
+ global_dir = Path.home() / ".kcli"
106
+ global_dir.mkdir(parents=True, exist_ok=True)
107
+ target = global_dir / "rules.md"
108
+ target.write_text(instructions.strip(), encoding="utf-8")
109
+ return target
@@ -0,0 +1,52 @@
1
+ """Small, dependency-free secret hygiene checks for K-CLI workspaces."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import re
6
+ from dataclasses import dataclass
7
+ from pathlib import Path
8
+ from typing import List
9
+
10
+
11
+ IGNORED = {".git", ".venv", "venv", "k_cli_env", "node_modules", "__pycache__", "data"}
12
+ SOURCE_SUFFIXES = {".py", ".js", ".ts", ".json", ".yaml", ".yml", ".toml", ".sh"}
13
+ RULES = {
14
+ "Hugging Face token": re.compile(r"\bhf_[A-Za-z0-9]{20,}\b"),
15
+ "OpenAI-style key": re.compile(r"\bsk-[A-Za-z0-9_-]{20,}\b"),
16
+ "private key": re.compile(r"-----BEGIN (?:RSA |EC |OPENSSH )?PRIVATE KEY-----"),
17
+ }
18
+
19
+
20
+ @dataclass(frozen=True)
21
+ class SecurityFinding:
22
+ path: str
23
+ line: int
24
+ rule: str
25
+
26
+
27
+ def scan_workspace(root_dir: str | Path = ".", max_findings: int = 25) -> List[SecurityFinding]:
28
+ """Find likely committed credentials without returning sensitive values."""
29
+ root = Path(root_dir).resolve()
30
+ findings: List[SecurityFinding] = []
31
+ for path in root.rglob("*"):
32
+ if len(findings) >= max_findings:
33
+ break
34
+ if not path.is_file() or path.suffix.lower() not in SOURCE_SUFFIXES:
35
+ continue
36
+ if any(part in IGNORED for part in path.parts):
37
+ continue
38
+ try:
39
+ lines = path.read_text(encoding="utf-8").splitlines()
40
+ except (OSError, UnicodeDecodeError):
41
+ continue
42
+ for line_number, line in enumerate(lines, start=1):
43
+ if any(dummy in line.lower() for dummy in ("mock", "example", "placeholder", "dummy", "sample", "test_key", "sk-ant-api03", "sk-proj-test", "sk-mock")):
44
+ continue
45
+ for rule_name, pattern in RULES.items():
46
+ if pattern.search(line):
47
+ findings.append(SecurityFinding(path=path.relative_to(root).as_posix(), line=line_number, rule=rule_name))
48
+ if len(findings) >= max_findings:
49
+ break
50
+ if len(findings) >= max_findings:
51
+ break
52
+ return findings