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.
Files changed (95) hide show
  1. devsquad-3.6.0.dist-info/METADATA +944 -0
  2. devsquad-3.6.0.dist-info/RECORD +95 -0
  3. devsquad-3.6.0.dist-info/WHEEL +5 -0
  4. devsquad-3.6.0.dist-info/entry_points.txt +2 -0
  5. devsquad-3.6.0.dist-info/licenses/LICENSE +21 -0
  6. devsquad-3.6.0.dist-info/top_level.txt +2 -0
  7. scripts/__init__.py +0 -0
  8. scripts/ai_semantic_matcher.py +512 -0
  9. scripts/alert_manager.py +505 -0
  10. scripts/api/__init__.py +43 -0
  11. scripts/api/models.py +386 -0
  12. scripts/api/routes/__init__.py +20 -0
  13. scripts/api/routes/dispatch.py +348 -0
  14. scripts/api/routes/lifecycle.py +330 -0
  15. scripts/api/routes/metrics_gates.py +347 -0
  16. scripts/api_server.py +318 -0
  17. scripts/auth.py +451 -0
  18. scripts/cli/__init__.py +1 -0
  19. scripts/cli/cli_visual.py +642 -0
  20. scripts/cli.py +1094 -0
  21. scripts/collaboration/__init__.py +212 -0
  22. scripts/collaboration/_version.py +1 -0
  23. scripts/collaboration/agent_briefing.py +656 -0
  24. scripts/collaboration/ai_semantic_matcher.py +260 -0
  25. scripts/collaboration/anchor_checker.py +281 -0
  26. scripts/collaboration/anti_rationalization.py +470 -0
  27. scripts/collaboration/async_integration_example.py +255 -0
  28. scripts/collaboration/batch_scheduler.py +149 -0
  29. scripts/collaboration/checkpoint_manager.py +561 -0
  30. scripts/collaboration/ci_feedback_adapter.py +351 -0
  31. scripts/collaboration/code_map_generator.py +247 -0
  32. scripts/collaboration/concern_pack_loader.py +352 -0
  33. scripts/collaboration/confidence_score.py +496 -0
  34. scripts/collaboration/config_loader.py +188 -0
  35. scripts/collaboration/consensus.py +244 -0
  36. scripts/collaboration/context_compressor.py +533 -0
  37. scripts/collaboration/coordinator.py +668 -0
  38. scripts/collaboration/dispatcher.py +1636 -0
  39. scripts/collaboration/dual_layer_context.py +128 -0
  40. scripts/collaboration/enhanced_worker.py +539 -0
  41. scripts/collaboration/feature_usage_tracker.py +206 -0
  42. scripts/collaboration/five_axis_consensus.py +334 -0
  43. scripts/collaboration/input_validator.py +401 -0
  44. scripts/collaboration/integration_example.py +287 -0
  45. scripts/collaboration/intent_workflow_mapper.py +350 -0
  46. scripts/collaboration/language_parsers.py +269 -0
  47. scripts/collaboration/lifecycle_protocol.py +1446 -0
  48. scripts/collaboration/llm_backend.py +453 -0
  49. scripts/collaboration/llm_cache.py +448 -0
  50. scripts/collaboration/llm_cache_async.py +347 -0
  51. scripts/collaboration/llm_retry.py +387 -0
  52. scripts/collaboration/llm_retry_async.py +389 -0
  53. scripts/collaboration/mce_adapter.py +597 -0
  54. scripts/collaboration/memory_bridge.py +1607 -0
  55. scripts/collaboration/models.py +537 -0
  56. scripts/collaboration/null_providers.py +297 -0
  57. scripts/collaboration/operation_classifier.py +289 -0
  58. scripts/collaboration/output_slicer.py +225 -0
  59. scripts/collaboration/performance_monitor.py +462 -0
  60. scripts/collaboration/permission_guard.py +865 -0
  61. scripts/collaboration/prompt_assembler.py +756 -0
  62. scripts/collaboration/prompt_variant_generator.py +483 -0
  63. scripts/collaboration/protocols.py +267 -0
  64. scripts/collaboration/report_formatter.py +352 -0
  65. scripts/collaboration/retrospective.py +279 -0
  66. scripts/collaboration/role_matcher.py +92 -0
  67. scripts/collaboration/role_template_market.py +352 -0
  68. scripts/collaboration/rule_collector.py +678 -0
  69. scripts/collaboration/scratchpad.py +346 -0
  70. scripts/collaboration/skill_registry.py +151 -0
  71. scripts/collaboration/skillifier.py +878 -0
  72. scripts/collaboration/standardized_role_template.py +317 -0
  73. scripts/collaboration/task_completion_checker.py +237 -0
  74. scripts/collaboration/test_quality_guard.py +695 -0
  75. scripts/collaboration/unified_gate_engine.py +598 -0
  76. scripts/collaboration/usage_tracker.py +309 -0
  77. scripts/collaboration/user_friendly_error.py +176 -0
  78. scripts/collaboration/verification_gate.py +312 -0
  79. scripts/collaboration/warmup_manager.py +635 -0
  80. scripts/collaboration/worker.py +513 -0
  81. scripts/collaboration/workflow_engine.py +684 -0
  82. scripts/dashboard.py +1088 -0
  83. scripts/generate_benchmark_report.py +786 -0
  84. scripts/history_manager.py +604 -0
  85. scripts/mcp_server.py +289 -0
  86. skills/__init__.py +32 -0
  87. skills/dispatch/handler.py +52 -0
  88. skills/intent/handler.py +59 -0
  89. skills/registry.py +67 -0
  90. skills/retrospective/__init__.py +0 -0
  91. skills/retrospective/handler.py +125 -0
  92. skills/review/handler.py +356 -0
  93. skills/security/handler.py +454 -0
  94. skills/test/__init__.py +0 -0
  95. skills/test/handler.py +78 -0
@@ -0,0 +1,352 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ Role Template Market
5
+
6
+ A marketplace for sharing and discovering role templates:
7
+ - Users can publish their custom role templates
8
+ - Community members can browse, install, and rate templates
9
+ - Templates include role prompts, rules, and configuration presets
10
+ - Compatible with SkillRegistry for cross-session persistence
11
+
12
+ Usage:
13
+ from scripts.collaboration.role_template_market import RoleTemplateMarket
14
+
15
+ market = RoleTemplateMarket()
16
+ market.publish(template)
17
+ results = market.search("security auditor")
18
+ market.install(results[0].template_id)
19
+
20
+ Version: v1.0
21
+ Created: 2026-05-01
22
+ """
23
+
24
+ import json
25
+ import hashlib
26
+ import logging
27
+ import os
28
+ import re
29
+ from typing import Dict, List, Any, Optional
30
+ from dataclasses import dataclass, field, asdict
31
+ from datetime import datetime
32
+ from pathlib import Path
33
+
34
+ logger = logging.getLogger(__name__)
35
+
36
+ _SAFE_ID_RE = re.compile(r'[^\w\-.]')
37
+
38
+
39
+ @dataclass
40
+ class RoleTemplate:
41
+ """A reusable role template definition."""
42
+ template_id: str = ""
43
+ name: str = ""
44
+ description: str = ""
45
+ role_id: str = ""
46
+ role_prompt: str = ""
47
+ author: str = ""
48
+ version: str = "1.0.0"
49
+ category: str = "general"
50
+ tags: List[str] = field(default_factory=list)
51
+ rules: List[Dict[str, Any]] = field(default_factory=list)
52
+ config_overrides: Dict[str, Any] = field(default_factory=dict)
53
+ rating: float = 0.0
54
+ install_count: int = 0
55
+ created_at: str = field(default_factory=lambda: datetime.now().isoformat())
56
+ updated_at: str = field(default_factory=lambda: datetime.now().isoformat())
57
+
58
+ def __post_init__(self):
59
+ if not self.template_id:
60
+ raw = f"{self.name}:{self.role_id}:{self.author}:{datetime.now().isoformat()}"
61
+ self.template_id = f"tpl-{hashlib.md5(raw.encode()).hexdigest()[:8]}"
62
+
63
+ def to_dict(self) -> Dict[str, Any]:
64
+ return asdict(self)
65
+
66
+ @classmethod
67
+ def from_dict(cls, data: Dict[str, Any]) -> 'RoleTemplate':
68
+ valid_keys = cls.__dataclass_fields__.keys()
69
+ return cls(**{k: v for k, v in data.items() if k in valid_keys})
70
+
71
+ def validate(self) -> List[str]:
72
+ """Validate template fields. Returns list of error messages."""
73
+ errors = []
74
+ if not self.name or len(self.name) < 2:
75
+ errors.append("name must be at least 2 characters")
76
+ if not self.role_id or len(self.role_id) < 2:
77
+ errors.append("role_id must be at least 2 characters")
78
+ if not self.role_prompt or len(self.role_prompt) < 10:
79
+ errors.append("role_prompt must be at least 10 characters")
80
+ if not self.author:
81
+ errors.append("author is required")
82
+ if self.rating < 0 or self.rating > 5:
83
+ errors.append("rating must be between 0 and 5")
84
+ return errors
85
+
86
+
87
+ @dataclass
88
+ class TemplateRating:
89
+ """A user rating for a template."""
90
+ template_id: str = ""
91
+ user_id: str = ""
92
+ score: float = 0.0
93
+ comment: str = ""
94
+ created_at: str = field(default_factory=lambda: datetime.now().isoformat())
95
+
96
+
97
+ class RoleTemplateMarket:
98
+ """
99
+ Role Template Market - discover, share, and install role templates.
100
+
101
+ Features:
102
+ - Publish custom role templates for community use
103
+ - Search templates by keyword, category, or tags
104
+ - Rate and review templates
105
+ - Install templates for use in dispatch sessions
106
+ - Export/import templates as JSON files
107
+ """
108
+
109
+ def __init__(self, storage_dir: Optional[str] = None):
110
+ """
111
+ Initialize Role Template Market.
112
+
113
+ Args:
114
+ storage_dir: Directory for template persistence
115
+ (default: data/role_templates)
116
+ """
117
+ self.storage_dir = Path(storage_dir or "data/role_templates")
118
+ self.storage_dir.mkdir(parents=True, exist_ok=True)
119
+ self._templates: Dict[str, RoleTemplate] = {}
120
+ self._ratings: Dict[str, List[TemplateRating]] = {}
121
+ self._load_from_disk()
122
+
123
+ def _load_from_disk(self):
124
+ """Load templates from disk storage."""
125
+ templates_file = self.storage_dir / "templates.json"
126
+ if templates_file.exists():
127
+ try:
128
+ data = json.loads(templates_file.read_text(encoding='utf-8'))
129
+ for tpl_data in data:
130
+ tpl = RoleTemplate.from_dict(tpl_data)
131
+ self._templates[tpl.template_id] = tpl
132
+ except Exception as e:
133
+ logger.warning("Failed to load templates: %s", e)
134
+
135
+ ratings_file = self.storage_dir / "ratings.json"
136
+ if ratings_file.exists():
137
+ try:
138
+ data = json.loads(ratings_file.read_text(encoding='utf-8'))
139
+ for r_data in data:
140
+ rating = TemplateRating(**r_data)
141
+ tid = rating.template_id
142
+ if tid not in self._ratings:
143
+ self._ratings[tid] = []
144
+ self._ratings[tid].append(rating)
145
+ except Exception as e:
146
+ logger.warning("Failed to load ratings: %s", e)
147
+
148
+ def _save_to_disk(self):
149
+ """Persist templates and ratings to disk."""
150
+ try:
151
+ templates_file = self.storage_dir / "templates.json"
152
+ templates_data = [tpl.to_dict() for tpl in self._templates.values()]
153
+ templates_file.write_text(
154
+ json.dumps(templates_data, indent=2, ensure_ascii=False),
155
+ encoding='utf-8'
156
+ )
157
+
158
+ ratings_file = self.storage_dir / "ratings.json"
159
+ all_ratings = []
160
+ for ratings_list in self._ratings.values():
161
+ all_ratings.extend([asdict(r) for r in ratings_list])
162
+ ratings_file.write_text(
163
+ json.dumps(all_ratings, indent=2, ensure_ascii=False),
164
+ encoding='utf-8'
165
+ )
166
+ except Exception as e:
167
+ logger.warning("Failed to save templates: %s", e)
168
+
169
+ def publish(self, template: RoleTemplate) -> str:
170
+ """
171
+ Publish a role template to the market.
172
+
173
+ Args:
174
+ template: RoleTemplate to publish
175
+
176
+ Returns:
177
+ str: template_id of the published template
178
+
179
+ Raises:
180
+ ValueError: If template validation fails
181
+ """
182
+ errors = template.validate()
183
+ if errors:
184
+ raise ValueError(f"Template validation failed: {'; '.join(errors)}")
185
+
186
+ self._templates[template.template_id] = template
187
+ self._save_to_disk()
188
+ logger.info("Published template: %s (%s)", template.name, template.template_id)
189
+ return template.template_id
190
+
191
+ def search(self, query: str = "", category: str = "",
192
+ tags: List[str] = None, limit: int = 20) -> List[RoleTemplate]:
193
+ """
194
+ Search templates by keyword, category, or tags.
195
+
196
+ Args:
197
+ query: Search keyword (matches name, description, role_id)
198
+ category: Filter by category
199
+ tags: Filter by tags (OR match)
200
+ limit: Maximum results to return
201
+
202
+ Returns:
203
+ List of matching RoleTemplate objects, sorted by rating
204
+ """
205
+ results = []
206
+ query_lower = query.lower() if query else ""
207
+
208
+ for tpl in self._templates.values():
209
+ if category and tpl.category != category:
210
+ continue
211
+
212
+ if tags:
213
+ if not any(t in tpl.tags for t in tags):
214
+ continue
215
+
216
+ if query_lower:
217
+ searchable = f"{tpl.name} {tpl.description} {tpl.role_id} {' '.join(tpl.tags)}".lower()
218
+ if query_lower not in searchable:
219
+ continue
220
+
221
+ results.append(tpl)
222
+
223
+ results.sort(key=lambda t: t.rating, reverse=True)
224
+ return results[:limit]
225
+
226
+ def get(self, template_id: str) -> Optional[RoleTemplate]:
227
+ """Get a template by ID."""
228
+ return self._templates.get(template_id)
229
+
230
+ def install(self, template_id: str) -> Optional[RoleTemplate]:
231
+ """
232
+ Install a template for use in dispatch sessions.
233
+
234
+ Increments install count and returns the template.
235
+
236
+ Args:
237
+ template_id: Template ID to install
238
+
239
+ Returns:
240
+ RoleTemplate if found, None otherwise
241
+ """
242
+ tpl = self._templates.get(template_id)
243
+ if not tpl:
244
+ return None
245
+
246
+ tpl.install_count += 1
247
+ tpl.updated_at = datetime.now().isoformat()
248
+ self._save_to_disk()
249
+
250
+ install_dir = self.storage_dir / "installed"
251
+ install_dir.mkdir(exist_ok=True)
252
+ safe_id = _SAFE_ID_RE.sub('_', template_id)
253
+ install_file = install_dir / f"{safe_id}.json"
254
+ install_file.write_text(
255
+ json.dumps(tpl.to_dict(), indent=2, ensure_ascii=False),
256
+ encoding='utf-8'
257
+ )
258
+
259
+ logger.info("Installed template: %s (%s)", tpl.name, template_id)
260
+ return tpl
261
+
262
+ def rate(self, template_id: str, user_id: str, score: float,
263
+ comment: str = "") -> bool:
264
+ """
265
+ Rate a template.
266
+
267
+ Args:
268
+ template_id: Template ID to rate
269
+ user_id: Rater's user ID
270
+ score: Rating score (0-5)
271
+ comment: Optional comment
272
+
273
+ Returns:
274
+ True if rating was recorded, False if template not found
275
+ """
276
+ tpl = self._templates.get(template_id)
277
+ if not tpl:
278
+ return False
279
+
280
+ if score < 0 or score > 5:
281
+ raise ValueError("Score must be between 0 and 5")
282
+
283
+ rating = TemplateRating(
284
+ template_id=template_id,
285
+ user_id=user_id,
286
+ score=score,
287
+ comment=comment,
288
+ )
289
+
290
+ if template_id not in self._ratings:
291
+ self._ratings[template_id] = []
292
+ self._ratings[template_id].append(rating)
293
+
294
+ all_scores = [r.score for r in self._ratings[template_id]]
295
+ tpl.rating = round(sum(all_scores) / len(all_scores), 1)
296
+ tpl.updated_at = datetime.now().isoformat()
297
+
298
+ self._save_to_disk()
299
+ return True
300
+
301
+ def list_categories(self) -> List[str]:
302
+ """List all available template categories."""
303
+ return sorted(set(tpl.category for tpl in self._templates.values()))
304
+
305
+ def get_popular(self, limit: int = 10) -> List[RoleTemplate]:
306
+ """Get most popular templates by install count."""
307
+ sorted_tpls = sorted(
308
+ self._templates.values(),
309
+ key=lambda t: t.install_count,
310
+ reverse=True
311
+ )
312
+ return sorted_tpls[:limit]
313
+
314
+ def export_template(self, template_id: str, output_path: str) -> bool:
315
+ """Export a template to a JSON file."""
316
+ tpl = self._templates.get(template_id)
317
+ if not tpl:
318
+ return False
319
+ try:
320
+ Path(output_path).write_text(
321
+ json.dumps(tpl.to_dict(), indent=2, ensure_ascii=False),
322
+ encoding='utf-8'
323
+ )
324
+ return True
325
+ except Exception as e:
326
+ logger.warning("Export failed: %s", e)
327
+ return False
328
+
329
+ def import_template(self, input_path: str) -> Optional[str]:
330
+ """Import a template from a JSON file."""
331
+ try:
332
+ data = json.loads(Path(input_path).read_text(encoding='utf-8'))
333
+ tpl = RoleTemplate.from_dict(data)
334
+ return self.publish(tpl)
335
+ except Exception as e:
336
+ logger.warning("Import failed: %s", e)
337
+ return None
338
+
339
+ def get_stats(self) -> Dict[str, Any]:
340
+ """Get market statistics."""
341
+ return {
342
+ "total_templates": len(self._templates),
343
+ "total_categories": len(self.list_categories()),
344
+ "total_ratings": sum(len(r) for r in self._ratings.values()),
345
+ "total_installs": sum(t.install_count for t in self._templates.values()),
346
+ "avg_rating": round(
347
+ sum(t.rating for t in self._templates.values()) / len(self._templates), 1
348
+ ) if self._templates else 0.0,
349
+ }
350
+
351
+
352
+ __all__ = ["RoleTemplate", "TemplateRating", "RoleTemplateMarket"]