crackerjack 0.30.3__py3-none-any.whl → 0.31.4__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.

Potentially problematic release.


This version of crackerjack might be problematic. Click here for more details.

Files changed (155) hide show
  1. crackerjack/CLAUDE.md +1005 -0
  2. crackerjack/RULES.md +380 -0
  3. crackerjack/__init__.py +42 -13
  4. crackerjack/__main__.py +225 -299
  5. crackerjack/agents/__init__.py +41 -0
  6. crackerjack/agents/architect_agent.py +281 -0
  7. crackerjack/agents/base.py +169 -0
  8. crackerjack/agents/coordinator.py +512 -0
  9. crackerjack/agents/documentation_agent.py +498 -0
  10. crackerjack/agents/dry_agent.py +388 -0
  11. crackerjack/agents/formatting_agent.py +245 -0
  12. crackerjack/agents/import_optimization_agent.py +281 -0
  13. crackerjack/agents/performance_agent.py +669 -0
  14. crackerjack/agents/proactive_agent.py +104 -0
  15. crackerjack/agents/refactoring_agent.py +788 -0
  16. crackerjack/agents/security_agent.py +529 -0
  17. crackerjack/agents/test_creation_agent.py +652 -0
  18. crackerjack/agents/test_specialist_agent.py +486 -0
  19. crackerjack/agents/tracker.py +212 -0
  20. crackerjack/api.py +560 -0
  21. crackerjack/cli/__init__.py +24 -0
  22. crackerjack/cli/facade.py +104 -0
  23. crackerjack/cli/handlers.py +267 -0
  24. crackerjack/cli/interactive.py +471 -0
  25. crackerjack/cli/options.py +401 -0
  26. crackerjack/cli/utils.py +18 -0
  27. crackerjack/code_cleaner.py +618 -928
  28. crackerjack/config/__init__.py +19 -0
  29. crackerjack/config/hooks.py +218 -0
  30. crackerjack/core/__init__.py +0 -0
  31. crackerjack/core/async_workflow_orchestrator.py +406 -0
  32. crackerjack/core/autofix_coordinator.py +200 -0
  33. crackerjack/core/container.py +104 -0
  34. crackerjack/core/enhanced_container.py +542 -0
  35. crackerjack/core/performance.py +243 -0
  36. crackerjack/core/phase_coordinator.py +561 -0
  37. crackerjack/core/proactive_workflow.py +316 -0
  38. crackerjack/core/session_coordinator.py +289 -0
  39. crackerjack/core/workflow_orchestrator.py +640 -0
  40. crackerjack/dynamic_config.py +94 -103
  41. crackerjack/errors.py +263 -41
  42. crackerjack/executors/__init__.py +11 -0
  43. crackerjack/executors/async_hook_executor.py +431 -0
  44. crackerjack/executors/cached_hook_executor.py +242 -0
  45. crackerjack/executors/hook_executor.py +345 -0
  46. crackerjack/executors/individual_hook_executor.py +669 -0
  47. crackerjack/intelligence/__init__.py +44 -0
  48. crackerjack/intelligence/adaptive_learning.py +751 -0
  49. crackerjack/intelligence/agent_orchestrator.py +551 -0
  50. crackerjack/intelligence/agent_registry.py +414 -0
  51. crackerjack/intelligence/agent_selector.py +502 -0
  52. crackerjack/intelligence/integration.py +290 -0
  53. crackerjack/interactive.py +576 -315
  54. crackerjack/managers/__init__.py +11 -0
  55. crackerjack/managers/async_hook_manager.py +135 -0
  56. crackerjack/managers/hook_manager.py +137 -0
  57. crackerjack/managers/publish_manager.py +411 -0
  58. crackerjack/managers/test_command_builder.py +151 -0
  59. crackerjack/managers/test_executor.py +435 -0
  60. crackerjack/managers/test_manager.py +258 -0
  61. crackerjack/managers/test_manager_backup.py +1124 -0
  62. crackerjack/managers/test_progress.py +144 -0
  63. crackerjack/mcp/__init__.py +0 -0
  64. crackerjack/mcp/cache.py +336 -0
  65. crackerjack/mcp/client_runner.py +104 -0
  66. crackerjack/mcp/context.py +615 -0
  67. crackerjack/mcp/dashboard.py +636 -0
  68. crackerjack/mcp/enhanced_progress_monitor.py +479 -0
  69. crackerjack/mcp/file_monitor.py +336 -0
  70. crackerjack/mcp/progress_components.py +569 -0
  71. crackerjack/mcp/progress_monitor.py +949 -0
  72. crackerjack/mcp/rate_limiter.py +332 -0
  73. crackerjack/mcp/server.py +22 -0
  74. crackerjack/mcp/server_core.py +244 -0
  75. crackerjack/mcp/service_watchdog.py +501 -0
  76. crackerjack/mcp/state.py +395 -0
  77. crackerjack/mcp/task_manager.py +257 -0
  78. crackerjack/mcp/tools/__init__.py +17 -0
  79. crackerjack/mcp/tools/core_tools.py +249 -0
  80. crackerjack/mcp/tools/error_analyzer.py +308 -0
  81. crackerjack/mcp/tools/execution_tools.py +370 -0
  82. crackerjack/mcp/tools/execution_tools_backup.py +1097 -0
  83. crackerjack/mcp/tools/intelligence_tool_registry.py +80 -0
  84. crackerjack/mcp/tools/intelligence_tools.py +314 -0
  85. crackerjack/mcp/tools/monitoring_tools.py +502 -0
  86. crackerjack/mcp/tools/proactive_tools.py +384 -0
  87. crackerjack/mcp/tools/progress_tools.py +141 -0
  88. crackerjack/mcp/tools/utility_tools.py +341 -0
  89. crackerjack/mcp/tools/workflow_executor.py +360 -0
  90. crackerjack/mcp/websocket/__init__.py +14 -0
  91. crackerjack/mcp/websocket/app.py +39 -0
  92. crackerjack/mcp/websocket/endpoints.py +559 -0
  93. crackerjack/mcp/websocket/jobs.py +253 -0
  94. crackerjack/mcp/websocket/server.py +116 -0
  95. crackerjack/mcp/websocket/websocket_handler.py +78 -0
  96. crackerjack/mcp/websocket_server.py +10 -0
  97. crackerjack/models/__init__.py +31 -0
  98. crackerjack/models/config.py +93 -0
  99. crackerjack/models/config_adapter.py +230 -0
  100. crackerjack/models/protocols.py +118 -0
  101. crackerjack/models/task.py +154 -0
  102. crackerjack/monitoring/ai_agent_watchdog.py +450 -0
  103. crackerjack/monitoring/regression_prevention.py +638 -0
  104. crackerjack/orchestration/__init__.py +0 -0
  105. crackerjack/orchestration/advanced_orchestrator.py +970 -0
  106. crackerjack/orchestration/execution_strategies.py +341 -0
  107. crackerjack/orchestration/test_progress_streamer.py +636 -0
  108. crackerjack/plugins/__init__.py +15 -0
  109. crackerjack/plugins/base.py +200 -0
  110. crackerjack/plugins/hooks.py +246 -0
  111. crackerjack/plugins/loader.py +335 -0
  112. crackerjack/plugins/managers.py +259 -0
  113. crackerjack/py313.py +8 -3
  114. crackerjack/services/__init__.py +22 -0
  115. crackerjack/services/cache.py +314 -0
  116. crackerjack/services/config.py +347 -0
  117. crackerjack/services/config_integrity.py +99 -0
  118. crackerjack/services/contextual_ai_assistant.py +516 -0
  119. crackerjack/services/coverage_ratchet.py +347 -0
  120. crackerjack/services/debug.py +736 -0
  121. crackerjack/services/dependency_monitor.py +617 -0
  122. crackerjack/services/enhanced_filesystem.py +439 -0
  123. crackerjack/services/file_hasher.py +151 -0
  124. crackerjack/services/filesystem.py +395 -0
  125. crackerjack/services/git.py +165 -0
  126. crackerjack/services/health_metrics.py +611 -0
  127. crackerjack/services/initialization.py +847 -0
  128. crackerjack/services/log_manager.py +286 -0
  129. crackerjack/services/logging.py +174 -0
  130. crackerjack/services/metrics.py +578 -0
  131. crackerjack/services/pattern_cache.py +362 -0
  132. crackerjack/services/pattern_detector.py +515 -0
  133. crackerjack/services/performance_benchmarks.py +653 -0
  134. crackerjack/services/security.py +163 -0
  135. crackerjack/services/server_manager.py +234 -0
  136. crackerjack/services/smart_scheduling.py +144 -0
  137. crackerjack/services/tool_version_service.py +61 -0
  138. crackerjack/services/unified_config.py +437 -0
  139. crackerjack/services/version_checker.py +248 -0
  140. crackerjack/slash_commands/__init__.py +14 -0
  141. crackerjack/slash_commands/init.md +122 -0
  142. crackerjack/slash_commands/run.md +163 -0
  143. crackerjack/slash_commands/status.md +127 -0
  144. crackerjack-0.31.4.dist-info/METADATA +742 -0
  145. crackerjack-0.31.4.dist-info/RECORD +148 -0
  146. crackerjack-0.31.4.dist-info/entry_points.txt +2 -0
  147. crackerjack/.gitignore +0 -34
  148. crackerjack/.libcst.codemod.yaml +0 -18
  149. crackerjack/.pdm.toml +0 -1
  150. crackerjack/crackerjack.py +0 -3805
  151. crackerjack/pyproject.toml +0 -286
  152. crackerjack-0.30.3.dist-info/METADATA +0 -1290
  153. crackerjack-0.30.3.dist-info/RECORD +0 -16
  154. {crackerjack-0.30.3.dist-info → crackerjack-0.31.4.dist-info}/WHEEL +0 -0
  155. {crackerjack-0.30.3.dist-info → crackerjack-0.31.4.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,384 @@
1
+ import typing as t
2
+
3
+ # Helper functions for creating responses and assessments
4
+
5
+
6
+ def _create_architectural_assessment(args: str, parsed_kwargs: dict) -> dict:
7
+ """Create architectural assessment with recommendations."""
8
+ assessment = {
9
+ "feature": parsed_kwargs.get("feature", "unknown"),
10
+ "complexity": parsed_kwargs.get("complexity", "medium"),
11
+ "context": args or "General development planning",
12
+ "proactive_recommendations": [],
13
+ }
14
+
15
+ recommendations = []
16
+
17
+ # Always recommend crackerjack-architect for planning
18
+ recommendations.append(
19
+ {
20
+ "agent": "crackerjack-architect",
21
+ "priority": "high",
22
+ "reason": "Expert architectural planning for crackerjack compliance",
23
+ "action": 'Task tool with subagent_type="crackerjack-architect" for feature planning and architecture',
24
+ "benefits": [
25
+ "Prevents violations through proper initial architecture",
26
+ "Applies crackerjack patterns from the start",
27
+ "Reduces iteration cycles by 50%+",
28
+ "Ensures consistency with project standards",
29
+ ],
30
+ }
31
+ )
32
+
33
+ # Add complexity-specific recommendations
34
+ complexity = parsed_kwargs.get("complexity", "medium")
35
+ if complexity in ("high", "complex"):
36
+ recommendations.append(
37
+ {
38
+ "agent": "refactoring-specialist",
39
+ "priority": "high",
40
+ "reason": "Complex features require careful structural planning",
41
+ "action": 'Task tool with subagent_type="refactoring-specialist" for complexity management',
42
+ }
43
+ )
44
+
45
+ # Add security recommendations for certain features
46
+ feature = parsed_kwargs.get("feature", "")
47
+ if any(
48
+ keyword in feature.lower() for keyword in ("auth", "security", "api", "data")
49
+ ):
50
+ recommendations.append(
51
+ {
52
+ "agent": "security-auditor",
53
+ "priority": "medium",
54
+ "reason": "Security-sensitive feature requires expert review",
55
+ "action": 'Task tool with subagent_type="security-auditor" for security validation',
56
+ }
57
+ )
58
+
59
+ assessment["proactive_recommendations"] = recommendations
60
+ assessment["planning_strategy"] = "proactive_architecture_first"
61
+
62
+ return assessment
63
+
64
+
65
+ def _create_validation_results(file_path: str) -> dict:
66
+ """Create validation results for architectural compliance."""
67
+ validation = {
68
+ "file_path": file_path,
69
+ "validation_results": [],
70
+ "architectural_compliance": "unknown",
71
+ "recommendations": [],
72
+ }
73
+
74
+ # Check for crackerjack compliance patterns
75
+ compliance_checks = [
76
+ {
77
+ "check": "complexity_compliance",
78
+ "status": "requires_analysis",
79
+ "message": "Cognitive complexity should be ≤13 per function",
80
+ "tool": "complexipy",
81
+ },
82
+ {
83
+ "check": "clean_code_patterns",
84
+ "status": "requires_analysis",
85
+ "message": "Follow DRY, YAGNI, KISS principles",
86
+ "patterns": [
87
+ "extract_method",
88
+ "protocol_interfaces",
89
+ "dependency_injection",
90
+ ],
91
+ },
92
+ {
93
+ "check": "security_patterns",
94
+ "status": "requires_analysis",
95
+ "message": "Use secure temp files, proper input validation",
96
+ "tool": "bandit",
97
+ },
98
+ {
99
+ "check": "type_annotations",
100
+ "status": "requires_analysis",
101
+ "message": "All functions must have proper type hints",
102
+ "tool": "pyright",
103
+ },
104
+ ]
105
+
106
+ validation["validation_results"] = compliance_checks
107
+
108
+ # Add proactive recommendations
109
+ validation["recommendations"] = [
110
+ "Run full crackerjack quality process: python -m crackerjack -t",
111
+ "Use crackerjack-architect for complex refactoring decisions",
112
+ "Apply pattern learning from successful fixes",
113
+ "Validate against architectural plan before committing",
114
+ ]
115
+
116
+ validation["next_steps"] = [
117
+ 'Task tool with subagent_type="crackerjack-architect" for architectural guidance',
118
+ "Run comprehensive quality checks",
119
+ "Apply learned patterns from pattern cache",
120
+ ]
121
+
122
+ return validation
123
+
124
+
125
+ def _create_pattern_suggestions(problem_context: str) -> dict:
126
+ """Create pattern suggestions based on context."""
127
+ pattern_suggestions = {
128
+ "context": problem_context,
129
+ "recommended_patterns": [],
130
+ "implementation_guidance": [],
131
+ "specialist_agents": [],
132
+ }
133
+
134
+ # Add context-specific patterns
135
+ _add_complexity_patterns(pattern_suggestions, problem_context)
136
+ _add_dry_patterns(pattern_suggestions, problem_context)
137
+ _add_performance_patterns(pattern_suggestions, problem_context)
138
+ _add_security_patterns(pattern_suggestions, problem_context)
139
+
140
+ # Add specialist agent recommendations
141
+ pattern_suggestions["specialist_agents"] = [
142
+ {
143
+ "agent": "crackerjack-architect",
144
+ "when_to_use": "For architectural decisions and complex pattern application",
145
+ "action": 'Task tool with subagent_type="crackerjack-architect"',
146
+ },
147
+ {
148
+ "agent": "refactoring-specialist",
149
+ "when_to_use": "For complexity reduction and structural improvements",
150
+ "action": 'Task tool with subagent_type="refactoring-specialist"',
151
+ },
152
+ {
153
+ "agent": "security-auditor",
154
+ "when_to_use": "For security pattern validation and vulnerability assessment",
155
+ "action": 'Task tool with subagent_type="security-auditor"',
156
+ },
157
+ ]
158
+
159
+ # Implementation guidance
160
+ pattern_suggestions["implementation_guidance"] = [
161
+ "Start with crackerjack-architect for overall planning",
162
+ "Apply one pattern at a time to avoid complexity",
163
+ "Validate each pattern with crackerjack quality checks",
164
+ "Cache successful patterns for future use",
165
+ "Document architectural decisions for team knowledge",
166
+ ]
167
+
168
+ # Default patterns if none specified
169
+ if not pattern_suggestions["recommended_patterns"]:
170
+ pattern_suggestions["recommended_patterns"] = [
171
+ {
172
+ "pattern": "standard_crackerjack_patterns",
173
+ "description": "Apply standard crackerjack clean code patterns",
174
+ "benefits": [
175
+ "Consistent code quality",
176
+ "Better maintainability",
177
+ "Team alignment",
178
+ ],
179
+ }
180
+ ]
181
+
182
+ return pattern_suggestions
183
+
184
+
185
+ def _add_complexity_patterns(pattern_suggestions: dict, problem_context: str) -> None:
186
+ """Add complexity-related patterns if relevant."""
187
+ if any(
188
+ keyword in problem_context.lower()
189
+ for keyword in ("complex", "refactor", "cleanup")
190
+ ):
191
+ pattern_suggestions["recommended_patterns"].extend(
192
+ [
193
+ {
194
+ "pattern": "extract_method",
195
+ "description": "Break complex functions into smaller, focused methods",
196
+ "benefits": [
197
+ "Reduces cognitive complexity",
198
+ "Improves testability",
199
+ "Follows KISS principle",
200
+ ],
201
+ },
202
+ {
203
+ "pattern": "dependency_injection",
204
+ "description": "Use protocol interfaces for better decoupling",
205
+ "benefits": [
206
+ "Improves testability",
207
+ "Reduces coupling",
208
+ "Enables better mocking",
209
+ ],
210
+ },
211
+ ]
212
+ )
213
+
214
+
215
+ def _add_dry_patterns(pattern_suggestions: dict, problem_context: str) -> None:
216
+ """Add DRY violation patterns if relevant."""
217
+ if any(
218
+ keyword in problem_context.lower() for keyword in ("duplicate", "repeat", "dry")
219
+ ):
220
+ pattern_suggestions["recommended_patterns"].extend(
221
+ [
222
+ {
223
+ "pattern": "common_base_class",
224
+ "description": "Extract shared functionality to base classes or mixins",
225
+ "benefits": [
226
+ "Reduces duplication",
227
+ "Centralizes common logic",
228
+ "Easier maintenance",
229
+ ],
230
+ },
231
+ {
232
+ "pattern": "utility_functions",
233
+ "description": "Create reusable utility functions for common operations",
234
+ "benefits": [
235
+ "Single source of truth",
236
+ "Reduces code duplication",
237
+ "Easier testing",
238
+ ],
239
+ },
240
+ ]
241
+ )
242
+
243
+
244
+ def _add_performance_patterns(pattern_suggestions: dict, problem_context: str) -> None:
245
+ """Add performance patterns if relevant."""
246
+ if any(
247
+ keyword in problem_context.lower()
248
+ for keyword in ("slow", "performance", "optimize")
249
+ ):
250
+ pattern_suggestions["recommended_patterns"].extend(
251
+ [
252
+ {
253
+ "pattern": "list_comprehension",
254
+ "description": "Use list comprehensions instead of manual loops",
255
+ "benefits": [
256
+ "Better performance",
257
+ "More readable",
258
+ "Pythonic code",
259
+ ],
260
+ },
261
+ {
262
+ "pattern": "generator_pattern",
263
+ "description": "Use generators for memory-efficient processing",
264
+ "benefits": [
265
+ "Reduced memory usage",
266
+ "Lazy evaluation",
267
+ "Better for large datasets",
268
+ ],
269
+ },
270
+ ]
271
+ )
272
+
273
+
274
+ def _add_security_patterns(pattern_suggestions: dict, problem_context: str) -> None:
275
+ """Add security patterns if relevant."""
276
+ if any(
277
+ keyword in problem_context.lower() for keyword in ("security", "safe", "secure")
278
+ ):
279
+ pattern_suggestions["recommended_patterns"].extend(
280
+ [
281
+ {
282
+ "pattern": "secure_temp_files",
283
+ "description": "Use tempfile module instead of hardcoded paths",
284
+ "benefits": [
285
+ "Prevents security vulnerabilities",
286
+ "Cross-platform compatibility",
287
+ "Automatic cleanup",
288
+ ],
289
+ },
290
+ {
291
+ "pattern": "input_validation",
292
+ "description": "Validate and sanitize all user inputs",
293
+ "benefits": [
294
+ "Prevents injection attacks",
295
+ "Better error handling",
296
+ "Data integrity",
297
+ ],
298
+ },
299
+ ]
300
+ )
301
+
302
+
303
+ def _create_error_response(error: Exception, recommendation: str) -> str:
304
+ """Create standardized error response."""
305
+ import json
306
+
307
+ return json.dumps(
308
+ {
309
+ "error": str(error),
310
+ "fallback_patterns": [
311
+ "clean_code",
312
+ "single_responsibility",
313
+ "dry_principle",
314
+ ],
315
+ "recommendation": recommendation,
316
+ }
317
+ )
318
+
319
+
320
+ def register_proactive_tools(mcp_app: t.Any) -> None:
321
+ """Register proactive planning and execution tools."""
322
+ return _register_proactive_tools(mcp_app)
323
+
324
+
325
+ def _register_proactive_tools(mcp_app: t.Any) -> None:
326
+ """Register proactive planning and execution tools."""
327
+ _register_plan_development_tool(mcp_app)
328
+ _register_validate_architecture_tool(mcp_app)
329
+ _register_suggest_patterns_tool(mcp_app)
330
+
331
+
332
+ def _register_plan_development_tool(mcp_app: t.Any) -> None:
333
+ """Register the plan_development tool."""
334
+
335
+ @mcp_app.tool()
336
+ async def plan_development(args: str, kwargs: str) -> str:
337
+ """Plan development approach using crackerjack-architect specialist."""
338
+ import json
339
+
340
+ try:
341
+ parsed_kwargs = json.loads(kwargs) if kwargs else {}
342
+ assessment = _create_architectural_assessment(args, parsed_kwargs)
343
+ return json.dumps(assessment, indent=2)
344
+ except Exception as e:
345
+ return _create_error_response(e, "Use standard development approach")
346
+
347
+
348
+ def _register_validate_architecture_tool(mcp_app: t.Any) -> None:
349
+ """Register the validate_architecture tool."""
350
+
351
+ @mcp_app.tool()
352
+ async def validate_architecture(args: str, kwargs: str) -> str:
353
+ """Validate code against architectural patterns and crackerjack standards."""
354
+ import json
355
+
356
+ try:
357
+ parsed_kwargs = json.loads(kwargs) if kwargs else {}
358
+ file_path = args or parsed_kwargs.get("file_path", "")
359
+ validation = _create_validation_results(file_path)
360
+ return json.dumps(validation, indent=2)
361
+ except Exception as e:
362
+ return _create_error_response(
363
+ e, "Run standard crackerjack validation: python -m crackerjack"
364
+ )
365
+
366
+
367
+ def _register_suggest_patterns_tool(mcp_app: t.Any) -> None:
368
+ """Register the suggest_patterns tool."""
369
+
370
+ @mcp_app.tool()
371
+ async def suggest_patterns(args: str, kwargs: str) -> str:
372
+ """Suggest crackerjack patterns for current development context."""
373
+ import json
374
+
375
+ try:
376
+ json.loads(kwargs) if kwargs else {}
377
+ problem_context = args or "General pattern suggestions"
378
+ pattern_suggestions = _create_pattern_suggestions(problem_context)
379
+ return json.dumps(pattern_suggestions, indent=2)
380
+ except Exception as e:
381
+ return _create_error_response(
382
+ e,
383
+ 'Use Task tool with subagent_type="crackerjack-architect" for expert guidance',
384
+ )
@@ -0,0 +1,141 @@
1
+ import contextlib
2
+ import json
3
+ import typing as t
4
+ from pathlib import Path
5
+
6
+ from crackerjack.mcp.context import get_context
7
+
8
+
9
+ def _create_progress_file(job_id: str) -> Path:
10
+ import re
11
+ import tempfile
12
+
13
+ if not job_id or not isinstance(job_id, str):
14
+ msg = f"Invalid job_id: {job_id}"
15
+ raise ValueError(msg)
16
+ if not re.match(r"^[a-zA-Z0-9_-]+$", job_id):
17
+ msg = f"Invalid job_id format: {job_id}"
18
+ raise ValueError(msg)
19
+
20
+ context = get_context()
21
+ if context:
22
+ return context.progress_dir / f"job-{job_id}.json"
23
+ progress_dir = Path(tempfile.gettempdir()) / "crackerjack-mcp-progress"
24
+ progress_dir.mkdir(exist_ok=True)
25
+ return progress_dir / f"job-{job_id}.json"
26
+
27
+
28
+ def _update_progress(
29
+ job_id: str,
30
+ status: str = "running",
31
+ iteration: int = 1,
32
+ max_iterations: int = 10,
33
+ overall_progress: int = 0,
34
+ current_stage: str = "initialization",
35
+ stage_progress: int = 0,
36
+ message: str = "",
37
+ ) -> None:
38
+ try:
39
+ progress_file = _create_progress_file(job_id)
40
+
41
+ progress_data = {
42
+ "job_id": job_id,
43
+ "status": status,
44
+ "iteration": iteration,
45
+ "max_iterations": max_iterations,
46
+ "overall_progress": min(100, max(0, overall_progress)),
47
+ "current_stage": current_stage,
48
+ "stage_progress": min(100, max(0, stage_progress)),
49
+ "message": message,
50
+ "timestamp": get_context().get_current_time() if get_context() else "",
51
+ }
52
+
53
+ progress_file.write_text(json.dumps(progress_data, indent=2))
54
+
55
+ context = get_context()
56
+ if context and hasattr(context, "websocket_progress_queue"):
57
+ with contextlib.suppress(Exception):
58
+ context.websocket_progress_queue.put_nowait(progress_data)
59
+
60
+ except Exception as e:
61
+ context = get_context()
62
+ if context:
63
+ context.safe_print(f"Warning: Failed to update progress for {job_id}: {e}")
64
+
65
+
66
+ def _handle_get_job_progress(job_id: str) -> str:
67
+ context = get_context()
68
+ if not context:
69
+ return '{"error": "Server context not available"}'
70
+
71
+ if not context.validate_job_id(job_id):
72
+ return f'{{"error": "Invalid job_id: {job_id}"}}'
73
+
74
+ try:
75
+ progress_file = _create_progress_file(job_id)
76
+
77
+ if not progress_file.exists():
78
+ return f'{{"error": "Job {job_id} not found", "job_id": "{job_id}"}}'
79
+
80
+ progress_data = json.loads(progress_file.read_text())
81
+ return json.dumps(progress_data, indent=2)
82
+
83
+ except json.JSONDecodeError as e:
84
+ return f'{{"error": "Invalid progress data for job {job_id}: {e}"}}'
85
+ except Exception as e:
86
+ return f'{{"error": "Failed to get progress for job {job_id}: {e}"}}'
87
+
88
+
89
+ def _execute_session_action(
90
+ state_manager,
91
+ action: str,
92
+ checkpoint_name: str | None,
93
+ context,
94
+ ) -> str:
95
+ if action == "start":
96
+ state_manager.start_session()
97
+ return '{"status": "session_started", "action": "start"}'
98
+
99
+ if action == "checkpoint":
100
+ checkpoint_name = checkpoint_name or f"checkpoint_{context.get_current_time()}"
101
+ state_manager.create_checkpoint(checkpoint_name)
102
+ return f'{{"status": "checkpoint_created", "action": "checkpoint", "name": "{checkpoint_name}"}}'
103
+
104
+ if action == "complete":
105
+ state_manager.complete_session()
106
+ return '{"status": "session_completed", "action": "complete"}'
107
+
108
+ if action == "reset":
109
+ state_manager.reset_session()
110
+ return '{"status": "session_reset", "action": "reset"}'
111
+
112
+ return f'{{"error": "Invalid action: {action}. Valid actions: start, checkpoint, complete, reset"}}'
113
+
114
+
115
+ def _handle_session_management(action: str, checkpoint_name: str | None = None) -> str:
116
+ context = get_context()
117
+ if not context:
118
+ return '{"error": "Server context not available"}'
119
+
120
+ try:
121
+ state_manager = getattr(context, "state_manager", None)
122
+ if not state_manager:
123
+ return '{"error": "State manager not available"}'
124
+
125
+ return _execute_session_action(state_manager, action, checkpoint_name, context)
126
+
127
+ except Exception as e:
128
+ return f'{{"error": "Session management failed: {e}"}}'
129
+
130
+
131
+ def register_progress_tools(mcp_app: t.Any) -> None:
132
+ @mcp_app.tool()
133
+ async def get_job_progress(job_id: str) -> str:
134
+ return _handle_get_job_progress(job_id)
135
+
136
+ @mcp_app.tool()
137
+ async def session_management(
138
+ action: str,
139
+ checkpoint_name: str | None = None,
140
+ ) -> str:
141
+ return _handle_session_management(action, checkpoint_name)