k-cli-for-devs 1.0.0__tar.gz → 1.0.1__tar.gz
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_for_devs-1.0.0/k_cli_for_devs.egg-info → k_cli_for_devs-1.0.1}/PKG-INFO +22 -3
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/README.md +21 -2
- k_cli_for_devs-1.0.1/k_cli/agents/autonomous_agent.py +539 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/k_cli/ui/simple_repl.py +10 -13
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/k_cli/web/server.py +23 -82
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1/k_cli_for_devs.egg-info}/PKG-INFO +22 -3
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/k_cli_for_devs.egg-info/SOURCES.txt +2 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/pyproject.toml +1 -1
- k_cli_for_devs-1.0.1/tests/test_autonomous_agent.py +60 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/LICENSE +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/MANIFEST.in +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/k_cli/__init__.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/k_cli/agents/__init__.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/k_cli/agents/adversarial_swarm.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/k_cli/agents/agent_core.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/k_cli/agents/background_daemon.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/k_cli/agents/orchestrator.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/k_cli/agents/persona.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/k_cli/agents/scaffold_engine.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/k_cli/agents/strands_agent.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/k_cli/agents/subagents.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/k_cli/cli.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/k_cli/core/__init__.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/k_cli/core/airgap.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/k_cli/core/credentials.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/k_cli/core/intent_sensor.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/k_cli/core/llm_driver.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/k_cli/core/model_manager.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/k_cli/core/models_hub.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/k_cli/core/prompting.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/k_cli/core/sdk.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/k_cli/core/session.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/k_cli/core/smart_router.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/k_cli/core/storage_manager.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/k_cli/core/viewport_engine.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/k_cli/demo/demo_runner.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/k_cli/git/__init__.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/k_cli/git/ai_bisect.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/k_cli/git/conflict_resolver.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/k_cli/git/git_guard.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/k_cli/git/patcher.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/k_cli/git/repo_map.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/k_cli/git/smart_git.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/k_cli/git/verifier.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/k_cli/github/__init__.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/k_cli/github/dedup_engine.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/k_cli/github/github_client.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/k_cli/github/github_engine.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/k_cli/github/local_hub.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/k_cli/github/pr_watcher.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/k_cli/github/trending.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/k_cli/tools/__init__.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/k_cli/tools/audit.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/k_cli/tools/chaos_immunity.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/k_cli/tools/codebase_qa.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/k_cli/tools/command_runner.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/k_cli/tools/diagram_generator.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/k_cli/tools/doc_retriever.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/k_cli/tools/feature.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/k_cli/tools/ghost_daemon.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/k_cli/tools/incident_triage.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/k_cli/tools/mcp_client.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/k_cli/tools/repo_gardener.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/k_cli/tools/rules.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/k_cli/tools/security.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/k_cli/tools/security_healer.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/k_cli/tools/synapse_graph.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/k_cli/tui/__init__.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/k_cli/tui/diff_viewer.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/k_cli/tui/tui.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/k_cli/tui/tui_animations.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/k_cli/tui/tui_app.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/k_cli/ui/__init__.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/k_cli/web/__init__.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/k_cli/web/static/app.js +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/k_cli/web/static/index.html +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/k_cli/web/static/monitor.html +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/k_cli/web/static/style.css +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/k_cli_for_devs.egg-info/dependency_links.txt +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/k_cli_for_devs.egg-info/entry_points.txt +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/k_cli_for_devs.egg-info/requires.txt +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/k_cli_for_devs.egg-info/top_level.txt +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/setup.cfg +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/tests/test_adaptive_smart_router.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/tests/test_adversarial_knowledge.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/tests/test_adversarial_m6_patcher_git_session.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/tests/test_challenger_m1_driver_and_mock.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/tests/test_challenger_m1_streaming.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/tests/test_chaos_immunity.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/tests/test_cli.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/tests/test_cli_fuzzer_traversal.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/tests/test_cli_github_conflict_mcp.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/tests/test_codex_hub.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/tests/test_command_runner.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/tests/test_conflict_resolver.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/tests/test_credentials.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/tests/test_dedup_engine.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/tests/test_devdocs_expansion.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/tests/test_diagram_generator.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/tests/test_doc_retriever.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/tests/test_dynamic_discovery_and_audit.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/tests/test_e2e_suite.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/tests/test_git_guard.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/tests/test_github_client.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/tests/test_github_engine.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/tests/test_hackathon_bedrock_and_daemon.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/tests/test_incident_triage.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/tests/test_intent_sensor.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/tests/test_k_cli_full_e2e.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/tests/test_killer_features.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/tests/test_llm_driver.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/tests/test_local_hub.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/tests/test_mcp_client.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/tests/test_model_manager.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/tests/test_models_hub.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/tests/test_multi_provider_llm_driver.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/tests/test_orchestrator.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/tests/test_patcher.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/tests/test_persona_system.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/tests/test_repo_map.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/tests/test_rules.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/tests/test_sdk.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/tests/test_security_healer.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/tests/test_session.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/tests/test_simple_ui_and_rules.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/tests/test_smart_git.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/tests/test_storage_manager.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/tests/test_strands_agent.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/tests/test_subagents.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/tests/test_tui.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/tests/test_tui_animations.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/tests/test_tui_app.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/tests/test_tui_app_screens.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/tests/test_tui_pilot.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/tests/test_verifier.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/tests/test_viewport_engine.py +0 -0
- {k_cli_for_devs-1.0.0 → k_cli_for_devs-1.0.1}/tests/test_web_ui.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: k-cli-for-devs
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.1
|
|
4
4
|
Summary: K-CLI for Devs: Autonomous Self-Healing DevOps & Engineering Agent built with AWS Strands Agents SDK
|
|
5
5
|
License: MIT License
|
|
6
6
|
|
|
@@ -61,6 +61,8 @@ Dynamic: license-file
|
|
|
61
61
|
|
|
62
62
|
---
|
|
63
63
|
|
|
64
|
+
[](https://pypi.org/project/k-cli-for-devs/)
|
|
65
|
+
[](https://pypi.org/project/k-cli-for-devs/)
|
|
64
66
|
[](LICENSE)
|
|
65
67
|
[](https://python.org)
|
|
66
68
|
[](https://strandsagents.com)
|
|
@@ -69,6 +71,10 @@ Dynamic: license-file
|
|
|
69
71
|
[](#-zero-trust-ast-compiler-verification-guardrails)
|
|
70
72
|
[](#-sovereign-air-gapped-offline-engine)
|
|
71
73
|
|
|
74
|
+
```bash
|
|
75
|
+
pip install k-cli-for-devs
|
|
76
|
+
```
|
|
77
|
+
|
|
72
78
|
> **K-CLI is the next-generation autonomous developer workstation that unites 3 unified UI tiers, zero-latency intent sensing, compiler-grounded AST verification, Google Antigravity-grade local shell execution, and AWS Bedrock AgentCore into a sovereign, production-grade CLI.**
|
|
73
79
|
|
|
74
80
|
---
|
|
@@ -262,6 +268,13 @@ K-CLI provides 3 purpose-built interfaces tailored to developer workflows:
|
|
|
262
268
|
|
|
263
269
|
## 🏛️ System Architecture Diagram
|
|
264
270
|
|
|
271
|
+

|
|
272
|
+
|
|
273
|
+
> 📄 **Official PDF Specification**: Download the vector [`docs/assets/architecture_diagram.pdf`](docs/assets/architecture_diagram.pdf) submitted to the AWS Hackathon.
|
|
274
|
+
|
|
275
|
+
<details>
|
|
276
|
+
<summary><b>Click to expand raw Mermaid flowchart specification</b></summary>
|
|
277
|
+
|
|
265
278
|
```mermaid
|
|
266
279
|
flowchart TD
|
|
267
280
|
subgraph Ingestion ["📥 Incident & Multi-Interface Ingestion Layer"]
|
|
@@ -313,16 +326,22 @@ flowchart TD
|
|
|
313
326
|
Gate -- "Passed" --> Delivery
|
|
314
327
|
```
|
|
315
328
|
|
|
329
|
+
</details>
|
|
330
|
+
|
|
316
331
|
---
|
|
317
332
|
|
|
318
333
|
## 📦 Installation & Quickstart
|
|
319
334
|
|
|
320
|
-
### 1.
|
|
335
|
+
### 1. Fast Install via PyPI (Recommended)
|
|
336
|
+
```bash
|
|
337
|
+
pip install k-cli-for-devs
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
### 2. Prerequisites & Build from Source
|
|
321
341
|
* **Python 3.11** or **Python 3.12**
|
|
322
342
|
* **Git** installed on host machine
|
|
323
343
|
* (Optional) **AWS Credentials** configured for Amazon Bedrock / Strands Agents
|
|
324
344
|
|
|
325
|
-
### 2. Clone and Install
|
|
326
345
|
```bash
|
|
327
346
|
# Clone repository
|
|
328
347
|
git clone https://github.com/krishivjoshi219-collab/K-Cli-for-Devs.git
|
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
+
[](https://pypi.org/project/k-cli-for-devs/)
|
|
9
|
+
[](https://pypi.org/project/k-cli-for-devs/)
|
|
8
10
|
[](LICENSE)
|
|
9
11
|
[](https://python.org)
|
|
10
12
|
[](https://strandsagents.com)
|
|
@@ -13,6 +15,10 @@
|
|
|
13
15
|
[](#-zero-trust-ast-compiler-verification-guardrails)
|
|
14
16
|
[](#-sovereign-air-gapped-offline-engine)
|
|
15
17
|
|
|
18
|
+
```bash
|
|
19
|
+
pip install k-cli-for-devs
|
|
20
|
+
```
|
|
21
|
+
|
|
16
22
|
> **K-CLI is the next-generation autonomous developer workstation that unites 3 unified UI tiers, zero-latency intent sensing, compiler-grounded AST verification, Google Antigravity-grade local shell execution, and AWS Bedrock AgentCore into a sovereign, production-grade CLI.**
|
|
17
23
|
|
|
18
24
|
---
|
|
@@ -206,6 +212,13 @@ K-CLI provides 3 purpose-built interfaces tailored to developer workflows:
|
|
|
206
212
|
|
|
207
213
|
## 🏛️ System Architecture Diagram
|
|
208
214
|
|
|
215
|
+

|
|
216
|
+
|
|
217
|
+
> 📄 **Official PDF Specification**: Download the vector [`docs/assets/architecture_diagram.pdf`](docs/assets/architecture_diagram.pdf) submitted to the AWS Hackathon.
|
|
218
|
+
|
|
219
|
+
<details>
|
|
220
|
+
<summary><b>Click to expand raw Mermaid flowchart specification</b></summary>
|
|
221
|
+
|
|
209
222
|
```mermaid
|
|
210
223
|
flowchart TD
|
|
211
224
|
subgraph Ingestion ["📥 Incident & Multi-Interface Ingestion Layer"]
|
|
@@ -257,16 +270,22 @@ flowchart TD
|
|
|
257
270
|
Gate -- "Passed" --> Delivery
|
|
258
271
|
```
|
|
259
272
|
|
|
273
|
+
</details>
|
|
274
|
+
|
|
260
275
|
---
|
|
261
276
|
|
|
262
277
|
## 📦 Installation & Quickstart
|
|
263
278
|
|
|
264
|
-
### 1.
|
|
279
|
+
### 1. Fast Install via PyPI (Recommended)
|
|
280
|
+
```bash
|
|
281
|
+
pip install k-cli-for-devs
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
### 2. Prerequisites & Build from Source
|
|
265
285
|
* **Python 3.11** or **Python 3.12**
|
|
266
286
|
* **Git** installed on host machine
|
|
267
287
|
* (Optional) **AWS Credentials** configured for Amazon Bedrock / Strands Agents
|
|
268
288
|
|
|
269
|
-
### 2. Clone and Install
|
|
270
289
|
```bash
|
|
271
290
|
# Clone repository
|
|
272
291
|
git clone https://github.com/krishivjoshi219-collab/K-Cli-for-Devs.git
|
|
@@ -0,0 +1,539 @@
|
|
|
1
|
+
"""
|
|
2
|
+
autonomous_agent.py - True Agentic Autonomous Execution Engine for K-CLI
|
|
3
|
+
Project Bankai v1.0.0
|
|
4
|
+
|
|
5
|
+
Enables K-CLI to act as a full autonomous developer workstation (like Google Antigravity, Aider, and Claude Code):
|
|
6
|
+
1. Proactive local machine & workspace awareness (inspect files, explore directories).
|
|
7
|
+
2. Autonomous ReAct tool execution loop (read, write, edit, execute shell commands, verify with compilers).
|
|
8
|
+
3. Multi-file project synthesis on the user's computer with AST verification and test execution.
|
|
9
|
+
4. Real-time streaming of thoughts, tool invocations, and tool results.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import ast
|
|
15
|
+
import json
|
|
16
|
+
import logging
|
|
17
|
+
import os
|
|
18
|
+
import re
|
|
19
|
+
import subprocess
|
|
20
|
+
import time
|
|
21
|
+
from dataclasses import dataclass, field
|
|
22
|
+
from pathlib import Path
|
|
23
|
+
from typing import Any, Callable, Dict, List, Optional, Tuple, Union
|
|
24
|
+
|
|
25
|
+
from k_cli.core.llm_driver import LLMDriver
|
|
26
|
+
from k_cli.tools.command_runner import global_command_executor
|
|
27
|
+
|
|
28
|
+
logger = logging.getLogger("k_cli.agents.autonomous_agent")
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
# ==============================================================================
|
|
32
|
+
# LOCAL WORKSPACE TOOLS
|
|
33
|
+
# ==============================================================================
|
|
34
|
+
|
|
35
|
+
def tool_list_dir(directory: str = ".", max_depth: int = 2) -> str:
|
|
36
|
+
"""Lists files and directories in the workspace with hierarchy and file sizes."""
|
|
37
|
+
try:
|
|
38
|
+
target = Path(directory).resolve()
|
|
39
|
+
if not target.exists():
|
|
40
|
+
return f"Error: Directory '{directory}' does not exist (resolved to {target})."
|
|
41
|
+
|
|
42
|
+
lines = [f"📁 Directory listing for: {target}"]
|
|
43
|
+
ignored_names = {".git", ".venv", "venv", "k_cli_env", "__pycache__", "node_modules", ".pytest_cache", ".ruff_cache", ".mypy_cache"}
|
|
44
|
+
|
|
45
|
+
count = 0
|
|
46
|
+
for root, dirs, files in os.walk(target):
|
|
47
|
+
dirs[:] = [d for d in dirs if d not in ignored_names and not d.startswith(".")]
|
|
48
|
+
rel = os.path.relpath(root, target)
|
|
49
|
+
depth = 0 if rel == "." else rel.count(os.sep) + 1
|
|
50
|
+
if depth > max_depth:
|
|
51
|
+
dirs.clear()
|
|
52
|
+
continue
|
|
53
|
+
|
|
54
|
+
indent = " " * depth
|
|
55
|
+
if rel != ".":
|
|
56
|
+
lines.append(f"{indent}📂 {os.path.basename(root)}/")
|
|
57
|
+
|
|
58
|
+
for f in sorted(files):
|
|
59
|
+
if f.startswith(".") or f.endswith((".pyc", ".pyo", ".egg-info")):
|
|
60
|
+
continue
|
|
61
|
+
fpath = Path(root) / f
|
|
62
|
+
sz = fpath.stat().st_size if fpath.exists() else 0
|
|
63
|
+
lines.append(f"{indent} 📄 {f} ({sz} bytes)")
|
|
64
|
+
count += 1
|
|
65
|
+
if count > 120:
|
|
66
|
+
lines.append(f"{indent} ... [truncated, showing top 120 items]")
|
|
67
|
+
return "\n".join(lines)
|
|
68
|
+
|
|
69
|
+
return "\n".join(lines)
|
|
70
|
+
except Exception as e:
|
|
71
|
+
return f"Error in list_dir: {e}"
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def tool_read_workspace_file(file_path: str, start_line: int = 1, max_lines: int = 200) -> str:
|
|
75
|
+
"""Reads lines from a local file with line numbering."""
|
|
76
|
+
try:
|
|
77
|
+
p = Path(file_path).resolve()
|
|
78
|
+
if not p.exists():
|
|
79
|
+
return f"Error: File '{file_path}' does not exist."
|
|
80
|
+
if not p.is_file():
|
|
81
|
+
return f"Error: '{file_path}' is a directory, not a file."
|
|
82
|
+
|
|
83
|
+
text = p.read_text(encoding="utf-8", errors="replace")
|
|
84
|
+
lines = text.splitlines()
|
|
85
|
+
total_lines = len(lines)
|
|
86
|
+
|
|
87
|
+
start_idx = max(0, start_line - 1)
|
|
88
|
+
end_idx = min(total_lines, start_idx + max_lines)
|
|
89
|
+
selected = lines[start_idx:end_idx]
|
|
90
|
+
|
|
91
|
+
output = [f"--- File: {p} (Lines {start_line}-{end_idx} of {total_lines}) ---"]
|
|
92
|
+
for i, line in enumerate(selected):
|
|
93
|
+
output.append(f"{start_line + i:4d} | {line}")
|
|
94
|
+
|
|
95
|
+
if end_idx < total_lines:
|
|
96
|
+
output.append(f"... [{total_lines - end_idx} more lines remaining]")
|
|
97
|
+
|
|
98
|
+
return "\n".join(output)
|
|
99
|
+
except Exception as e:
|
|
100
|
+
return f"Error reading file '{file_path}': {e}"
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def tool_write_workspace_file(file_path: str, content: str) -> str:
|
|
104
|
+
"""Creates or overwrites a file on the user's computer, with AST syntax validation for Python."""
|
|
105
|
+
try:
|
|
106
|
+
p = Path(file_path).resolve()
|
|
107
|
+
p.parent.mkdir(parents=True, exist_ok=True)
|
|
108
|
+
p.write_text(content, encoding="utf-8")
|
|
109
|
+
|
|
110
|
+
status_msg = f"Successfully wrote {len(content.encode('utf-8'))} bytes to '{p}'"
|
|
111
|
+
|
|
112
|
+
# If Python file, run py_compile
|
|
113
|
+
if p.suffix.lower() == ".py":
|
|
114
|
+
import py_compile
|
|
115
|
+
try:
|
|
116
|
+
py_compile.compile(str(p), doraise=True)
|
|
117
|
+
status_msg += " (AST py_compile verified: VALID SYNTAX)"
|
|
118
|
+
except py_compile.PyCompileError as pe:
|
|
119
|
+
status_msg += f" (WARNING: Syntax error detected: {pe})"
|
|
120
|
+
|
|
121
|
+
return status_msg
|
|
122
|
+
except Exception as e:
|
|
123
|
+
return f"Error writing file '{file_path}': {e}"
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def tool_edit_workspace_file(file_path: str, target_content: str, replacement_content: str) -> str:
|
|
127
|
+
"""Surgically replaces an exact text block in an existing file."""
|
|
128
|
+
try:
|
|
129
|
+
p = Path(file_path).resolve()
|
|
130
|
+
if not p.exists():
|
|
131
|
+
return f"Error: File '{file_path}' does not exist."
|
|
132
|
+
|
|
133
|
+
text = p.read_text(encoding="utf-8", errors="replace")
|
|
134
|
+
if target_content not in text:
|
|
135
|
+
return f"Error: target_content not found in '{file_path}'."
|
|
136
|
+
|
|
137
|
+
new_text = text.replace(target_content, replacement_content, 1)
|
|
138
|
+
p.write_text(new_text, encoding="utf-8")
|
|
139
|
+
|
|
140
|
+
if p.suffix.lower() == ".py":
|
|
141
|
+
import py_compile
|
|
142
|
+
try:
|
|
143
|
+
py_compile.compile(str(p), doraise=True)
|
|
144
|
+
return f"Successfully updated '{file_path}' (AST py_compile: VALID)"
|
|
145
|
+
except py_compile.PyCompileError as pe:
|
|
146
|
+
return f"Updated '{file_path}' but syntax error detected: {pe}"
|
|
147
|
+
|
|
148
|
+
return f"Successfully updated '{file_path}'"
|
|
149
|
+
except Exception as e:
|
|
150
|
+
return f"Error editing file '{file_path}': {e}"
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def tool_execute_command(command: str, cwd: str = ".", timeout_seconds: int = 60) -> str:
|
|
154
|
+
"""Executes any terminal/shell command on the local machine (Google Antigravity engine)."""
|
|
155
|
+
try:
|
|
156
|
+
res = global_command_executor.execute(command, cwd=cwd, timeout=timeout_seconds)
|
|
157
|
+
output_parts = [
|
|
158
|
+
f"$ {res.command} (exit code: {res.exit_code}, duration: {res.duration_sec:.2f}s)"
|
|
159
|
+
]
|
|
160
|
+
if res.stdout.strip():
|
|
161
|
+
output_parts.append("[stdout]\n" + res.stdout.strip())
|
|
162
|
+
if res.stderr.strip():
|
|
163
|
+
output_parts.append("[stderr]\n" + res.stderr.strip())
|
|
164
|
+
if not res.stdout.strip() and not res.stderr.strip():
|
|
165
|
+
output_parts.append("[command completed with no output]")
|
|
166
|
+
return "\n".join(output_parts)
|
|
167
|
+
except Exception as e:
|
|
168
|
+
return f"Error executing command '{command}': {e}"
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def tool_inspect_repo_structure(directory: str = ".") -> str:
|
|
172
|
+
"""Generates an AST symbol map of functions, classes, and imports across the codebase."""
|
|
173
|
+
try:
|
|
174
|
+
from k_cli.git.repo_map import RepoMap
|
|
175
|
+
rm = RepoMap(root_dir=directory)
|
|
176
|
+
return rm.render_summary(max_tokens=600)
|
|
177
|
+
except Exception as e:
|
|
178
|
+
return tool_list_dir(directory=directory, max_depth=2)
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def tool_verify_code_file(file_path: str) -> str:
|
|
182
|
+
"""Runs compiler syntax checks and test suites for a workspace file."""
|
|
183
|
+
try:
|
|
184
|
+
from k_cli.git.verifier import Verifier
|
|
185
|
+
v = Verifier()
|
|
186
|
+
p = Path(file_path).resolve()
|
|
187
|
+
if not p.exists():
|
|
188
|
+
return f"Error: File '{file_path}' does not exist."
|
|
189
|
+
content = p.read_text(encoding="utf-8", errors="replace")
|
|
190
|
+
res = v.verify(content, language="python")
|
|
191
|
+
return f"Verification Result for '{file_path}': Success={res.success}, Type={res.verification_type} (Errors: {res.error_trace or 'None'})"
|
|
192
|
+
except Exception as e:
|
|
193
|
+
return f"Error in verify_code_file: {e}"
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def tool_search_codebase(query: str, directory: str = ".") -> str:
|
|
197
|
+
"""Searches for text, functions, or patterns across workspace source files."""
|
|
198
|
+
try:
|
|
199
|
+
root = Path(directory).resolve()
|
|
200
|
+
matches = []
|
|
201
|
+
ignored = {".git", ".venv", "venv", "k_cli_env", "__pycache__", "node_modules"}
|
|
202
|
+
|
|
203
|
+
for p in root.rglob("*"):
|
|
204
|
+
if any(part in ignored for part in p.parts):
|
|
205
|
+
continue
|
|
206
|
+
if not p.is_file() or p.suffix.lower() not in {".py", ".js", ".ts", ".html", ".css", ".md", ".json", ".toml", ".yaml", ".sh"}:
|
|
207
|
+
continue
|
|
208
|
+
try:
|
|
209
|
+
lines = p.read_text(encoding="utf-8", errors="replace").splitlines()
|
|
210
|
+
for idx, line in enumerate(lines):
|
|
211
|
+
if query.lower() in line.lower():
|
|
212
|
+
rel = os.path.relpath(p, root)
|
|
213
|
+
matches.append(f"{rel}:{idx + 1}: {line.strip()}")
|
|
214
|
+
if len(matches) >= 40:
|
|
215
|
+
matches.append("... [truncated, 40+ matches found]")
|
|
216
|
+
return "\n".join(matches)
|
|
217
|
+
except Exception:
|
|
218
|
+
continue
|
|
219
|
+
|
|
220
|
+
if not matches:
|
|
221
|
+
return f"No matches found for '{query}' in {root}"
|
|
222
|
+
return "\n".join(matches)
|
|
223
|
+
except Exception as e:
|
|
224
|
+
return f"Error searching codebase: {e}"
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def tool_triage_and_heal_incident(error_traceback: str) -> str:
|
|
228
|
+
"""Triages multi-language crash tracebacks and attempts automated patch healing."""
|
|
229
|
+
try:
|
|
230
|
+
from k_cli.agents.strands_agent import triage_and_heal_incident
|
|
231
|
+
return triage_and_heal_incident(error_traceback)
|
|
232
|
+
except Exception as e:
|
|
233
|
+
return f"Error in incident triage: {e}"
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
# Tool registry map
|
|
237
|
+
AVAILABLE_TOOLS: Dict[str, Callable[..., str]] = {
|
|
238
|
+
"list_dir": tool_list_dir,
|
|
239
|
+
"read_workspace_file": tool_read_workspace_file,
|
|
240
|
+
"write_workspace_file": tool_write_workspace_file,
|
|
241
|
+
"edit_workspace_file": tool_edit_workspace_file,
|
|
242
|
+
"execute_command": tool_execute_command,
|
|
243
|
+
"inspect_repo_structure": tool_inspect_repo_structure,
|
|
244
|
+
"verify_code_file": tool_verify_code_file,
|
|
245
|
+
"search_codebase": tool_search_codebase,
|
|
246
|
+
"triage_and_heal_incident": tool_triage_and_heal_incident,
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
# ==============================================================================
|
|
251
|
+
# AUTONOMOUS AGENT SYSTEM PROMPT
|
|
252
|
+
# ==============================================================================
|
|
253
|
+
|
|
254
|
+
def build_agentic_system_prompt(cwd: Optional[str] = None) -> str:
|
|
255
|
+
"""Generates the mission-critical system prompt establishing local machine authority."""
|
|
256
|
+
cwd_path = Path(cwd or ".").resolve()
|
|
257
|
+
|
|
258
|
+
top_items = []
|
|
259
|
+
try:
|
|
260
|
+
for item in sorted(cwd_path.iterdir()):
|
|
261
|
+
if not item.name.startswith((".", "__")) and item.name not in ("k_cli_env", "venv", "node_modules"):
|
|
262
|
+
top_items.append(f"{item.name}{'/' if item.is_dir() else ''}")
|
|
263
|
+
except Exception:
|
|
264
|
+
pass
|
|
265
|
+
|
|
266
|
+
top_manifest = ", ".join(top_items[:20]) or "standard project tree"
|
|
267
|
+
|
|
268
|
+
return f"""You are K-CLI, a fully autonomous AI DevOps and software engineering workstation running directly on the developer's Linux machine (comparable to Google Antigravity, Aider, and Claude Code).
|
|
269
|
+
|
|
270
|
+
LOCAL MACHINE ENVIRONMENT:
|
|
271
|
+
- Working Directory: {cwd_path}
|
|
272
|
+
- Workspace Root Items: {top_manifest}
|
|
273
|
+
- Execution Privilege: Full local machine terminal execution & file system read/write via tools.
|
|
274
|
+
|
|
275
|
+
CRITICAL OPERATIONAL RULES:
|
|
276
|
+
1. YOU ARE NOT A CHATBOT. You are an autonomous software engineering agent with REAL tools on this local machine.
|
|
277
|
+
2. NEVER say "I do not have access to local file systems" or "I cannot check folders on your computer". YOU DO HAVE ACCESS through your tools!
|
|
278
|
+
3. When the user asks you to:
|
|
279
|
+
- "check", "inspect", or "explore" a folder or project:
|
|
280
|
+
Use `list_dir` or `read_workspace_file` or `inspect_repo_structure` to inspect the real files and evaluate them.
|
|
281
|
+
- "create", "write", "build", or "implement" a project, app, game, or script:
|
|
282
|
+
Autonomously write the files to disk using `write_workspace_file`, verify them with `execute_command` or `verify_code_file`, and confirm they are saved.
|
|
283
|
+
- "run", "test", or "fix" something:
|
|
284
|
+
Use `execute_command` to run the real terminal commands (pytest, python, git, etc.) and inspect the outputs.
|
|
285
|
+
|
|
286
|
+
TOOL CALLING PROTOCOL:
|
|
287
|
+
When you need to execute an action on the local machine, output a tool call block:
|
|
288
|
+
```tool_call
|
|
289
|
+
{{"name": "tool_name", "arguments": {{"arg1": "value1"}}}}
|
|
290
|
+
```
|
|
291
|
+
Stop immediately after the tool call block and wait for the real result. DO NOT simulate or make up the tool result.
|
|
292
|
+
|
|
293
|
+
Available Tools:
|
|
294
|
+
- `list_dir(directory=".", max_depth=2)`: List files and subfolders.
|
|
295
|
+
- `read_workspace_file(file_path="...", start_line=1, max_lines=200)`: Read file contents with line numbers.
|
|
296
|
+
- `write_workspace_file(file_path="...", content="...")`: Write or create a file on disk (auto-verifies Python syntax).
|
|
297
|
+
- `edit_workspace_file(file_path="...", target_content="...", replacement_content="...")`: Surgical search/replace edit.
|
|
298
|
+
- `execute_command(command="...", cwd=".")`: Execute ANY shell command locally on host (pytest, git, python, etc.).
|
|
299
|
+
- `inspect_repo_structure(directory=".")`: AST map of classes and functions.
|
|
300
|
+
- `verify_code_file(file_path="...")`: Ground-truth compiler and test verification.
|
|
301
|
+
- `search_codebase(query="...", directory=".")`: Search for symbols or text across files.
|
|
302
|
+
- `triage_and_heal_incident(error_traceback="...")`: Automated crash traceback triage and repair.
|
|
303
|
+
|
|
304
|
+
Always take real action. Inspect real files, write real code to disk, and verify before giving your final answer."""
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
# ==============================================================================
|
|
308
|
+
# AUTONOMOUS AGENT RUNNER
|
|
309
|
+
# ==============================================================================
|
|
310
|
+
|
|
311
|
+
@dataclass
|
|
312
|
+
class AgentStep:
|
|
313
|
+
step_num: int
|
|
314
|
+
thought: str
|
|
315
|
+
tool_name: Optional[str] = None
|
|
316
|
+
tool_args: Optional[Dict[str, Any]] = None
|
|
317
|
+
tool_output: Optional[str] = None
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
@dataclass
|
|
321
|
+
class AutonomousAgentResult:
|
|
322
|
+
success: bool
|
|
323
|
+
final_response: str
|
|
324
|
+
steps: List[AgentStep] = field(default_factory=list)
|
|
325
|
+
tools_executed: List[str] = field(default_factory=list)
|
|
326
|
+
total_tokens: int = 0
|
|
327
|
+
duration_sec: float = 0.0
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
class AutonomousAgent:
|
|
331
|
+
"""
|
|
332
|
+
Autonomous Agentic Execution Engine.
|
|
333
|
+
Executes a multi-turn ReAct loop with local machine tools, streaming progress in real time.
|
|
334
|
+
"""
|
|
335
|
+
|
|
336
|
+
def __init__(
|
|
337
|
+
self,
|
|
338
|
+
driver: Optional[LLMDriver] = None,
|
|
339
|
+
model_name: str = "auto",
|
|
340
|
+
cwd: Optional[str] = None,
|
|
341
|
+
max_steps: int = 8,
|
|
342
|
+
):
|
|
343
|
+
self.driver = driver or LLMDriver(model_name=model_name)
|
|
344
|
+
self.cwd = cwd or os.getcwd()
|
|
345
|
+
self.max_steps = max_steps
|
|
346
|
+
|
|
347
|
+
def _extract_tool_call(self, text: str) -> Optional[Tuple[str, Dict[str, Any]]]:
|
|
348
|
+
"""Extracts tool name and arguments from JSON or Python-style model output."""
|
|
349
|
+
# 1. Check for JSON tool call
|
|
350
|
+
for match in re.finditer(r"```(?:tool_call|json)?\s*(\{.*?\})\s*```|<tool_call>\s*(\{.*?\})\s*</tool_call>", text, re.DOTALL):
|
|
351
|
+
raw = match.group(1) or match.group(2)
|
|
352
|
+
try:
|
|
353
|
+
d = json.loads(raw.strip())
|
|
354
|
+
if "name" in d and d["name"] in AVAILABLE_TOOLS:
|
|
355
|
+
return d["name"], d.get("arguments", {})
|
|
356
|
+
except Exception:
|
|
357
|
+
continue
|
|
358
|
+
|
|
359
|
+
# 2. Check for Python-style tool call (e.g. write_workspace_file(...) or execute_command(...))
|
|
360
|
+
for block in re.findall(r"```(?:tool_code|tool_call|python)?\s*([a-zA-Z_]\w*\s*\(.*?\))\s*```|<tool_code>\s*([a-zA-Z_]\w*\s*\(.*?\))\s*</tool_code>", text, re.DOTALL):
|
|
361
|
+
raw_code = (block[0] or block[1]).strip()
|
|
362
|
+
try:
|
|
363
|
+
tree = ast.parse(raw_code)
|
|
364
|
+
for node in ast.walk(tree):
|
|
365
|
+
if isinstance(node, ast.Call) and isinstance(node.func, ast.Name):
|
|
366
|
+
fn_name = node.func.id
|
|
367
|
+
if fn_name in AVAILABLE_TOOLS:
|
|
368
|
+
kwargs = {}
|
|
369
|
+
for kw in node.keywords:
|
|
370
|
+
try:
|
|
371
|
+
kwargs[kw.arg] = ast.literal_eval(kw.value)
|
|
372
|
+
except Exception:
|
|
373
|
+
kwargs[kw.arg] = ast.unparse(kw.value) if hasattr(ast, "unparse") else str(kw.value)
|
|
374
|
+
# Handle positional args if any
|
|
375
|
+
if node.args and fn_name == "list_dir":
|
|
376
|
+
kwargs["directory"] = ast.literal_eval(node.args[0])
|
|
377
|
+
elif node.args and fn_name == "execute_command":
|
|
378
|
+
kwargs["command"] = ast.literal_eval(node.args[0])
|
|
379
|
+
elif node.args and fn_name == "read_workspace_file":
|
|
380
|
+
kwargs["file_path"] = ast.literal_eval(node.args[0])
|
|
381
|
+
return fn_name, kwargs
|
|
382
|
+
except Exception:
|
|
383
|
+
continue
|
|
384
|
+
|
|
385
|
+
# 3. Inline function call scan (e.g. `write_workspace_file(...)`)
|
|
386
|
+
for fn_name in AVAILABLE_TOOLS:
|
|
387
|
+
pattern = re.compile(rf"\b{fn_name}\s*\((.*?)\)", re.DOTALL)
|
|
388
|
+
m = pattern.search(text)
|
|
389
|
+
if m:
|
|
390
|
+
raw_call = m.group(0)
|
|
391
|
+
try:
|
|
392
|
+
tree = ast.parse(raw_call.strip())
|
|
393
|
+
for node in ast.walk(tree):
|
|
394
|
+
if isinstance(node, ast.Call) and isinstance(node.func, ast.Name) and node.func.id == fn_name:
|
|
395
|
+
kwargs = {}
|
|
396
|
+
for kw in node.keywords:
|
|
397
|
+
try:
|
|
398
|
+
kwargs[kw.arg] = ast.literal_eval(kw.value)
|
|
399
|
+
except Exception:
|
|
400
|
+
kwargs[kw.arg] = str(kw.value)
|
|
401
|
+
return fn_name, kwargs
|
|
402
|
+
except Exception:
|
|
403
|
+
continue
|
|
404
|
+
|
|
405
|
+
return None
|
|
406
|
+
|
|
407
|
+
def execute_tool(self, name: str, args: Dict[str, Any]) -> str:
|
|
408
|
+
"""Executes a registered local tool with arguments."""
|
|
409
|
+
if name not in AVAILABLE_TOOLS:
|
|
410
|
+
return f"Error: Tool '{name}' is not recognized. Available tools: {list(AVAILABLE_TOOLS.keys())}"
|
|
411
|
+
|
|
412
|
+
tool_fn = AVAILABLE_TOOLS[name]
|
|
413
|
+
try:
|
|
414
|
+
logger.info(f"Executing tool: {name} with args {args}")
|
|
415
|
+
return tool_fn(**args)
|
|
416
|
+
except TypeError as te:
|
|
417
|
+
try:
|
|
418
|
+
if len(args) == 1:
|
|
419
|
+
val = next(iter(args.values()))
|
|
420
|
+
return tool_fn(val)
|
|
421
|
+
except Exception:
|
|
422
|
+
pass
|
|
423
|
+
return f"Error calling {name}: invalid arguments {args} ({te})"
|
|
424
|
+
except Exception as e:
|
|
425
|
+
return f"Error executing tool '{name}': {e}"
|
|
426
|
+
|
|
427
|
+
def run(
|
|
428
|
+
self,
|
|
429
|
+
prompt: str,
|
|
430
|
+
token_callback: Optional[Callable[[str, str], None]] = None,
|
|
431
|
+
) -> AutonomousAgentResult:
|
|
432
|
+
"""
|
|
433
|
+
Executes the autonomous agent ReAct loop.
|
|
434
|
+
token_callback(persona_label, token_or_chunk) is called for real-time streaming.
|
|
435
|
+
"""
|
|
436
|
+
start_time = time.time()
|
|
437
|
+
system_prompt = build_agentic_system_prompt(self.cwd)
|
|
438
|
+
steps: List[AgentStep] = []
|
|
439
|
+
tools_executed: List[str] = []
|
|
440
|
+
|
|
441
|
+
# 1. Proactive Reconnaissance:
|
|
442
|
+
lower_prompt = prompt.lower()
|
|
443
|
+
extra_context = ""
|
|
444
|
+
if any(w in lower_prompt for w in ("check", "inspect", "explore", "what is inside", "list folder", "folder", "directory", "structure", "impressive", "review")):
|
|
445
|
+
target_dir = "."
|
|
446
|
+
words = prompt.split()
|
|
447
|
+
for i, w in enumerate(words):
|
|
448
|
+
clean_w = w.strip("`'\",:;")
|
|
449
|
+
if clean_w in ("folder", "directory", "repo", "project") and i > 0:
|
|
450
|
+
prev = words[i - 1].strip("`'\",:;")
|
|
451
|
+
if prev not in ("the", "this", "my", "a", "our"):
|
|
452
|
+
target_dir = prev
|
|
453
|
+
elif Path(clean_w).is_dir():
|
|
454
|
+
target_dir = clean_w
|
|
455
|
+
break
|
|
456
|
+
|
|
457
|
+
recon_listing = tool_list_dir(target_dir, max_depth=2)
|
|
458
|
+
extra_context = f"\n\n[LOCAL WORKSPACE RECONNAISSANCE - Items in '{target_dir}']:\n{recon_listing}\n"
|
|
459
|
+
if token_callback:
|
|
460
|
+
token_callback("RECON", f"⚡ [Inspecting local directory: {target_dir}]\n")
|
|
461
|
+
|
|
462
|
+
conversation_history = [
|
|
463
|
+
f"User Task: {prompt}{extra_context}"
|
|
464
|
+
]
|
|
465
|
+
|
|
466
|
+
final_response = ""
|
|
467
|
+
current_persona = "RESEARCHER"
|
|
468
|
+
|
|
469
|
+
for step_idx in range(1, self.max_steps + 1):
|
|
470
|
+
full_prompt = "\n\n".join(conversation_history)
|
|
471
|
+
current_turn_tokens: List[str] = []
|
|
472
|
+
|
|
473
|
+
def turn_stream_cb(token: str):
|
|
474
|
+
current_turn_tokens.append(token)
|
|
475
|
+
if token_callback:
|
|
476
|
+
token_callback(current_persona, token)
|
|
477
|
+
|
|
478
|
+
model_out = self.driver.generate(
|
|
479
|
+
prompt=full_prompt,
|
|
480
|
+
system_prompt=system_prompt,
|
|
481
|
+
stream_callback=turn_stream_cb,
|
|
482
|
+
temperature=0.2,
|
|
483
|
+
)
|
|
484
|
+
|
|
485
|
+
if not model_out and current_turn_tokens:
|
|
486
|
+
model_out = "".join(current_turn_tokens)
|
|
487
|
+
|
|
488
|
+
tool_call_info = self._extract_tool_call(model_out)
|
|
489
|
+
|
|
490
|
+
if not tool_call_info:
|
|
491
|
+
final_response = model_out
|
|
492
|
+
steps.append(AgentStep(step_num=step_idx, thought=model_out))
|
|
493
|
+
break
|
|
494
|
+
|
|
495
|
+
tool_name, tool_args = tool_call_info
|
|
496
|
+
tools_executed.append(tool_name)
|
|
497
|
+
current_persona = "TOOL EXEC"
|
|
498
|
+
|
|
499
|
+
# Execute tool locally on developer's machine
|
|
500
|
+
args_summary = ", ".join(f"{k}={repr(v)[:50]}" for k, v in tool_args.items())
|
|
501
|
+
if token_callback:
|
|
502
|
+
token_callback("TOOL EXEC", f"\n\n⚡ [Executing Tool: {tool_name}({args_summary})]\n")
|
|
503
|
+
|
|
504
|
+
tool_result = self.execute_tool(tool_name, tool_args)
|
|
505
|
+
|
|
506
|
+
if token_callback:
|
|
507
|
+
res_lines = tool_result.strip().splitlines()
|
|
508
|
+
summary_preview = res_lines[0] if res_lines else "Completed"
|
|
509
|
+
token_callback("TOOL RESULT", f"✔ [{tool_name} Result]: {summary_preview} ({len(tool_result)} bytes)\n\n")
|
|
510
|
+
|
|
511
|
+
steps.append(
|
|
512
|
+
AgentStep(
|
|
513
|
+
step_num=step_idx,
|
|
514
|
+
thought=model_out,
|
|
515
|
+
tool_name=tool_name,
|
|
516
|
+
tool_args=tool_args,
|
|
517
|
+
tool_output=tool_result,
|
|
518
|
+
)
|
|
519
|
+
)
|
|
520
|
+
|
|
521
|
+
# Feed tool execution back to conversation history
|
|
522
|
+
conversation_history.append(
|
|
523
|
+
f"Assistant Action:\n{model_out}\n\n"
|
|
524
|
+
f"<tool_result tool=\"{tool_name}\">\n{tool_result}\n</tool_result>\n"
|
|
525
|
+
f"The tool execution succeeded. Now continue your response, invoke any additional tools if needed, or conclude your answer."
|
|
526
|
+
)
|
|
527
|
+
current_persona = "CODER" if "write" in tool_name else "VERIFIER"
|
|
528
|
+
|
|
529
|
+
if not final_response:
|
|
530
|
+
final_response = model_out or "Task completed by K-CLI Autonomous Agent."
|
|
531
|
+
|
|
532
|
+
duration = time.time() - start_time
|
|
533
|
+
return AutonomousAgentResult(
|
|
534
|
+
success=True,
|
|
535
|
+
final_response=final_response,
|
|
536
|
+
steps=steps,
|
|
537
|
+
tools_executed=tools_executed,
|
|
538
|
+
duration_sec=duration,
|
|
539
|
+
)
|