tapps-agents 3.5.41__py3-none-any.whl → 3.6.1__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 (141) hide show
  1. tapps_agents/__init__.py +2 -2
  2. tapps_agents/agents/reviewer/scoring.py +1566 -1566
  3. tapps_agents/agents/reviewer/tools/__init__.py +41 -41
  4. tapps_agents/cli/commands/health.py +665 -665
  5. tapps_agents/cli/commands/top_level.py +3586 -3586
  6. tapps_agents/core/artifact_context_builder.py +293 -0
  7. tapps_agents/core/config.py +33 -0
  8. tapps_agents/health/orchestrator.py +271 -271
  9. tapps_agents/resources/__init__.py +5 -0
  10. tapps_agents/resources/claude/__init__.py +1 -0
  11. tapps_agents/resources/claude/commands/README.md +156 -0
  12. tapps_agents/resources/claude/commands/__init__.py +1 -0
  13. tapps_agents/resources/claude/commands/build-fix.md +22 -0
  14. tapps_agents/resources/claude/commands/build.md +77 -0
  15. tapps_agents/resources/claude/commands/debug.md +53 -0
  16. tapps_agents/resources/claude/commands/design.md +68 -0
  17. tapps_agents/resources/claude/commands/docs.md +53 -0
  18. tapps_agents/resources/claude/commands/e2e.md +22 -0
  19. tapps_agents/resources/claude/commands/fix.md +54 -0
  20. tapps_agents/resources/claude/commands/implement.md +53 -0
  21. tapps_agents/resources/claude/commands/improve.md +53 -0
  22. tapps_agents/resources/claude/commands/library-docs.md +64 -0
  23. tapps_agents/resources/claude/commands/lint.md +52 -0
  24. tapps_agents/resources/claude/commands/plan.md +65 -0
  25. tapps_agents/resources/claude/commands/refactor-clean.md +21 -0
  26. tapps_agents/resources/claude/commands/refactor.md +55 -0
  27. tapps_agents/resources/claude/commands/review.md +67 -0
  28. tapps_agents/resources/claude/commands/score.md +60 -0
  29. tapps_agents/resources/claude/commands/security-review.md +22 -0
  30. tapps_agents/resources/claude/commands/security-scan.md +54 -0
  31. tapps_agents/resources/claude/commands/tdd.md +24 -0
  32. tapps_agents/resources/claude/commands/test-coverage.md +21 -0
  33. tapps_agents/resources/claude/commands/test.md +54 -0
  34. tapps_agents/resources/claude/commands/update-codemaps.md +20 -0
  35. tapps_agents/resources/claude/commands/update-docs.md +21 -0
  36. tapps_agents/resources/claude/skills/__init__.py +1 -0
  37. tapps_agents/resources/claude/skills/analyst/SKILL.md +272 -0
  38. tapps_agents/resources/claude/skills/analyst/__init__.py +1 -0
  39. tapps_agents/resources/claude/skills/architect/SKILL.md +282 -0
  40. tapps_agents/resources/claude/skills/architect/__init__.py +1 -0
  41. tapps_agents/resources/claude/skills/backend-patterns/SKILL.md +30 -0
  42. tapps_agents/resources/claude/skills/backend-patterns/__init__.py +1 -0
  43. tapps_agents/resources/claude/skills/coding-standards/SKILL.md +29 -0
  44. tapps_agents/resources/claude/skills/coding-standards/__init__.py +1 -0
  45. tapps_agents/resources/claude/skills/debugger/SKILL.md +203 -0
  46. tapps_agents/resources/claude/skills/debugger/__init__.py +1 -0
  47. tapps_agents/resources/claude/skills/designer/SKILL.md +243 -0
  48. tapps_agents/resources/claude/skills/designer/__init__.py +1 -0
  49. tapps_agents/resources/claude/skills/documenter/SKILL.md +252 -0
  50. tapps_agents/resources/claude/skills/documenter/__init__.py +1 -0
  51. tapps_agents/resources/claude/skills/enhancer/SKILL.md +307 -0
  52. tapps_agents/resources/claude/skills/enhancer/__init__.py +1 -0
  53. tapps_agents/resources/claude/skills/evaluator/SKILL.md +204 -0
  54. tapps_agents/resources/claude/skills/evaluator/__init__.py +1 -0
  55. tapps_agents/resources/claude/skills/frontend-patterns/SKILL.md +29 -0
  56. tapps_agents/resources/claude/skills/frontend-patterns/__init__.py +1 -0
  57. tapps_agents/resources/claude/skills/implementer/SKILL.md +188 -0
  58. tapps_agents/resources/claude/skills/implementer/__init__.py +1 -0
  59. tapps_agents/resources/claude/skills/improver/SKILL.md +218 -0
  60. tapps_agents/resources/claude/skills/improver/__init__.py +1 -0
  61. tapps_agents/resources/claude/skills/ops/SKILL.md +281 -0
  62. tapps_agents/resources/claude/skills/ops/__init__.py +1 -0
  63. tapps_agents/resources/claude/skills/orchestrator/SKILL.md +390 -0
  64. tapps_agents/resources/claude/skills/orchestrator/__init__.py +1 -0
  65. tapps_agents/resources/claude/skills/planner/SKILL.md +254 -0
  66. tapps_agents/resources/claude/skills/planner/__init__.py +1 -0
  67. tapps_agents/resources/claude/skills/reviewer/SKILL.md +434 -0
  68. tapps_agents/resources/claude/skills/reviewer/__init__.py +1 -0
  69. tapps_agents/resources/claude/skills/security-review/SKILL.md +31 -0
  70. tapps_agents/resources/claude/skills/security-review/__init__.py +1 -0
  71. tapps_agents/resources/claude/skills/simple-mode/SKILL.md +695 -0
  72. tapps_agents/resources/claude/skills/simple-mode/__init__.py +1 -0
  73. tapps_agents/resources/claude/skills/tester/SKILL.md +219 -0
  74. tapps_agents/resources/claude/skills/tester/__init__.py +1 -0
  75. tapps_agents/resources/cursor/.cursorignore +35 -0
  76. tapps_agents/resources/cursor/__init__.py +1 -0
  77. tapps_agents/resources/cursor/commands/__init__.py +1 -0
  78. tapps_agents/resources/cursor/commands/build-fix.md +11 -0
  79. tapps_agents/resources/cursor/commands/build.md +11 -0
  80. tapps_agents/resources/cursor/commands/e2e.md +11 -0
  81. tapps_agents/resources/cursor/commands/fix.md +11 -0
  82. tapps_agents/resources/cursor/commands/refactor-clean.md +11 -0
  83. tapps_agents/resources/cursor/commands/review.md +11 -0
  84. tapps_agents/resources/cursor/commands/security-review.md +11 -0
  85. tapps_agents/resources/cursor/commands/tdd.md +11 -0
  86. tapps_agents/resources/cursor/commands/test-coverage.md +11 -0
  87. tapps_agents/resources/cursor/commands/test.md +11 -0
  88. tapps_agents/resources/cursor/commands/update-codemaps.md +10 -0
  89. tapps_agents/resources/cursor/commands/update-docs.md +11 -0
  90. tapps_agents/resources/cursor/rules/__init__.py +1 -0
  91. tapps_agents/resources/cursor/rules/agent-capabilities.mdc +687 -0
  92. tapps_agents/resources/cursor/rules/coding-style.mdc +31 -0
  93. tapps_agents/resources/cursor/rules/command-reference.mdc +2081 -0
  94. tapps_agents/resources/cursor/rules/cursor-mode-usage.mdc +125 -0
  95. tapps_agents/resources/cursor/rules/git-workflow.mdc +29 -0
  96. tapps_agents/resources/cursor/rules/performance.mdc +29 -0
  97. tapps_agents/resources/cursor/rules/project-context.mdc +163 -0
  98. tapps_agents/resources/cursor/rules/project-profiling.mdc +197 -0
  99. tapps_agents/resources/cursor/rules/quick-reference.mdc +630 -0
  100. tapps_agents/resources/cursor/rules/security.mdc +32 -0
  101. tapps_agents/resources/cursor/rules/simple-mode.mdc +500 -0
  102. tapps_agents/resources/cursor/rules/testing.mdc +31 -0
  103. tapps_agents/resources/cursor/rules/when-to-use.mdc +156 -0
  104. tapps_agents/resources/cursor/rules/workflow-presets.mdc +179 -0
  105. tapps_agents/resources/customizations/__init__.py +1 -0
  106. tapps_agents/resources/customizations/example-custom.yaml +83 -0
  107. tapps_agents/resources/hooks/__init__.py +1 -0
  108. tapps_agents/resources/hooks/templates/README.md +5 -0
  109. tapps_agents/resources/hooks/templates/__init__.py +1 -0
  110. tapps_agents/resources/hooks/templates/add-project-context.yaml +8 -0
  111. tapps_agents/resources/hooks/templates/auto-format-js.yaml +10 -0
  112. tapps_agents/resources/hooks/templates/auto-format-python.yaml +10 -0
  113. tapps_agents/resources/hooks/templates/git-commit-check.yaml +7 -0
  114. tapps_agents/resources/hooks/templates/notify-on-complete.yaml +8 -0
  115. tapps_agents/resources/hooks/templates/quality-gate.yaml +8 -0
  116. tapps_agents/resources/hooks/templates/security-scan-on-edit.yaml +10 -0
  117. tapps_agents/resources/hooks/templates/session-end-log.yaml +7 -0
  118. tapps_agents/resources/hooks/templates/show-beads-ready.yaml +8 -0
  119. tapps_agents/resources/hooks/templates/test-on-edit.yaml +10 -0
  120. tapps_agents/resources/hooks/templates/update-docs-on-complete.yaml +8 -0
  121. tapps_agents/resources/hooks/templates/user-prompt-log.yaml +7 -0
  122. tapps_agents/resources/scripts/__init__.py +1 -0
  123. tapps_agents/resources/scripts/set_bd_path.ps1 +51 -0
  124. tapps_agents/resources/workflows/__init__.py +1 -0
  125. tapps_agents/resources/workflows/presets/__init__.py +1 -0
  126. tapps_agents/resources/workflows/presets/brownfield-analysis.yaml +235 -0
  127. tapps_agents/resources/workflows/presets/fix.yaml +78 -0
  128. tapps_agents/resources/workflows/presets/full-sdlc.yaml +122 -0
  129. tapps_agents/resources/workflows/presets/quality.yaml +82 -0
  130. tapps_agents/resources/workflows/presets/rapid-dev.yaml +84 -0
  131. tapps_agents/simple_mode/orchestrators/base.py +185 -185
  132. tapps_agents/simple_mode/orchestrators/build_orchestrator.py +2700 -2667
  133. tapps_agents/simple_mode/orchestrators/fix_orchestrator.py +723 -723
  134. tapps_agents/workflow/cursor_executor.py +2337 -2337
  135. tapps_agents/workflow/message_formatter.py +188 -188
  136. {tapps_agents-3.5.41.dist-info → tapps_agents-3.6.1.dist-info}/METADATA +6 -6
  137. {tapps_agents-3.5.41.dist-info → tapps_agents-3.6.1.dist-info}/RECORD +141 -18
  138. {tapps_agents-3.5.41.dist-info → tapps_agents-3.6.1.dist-info}/WHEEL +0 -0
  139. {tapps_agents-3.5.41.dist-info → tapps_agents-3.6.1.dist-info}/entry_points.txt +0 -0
  140. {tapps_agents-3.5.41.dist-info → tapps_agents-3.6.1.dist-info}/licenses/LICENSE +0 -0
  141. {tapps_agents-3.5.41.dist-info → tapps_agents-3.6.1.dist-info}/top_level.txt +0 -0
@@ -1,271 +1,271 @@
1
- """
2
- Health Check Orchestrator.
3
-
4
- Coordinates execution of all health checks and aggregates results.
5
- """
6
-
7
- from __future__ import annotations
8
-
9
- import concurrent.futures
10
- import logging
11
- from pathlib import Path
12
- from typing import Any
13
-
14
- from .base import HealthCheckResult
15
- from .collector import HealthMetricsCollector
16
- from .registry import HealthCheckRegistry
17
-
18
- logger = logging.getLogger(__name__)
19
-
20
-
21
- class HealthOrchestrator:
22
- """Orchestrates health check execution and aggregation."""
23
-
24
- def __init__(
25
- self,
26
- registry: HealthCheckRegistry | None = None,
27
- metrics_collector: HealthMetricsCollector | None = None,
28
- project_root: Path | None = None,
29
- ):
30
- """
31
- Initialize health orchestrator.
32
-
33
- Args:
34
- registry: Health check registry (creates default if None)
35
- metrics_collector: Metrics collector (creates default if None)
36
- project_root: Project root directory
37
- """
38
- self.registry = registry or HealthCheckRegistry()
39
- self.metrics_collector = metrics_collector or HealthMetricsCollector(
40
- project_root=project_root
41
- )
42
- self.project_root = project_root or Path.cwd()
43
-
44
- def run_all_checks(
45
- self, check_names: list[str] | None = None, save_metrics: bool = True
46
- ) -> dict[str, HealthCheckResult]:
47
- """
48
- Run all health checks (or specified subset).
49
-
50
- Args:
51
- check_names: Optional list of check names to run. If None, runs all.
52
- save_metrics: Whether to save results to metrics storage
53
-
54
- Returns:
55
- Dictionary mapping check names to HealthCheckResult instances
56
- """
57
- results = self.registry.run_all(check_names)
58
-
59
- # Save metrics if requested
60
- if save_metrics:
61
- for result in results.values():
62
- if result:
63
- self.metrics_collector.record_health_check_result(result)
64
-
65
- return results
66
-
67
- def run_checks_parallel(
68
- self, check_names: list[str] | None = None, max_workers: int = 4
69
- ) -> dict[str, HealthCheckResult]:
70
- """
71
- Run health checks in parallel where possible.
72
-
73
- Args:
74
- check_names: Optional list of check names to run
75
- max_workers: Maximum number of parallel workers
76
-
77
- Returns:
78
- Dictionary mapping check names to HealthCheckResult instances
79
- """
80
- if check_names is None:
81
- check_names = self.registry.list_names()
82
-
83
- # Build dependency graph
84
- dependency_graph: dict[str, set[str]] = {}
85
- independent_checks: list[str] = []
86
- dependent_checks: list[str] = []
87
-
88
- for name in check_names:
89
- check = self.registry.get(name)
90
- if check:
91
- deps = check.get_dependencies()
92
- dependency_graph[name] = set(deps)
93
- if not deps:
94
- independent_checks.append(name)
95
- else:
96
- dependent_checks.append(name)
97
-
98
- results: dict[str, HealthCheckResult] = {}
99
-
100
- # Run independent checks in parallel
101
- if independent_checks:
102
- with concurrent.futures.ThreadPoolExecutor(max_workers=max_workers) as executor:
103
- future_to_check = {
104
- executor.submit(self.registry.run, name): name for name in independent_checks
105
- }
106
- for future in concurrent.futures.as_completed(future_to_check):
107
- check_name = future_to_check[future]
108
- try:
109
- result = future.result()
110
- if result:
111
- results[check_name] = result
112
- except Exception as e:
113
- logger.error(f"Error running check {check_name}: {e}", exc_info=True)
114
- results[check_name] = HealthCheckResult(
115
- name=check_name,
116
- status="unhealthy",
117
- score=0.0,
118
- message=f"Check failed with error: {e}",
119
- details={"error": str(e)},
120
- )
121
-
122
- # Run dependent checks sequentially (respecting dependencies)
123
- execution_order = self.registry._topological_sort(dependent_checks)
124
- for name in execution_order:
125
- if name not in results:
126
- result = self.registry.run(name)
127
- if result:
128
- results[name] = result
129
-
130
- # Save metrics
131
- for result in results.values():
132
- if result:
133
- self.metrics_collector.record_health_check_result(result)
134
-
135
- return results
136
-
137
- def get_overall_health(self, results: dict[str, HealthCheckResult] | None = None) -> dict[str, Any]:
138
- """
139
- Calculate overall health status from check results.
140
-
141
- Args:
142
- results: Optional check results (runs all checks if None)
143
-
144
- Returns:
145
- Dictionary with overall health information
146
- """
147
- if results is None:
148
- results = self.run_all_checks(save_metrics=True)
149
-
150
- if not results:
151
- return {
152
- "status": "unknown",
153
- "score": 0.0,
154
- "message": "No health checks available",
155
- "checks_count": 0,
156
- }
157
-
158
- # Calculate weighted average score
159
- # Critical checks (environment, execution) have higher weight
160
- critical_checks = {"environment", "execution"}
161
- total_weight = 0.0
162
- weighted_score = 0.0
163
-
164
- status_counts = {"healthy": 0, "degraded": 0, "unhealthy": 0}
165
-
166
- for name, result in results.items():
167
- if not result:
168
- continue
169
-
170
- weight = 2.0 if name in critical_checks else 1.0
171
- total_weight += weight
172
- weighted_score += result.score * weight
173
-
174
- status_counts[result.status] = status_counts.get(result.status, 0) + 1
175
-
176
- overall_score = weighted_score / total_weight if total_weight > 0 else 0.0
177
-
178
- # Determine overall status (HM-001-S3: degraded when score >= 75 and only non-critical unhealthy)
179
- critical_checks = {"environment", "execution"}
180
- non_critical_checks = {"outcomes", "knowledge_base", "context7_cache", "automation"}
181
- unhealthy_checks = [
182
- name
183
- for name, result in results.items()
184
- if result and result.status == "unhealthy"
185
- ]
186
- critical_healthy = all(
187
- (results.get(name) and results[name].status != "unhealthy")
188
- for name in critical_checks
189
- if name in results
190
- )
191
-
192
- status_reason: str | None = None
193
- if status_counts["unhealthy"] > 0:
194
- overall_status = "unhealthy"
195
- if (
196
- overall_score >= 75.0
197
- and critical_healthy
198
- and unhealthy_checks
199
- and all(c in non_critical_checks for c in unhealthy_checks)
200
- ):
201
- overall_status = "degraded"
202
- status_reason = (
203
- "Status degraded due to non-critical checks; core functionality is healthy"
204
- )
205
- elif status_counts["degraded"] > 0:
206
- overall_status = "degraded"
207
- else:
208
- overall_status = "healthy"
209
-
210
- # Build remediation list (prioritized)
211
- all_remediations: list[str] = []
212
- for name, result in results.items():
213
- if result and result.remediation:
214
- if isinstance(result.remediation, list):
215
- all_remediations.extend(result.remediation)
216
- elif isinstance(result.remediation, str):
217
- all_remediations.append(result.remediation)
218
-
219
- # Deduplicate and prioritize
220
- unique_remediations = []
221
- seen = set()
222
- for rem in all_remediations:
223
- rem_lower = rem.lower()
224
- if rem_lower not in seen:
225
- seen.add(rem_lower)
226
- unique_remediations.append(rem)
227
-
228
- # Prioritize: unhealthy checks first, then degraded
229
- prioritized_remediations = []
230
- for name, result in results.items():
231
- if result and result.remediation and result.status == "unhealthy":
232
- if isinstance(result.remediation, list):
233
- prioritized_remediations.extend(result.remediation)
234
- else:
235
- prioritized_remediations.append(result.remediation)
236
-
237
- for name, result in results.items():
238
- if result and result.remediation and result.status == "degraded":
239
- if isinstance(result.remediation, list):
240
- prioritized_remediations.extend(result.remediation)
241
- else:
242
- prioritized_remediations.append(result.remediation)
243
-
244
- # Add remaining unique remediations
245
- for rem in unique_remediations:
246
- if rem not in prioritized_remediations:
247
- prioritized_remediations.append(rem)
248
-
249
- details: dict[str, Any] = {}
250
- if status_reason:
251
- details["status_reason"] = status_reason
252
-
253
- return {
254
- "status": overall_status,
255
- "score": overall_score,
256
- "message": f"Overall health: {overall_status} ({overall_score:.1f}/100)",
257
- "checks_count": len(results),
258
- "status_counts": status_counts,
259
- "details": details,
260
- "checks": {
261
- name: {
262
- "status": result.status,
263
- "score": result.score,
264
- "message": result.message,
265
- }
266
- for name, result in results.items()
267
- if result
268
- },
269
- "remediation": prioritized_remediations[:5], # Top 5 remediation actions
270
- }
271
-
1
+ """
2
+ Health Check Orchestrator.
3
+
4
+ Coordinates execution of all health checks and aggregates results.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import concurrent.futures
10
+ import logging
11
+ from pathlib import Path
12
+ from typing import Any
13
+
14
+ from .base import HealthCheckResult
15
+ from .collector import HealthMetricsCollector
16
+ from .registry import HealthCheckRegistry
17
+
18
+ logger = logging.getLogger(__name__)
19
+
20
+
21
+ class HealthOrchestrator:
22
+ """Orchestrates health check execution and aggregation."""
23
+
24
+ def __init__(
25
+ self,
26
+ registry: HealthCheckRegistry | None = None,
27
+ metrics_collector: HealthMetricsCollector | None = None,
28
+ project_root: Path | None = None,
29
+ ):
30
+ """
31
+ Initialize health orchestrator.
32
+
33
+ Args:
34
+ registry: Health check registry (creates default if None)
35
+ metrics_collector: Metrics collector (creates default if None)
36
+ project_root: Project root directory
37
+ """
38
+ self.registry = registry or HealthCheckRegistry()
39
+ self.metrics_collector = metrics_collector or HealthMetricsCollector(
40
+ project_root=project_root
41
+ )
42
+ self.project_root = project_root or Path.cwd()
43
+
44
+ def run_all_checks(
45
+ self, check_names: list[str] | None = None, save_metrics: bool = True
46
+ ) -> dict[str, HealthCheckResult]:
47
+ """
48
+ Run all health checks (or specified subset).
49
+
50
+ Args:
51
+ check_names: Optional list of check names to run. If None, runs all.
52
+ save_metrics: Whether to save results to metrics storage
53
+
54
+ Returns:
55
+ Dictionary mapping check names to HealthCheckResult instances
56
+ """
57
+ results = self.registry.run_all(check_names)
58
+
59
+ # Save metrics if requested
60
+ if save_metrics:
61
+ for result in results.values():
62
+ if result:
63
+ self.metrics_collector.record_health_check_result(result)
64
+
65
+ return results
66
+
67
+ def run_checks_parallel(
68
+ self, check_names: list[str] | None = None, max_workers: int = 4
69
+ ) -> dict[str, HealthCheckResult]:
70
+ """
71
+ Run health checks in parallel where possible.
72
+
73
+ Args:
74
+ check_names: Optional list of check names to run
75
+ max_workers: Maximum number of parallel workers
76
+
77
+ Returns:
78
+ Dictionary mapping check names to HealthCheckResult instances
79
+ """
80
+ if check_names is None:
81
+ check_names = self.registry.list_names()
82
+
83
+ # Build dependency graph
84
+ dependency_graph: dict[str, set[str]] = {}
85
+ independent_checks: list[str] = []
86
+ dependent_checks: list[str] = []
87
+
88
+ for name in check_names:
89
+ check = self.registry.get(name)
90
+ if check:
91
+ deps = check.get_dependencies()
92
+ dependency_graph[name] = set(deps)
93
+ if not deps:
94
+ independent_checks.append(name)
95
+ else:
96
+ dependent_checks.append(name)
97
+
98
+ results: dict[str, HealthCheckResult] = {}
99
+
100
+ # Run independent checks in parallel
101
+ if independent_checks:
102
+ with concurrent.futures.ThreadPoolExecutor(max_workers=max_workers) as executor:
103
+ future_to_check = {
104
+ executor.submit(self.registry.run, name): name for name in independent_checks
105
+ }
106
+ for future in concurrent.futures.as_completed(future_to_check):
107
+ check_name = future_to_check[future]
108
+ try:
109
+ result = future.result()
110
+ if result:
111
+ results[check_name] = result
112
+ except Exception as e:
113
+ logger.error(f"Error running check {check_name}: {e}", exc_info=True)
114
+ results[check_name] = HealthCheckResult(
115
+ name=check_name,
116
+ status="unhealthy",
117
+ score=0.0,
118
+ message=f"Check failed with error: {e}",
119
+ details={"error": str(e)},
120
+ )
121
+
122
+ # Run dependent checks sequentially (respecting dependencies)
123
+ execution_order = self.registry._topological_sort(dependent_checks)
124
+ for name in execution_order:
125
+ if name not in results:
126
+ result = self.registry.run(name)
127
+ if result:
128
+ results[name] = result
129
+
130
+ # Save metrics
131
+ for result in results.values():
132
+ if result:
133
+ self.metrics_collector.record_health_check_result(result)
134
+
135
+ return results
136
+
137
+ def get_overall_health(self, results: dict[str, HealthCheckResult] | None = None) -> dict[str, Any]:
138
+ """
139
+ Calculate overall health status from check results.
140
+
141
+ Args:
142
+ results: Optional check results (runs all checks if None)
143
+
144
+ Returns:
145
+ Dictionary with overall health information
146
+ """
147
+ if results is None:
148
+ results = self.run_all_checks(save_metrics=True)
149
+
150
+ if not results:
151
+ return {
152
+ "status": "unknown",
153
+ "score": 0.0,
154
+ "message": "No health checks available",
155
+ "checks_count": 0,
156
+ }
157
+
158
+ # Calculate weighted average score
159
+ # Critical checks (environment, execution) have higher weight
160
+ critical_checks = {"environment", "execution"}
161
+ total_weight = 0.0
162
+ weighted_score = 0.0
163
+
164
+ status_counts = {"healthy": 0, "degraded": 0, "unhealthy": 0}
165
+
166
+ for name, result in results.items():
167
+ if not result:
168
+ continue
169
+
170
+ weight = 2.0 if name in critical_checks else 1.0
171
+ total_weight += weight
172
+ weighted_score += result.score * weight
173
+
174
+ status_counts[result.status] = status_counts.get(result.status, 0) + 1
175
+
176
+ overall_score = weighted_score / total_weight if total_weight > 0 else 0.0
177
+
178
+ # Determine overall status (HM-001-S3: degraded when score >= 75 and only non-critical unhealthy)
179
+ critical_checks = {"environment", "execution"}
180
+ non_critical_checks = {"outcomes", "knowledge_base", "context7_cache", "automation"}
181
+ unhealthy_checks = [
182
+ name
183
+ for name, result in results.items()
184
+ if result and result.status == "unhealthy"
185
+ ]
186
+ critical_healthy = all(
187
+ (results.get(name) and results[name].status != "unhealthy")
188
+ for name in critical_checks
189
+ if name in results
190
+ )
191
+
192
+ status_reason: str | None = None
193
+ if status_counts["unhealthy"] > 0:
194
+ overall_status = "unhealthy"
195
+ if (
196
+ overall_score >= 75.0
197
+ and critical_healthy
198
+ and unhealthy_checks
199
+ and all(c in non_critical_checks for c in unhealthy_checks)
200
+ ):
201
+ overall_status = "degraded"
202
+ status_reason = (
203
+ "Status degraded due to non-critical checks; core functionality is healthy"
204
+ )
205
+ elif status_counts["degraded"] > 0:
206
+ overall_status = "degraded"
207
+ else:
208
+ overall_status = "healthy"
209
+
210
+ # Build remediation list (prioritized)
211
+ all_remediations: list[str] = []
212
+ for name, result in results.items():
213
+ if result and result.remediation:
214
+ if isinstance(result.remediation, list):
215
+ all_remediations.extend(result.remediation)
216
+ elif isinstance(result.remediation, str):
217
+ all_remediations.append(result.remediation)
218
+
219
+ # Deduplicate and prioritize
220
+ unique_remediations = []
221
+ seen = set()
222
+ for rem in all_remediations:
223
+ rem_lower = rem.lower()
224
+ if rem_lower not in seen:
225
+ seen.add(rem_lower)
226
+ unique_remediations.append(rem)
227
+
228
+ # Prioritize: unhealthy checks first, then degraded
229
+ prioritized_remediations = []
230
+ for name, result in results.items():
231
+ if result and result.remediation and result.status == "unhealthy":
232
+ if isinstance(result.remediation, list):
233
+ prioritized_remediations.extend(result.remediation)
234
+ else:
235
+ prioritized_remediations.append(result.remediation)
236
+
237
+ for name, result in results.items():
238
+ if result and result.remediation and result.status == "degraded":
239
+ if isinstance(result.remediation, list):
240
+ prioritized_remediations.extend(result.remediation)
241
+ else:
242
+ prioritized_remediations.append(result.remediation)
243
+
244
+ # Add remaining unique remediations
245
+ for rem in unique_remediations:
246
+ if rem not in prioritized_remediations:
247
+ prioritized_remediations.append(rem)
248
+
249
+ details: dict[str, Any] = {}
250
+ if status_reason:
251
+ details["status_reason"] = status_reason
252
+
253
+ return {
254
+ "status": overall_status,
255
+ "score": overall_score,
256
+ "message": f"Overall health: {overall_status} ({overall_score:.1f}/100)",
257
+ "checks_count": len(results),
258
+ "status_counts": status_counts,
259
+ "details": details,
260
+ "checks": {
261
+ name: {
262
+ "status": result.status,
263
+ "score": result.score,
264
+ "message": result.message,
265
+ }
266
+ for name, result in results.items()
267
+ if result
268
+ },
269
+ "remediation": prioritized_remediations[:5], # Top 5 remediation actions
270
+ }
271
+
@@ -0,0 +1,5 @@
1
+ """Packaged non-code resources used for project initialization.
2
+
3
+ These resources are bundled in wheels/sdists so `tapps-agents init` works even when
4
+ TappsCodingAgents is installed from PyPI.
5
+ """
@@ -0,0 +1 @@
1
+ """Claude Desktop integration resources."""