devsquad 3.6.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.
- devsquad-3.6.0.dist-info/METADATA +944 -0
- devsquad-3.6.0.dist-info/RECORD +95 -0
- devsquad-3.6.0.dist-info/WHEEL +5 -0
- devsquad-3.6.0.dist-info/entry_points.txt +2 -0
- devsquad-3.6.0.dist-info/licenses/LICENSE +21 -0
- devsquad-3.6.0.dist-info/top_level.txt +2 -0
- scripts/__init__.py +0 -0
- scripts/ai_semantic_matcher.py +512 -0
- scripts/alert_manager.py +505 -0
- scripts/api/__init__.py +43 -0
- scripts/api/models.py +386 -0
- scripts/api/routes/__init__.py +20 -0
- scripts/api/routes/dispatch.py +348 -0
- scripts/api/routes/lifecycle.py +330 -0
- scripts/api/routes/metrics_gates.py +347 -0
- scripts/api_server.py +318 -0
- scripts/auth.py +451 -0
- scripts/cli/__init__.py +1 -0
- scripts/cli/cli_visual.py +642 -0
- scripts/cli.py +1094 -0
- scripts/collaboration/__init__.py +212 -0
- scripts/collaboration/_version.py +1 -0
- scripts/collaboration/agent_briefing.py +656 -0
- scripts/collaboration/ai_semantic_matcher.py +260 -0
- scripts/collaboration/anchor_checker.py +281 -0
- scripts/collaboration/anti_rationalization.py +470 -0
- scripts/collaboration/async_integration_example.py +255 -0
- scripts/collaboration/batch_scheduler.py +149 -0
- scripts/collaboration/checkpoint_manager.py +561 -0
- scripts/collaboration/ci_feedback_adapter.py +351 -0
- scripts/collaboration/code_map_generator.py +247 -0
- scripts/collaboration/concern_pack_loader.py +352 -0
- scripts/collaboration/confidence_score.py +496 -0
- scripts/collaboration/config_loader.py +188 -0
- scripts/collaboration/consensus.py +244 -0
- scripts/collaboration/context_compressor.py +533 -0
- scripts/collaboration/coordinator.py +668 -0
- scripts/collaboration/dispatcher.py +1636 -0
- scripts/collaboration/dual_layer_context.py +128 -0
- scripts/collaboration/enhanced_worker.py +539 -0
- scripts/collaboration/feature_usage_tracker.py +206 -0
- scripts/collaboration/five_axis_consensus.py +334 -0
- scripts/collaboration/input_validator.py +401 -0
- scripts/collaboration/integration_example.py +287 -0
- scripts/collaboration/intent_workflow_mapper.py +350 -0
- scripts/collaboration/language_parsers.py +269 -0
- scripts/collaboration/lifecycle_protocol.py +1446 -0
- scripts/collaboration/llm_backend.py +453 -0
- scripts/collaboration/llm_cache.py +448 -0
- scripts/collaboration/llm_cache_async.py +347 -0
- scripts/collaboration/llm_retry.py +387 -0
- scripts/collaboration/llm_retry_async.py +389 -0
- scripts/collaboration/mce_adapter.py +597 -0
- scripts/collaboration/memory_bridge.py +1607 -0
- scripts/collaboration/models.py +537 -0
- scripts/collaboration/null_providers.py +297 -0
- scripts/collaboration/operation_classifier.py +289 -0
- scripts/collaboration/output_slicer.py +225 -0
- scripts/collaboration/performance_monitor.py +462 -0
- scripts/collaboration/permission_guard.py +865 -0
- scripts/collaboration/prompt_assembler.py +756 -0
- scripts/collaboration/prompt_variant_generator.py +483 -0
- scripts/collaboration/protocols.py +267 -0
- scripts/collaboration/report_formatter.py +352 -0
- scripts/collaboration/retrospective.py +279 -0
- scripts/collaboration/role_matcher.py +92 -0
- scripts/collaboration/role_template_market.py +352 -0
- scripts/collaboration/rule_collector.py +678 -0
- scripts/collaboration/scratchpad.py +346 -0
- scripts/collaboration/skill_registry.py +151 -0
- scripts/collaboration/skillifier.py +878 -0
- scripts/collaboration/standardized_role_template.py +317 -0
- scripts/collaboration/task_completion_checker.py +237 -0
- scripts/collaboration/test_quality_guard.py +695 -0
- scripts/collaboration/unified_gate_engine.py +598 -0
- scripts/collaboration/usage_tracker.py +309 -0
- scripts/collaboration/user_friendly_error.py +176 -0
- scripts/collaboration/verification_gate.py +312 -0
- scripts/collaboration/warmup_manager.py +635 -0
- scripts/collaboration/worker.py +513 -0
- scripts/collaboration/workflow_engine.py +684 -0
- scripts/dashboard.py +1088 -0
- scripts/generate_benchmark_report.py +786 -0
- scripts/history_manager.py +604 -0
- scripts/mcp_server.py +289 -0
- skills/__init__.py +32 -0
- skills/dispatch/handler.py +52 -0
- skills/intent/handler.py +59 -0
- skills/registry.py +67 -0
- skills/retrospective/__init__.py +0 -0
- skills/retrospective/handler.py +125 -0
- skills/review/handler.py +356 -0
- skills/security/handler.py +454 -0
- skills/test/__init__.py +0 -0
- skills/test/handler.py +78 -0
|
@@ -0,0 +1,656 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
Agent Briefing System
|
|
5
|
+
|
|
6
|
+
Provides intelligent briefing generation for agents to understand:
|
|
7
|
+
- Project context and goals
|
|
8
|
+
- Current task requirements
|
|
9
|
+
- Historical decisions and patterns
|
|
10
|
+
- Team collaboration context
|
|
11
|
+
|
|
12
|
+
Usage:
|
|
13
|
+
from scripts.collaboration.agent_briefing import AgentBriefing
|
|
14
|
+
|
|
15
|
+
briefing = AgentBriefing(
|
|
16
|
+
agent_role="Architect",
|
|
17
|
+
project_context={"name": "DevSquad", "version": "3.6.0-Prod"}
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
# Generate briefing
|
|
21
|
+
content = briefing.generate_briefing(
|
|
22
|
+
task="Design Protocol interface system",
|
|
23
|
+
context={"priority": "high"}
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
# Generate project overview
|
|
27
|
+
overview = briefing.generate_project_overview(".")
|
|
28
|
+
|
|
29
|
+
# Generate role-specific understanding
|
|
30
|
+
understanding = briefing.generate_role_understanding("security", ".")
|
|
31
|
+
|
|
32
|
+
# Update with new information
|
|
33
|
+
briefing.update_briefing(
|
|
34
|
+
key="decisions",
|
|
35
|
+
value="Use Python Protocol for interface definition"
|
|
36
|
+
)
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
import json
|
|
40
|
+
import logging
|
|
41
|
+
import re
|
|
42
|
+
from typing import Dict, Any, List, Optional
|
|
43
|
+
from dataclasses import dataclass, field, asdict
|
|
44
|
+
from datetime import datetime
|
|
45
|
+
from pathlib import Path
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
logger = logging.getLogger(__name__)
|
|
49
|
+
|
|
50
|
+
_SAFE_FILENAME_RE = re.compile(r'[^\w\-.]')
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@dataclass
|
|
54
|
+
class BriefingSection:
|
|
55
|
+
"""Briefing section data structure"""
|
|
56
|
+
title: str
|
|
57
|
+
content: str
|
|
58
|
+
priority: int = 1 # 1=high, 2=medium, 3=low
|
|
59
|
+
timestamp: float = field(default_factory=lambda: datetime.now().timestamp())
|
|
60
|
+
metadata: Dict[str, Any] = field(default_factory=dict)
|
|
61
|
+
|
|
62
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
63
|
+
"""Convert to dictionary"""
|
|
64
|
+
return asdict(self)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
@dataclass
|
|
68
|
+
class AgentContext:
|
|
69
|
+
"""Agent context information"""
|
|
70
|
+
role: str
|
|
71
|
+
capabilities: List[str] = field(default_factory=list)
|
|
72
|
+
constraints: List[str] = field(default_factory=list)
|
|
73
|
+
preferences: Dict[str, Any] = field(default_factory=dict)
|
|
74
|
+
history: List[Dict[str, Any]] = field(default_factory=list)
|
|
75
|
+
|
|
76
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
77
|
+
"""Convert to dictionary"""
|
|
78
|
+
return asdict(self)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class AgentBriefing:
|
|
82
|
+
"""
|
|
83
|
+
Agent Briefing Generator
|
|
84
|
+
|
|
85
|
+
Features:
|
|
86
|
+
- Context-aware briefing generation
|
|
87
|
+
- Historical pattern recognition
|
|
88
|
+
- Priority-based information filtering
|
|
89
|
+
- Incremental updates
|
|
90
|
+
- Persistence support
|
|
91
|
+
- Project overview generation (V3.6.0)
|
|
92
|
+
- Role-specific understanding generation (V3.6.0)
|
|
93
|
+
"""
|
|
94
|
+
|
|
95
|
+
def __init__(
|
|
96
|
+
self,
|
|
97
|
+
agent_role: str,
|
|
98
|
+
project_context: Optional[Dict[str, Any]] = None,
|
|
99
|
+
storage_dir: Optional[str] = None
|
|
100
|
+
):
|
|
101
|
+
self.agent_role = agent_role
|
|
102
|
+
self.project_context = project_context or {}
|
|
103
|
+
self.storage_dir = Path(storage_dir or "data/briefings")
|
|
104
|
+
self.storage_dir.mkdir(parents=True, exist_ok=True)
|
|
105
|
+
|
|
106
|
+
self.sections: Dict[str, BriefingSection] = {}
|
|
107
|
+
self.agent_context = AgentContext(role=agent_role)
|
|
108
|
+
|
|
109
|
+
self._load_briefing()
|
|
110
|
+
|
|
111
|
+
def generate_briefing(
|
|
112
|
+
self,
|
|
113
|
+
task: str,
|
|
114
|
+
context: Optional[Dict[str, Any]] = None,
|
|
115
|
+
max_length: Optional[int] = None
|
|
116
|
+
) -> str:
|
|
117
|
+
context = context or {}
|
|
118
|
+
|
|
119
|
+
briefing_parts = []
|
|
120
|
+
|
|
121
|
+
briefing_parts.append(self._generate_role_section())
|
|
122
|
+
briefing_parts.append(self._generate_project_section())
|
|
123
|
+
briefing_parts.append(self._generate_task_section(task, context))
|
|
124
|
+
|
|
125
|
+
if self.agent_context.history:
|
|
126
|
+
briefing_parts.append(self._generate_history_section())
|
|
127
|
+
|
|
128
|
+
sorted_sections = sorted(
|
|
129
|
+
self.sections.values(),
|
|
130
|
+
key=lambda s: (s.priority, -s.timestamp)
|
|
131
|
+
)
|
|
132
|
+
for section in sorted_sections:
|
|
133
|
+
briefing_parts.append(f"## {section.title}\n\n{section.content}")
|
|
134
|
+
|
|
135
|
+
full_briefing = "\n\n".join(briefing_parts)
|
|
136
|
+
|
|
137
|
+
if max_length and len(full_briefing) > max_length:
|
|
138
|
+
full_briefing = full_briefing[:max_length] + "\n\n[Briefing truncated...]"
|
|
139
|
+
|
|
140
|
+
self._add_to_history(task, context, full_briefing)
|
|
141
|
+
|
|
142
|
+
return full_briefing
|
|
143
|
+
|
|
144
|
+
def generate_project_overview(self, project_root: str = ".") -> str:
|
|
145
|
+
"""
|
|
146
|
+
Generate project overview document.
|
|
147
|
+
|
|
148
|
+
Analyzes project structure to produce a module-level understanding
|
|
149
|
+
document covering tech stack, module structure, core components,
|
|
150
|
+
and dependency relationships.
|
|
151
|
+
|
|
152
|
+
Args:
|
|
153
|
+
project_root: Root directory of the project to analyze
|
|
154
|
+
|
|
155
|
+
Returns:
|
|
156
|
+
Markdown-formatted project overview document
|
|
157
|
+
"""
|
|
158
|
+
root = Path(project_root)
|
|
159
|
+
|
|
160
|
+
tech_stack = self._analyze_tech_stack(root)
|
|
161
|
+
modules = self._identify_modules(root)
|
|
162
|
+
core_components = self._identify_core_components(root)
|
|
163
|
+
|
|
164
|
+
lines = ["# Project Overview", ""]
|
|
165
|
+
|
|
166
|
+
lines.append("## Tech Stack")
|
|
167
|
+
for category, items in tech_stack.items():
|
|
168
|
+
lines.append(f"- **{category}**: {', '.join(items)}")
|
|
169
|
+
lines.append("")
|
|
170
|
+
|
|
171
|
+
lines.append("## Module Structure")
|
|
172
|
+
for mod in modules:
|
|
173
|
+
indent = " " * mod.get("depth", 0)
|
|
174
|
+
lines.append(f"{indent}- {mod['icon']} **{mod['name']}** — {mod['description']}")
|
|
175
|
+
lines.append("")
|
|
176
|
+
|
|
177
|
+
lines.append("## Core Components")
|
|
178
|
+
for comp in core_components:
|
|
179
|
+
lines.append(f"- **{comp['name']}** ({comp['file']}): {comp['description']}")
|
|
180
|
+
lines.append("")
|
|
181
|
+
|
|
182
|
+
return "\n".join(lines)
|
|
183
|
+
|
|
184
|
+
def generate_role_understanding(
|
|
185
|
+
self, role: str, project_root: str = "."
|
|
186
|
+
) -> str:
|
|
187
|
+
"""
|
|
188
|
+
Generate role-specific project understanding document.
|
|
189
|
+
|
|
190
|
+
Produces a tailored understanding document for a specific role,
|
|
191
|
+
highlighting aspects most relevant to that role's responsibilities.
|
|
192
|
+
|
|
193
|
+
Args:
|
|
194
|
+
role: Role identifier (e.g., "architect", "security", "tester")
|
|
195
|
+
project_root: Root directory of the project to analyze
|
|
196
|
+
|
|
197
|
+
Returns:
|
|
198
|
+
Markdown-formatted role-specific understanding document
|
|
199
|
+
"""
|
|
200
|
+
root = Path(project_root)
|
|
201
|
+
tech_stack = self._analyze_tech_stack(root)
|
|
202
|
+
modules = self._identify_modules(root)
|
|
203
|
+
|
|
204
|
+
role_focus = {
|
|
205
|
+
"architect": {
|
|
206
|
+
"title": "Architecture Understanding",
|
|
207
|
+
"focus_areas": ["Module Structure", "Dependency Graph", "Design Patterns"],
|
|
208
|
+
"key_questions": [
|
|
209
|
+
"Is the module decomposition following high cohesion / low coupling?",
|
|
210
|
+
"Are there circular dependencies?",
|
|
211
|
+
"Is the abstraction level appropriate?",
|
|
212
|
+
],
|
|
213
|
+
},
|
|
214
|
+
"security": {
|
|
215
|
+
"title": "Security Understanding",
|
|
216
|
+
"focus_areas": ["Authentication", "Authorization", "Data Protection", "Input Validation"],
|
|
217
|
+
"key_questions": [
|
|
218
|
+
"Where are the authentication and authorization checks?",
|
|
219
|
+
"How is sensitive data protected at rest and in transit?",
|
|
220
|
+
"Are all external inputs validated?",
|
|
221
|
+
],
|
|
222
|
+
},
|
|
223
|
+
"tester": {
|
|
224
|
+
"title": "Testing Understanding",
|
|
225
|
+
"focus_areas": ["Test Coverage", "Test Strategy", "Critical Paths"],
|
|
226
|
+
"key_questions": [
|
|
227
|
+
"What is the current test coverage?",
|
|
228
|
+
"Are boundary conditions and error paths tested?",
|
|
229
|
+
"What are the most critical code paths to test?",
|
|
230
|
+
],
|
|
231
|
+
},
|
|
232
|
+
"solo-coder": {
|
|
233
|
+
"title": "Development Understanding",
|
|
234
|
+
"focus_areas": ["Code Conventions", "Hot Spots", "Technical Debt"],
|
|
235
|
+
"key_questions": [
|
|
236
|
+
"What are the coding conventions used?",
|
|
237
|
+
"Where are the most complex functions?",
|
|
238
|
+
"What technical debt exists?",
|
|
239
|
+
],
|
|
240
|
+
},
|
|
241
|
+
"devops": {
|
|
242
|
+
"title": "Operations Understanding",
|
|
243
|
+
"focus_areas": ["Deployment", "Monitoring", "Configuration", "Logging"],
|
|
244
|
+
"key_questions": [
|
|
245
|
+
"How is the application deployed?",
|
|
246
|
+
"What metrics are monitored?",
|
|
247
|
+
"How is configuration managed across environments?",
|
|
248
|
+
],
|
|
249
|
+
},
|
|
250
|
+
"product-manager": {
|
|
251
|
+
"title": "Product Understanding",
|
|
252
|
+
"focus_areas": ["Features", "User Flows", "Acceptance Criteria"],
|
|
253
|
+
"key_questions": [
|
|
254
|
+
"What features are currently implemented?",
|
|
255
|
+
"What are the core user flows?",
|
|
256
|
+
"Are all acceptance criteria met?",
|
|
257
|
+
],
|
|
258
|
+
},
|
|
259
|
+
"ui-designer": {
|
|
260
|
+
"title": "UI/UX Understanding",
|
|
261
|
+
"focus_areas": ["Component Library", "Interaction Patterns", "Accessibility"],
|
|
262
|
+
"key_questions": [
|
|
263
|
+
"What UI components are available?",
|
|
264
|
+
"Are interaction patterns consistent?",
|
|
265
|
+
"Is accessibility supported?",
|
|
266
|
+
],
|
|
267
|
+
},
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
focus = role_focus.get(role, role_focus.get("solo-coder", {}))
|
|
271
|
+
|
|
272
|
+
lines = [f"# {focus.get('title', 'Project Understanding')}", ""]
|
|
273
|
+
lines.append(f"**Role**: {role}")
|
|
274
|
+
lines.append("")
|
|
275
|
+
|
|
276
|
+
lines.append("## Focus Areas")
|
|
277
|
+
for area in focus.get("focus_areas", []):
|
|
278
|
+
lines.append(f"- {area}")
|
|
279
|
+
lines.append("")
|
|
280
|
+
|
|
281
|
+
lines.append("## Key Questions to Answer")
|
|
282
|
+
for q in focus.get("key_questions", []):
|
|
283
|
+
lines.append(f"- [ ] {q}")
|
|
284
|
+
lines.append("")
|
|
285
|
+
|
|
286
|
+
lines.append("## Tech Stack Summary")
|
|
287
|
+
for category, items in tech_stack.items():
|
|
288
|
+
lines.append(f"- **{category}**: {', '.join(items)}")
|
|
289
|
+
lines.append("")
|
|
290
|
+
|
|
291
|
+
lines.append("## Relevant Modules")
|
|
292
|
+
for mod in modules:
|
|
293
|
+
indent = " " * mod.get("depth", 0)
|
|
294
|
+
lines.append(f"{indent}- {mod['icon']} **{mod['name']}** — {mod['description']}")
|
|
295
|
+
lines.append("")
|
|
296
|
+
|
|
297
|
+
return "\n".join(lines)
|
|
298
|
+
|
|
299
|
+
def _analyze_tech_stack(self, root: Path) -> Dict[str, List[str]]:
|
|
300
|
+
"""Analyze project tech stack from config files."""
|
|
301
|
+
stack: Dict[str, List[str]] = {}
|
|
302
|
+
|
|
303
|
+
pyproject = root / "pyproject.toml"
|
|
304
|
+
if pyproject.exists():
|
|
305
|
+
stack["Language"] = ["Python"]
|
|
306
|
+
try:
|
|
307
|
+
content = pyproject.read_text(encoding="utf-8")
|
|
308
|
+
if "fastapi" in content.lower():
|
|
309
|
+
stack.setdefault("Web Framework", []).append("FastAPI")
|
|
310
|
+
if "streamlit" in content.lower():
|
|
311
|
+
stack.setdefault("Web Framework", []).append("Streamlit")
|
|
312
|
+
if "openai" in content.lower():
|
|
313
|
+
stack.setdefault("AI Backend", []).append("OpenAI")
|
|
314
|
+
if "anthropic" in content.lower():
|
|
315
|
+
stack.setdefault("AI Backend", []).append("Anthropic")
|
|
316
|
+
if "sqlite" in content.lower():
|
|
317
|
+
stack.setdefault("Database", []).append("SQLite")
|
|
318
|
+
if "pyyaml" in content.lower() or "yaml" in content.lower():
|
|
319
|
+
stack.setdefault("Config", []).append("YAML")
|
|
320
|
+
except Exception:
|
|
321
|
+
pass
|
|
322
|
+
|
|
323
|
+
dockerfile = root / "Dockerfile"
|
|
324
|
+
if dockerfile.exists():
|
|
325
|
+
stack.setdefault("Deployment", []).append("Docker")
|
|
326
|
+
|
|
327
|
+
github_dir = root / ".github"
|
|
328
|
+
if github_dir.exists():
|
|
329
|
+
stack.setdefault("CI/CD", []).append("GitHub Actions")
|
|
330
|
+
|
|
331
|
+
if not stack:
|
|
332
|
+
stack["Language"] = ["Unknown"]
|
|
333
|
+
|
|
334
|
+
return stack
|
|
335
|
+
|
|
336
|
+
def _identify_modules(self, root: Path) -> List[Dict[str, Any]]:
|
|
337
|
+
"""Identify project modules and their descriptions."""
|
|
338
|
+
modules: List[Dict[str, Any]] = []
|
|
339
|
+
|
|
340
|
+
known_dirs = {
|
|
341
|
+
"scripts/collaboration": {"icon": "🔧", "description": "Core collaboration engine"},
|
|
342
|
+
"scripts/cli": {"icon": "💻", "description": "CLI interface"},
|
|
343
|
+
"scripts/dashboard": {"icon": "📊", "description": "Web dashboard"},
|
|
344
|
+
"scripts/api": {"icon": "🌐", "description": "REST API server"},
|
|
345
|
+
"templates/concerns": {"icon": "📦", "description": "Concern packs"},
|
|
346
|
+
"docs": {"icon": "📄", "description": "Documentation"},
|
|
347
|
+
"tests": {"icon": "🧪", "description": "Test suite"},
|
|
348
|
+
"benchmarks": {"icon": "⚡", "description": "Performance benchmarks"},
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
for dir_path, info in known_dirs.items():
|
|
352
|
+
full_path = root / dir_path
|
|
353
|
+
if full_path.exists() and full_path.is_dir():
|
|
354
|
+
py_files = list(full_path.rglob("*.py"))
|
|
355
|
+
modules.append({
|
|
356
|
+
"name": dir_path,
|
|
357
|
+
"icon": info["icon"],
|
|
358
|
+
"description": f"{info['description']} ({len(py_files)} files)",
|
|
359
|
+
"depth": dir_path.count("/"),
|
|
360
|
+
})
|
|
361
|
+
|
|
362
|
+
return modules
|
|
363
|
+
|
|
364
|
+
def _identify_core_components(self, root: Path) -> List[Dict[str, str]]:
|
|
365
|
+
"""Identify core components from key source files."""
|
|
366
|
+
components: List[Dict[str, str]] = []
|
|
367
|
+
|
|
368
|
+
known_components = [
|
|
369
|
+
("dispatcher.py", "MultiAgentDispatcher", "Unified dispatch entry point"),
|
|
370
|
+
("coordinator.py", "Coordinator", "Global orchestrator for multi-agent collaboration"),
|
|
371
|
+
("worker.py", "Worker", "Role execution unit"),
|
|
372
|
+
("scratchpad.py", "Scratchpad", "Shared blackboard for inter-agent communication"),
|
|
373
|
+
("consensus.py", "ConsensusEngine", "Weighted voting consensus mechanism"),
|
|
374
|
+
("five_axis_consensus.py", "FiveAxisConsensusEngine", "Five-axis code review consensus"),
|
|
375
|
+
("llm_backend.py", "LLMBackend", "Multi-backend LLM abstraction layer"),
|
|
376
|
+
("permission_guard.py", "PermissionGuard", "4-level permission control system"),
|
|
377
|
+
("input_validator.py", "InputValidator", "Input validation and injection detection"),
|
|
378
|
+
("memory_bridge.py", "MemoryBridge", "Cross-session memory management"),
|
|
379
|
+
("workflow_engine.py", "WorkflowEngine", "Task-to-workflow orchestration"),
|
|
380
|
+
("context_compressor.py", "ContextCompressor", "4-level context compression"),
|
|
381
|
+
("anti_rationalization.py", "AntiRationalizationEngine", "Anti-rationalization pattern injection"),
|
|
382
|
+
("verification_gate.py", "VerificationGate", "Evidence-based completion verification"),
|
|
383
|
+
]
|
|
384
|
+
|
|
385
|
+
collab_dir = root / "scripts" / "collaboration"
|
|
386
|
+
for filename, class_name, description in known_components:
|
|
387
|
+
if (collab_dir / filename).exists():
|
|
388
|
+
components.append({
|
|
389
|
+
"name": class_name,
|
|
390
|
+
"file": f"scripts/collaboration/{filename}",
|
|
391
|
+
"description": description,
|
|
392
|
+
})
|
|
393
|
+
|
|
394
|
+
return components
|
|
395
|
+
|
|
396
|
+
def _generate_role_section(self) -> str:
|
|
397
|
+
"""Generate agent role section"""
|
|
398
|
+
content = f"# Agent Briefing: {self.agent_role}\n\n"
|
|
399
|
+
content += f"**Role**: {self.agent_role}\n\n"
|
|
400
|
+
|
|
401
|
+
if self.agent_context.capabilities:
|
|
402
|
+
content += "**Capabilities**:\n"
|
|
403
|
+
for cap in self.agent_context.capabilities:
|
|
404
|
+
content += f"- {cap}\n"
|
|
405
|
+
content += "\n"
|
|
406
|
+
|
|
407
|
+
if self.agent_context.constraints:
|
|
408
|
+
content += "**Constraints**:\n"
|
|
409
|
+
for constraint in self.agent_context.constraints:
|
|
410
|
+
content += f"- {constraint}\n"
|
|
411
|
+
content += "\n"
|
|
412
|
+
|
|
413
|
+
return content.strip()
|
|
414
|
+
|
|
415
|
+
def _generate_project_section(self) -> str:
|
|
416
|
+
"""Generate project context section"""
|
|
417
|
+
if not self.project_context:
|
|
418
|
+
return ""
|
|
419
|
+
|
|
420
|
+
content = "## Project Context\n\n"
|
|
421
|
+
|
|
422
|
+
for key, value in self.project_context.items():
|
|
423
|
+
if isinstance(value, (list, dict)):
|
|
424
|
+
content += f"**{key.replace('_', ' ').title()}**:\n"
|
|
425
|
+
content += f"```json\n{json.dumps(value, indent=2)}\n```\n\n"
|
|
426
|
+
else:
|
|
427
|
+
content += f"**{key.replace('_', ' ').title()}**: {value}\n\n"
|
|
428
|
+
|
|
429
|
+
return content.strip()
|
|
430
|
+
|
|
431
|
+
def _generate_task_section(self, task: str, context: Dict[str, Any]) -> str:
|
|
432
|
+
"""Generate current task section"""
|
|
433
|
+
content = "## Current Task\n\n"
|
|
434
|
+
content += f"{task}\n\n"
|
|
435
|
+
|
|
436
|
+
if context:
|
|
437
|
+
content += "**Task Context**:\n"
|
|
438
|
+
for key, value in context.items():
|
|
439
|
+
content += f"- **{key.replace('_', ' ').title()}**: {value}\n"
|
|
440
|
+
content += "\n"
|
|
441
|
+
|
|
442
|
+
return content.strip()
|
|
443
|
+
|
|
444
|
+
def _generate_history_section(self, limit: int = 5) -> str:
|
|
445
|
+
"""Generate historical context section"""
|
|
446
|
+
content = "## Recent History\n\n"
|
|
447
|
+
|
|
448
|
+
recent_history = self.agent_context.history[-limit:]
|
|
449
|
+
for i, entry in enumerate(reversed(recent_history), 1):
|
|
450
|
+
task = entry.get("task", "Unknown task")
|
|
451
|
+
timestamp = entry.get("timestamp", 0)
|
|
452
|
+
dt = datetime.fromtimestamp(timestamp)
|
|
453
|
+
|
|
454
|
+
content += f"{i}. **{task}** ({dt.strftime('%Y-%m-%d %H:%M')})\n"
|
|
455
|
+
|
|
456
|
+
if "outcome" in entry:
|
|
457
|
+
content += f" - Outcome: {entry['outcome']}\n"
|
|
458
|
+
|
|
459
|
+
if "key_decisions" in entry:
|
|
460
|
+
content += f" - Key decisions: {', '.join(entry['key_decisions'])}\n"
|
|
461
|
+
|
|
462
|
+
content += "\n"
|
|
463
|
+
|
|
464
|
+
return content.strip()
|
|
465
|
+
|
|
466
|
+
def update_briefing(
|
|
467
|
+
self,
|
|
468
|
+
key: str,
|
|
469
|
+
value: Any,
|
|
470
|
+
section: Optional[str] = None,
|
|
471
|
+
priority: int = 2
|
|
472
|
+
) -> None:
|
|
473
|
+
if section:
|
|
474
|
+
if section not in self.sections:
|
|
475
|
+
self.sections[section] = BriefingSection(
|
|
476
|
+
title=section,
|
|
477
|
+
content="",
|
|
478
|
+
priority=priority
|
|
479
|
+
)
|
|
480
|
+
|
|
481
|
+
if isinstance(value, str):
|
|
482
|
+
self.sections[section].content += f"\n- **{key}**: {value}"
|
|
483
|
+
else:
|
|
484
|
+
self.sections[section].content += f"\n- **{key}**: {json.dumps(value)}"
|
|
485
|
+
|
|
486
|
+
self.sections[section].timestamp = datetime.now().timestamp()
|
|
487
|
+
else:
|
|
488
|
+
if key == "capabilities":
|
|
489
|
+
if isinstance(value, list):
|
|
490
|
+
self.agent_context.capabilities.extend(value)
|
|
491
|
+
else:
|
|
492
|
+
self.agent_context.capabilities.append(value)
|
|
493
|
+
elif key == "constraints":
|
|
494
|
+
if isinstance(value, list):
|
|
495
|
+
self.agent_context.constraints.extend(value)
|
|
496
|
+
else:
|
|
497
|
+
self.agent_context.constraints.append(value)
|
|
498
|
+
elif key == "preferences":
|
|
499
|
+
if isinstance(value, dict):
|
|
500
|
+
self.agent_context.preferences.update(value)
|
|
501
|
+
else:
|
|
502
|
+
self.agent_context.preferences[key] = value
|
|
503
|
+
|
|
504
|
+
self._save_briefing()
|
|
505
|
+
|
|
506
|
+
def add_section(
|
|
507
|
+
self,
|
|
508
|
+
title: str,
|
|
509
|
+
content: str,
|
|
510
|
+
priority: int = 2,
|
|
511
|
+
metadata: Optional[Dict[str, Any]] = None
|
|
512
|
+
) -> None:
|
|
513
|
+
self.sections[title] = BriefingSection(
|
|
514
|
+
title=title,
|
|
515
|
+
content=content,
|
|
516
|
+
priority=priority,
|
|
517
|
+
metadata=metadata or {}
|
|
518
|
+
)
|
|
519
|
+
|
|
520
|
+
self._save_briefing()
|
|
521
|
+
|
|
522
|
+
def remove_section(self, title: str) -> bool:
|
|
523
|
+
if title in self.sections:
|
|
524
|
+
del self.sections[title]
|
|
525
|
+
self._save_briefing()
|
|
526
|
+
return True
|
|
527
|
+
return False
|
|
528
|
+
|
|
529
|
+
def get_section(self, title: str) -> Optional[BriefingSection]:
|
|
530
|
+
return self.sections.get(title)
|
|
531
|
+
|
|
532
|
+
def list_sections(self) -> List[str]:
|
|
533
|
+
return list(self.sections.keys())
|
|
534
|
+
|
|
535
|
+
def clear_history(self) -> None:
|
|
536
|
+
self.agent_context.history.clear()
|
|
537
|
+
self._save_briefing()
|
|
538
|
+
|
|
539
|
+
def export_briefing(self, output_path: str) -> None:
|
|
540
|
+
briefing_data = {
|
|
541
|
+
"agent_role": self.agent_role,
|
|
542
|
+
"project_context": self.project_context,
|
|
543
|
+
"agent_context": self.agent_context.to_dict(),
|
|
544
|
+
"sections": {
|
|
545
|
+
title: section.to_dict()
|
|
546
|
+
for title, section in self.sections.items()
|
|
547
|
+
},
|
|
548
|
+
"exported_at": datetime.now().isoformat()
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
Path(output_path).write_text(
|
|
552
|
+
json.dumps(briefing_data, indent=2),
|
|
553
|
+
encoding='utf-8'
|
|
554
|
+
)
|
|
555
|
+
|
|
556
|
+
logger.info(f"Briefing exported to {output_path}")
|
|
557
|
+
|
|
558
|
+
def _add_to_history(
|
|
559
|
+
self,
|
|
560
|
+
task: str,
|
|
561
|
+
context: Dict[str, Any],
|
|
562
|
+
briefing: str
|
|
563
|
+
) -> None:
|
|
564
|
+
history_entry = {
|
|
565
|
+
"task": task,
|
|
566
|
+
"context": context,
|
|
567
|
+
"briefing_length": len(briefing),
|
|
568
|
+
"timestamp": datetime.now().timestamp()
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
self.agent_context.history.append(history_entry)
|
|
572
|
+
|
|
573
|
+
if len(self.agent_context.history) > 100:
|
|
574
|
+
self.agent_context.history = self.agent_context.history[-100:]
|
|
575
|
+
|
|
576
|
+
self._save_briefing()
|
|
577
|
+
|
|
578
|
+
def _save_briefing(self) -> None:
|
|
579
|
+
try:
|
|
580
|
+
safe_role = _SAFE_FILENAME_RE.sub('_', self.agent_role.lower())
|
|
581
|
+
briefing_file = self.storage_dir / f"{safe_role}_briefing.json"
|
|
582
|
+
|
|
583
|
+
briefing_data = {
|
|
584
|
+
"agent_role": self.agent_role,
|
|
585
|
+
"project_context": self.project_context,
|
|
586
|
+
"agent_context": self.agent_context.to_dict(),
|
|
587
|
+
"sections": {
|
|
588
|
+
title: section.to_dict()
|
|
589
|
+
for title, section in self.sections.items()
|
|
590
|
+
},
|
|
591
|
+
"updated_at": datetime.now().isoformat()
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
briefing_file.write_text(
|
|
595
|
+
json.dumps(briefing_data, indent=2),
|
|
596
|
+
encoding='utf-8'
|
|
597
|
+
)
|
|
598
|
+
except Exception as e:
|
|
599
|
+
logger.warning(f"Failed to save briefing: {e}")
|
|
600
|
+
|
|
601
|
+
def _load_briefing(self) -> None:
|
|
602
|
+
try:
|
|
603
|
+
safe_role = _SAFE_FILENAME_RE.sub('_', self.agent_role.lower())
|
|
604
|
+
briefing_file = self.storage_dir / f"{safe_role}_briefing.json"
|
|
605
|
+
|
|
606
|
+
if not briefing_file.exists():
|
|
607
|
+
return
|
|
608
|
+
|
|
609
|
+
briefing_data = json.loads(briefing_file.read_text(encoding='utf-8'))
|
|
610
|
+
|
|
611
|
+
self.project_context = briefing_data.get("project_context", {})
|
|
612
|
+
|
|
613
|
+
agent_context_data = briefing_data.get("agent_context", {})
|
|
614
|
+
self.agent_context = AgentContext(**agent_context_data)
|
|
615
|
+
|
|
616
|
+
sections_data = briefing_data.get("sections", {})
|
|
617
|
+
for title, section_data in sections_data.items():
|
|
618
|
+
self.sections[title] = BriefingSection(**section_data)
|
|
619
|
+
|
|
620
|
+
logger.info(f"Loaded briefing for {self.agent_role}")
|
|
621
|
+
except Exception as e:
|
|
622
|
+
logger.warning(f"Failed to load briefing: {e}")
|
|
623
|
+
|
|
624
|
+
|
|
625
|
+
_briefing_instances: Dict[str, AgentBriefing] = {}
|
|
626
|
+
|
|
627
|
+
|
|
628
|
+
def get_agent_briefing(
|
|
629
|
+
agent_role: str,
|
|
630
|
+
project_context: Optional[Dict[str, Any]] = None,
|
|
631
|
+
storage_dir: Optional[str] = None
|
|
632
|
+
) -> AgentBriefing:
|
|
633
|
+
if agent_role not in _briefing_instances:
|
|
634
|
+
_briefing_instances[agent_role] = AgentBriefing(
|
|
635
|
+
agent_role=agent_role,
|
|
636
|
+
project_context=project_context,
|
|
637
|
+
storage_dir=storage_dir
|
|
638
|
+
)
|
|
639
|
+
|
|
640
|
+
return _briefing_instances[agent_role]
|
|
641
|
+
|
|
642
|
+
|
|
643
|
+
def reset_briefings() -> None:
|
|
644
|
+
"""Reset all briefing instances (for testing)"""
|
|
645
|
+
global _briefing_instances
|
|
646
|
+
_briefing_instances.clear()
|
|
647
|
+
|
|
648
|
+
|
|
649
|
+
__version__ = "3.6.0"
|
|
650
|
+
__all__ = [
|
|
651
|
+
"AgentBriefing",
|
|
652
|
+
"BriefingSection",
|
|
653
|
+
"AgentContext",
|
|
654
|
+
"get_agent_briefing",
|
|
655
|
+
"reset_briefings",
|
|
656
|
+
]
|