crackerjack 0.39.11__py3-none-any.whl → 0.40.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.
Potentially problematic release.
This version of crackerjack might be problematic. Click here for more details.
- crackerjack/adapters/__init__.py +5 -0
- crackerjack/adapters/ai/__init__.py +5 -0
- crackerjack/adapters/ai/claude.py +841 -0
- crackerjack/agents/claude_code_bridge.py +202 -1
- crackerjack/cli/facade.py +62 -0
- crackerjack/cli/options.py +14 -4
- crackerjack/dynamic_config.py +1 -1
- crackerjack/services/__init__.py +8 -21
- crackerjack/services/file_modifier.py +520 -0
- crackerjack/workflows/__init__.py +10 -0
- crackerjack/workflows/auto_fix.py +443 -0
- {crackerjack-0.39.11.dist-info → crackerjack-0.40.1.dist-info}/METADATA +60 -17
- {crackerjack-0.39.11.dist-info → crackerjack-0.40.1.dist-info}/RECORD +16 -11
- {crackerjack-0.39.11.dist-info → crackerjack-0.40.1.dist-info}/WHEEL +0 -0
- {crackerjack-0.39.11.dist-info → crackerjack-0.40.1.dist-info}/entry_points.txt +0 -0
- {crackerjack-0.39.11.dist-info → crackerjack-0.40.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,443 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Iterative auto-fix workflow for crackerjack.
|
|
3
|
+
|
|
4
|
+
This module implements the AutoFixWorkflow class that runs an iterative
|
|
5
|
+
loop to detect issues via pre-commit hooks, apply AI-powered fixes, and
|
|
6
|
+
verify the fixes until convergence or max iterations is reached.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import asyncio
|
|
10
|
+
import logging
|
|
11
|
+
import typing as t
|
|
12
|
+
from dataclasses import dataclass, field
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
|
|
15
|
+
from rich.console import Console
|
|
16
|
+
|
|
17
|
+
from crackerjack.agents.base import AgentContext, Issue, IssueType, Priority
|
|
18
|
+
from crackerjack.agents.enhanced_coordinator import EnhancedAgentCoordinator
|
|
19
|
+
from crackerjack.managers.async_hook_manager import AsyncHookManager
|
|
20
|
+
from crackerjack.models.task import HookResult
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dataclass
|
|
24
|
+
class FixIteration:
|
|
25
|
+
"""Records data from a single iteration of the fix cycle."""
|
|
26
|
+
|
|
27
|
+
iteration_num: int
|
|
28
|
+
hooks_run: list[str]
|
|
29
|
+
issues_found: int
|
|
30
|
+
fixes_applied: int
|
|
31
|
+
fixes_successful: int
|
|
32
|
+
hooks_passing: list[str]
|
|
33
|
+
hooks_failing: list[str]
|
|
34
|
+
duration: float = 0.0
|
|
35
|
+
convergence_status: str = "incomplete"
|
|
36
|
+
|
|
37
|
+
def to_dict(self) -> dict[str, t.Any]:
|
|
38
|
+
"""Convert iteration to dictionary for serialization."""
|
|
39
|
+
return {
|
|
40
|
+
"iteration_num": self.iteration_num,
|
|
41
|
+
"hooks_run": self.hooks_run,
|
|
42
|
+
"issues_found": self.issues_found,
|
|
43
|
+
"fixes_applied": self.fixes_applied,
|
|
44
|
+
"fixes_successful": self.fixes_successful,
|
|
45
|
+
"hooks_passing": self.hooks_passing,
|
|
46
|
+
"hooks_failing": self.hooks_failing,
|
|
47
|
+
"duration": self.duration,
|
|
48
|
+
"convergence_status": self.convergence_status,
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@dataclass
|
|
53
|
+
class WorkflowResult:
|
|
54
|
+
"""Results from the complete auto-fix workflow."""
|
|
55
|
+
|
|
56
|
+
success: bool
|
|
57
|
+
iterations: list[FixIteration] = field(default_factory=list)
|
|
58
|
+
total_fixes: int = 0
|
|
59
|
+
total_issues_found: int = 0
|
|
60
|
+
final_status: str = "incomplete"
|
|
61
|
+
total_duration: float = 0.0
|
|
62
|
+
convergence_achieved: bool = False
|
|
63
|
+
exit_reason: str = "unknown"
|
|
64
|
+
|
|
65
|
+
def to_dict(self) -> dict[str, t.Any]:
|
|
66
|
+
"""Convert result to dictionary for serialization."""
|
|
67
|
+
return {
|
|
68
|
+
"success": self.success,
|
|
69
|
+
"iterations": [it.to_dict() for it in self.iterations],
|
|
70
|
+
"total_fixes": self.total_fixes,
|
|
71
|
+
"total_issues_found": self.total_issues_found,
|
|
72
|
+
"final_status": self.final_status,
|
|
73
|
+
"total_duration": self.total_duration,
|
|
74
|
+
"convergence_achieved": self.convergence_achieved,
|
|
75
|
+
"exit_reason": self.exit_reason,
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class AutoFixWorkflow:
|
|
80
|
+
"""Iterative auto-fix workflow with convergence detection."""
|
|
81
|
+
|
|
82
|
+
MAX_ITERATIONS = 10
|
|
83
|
+
CONVERGENCE_THRESHOLD = 0 # No new fixes needed
|
|
84
|
+
|
|
85
|
+
def __init__(
|
|
86
|
+
self,
|
|
87
|
+
project_path: Path | None = None,
|
|
88
|
+
console: Console | None = None,
|
|
89
|
+
enable_external_agents: bool = True,
|
|
90
|
+
) -> None:
|
|
91
|
+
"""Initialize the auto-fix workflow.
|
|
92
|
+
|
|
93
|
+
Args:
|
|
94
|
+
project_path: Path to project root (defaults to cwd)
|
|
95
|
+
console: Rich console for output (creates default if None)
|
|
96
|
+
enable_external_agents: Enable Claude Code external agent integration
|
|
97
|
+
"""
|
|
98
|
+
self.project_path = project_path or Path.cwd()
|
|
99
|
+
self.console = console or Console()
|
|
100
|
+
self.logger = logging.getLogger(__name__)
|
|
101
|
+
|
|
102
|
+
# Initialize agent context
|
|
103
|
+
self.context = AgentContext(
|
|
104
|
+
project_path=self.project_path,
|
|
105
|
+
temp_dir=self.project_path / ".crackerjack" / "temp",
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
# Initialize agent coordinator with external agent support
|
|
109
|
+
self.coordinator = EnhancedAgentCoordinator(
|
|
110
|
+
context=self.context,
|
|
111
|
+
enable_external_agents=enable_external_agents,
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
# Initialize hook manager
|
|
115
|
+
self.hook_manager = AsyncHookManager(
|
|
116
|
+
console=self.console,
|
|
117
|
+
pkg_path=self.project_path,
|
|
118
|
+
max_concurrent=3,
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
async def run(
|
|
122
|
+
self, command: str = "check", max_iterations: int | None = None
|
|
123
|
+
) -> WorkflowResult:
|
|
124
|
+
"""Run iterative auto-fix workflow.
|
|
125
|
+
|
|
126
|
+
Args:
|
|
127
|
+
command: Semantic command (test, lint, check, etc.)
|
|
128
|
+
max_iterations: Max fix iterations (default: 10)
|
|
129
|
+
|
|
130
|
+
Returns:
|
|
131
|
+
WorkflowResult with iteration history and final status
|
|
132
|
+
"""
|
|
133
|
+
max_iter = max_iterations or self.MAX_ITERATIONS
|
|
134
|
+
iterations: list[FixIteration] = []
|
|
135
|
+
workflow_start_time = asyncio.get_event_loop().time()
|
|
136
|
+
|
|
137
|
+
self.logger.info(
|
|
138
|
+
f"Starting auto-fix workflow: {command} (max {max_iter} iterations)"
|
|
139
|
+
)
|
|
140
|
+
self.console.print("[bold cyan]🔄 Auto-Fix Workflow Started[/bold cyan]")
|
|
141
|
+
self.console.print(
|
|
142
|
+
f"[dim]Command: {command} | Max iterations: {max_iter}[/dim]\n"
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
exit_reason = "unknown"
|
|
146
|
+
all_passing = False
|
|
147
|
+
|
|
148
|
+
for i in range(1, max_iter + 1):
|
|
149
|
+
iteration_start_time = asyncio.get_event_loop().time()
|
|
150
|
+
|
|
151
|
+
self.logger.info(f"Iteration {i}/{max_iter}")
|
|
152
|
+
self.console.print(f"[bold]━━━ Iteration {i}/{max_iter} ━━━[/bold]")
|
|
153
|
+
|
|
154
|
+
# Step 1: Run hooks and collect failures
|
|
155
|
+
hook_results = await self._run_hooks(command)
|
|
156
|
+
|
|
157
|
+
# Step 2: Check for convergence (all passing)
|
|
158
|
+
if hook_results["all_passing"]:
|
|
159
|
+
all_passing = True
|
|
160
|
+
exit_reason = "convergence"
|
|
161
|
+
|
|
162
|
+
iteration_duration = (
|
|
163
|
+
asyncio.get_event_loop().time() - iteration_start_time
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
iteration = FixIteration(
|
|
167
|
+
iteration_num=i,
|
|
168
|
+
hooks_run=hook_results["hooks_run"],
|
|
169
|
+
issues_found=0,
|
|
170
|
+
fixes_applied=0,
|
|
171
|
+
fixes_successful=0,
|
|
172
|
+
hooks_passing=hook_results["passing"],
|
|
173
|
+
hooks_failing=[],
|
|
174
|
+
duration=iteration_duration,
|
|
175
|
+
convergence_status="converged",
|
|
176
|
+
)
|
|
177
|
+
iterations.append(iteration)
|
|
178
|
+
|
|
179
|
+
self.logger.info("✅ All hooks passing - convergence achieved!")
|
|
180
|
+
self.console.print(
|
|
181
|
+
"[bold green]✅ All hooks passing - convergence achieved![/bold green]\n"
|
|
182
|
+
)
|
|
183
|
+
break
|
|
184
|
+
|
|
185
|
+
# Step 3: Apply AI fixes for failures
|
|
186
|
+
fix_results = await self._apply_fixes(hook_results["failures"])
|
|
187
|
+
|
|
188
|
+
iteration_duration = asyncio.get_event_loop().time() - iteration_start_time
|
|
189
|
+
|
|
190
|
+
# Step 4: Record iteration
|
|
191
|
+
iteration = FixIteration(
|
|
192
|
+
iteration_num=i,
|
|
193
|
+
hooks_run=hook_results["hooks_run"],
|
|
194
|
+
issues_found=len(hook_results["failures"]),
|
|
195
|
+
fixes_applied=fix_results["fixes_applied"],
|
|
196
|
+
fixes_successful=fix_results["fixes_successful"],
|
|
197
|
+
hooks_passing=hook_results["passing"],
|
|
198
|
+
hooks_failing=hook_results["failing"],
|
|
199
|
+
duration=iteration_duration,
|
|
200
|
+
convergence_status="in_progress",
|
|
201
|
+
)
|
|
202
|
+
iterations.append(iteration)
|
|
203
|
+
|
|
204
|
+
self.console.print(
|
|
205
|
+
f"[dim]Issues found: {iteration.issues_found} | "
|
|
206
|
+
f"Fixes applied: {iteration.fixes_applied} | "
|
|
207
|
+
f"Successful: {iteration.fixes_successful}[/dim]\n"
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
# Step 5: Check for convergence (no fixes possible)
|
|
211
|
+
if fix_results["fixes_applied"] == 0:
|
|
212
|
+
exit_reason = "no_progress"
|
|
213
|
+
self.logger.warning("⚠️ No fixes applied - cannot make progress")
|
|
214
|
+
self.console.print(
|
|
215
|
+
"[bold yellow]⚠️ No fixes applied - cannot make progress[/bold yellow]\n"
|
|
216
|
+
)
|
|
217
|
+
break
|
|
218
|
+
|
|
219
|
+
# Handle max iterations reached
|
|
220
|
+
if not all_passing and exit_reason == "unknown":
|
|
221
|
+
exit_reason = "max_iterations"
|
|
222
|
+
self.logger.warning(
|
|
223
|
+
f"⚠️ Max iterations ({max_iter}) reached without full convergence"
|
|
224
|
+
)
|
|
225
|
+
self.console.print(
|
|
226
|
+
f"[bold yellow]⚠️ Max iterations ({max_iter}) reached[/bold yellow]\n"
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
# Calculate final statistics
|
|
230
|
+
total_duration = asyncio.get_event_loop().time() - workflow_start_time
|
|
231
|
+
total_fixes = sum(it.fixes_successful for it in iterations)
|
|
232
|
+
total_issues = sum(it.issues_found for it in iterations)
|
|
233
|
+
|
|
234
|
+
result = WorkflowResult(
|
|
235
|
+
success=all_passing,
|
|
236
|
+
iterations=iterations,
|
|
237
|
+
total_fixes=total_fixes,
|
|
238
|
+
total_issues_found=total_issues,
|
|
239
|
+
final_status="converged" if all_passing else "incomplete",
|
|
240
|
+
total_duration=total_duration,
|
|
241
|
+
convergence_achieved=all_passing,
|
|
242
|
+
exit_reason=exit_reason,
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
# Print summary
|
|
246
|
+
self._print_summary(result)
|
|
247
|
+
|
|
248
|
+
return result
|
|
249
|
+
|
|
250
|
+
async def _run_hooks(self, command: str) -> dict[str, t.Any]:
|
|
251
|
+
"""Run pre-commit hooks and collect results.
|
|
252
|
+
|
|
253
|
+
Args:
|
|
254
|
+
command: Semantic command (determines which hooks to run)
|
|
255
|
+
|
|
256
|
+
Returns:
|
|
257
|
+
Dictionary with hook results and status
|
|
258
|
+
"""
|
|
259
|
+
self.logger.debug(f"Running hooks for command: {command}")
|
|
260
|
+
|
|
261
|
+
# Choose hook strategy based on command
|
|
262
|
+
if command in ("test", "check", "all"):
|
|
263
|
+
results = await self.hook_manager.run_comprehensive_hooks_async()
|
|
264
|
+
else:
|
|
265
|
+
results = await self.hook_manager.run_fast_hooks_async()
|
|
266
|
+
|
|
267
|
+
# Parse results
|
|
268
|
+
hooks_run = [r.name for r in results]
|
|
269
|
+
passing = [r.name for r in results if r.status == "passed"]
|
|
270
|
+
failing = [r.name for r in results if r.status != "passed"]
|
|
271
|
+
all_passing = len(failing) == 0
|
|
272
|
+
|
|
273
|
+
self.logger.debug(
|
|
274
|
+
f"Hook results: {len(passing)} passing, {len(failing)} failing"
|
|
275
|
+
)
|
|
276
|
+
|
|
277
|
+
return {
|
|
278
|
+
"hooks_run": hooks_run,
|
|
279
|
+
"passing": passing,
|
|
280
|
+
"failing": failing,
|
|
281
|
+
"failures": [r for r in results if r.status != "passed"],
|
|
282
|
+
"all_passing": all_passing,
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
async def _apply_fixes(self, failures: list[HookResult]) -> dict[str, int]:
|
|
286
|
+
"""Apply AI fixes for all failures.
|
|
287
|
+
|
|
288
|
+
Args:
|
|
289
|
+
failures: List of failed HookResults
|
|
290
|
+
|
|
291
|
+
Returns:
|
|
292
|
+
Dictionary with fix statistics
|
|
293
|
+
"""
|
|
294
|
+
fixes_applied = 0
|
|
295
|
+
fixes_successful = 0
|
|
296
|
+
|
|
297
|
+
self.logger.info(f"Applying fixes for {len(failures)} failures")
|
|
298
|
+
|
|
299
|
+
for failure in failures:
|
|
300
|
+
try:
|
|
301
|
+
# Convert HookResult to Issue for agent system
|
|
302
|
+
issue = self._hook_result_to_issue(failure)
|
|
303
|
+
|
|
304
|
+
self.logger.debug(f"Fixing {failure.name}: {issue.message}")
|
|
305
|
+
|
|
306
|
+
# Coordinate fix via enhanced agent coordinator
|
|
307
|
+
result = await self.coordinator.handle_issues_proactively([issue])
|
|
308
|
+
|
|
309
|
+
if result.fixes_applied:
|
|
310
|
+
fixes_applied += len(result.fixes_applied)
|
|
311
|
+
|
|
312
|
+
if result.success:
|
|
313
|
+
fixes_successful += len(result.fixes_applied)
|
|
314
|
+
self.logger.info(
|
|
315
|
+
f"✅ Successfully fixed {failure.name} ({result.confidence:.2f} confidence)"
|
|
316
|
+
)
|
|
317
|
+
else:
|
|
318
|
+
self.logger.warning(
|
|
319
|
+
f"⚠️ Partial fix for {failure.name} ({result.confidence:.2f} confidence)"
|
|
320
|
+
)
|
|
321
|
+
else:
|
|
322
|
+
self.logger.debug(f"No fixes applied for {failure.name}")
|
|
323
|
+
|
|
324
|
+
except Exception as e:
|
|
325
|
+
self.logger.exception(f"Fix failed for {failure.name}: {e}")
|
|
326
|
+
self.console.print(f"[red]❌ Error fixing {failure.name}: {e}[/red]")
|
|
327
|
+
|
|
328
|
+
return {
|
|
329
|
+
"fixes_applied": fixes_applied,
|
|
330
|
+
"fixes_successful": fixes_successful,
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
def _hook_result_to_issue(self, hook_result: HookResult) -> Issue:
|
|
334
|
+
"""Convert a HookResult to an Issue for the agent system.
|
|
335
|
+
|
|
336
|
+
Args:
|
|
337
|
+
hook_result: Hook execution result
|
|
338
|
+
|
|
339
|
+
Returns:
|
|
340
|
+
Issue object for agent processing
|
|
341
|
+
"""
|
|
342
|
+
# Map hook names to issue types
|
|
343
|
+
issue_type_mapping: dict[str, IssueType] = {
|
|
344
|
+
"refurb": IssueType.DRY_VIOLATION,
|
|
345
|
+
"pyright": IssueType.TYPE_ERROR,
|
|
346
|
+
"bandit": IssueType.SECURITY,
|
|
347
|
+
"ruff": IssueType.FORMATTING,
|
|
348
|
+
"pytest": IssueType.TEST_FAILURE,
|
|
349
|
+
"complexipy": IssueType.COMPLEXITY,
|
|
350
|
+
"vulture": IssueType.DEAD_CODE,
|
|
351
|
+
"creosote": IssueType.DEPENDENCY,
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
# Determine issue type from hook name
|
|
355
|
+
issue_type = IssueType.FORMATTING # Default
|
|
356
|
+
for hook_prefix, mapped_type in issue_type_mapping.items():
|
|
357
|
+
if hook_result.name.lower().startswith(hook_prefix):
|
|
358
|
+
issue_type = mapped_type
|
|
359
|
+
break
|
|
360
|
+
|
|
361
|
+
# Determine severity based on hook status
|
|
362
|
+
severity = Priority.HIGH if hook_result.status == "failed" else Priority.MEDIUM
|
|
363
|
+
|
|
364
|
+
# Create issue
|
|
365
|
+
issue = Issue(
|
|
366
|
+
id=f"{hook_result.id}_{hook_result.name}",
|
|
367
|
+
type=issue_type,
|
|
368
|
+
severity=severity,
|
|
369
|
+
message=f"Hook {hook_result.name} failed",
|
|
370
|
+
details=hook_result.issues_found or [],
|
|
371
|
+
stage=hook_result.stage,
|
|
372
|
+
)
|
|
373
|
+
|
|
374
|
+
return issue
|
|
375
|
+
|
|
376
|
+
def _check_convergence(self, hook_results: dict[str, t.Any]) -> bool:
|
|
377
|
+
"""Determine if workflow has converged.
|
|
378
|
+
|
|
379
|
+
Args:
|
|
380
|
+
hook_results: Results from _run_hooks
|
|
381
|
+
|
|
382
|
+
Returns:
|
|
383
|
+
True if converged (all hooks passing)
|
|
384
|
+
"""
|
|
385
|
+
return bool(hook_results["all_passing"])
|
|
386
|
+
|
|
387
|
+
def _print_summary(self, result: WorkflowResult) -> None:
|
|
388
|
+
"""Print workflow summary to console.
|
|
389
|
+
|
|
390
|
+
Args:
|
|
391
|
+
result: Final workflow result
|
|
392
|
+
"""
|
|
393
|
+
self.console.print("\n[bold cyan]━━━ Auto-Fix Summary ━━━[/bold cyan]")
|
|
394
|
+
|
|
395
|
+
# Status
|
|
396
|
+
status_color = "green" if result.success else "yellow"
|
|
397
|
+
status_icon = "✅" if result.success else "⚠️"
|
|
398
|
+
self.console.print(
|
|
399
|
+
f"{status_icon} [bold {status_color}]Status:[/bold {status_color}] {result.final_status}"
|
|
400
|
+
)
|
|
401
|
+
|
|
402
|
+
# Statistics
|
|
403
|
+
self.console.print("📊 [bold]Statistics:[/bold]")
|
|
404
|
+
self.console.print(f" • Iterations: {len(result.iterations)}")
|
|
405
|
+
self.console.print(f" • Total issues found: {result.total_issues_found}")
|
|
406
|
+
self.console.print(f" • Total fixes applied: {result.total_fixes}")
|
|
407
|
+
self.console.print(f" • Total duration: {result.total_duration:.2f}s")
|
|
408
|
+
self.console.print(f" • Exit reason: {result.exit_reason}")
|
|
409
|
+
|
|
410
|
+
# Iteration breakdown
|
|
411
|
+
if result.iterations:
|
|
412
|
+
self.console.print("\n📋 [bold]Iteration Breakdown:[/bold]")
|
|
413
|
+
for iteration in result.iterations:
|
|
414
|
+
status_symbol = "✅" if not iteration.hooks_failing else "❌"
|
|
415
|
+
self.console.print(
|
|
416
|
+
f" {status_symbol} Iteration {iteration.iteration_num}: "
|
|
417
|
+
f"{iteration.fixes_successful}/{iteration.fixes_applied} fixes successful "
|
|
418
|
+
f"({iteration.duration:.2f}s)"
|
|
419
|
+
)
|
|
420
|
+
|
|
421
|
+
self.console.print()
|
|
422
|
+
|
|
423
|
+
|
|
424
|
+
def create_auto_fix_workflow(
|
|
425
|
+
project_path: Path | None = None,
|
|
426
|
+
console: Console | None = None,
|
|
427
|
+
enable_external_agents: bool = True,
|
|
428
|
+
) -> AutoFixWorkflow:
|
|
429
|
+
"""Factory function to create an AutoFixWorkflow.
|
|
430
|
+
|
|
431
|
+
Args:
|
|
432
|
+
project_path: Path to project root (defaults to cwd)
|
|
433
|
+
console: Rich console for output (creates default if None)
|
|
434
|
+
enable_external_agents: Enable Claude Code external agent integration
|
|
435
|
+
|
|
436
|
+
Returns:
|
|
437
|
+
Configured AutoFixWorkflow instance
|
|
438
|
+
"""
|
|
439
|
+
return AutoFixWorkflow(
|
|
440
|
+
project_path=project_path,
|
|
441
|
+
console=console,
|
|
442
|
+
enable_external_agents=enable_external_agents,
|
|
443
|
+
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: crackerjack
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.40.1
|
|
4
4
|
Summary: Crackerjack Python project management tool
|
|
5
5
|
Project-URL: documentation, https://github.com/lesleslie/crackerjack
|
|
6
6
|
Project-URL: homepage, https://github.com/lesleslie/crackerjack
|
|
@@ -79,7 +79,7 @@ Description-Content-Type: text/markdown
|
|
|
79
79
|
[](https://github.com/astral-sh/uv)
|
|
80
80
|
[](https://github.com/pre-commit/pre-commit)
|
|
81
81
|
[](https://opensource.org/licenses/BSD-3-Clause)
|
|
82
|
-

|
|
83
83
|
|
|
84
84
|
## 🎯 Purpose
|
|
85
85
|
|
|
@@ -142,17 +142,17 @@ Crackerjack is built on the following core principles:
|
|
|
142
142
|
|
|
143
143
|
## Table of Contents
|
|
144
144
|
|
|
145
|
-
-
|
|
146
|
-
-
|
|
147
|
-
-
|
|
148
|
-
-
|
|
149
|
-
-
|
|
150
|
-
-
|
|
151
|
-
-
|
|
152
|
-
-
|
|
153
|
-
-
|
|
154
|
-
-
|
|
155
|
-
-
|
|
145
|
+
- [Installation](<#installation>)
|
|
146
|
+
- [Quick Start](<#quick-start>)
|
|
147
|
+
- [AI Auto-Fix Features](<#ai-auto-fix-features>)
|
|
148
|
+
- [Core Workflow](<#core-workflow>)
|
|
149
|
+
- [Core Features](<#core-features>)
|
|
150
|
+
- [MCP Server Configuration](<#mcp-server-configuration>)
|
|
151
|
+
- [Pre-commit Hook Modes](<#pre-commit-hook-modes>)
|
|
152
|
+
- [Command Reference](<#command-reference>)
|
|
153
|
+
- [Style Guide](<#style-guide>)
|
|
154
|
+
- [Publishing & Version Management](<#publishing--version-management>)
|
|
155
|
+
- [Troubleshooting](<#-troubleshooting>)
|
|
156
156
|
|
|
157
157
|
## Installation
|
|
158
158
|
|
|
@@ -241,6 +241,12 @@ The AI agent intelligently fixes:
|
|
|
241
241
|
# Standard AI agent mode (recommended)
|
|
242
242
|
python -m crackerjack --ai-fix --run-tests --verbose
|
|
243
243
|
|
|
244
|
+
# Preview fixes without applying (dry-run mode)
|
|
245
|
+
python -m crackerjack --dry-run --run-tests --verbose
|
|
246
|
+
|
|
247
|
+
# Custom iteration limit
|
|
248
|
+
python -m crackerjack --ai-fix --max-iterations 15
|
|
249
|
+
|
|
244
250
|
# MCP server with WebSocket support (localhost:8675)
|
|
245
251
|
python -m crackerjack --start-mcp-server
|
|
246
252
|
|
|
@@ -248,6 +254,43 @@ python -m crackerjack --start-mcp-server
|
|
|
248
254
|
python -m crackerjack.mcp.progress_monitor <job_id> ws://localhost:8675
|
|
249
255
|
```
|
|
250
256
|
|
|
257
|
+
#### MCP Integration
|
|
258
|
+
|
|
259
|
+
When using crackerjack via MCP tools (session-mgmt-mcp):
|
|
260
|
+
|
|
261
|
+
```python
|
|
262
|
+
# ✅ CORRECT - Use semantic command + ai_agent_mode parameter
|
|
263
|
+
crackerjack_run(command="test", ai_agent_mode=True)
|
|
264
|
+
|
|
265
|
+
# ✅ CORRECT - With additional arguments
|
|
266
|
+
crackerjack_run(command="check", args="--verbose", ai_agent_mode=True, timeout=600)
|
|
267
|
+
|
|
268
|
+
# ✅ CORRECT - Dry-run mode
|
|
269
|
+
crackerjack_run(command="test", args="--dry-run", ai_agent_mode=True)
|
|
270
|
+
|
|
271
|
+
# ❌ WRONG - Don't put flags in command parameter
|
|
272
|
+
crackerjack_run(command="--ai-fix -t") # This will error!
|
|
273
|
+
|
|
274
|
+
# ❌ WRONG - Don't use --ai-fix in args
|
|
275
|
+
crackerjack_run(command="test", args="--ai-fix") # Use ai_agent_mode=True instead
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
#### Configuration
|
|
279
|
+
|
|
280
|
+
Auto-fix requires:
|
|
281
|
+
|
|
282
|
+
1. **Anthropic API key**: Set environment variable
|
|
283
|
+
|
|
284
|
+
```bash
|
|
285
|
+
export ANTHROPIC_API_KEY=sk-ant-...
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
1. **Configuration file**: `settings/adapters.yml`
|
|
289
|
+
|
|
290
|
+
```yaml
|
|
291
|
+
ai: claude
|
|
292
|
+
```
|
|
293
|
+
|
|
251
294
|
#### Key Benefits
|
|
252
295
|
|
|
253
296
|
- **Zero Configuration**: No complex flag combinations needed
|
|
@@ -736,7 +779,7 @@ python -m crackerjack --ai-fix
|
|
|
736
779
|
| **Clear Caches** | `python -m crackerjack --clear-cache` | Reset all cache data |
|
|
737
780
|
| **Fast Iteration** | `python -m crackerjack --skip-hooks` | Skip quality checks during dev |
|
|
738
781
|
| **Documentation** | `python -m crackerjack --generate-docs` | Generate API documentation |
|
|
739
|
-
| **Advanced Features** | See
|
|
782
|
+
| **Advanced Features** | See [ADVANCED-FEATURES.md](<./docs/guides/ADVANCED-FEATURES.md>) | 82 enterprise/power user flags |
|
|
740
783
|
|
|
741
784
|
**📑 Alphabetical Flag Reference**
|
|
742
785
|
|
|
@@ -777,9 +820,9 @@ python -m crackerjack --ai-fix
|
|
|
777
820
|
|
|
778
821
|
**🔗 Related Documentation**
|
|
779
822
|
|
|
780
|
-
- **Advanced Features**:
|
|
781
|
-
- **Developer Guide**:
|
|
782
|
-
- **Phase 2 Analysis**:
|
|
823
|
+
- **Advanced Features**: [ADVANCED-FEATURES.md](<./docs/guides/ADVANCED-FEATURES.md>) - 82 enterprise/power user flags
|
|
824
|
+
- **Developer Guide**: [CLAUDE.md](<./CLAUDE.md>) - AI assistant guidelines and developer commands
|
|
825
|
+
- **Phase 2 Analysis**: [PHASE2-CROSS-REFERENCE-ANALYSIS.md](<./docs/history/phases/PHASE2-CROSS-REFERENCE-ANALYSIS.md>) - CLI flag audit (archived)
|
|
783
826
|
|
|
784
827
|
______________________________________________________________________
|
|
785
828
|
|
|
@@ -2,20 +2,22 @@ crackerjack/__init__.py,sha256=DajG9zHB8qBdgdiKMumrrssUbKeMXmtIQ3oOaSTb46Y,1426
|
|
|
2
2
|
crackerjack/__main__.py,sha256=jf-77hiq9_OVWFUN3qaX1eiuFEg-GMDRnszwyujx22I,59085
|
|
3
3
|
crackerjack/api.py,sha256=PyCRaZHvKWdu62_2O4t_HcEfKNBdqyrfPdonS_PNn4c,21495
|
|
4
4
|
crackerjack/code_cleaner.py,sha256=M1zVaq31uW0nOkPneKR8kfR3892gyyVx0VhFgRaxsj4,44338
|
|
5
|
-
crackerjack/dynamic_config.py,sha256=
|
|
5
|
+
crackerjack/dynamic_config.py,sha256=X0KfFfu9vdMl8aCRnJ4qExaMvssPKNBv3NznUOVO0wo,21769
|
|
6
6
|
crackerjack/errors.py,sha256=yYbZ92kn_y6acEWgQvEPvozAYs2HT65uLwAXrtXxGsE,10049
|
|
7
7
|
crackerjack/interactive.py,sha256=7hSyCfUxuzyvVUdkO1XsOfeQ5vAlTjIH_TB8o3af-9g,21990
|
|
8
8
|
crackerjack/py313.py,sha256=TbzL6vJ8kf5GbuAFVjZ6wgfXnhcoLlzwATaFhV8HEVo,6256
|
|
9
|
-
crackerjack/adapters/__init__.py,sha256=
|
|
9
|
+
crackerjack/adapters/__init__.py,sha256=SPiokldDNDbwLFxEH2qYpSRuEsM4aEBbhLCleGXFiyc,593
|
|
10
10
|
crackerjack/adapters/lsp_client.py,sha256=4kQ3T5JiWC7uc6kOjZuPdtUboseKSDjZpuKQpV74onc,10963
|
|
11
11
|
crackerjack/adapters/rust_tool_adapter.py,sha256=ui_qMt_WIwInRvRCeT7MnIdp8eln7Fvp4hakXQiVnjg,5999
|
|
12
12
|
crackerjack/adapters/rust_tool_manager.py,sha256=RotOX7WUMAm4GT9bD8xOhSeHFXaME37R9dW-YLTpVcc,6590
|
|
13
13
|
crackerjack/adapters/skylos_adapter.py,sha256=aUO8P7daTXstM3UxaAKOwZDLdMPIUbjmhmXROTF5RlU,7890
|
|
14
14
|
crackerjack/adapters/zuban_adapter.py,sha256=NHg2vAsaONEMMlV3a5lmjPm61poIojL-gqZWqoX-R9o,19663
|
|
15
|
+
crackerjack/adapters/ai/__init__.py,sha256=airGrN2riDDdhqM75j4dIOWeFnuINYYzGPns2ZT1CGg,168
|
|
16
|
+
crackerjack/adapters/ai/claude.py,sha256=kQTCC_9tJA3ZXPdZRFjtB_SJYbesA3EdAPm8vm5Tl2k,28895
|
|
15
17
|
crackerjack/agents/__init__.py,sha256=Mja1iIHEW4wsvci5g6CXHtR1OEltd3N00XOZLUfaHPU,950
|
|
16
18
|
crackerjack/agents/architect_agent.py,sha256=Gq9eAy1_pl-1iEYC28xn4rdIPi9AVynZ7Sd5FnBrNn8,8130
|
|
17
19
|
crackerjack/agents/base.py,sha256=qu3wmtFZwXsaf75WXiRdyvJJaNp_EE7MptJQI_7Wd3o,4930
|
|
18
|
-
crackerjack/agents/claude_code_bridge.py,sha256=
|
|
20
|
+
crackerjack/agents/claude_code_bridge.py,sha256=B8tDvU0I5xlioSTkJ9PdpD9utxS9JEfOAz7TuKPQFOI,20606
|
|
19
21
|
crackerjack/agents/coordinator.py,sha256=jK22whXR6WKe7NWyhQAwTnlPNlNqJigJTn3-COf2Wpc,22637
|
|
20
22
|
crackerjack/agents/documentation_agent.py,sha256=bI9qPJ5rTZrjimythO_N27zpJFrFcP6i1N2hYZMr_u4,17178
|
|
21
23
|
crackerjack/agents/dry_agent.py,sha256=mAcwIRCnuf5X6MKMxOaTLx4895VvPVV-CZcnL-DSJOo,20501
|
|
@@ -37,10 +39,10 @@ crackerjack/agents/tracker.py,sha256=5Hje7Ox-hSPq2yTqkbucwChEvzwlCQxjgVri0KSUUPY
|
|
|
37
39
|
crackerjack/cli/__init__.py,sha256=6pnXZIglNzax4XA2lWfiqAZQs24Dr7GbXUwIwCF_9_w,583
|
|
38
40
|
crackerjack/cli/cache_handlers.py,sha256=Wa-1ZJdWO7RufLd6Fo62pUTij-a3epFi92h5CfJLlDM,6757
|
|
39
41
|
crackerjack/cli/cache_handlers_enhanced.py,sha256=6X5rYSo1l-rj9eb7eB8mpA-6BlUagyDu-IvCaHwYy5k,24841
|
|
40
|
-
crackerjack/cli/facade.py,sha256=
|
|
42
|
+
crackerjack/cli/facade.py,sha256=VlLaubyWoD19HNjKzdH0hAtH3e2ABoT658JfLRTbVCc,5856
|
|
41
43
|
crackerjack/cli/handlers.py,sha256=mYhwMLUKid6mQLff0ScpcnhP0yUS9IzOIMdM7VLkUCc,17178
|
|
42
44
|
crackerjack/cli/interactive.py,sha256=E7WgzgNRlgOuKLbi5Io3RQ3BBXS8sIDiUQcJh6b2x9I,17583
|
|
43
|
-
crackerjack/cli/options.py,sha256=
|
|
45
|
+
crackerjack/cli/options.py,sha256=XAyN_Gj_tQH4r9RDZ7CH8FxY4lrCHkha0KEsy0bAcfg,37790
|
|
44
46
|
crackerjack/cli/semantic_handlers.py,sha256=QrlyKfy3K7wI0mLYv7W4nL9WZ14SA4zqWDUc21jM9is,10103
|
|
45
47
|
crackerjack/cli/utils.py,sha256=XC7dT8GNidhORjUe2p2hQOpZgCi2KvVCNu6g3azzgqY,584
|
|
46
48
|
crackerjack/config/__init__.py,sha256=b0481N2f_JvGufMPcbo5IXu2VjYd111r1BHw0oD3x7o,330
|
|
@@ -159,7 +161,7 @@ crackerjack/plugins/loader.py,sha256=2U4_vWLXsc7qOji-4dzJDF993EU7p8SH23ijhqMHahY
|
|
|
159
161
|
crackerjack/plugins/managers.py,sha256=3kQlxjvcHyHDgZIdr-JZBO1kqz2asqA4kf2XVAA1K6A,8824
|
|
160
162
|
crackerjack/security/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
161
163
|
crackerjack/security/audit.py,sha256=NZJRREvjeKJMhusNZkL8GnBf4XiQFnTwKGcu6NqmvgI,6728
|
|
162
|
-
crackerjack/services/__init__.py,sha256=
|
|
164
|
+
crackerjack/services/__init__.py,sha256=EJoHoZfGbD8TiuIJgQSE8oFHWjJYufZpMVXwzPneKQk,254
|
|
163
165
|
crackerjack/services/anomaly_detector.py,sha256=vUwhhGxNGarHQAly4GaN7kc5PBaBvlTfzuvbNYJNf3g,13405
|
|
164
166
|
crackerjack/services/api_extractor.py,sha256=iN4JDkvSyfHRNlhgV0Sf5B9cg4NVaCZn6p-zQlp9YGU,23836
|
|
165
167
|
crackerjack/services/backup_service.py,sha256=0e8AAo9svSBtbsbI9vwnAVSilB2fjph61lskgsUvDLk,15528
|
|
@@ -183,6 +185,7 @@ crackerjack/services/enhanced_filesystem.py,sha256=IT-obfj2U2Sfy0iJOq0ZAiczEN6Ld
|
|
|
183
185
|
crackerjack/services/enterprise_optimizer.py,sha256=q6srIGxe18N9zH-MNhZ9R34sWnr_-ybGqOWnKyI17Zk,33798
|
|
184
186
|
crackerjack/services/error_pattern_analyzer.py,sha256=YKFQi_nXx5dq-Wvi6AngKEaA-Bv5ggTQ2B2ML0AtF0c,24415
|
|
185
187
|
crackerjack/services/file_hasher.py,sha256=eReytwwK-_-B8JBnpwytDC52cKKgg4qpaxaZKcQjD-0,5211
|
|
188
|
+
crackerjack/services/file_modifier.py,sha256=mgnr9rROfBGzUowi0vDBhEf3rABfqG-I74su5pqkl90,17013
|
|
186
189
|
crackerjack/services/filesystem.py,sha256=nmL3mYqylS_BSQpwFbC7EMHoA44K5qUxa9CPg1QFZvc,17480
|
|
187
190
|
crackerjack/services/git.py,sha256=E4z-hdA0UR-y5FY2A8d3fugt5Q41lonLAIt_op2Zde0,12727
|
|
188
191
|
crackerjack/services/health_metrics.py,sha256=nDuKEC2a5csOhMpy6zXJkls1Y4Vfrr62-4cFcWCr8ow,21536
|
|
@@ -235,8 +238,10 @@ crackerjack/tools/validate_input_validator_patterns.py,sha256=NN7smYlXWrHLQXTb-8
|
|
|
235
238
|
crackerjack/tools/validate_regex_patterns.py,sha256=J7GG9EP1fASpRIsG8qRPeiCSkdCwmk0sdo29GgoJ6w8,5863
|
|
236
239
|
crackerjack/ui/__init__.py,sha256=eMb1OeTU-dSLICAACn0YdYB4Amdr8wHckjKfn0wOIZE,37
|
|
237
240
|
crackerjack/ui/server_panels.py,sha256=F5IH6SNN06BaZQMsFx_D-OA286aojmaFPJ5kvvSRv_c,4232
|
|
238
|
-
crackerjack
|
|
239
|
-
crackerjack
|
|
240
|
-
crackerjack-0.
|
|
241
|
-
crackerjack-0.
|
|
242
|
-
crackerjack-0.
|
|
241
|
+
crackerjack/workflows/__init__.py,sha256=jaJdTSPM_zKREXp7F6nUlk9tTrKHaWC8UqlXZbHBRkE,292
|
|
242
|
+
crackerjack/workflows/auto_fix.py,sha256=q-3yQB_cHc-kgiG0BmPrwK-R3x3TJFqs63Uy5H9yKC0,16037
|
|
243
|
+
crackerjack-0.40.1.dist-info/METADATA,sha256=AEJAbxiemr1z-Wb3RhqAwMEInCLryj8cPu18N3UwprE,43456
|
|
244
|
+
crackerjack-0.40.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
245
|
+
crackerjack-0.40.1.dist-info/entry_points.txt,sha256=AJKNft0WXm9xoGUJ3Trl-iXHOWxRAYbagQiza3AILr4,57
|
|
246
|
+
crackerjack-0.40.1.dist-info/licenses/LICENSE,sha256=fDt371P6_6sCu7RyqiZH_AhT1LdN3sN1zjBtqEhDYCk,1531
|
|
247
|
+
crackerjack-0.40.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|