devhive 0.1.6__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.
- devhive-0.1.6/PKG-INFO +5 -0
- devhive-0.1.6/README.md +224 -0
- devhive-0.1.6/devhive.egg-info/PKG-INFO +5 -0
- devhive-0.1.6/devhive.egg-info/SOURCES.txt +22 -0
- devhive-0.1.6/devhive.egg-info/dependency_links.txt +1 -0
- devhive-0.1.6/devhive.egg-info/entry_points.txt +3 -0
- devhive-0.1.6/devhive.egg-info/top_level.txt +1 -0
- devhive-0.1.6/mcp_server/__init__.py +1 -0
- devhive-0.1.6/mcp_server/agents/__init__.py +10 -0
- devhive-0.1.6/mcp_server/agents/architect.py +28 -0
- devhive-0.1.6/mcp_server/agents/archivist.py +19 -0
- devhive-0.1.6/mcp_server/agents/auditor.py +28 -0
- devhive-0.1.6/mcp_server/agents/base_agent.py +104 -0
- devhive-0.1.6/mcp_server/agents/ceo.py +171 -0
- devhive-0.1.6/mcp_server/agents/developer.py +44 -0
- devhive-0.1.6/mcp_server/agents/explorer.py +36 -0
- devhive-0.1.6/mcp_server/agents/proposal.py +30 -0
- devhive-0.1.6/mcp_server/agents/qa.py +46 -0
- devhive-0.1.6/mcp_server/agents/task_agent.py +27 -0
- devhive-0.1.6/mcp_server/cli.py +314 -0
- devhive-0.1.6/mcp_server/server.py +801 -0
- devhive-0.1.6/pyproject.toml +17 -0
- devhive-0.1.6/setup.cfg +4 -0
- devhive-0.1.6/setup.py +16 -0
devhive-0.1.6/PKG-INFO
ADDED
devhive-0.1.6/README.md
ADDED
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
# DevHive MCP Server
|
|
2
|
+
|
|
3
|
+
[](https://www.python.org/)
|
|
4
|
+
[](https://modelcontextprotocol.io/)
|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
|
+
|
|
7
|
+
**DevHive** is a powerful **Model Context Protocol (MCP)** server that orchestrates an autonomous AI software development team. It transforms a single LLM session into a sequential, multi-agent pipeline capable of handling complex software engineering tasksβfrom initial requirements analysis to final code archiving.
|
|
8
|
+
|
|
9
|
+
Designed to integrate seamlessly with **OpenCode** and **GitHub Copilot**, DevHive provides the tooling and state management necessary for AI agents to work continuously on large projects without losing context.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## π Key Features
|
|
14
|
+
|
|
15
|
+
* **8-Stage Agent Pipeline:** A structured workflow guiding development through specialized roles:
|
|
16
|
+
1. **Explorer:** Analyzes requirements and feasibility.
|
|
17
|
+
2. **Proposal:** Creates detailed feature proposals.
|
|
18
|
+
3. **Architect:** Designs technical architecture and system diagrams.
|
|
19
|
+
4. **TaskPlanner:** Breaks down work into actionable developer tasks.
|
|
20
|
+
5. **Developer:** Implements code and writes files.
|
|
21
|
+
6. **QA:** Generates and executes test suites.
|
|
22
|
+
7. **Auditor:** Verifies consistency against architecture and requirements.
|
|
23
|
+
8. **Archivist:** Finalizes and archives the project state.
|
|
24
|
+
* **Persistent State Management:** Tracks project progress, artifacts, and file generation across sessions.
|
|
25
|
+
* **Context Optimization (RAG):** Built-in memory system using TF-IDF to retrieve relevant context from previous steps, reducing token usage and hallucination.
|
|
26
|
+
* **Cross-Platform CLI:** Easy-to-use command-line interface for configuration and server management.
|
|
27
|
+
* **Universal Compatibility:** Works as a local MCP server for OpenCode, Claude Desktop, or any MCP-compliant client.
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## π Architecture
|
|
32
|
+
|
|
33
|
+
DevHive operates on a strictly sequential pipeline model. Each agent produces a specific **Artifact** (JSON document) that serves as the input for the next agent.
|
|
34
|
+
|
|
35
|
+
```mermaid
|
|
36
|
+
graph LR
|
|
37
|
+
User[User Request] --> Explorer
|
|
38
|
+
Explorer --> Proposal
|
|
39
|
+
Proposal --> Architect
|
|
40
|
+
Architect --> TaskPlanner
|
|
41
|
+
TaskPlanner --> Developer
|
|
42
|
+
Developer --> QA
|
|
43
|
+
QA --> Auditor
|
|
44
|
+
Auditor --> Archivist
|
|
45
|
+
Archivist --> Done((Project Complete))
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Core Components
|
|
49
|
+
|
|
50
|
+
* **`mcp_server`**: The main Python package containing the server logic.
|
|
51
|
+
* **`TaskOrchestrator`**: Manages the transition between agents and validates outputs.
|
|
52
|
+
* **`MemoryStore`**: A local vector-lite store (TF-IDF) for semantic search over project history.
|
|
53
|
+
* **`ProjectStateManager`**: Persists project status to `project_state.json`.
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## π¦ Installation
|
|
58
|
+
|
|
59
|
+
### Prerequisites
|
|
60
|
+
|
|
61
|
+
* Python 3.10 or higher
|
|
62
|
+
* `pip`
|
|
63
|
+
|
|
64
|
+
### Install from Source
|
|
65
|
+
|
|
66
|
+
Clone the repository and install the package in editable mode:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
git clone https://github.com/yourusername/devhive.git
|
|
70
|
+
cd devhive
|
|
71
|
+
pip install -e .
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
This installs the `devhive` CLI tool globally in your environment.
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## βοΈ Configuration
|
|
79
|
+
|
|
80
|
+
DevHive includes a smart configuration tool that sets up your environment automatically.
|
|
81
|
+
|
|
82
|
+
### 1. Run the Setup Wizard
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
devhive configure
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
The wizard will ask:
|
|
89
|
+
* **Client Selection:** OpenCode, GitHub Copilot (VS Code), or both.
|
|
90
|
+
* **MCP Server Registration:** For OpenCode users, it can automatically add the DevHive server to your `config.json`.
|
|
91
|
+
* **VS Code Integration:** For Copilot users, it injects the agent system prompts directly into your global VS Code settings (`github.copilot.chat.codeGeneration.instructions`).
|
|
92
|
+
|
|
93
|
+
### 2. Manual Configuration (Optional)
|
|
94
|
+
|
|
95
|
+
If you prefer manual setup:
|
|
96
|
+
|
|
97
|
+
**OpenCode (`~/.config/opencode/opencode.json`):**
|
|
98
|
+
```json
|
|
99
|
+
{
|
|
100
|
+
"mcp": {
|
|
101
|
+
"devhive": {
|
|
102
|
+
"type": "local",
|
|
103
|
+
"command": ["python3", "-m", "mcp_server.server"],
|
|
104
|
+
"enabled": true,
|
|
105
|
+
"env": { "PYTHONUNBUFFERED": "1" }
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
**VS Code (`settings.json`):**
|
|
112
|
+
```json
|
|
113
|
+
"github.copilot.chat.codeGeneration.instructions": [
|
|
114
|
+
{
|
|
115
|
+
"file": "/path/to/devhive/DEVHIVE_AGENT.md"
|
|
116
|
+
}
|
|
117
|
+
]
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## π₯ Usage
|
|
123
|
+
|
|
124
|
+
### Starting the Server
|
|
125
|
+
|
|
126
|
+
To start the MCP server locally (useful for debugging or manual connection):
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
devhive start
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
*Note: If configured correctly in OpenCode, the server starts automatically in the background.*
|
|
133
|
+
|
|
134
|
+
### Workflow with AI Clients
|
|
135
|
+
|
|
136
|
+
1. **Open your AI Client** (OpenCode or VS Code Copilot).
|
|
137
|
+
2. **Initialize a Project:**
|
|
138
|
+
Ask the agent: *"Start a new pipeline for a CSV export feature."*
|
|
139
|
+
|
|
140
|
+
The agent will use `devhive_start_pipeline`.
|
|
141
|
+
3. **Follow the Steps:**
|
|
142
|
+
The AI will automatically assume the role of the **Explorer**.
|
|
143
|
+
* It will analyze the request.
|
|
144
|
+
* It will submit its findings using `devhive_submit_result`.
|
|
145
|
+
4. **Iterate:**
|
|
146
|
+
DevHive will return the prompt for the next agent (Proposal). The AI client will switch roles and continue until the **Archivist** declares the project complete.
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
## π API / Tool Reference
|
|
151
|
+
|
|
152
|
+
The server exposes the following MCP tools to the AI agent:
|
|
153
|
+
|
|
154
|
+
| Tool | Description |
|
|
155
|
+
|------|-------------|
|
|
156
|
+
| `devhive_start_pipeline` | Initializes a project and retrieves the first task (Explorer). |
|
|
157
|
+
| `devhive_submit_result` | Submits work from the current agent, saves artifacts, and retrieves the next task. |
|
|
158
|
+
| `devhive_search_memory` | Searches project history/context using semantic query. |
|
|
159
|
+
| `devhive_get_recent_memories` | Retrieves the most recent interactions for context refreshment. |
|
|
160
|
+
| `devhive_get_memory_stats` | Returns usage statistics for the project memory. |
|
|
161
|
+
| `list_workspace_files` | Lists files in the current working directory. |
|
|
162
|
+
| `read_workspace_file` | Reads content of a specific file. |
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## π Project Structure
|
|
167
|
+
|
|
168
|
+
```text
|
|
169
|
+
devhive/
|
|
170
|
+
βββ DEVHIVE_AGENT.md # The "Brain" - System prompt for the AI agent
|
|
171
|
+
βββ AGENTS.md # Documentation for agent roles
|
|
172
|
+
βββ pyproject.toml # Python project configuration
|
|
173
|
+
βββ setup.py # Legacy setup support
|
|
174
|
+
βββ mcp_server/ # Source code
|
|
175
|
+
β βββ agents/ # Logic for individual agents (Explorer, Developer, etc.)
|
|
176
|
+
β βββ core/ # Core infrastructure (LLM, Memory, State)
|
|
177
|
+
β βββ utils/ # Filesystem and validation utilities
|
|
178
|
+
β βββ cli.py # CLI implementation (devhive command)
|
|
179
|
+
β βββ server.py # FastMCP server entry point
|
|
180
|
+
βββ tests/ # Test suite
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## π§ͺ Development
|
|
186
|
+
|
|
187
|
+
### Running Tests
|
|
188
|
+
|
|
189
|
+
DevHive uses `pytest` for testing.
|
|
190
|
+
|
|
191
|
+
```bash
|
|
192
|
+
# Install test dependencies
|
|
193
|
+
pip install pytest
|
|
194
|
+
|
|
195
|
+
# Run all tests
|
|
196
|
+
pytest
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### Adding a New Agent
|
|
200
|
+
|
|
201
|
+
1. Create a new class in `mcp_server/agents/` inheriting from `BaseAgent`.
|
|
202
|
+
2. Define the agent's specific validation logic in `mcp_server/utils/validation.py`.
|
|
203
|
+
3. Register the agent in `mcp_server/server.py` and `mcp_server/core/task_orchestrator.py`.
|
|
204
|
+
4. Update `DEVHIVE_AGENT.md` to include the new role in the pipeline instructions.
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
## π€ Contributing
|
|
209
|
+
|
|
210
|
+
Contributions are welcome! Please follow these steps:
|
|
211
|
+
|
|
212
|
+
1. Fork the repository.
|
|
213
|
+
2. Create a feature branch (`git checkout -b feature/amazing-feature`).
|
|
214
|
+
3. Commit your changes (`git commit -m 'Add some amazing feature'`).
|
|
215
|
+
4. Push to the branch (`git push origin feature/amazing-feature`).
|
|
216
|
+
5. Open a Pull Request.
|
|
217
|
+
|
|
218
|
+
Please ensure all tests pass and your code follows the existing style conventions (Python 3.10+ type hinting).
|
|
219
|
+
|
|
220
|
+
---
|
|
221
|
+
|
|
222
|
+
## π License
|
|
223
|
+
|
|
224
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
setup.py
|
|
4
|
+
devhive.egg-info/PKG-INFO
|
|
5
|
+
devhive.egg-info/SOURCES.txt
|
|
6
|
+
devhive.egg-info/dependency_links.txt
|
|
7
|
+
devhive.egg-info/entry_points.txt
|
|
8
|
+
devhive.egg-info/top_level.txt
|
|
9
|
+
mcp_server/__init__.py
|
|
10
|
+
mcp_server/cli.py
|
|
11
|
+
mcp_server/server.py
|
|
12
|
+
mcp_server/agents/__init__.py
|
|
13
|
+
mcp_server/agents/architect.py
|
|
14
|
+
mcp_server/agents/archivist.py
|
|
15
|
+
mcp_server/agents/auditor.py
|
|
16
|
+
mcp_server/agents/base_agent.py
|
|
17
|
+
mcp_server/agents/ceo.py
|
|
18
|
+
mcp_server/agents/developer.py
|
|
19
|
+
mcp_server/agents/explorer.py
|
|
20
|
+
mcp_server/agents/proposal.py
|
|
21
|
+
mcp_server/agents/qa.py
|
|
22
|
+
mcp_server/agents/task_agent.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
mcp_server
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# MCP Server Package
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
from mcp_server.agents.base_agent import BaseAgent
|
|
2
|
+
from mcp_server.agents.explorer import ExplorerAgent
|
|
3
|
+
from mcp_server.agents.proposal import ProposalAgent
|
|
4
|
+
from mcp_server.agents.architect import ArchitectAgent
|
|
5
|
+
from mcp_server.agents.task_agent import TaskAgent
|
|
6
|
+
from mcp_server.agents.developer import DeveloperAgent
|
|
7
|
+
from mcp_server.agents.qa import QAAgent
|
|
8
|
+
from mcp_server.agents.auditor import AuditorAgent
|
|
9
|
+
from mcp_server.agents.archivist import ArchivistAgent
|
|
10
|
+
from mcp_server.agents.ceo import CEOAgent
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from typing import Dict, Any
|
|
3
|
+
from mcp.server.fastmcp import Context
|
|
4
|
+
from mcp_server.agents.base_agent import BaseAgent
|
|
5
|
+
|
|
6
|
+
class ArchitectAgent(BaseAgent):
|
|
7
|
+
role = "Architect"
|
|
8
|
+
|
|
9
|
+
async def execute(self, ctx: Context, **kwargs) -> str:
|
|
10
|
+
context = self.get_context()
|
|
11
|
+
sys_prompt = "You are the Tech Lead. Output JSON only."
|
|
12
|
+
user_prompt = f"""Design architecture.
|
|
13
|
+
Context: {json.dumps(context, default=str)}
|
|
14
|
+
Return JSON with keys: architecture_pattern, components, data_models, apis."""
|
|
15
|
+
|
|
16
|
+
resp = await self._call_llm(ctx, sys_prompt, user_prompt)
|
|
17
|
+
data = self._parse_json(resp)
|
|
18
|
+
return self.save_artifact("architecture", data)
|
|
19
|
+
|
|
20
|
+
def generate_summary(self, data: Dict[str, Any]) -> str:
|
|
21
|
+
"""Generate executive summary for Architect agent."""
|
|
22
|
+
components_count = len(data.get("components", []))
|
|
23
|
+
apis_count = len(data.get("apis", []))
|
|
24
|
+
pattern = data.get("architecture_pattern", "N/A")
|
|
25
|
+
return (
|
|
26
|
+
f"Designed architecture using {pattern} pattern "
|
|
27
|
+
f"with {components_count} components and {apis_count} APIs."
|
|
28
|
+
)
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from typing import Dict, Any
|
|
3
|
+
from mcp.server.fastmcp import Context
|
|
4
|
+
from mcp_server.agents.base_agent import BaseAgent
|
|
5
|
+
|
|
6
|
+
class ArchivistAgent(BaseAgent):
|
|
7
|
+
role = "Archivist"
|
|
8
|
+
async def execute(self, ctx: Context, **kwargs) -> str:
|
|
9
|
+
state = self.state_manager.get_state()
|
|
10
|
+
state["status"] = "completed"
|
|
11
|
+
if "artifacts" not in state: state["artifacts"] = {}
|
|
12
|
+
state["artifacts"]["archive"] = "archived"
|
|
13
|
+
self.state_manager.update_state(state)
|
|
14
|
+
return "Project Archived Successfully"
|
|
15
|
+
|
|
16
|
+
def generate_summary(self, data: Dict[str, Any]) -> str:
|
|
17
|
+
"""Generate executive summary for Archivist agent."""
|
|
18
|
+
# Archivist doesn't use structured data, just returns success message
|
|
19
|
+
return "Project archived successfully. All artifacts and state preserved."
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from typing import Dict, Any
|
|
3
|
+
from mcp.server.fastmcp import Context
|
|
4
|
+
from mcp_server.agents.base_agent import BaseAgent
|
|
5
|
+
|
|
6
|
+
class AuditorAgent(BaseAgent):
|
|
7
|
+
role = "Auditor"
|
|
8
|
+
async def execute(self, ctx: Context, **kwargs) -> str:
|
|
9
|
+
context = self.get_context()
|
|
10
|
+
sys_prompt = "You are the Auditor. Output JSON only."
|
|
11
|
+
user_prompt = f"""Verify the project.
|
|
12
|
+
Context: {json.dumps(context, default=str)}
|
|
13
|
+
Return JSON with keys: architecture_consistency (bool), missing_pieces (list), security_risks (list)."""
|
|
14
|
+
|
|
15
|
+
resp = await self._call_llm(ctx, sys_prompt, user_prompt)
|
|
16
|
+
data = self._parse_json(resp)
|
|
17
|
+
return self.save_artifact("verification", data)
|
|
18
|
+
|
|
19
|
+
def generate_summary(self, data: Dict[str, Any]) -> str:
|
|
20
|
+
"""Generate executive summary for Auditor agent."""
|
|
21
|
+
consistent = data.get("architecture_consistency", False)
|
|
22
|
+
missing_count = len(data.get("missing_pieces", []))
|
|
23
|
+
security_count = len(data.get("security_risks", []))
|
|
24
|
+
status = "β Passed" if consistent and missing_count == 0 else "β Issues Found"
|
|
25
|
+
return (
|
|
26
|
+
f"{status}. Missing pieces: {missing_count}, "
|
|
27
|
+
f"Security risks: {security_count}"
|
|
28
|
+
)
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import logging
|
|
3
|
+
from typing import Dict, Any, Optional
|
|
4
|
+
from mcp.types import SamplingMessage, TextContent
|
|
5
|
+
from mcp.server.fastmcp import Context
|
|
6
|
+
from mcp_server.core.project_state_manager import ProjectStateManager
|
|
7
|
+
from mcp_server.core.artifact_manager import ArtifactManager
|
|
8
|
+
from mcp_server.core.context_router import ContextRouter
|
|
9
|
+
from mcp_server.core.llm import LLM
|
|
10
|
+
|
|
11
|
+
logger = logging.getLogger(__name__)
|
|
12
|
+
|
|
13
|
+
class BaseAgent:
|
|
14
|
+
role: str = "Base"
|
|
15
|
+
|
|
16
|
+
def __init__(self, project_name: str):
|
|
17
|
+
self.project_name = project_name
|
|
18
|
+
self.state_manager = ProjectStateManager(project_name)
|
|
19
|
+
self.artifact_manager = ArtifactManager(project_name)
|
|
20
|
+
self.context_router = ContextRouter(self.state_manager, self.artifact_manager)
|
|
21
|
+
|
|
22
|
+
def get_context(self) -> Dict[str, Any]:
|
|
23
|
+
return self.context_router.get_context(self.role)
|
|
24
|
+
|
|
25
|
+
def save_artifact(self, step_name: str, content: Dict[str, Any]) -> str:
|
|
26
|
+
aid = self.artifact_manager.save_artifact(step_name, content)
|
|
27
|
+
self.state_manager.update_artifact(step_name, aid)
|
|
28
|
+
return aid
|
|
29
|
+
|
|
30
|
+
def generate_summary(self, data: Dict[str, Any]) -> str:
|
|
31
|
+
"""
|
|
32
|
+
Generate a 1-3 sentence executive summary of the agent's work.
|
|
33
|
+
|
|
34
|
+
This default implementation provides a basic summary. Subclasses should
|
|
35
|
+
override this method to provide more specific summaries based on their
|
|
36
|
+
role and the data they produce.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
data: The artifact data produced by the agent
|
|
40
|
+
|
|
41
|
+
Returns:
|
|
42
|
+
A concise 1-3 sentence summary suitable for orchestrator context
|
|
43
|
+
"""
|
|
44
|
+
return f"{self.role} completed successfully."
|
|
45
|
+
|
|
46
|
+
async def _call_llm(self, ctx: Context, system_prompt: str, user_prompt: str, max_tokens: int = 2000)-> str:
|
|
47
|
+
decision = await LLM.generate_json(
|
|
48
|
+
ctx,
|
|
49
|
+
system_prompt,
|
|
50
|
+
user_prompt
|
|
51
|
+
)
|
|
52
|
+
return json.dumps(decision)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
async def _call_llm_old(self, ctx: Context, system_prompt: str, user_prompt: str, max_tokens: int = 2000) -> str:
|
|
56
|
+
"""Helper to call LLM via Sampling."""
|
|
57
|
+
try:
|
|
58
|
+
messages = [
|
|
59
|
+
{"role": "user", "content": { "type": "text", "text": f"{system_prompt}\n\n{user_prompt}" }}
|
|
60
|
+
]
|
|
61
|
+
|
|
62
|
+
# Using create_message which is standard
|
|
63
|
+
if hasattr(ctx.session, 'create_message'):
|
|
64
|
+
try:
|
|
65
|
+
result = await ctx.session.create_message(
|
|
66
|
+
messages=messages,
|
|
67
|
+
max_tokens=max_tokens,
|
|
68
|
+
system_prompt=system_prompt
|
|
69
|
+
)
|
|
70
|
+
except:
|
|
71
|
+
# Retry with just user prompt if system prompt arg fails
|
|
72
|
+
result = await ctx.session.create_message(
|
|
73
|
+
messages=messages,
|
|
74
|
+
max_tokens=max_tokens
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
if hasattr(result, 'content') and hasattr(result.content, 'text'):
|
|
78
|
+
return result.content.text
|
|
79
|
+
return str(result)
|
|
80
|
+
|
|
81
|
+
# Fallback
|
|
82
|
+
if hasattr(ctx.session, 'sample'):
|
|
83
|
+
return str(await ctx.session.sample(messages=messages, max_tokens=max_tokens))
|
|
84
|
+
|
|
85
|
+
return '{"error": "No sampling capability found on session"}'
|
|
86
|
+
|
|
87
|
+
except Exception as e:
|
|
88
|
+
logger.error(f"LLM Call failed: {e}")
|
|
89
|
+
return json.dumps({"error": str(e)})
|
|
90
|
+
|
|
91
|
+
def _parse_json(self, text: str) -> Dict[str, Any]:
|
|
92
|
+
""" Robust JSON parser """
|
|
93
|
+
text = text.strip()
|
|
94
|
+
if "```json" in text:
|
|
95
|
+
text = text.split("```json")[1].split("```")[0]
|
|
96
|
+
elif "```" in text:
|
|
97
|
+
text = text.split("```")[1].split("```")[0]
|
|
98
|
+
try:
|
|
99
|
+
return json.loads(text)
|
|
100
|
+
except:
|
|
101
|
+
return {"raw_text": text}
|
|
102
|
+
|
|
103
|
+
async def execute(self, ctx: Context, **kwargs) -> Any:
|
|
104
|
+
raise NotImplementedError
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from typing import Dict, Any
|
|
3
|
+
from mcp.server.fastmcp import Context
|
|
4
|
+
from mcp_server.agents.base_agent import BaseAgent
|
|
5
|
+
from mcp_server.core.project_state_manager import ProjectStateManager
|
|
6
|
+
|
|
7
|
+
class CEOAgent(BaseAgent):
|
|
8
|
+
role = "CEO"
|
|
9
|
+
|
|
10
|
+
def get_next_agent_deterministic(self) -> Dict[str, Any]:
|
|
11
|
+
"""
|
|
12
|
+
Determine next agent to run based on current state (no LLM needed).
|
|
13
|
+
This is a rule-based decision making process following the strict pipeline sequence.
|
|
14
|
+
|
|
15
|
+
Pipeline order:
|
|
16
|
+
1. Explorer
|
|
17
|
+
2. Proposal
|
|
18
|
+
3. Architect
|
|
19
|
+
4. TaskPlanner
|
|
20
|
+
5. Developer
|
|
21
|
+
6. QA
|
|
22
|
+
7. Auditor
|
|
23
|
+
8. Archivist
|
|
24
|
+
|
|
25
|
+
Returns:
|
|
26
|
+
Dict with 'agent' (role name) and 'reason' (explanation)
|
|
27
|
+
"""
|
|
28
|
+
state = self.state_manager.get_state()
|
|
29
|
+
artifacts = state.get("artifacts", {})
|
|
30
|
+
|
|
31
|
+
# Check each stage in order
|
|
32
|
+
if artifacts.get("exploration") is None:
|
|
33
|
+
return {
|
|
34
|
+
"agent": "Explorer",
|
|
35
|
+
"reason": "Need initial feature analysis and exploration"
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
# Determine complexity from exploration artifact
|
|
39
|
+
complexity = "high" # Default to high safety
|
|
40
|
+
try:
|
|
41
|
+
expl_id = artifacts.get("exploration")
|
|
42
|
+
if expl_id:
|
|
43
|
+
expl_data = self.artifact_manager.load_artifact(expl_id)
|
|
44
|
+
complexity = expl_data.get("complexity", "high").lower()
|
|
45
|
+
except Exception:
|
|
46
|
+
pass # Fallback to high complexity on error
|
|
47
|
+
|
|
48
|
+
# Dynamic Routing based on Complexity
|
|
49
|
+
# Low: Explorer -> Developer -> QA -> Archivist
|
|
50
|
+
# Medium: Explorer -> Proposal -> Developer -> QA -> Archivist
|
|
51
|
+
# High: Full Suite
|
|
52
|
+
|
|
53
|
+
if artifacts.get("proposal") is None:
|
|
54
|
+
if complexity == "low":
|
|
55
|
+
pass # Skip Proposal
|
|
56
|
+
else:
|
|
57
|
+
return {
|
|
58
|
+
"agent": "Proposal",
|
|
59
|
+
"reason": "Exploration complete, need feature proposal"
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if artifacts.get("architecture") is None:
|
|
63
|
+
if complexity in ["low", "medium"]:
|
|
64
|
+
pass # Skip Architecture
|
|
65
|
+
else:
|
|
66
|
+
return {
|
|
67
|
+
"agent": "Architect",
|
|
68
|
+
"reason": "Proposal complete, need technical architecture design"
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if artifacts.get("tasks") is None:
|
|
72
|
+
if complexity in ["low", "medium"]:
|
|
73
|
+
pass # Skip Task Planning
|
|
74
|
+
else:
|
|
75
|
+
return {
|
|
76
|
+
"agent": "TaskPlanner",
|
|
77
|
+
"reason": "Architecture complete, need task breakdown"
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if artifacts.get("implementation") is None:
|
|
81
|
+
return {
|
|
82
|
+
"agent": "Developer",
|
|
83
|
+
"reason": "Ready for implementation"
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if artifacts.get("tests") is None:
|
|
87
|
+
return {
|
|
88
|
+
"agent": "QA",
|
|
89
|
+
"reason": "Implementation complete, need tests"
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if artifacts.get("verification") is None:
|
|
93
|
+
if complexity in ["low", "medium"]:
|
|
94
|
+
pass # Skip Auditor
|
|
95
|
+
else:
|
|
96
|
+
return {
|
|
97
|
+
"agent": "Auditor",
|
|
98
|
+
"reason": "Tests complete, need final verification"
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if artifacts.get("archive") is None:
|
|
102
|
+
return {
|
|
103
|
+
"agent": "Archivist",
|
|
104
|
+
"reason": "All stages complete, ready to archive project"
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
# All done
|
|
108
|
+
return {
|
|
109
|
+
"agent": "Complete",
|
|
110
|
+
"reason": "All pipeline stages finished, project is complete"
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
async def execute(self, ctx: Context, **kwargs) -> Dict[str, Any]:
|
|
114
|
+
"""
|
|
115
|
+
Original LLM-based decision making (requires sampling support).
|
|
116
|
+
Currently not used in manual workflow.
|
|
117
|
+
"""
|
|
118
|
+
state_manager = ProjectStateManager(self.project_name)
|
|
119
|
+
state = state_manager.get_state()
|
|
120
|
+
|
|
121
|
+
context = {
|
|
122
|
+
"project_status": state.get('status'),
|
|
123
|
+
"artifacts_summary": state.get("artifacts", {})
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
sys_prompt = """
|
|
127
|
+
You are the CEO orchestrating an AI software development pipeline.
|
|
128
|
+
|
|
129
|
+
The pipeline is strictly sequential:
|
|
130
|
+
|
|
131
|
+
1 Explorer
|
|
132
|
+
2 Proposal
|
|
133
|
+
3 Architect
|
|
134
|
+
4 TaskPlanner
|
|
135
|
+
5 Developer
|
|
136
|
+
6 QA
|
|
137
|
+
7 Auditor
|
|
138
|
+
8 Archivist
|
|
139
|
+
|
|
140
|
+
Rules:
|
|
141
|
+
|
|
142
|
+
- NEVER return Wait unless the pipeline is finished
|
|
143
|
+
- ALWAYS select the next missing role
|
|
144
|
+
- Each role produces an artifact
|
|
145
|
+
|
|
146
|
+
Explorer β exploration
|
|
147
|
+
Proposal β proposal
|
|
148
|
+
Architect β architecture
|
|
149
|
+
TaskPlanner β tasks
|
|
150
|
+
Developer β implementation
|
|
151
|
+
QA β tests
|
|
152
|
+
Auditor β verification
|
|
153
|
+
Archivist β archived
|
|
154
|
+
|
|
155
|
+
Return JSON only:
|
|
156
|
+
{ "decision": "Run <Role>", "reason": "..." }
|
|
157
|
+
"""
|
|
158
|
+
user_prompt = f"""Decide next step.
|
|
159
|
+
|
|
160
|
+
Project: {context.get('project_name')}
|
|
161
|
+
Status: {context.get('project_status')}
|
|
162
|
+
Stage: {context.get('stage')}
|
|
163
|
+
Artifacts: {json.dumps(context.get('artifacts_summary', {}), indent=2)}
|
|
164
|
+
|
|
165
|
+
Available Roles: Explorer, Proposal, Architect, TaskPlanner, Developer, QA, Auditor, Archivist.
|
|
166
|
+
Return JSON: {{ "decision": "Run <Role>", "reason": "..." }}
|
|
167
|
+
If finished, "Run Archivist".
|
|
168
|
+
If stage is "initialization", "Run Explorer"
|
|
169
|
+
"""
|
|
170
|
+
resp = await self._call_llm(ctx, sys_prompt, user_prompt)
|
|
171
|
+
return self._parse_json(resp)
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from typing import Dict, Any
|
|
3
|
+
from mcp.server.fastmcp import Context
|
|
4
|
+
from mcp_server.agents.base_agent import BaseAgent
|
|
5
|
+
|
|
6
|
+
class DeveloperAgent(BaseAgent):
|
|
7
|
+
role = "Developer"
|
|
8
|
+
|
|
9
|
+
def write_files(self, data: Dict[str, Any]) -> list[str]:
|
|
10
|
+
"""Write implementation files to disk. Returns list of file paths."""
|
|
11
|
+
files = data.get("files", [])
|
|
12
|
+
from mcp_server.utils.filesystem import write_file
|
|
13
|
+
file_paths = []
|
|
14
|
+
for f in files:
|
|
15
|
+
if isinstance(f, dict) and "path" in f and "content" in f:
|
|
16
|
+
write_file(f["path"], f["content"])
|
|
17
|
+
file_paths.append(f["path"])
|
|
18
|
+
return file_paths
|
|
19
|
+
|
|
20
|
+
async def execute(self, ctx: Context, **kwargs) -> str:
|
|
21
|
+
context = self.get_context()
|
|
22
|
+
sys_prompt = "You are the Developer. Output JSON only."
|
|
23
|
+
user_prompt = f"""Implement the feature.
|
|
24
|
+
Context: {json.dumps(context, default=str)}
|
|
25
|
+
Return JSON with keys: implementation_strategy, file_structure, pseudocode, files (list of {{path, content}})."""
|
|
26
|
+
|
|
27
|
+
resp = await self._call_llm(ctx, sys_prompt, user_prompt, max_tokens=4000)
|
|
28
|
+
data = self._parse_json(resp)
|
|
29
|
+
|
|
30
|
+
# Write files (using extracted method)
|
|
31
|
+
file_paths = self.write_files(data)
|
|
32
|
+
self.state_manager.add_files(file_paths)
|
|
33
|
+
return self.save_artifact("implementation", data)
|
|
34
|
+
|
|
35
|
+
def generate_summary(self, data: Dict[str, Any]) -> str:
|
|
36
|
+
"""Generate executive summary for Developer agent."""
|
|
37
|
+
files_count = len(data.get("files", []))
|
|
38
|
+
strategy = data.get("implementation_strategy", "N/A")
|
|
39
|
+
# Truncate if too long
|
|
40
|
+
if len(strategy) > 80:
|
|
41
|
+
strategy = strategy[:80] + "..."
|
|
42
|
+
return (
|
|
43
|
+
f"Implemented {files_count} files using strategy: {strategy}"
|
|
44
|
+
)
|