tapps-agents 3.5.39__py3-none-any.whl → 3.5.40__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 (70) hide show
  1. tapps_agents/__init__.py +2 -2
  2. tapps_agents/agents/enhancer/agent.py +2728 -2728
  3. tapps_agents/agents/implementer/agent.py +35 -13
  4. tapps_agents/agents/reviewer/agent.py +43 -10
  5. tapps_agents/agents/reviewer/scoring.py +59 -68
  6. tapps_agents/agents/reviewer/tools/__init__.py +24 -0
  7. tapps_agents/agents/reviewer/tools/ruff_grouping.py +250 -0
  8. tapps_agents/agents/reviewer/tools/scoped_mypy.py +284 -0
  9. tapps_agents/beads/__init__.py +11 -0
  10. tapps_agents/beads/hydration.py +213 -0
  11. tapps_agents/beads/specs.py +206 -0
  12. tapps_agents/cli/commands/health.py +19 -3
  13. tapps_agents/cli/commands/simple_mode.py +842 -676
  14. tapps_agents/cli/commands/task.py +219 -0
  15. tapps_agents/cli/commands/top_level.py +13 -0
  16. tapps_agents/cli/main.py +658 -651
  17. tapps_agents/cli/parsers/top_level.py +1978 -1881
  18. tapps_agents/core/config.py +1622 -1622
  19. tapps_agents/core/init_project.py +3012 -2897
  20. tapps_agents/epic/markdown_sync.py +105 -0
  21. tapps_agents/epic/orchestrator.py +1 -2
  22. tapps_agents/epic/parser.py +427 -423
  23. tapps_agents/experts/adaptive_domain_detector.py +0 -2
  24. tapps_agents/experts/knowledge/api-design-integration/api-security-patterns.md +15 -15
  25. tapps_agents/experts/knowledge/api-design-integration/external-api-integration.md +19 -44
  26. tapps_agents/health/checks/outcomes.backup_20260204_064058.py +324 -0
  27. tapps_agents/health/checks/outcomes.backup_20260204_064256.py +324 -0
  28. tapps_agents/health/checks/outcomes.backup_20260204_064600.py +324 -0
  29. tapps_agents/health/checks/outcomes.py +134 -46
  30. tapps_agents/health/orchestrator.py +12 -4
  31. tapps_agents/hooks/__init__.py +33 -0
  32. tapps_agents/hooks/config.py +140 -0
  33. tapps_agents/hooks/events.py +135 -0
  34. tapps_agents/hooks/executor.py +128 -0
  35. tapps_agents/hooks/manager.py +143 -0
  36. tapps_agents/session/__init__.py +19 -0
  37. tapps_agents/session/manager.py +256 -0
  38. tapps_agents/simple_mode/code_snippet_handler.py +382 -0
  39. tapps_agents/simple_mode/intent_parser.py +29 -4
  40. tapps_agents/simple_mode/orchestrators/base.py +185 -59
  41. tapps_agents/simple_mode/orchestrators/build_orchestrator.py +2667 -2642
  42. tapps_agents/simple_mode/orchestrators/fix_orchestrator.py +2 -2
  43. tapps_agents/simple_mode/workflow_suggester.py +37 -3
  44. tapps_agents/workflow/agent_handlers/implementer_handler.py +18 -3
  45. tapps_agents/workflow/cursor_executor.py +2196 -2118
  46. tapps_agents/workflow/direct_execution_fallback.py +16 -3
  47. tapps_agents/workflow/message_formatter.py +2 -1
  48. tapps_agents/workflow/parallel_executor.py +43 -4
  49. tapps_agents/workflow/parser.py +375 -357
  50. tapps_agents/workflow/rules_generator.py +337 -337
  51. tapps_agents/workflow/skill_invoker.py +9 -3
  52. {tapps_agents-3.5.39.dist-info → tapps_agents-3.5.40.dist-info}/METADATA +5 -1
  53. {tapps_agents-3.5.39.dist-info → tapps_agents-3.5.40.dist-info}/RECORD +57 -53
  54. tapps_agents/agents/analyst/SKILL.md +0 -85
  55. tapps_agents/agents/architect/SKILL.md +0 -80
  56. tapps_agents/agents/debugger/SKILL.md +0 -66
  57. tapps_agents/agents/designer/SKILL.md +0 -78
  58. tapps_agents/agents/documenter/SKILL.md +0 -95
  59. tapps_agents/agents/enhancer/SKILL.md +0 -189
  60. tapps_agents/agents/implementer/SKILL.md +0 -117
  61. tapps_agents/agents/improver/SKILL.md +0 -55
  62. tapps_agents/agents/ops/SKILL.md +0 -64
  63. tapps_agents/agents/orchestrator/SKILL.md +0 -238
  64. tapps_agents/agents/planner/story_template.md +0 -37
  65. tapps_agents/agents/reviewer/templates/quality-dashboard.html.j2 +0 -150
  66. tapps_agents/agents/tester/SKILL.md +0 -71
  67. {tapps_agents-3.5.39.dist-info → tapps_agents-3.5.40.dist-info}/WHEEL +0 -0
  68. {tapps_agents-3.5.39.dist-info → tapps_agents-3.5.40.dist-info}/entry_points.txt +0 -0
  69. {tapps_agents-3.5.39.dist-info → tapps_agents-3.5.40.dist-info}/licenses/LICENSE +0 -0
  70. {tapps_agents-3.5.39.dist-info → tapps_agents-3.5.40.dist-info}/top_level.txt +0 -0
@@ -1,1622 +1,1622 @@
1
- """
2
- Configuration management for TappsCodingAgents.
3
-
4
- Provides Pydantic models for type-safe configuration and YAML loading.
5
- """
6
-
7
- from pathlib import Path
8
- from typing import Any
9
-
10
- import yaml
11
- from pydantic import BaseModel, Field, model_validator
12
-
13
-
14
- class ScoringWeightsConfig(BaseModel):
15
- """Configuration for code scoring weights (must sum to 1.0). 7-category: Structure, DevEx added (MCP_SYSTEMS_IMPROVEMENT_RECOMMENDATIONS §3.2)."""
16
-
17
- complexity: float = Field(
18
- default=0.18, ge=0.0, le=1.0, description="Weight for complexity score"
19
- )
20
- security: float = Field(
21
- default=0.27, ge=0.0, le=1.0, description="Weight for security score"
22
- )
23
- maintainability: float = Field(
24
- default=0.24, ge=0.0, le=1.0, description="Weight for maintainability score"
25
- )
26
- test_coverage: float = Field(
27
- default=0.13, ge=0.0, le=1.0, description="Weight for test coverage score"
28
- )
29
- performance: float = Field(
30
- default=0.08, ge=0.0, le=1.0, description="Weight for performance score"
31
- )
32
- structure: float = Field(
33
- default=0.05, ge=0.0, le=1.0, description="Weight for structure score (project layout, key files)"
34
- )
35
- devex: float = Field(
36
- default=0.05, ge=0.0, le=1.0, description="Weight for developer experience score (docs, config, tooling)"
37
- )
38
-
39
- @model_validator(mode="after")
40
- def validate_weights_sum(self):
41
- """Ensure weights sum to approximately 1.0"""
42
- total = (
43
- self.complexity
44
- + self.security
45
- + self.maintainability
46
- + self.test_coverage
47
- + self.performance
48
- + self.structure
49
- + self.devex
50
- )
51
- if abs(total - 1.0) > 0.01: # Allow small floating point errors
52
- raise ValueError(f"Scoring weights must sum to 1.0, got {total}")
53
- return self
54
-
55
-
56
- class TestCoverageConfig(BaseModel):
57
- """Test coverage configuration for quality gates."""
58
-
59
- enabled: bool = Field(default=True, description="Enable test coverage checks")
60
- threshold: float = Field(
61
- default=0.8, ge=0.0, le=1.0, description="Minimum coverage threshold (0.0-1.0)"
62
- )
63
- critical_services_threshold: float = Field(
64
- default=0.8,
65
- ge=0.0,
66
- le=1.0,
67
- description="Minimum coverage for critical services (0.0-1.0)",
68
- )
69
- warning_threshold: float = Field(
70
- default=0.6,
71
- ge=0.0,
72
- le=1.0,
73
- description="Coverage threshold for warnings (0.0-1.0)",
74
- )
75
-
76
-
77
- class QualityGatesConfig(BaseModel):
78
- """Quality gates configuration."""
79
-
80
- enabled: bool = Field(default=True, description="Enable quality gates")
81
- test_coverage: TestCoverageConfig = Field(
82
- default_factory=TestCoverageConfig, description="Test coverage configuration"
83
- )
84
-
85
-
86
- class ScoringConfig(BaseModel):
87
- """Configuration for code scoring system"""
88
-
89
- weights: ScoringWeightsConfig = Field(default_factory=ScoringWeightsConfig)
90
- quality_threshold: float = Field(
91
- default=70.0, ge=0.0, le=100.0, description="Minimum overall score to pass"
92
- )
93
- quality_gates: QualityGatesConfig = Field(
94
- default_factory=QualityGatesConfig, description="Quality gates configuration"
95
- )
96
-
97
-
98
- class ReviewerAgentContext7Config(BaseModel):
99
- """Context7 configuration for Reviewer Agent"""
100
-
101
- auto_detect: bool = Field(
102
- default=True, description="Enable automatic library detection"
103
- )
104
- topics: list[str] = Field(
105
- default_factory=lambda: ["best-practices", "routing", "api-design"],
106
- description="Default topics to fetch for detected libraries"
107
- )
108
-
109
-
110
- class ReviewerAgentConfig(BaseModel):
111
- """Configuration specific to Reviewer Agent"""
112
-
113
- quality_threshold: float = Field(
114
- default=70.0, ge=0.0, le=100.0, description="Minimum score to pass review"
115
- )
116
- include_scoring: bool = Field(
117
- default=True, description="Include code scoring in review"
118
- )
119
- include_llm_feedback: bool = Field(
120
- default=True, description="Include LLM-generated feedback"
121
- )
122
- max_file_size: int = Field(
123
- default=1024 * 1024,
124
- ge=1024,
125
- description="Maximum file size in bytes (1MB default)",
126
- )
127
- min_confidence_threshold: float = Field(
128
- default=0.8,
129
- ge=0.0,
130
- le=1.0,
131
- description="Minimum expert confidence threshold (0.0-1.0)",
132
- )
133
- operation_timeout: float = Field(
134
- default=300.0,
135
- ge=10.0,
136
- description="Timeout in seconds for reviewer operations (default: 5 minutes)",
137
- )
138
- tool_timeout: float = Field(
139
- default=30.0,
140
- ge=5.0,
141
- description="Timeout in seconds for individual quality tools (default: 30 seconds)",
142
- )
143
- enable_parallel_tools: bool = Field(
144
- default=True,
145
- description="Enable parallel execution of quality tools (Ruff, mypy, bandit)",
146
- )
147
- context7: ReviewerAgentContext7Config = Field(
148
- default_factory=ReviewerAgentContext7Config,
149
- description="Context7 integration settings for Reviewer Agent"
150
- )
151
- # NEW: Library detection config
152
- auto_library_detection: bool = Field(
153
- default=True,
154
- description="Automatically detect libraries from code and dependency files"
155
- )
156
- library_detection_depth: str = Field(
157
- default="both",
158
- description="What to detect: 'code' (imports only), 'dependencies' (requirements.txt/pyproject.toml), or 'both'"
159
- )
160
- # NEW: Context7 integration config
161
- auto_context7_lookups: bool = Field(
162
- default=True,
163
- description="Automatically lookup library documentation from Context7"
164
- )
165
- context7_timeout: int = Field(
166
- default=30,
167
- ge=1,
168
- le=300,
169
- description="Timeout for Context7 lookups in seconds"
170
- )
171
- context7_cache_enabled: bool = Field(
172
- default=True,
173
- description="Cache Context7 responses to avoid duplicate lookups"
174
- )
175
- # NEW: Pattern detection config
176
- pattern_detection_enabled: bool = Field(
177
- default=True,
178
- description="Enable domain-specific pattern detection"
179
- )
180
- pattern_confidence_threshold: float = Field(
181
- default=0.5,
182
- ge=0.0,
183
- le=1.0,
184
- description="Minimum confidence to report detected patterns (0.0-1.0)"
185
- )
186
-
187
-
188
- class PlannerAgentConfig(BaseModel):
189
- """Configuration specific to Planner Agent"""
190
-
191
- stories_dir: str | None = Field(
192
- default=None, description="Directory for storing stories (default: stories/)"
193
- )
194
- default_priority: str = Field(
195
- default="medium",
196
- description="Default priority for new stories (high/medium/low)",
197
- )
198
- min_confidence_threshold: float = Field(
199
- default=0.6,
200
- ge=0.0,
201
- le=1.0,
202
- description="Minimum expert confidence threshold (0.0-1.0)",
203
- )
204
-
205
-
206
- class ImplementerAgentContext7Config(BaseModel):
207
- """Context7 configuration for Implementer Agent"""
208
-
209
- auto_detect: bool = Field(
210
- default=True, description="Enable automatic library detection"
211
- )
212
- detect_from_prompt: bool = Field(
213
- default=True, description="Detect libraries from prompt/specification text"
214
- )
215
-
216
-
217
- class ImplementerAgentConfig(BaseModel):
218
- """Configuration specific to Implementer Agent"""
219
-
220
- require_review: bool = Field(
221
- default=True, description="Require code review before writing files"
222
- )
223
- auto_approve_threshold: float = Field(
224
- default=80.0, ge=0.0, le=100.0, description="Auto-approve if score >= threshold"
225
- )
226
- backup_files: bool = Field(
227
- default=True, description="Create backup before overwriting existing files"
228
- )
229
- max_file_size: int = Field(
230
- default=10 * 1024 * 1024,
231
- ge=1024,
232
- description="Maximum file size in bytes (10MB default)",
233
- )
234
- min_confidence_threshold: float = Field(
235
- default=0.7,
236
- ge=0.0,
237
- le=1.0,
238
- description="Minimum expert confidence threshold (0.0-1.0)",
239
- )
240
- context7: ImplementerAgentContext7Config = Field(
241
- default_factory=ImplementerAgentContext7Config,
242
- description="Context7 integration settings for Implementer Agent"
243
- )
244
-
245
-
246
- class TesterAgentConfig(BaseModel):
247
- """Configuration specific to Tester Agent"""
248
-
249
- test_framework: str = Field(
250
- default="pytest", description="Test framework to use (pytest/unittest)"
251
- )
252
- tests_dir: str | None = Field(
253
- default=None, description="Directory for tests (default: tests/)"
254
- )
255
- coverage_threshold: float = Field(
256
- default=80.0, ge=0.0, le=100.0, description="Target test coverage percentage"
257
- )
258
- auto_write_tests: bool = Field(
259
- default=True, description="Automatically write generated tests to files"
260
- )
261
- min_confidence_threshold: float = Field(
262
- default=0.7,
263
- ge=0.0,
264
- le=1.0,
265
- description="Minimum expert confidence threshold (0.0-1.0)",
266
- )
267
-
268
-
269
- class DebuggerAgentContext7Config(BaseModel):
270
- """Context7 configuration for Debugger Agent"""
271
-
272
- auto_detect: bool = Field(
273
- default=True, description="Enable automatic library detection"
274
- )
275
- detect_from_errors: bool = Field(
276
- default=True, description="Detect libraries from error messages and stack traces"
277
- )
278
-
279
-
280
- class DebuggerAgentConfig(BaseModel):
281
- """Configuration specific to Debugger Agent"""
282
-
283
- include_code_examples: bool = Field(
284
- default=True, description="Include code examples in fix suggestions"
285
- )
286
- max_context_lines: int = Field(
287
- default=50,
288
- ge=10,
289
- le=200,
290
- description="Maximum lines of code context to include in analysis",
291
- )
292
- min_confidence_threshold: float = Field(
293
- default=0.7,
294
- ge=0.0,
295
- le=1.0,
296
- description="Minimum expert confidence threshold (0.0-1.0)",
297
- )
298
- context7: DebuggerAgentContext7Config = Field(
299
- default_factory=DebuggerAgentContext7Config,
300
- description="Context7 integration settings for Debugger Agent"
301
- )
302
-
303
-
304
- class DocumenterAgentConfig(BaseModel):
305
- """Configuration specific to Documenter Agent"""
306
-
307
- docs_dir: str | None = Field(
308
- default=None, description="Directory for generated docs (default: docs/)"
309
- )
310
- include_examples: bool = Field(
311
- default=True, description="Include code examples in documentation"
312
- )
313
- docstring_format: str = Field(
314
- default="google", description="Docstring format (google/numpy/sphinx)"
315
- )
316
- min_confidence_threshold: float = Field(
317
- default=0.5,
318
- ge=0.0,
319
- le=1.0,
320
- description="Minimum expert confidence threshold (0.0-1.0)",
321
- )
322
-
323
-
324
- class Context7KnowledgeBaseConfig(BaseModel):
325
- """Configuration for Context7 knowledge base caching"""
326
-
327
- enabled: bool = Field(default=True, description="Enable KB caching")
328
- location: str = Field(
329
- default=".tapps-agents/kb/context7-cache", description="KB cache directory"
330
- )
331
- sharding: bool = Field(default=True, description="Enable library-based sharding")
332
- indexing: bool = Field(default=True, description="Enable indexing")
333
- max_cache_size: str = Field(default="100MB", description="Maximum cache size")
334
- hit_rate_threshold: float = Field(
335
- default=0.7, ge=0.0, le=1.0, description="Target hit rate threshold"
336
- )
337
- fuzzy_match_threshold: float = Field(
338
- default=0.7, ge=0.0, le=1.0, description="Fuzzy match confidence threshold"
339
- )
340
-
341
-
342
- class Context7RefreshConfig(BaseModel):
343
- """Configuration for Context7 auto-refresh system"""
344
-
345
- enabled: bool = Field(default=True, description="Enable auto-refresh")
346
- default_max_age_days: int = Field(
347
- default=30, ge=1, description="Default max age for cache entries (days)"
348
- )
349
- check_on_access: bool = Field(default=True, description="Check staleness on access")
350
- auto_queue: bool = Field(
351
- default=True, description="Automatically queue stale entries"
352
- )
353
- auto_process_on_startup: bool = Field(
354
- default=False, description="Process queue on agent startup"
355
- )
356
-
357
-
358
- class Context7Config(BaseModel):
359
- """Configuration for Context7 integration"""
360
-
361
- enabled: bool = Field(default=True, description="Enable Context7 integration")
362
- default_token_limit: int = Field(
363
- default=3000, ge=100, description="Default token limit for Context7 docs"
364
- )
365
- cache_duration: int = Field(
366
- default=3600, ge=0, description="Cache duration in seconds"
367
- )
368
- integration_level: str = Field(
369
- default="optional", description="Integration level (mandatory/optional)"
370
- )
371
- usage_requirement: str | None = Field(
372
- default=None, description="Usage requirement description"
373
- )
374
- bypass_forbidden: bool = Field(
375
- default=True, description="Allow bypassing if Context7 unavailable"
376
- )
377
- # Enhancement: Automatic Context7 detection and fetching
378
- auto_detect: bool = Field(
379
- default=True, description="Enable automatic library detection from code, prompts, and errors"
380
- )
381
- auto_fetch: bool = Field(
382
- default=True, description="Automatically fetch docs for detected libraries"
383
- )
384
- proactive_suggestions: bool = Field(
385
- default=True, description="Proactively suggest Context7 lookups when library patterns detected"
386
- )
387
-
388
- knowledge_base: Context7KnowledgeBaseConfig = Field(
389
- default_factory=Context7KnowledgeBaseConfig
390
- )
391
- refresh: Context7RefreshConfig = Field(default_factory=Context7RefreshConfig)
392
-
393
-
394
- class QualityToolsConfig(BaseModel):
395
- """Configuration for quality analysis tools (Phase 6 - 2025 Standards)"""
396
-
397
- # Ruff configuration
398
- ruff_enabled: bool = Field(default=True, description="Enable Ruff linting")
399
- ruff_config_path: str | None = Field(
400
- default=None,
401
- description="Path to ruff.toml or pyproject.toml (auto-detected if None)",
402
- )
403
-
404
- # mypy configuration
405
- mypy_enabled: bool = Field(default=True, description="Enable mypy type checking")
406
- mypy_strict: bool = Field(default=False, description="Enable strict mode for mypy")
407
- mypy_config_path: str | None = Field(
408
- default=None,
409
- description="Path to mypy.ini or pyproject.toml (auto-detected if None)",
410
- )
411
-
412
- # jscpd configuration
413
- jscpd_enabled: bool = Field(
414
- default=True, description="Enable jscpd duplication detection"
415
- )
416
- duplication_threshold: float = Field(
417
- default=3.0,
418
- ge=0.0,
419
- le=100.0,
420
- description="Maximum duplication percentage threshold",
421
- )
422
- min_duplication_lines: int = Field(
423
- default=5, ge=3, description="Minimum lines for duplication detection"
424
- )
425
-
426
- # TypeScript/JavaScript configuration
427
- typescript_enabled: bool = Field(
428
- default=True, description="Enable TypeScript/JavaScript support"
429
- )
430
- eslint_config: str | None = Field(
431
- default=None, description="Path to ESLint config file"
432
- )
433
- tsconfig_path: str | None = Field(default=None, description="Path to tsconfig.json")
434
-
435
- # Dependency security auditing
436
- pip_audit_enabled: bool = Field(
437
- default=True, description="Enable pip-audit security scanning"
438
- )
439
- dependency_audit_threshold: str = Field(
440
- default="high",
441
- description="Minimum severity threshold (low/medium/high/critical)",
442
- )
443
-
444
-
445
- class ToolingTargetsConfig(BaseModel):
446
- """Pin runtime/tool targets so agents and CI behave deterministically."""
447
-
448
- python: str = Field(
449
- default="3.13.3",
450
- description="Target Python version for this project (pin exact patch where possible)",
451
- )
452
- python_requires: str = Field(
453
- default=">=3.13",
454
- description="PEP 440 requires-python constraint enforced by packaging/CI",
455
- )
456
- os_targets: list[str] = Field(
457
- default_factory=lambda: ["windows", "linux"],
458
- description="Primary OS targets (used for setup/doctor guidance)",
459
- )
460
- node: str | None = Field(
461
- default=None,
462
- description="Optional Node.js version (only needed if TypeScript/jscpd tooling is enabled)",
463
- )
464
-
465
-
466
- class ToolingPolicyConfig(BaseModel):
467
- """Policy for how missing/optional tools are handled."""
468
-
469
- external_tools_mode: str = Field(
470
- default="soft",
471
- description="How to handle missing external tools: soft=warn/skip, hard=fail",
472
- )
473
- mypy_staged: bool = Field(
474
- default=True, description="Stage mypy enforcement module-by-module"
475
- )
476
- mypy_stage_paths: list[str] = Field(
477
- default_factory=lambda: [
478
- "tapps_agents/core",
479
- "tapps_agents/workflow",
480
- "tapps_agents/context7",
481
- ],
482
- description="Paths enforced by mypy during the staged rollout",
483
- )
484
-
485
-
486
- class ToolingConfig(BaseModel):
487
- """Canonical tooling/targets configuration (single source of truth)."""
488
-
489
- targets: ToolingTargetsConfig = Field(default_factory=ToolingTargetsConfig)
490
- policy: ToolingPolicyConfig = Field(default_factory=ToolingPolicyConfig)
491
-
492
-
493
- class ArchitectAgentConfig(BaseModel):
494
- """Configuration specific to Architect Agent"""
495
-
496
- min_confidence_threshold: float = Field(
497
- default=0.75,
498
- ge=0.0,
499
- le=1.0,
500
- description="Minimum expert confidence threshold (0.0-1.0)",
501
- )
502
-
503
-
504
- class DesignerAgentConfig(BaseModel):
505
- """Configuration specific to Designer Agent"""
506
-
507
- min_confidence_threshold: float = Field(
508
- default=0.65,
509
- ge=0.0,
510
- le=1.0,
511
- description="Minimum expert confidence threshold (0.0-1.0)",
512
- )
513
-
514
-
515
- class OpsAgentConfig(BaseModel):
516
- """Configuration specific to Ops Agent"""
517
-
518
- min_confidence_threshold: float = Field(
519
- default=0.75,
520
- ge=0.0,
521
- le=1.0,
522
- description="Minimum expert confidence threshold (0.0-1.0)",
523
- )
524
-
525
-
526
- class EnhancerAgentConfig(BaseModel):
527
- """Configuration specific to Enhancer Agent"""
528
-
529
- min_confidence_threshold: float = Field(
530
- default=0.6,
531
- ge=0.0,
532
- le=1.0,
533
- description="Minimum expert confidence threshold (0.0-1.0)",
534
- )
535
-
536
-
537
- class AnalystAgentConfig(BaseModel):
538
- """Configuration specific to Analyst Agent"""
539
-
540
- min_confidence_threshold: float = Field(
541
- default=0.65,
542
- ge=0.0,
543
- le=1.0,
544
- description="Minimum expert confidence threshold (0.0-1.0)",
545
- )
546
-
547
-
548
- class OrchestratorAgentConfig(BaseModel):
549
- """Configuration specific to Orchestrator Agent"""
550
-
551
- min_confidence_threshold: float = Field(
552
- default=0.6,
553
- ge=0.0,
554
- le=1.0,
555
- description="Minimum expert confidence threshold (0.0-1.0)",
556
- )
557
-
558
-
559
- class EvaluatorAgentConfig(BaseModel):
560
- """Configuration specific to Evaluator Agent"""
561
-
562
- auto_run: bool = Field(
563
- default=False,
564
- description="Run evaluator automatically at end of workflows (build, full, etc.)"
565
- )
566
- output_dir: str = Field(
567
- default=".tapps-agents/evaluations",
568
- description="Directory for evaluation reports"
569
- )
570
- thresholds: dict[str, float] = Field(
571
- default_factory=lambda: {
572
- "quality_score": 70.0,
573
- "workflow_completion": 0.8,
574
- },
575
- description="Thresholds for evaluation metrics"
576
- )
577
-
578
-
579
- class CleanupAgentConfig(BaseModel):
580
- """Configuration specific to Cleanup Agent"""
581
-
582
- dry_run_default: bool = Field(
583
- default=True,
584
- description="Run in dry-run mode by default (preview changes without executing)"
585
- )
586
- backup_enabled: bool = Field(
587
- default=True,
588
- description="Create backups before destructive operations"
589
- )
590
- interactive_mode: bool = Field(
591
- default=True,
592
- description="Prompt for confirmation on risky operations"
593
- )
594
- default_pattern: str = Field(
595
- default="*.md",
596
- description="Default file pattern for analysis"
597
- )
598
- age_threshold_days: int = Field(
599
- default=90,
600
- ge=1,
601
- le=365,
602
- description="Days threshold for detecting outdated files"
603
- )
604
- similarity_threshold: float = Field(
605
- default=0.8,
606
- ge=0.0,
607
- le=1.0,
608
- description="Content similarity threshold for detecting near-duplicates"
609
- )
610
- backup_dir: str = Field(
611
- default=".cleanup-backups",
612
- description="Directory for cleanup backups"
613
- )
614
-
615
-
616
- class ExpertConfig(BaseModel):
617
- """Configuration for expert consultation system"""
618
-
619
- # Agent-specific confidence thresholds
620
- agent_confidence_thresholds: dict[str, float] = Field(
621
- default_factory=lambda: {
622
- "reviewer": 0.8,
623
- "architect": 0.75,
624
- "implementer": 0.7,
625
- "designer": 0.65,
626
- "tester": 0.7,
627
- "ops": 0.75,
628
- "enhancer": 0.6,
629
- "analyst": 0.65,
630
- "planner": 0.6,
631
- "debugger": 0.7,
632
- "documenter": 0.5,
633
- "orchestrator": 0.6,
634
- "default": 0.7,
635
- },
636
- description="Agent-specific confidence thresholds (0.0-1.0)",
637
- )
638
-
639
- # Confidence calculation weights
640
- weight_max_confidence: float = Field(
641
- default=0.35, ge=0.0, le=1.0, description="Weight for maximum expert confidence"
642
- )
643
- weight_agreement: float = Field(
644
- default=0.25, ge=0.0, le=1.0, description="Weight for expert agreement level"
645
- )
646
- weight_rag_quality: float = Field(
647
- default=0.2, ge=0.0, le=1.0, description="Weight for RAG knowledge base quality"
648
- )
649
- weight_domain_relevance: float = Field(
650
- default=0.1, ge=0.0, le=1.0, description="Weight for domain relevance"
651
- )
652
- weight_project_context: float = Field(
653
- default=0.1, ge=0.0, le=1.0, description="Weight for project context relevance"
654
- )
655
-
656
- # Agreement and similarity thresholds
657
- high_agreement_threshold: float = Field(
658
- default=0.75,
659
- ge=0.0,
660
- le=1.0,
661
- description="High agreement threshold for expert consensus (0.0-1.0)",
662
- )
663
- similarity_threshold: float = Field(
664
- default=0.6,
665
- ge=0.0,
666
- le=1.0,
667
- description="Similarity threshold for expert response comparison (0.0-1.0)",
668
- )
669
-
670
- # RAG (Retrieval-Augmented Generation) parameters
671
- rag_max_length: int = Field(
672
- default=2000,
673
- ge=100,
674
- le=10000,
675
- description="Maximum length for RAG context retrieval (characters)",
676
- )
677
- rag_max_results: int = Field(
678
- default=8, ge=1, le=20, description="Maximum number of RAG results to retrieve"
679
- )
680
- rag_default_quality: float = Field(
681
- default=0.8,
682
- ge=0.0,
683
- le=1.0,
684
- description="Default RAG quality score when not provided (0.0-1.0)",
685
- )
686
-
687
- # Profile confidence threshold
688
- profile_confidence_threshold: float = Field(
689
- default=0.7,
690
- ge=0.0,
691
- le=1.0,
692
- description="Minimum confidence threshold for including profile values (0.0-1.0)",
693
- )
694
-
695
- # Supporting expert weight
696
- supporting_expert_weight: float = Field(
697
- default=0.15,
698
- ge=0.0,
699
- le=1.0,
700
- description="Approximate weight for supporting experts in agreement calculation",
701
- )
702
-
703
- @model_validator(mode="after")
704
- def validate_weights_sum(self):
705
- """Ensure confidence weights sum to approximately 1.0"""
706
- total = (
707
- self.weight_max_confidence
708
- + self.weight_agreement
709
- + self.weight_rag_quality
710
- + self.weight_domain_relevance
711
- + self.weight_project_context
712
- )
713
- if abs(total - 1.0) > 0.01: # Allow small floating point errors
714
- raise ValueError(f"Expert confidence weights must sum to 1.0, got {total}")
715
- return self
716
-
717
-
718
- class AgentsConfig(BaseModel):
719
- """Configuration for all agents"""
720
-
721
- reviewer: ReviewerAgentConfig = Field(default_factory=ReviewerAgentConfig)
722
- planner: PlannerAgentConfig = Field(default_factory=PlannerAgentConfig)
723
- implementer: ImplementerAgentConfig = Field(default_factory=ImplementerAgentConfig)
724
- tester: TesterAgentConfig = Field(default_factory=TesterAgentConfig)
725
- debugger: DebuggerAgentConfig = Field(default_factory=DebuggerAgentConfig)
726
- documenter: DocumenterAgentConfig = Field(default_factory=DocumenterAgentConfig)
727
- architect: ArchitectAgentConfig = Field(default_factory=ArchitectAgentConfig)
728
- designer: DesignerAgentConfig = Field(default_factory=DesignerAgentConfig)
729
- ops: OpsAgentConfig = Field(default_factory=OpsAgentConfig)
730
- enhancer: EnhancerAgentConfig = Field(default_factory=EnhancerAgentConfig)
731
- analyst: AnalystAgentConfig = Field(default_factory=AnalystAgentConfig)
732
- orchestrator: OrchestratorAgentConfig = Field(
733
- default_factory=OrchestratorAgentConfig
734
- )
735
- evaluator: EvaluatorAgentConfig = Field(default_factory=EvaluatorAgentConfig)
736
- cleanup_agent: CleanupAgentConfig = Field(default_factory=CleanupAgentConfig)
737
-
738
-
739
- class CheckpointFrequencyConfig(BaseModel):
740
- """Configuration for checkpoint frequency"""
741
-
742
- mode: str = Field(
743
- default="every_step",
744
- description="Checkpoint frequency mode: every_step, every_n_steps, on_gates, time_based, manual",
745
- )
746
- interval: int = Field(
747
- default=1,
748
- ge=1,
749
- description="Interval for every_n_steps (step count) or time_based (seconds)",
750
- )
751
- enabled: bool = Field(
752
- default=True,
753
- description="Enable checkpointing",
754
- )
755
-
756
-
757
- class StateCleanupPolicyConfig(BaseModel):
758
- """Configuration for state cleanup policies"""
759
-
760
- enabled: bool = Field(
761
- default=True,
762
- description="Enable automatic state cleanup",
763
- )
764
- retention_days: int | None = Field(
765
- default=None,
766
- ge=1,
767
- description="Delete states older than N days (None = no retention limit)",
768
- )
769
- max_size_mb: int | None = Field(
770
- default=None,
771
- ge=1,
772
- description="Maximum total state size in MB (None = no size limit)",
773
- )
774
- cleanup_schedule: str = Field(
775
- default="daily",
776
- description="Cleanup schedule: daily, weekly, monthly, on_startup, manual",
777
- )
778
- keep_latest: int = Field(
779
- default=10,
780
- ge=1,
781
- description="Always keep the N most recent states",
782
- )
783
-
784
-
785
- class StatePersistenceConfig(BaseModel):
786
- """Configuration for state persistence and checkpointing"""
787
-
788
- enabled: bool = Field(
789
- default=True,
790
- description="Enable state persistence",
791
- )
792
- storage_location: str = Field(
793
- default=".tapps-agents/workflow-state",
794
- description="Directory for storing workflow state",
795
- )
796
- format: str = Field(
797
- default="json",
798
- description="State storage format: json, json_gzip",
799
- )
800
- compression: bool = Field(
801
- default=False,
802
- description="Enable compression for state files",
803
- )
804
- checkpoint: CheckpointFrequencyConfig = Field(
805
- default_factory=CheckpointFrequencyConfig,
806
- description="Checkpoint frequency configuration",
807
- )
808
- cleanup: StateCleanupPolicyConfig = Field(
809
- default_factory=StateCleanupPolicyConfig,
810
- description="State cleanup policy configuration",
811
- )
812
-
813
-
814
- class BranchCleanupConfig(BaseModel):
815
- """Configuration for Git branch cleanup after workflow execution"""
816
-
817
- enabled: bool = Field(
818
- default=True,
819
- description="Enable automatic branch cleanup",
820
- )
821
- delete_branches_on_cleanup: bool = Field(
822
- default=True,
823
- description="Delete associated Git branches when worktrees are removed",
824
- )
825
- retention_days: int = Field(
826
- default=7,
827
- ge=0,
828
- description="Number of days to retain branches before cleanup (0 = immediate cleanup)",
829
- )
830
- auto_cleanup_on_completion: bool = Field(
831
- default=True,
832
- description="Automatically cleanup branches when workflow step completes",
833
- )
834
- patterns: dict[str, str] = Field(
835
- default_factory=lambda: {
836
- "workflow": "workflow/*",
837
- "agent": "agent/*",
838
- },
839
- description="Branch patterns to match for cleanup (wildcards supported)",
840
- )
841
-
842
-
843
- class WorkflowDocsCleanupConfig(BaseModel):
844
- """Configuration for workflow documentation cleanup"""
845
-
846
- enabled: bool = Field(
847
- default=True,
848
- description="Enable workflow documentation cleanup",
849
- )
850
- keep_latest: int = Field(
851
- default=5,
852
- ge=1,
853
- le=100,
854
- description="Keep N most recent workflows visible",
855
- )
856
- retention_days: int = Field(
857
- default=30,
858
- ge=1,
859
- description="Archive workflows older than N days",
860
- )
861
- archive_enabled: bool = Field(
862
- default=True,
863
- description="Enable archival of old workflows",
864
- )
865
- archive_dir: Path = Field(
866
- default=Path(".tapps-agents/archives/workflows/"),
867
- description="Directory for archived workflows (relative to project root)",
868
- )
869
- exclude_patterns: list[str] = Field(
870
- default_factory=list,
871
- description="Workflow IDs or patterns to never archive",
872
- )
873
-
874
-
875
- class SessionsCleanupConfig(BaseModel):
876
- """Configuration for .tapps-agents/sessions cleanup (enhancer + SessionManager)."""
877
-
878
- keep_latest: int = Field(
879
- default=50,
880
- ge=1,
881
- le=500,
882
- description="Keep N most recent enhancer session files",
883
- )
884
- max_age_days: int = Field(
885
- default=30,
886
- ge=1,
887
- description="Remove enhancer sessions older than N days; also used for SessionManager cleanup (hours = max_age_days * 24)",
888
- )
889
- auto_cleanup_on_enhance: bool = Field(
890
- default=False,
891
- description="When True, run sessions cleanup after each enhancer save so the folder does not grow unbounded",
892
- )
893
-
894
-
895
- class CleanupConfig(BaseModel):
896
- """Configuration for cleanup operations"""
897
-
898
- workflow_docs: WorkflowDocsCleanupConfig = Field(
899
- default_factory=WorkflowDocsCleanupConfig,
900
- description="Workflow documentation cleanup configuration",
901
- )
902
- sessions: SessionsCleanupConfig = Field(
903
- default_factory=SessionsCleanupConfig,
904
- description="Sessions cleanup (enhancer + long-running agent sessions)",
905
- )
906
-
907
-
908
- class WorkflowArtifactConfig(BaseModel):
909
- """Configuration for workflow artifact paths and naming."""
910
-
911
- base_dir: str = Field(
912
- default="docs/workflows",
913
- description="Base directory for workflow artifacts (relative to project root)",
914
- )
915
- simple_mode_subdir: str = Field(
916
- default="simple-mode",
917
- description="Subdirectory for Simple Mode workflow artifacts",
918
- )
919
- auto_detect_existing: bool = Field(
920
- default=True,
921
- description="Auto-detect existing artifacts and adjust paths to avoid conflicts",
922
- )
923
- naming_pattern: str = Field(
924
- default="{workflow_id}/{step_name}.md",
925
- description="Pattern for artifact file naming. Supports: {workflow_id}, {step_name}, {timestamp}, {agent}",
926
- )
927
- print_paths_on_completion: bool = Field(
928
- default=True,
929
- description="Print artifact file paths after each step completes",
930
- )
931
-
932
- def get_artifact_dir(self, project_root: str | None = None) -> str:
933
- """Get the full artifact directory path."""
934
- from pathlib import Path
935
- base = Path(project_root) if project_root else Path.cwd()
936
- return str(base / self.base_dir / self.simple_mode_subdir)
937
-
938
-
939
- class WorkflowFailureConfig(BaseModel):
940
- """On-step-failure behavior: retry, skip, escalate, or fail (plan 3.1)."""
941
-
942
- on_step_fail: str = Field(
943
- default="fail",
944
- description="Behavior on step failure: 'fail' | 'retry' | 'skip' | 'escalate'.",
945
- )
946
- retry_count: int = Field(
947
- default=1,
948
- ge=0,
949
- le=10,
950
- description="Max retries when on_step_fail=='retry'.",
951
- )
952
- escalate_to_pause: bool = Field(
953
- default=True,
954
- description="When on_step_fail is 'escalate' or 'fail', set status to 'paused' (True) or 'failed' (False).",
955
- )
956
-
957
-
958
- class WorkflowConfig(BaseModel):
959
- """Configuration for workflow execution"""
960
-
961
- failure: WorkflowFailureConfig = Field(
962
- default_factory=WorkflowFailureConfig,
963
- description="On-step-failure behavior: retry, skip, escalate (plan 3.1).",
964
- )
965
- polling_interval: float = Field(
966
- default=5.0,
967
- ge=1.0,
968
- description="Seconds between status checks when polling for completion",
969
- )
970
- timeout_seconds: float = Field(
971
- default=3600.0,
972
- ge=1.0,
973
- description="Maximum time to wait for step completion (seconds)",
974
- )
975
- duration_threshold_seconds: float = Field(
976
- default=30.0,
977
- ge=1.0,
978
- description="Duration threshold for background agent routing (seconds). Tasks estimated to take longer use background agents.",
979
- )
980
- state_persistence: StatePersistenceConfig = Field(
981
- default_factory=StatePersistenceConfig,
982
- description="State persistence and checkpointing configuration",
983
- )
984
- branch_cleanup: BranchCleanupConfig = Field(
985
- default_factory=BranchCleanupConfig,
986
- description="Git branch cleanup configuration for workflow worktrees",
987
- )
988
- artifacts: WorkflowArtifactConfig = Field(
989
- default_factory=WorkflowArtifactConfig,
990
- description="Artifact paths and naming configuration",
991
- )
992
- graceful_partial_completion: bool = Field(
993
- default=True,
994
- description="Save partial results on failure instead of losing all progress",
995
- )
996
- pre_flight_validation: bool = Field(
997
- default=True,
998
- description="Run pre-flight validation before workflow execution",
999
- )
1000
-
1001
-
1002
- class ContextBudgetConfig(BaseModel):
1003
- """Context budget when assembling Context7 and expert chunks (plan 3.2)."""
1004
-
1005
- max_tokens_per_step: int = Field(
1006
- default=0,
1007
- ge=0,
1008
- description="Max tokens per step (0=no hard limit).",
1009
- )
1010
- max_chunks_per_step: int = Field(
1011
- default=50,
1012
- ge=1,
1013
- le=500,
1014
- description="Max chunks when injecting Context7 or expert docs.",
1015
- )
1016
- priority: list[str] = Field(
1017
- default_factory=lambda: ["current_task", "open_files", "recent_changes", "context7", "experts"],
1018
- description="Priority order when assembling context (for future use).",
1019
- )
1020
-
1021
-
1022
- class BugFixAgentConfig(BaseModel):
1023
- """Configuration for Bug Fix Agent."""
1024
-
1025
- max_iterations: int = Field(
1026
- default=3,
1027
- ge=1,
1028
- le=10,
1029
- description="Maximum iterations for fix loopback (1-10)",
1030
- )
1031
- auto_commit: bool = Field(
1032
- default=True,
1033
- description="Automatically commit changes to main branch when quality passes",
1034
- )
1035
- quality_thresholds: dict[str, float] = Field(
1036
- default_factory=lambda: {
1037
- "overall_min": 7.0,
1038
- "security_min": 6.5,
1039
- "maintainability_min": 7.0,
1040
- },
1041
- description="Quality thresholds for bug fixes (lower than full SDLC)",
1042
- )
1043
- escalation_threshold: int = Field(
1044
- default=2,
1045
- ge=1,
1046
- le=5,
1047
- description="Escalate to human after N failed iterations (1-5)",
1048
- )
1049
- escalation_enabled: bool = Field(
1050
- default=True,
1051
- description="Enable human escalation after failed iterations",
1052
- )
1053
- pre_commit_security_scan: bool = Field(
1054
- default=True,
1055
- description="Run security scan before committing changes",
1056
- )
1057
- metrics_enabled: bool = Field(
1058
- default=True,
1059
- description="Enable metrics collection for observability",
1060
- )
1061
- commit_strategy: str = Field(
1062
- default="direct_main",
1063
- description="Commit strategy: 'direct_main' (commit directly) or 'pull_request' (create PR)",
1064
- )
1065
- auto_merge_pr: bool = Field(
1066
- default=False,
1067
- description="Auto-merge PR if quality gates pass (requires PR workflow)",
1068
- )
1069
- require_pr_review: bool = Field(
1070
- default=False,
1071
- description="Require human review before merging PR",
1072
- )
1073
-
1074
-
1075
- class ContinuousBugFixConfig(BaseModel):
1076
- """Configuration for Continuous Bug Fix feature."""
1077
-
1078
- max_iterations: int = Field(
1079
- default=10,
1080
- ge=1,
1081
- le=100,
1082
- description="Maximum loop iterations (1-100)",
1083
- )
1084
- commit_strategy: str = Field(
1085
- default="one-per-bug",
1086
- description="Commit strategy: 'one-per-bug' (default) or 'batch'",
1087
- )
1088
- auto_commit: bool = Field(
1089
- default=True,
1090
- description="Automatically commit fixes after bug-fix-agent succeeds",
1091
- )
1092
- test_path: str = Field(
1093
- default="tests/",
1094
- description="Default test directory or file to run",
1095
- )
1096
- skip_patterns: list[str] = Field(
1097
- default_factory=list,
1098
- description="Patterns for bugs to skip (not yet implemented)",
1099
- )
1100
-
1101
-
1102
- class BeadsConfig(BaseModel):
1103
- """Configuration for Beads (bd) task-tracking integration."""
1104
-
1105
- enabled: bool = Field(
1106
- default=True,
1107
- description="Enable Beads (bd) integration. When false, all beads features are no-ops.",
1108
- )
1109
- required: bool = Field(
1110
- default=False,
1111
- description="When true, workflows fail if bd is unavailable or .beads not initialized.",
1112
- )
1113
- sync_epic: bool = Field(
1114
- default=True,
1115
- description="When beads.enabled is true, sync epic to bd (create issues + deps) before *epic run.",
1116
- )
1117
- hooks_simple_mode: bool = Field(
1118
- default=True,
1119
- description="When beads.enabled is true, create/close bd issues at start/end of *build and *fix.",
1120
- )
1121
- hooks_workflow: bool = Field(
1122
- default=True,
1123
- description="When beads.enabled is true, create/close a bd issue at start/end of CLI workflow runs.",
1124
- )
1125
- hooks_review: bool = Field(
1126
- default=False,
1127
- description="When beads.enabled is true, create/close for *review.",
1128
- )
1129
- hooks_test: bool = Field(
1130
- default=False,
1131
- description="When beads.enabled is true, create/close for *test.",
1132
- )
1133
- hooks_refactor: bool = Field(
1134
- default=False,
1135
- description="When beads.enabled is true, create/close for *refactor.",
1136
- )
1137
-
1138
-
1139
- class SimpleModeConfig(BaseModel):
1140
- """Configuration for Simple Mode."""
1141
-
1142
- enabled: bool = Field(
1143
- default=True, description="Enable Simple Mode (intent-based agent orchestration)"
1144
- )
1145
- auto_detect: bool = Field(
1146
- default=True,
1147
- description="Auto-enable Simple Mode for first-time users",
1148
- )
1149
- show_advanced: bool = Field(
1150
- default=False, description="Show advanced agent-specific options"
1151
- )
1152
- natural_language: bool = Field(
1153
- default=True, description="Enable natural language command parsing"
1154
- )
1155
- fast_mode_default: bool = Field(
1156
- default=False,
1157
- description="Default to fast mode for Simple Mode workflows (skip documentation steps)",
1158
- )
1159
- state_persistence_enabled: bool = Field(
1160
- default=True,
1161
- description="Enable workflow state persistence for resume capability",
1162
- )
1163
- checkpoint_retention_days: int = Field(
1164
- default=30,
1165
- ge=1,
1166
- description="Days to retain workflow checkpoints",
1167
- )
1168
- documentation_organized: bool = Field(
1169
- default=True,
1170
- description="Organize documentation by workflow ID",
1171
- )
1172
- create_latest_symlink: bool = Field(
1173
- default=False,
1174
- description="Create 'latest' symlink to most recent workflow",
1175
- )
1176
- context7_refresh_after_planning: bool = Field(
1177
- default=True,
1178
- description="After build planning (Step 2), refresh Context7 cache for libraries from the plan. Set false to disable.",
1179
- )
1180
- context7_refresh_max_libraries: int = Field(
1181
- default=10,
1182
- ge=1,
1183
- le=50,
1184
- description="Max number of libraries to warm in post-planning Context7 refresh (limits API use).",
1185
- )
1186
- enable_checkpoints: bool = Field(
1187
- default=True,
1188
- description="Enable mid-execution workflow checkpoints (Checkpoint 1: After Enhance, Checkpoint 2: After Planning, Checkpoint 3: Quality Gate)",
1189
- )
1190
- checkpoint_confidence_threshold: float = Field(
1191
- default=0.70,
1192
- ge=0.0,
1193
- le=1.0,
1194
- description="Minimum confidence threshold for checkpoint recommendations (0.0-1.0)",
1195
- )
1196
-
1197
-
1198
- class AutoEnhancementConfig(BaseModel):
1199
- """Configuration for automatic prompt enhancement"""
1200
-
1201
- enabled: bool = Field(
1202
- default=True, description="Enable auto-enhancement globally"
1203
- )
1204
- mode: str = Field(
1205
- default="cursor-skills",
1206
- description="Enhancement mode: cursor-skills, structured, or smart",
1207
- )
1208
- min_prompt_length: int = Field(
1209
- default=20, description="Minimum prompt length to trigger enhancement"
1210
- )
1211
- quality_threshold: float = Field(
1212
- default=50.0,
1213
- ge=0.0,
1214
- le=100.0,
1215
- description="Quality score below which to enhance",
1216
- )
1217
-
1218
- commands: dict[str, dict[str, Any]] = Field(
1219
- default_factory=lambda: {
1220
- "implementer": {"enabled": True, "synthesis_mode": "full"},
1221
- "planner": {"enabled": True, "synthesis_mode": "quick"},
1222
- "analyst": {"enabled": True, "synthesis_mode": "quick"},
1223
- "architect": {"enabled": False}, # Off by default to avoid over-use
1224
- "designer": {"enabled": False}, # Off by default to avoid over-use
1225
- },
1226
- description="Per-command enhancement settings",
1227
- )
1228
-
1229
-
1230
- class EvaluationConfig(BaseModel):
1231
- """Configuration for Evaluation & Quality Assurance Engine (Tier 1)"""
1232
-
1233
- enabled: bool = Field(
1234
- default=True, description="Enable comprehensive evaluation engine"
1235
- )
1236
- include_behavioral_validation: bool = Field(
1237
- default=True, description="Include behavioral correctness validation"
1238
- )
1239
- include_spec_compliance: bool = Field(
1240
- default=True, description="Include specification compliance checking"
1241
- )
1242
- include_architectural_checks: bool = Field(
1243
- default=True, description="Include architectural pattern adherence checking"
1244
- )
1245
- issue_severity_thresholds: dict[str, int] = Field(
1246
- default_factory=lambda: {
1247
- "critical": 0, # Hard fail
1248
- "high": 5, # Soft fail/loopback
1249
- "medium": 10,
1250
- "low": 20,
1251
- },
1252
- description="Maximum allowed issues by severity for gate passing",
1253
- )
1254
- enable_remediation_loop: bool = Field(
1255
- default=True, description="Enable automatic remediation loops"
1256
- )
1257
- max_remediation_retries: int = Field(
1258
- default=3, ge=1, le=10, description="Maximum remediation retry attempts"
1259
- )
1260
-
1261
-
1262
- class PromptLearningConfig(BaseModel):
1263
- """Configuration for Continual System Prompt Learning (Tier 1)"""
1264
-
1265
- enabled: bool = Field(
1266
- default=True, description="Enable prompt learning system"
1267
- )
1268
- feedback_collection_enabled: bool = Field(
1269
- default=True, description="Enable feedback collection from Cursor IDE"
1270
- )
1271
- ab_testing_enabled: bool = Field(
1272
- default=True, description="Enable A/B testing for prompt variations"
1273
- )
1274
- auto_optimize_enabled: bool = Field(
1275
- default=True, description="Enable automatic prompt optimization"
1276
- )
1277
- min_samples_for_optimization: int = Field(
1278
- default=10, ge=5, description="Minimum feedback samples before optimization"
1279
- )
1280
- optimization_interval_hours: int = Field(
1281
- default=24, ge=1, description="Hours between optimization runs"
1282
- )
1283
- prompt_version_retention_days: int = Field(
1284
- default=90, ge=7, description="Days to retain prompt version history"
1285
- )
1286
- auto_update_skills: bool = Field(
1287
- default=False, description="Automatically update Skills with learned prompts"
1288
- )
1289
- require_approval_for_updates: bool = Field(
1290
- default=True, description="Require approval before updating prompts"
1291
- )
1292
-
1293
-
1294
- class KnowledgeEngineConfig(BaseModel):
1295
- """Configuration for Knowledge Ecosystem Enhancement (Tier 1)"""
1296
-
1297
- enabled: bool = Field(
1298
- default=True, description="Enable always-on knowledge orchestration engine"
1299
- )
1300
- auto_detect_domains: bool = Field(
1301
- default=True, description="Automatically detect project domains"
1302
- )
1303
- auto_create_experts: bool = Field(
1304
- default=True, description="Automatically create project experts"
1305
- )
1306
- knowledge_ingestion_enabled: bool = Field(
1307
- default=True, description="Enable automatic knowledge ingestion"
1308
- )
1309
- proactive_consultation: bool = Field(
1310
- default=True, description="Proactively consult experts before needed"
1311
- )
1312
- metrics_tracking_enabled: bool = Field(
1313
- default=True, description="Enable metrics tracking for knowledge quality"
1314
- )
1315
- kb_maintenance_schedule: str = Field(
1316
- default="weekly", description="KB maintenance schedule: daily, weekly, monthly"
1317
- )
1318
- governance_enabled: bool = Field(
1319
- default=True, description="Enable governance and safety layer"
1320
- )
1321
- require_approval_for_writes: bool = Field(
1322
- default=False, description="Require approval before writing to knowledge base"
1323
- )
1324
-
1325
-
1326
- class ContextIntelligenceConfig(BaseModel):
1327
- """Configuration for Context Intelligence Engine (Tier 1)"""
1328
-
1329
- enabled: bool = Field(
1330
- default=True, description="Enable context intelligence engine"
1331
- )
1332
- dynamic_gathering_enabled: bool = Field(
1333
- default=True, description="Enable dynamic context gathering"
1334
- )
1335
- repository_exploration_enabled: bool = Field(
1336
- default=True, description="Enable autonomous repository exploration"
1337
- )
1338
- pattern_recognition_enabled: bool = Field(
1339
- default=True, description="Enable pattern recognition and cataloging"
1340
- )
1341
- similarity_detection_enabled: bool = Field(
1342
- default=True, description="Enable similarity detection for code reuse"
1343
- )
1344
- historical_analysis_enabled: bool = Field(
1345
- default=True, description="Enable historical context analysis"
1346
- )
1347
- relevance_ranking_enabled: bool = Field(
1348
- default=True, description="Enable relevance-based context ranking"
1349
- )
1350
- token_budget_management_enabled: bool = Field(
1351
- default=True, description="Enable token budget management"
1352
- )
1353
- default_token_budget: int = Field(
1354
- default=4000, ge=1000, le=16000, description="Default token budget for context"
1355
- )
1356
- progressive_loading_enabled: bool = Field(
1357
- default=True, description="Enable progressive context loading"
1358
- )
1359
-
1360
-
1361
- class AnalyticsConfig(BaseModel):
1362
- """Configuration for analytics and dual-write from execution metrics."""
1363
-
1364
- record_from_execution: bool = Field(
1365
- default=True,
1366
- description="When True, also write to .tapps-agents/analytics/ when recording execution metrics (step and workflow). Set False for metrics-only (no analytics).",
1367
- )
1368
-
1369
-
1370
- class GuardrailConfig(BaseModel):
1371
- """Security guardrails for subprocess and file writes (plan 2.2)."""
1372
-
1373
- sandbox_subprocess: bool = Field(
1374
- default=True,
1375
- description="When True, never use shell for subprocess; use cwd=project_root.",
1376
- )
1377
- allowed_paths_write: list[str] = Field(
1378
- default_factory=list,
1379
- description="When non-empty, writes must be under project_root and under one of these prefixes (e.g. src, tests, docs, .tapps-agents). When empty, only under project_root.",
1380
- )
1381
-
1382
-
1383
- class HumanOversightConfig(BaseModel):
1384
- """Human-in-the-loop oversight for *build and *full (plan 2.3)."""
1385
-
1386
- checkpoints_before_steps: list[str] = Field(
1387
- default_factory=list,
1388
- description="Step/agent names before which to prompt: e.g. implementer, designer. If --auto, skipped.",
1389
- )
1390
- require_diff_review_implementer: bool = Field(
1391
- default=False,
1392
- description="Reserved: when implementer supports preview/diff, require review before apply. Not implemented.",
1393
- )
1394
- branch_for_agent_changes: bool = Field(
1395
- default=True,
1396
- description="For *build and *full: create branch tapps-agents/build-{workflow_id}; merge to main only after human review.",
1397
- )
1398
-
1399
-
1400
- class ProjectConfig(BaseModel):
1401
- """Root configuration model for TappsCodingAgents project"""
1402
-
1403
- # Project metadata
1404
- project_name: str | None = Field(default=None, description="Project name")
1405
- version: str | None = Field(default=None, description="Project version")
1406
-
1407
- # CLI / runtime
1408
- offline_mode: bool = Field(
1409
- default=False,
1410
- description="Use offline mode (no network) when possible for CLI and agents",
1411
- )
1412
-
1413
- # Core configuration
1414
- tooling: ToolingConfig = Field(
1415
- default_factory=ToolingConfig,
1416
- description="Canonical runtime/tool targets and policy (used by doctor/CI/agents)",
1417
- )
1418
- agents: AgentsConfig = Field(default_factory=AgentsConfig)
1419
- scoring: ScoringConfig = Field(default_factory=ScoringConfig)
1420
- expert: ExpertConfig = Field(
1421
- default_factory=ExpertConfig,
1422
- description="Expert consultation system configuration",
1423
- )
1424
- context7: Context7Config | None = Field(
1425
- default_factory=Context7Config, description="Context7 integration configuration"
1426
- )
1427
- context_budget: ContextBudgetConfig = Field(
1428
- default_factory=ContextBudgetConfig,
1429
- description="Context budget: max_chunks for Context7/expert injection; surface cached_at (plan 3.2).",
1430
- )
1431
- quality_tools: QualityToolsConfig | None = Field(
1432
- default_factory=QualityToolsConfig,
1433
- description="Quality analysis tools configuration (Phase 6)",
1434
- )
1435
- workflow: WorkflowConfig = Field(
1436
- default_factory=WorkflowConfig,
1437
- description="Workflow execution configuration",
1438
- )
1439
- analytics: AnalyticsConfig = Field(
1440
- default_factory=AnalyticsConfig,
1441
- description="Analytics and dual-write from execution metrics (health usage, outcomes).",
1442
- )
1443
- guardrails: GuardrailConfig = Field(
1444
- default_factory=GuardrailConfig,
1445
- description="Security guardrails: subprocess sandbox, path allowlist for writes (plan 2.2).",
1446
- )
1447
- human_oversight: HumanOversightConfig = Field(
1448
- default_factory=HumanOversightConfig,
1449
- description="Human oversight: step checkpoints, branch-for-agent-changes (plan 2.3). require_diff_review_implementer reserved.",
1450
- )
1451
- simple_mode: SimpleModeConfig = Field(
1452
- default_factory=SimpleModeConfig,
1453
- description="Simple Mode configuration (intent-based orchestration)",
1454
- )
1455
- beads: BeadsConfig = Field(
1456
- default_factory=BeadsConfig,
1457
- description="Beads (bd) task-tracking integration. See docs/BEADS_INTEGRATION.md.",
1458
- )
1459
- bug_fix_agent: BugFixAgentConfig = Field(
1460
- default_factory=BugFixAgentConfig,
1461
- description="Bug Fix Agent configuration (automated bug fixing with auto-commit)",
1462
- )
1463
- continuous_bug_fix: ContinuousBugFixConfig = Field(
1464
- default_factory=ContinuousBugFixConfig,
1465
- description="Continuous Bug Fix configuration (automated bug finding and fixing loop)",
1466
- )
1467
- auto_enhancement: AutoEnhancementConfig = Field(
1468
- default_factory=AutoEnhancementConfig,
1469
- description="Automatic prompt enhancement configuration",
1470
- )
1471
-
1472
- # Tier 1 Enhancement Configurations
1473
- evaluation: EvaluationConfig = Field(
1474
- default_factory=EvaluationConfig,
1475
- description="Evaluation & Quality Assurance Engine configuration (Tier 1)",
1476
- )
1477
- prompt_learning: PromptLearningConfig = Field(
1478
- default_factory=PromptLearningConfig,
1479
- description="Continual System Prompt Learning configuration (Tier 1)",
1480
- )
1481
- knowledge_engine: KnowledgeEngineConfig = Field(
1482
- default_factory=KnowledgeEngineConfig,
1483
- description="Knowledge Ecosystem Enhancement configuration (Tier 1)",
1484
- )
1485
- context_intelligence: ContextIntelligenceConfig = Field(
1486
- default_factory=ContextIntelligenceConfig,
1487
- description="Context Intelligence Engine configuration (Tier 1)",
1488
- )
1489
- cleanup: CleanupConfig = Field(
1490
- default_factory=CleanupConfig,
1491
- description="Cleanup operations configuration",
1492
- )
1493
-
1494
- model_config = {
1495
- "extra": "ignore", # Ignore unknown fields
1496
- "validate_assignment": True,
1497
- }
1498
-
1499
-
1500
- def load_config(config_path: Path | None = None) -> ProjectConfig:
1501
- """
1502
- Load configuration from YAML file.
1503
-
1504
- Args:
1505
- config_path: Path to config.yaml file. If None, looks for `.tapps-agents/config.yaml`
1506
- in current directory or parent directories.
1507
-
1508
- Returns:
1509
- ProjectConfig instance with loaded values
1510
-
1511
- Raises:
1512
- FileNotFoundError: If config file doesn't exist and no defaults available
1513
- ValueError: If config file is invalid
1514
- """
1515
- if config_path is None:
1516
- # Look for .tapps-agents/config.yaml in current and parent directories
1517
- current = Path.cwd()
1518
- for parent in [current] + list(current.parents):
1519
- candidate = parent / ".tapps-agents" / "config.yaml"
1520
- if candidate.exists():
1521
- config_path = candidate
1522
- break
1523
-
1524
- if config_path is None:
1525
- # Return defaults if no config file found
1526
- return ProjectConfig()
1527
-
1528
- if not config_path.exists():
1529
- # Return defaults if file doesn't exist
1530
- return ProjectConfig()
1531
-
1532
- try:
1533
- # Use utf-8-sig to automatically handle UTF-8 BOM (common on Windows)
1534
- # This prevents YAML parsing failures when files have BOM prefix (\xef\xbb\xbf)
1535
- with open(config_path, encoding="utf-8-sig") as f:
1536
- data = yaml.safe_load(f)
1537
-
1538
- if data is None:
1539
- # Empty file, return defaults
1540
- return ProjectConfig()
1541
-
1542
- # Validate and load config
1543
- return ProjectConfig(**data)
1544
- except yaml.YAMLError as e:
1545
- raise ValueError(f"Invalid YAML in config file {config_path}: {e}") from e
1546
- except Exception as e:
1547
- raise ValueError(f"Error loading config from {config_path}: {e}") from e
1548
-
1549
-
1550
- def save_config(config_path: Path, config: ProjectConfig) -> None:
1551
- """
1552
- Save configuration to a YAML file.
1553
-
1554
- Args:
1555
- config_path: Path to the config file
1556
- config: ProjectConfig instance to save
1557
-
1558
- Raises:
1559
- OSError: If the file cannot be written
1560
- """
1561
- config_path.parent.mkdir(parents=True, exist_ok=True)
1562
-
1563
- # Load existing config to preserve other settings
1564
- existing_data: dict[str, Any] = {}
1565
- if config_path.exists():
1566
- try:
1567
- # Use utf-8-sig to handle UTF-8 BOM (common on Windows)
1568
- with open(config_path, encoding="utf-8-sig") as f:
1569
- existing_data = yaml.safe_load(f) or {}
1570
- except Exception:
1571
- # If we can't read existing config, start fresh
1572
- existing_data = {}
1573
-
1574
- # Merge with new config (convert to dict, preserving existing values)
1575
- new_data = config.model_dump(exclude_none=True, mode="json")
1576
-
1577
- def deep_merge(existing: dict[str, Any], new: dict[str, Any]) -> dict[str, Any]:
1578
- """Recursively merge new dict into existing dict."""
1579
- result = existing.copy()
1580
- for key, value in new.items():
1581
- if key in result and isinstance(result[key], dict) and isinstance(value, dict):
1582
- # Recursively merge nested dictionaries
1583
- result[key] = deep_merge(result[key], value)
1584
- else:
1585
- # Overwrite with new value
1586
- result[key] = value
1587
- return result
1588
-
1589
- # Deep merge: preserve existing nested values
1590
- existing_data = deep_merge(existing_data, new_data)
1591
-
1592
- # Save
1593
- with open(config_path, "w", encoding="utf-8") as f:
1594
- yaml.dump(existing_data, f, default_flow_style=False, sort_keys=False, allow_unicode=True)
1595
-
1596
-
1597
- def get_default_config() -> dict[str, Any]:
1598
- """
1599
- Get default configuration as a dictionary (for template generation).
1600
-
1601
- Returns:
1602
- Dictionary representation of default config with Path objects as strings
1603
- """
1604
- config = ProjectConfig()
1605
- # Use model_dump with mode="json" to serialize Path objects as strings
1606
- # This is required for YAML serialization (yaml.safe_dump can't handle Path objects)
1607
- return config.model_dump(exclude_none=True, mode="json")
1608
-
1609
-
1610
- def get_expert_config(config: ProjectConfig | None = None) -> ExpertConfig:
1611
- """
1612
- Get expert configuration, loading from file if needed.
1613
-
1614
- Args:
1615
- config: Optional ProjectConfig instance. If None, loads from file.
1616
-
1617
- Returns:
1618
- ExpertConfig instance
1619
- """
1620
- if config is None:
1621
- config = load_config()
1622
- return config.expert
1
+ """
2
+ Configuration management for TappsCodingAgents.
3
+
4
+ Provides Pydantic models for type-safe configuration and YAML loading.
5
+ """
6
+
7
+ from pathlib import Path
8
+ from typing import Any
9
+
10
+ import yaml
11
+ from pydantic import BaseModel, Field, model_validator
12
+
13
+
14
+ class ScoringWeightsConfig(BaseModel):
15
+ """Configuration for code scoring weights (must sum to 1.0). 7-category: Structure, DevEx added (MCP_SYSTEMS_IMPROVEMENT_RECOMMENDATIONS §3.2)."""
16
+
17
+ complexity: float = Field(
18
+ default=0.18, ge=0.0, le=1.0, description="Weight for complexity score"
19
+ )
20
+ security: float = Field(
21
+ default=0.27, ge=0.0, le=1.0, description="Weight for security score"
22
+ )
23
+ maintainability: float = Field(
24
+ default=0.24, ge=0.0, le=1.0, description="Weight for maintainability score"
25
+ )
26
+ test_coverage: float = Field(
27
+ default=0.13, ge=0.0, le=1.0, description="Weight for test coverage score"
28
+ )
29
+ performance: float = Field(
30
+ default=0.08, ge=0.0, le=1.0, description="Weight for performance score"
31
+ )
32
+ structure: float = Field(
33
+ default=0.05, ge=0.0, le=1.0, description="Weight for structure score (project layout, key files)"
34
+ )
35
+ devex: float = Field(
36
+ default=0.05, ge=0.0, le=1.0, description="Weight for developer experience score (docs, config, tooling)"
37
+ )
38
+
39
+ @model_validator(mode="after")
40
+ def validate_weights_sum(self):
41
+ """Ensure weights sum to approximately 1.0"""
42
+ total = (
43
+ self.complexity
44
+ + self.security
45
+ + self.maintainability
46
+ + self.test_coverage
47
+ + self.performance
48
+ + self.structure
49
+ + self.devex
50
+ )
51
+ if abs(total - 1.0) > 0.01: # Allow small floating point errors
52
+ raise ValueError(f"Scoring weights must sum to 1.0, got {total}")
53
+ return self
54
+
55
+
56
+ class TestCoverageConfig(BaseModel):
57
+ """Test coverage configuration for quality gates."""
58
+
59
+ enabled: bool = Field(default=True, description="Enable test coverage checks")
60
+ threshold: float = Field(
61
+ default=0.8, ge=0.0, le=1.0, description="Minimum coverage threshold (0.0-1.0)"
62
+ )
63
+ critical_services_threshold: float = Field(
64
+ default=0.8,
65
+ ge=0.0,
66
+ le=1.0,
67
+ description="Minimum coverage for critical services (0.0-1.0)",
68
+ )
69
+ warning_threshold: float = Field(
70
+ default=0.6,
71
+ ge=0.0,
72
+ le=1.0,
73
+ description="Coverage threshold for warnings (0.0-1.0)",
74
+ )
75
+
76
+
77
+ class QualityGatesConfig(BaseModel):
78
+ """Quality gates configuration."""
79
+
80
+ enabled: bool = Field(default=True, description="Enable quality gates")
81
+ test_coverage: TestCoverageConfig = Field(
82
+ default_factory=TestCoverageConfig, description="Test coverage configuration"
83
+ )
84
+
85
+
86
+ class ScoringConfig(BaseModel):
87
+ """Configuration for code scoring system"""
88
+
89
+ weights: ScoringWeightsConfig = Field(default_factory=ScoringWeightsConfig)
90
+ quality_threshold: float = Field(
91
+ default=70.0, ge=0.0, le=100.0, description="Minimum overall score to pass"
92
+ )
93
+ quality_gates: QualityGatesConfig = Field(
94
+ default_factory=QualityGatesConfig, description="Quality gates configuration"
95
+ )
96
+
97
+
98
+ class ReviewerAgentContext7Config(BaseModel):
99
+ """Context7 configuration for Reviewer Agent"""
100
+
101
+ auto_detect: bool = Field(
102
+ default=True, description="Enable automatic library detection"
103
+ )
104
+ topics: list[str] = Field(
105
+ default_factory=lambda: ["best-practices", "routing", "api-design"],
106
+ description="Default topics to fetch for detected libraries"
107
+ )
108
+
109
+
110
+ class ReviewerAgentConfig(BaseModel):
111
+ """Configuration specific to Reviewer Agent"""
112
+
113
+ quality_threshold: float = Field(
114
+ default=70.0, ge=0.0, le=100.0, description="Minimum score to pass review"
115
+ )
116
+ include_scoring: bool = Field(
117
+ default=True, description="Include code scoring in review"
118
+ )
119
+ include_llm_feedback: bool = Field(
120
+ default=True, description="Include LLM-generated feedback"
121
+ )
122
+ max_file_size: int = Field(
123
+ default=1024 * 1024,
124
+ ge=1024,
125
+ description="Maximum file size in bytes (1MB default)",
126
+ )
127
+ min_confidence_threshold: float = Field(
128
+ default=0.8,
129
+ ge=0.0,
130
+ le=1.0,
131
+ description="Minimum expert confidence threshold (0.0-1.0)",
132
+ )
133
+ operation_timeout: float = Field(
134
+ default=300.0,
135
+ ge=10.0,
136
+ description="Timeout in seconds for reviewer operations (default: 5 minutes)",
137
+ )
138
+ tool_timeout: float = Field(
139
+ default=30.0,
140
+ ge=5.0,
141
+ description="Timeout in seconds for individual quality tools (default: 30 seconds)",
142
+ )
143
+ enable_parallel_tools: bool = Field(
144
+ default=True,
145
+ description="Enable parallel execution of quality tools (Ruff, mypy, bandit)",
146
+ )
147
+ context7: ReviewerAgentContext7Config = Field(
148
+ default_factory=ReviewerAgentContext7Config,
149
+ description="Context7 integration settings for Reviewer Agent"
150
+ )
151
+ # NEW: Library detection config
152
+ auto_library_detection: bool = Field(
153
+ default=True,
154
+ description="Automatically detect libraries from code and dependency files"
155
+ )
156
+ library_detection_depth: str = Field(
157
+ default="both",
158
+ description="What to detect: 'code' (imports only), 'dependencies' (requirements.txt/pyproject.toml), or 'both'"
159
+ )
160
+ # NEW: Context7 integration config
161
+ auto_context7_lookups: bool = Field(
162
+ default=True,
163
+ description="Automatically lookup library documentation from Context7"
164
+ )
165
+ context7_timeout: int = Field(
166
+ default=30,
167
+ ge=1,
168
+ le=300,
169
+ description="Timeout for Context7 lookups in seconds"
170
+ )
171
+ context7_cache_enabled: bool = Field(
172
+ default=True,
173
+ description="Cache Context7 responses to avoid duplicate lookups"
174
+ )
175
+ # NEW: Pattern detection config
176
+ pattern_detection_enabled: bool = Field(
177
+ default=True,
178
+ description="Enable domain-specific pattern detection"
179
+ )
180
+ pattern_confidence_threshold: float = Field(
181
+ default=0.5,
182
+ ge=0.0,
183
+ le=1.0,
184
+ description="Minimum confidence to report detected patterns (0.0-1.0)"
185
+ )
186
+
187
+
188
+ class PlannerAgentConfig(BaseModel):
189
+ """Configuration specific to Planner Agent"""
190
+
191
+ stories_dir: str | None = Field(
192
+ default=None, description="Directory for storing stories (default: stories/)"
193
+ )
194
+ default_priority: str = Field(
195
+ default="medium",
196
+ description="Default priority for new stories (high/medium/low)",
197
+ )
198
+ min_confidence_threshold: float = Field(
199
+ default=0.6,
200
+ ge=0.0,
201
+ le=1.0,
202
+ description="Minimum expert confidence threshold (0.0-1.0)",
203
+ )
204
+
205
+
206
+ class ImplementerAgentContext7Config(BaseModel):
207
+ """Context7 configuration for Implementer Agent"""
208
+
209
+ auto_detect: bool = Field(
210
+ default=True, description="Enable automatic library detection"
211
+ )
212
+ detect_from_prompt: bool = Field(
213
+ default=True, description="Detect libraries from prompt/specification text"
214
+ )
215
+
216
+
217
+ class ImplementerAgentConfig(BaseModel):
218
+ """Configuration specific to Implementer Agent"""
219
+
220
+ require_review: bool = Field(
221
+ default=True, description="Require code review before writing files"
222
+ )
223
+ auto_approve_threshold: float = Field(
224
+ default=80.0, ge=0.0, le=100.0, description="Auto-approve if score >= threshold"
225
+ )
226
+ backup_files: bool = Field(
227
+ default=True, description="Create backup before overwriting existing files"
228
+ )
229
+ max_file_size: int = Field(
230
+ default=10 * 1024 * 1024,
231
+ ge=1024,
232
+ description="Maximum file size in bytes (10MB default)",
233
+ )
234
+ min_confidence_threshold: float = Field(
235
+ default=0.7,
236
+ ge=0.0,
237
+ le=1.0,
238
+ description="Minimum expert confidence threshold (0.0-1.0)",
239
+ )
240
+ context7: ImplementerAgentContext7Config = Field(
241
+ default_factory=ImplementerAgentContext7Config,
242
+ description="Context7 integration settings for Implementer Agent"
243
+ )
244
+
245
+
246
+ class TesterAgentConfig(BaseModel):
247
+ """Configuration specific to Tester Agent"""
248
+
249
+ test_framework: str = Field(
250
+ default="pytest", description="Test framework to use (pytest/unittest)"
251
+ )
252
+ tests_dir: str | None = Field(
253
+ default=None, description="Directory for tests (default: tests/)"
254
+ )
255
+ coverage_threshold: float = Field(
256
+ default=80.0, ge=0.0, le=100.0, description="Target test coverage percentage"
257
+ )
258
+ auto_write_tests: bool = Field(
259
+ default=True, description="Automatically write generated tests to files"
260
+ )
261
+ min_confidence_threshold: float = Field(
262
+ default=0.7,
263
+ ge=0.0,
264
+ le=1.0,
265
+ description="Minimum expert confidence threshold (0.0-1.0)",
266
+ )
267
+
268
+
269
+ class DebuggerAgentContext7Config(BaseModel):
270
+ """Context7 configuration for Debugger Agent"""
271
+
272
+ auto_detect: bool = Field(
273
+ default=True, description="Enable automatic library detection"
274
+ )
275
+ detect_from_errors: bool = Field(
276
+ default=True, description="Detect libraries from error messages and stack traces"
277
+ )
278
+
279
+
280
+ class DebuggerAgentConfig(BaseModel):
281
+ """Configuration specific to Debugger Agent"""
282
+
283
+ include_code_examples: bool = Field(
284
+ default=True, description="Include code examples in fix suggestions"
285
+ )
286
+ max_context_lines: int = Field(
287
+ default=50,
288
+ ge=10,
289
+ le=200,
290
+ description="Maximum lines of code context to include in analysis",
291
+ )
292
+ min_confidence_threshold: float = Field(
293
+ default=0.7,
294
+ ge=0.0,
295
+ le=1.0,
296
+ description="Minimum expert confidence threshold (0.0-1.0)",
297
+ )
298
+ context7: DebuggerAgentContext7Config = Field(
299
+ default_factory=DebuggerAgentContext7Config,
300
+ description="Context7 integration settings for Debugger Agent"
301
+ )
302
+
303
+
304
+ class DocumenterAgentConfig(BaseModel):
305
+ """Configuration specific to Documenter Agent"""
306
+
307
+ docs_dir: str | None = Field(
308
+ default=None, description="Directory for generated docs (default: docs/)"
309
+ )
310
+ include_examples: bool = Field(
311
+ default=True, description="Include code examples in documentation"
312
+ )
313
+ docstring_format: str = Field(
314
+ default="google", description="Docstring format (google/numpy/sphinx)"
315
+ )
316
+ min_confidence_threshold: float = Field(
317
+ default=0.5,
318
+ ge=0.0,
319
+ le=1.0,
320
+ description="Minimum expert confidence threshold (0.0-1.0)",
321
+ )
322
+
323
+
324
+ class Context7KnowledgeBaseConfig(BaseModel):
325
+ """Configuration for Context7 knowledge base caching"""
326
+
327
+ enabled: bool = Field(default=True, description="Enable KB caching")
328
+ location: str = Field(
329
+ default=".tapps-agents/kb/context7-cache", description="KB cache directory"
330
+ )
331
+ sharding: bool = Field(default=True, description="Enable library-based sharding")
332
+ indexing: bool = Field(default=True, description="Enable indexing")
333
+ max_cache_size: str = Field(default="100MB", description="Maximum cache size")
334
+ hit_rate_threshold: float = Field(
335
+ default=0.7, ge=0.0, le=1.0, description="Target hit rate threshold"
336
+ )
337
+ fuzzy_match_threshold: float = Field(
338
+ default=0.7, ge=0.0, le=1.0, description="Fuzzy match confidence threshold"
339
+ )
340
+
341
+
342
+ class Context7RefreshConfig(BaseModel):
343
+ """Configuration for Context7 auto-refresh system"""
344
+
345
+ enabled: bool = Field(default=True, description="Enable auto-refresh")
346
+ default_max_age_days: int = Field(
347
+ default=30, ge=1, description="Default max age for cache entries (days)"
348
+ )
349
+ check_on_access: bool = Field(default=True, description="Check staleness on access")
350
+ auto_queue: bool = Field(
351
+ default=True, description="Automatically queue stale entries"
352
+ )
353
+ auto_process_on_startup: bool = Field(
354
+ default=False, description="Process queue on agent startup"
355
+ )
356
+
357
+
358
+ class Context7Config(BaseModel):
359
+ """Configuration for Context7 integration"""
360
+
361
+ enabled: bool = Field(default=True, description="Enable Context7 integration")
362
+ default_token_limit: int = Field(
363
+ default=3000, ge=100, description="Default token limit for Context7 docs"
364
+ )
365
+ cache_duration: int = Field(
366
+ default=3600, ge=0, description="Cache duration in seconds"
367
+ )
368
+ integration_level: str = Field(
369
+ default="optional", description="Integration level (mandatory/optional)"
370
+ )
371
+ usage_requirement: str | None = Field(
372
+ default=None, description="Usage requirement description"
373
+ )
374
+ bypass_forbidden: bool = Field(
375
+ default=True, description="Allow bypassing if Context7 unavailable"
376
+ )
377
+ # Enhancement: Automatic Context7 detection and fetching
378
+ auto_detect: bool = Field(
379
+ default=True, description="Enable automatic library detection from code, prompts, and errors"
380
+ )
381
+ auto_fetch: bool = Field(
382
+ default=True, description="Automatically fetch docs for detected libraries"
383
+ )
384
+ proactive_suggestions: bool = Field(
385
+ default=True, description="Proactively suggest Context7 lookups when library patterns detected"
386
+ )
387
+
388
+ knowledge_base: Context7KnowledgeBaseConfig = Field(
389
+ default_factory=Context7KnowledgeBaseConfig
390
+ )
391
+ refresh: Context7RefreshConfig = Field(default_factory=Context7RefreshConfig)
392
+
393
+
394
+ class QualityToolsConfig(BaseModel):
395
+ """Configuration for quality analysis tools (Phase 6 - 2025 Standards)"""
396
+
397
+ # Ruff configuration
398
+ ruff_enabled: bool = Field(default=True, description="Enable Ruff linting")
399
+ ruff_config_path: str | None = Field(
400
+ default=None,
401
+ description="Path to ruff.toml or pyproject.toml (auto-detected if None)",
402
+ )
403
+
404
+ # mypy configuration
405
+ mypy_enabled: bool = Field(default=True, description="Enable mypy type checking")
406
+ mypy_strict: bool = Field(default=False, description="Enable strict mode for mypy")
407
+ mypy_config_path: str | None = Field(
408
+ default=None,
409
+ description="Path to mypy.ini or pyproject.toml (auto-detected if None)",
410
+ )
411
+
412
+ # jscpd configuration
413
+ jscpd_enabled: bool = Field(
414
+ default=True, description="Enable jscpd duplication detection"
415
+ )
416
+ duplication_threshold: float = Field(
417
+ default=3.0,
418
+ ge=0.0,
419
+ le=100.0,
420
+ description="Maximum duplication percentage threshold",
421
+ )
422
+ min_duplication_lines: int = Field(
423
+ default=5, ge=3, description="Minimum lines for duplication detection"
424
+ )
425
+
426
+ # TypeScript/JavaScript configuration
427
+ typescript_enabled: bool = Field(
428
+ default=True, description="Enable TypeScript/JavaScript support"
429
+ )
430
+ eslint_config: str | None = Field(
431
+ default=None, description="Path to ESLint config file"
432
+ )
433
+ tsconfig_path: str | None = Field(default=None, description="Path to tsconfig.json")
434
+
435
+ # Dependency security auditing
436
+ pip_audit_enabled: bool = Field(
437
+ default=True, description="Enable pip-audit security scanning"
438
+ )
439
+ dependency_audit_threshold: str = Field(
440
+ default="high",
441
+ description="Minimum severity threshold (low/medium/high/critical)",
442
+ )
443
+
444
+
445
+ class ToolingTargetsConfig(BaseModel):
446
+ """Pin runtime/tool targets so agents and CI behave deterministically."""
447
+
448
+ python: str = Field(
449
+ default="3.13.3",
450
+ description="Target Python version for this project (pin exact patch where possible)",
451
+ )
452
+ python_requires: str = Field(
453
+ default=">=3.13",
454
+ description="PEP 440 requires-python constraint enforced by packaging/CI",
455
+ )
456
+ os_targets: list[str] = Field(
457
+ default_factory=lambda: ["windows", "linux"],
458
+ description="Primary OS targets (used for setup/doctor guidance)",
459
+ )
460
+ node: str | None = Field(
461
+ default=None,
462
+ description="Optional Node.js version (only needed if TypeScript/jscpd tooling is enabled)",
463
+ )
464
+
465
+
466
+ class ToolingPolicyConfig(BaseModel):
467
+ """Policy for how missing/optional tools are handled."""
468
+
469
+ external_tools_mode: str = Field(
470
+ default="soft",
471
+ description="How to handle missing external tools: soft=warn/skip, hard=fail",
472
+ )
473
+ mypy_staged: bool = Field(
474
+ default=True, description="Stage mypy enforcement module-by-module"
475
+ )
476
+ mypy_stage_paths: list[str] = Field(
477
+ default_factory=lambda: [
478
+ "tapps_agents/core",
479
+ "tapps_agents/workflow",
480
+ "tapps_agents/context7",
481
+ ],
482
+ description="Paths enforced by mypy during the staged rollout",
483
+ )
484
+
485
+
486
+ class ToolingConfig(BaseModel):
487
+ """Canonical tooling/targets configuration (single source of truth)."""
488
+
489
+ targets: ToolingTargetsConfig = Field(default_factory=ToolingTargetsConfig)
490
+ policy: ToolingPolicyConfig = Field(default_factory=ToolingPolicyConfig)
491
+
492
+
493
+ class ArchitectAgentConfig(BaseModel):
494
+ """Configuration specific to Architect Agent"""
495
+
496
+ min_confidence_threshold: float = Field(
497
+ default=0.75,
498
+ ge=0.0,
499
+ le=1.0,
500
+ description="Minimum expert confidence threshold (0.0-1.0)",
501
+ )
502
+
503
+
504
+ class DesignerAgentConfig(BaseModel):
505
+ """Configuration specific to Designer Agent"""
506
+
507
+ min_confidence_threshold: float = Field(
508
+ default=0.65,
509
+ ge=0.0,
510
+ le=1.0,
511
+ description="Minimum expert confidence threshold (0.0-1.0)",
512
+ )
513
+
514
+
515
+ class OpsAgentConfig(BaseModel):
516
+ """Configuration specific to Ops Agent"""
517
+
518
+ min_confidence_threshold: float = Field(
519
+ default=0.75,
520
+ ge=0.0,
521
+ le=1.0,
522
+ description="Minimum expert confidence threshold (0.0-1.0)",
523
+ )
524
+
525
+
526
+ class EnhancerAgentConfig(BaseModel):
527
+ """Configuration specific to Enhancer Agent"""
528
+
529
+ min_confidence_threshold: float = Field(
530
+ default=0.6,
531
+ ge=0.0,
532
+ le=1.0,
533
+ description="Minimum expert confidence threshold (0.0-1.0)",
534
+ )
535
+
536
+
537
+ class AnalystAgentConfig(BaseModel):
538
+ """Configuration specific to Analyst Agent"""
539
+
540
+ min_confidence_threshold: float = Field(
541
+ default=0.65,
542
+ ge=0.0,
543
+ le=1.0,
544
+ description="Minimum expert confidence threshold (0.0-1.0)",
545
+ )
546
+
547
+
548
+ class OrchestratorAgentConfig(BaseModel):
549
+ """Configuration specific to Orchestrator Agent"""
550
+
551
+ min_confidence_threshold: float = Field(
552
+ default=0.6,
553
+ ge=0.0,
554
+ le=1.0,
555
+ description="Minimum expert confidence threshold (0.0-1.0)",
556
+ )
557
+
558
+
559
+ class EvaluatorAgentConfig(BaseModel):
560
+ """Configuration specific to Evaluator Agent"""
561
+
562
+ auto_run: bool = Field(
563
+ default=False,
564
+ description="Run evaluator automatically at end of workflows (build, full, etc.)"
565
+ )
566
+ output_dir: str = Field(
567
+ default=".tapps-agents/evaluations",
568
+ description="Directory for evaluation reports"
569
+ )
570
+ thresholds: dict[str, float] = Field(
571
+ default_factory=lambda: {
572
+ "quality_score": 70.0,
573
+ "workflow_completion": 0.8,
574
+ },
575
+ description="Thresholds for evaluation metrics"
576
+ )
577
+
578
+
579
+ class CleanupAgentConfig(BaseModel):
580
+ """Configuration specific to Cleanup Agent"""
581
+
582
+ dry_run_default: bool = Field(
583
+ default=True,
584
+ description="Run in dry-run mode by default (preview changes without executing)"
585
+ )
586
+ backup_enabled: bool = Field(
587
+ default=True,
588
+ description="Create backups before destructive operations"
589
+ )
590
+ interactive_mode: bool = Field(
591
+ default=True,
592
+ description="Prompt for confirmation on risky operations"
593
+ )
594
+ default_pattern: str = Field(
595
+ default="*.md",
596
+ description="Default file pattern for analysis"
597
+ )
598
+ age_threshold_days: int = Field(
599
+ default=90,
600
+ ge=1,
601
+ le=365,
602
+ description="Days threshold for detecting outdated files"
603
+ )
604
+ similarity_threshold: float = Field(
605
+ default=0.8,
606
+ ge=0.0,
607
+ le=1.0,
608
+ description="Content similarity threshold for detecting near-duplicates"
609
+ )
610
+ backup_dir: str = Field(
611
+ default=".cleanup-backups",
612
+ description="Directory for cleanup backups"
613
+ )
614
+
615
+
616
+ class ExpertConfig(BaseModel):
617
+ """Configuration for expert consultation system"""
618
+
619
+ # Agent-specific confidence thresholds
620
+ agent_confidence_thresholds: dict[str, float] = Field(
621
+ default_factory=lambda: {
622
+ "reviewer": 0.8,
623
+ "architect": 0.75,
624
+ "implementer": 0.7,
625
+ "designer": 0.65,
626
+ "tester": 0.7,
627
+ "ops": 0.75,
628
+ "enhancer": 0.6,
629
+ "analyst": 0.65,
630
+ "planner": 0.6,
631
+ "debugger": 0.7,
632
+ "documenter": 0.5,
633
+ "orchestrator": 0.6,
634
+ "default": 0.7,
635
+ },
636
+ description="Agent-specific confidence thresholds (0.0-1.0)",
637
+ )
638
+
639
+ # Confidence calculation weights
640
+ weight_max_confidence: float = Field(
641
+ default=0.35, ge=0.0, le=1.0, description="Weight for maximum expert confidence"
642
+ )
643
+ weight_agreement: float = Field(
644
+ default=0.25, ge=0.0, le=1.0, description="Weight for expert agreement level"
645
+ )
646
+ weight_rag_quality: float = Field(
647
+ default=0.2, ge=0.0, le=1.0, description="Weight for RAG knowledge base quality"
648
+ )
649
+ weight_domain_relevance: float = Field(
650
+ default=0.1, ge=0.0, le=1.0, description="Weight for domain relevance"
651
+ )
652
+ weight_project_context: float = Field(
653
+ default=0.1, ge=0.0, le=1.0, description="Weight for project context relevance"
654
+ )
655
+
656
+ # Agreement and similarity thresholds
657
+ high_agreement_threshold: float = Field(
658
+ default=0.75,
659
+ ge=0.0,
660
+ le=1.0,
661
+ description="High agreement threshold for expert consensus (0.0-1.0)",
662
+ )
663
+ similarity_threshold: float = Field(
664
+ default=0.6,
665
+ ge=0.0,
666
+ le=1.0,
667
+ description="Similarity threshold for expert response comparison (0.0-1.0)",
668
+ )
669
+
670
+ # RAG (Retrieval-Augmented Generation) parameters
671
+ rag_max_length: int = Field(
672
+ default=2000,
673
+ ge=100,
674
+ le=10000,
675
+ description="Maximum length for RAG context retrieval (characters)",
676
+ )
677
+ rag_max_results: int = Field(
678
+ default=8, ge=1, le=20, description="Maximum number of RAG results to retrieve"
679
+ )
680
+ rag_default_quality: float = Field(
681
+ default=0.8,
682
+ ge=0.0,
683
+ le=1.0,
684
+ description="Default RAG quality score when not provided (0.0-1.0)",
685
+ )
686
+
687
+ # Profile confidence threshold
688
+ profile_confidence_threshold: float = Field(
689
+ default=0.7,
690
+ ge=0.0,
691
+ le=1.0,
692
+ description="Minimum confidence threshold for including profile values (0.0-1.0)",
693
+ )
694
+
695
+ # Supporting expert weight
696
+ supporting_expert_weight: float = Field(
697
+ default=0.15,
698
+ ge=0.0,
699
+ le=1.0,
700
+ description="Approximate weight for supporting experts in agreement calculation",
701
+ )
702
+
703
+ @model_validator(mode="after")
704
+ def validate_weights_sum(self):
705
+ """Ensure confidence weights sum to approximately 1.0"""
706
+ total = (
707
+ self.weight_max_confidence
708
+ + self.weight_agreement
709
+ + self.weight_rag_quality
710
+ + self.weight_domain_relevance
711
+ + self.weight_project_context
712
+ )
713
+ if abs(total - 1.0) > 0.01: # Allow small floating point errors
714
+ raise ValueError(f"Expert confidence weights must sum to 1.0, got {total}")
715
+ return self
716
+
717
+
718
+ class AgentsConfig(BaseModel):
719
+ """Configuration for all agents"""
720
+
721
+ reviewer: ReviewerAgentConfig = Field(default_factory=ReviewerAgentConfig)
722
+ planner: PlannerAgentConfig = Field(default_factory=PlannerAgentConfig)
723
+ implementer: ImplementerAgentConfig = Field(default_factory=ImplementerAgentConfig)
724
+ tester: TesterAgentConfig = Field(default_factory=TesterAgentConfig)
725
+ debugger: DebuggerAgentConfig = Field(default_factory=DebuggerAgentConfig)
726
+ documenter: DocumenterAgentConfig = Field(default_factory=DocumenterAgentConfig)
727
+ architect: ArchitectAgentConfig = Field(default_factory=ArchitectAgentConfig)
728
+ designer: DesignerAgentConfig = Field(default_factory=DesignerAgentConfig)
729
+ ops: OpsAgentConfig = Field(default_factory=OpsAgentConfig)
730
+ enhancer: EnhancerAgentConfig = Field(default_factory=EnhancerAgentConfig)
731
+ analyst: AnalystAgentConfig = Field(default_factory=AnalystAgentConfig)
732
+ orchestrator: OrchestratorAgentConfig = Field(
733
+ default_factory=OrchestratorAgentConfig
734
+ )
735
+ evaluator: EvaluatorAgentConfig = Field(default_factory=EvaluatorAgentConfig)
736
+ cleanup_agent: CleanupAgentConfig = Field(default_factory=CleanupAgentConfig)
737
+
738
+
739
+ class CheckpointFrequencyConfig(BaseModel):
740
+ """Configuration for checkpoint frequency"""
741
+
742
+ mode: str = Field(
743
+ default="every_step",
744
+ description="Checkpoint frequency mode: every_step, every_n_steps, on_gates, time_based, manual",
745
+ )
746
+ interval: int = Field(
747
+ default=1,
748
+ ge=1,
749
+ description="Interval for every_n_steps (step count) or time_based (seconds)",
750
+ )
751
+ enabled: bool = Field(
752
+ default=True,
753
+ description="Enable checkpointing",
754
+ )
755
+
756
+
757
+ class StateCleanupPolicyConfig(BaseModel):
758
+ """Configuration for state cleanup policies"""
759
+
760
+ enabled: bool = Field(
761
+ default=True,
762
+ description="Enable automatic state cleanup",
763
+ )
764
+ retention_days: int | None = Field(
765
+ default=None,
766
+ ge=1,
767
+ description="Delete states older than N days (None = no retention limit)",
768
+ )
769
+ max_size_mb: int | None = Field(
770
+ default=None,
771
+ ge=1,
772
+ description="Maximum total state size in MB (None = no size limit)",
773
+ )
774
+ cleanup_schedule: str = Field(
775
+ default="daily",
776
+ description="Cleanup schedule: daily, weekly, monthly, on_startup, manual",
777
+ )
778
+ keep_latest: int = Field(
779
+ default=10,
780
+ ge=1,
781
+ description="Always keep the N most recent states",
782
+ )
783
+
784
+
785
+ class StatePersistenceConfig(BaseModel):
786
+ """Configuration for state persistence and checkpointing"""
787
+
788
+ enabled: bool = Field(
789
+ default=True,
790
+ description="Enable state persistence",
791
+ )
792
+ storage_location: str = Field(
793
+ default=".tapps-agents/workflow-state",
794
+ description="Directory for storing workflow state",
795
+ )
796
+ format: str = Field(
797
+ default="json",
798
+ description="State storage format: json, json_gzip",
799
+ )
800
+ compression: bool = Field(
801
+ default=False,
802
+ description="Enable compression for state files",
803
+ )
804
+ checkpoint: CheckpointFrequencyConfig = Field(
805
+ default_factory=CheckpointFrequencyConfig,
806
+ description="Checkpoint frequency configuration",
807
+ )
808
+ cleanup: StateCleanupPolicyConfig = Field(
809
+ default_factory=StateCleanupPolicyConfig,
810
+ description="State cleanup policy configuration",
811
+ )
812
+
813
+
814
+ class BranchCleanupConfig(BaseModel):
815
+ """Configuration for Git branch cleanup after workflow execution"""
816
+
817
+ enabled: bool = Field(
818
+ default=True,
819
+ description="Enable automatic branch cleanup",
820
+ )
821
+ delete_branches_on_cleanup: bool = Field(
822
+ default=True,
823
+ description="Delete associated Git branches when worktrees are removed",
824
+ )
825
+ retention_days: int = Field(
826
+ default=7,
827
+ ge=0,
828
+ description="Number of days to retain branches before cleanup (0 = immediate cleanup)",
829
+ )
830
+ auto_cleanup_on_completion: bool = Field(
831
+ default=True,
832
+ description="Automatically cleanup branches when workflow step completes",
833
+ )
834
+ patterns: dict[str, str] = Field(
835
+ default_factory=lambda: {
836
+ "workflow": "workflow/*",
837
+ "agent": "agent/*",
838
+ },
839
+ description="Branch patterns to match for cleanup (wildcards supported)",
840
+ )
841
+
842
+
843
+ class WorkflowDocsCleanupConfig(BaseModel):
844
+ """Configuration for workflow documentation cleanup"""
845
+
846
+ enabled: bool = Field(
847
+ default=True,
848
+ description="Enable workflow documentation cleanup",
849
+ )
850
+ keep_latest: int = Field(
851
+ default=5,
852
+ ge=1,
853
+ le=100,
854
+ description="Keep N most recent workflows visible",
855
+ )
856
+ retention_days: int = Field(
857
+ default=30,
858
+ ge=1,
859
+ description="Archive workflows older than N days",
860
+ )
861
+ archive_enabled: bool = Field(
862
+ default=True,
863
+ description="Enable archival of old workflows",
864
+ )
865
+ archive_dir: Path = Field(
866
+ default=Path(".tapps-agents/archives/workflows/"),
867
+ description="Directory for archived workflows (relative to project root)",
868
+ )
869
+ exclude_patterns: list[str] = Field(
870
+ default_factory=list,
871
+ description="Workflow IDs or patterns to never archive",
872
+ )
873
+
874
+
875
+ class SessionsCleanupConfig(BaseModel):
876
+ """Configuration for .tapps-agents/sessions cleanup (enhancer + SessionManager)."""
877
+
878
+ keep_latest: int = Field(
879
+ default=50,
880
+ ge=1,
881
+ le=500,
882
+ description="Keep N most recent enhancer session files",
883
+ )
884
+ max_age_days: int = Field(
885
+ default=30,
886
+ ge=1,
887
+ description="Remove enhancer sessions older than N days; also used for SessionManager cleanup (hours = max_age_days * 24)",
888
+ )
889
+ auto_cleanup_on_enhance: bool = Field(
890
+ default=False,
891
+ description="When True, run sessions cleanup after each enhancer save so the folder does not grow unbounded",
892
+ )
893
+
894
+
895
+ class CleanupConfig(BaseModel):
896
+ """Configuration for cleanup operations"""
897
+
898
+ workflow_docs: WorkflowDocsCleanupConfig = Field(
899
+ default_factory=WorkflowDocsCleanupConfig,
900
+ description="Workflow documentation cleanup configuration",
901
+ )
902
+ sessions: SessionsCleanupConfig = Field(
903
+ default_factory=SessionsCleanupConfig,
904
+ description="Sessions cleanup (enhancer + long-running agent sessions)",
905
+ )
906
+
907
+
908
+ class WorkflowArtifactConfig(BaseModel):
909
+ """Configuration for workflow artifact paths and naming."""
910
+
911
+ base_dir: str = Field(
912
+ default="docs/workflows",
913
+ description="Base directory for workflow artifacts (relative to project root)",
914
+ )
915
+ simple_mode_subdir: str = Field(
916
+ default="simple-mode",
917
+ description="Subdirectory for Simple Mode workflow artifacts",
918
+ )
919
+ auto_detect_existing: bool = Field(
920
+ default=True,
921
+ description="Auto-detect existing artifacts and adjust paths to avoid conflicts",
922
+ )
923
+ naming_pattern: str = Field(
924
+ default="{workflow_id}/{step_name}.md",
925
+ description="Pattern for artifact file naming. Supports: {workflow_id}, {step_name}, {timestamp}, {agent}",
926
+ )
927
+ print_paths_on_completion: bool = Field(
928
+ default=True,
929
+ description="Print artifact file paths after each step completes",
930
+ )
931
+
932
+ def get_artifact_dir(self, project_root: str | None = None) -> str:
933
+ """Get the full artifact directory path."""
934
+ from pathlib import Path
935
+ base = Path(project_root) if project_root else Path.cwd()
936
+ return str(base / self.base_dir / self.simple_mode_subdir)
937
+
938
+
939
+ class WorkflowFailureConfig(BaseModel):
940
+ """On-step-failure behavior: retry, skip, escalate, or fail (plan 3.1)."""
941
+
942
+ on_step_fail: str = Field(
943
+ default="fail",
944
+ description="Behavior on step failure: 'fail' | 'retry' | 'skip' | 'escalate'.",
945
+ )
946
+ retry_count: int = Field(
947
+ default=1,
948
+ ge=0,
949
+ le=10,
950
+ description="Max retries when on_step_fail=='retry'.",
951
+ )
952
+ escalate_to_pause: bool = Field(
953
+ default=True,
954
+ description="When on_step_fail is 'escalate' or 'fail', set status to 'paused' (True) or 'failed' (False).",
955
+ )
956
+
957
+
958
+ class WorkflowConfig(BaseModel):
959
+ """Configuration for workflow execution"""
960
+
961
+ failure: WorkflowFailureConfig = Field(
962
+ default_factory=WorkflowFailureConfig,
963
+ description="On-step-failure behavior: retry, skip, escalate (plan 3.1).",
964
+ )
965
+ polling_interval: float = Field(
966
+ default=5.0,
967
+ ge=1.0,
968
+ description="Seconds between status checks when polling for completion",
969
+ )
970
+ timeout_seconds: float = Field(
971
+ default=3600.0,
972
+ ge=1.0,
973
+ description="Maximum time to wait for step completion (seconds)",
974
+ )
975
+ duration_threshold_seconds: float = Field(
976
+ default=30.0,
977
+ ge=1.0,
978
+ description="Duration threshold for background agent routing (seconds). Tasks estimated to take longer use background agents.",
979
+ )
980
+ state_persistence: StatePersistenceConfig = Field(
981
+ default_factory=StatePersistenceConfig,
982
+ description="State persistence and checkpointing configuration",
983
+ )
984
+ branch_cleanup: BranchCleanupConfig = Field(
985
+ default_factory=BranchCleanupConfig,
986
+ description="Git branch cleanup configuration for workflow worktrees",
987
+ )
988
+ artifacts: WorkflowArtifactConfig = Field(
989
+ default_factory=WorkflowArtifactConfig,
990
+ description="Artifact paths and naming configuration",
991
+ )
992
+ graceful_partial_completion: bool = Field(
993
+ default=True,
994
+ description="Save partial results on failure instead of losing all progress",
995
+ )
996
+ pre_flight_validation: bool = Field(
997
+ default=True,
998
+ description="Run pre-flight validation before workflow execution",
999
+ )
1000
+
1001
+
1002
+ class ContextBudgetConfig(BaseModel):
1003
+ """Context budget when assembling Context7 and expert chunks (plan 3.2)."""
1004
+
1005
+ max_tokens_per_step: int = Field(
1006
+ default=0,
1007
+ ge=0,
1008
+ description="Max tokens per step (0=no hard limit).",
1009
+ )
1010
+ max_chunks_per_step: int = Field(
1011
+ default=50,
1012
+ ge=1,
1013
+ le=500,
1014
+ description="Max chunks when injecting Context7 or expert docs.",
1015
+ )
1016
+ priority: list[str] = Field(
1017
+ default_factory=lambda: ["current_task", "open_files", "recent_changes", "context7", "experts"],
1018
+ description="Priority order when assembling context (for future use).",
1019
+ )
1020
+
1021
+
1022
+ class BugFixAgentConfig(BaseModel):
1023
+ """Configuration for Bug Fix Agent."""
1024
+
1025
+ max_iterations: int = Field(
1026
+ default=3,
1027
+ ge=1,
1028
+ le=10,
1029
+ description="Maximum iterations for fix loopback (1-10)",
1030
+ )
1031
+ auto_commit: bool = Field(
1032
+ default=True,
1033
+ description="Automatically commit changes to main branch when quality passes",
1034
+ )
1035
+ quality_thresholds: dict[str, float] = Field(
1036
+ default_factory=lambda: {
1037
+ "overall_min": 7.0,
1038
+ "security_min": 6.5,
1039
+ "maintainability_min": 7.0,
1040
+ },
1041
+ description="Quality thresholds for bug fixes (lower than full SDLC)",
1042
+ )
1043
+ escalation_threshold: int = Field(
1044
+ default=2,
1045
+ ge=1,
1046
+ le=5,
1047
+ description="Escalate to human after N failed iterations (1-5)",
1048
+ )
1049
+ escalation_enabled: bool = Field(
1050
+ default=True,
1051
+ description="Enable human escalation after failed iterations",
1052
+ )
1053
+ pre_commit_security_scan: bool = Field(
1054
+ default=True,
1055
+ description="Run security scan before committing changes",
1056
+ )
1057
+ metrics_enabled: bool = Field(
1058
+ default=True,
1059
+ description="Enable metrics collection for observability",
1060
+ )
1061
+ commit_strategy: str = Field(
1062
+ default="direct_main",
1063
+ description="Commit strategy: 'direct_main' (commit directly) or 'pull_request' (create PR)",
1064
+ )
1065
+ auto_merge_pr: bool = Field(
1066
+ default=False,
1067
+ description="Auto-merge PR if quality gates pass (requires PR workflow)",
1068
+ )
1069
+ require_pr_review: bool = Field(
1070
+ default=False,
1071
+ description="Require human review before merging PR",
1072
+ )
1073
+
1074
+
1075
+ class ContinuousBugFixConfig(BaseModel):
1076
+ """Configuration for Continuous Bug Fix feature."""
1077
+
1078
+ max_iterations: int = Field(
1079
+ default=10,
1080
+ ge=1,
1081
+ le=100,
1082
+ description="Maximum loop iterations (1-100)",
1083
+ )
1084
+ commit_strategy: str = Field(
1085
+ default="one-per-bug",
1086
+ description="Commit strategy: 'one-per-bug' (default) or 'batch'",
1087
+ )
1088
+ auto_commit: bool = Field(
1089
+ default=True,
1090
+ description="Automatically commit fixes after bug-fix-agent succeeds",
1091
+ )
1092
+ test_path: str = Field(
1093
+ default="tests/",
1094
+ description="Default test directory or file to run",
1095
+ )
1096
+ skip_patterns: list[str] = Field(
1097
+ default_factory=list,
1098
+ description="Patterns for bugs to skip (not yet implemented)",
1099
+ )
1100
+
1101
+
1102
+ class BeadsConfig(BaseModel):
1103
+ """Configuration for Beads (bd) task-tracking integration."""
1104
+
1105
+ enabled: bool = Field(
1106
+ default=True,
1107
+ description="Enable Beads (bd) integration. When false, all beads features are no-ops.",
1108
+ )
1109
+ required: bool = Field(
1110
+ default=False,
1111
+ description="When true, workflows fail if bd is unavailable or .beads not initialized.",
1112
+ )
1113
+ sync_epic: bool = Field(
1114
+ default=True,
1115
+ description="When beads.enabled is true, sync epic to bd (create issues + deps) before *epic run.",
1116
+ )
1117
+ hooks_simple_mode: bool = Field(
1118
+ default=True,
1119
+ description="When beads.enabled is true, create/close bd issues at start/end of *build and *fix.",
1120
+ )
1121
+ hooks_workflow: bool = Field(
1122
+ default=True,
1123
+ description="When beads.enabled is true, create/close a bd issue at start/end of CLI workflow runs.",
1124
+ )
1125
+ hooks_review: bool = Field(
1126
+ default=False,
1127
+ description="When beads.enabled is true, create/close for *review.",
1128
+ )
1129
+ hooks_test: bool = Field(
1130
+ default=False,
1131
+ description="When beads.enabled is true, create/close for *test.",
1132
+ )
1133
+ hooks_refactor: bool = Field(
1134
+ default=False,
1135
+ description="When beads.enabled is true, create/close for *refactor.",
1136
+ )
1137
+
1138
+
1139
+ class SimpleModeConfig(BaseModel):
1140
+ """Configuration for Simple Mode."""
1141
+
1142
+ enabled: bool = Field(
1143
+ default=True, description="Enable Simple Mode (intent-based agent orchestration)"
1144
+ )
1145
+ auto_detect: bool = Field(
1146
+ default=True,
1147
+ description="Auto-enable Simple Mode for first-time users",
1148
+ )
1149
+ show_advanced: bool = Field(
1150
+ default=False, description="Show advanced agent-specific options"
1151
+ )
1152
+ natural_language: bool = Field(
1153
+ default=True, description="Enable natural language command parsing"
1154
+ )
1155
+ fast_mode_default: bool = Field(
1156
+ default=False,
1157
+ description="Default to fast mode for Simple Mode workflows (skip documentation steps)",
1158
+ )
1159
+ state_persistence_enabled: bool = Field(
1160
+ default=True,
1161
+ description="Enable workflow state persistence for resume capability",
1162
+ )
1163
+ checkpoint_retention_days: int = Field(
1164
+ default=30,
1165
+ ge=1,
1166
+ description="Days to retain workflow checkpoints",
1167
+ )
1168
+ documentation_organized: bool = Field(
1169
+ default=True,
1170
+ description="Organize documentation by workflow ID",
1171
+ )
1172
+ create_latest_symlink: bool = Field(
1173
+ default=False,
1174
+ description="Create 'latest' symlink to most recent workflow",
1175
+ )
1176
+ context7_refresh_after_planning: bool = Field(
1177
+ default=True,
1178
+ description="After build planning (Step 2), refresh Context7 cache for libraries from the plan. Set false to disable.",
1179
+ )
1180
+ context7_refresh_max_libraries: int = Field(
1181
+ default=10,
1182
+ ge=1,
1183
+ le=50,
1184
+ description="Max number of libraries to warm in post-planning Context7 refresh (limits API use).",
1185
+ )
1186
+ enable_checkpoints: bool = Field(
1187
+ default=True,
1188
+ description="Enable mid-execution workflow checkpoints (Checkpoint 1: After Enhance, Checkpoint 2: After Planning, Checkpoint 3: Quality Gate)",
1189
+ )
1190
+ checkpoint_confidence_threshold: float = Field(
1191
+ default=0.70,
1192
+ ge=0.0,
1193
+ le=1.0,
1194
+ description="Minimum confidence threshold for checkpoint recommendations (0.0-1.0)",
1195
+ )
1196
+
1197
+
1198
+ class AutoEnhancementConfig(BaseModel):
1199
+ """Configuration for automatic prompt enhancement"""
1200
+
1201
+ enabled: bool = Field(
1202
+ default=True, description="Enable auto-enhancement globally"
1203
+ )
1204
+ mode: str = Field(
1205
+ default="cursor-skills",
1206
+ description="Enhancement mode: cursor-skills, structured, or smart",
1207
+ )
1208
+ min_prompt_length: int = Field(
1209
+ default=20, description="Minimum prompt length to trigger enhancement"
1210
+ )
1211
+ quality_threshold: float = Field(
1212
+ default=50.0,
1213
+ ge=0.0,
1214
+ le=100.0,
1215
+ description="Quality score below which to enhance",
1216
+ )
1217
+
1218
+ commands: dict[str, dict[str, Any]] = Field(
1219
+ default_factory=lambda: {
1220
+ "implementer": {"enabled": True, "synthesis_mode": "full"},
1221
+ "planner": {"enabled": True, "synthesis_mode": "quick"},
1222
+ "analyst": {"enabled": True, "synthesis_mode": "quick"},
1223
+ "architect": {"enabled": False}, # Off by default to avoid over-use
1224
+ "designer": {"enabled": False}, # Off by default to avoid over-use
1225
+ },
1226
+ description="Per-command enhancement settings",
1227
+ )
1228
+
1229
+
1230
+ class EvaluationConfig(BaseModel):
1231
+ """Configuration for Evaluation & Quality Assurance Engine (Tier 1)"""
1232
+
1233
+ enabled: bool = Field(
1234
+ default=True, description="Enable comprehensive evaluation engine"
1235
+ )
1236
+ include_behavioral_validation: bool = Field(
1237
+ default=True, description="Include behavioral correctness validation"
1238
+ )
1239
+ include_spec_compliance: bool = Field(
1240
+ default=True, description="Include specification compliance checking"
1241
+ )
1242
+ include_architectural_checks: bool = Field(
1243
+ default=True, description="Include architectural pattern adherence checking"
1244
+ )
1245
+ issue_severity_thresholds: dict[str, int] = Field(
1246
+ default_factory=lambda: {
1247
+ "critical": 0, # Hard fail
1248
+ "high": 5, # Soft fail/loopback
1249
+ "medium": 10,
1250
+ "low": 20,
1251
+ },
1252
+ description="Maximum allowed issues by severity for gate passing",
1253
+ )
1254
+ enable_remediation_loop: bool = Field(
1255
+ default=True, description="Enable automatic remediation loops"
1256
+ )
1257
+ max_remediation_retries: int = Field(
1258
+ default=3, ge=1, le=10, description="Maximum remediation retry attempts"
1259
+ )
1260
+
1261
+
1262
+ class PromptLearningConfig(BaseModel):
1263
+ """Configuration for Continual System Prompt Learning (Tier 1)"""
1264
+
1265
+ enabled: bool = Field(
1266
+ default=True, description="Enable prompt learning system"
1267
+ )
1268
+ feedback_collection_enabled: bool = Field(
1269
+ default=True, description="Enable feedback collection from Cursor IDE"
1270
+ )
1271
+ ab_testing_enabled: bool = Field(
1272
+ default=True, description="Enable A/B testing for prompt variations"
1273
+ )
1274
+ auto_optimize_enabled: bool = Field(
1275
+ default=True, description="Enable automatic prompt optimization"
1276
+ )
1277
+ min_samples_for_optimization: int = Field(
1278
+ default=10, ge=5, description="Minimum feedback samples before optimization"
1279
+ )
1280
+ optimization_interval_hours: int = Field(
1281
+ default=24, ge=1, description="Hours between optimization runs"
1282
+ )
1283
+ prompt_version_retention_days: int = Field(
1284
+ default=90, ge=7, description="Days to retain prompt version history"
1285
+ )
1286
+ auto_update_skills: bool = Field(
1287
+ default=False, description="Automatically update Skills with learned prompts"
1288
+ )
1289
+ require_approval_for_updates: bool = Field(
1290
+ default=True, description="Require approval before updating prompts"
1291
+ )
1292
+
1293
+
1294
+ class KnowledgeEngineConfig(BaseModel):
1295
+ """Configuration for Knowledge Ecosystem Enhancement (Tier 1)"""
1296
+
1297
+ enabled: bool = Field(
1298
+ default=True, description="Enable always-on knowledge orchestration engine"
1299
+ )
1300
+ auto_detect_domains: bool = Field(
1301
+ default=True, description="Automatically detect project domains"
1302
+ )
1303
+ auto_create_experts: bool = Field(
1304
+ default=True, description="Automatically create project experts"
1305
+ )
1306
+ knowledge_ingestion_enabled: bool = Field(
1307
+ default=True, description="Enable automatic knowledge ingestion"
1308
+ )
1309
+ proactive_consultation: bool = Field(
1310
+ default=True, description="Proactively consult experts before needed"
1311
+ )
1312
+ metrics_tracking_enabled: bool = Field(
1313
+ default=True, description="Enable metrics tracking for knowledge quality"
1314
+ )
1315
+ kb_maintenance_schedule: str = Field(
1316
+ default="weekly", description="KB maintenance schedule: daily, weekly, monthly"
1317
+ )
1318
+ governance_enabled: bool = Field(
1319
+ default=True, description="Enable governance and safety layer"
1320
+ )
1321
+ require_approval_for_writes: bool = Field(
1322
+ default=False, description="Require approval before writing to knowledge base"
1323
+ )
1324
+
1325
+
1326
+ class ContextIntelligenceConfig(BaseModel):
1327
+ """Configuration for Context Intelligence Engine (Tier 1)"""
1328
+
1329
+ enabled: bool = Field(
1330
+ default=True, description="Enable context intelligence engine"
1331
+ )
1332
+ dynamic_gathering_enabled: bool = Field(
1333
+ default=True, description="Enable dynamic context gathering"
1334
+ )
1335
+ repository_exploration_enabled: bool = Field(
1336
+ default=True, description="Enable autonomous repository exploration"
1337
+ )
1338
+ pattern_recognition_enabled: bool = Field(
1339
+ default=True, description="Enable pattern recognition and cataloging"
1340
+ )
1341
+ similarity_detection_enabled: bool = Field(
1342
+ default=True, description="Enable similarity detection for code reuse"
1343
+ )
1344
+ historical_analysis_enabled: bool = Field(
1345
+ default=True, description="Enable historical context analysis"
1346
+ )
1347
+ relevance_ranking_enabled: bool = Field(
1348
+ default=True, description="Enable relevance-based context ranking"
1349
+ )
1350
+ token_budget_management_enabled: bool = Field(
1351
+ default=True, description="Enable token budget management"
1352
+ )
1353
+ default_token_budget: int = Field(
1354
+ default=4000, ge=1000, le=16000, description="Default token budget for context"
1355
+ )
1356
+ progressive_loading_enabled: bool = Field(
1357
+ default=True, description="Enable progressive context loading"
1358
+ )
1359
+
1360
+
1361
+ class AnalyticsConfig(BaseModel):
1362
+ """Configuration for analytics and dual-write from execution metrics."""
1363
+
1364
+ record_from_execution: bool = Field(
1365
+ default=True,
1366
+ description="When True, also write to .tapps-agents/analytics/ when recording execution metrics (step and workflow). Set False for metrics-only (no analytics).",
1367
+ )
1368
+
1369
+
1370
+ class GuardrailConfig(BaseModel):
1371
+ """Security guardrails for subprocess and file writes (plan 2.2)."""
1372
+
1373
+ sandbox_subprocess: bool = Field(
1374
+ default=True,
1375
+ description="When True, never use shell for subprocess; use cwd=project_root.",
1376
+ )
1377
+ allowed_paths_write: list[str] = Field(
1378
+ default_factory=list,
1379
+ description="When non-empty, writes must be under project_root and under one of these prefixes (e.g. src, tests, docs, .tapps-agents). When empty, only under project_root.",
1380
+ )
1381
+
1382
+
1383
+ class HumanOversightConfig(BaseModel):
1384
+ """Human-in-the-loop oversight for *build and *full (plan 2.3)."""
1385
+
1386
+ checkpoints_before_steps: list[str] = Field(
1387
+ default_factory=list,
1388
+ description="Step/agent names before which to prompt: e.g. implementer, designer. If --auto, skipped.",
1389
+ )
1390
+ require_diff_review_implementer: bool = Field(
1391
+ default=False,
1392
+ description="Reserved: when implementer supports preview/diff, require review before apply. Not implemented.",
1393
+ )
1394
+ branch_for_agent_changes: bool = Field(
1395
+ default=True,
1396
+ description="For *build and *full: create branch tapps-agents/build-{workflow_id}; merge to main only after human review.",
1397
+ )
1398
+
1399
+
1400
+ class ProjectConfig(BaseModel):
1401
+ """Root configuration model for TappsCodingAgents project"""
1402
+
1403
+ # Project metadata
1404
+ project_name: str | None = Field(default=None, description="Project name")
1405
+ version: str | None = Field(default=None, description="Project version")
1406
+
1407
+ # CLI / runtime
1408
+ offline_mode: bool = Field(
1409
+ default=False,
1410
+ description="Use offline mode (no network) when possible for CLI and agents",
1411
+ )
1412
+
1413
+ # Core configuration
1414
+ tooling: ToolingConfig = Field(
1415
+ default_factory=ToolingConfig,
1416
+ description="Canonical runtime/tool targets and policy (used by doctor/CI/agents)",
1417
+ )
1418
+ agents: AgentsConfig = Field(default_factory=AgentsConfig)
1419
+ scoring: ScoringConfig = Field(default_factory=ScoringConfig)
1420
+ expert: ExpertConfig = Field(
1421
+ default_factory=ExpertConfig,
1422
+ description="Expert consultation system configuration",
1423
+ )
1424
+ context7: Context7Config | None = Field(
1425
+ default_factory=Context7Config, description="Context7 integration configuration"
1426
+ )
1427
+ context_budget: ContextBudgetConfig = Field(
1428
+ default_factory=ContextBudgetConfig,
1429
+ description="Context budget: max_chunks for Context7/expert injection; surface cached_at (plan 3.2).",
1430
+ )
1431
+ quality_tools: QualityToolsConfig | None = Field(
1432
+ default_factory=QualityToolsConfig,
1433
+ description="Quality analysis tools configuration (Phase 6)",
1434
+ )
1435
+ workflow: WorkflowConfig = Field(
1436
+ default_factory=WorkflowConfig,
1437
+ description="Workflow execution configuration",
1438
+ )
1439
+ analytics: AnalyticsConfig = Field(
1440
+ default_factory=AnalyticsConfig,
1441
+ description="Analytics and dual-write from execution metrics (health usage, outcomes).",
1442
+ )
1443
+ guardrails: GuardrailConfig = Field(
1444
+ default_factory=GuardrailConfig,
1445
+ description="Security guardrails: subprocess sandbox, path allowlist for writes (plan 2.2).",
1446
+ )
1447
+ human_oversight: HumanOversightConfig = Field(
1448
+ default_factory=HumanOversightConfig,
1449
+ description="Human oversight: step checkpoints, branch-for-agent-changes (plan 2.3). require_diff_review_implementer reserved.",
1450
+ )
1451
+ simple_mode: SimpleModeConfig = Field(
1452
+ default_factory=SimpleModeConfig,
1453
+ description="Simple Mode configuration (intent-based orchestration)",
1454
+ )
1455
+ beads: BeadsConfig = Field(
1456
+ default_factory=BeadsConfig,
1457
+ description="Beads (bd) task-tracking integration. See docs/BEADS_INTEGRATION.md.",
1458
+ )
1459
+ bug_fix_agent: BugFixAgentConfig = Field(
1460
+ default_factory=BugFixAgentConfig,
1461
+ description="Bug Fix Agent configuration (automated bug fixing with auto-commit)",
1462
+ )
1463
+ continuous_bug_fix: ContinuousBugFixConfig = Field(
1464
+ default_factory=ContinuousBugFixConfig,
1465
+ description="Continuous Bug Fix configuration (automated bug finding and fixing loop)",
1466
+ )
1467
+ auto_enhancement: AutoEnhancementConfig = Field(
1468
+ default_factory=AutoEnhancementConfig,
1469
+ description="Automatic prompt enhancement configuration",
1470
+ )
1471
+
1472
+ # Tier 1 Enhancement Configurations
1473
+ evaluation: EvaluationConfig = Field(
1474
+ default_factory=EvaluationConfig,
1475
+ description="Evaluation & Quality Assurance Engine configuration (Tier 1)",
1476
+ )
1477
+ prompt_learning: PromptLearningConfig = Field(
1478
+ default_factory=PromptLearningConfig,
1479
+ description="Continual System Prompt Learning configuration (Tier 1)",
1480
+ )
1481
+ knowledge_engine: KnowledgeEngineConfig = Field(
1482
+ default_factory=KnowledgeEngineConfig,
1483
+ description="Knowledge Ecosystem Enhancement configuration (Tier 1)",
1484
+ )
1485
+ context_intelligence: ContextIntelligenceConfig = Field(
1486
+ default_factory=ContextIntelligenceConfig,
1487
+ description="Context Intelligence Engine configuration (Tier 1)",
1488
+ )
1489
+ cleanup: CleanupConfig = Field(
1490
+ default_factory=CleanupConfig,
1491
+ description="Cleanup operations configuration",
1492
+ )
1493
+
1494
+ model_config = {
1495
+ "extra": "ignore", # Ignore unknown fields
1496
+ "validate_assignment": True,
1497
+ }
1498
+
1499
+
1500
+ def load_config(config_path: Path | None = None) -> ProjectConfig:
1501
+ """
1502
+ Load configuration from YAML file.
1503
+
1504
+ Args:
1505
+ config_path: Path to config.yaml file. If None, looks for `.tapps-agents/config.yaml`
1506
+ in current directory or parent directories.
1507
+
1508
+ Returns:
1509
+ ProjectConfig instance with loaded values
1510
+
1511
+ Raises:
1512
+ FileNotFoundError: If config file doesn't exist and no defaults available
1513
+ ValueError: If config file is invalid
1514
+ """
1515
+ if config_path is None:
1516
+ # Look for .tapps-agents/config.yaml in current and parent directories
1517
+ current = Path.cwd()
1518
+ for parent in [current] + list(current.parents):
1519
+ candidate = parent / ".tapps-agents" / "config.yaml"
1520
+ if candidate.exists():
1521
+ config_path = candidate
1522
+ break
1523
+
1524
+ if config_path is None:
1525
+ # Return defaults if no config file found
1526
+ return ProjectConfig()
1527
+
1528
+ if not config_path.exists():
1529
+ # Return defaults if file doesn't exist
1530
+ return ProjectConfig()
1531
+
1532
+ try:
1533
+ # Use utf-8-sig to automatically handle UTF-8 BOM (common on Windows)
1534
+ # This prevents YAML parsing failures when files have BOM prefix (\xef\xbb\xbf)
1535
+ with open(config_path, encoding="utf-8-sig") as f:
1536
+ data = yaml.safe_load(f)
1537
+
1538
+ if data is None:
1539
+ # Empty file, return defaults
1540
+ return ProjectConfig()
1541
+
1542
+ # Validate and load config
1543
+ return ProjectConfig(**data)
1544
+ except yaml.YAMLError as e:
1545
+ raise ValueError(f"Invalid YAML in config file {config_path}: {e}") from e
1546
+ except Exception as e:
1547
+ raise ValueError(f"Error loading config from {config_path}: {e}") from e
1548
+
1549
+
1550
+ def save_config(config_path: Path, config: ProjectConfig) -> None:
1551
+ """
1552
+ Save configuration to a YAML file.
1553
+
1554
+ Args:
1555
+ config_path: Path to the config file
1556
+ config: ProjectConfig instance to save
1557
+
1558
+ Raises:
1559
+ OSError: If the file cannot be written
1560
+ """
1561
+ config_path.parent.mkdir(parents=True, exist_ok=True)
1562
+
1563
+ # Load existing config to preserve other settings
1564
+ existing_data: dict[str, Any] = {}
1565
+ if config_path.exists():
1566
+ try:
1567
+ # Use utf-8-sig to handle UTF-8 BOM (common on Windows)
1568
+ with open(config_path, encoding="utf-8-sig") as f:
1569
+ existing_data = yaml.safe_load(f) or {}
1570
+ except Exception:
1571
+ # If we can't read existing config, start fresh
1572
+ existing_data = {}
1573
+
1574
+ # Merge with new config (convert to dict, preserving existing values)
1575
+ new_data = config.model_dump(exclude_none=True, mode="json")
1576
+
1577
+ def deep_merge(existing: dict[str, Any], new: dict[str, Any]) -> dict[str, Any]:
1578
+ """Recursively merge new dict into existing dict."""
1579
+ result = existing.copy()
1580
+ for key, value in new.items():
1581
+ if key in result and isinstance(result[key], dict) and isinstance(value, dict):
1582
+ # Recursively merge nested dictionaries
1583
+ result[key] = deep_merge(result[key], value)
1584
+ else:
1585
+ # Overwrite with new value
1586
+ result[key] = value
1587
+ return result
1588
+
1589
+ # Deep merge: preserve existing nested values
1590
+ existing_data = deep_merge(existing_data, new_data)
1591
+
1592
+ # Save
1593
+ with open(config_path, "w", encoding="utf-8") as f:
1594
+ yaml.dump(existing_data, f, default_flow_style=False, sort_keys=False, allow_unicode=True)
1595
+
1596
+
1597
+ def get_default_config() -> dict[str, Any]:
1598
+ """
1599
+ Get default configuration as a dictionary (for template generation).
1600
+
1601
+ Returns:
1602
+ Dictionary representation of default config with Path objects as strings
1603
+ """
1604
+ config = ProjectConfig()
1605
+ # Use model_dump with mode="json" to serialize Path objects as strings
1606
+ # This is required for YAML serialization (yaml.safe_dump can't handle Path objects)
1607
+ return config.model_dump(exclude_none=True, mode="json")
1608
+
1609
+
1610
+ def get_expert_config(config: ProjectConfig | None = None) -> ExpertConfig:
1611
+ """
1612
+ Get expert configuration, loading from file if needed.
1613
+
1614
+ Args:
1615
+ config: Optional ProjectConfig instance. If None, loads from file.
1616
+
1617
+ Returns:
1618
+ ExpertConfig instance
1619
+ """
1620
+ if config is None:
1621
+ config = load_config()
1622
+ return config.expert