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,401 @@
1
+ import typing as t
2
+ from enum import Enum
3
+
4
+ import typer
5
+ from pydantic import BaseModel, field_validator
6
+
7
+
8
+ class BumpOption(str, Enum):
9
+ patch = "patch"
10
+ minor = "minor"
11
+ major = "major"
12
+ interactive = "interactive"
13
+
14
+ def __str__(self) -> str:
15
+ return self.value
16
+
17
+
18
+ class Options(BaseModel):
19
+ commit: bool = False
20
+ interactive: bool = False
21
+ no_config_updates: bool = False
22
+ update_precommit: bool = False
23
+ publish: BumpOption | None = None
24
+ bump: BumpOption | None = None
25
+ verbose: bool = False
26
+ clean: bool = False
27
+ test: bool = False
28
+ benchmark: bool = False
29
+ test_workers: int = 0
30
+ test_timeout: int = 0
31
+ all: BumpOption | None = None
32
+ ai_agent: bool = False
33
+ start_mcp_server: bool = False
34
+ stop_mcp_server: bool = False
35
+ restart_mcp_server: bool = False
36
+ create_pr: bool = False
37
+ skip_hooks: bool = False
38
+ fast: bool = False
39
+ comp: bool = False
40
+ async_mode: bool = False
41
+ experimental_hooks: bool = False
42
+ enable_pyrefly: bool = False
43
+ enable_ty: bool = False
44
+ cleanup: t.Any | None = None
45
+ no_git_tags: bool = False
46
+ skip_version_check: bool = False
47
+ cleanup_pypi: bool = False
48
+ keep_releases: int = 10
49
+ track_progress: bool = False
50
+ orchestrated: bool = False
51
+ coverage: bool = False
52
+ orchestration_strategy: str = "adaptive"
53
+ orchestration_progress: str = "granular"
54
+ orchestration_ai_mode: str = "single-agent"
55
+ monitor: bool = False
56
+ enhanced_monitor: bool = False
57
+ watchdog: bool = False
58
+ start_websocket_server: bool = False
59
+ stop_websocket_server: bool = False
60
+ restart_websocket_server: bool = False
61
+ websocket_port: int | None = None
62
+ dev: bool = False
63
+ dashboard: bool = False
64
+ max_iterations: int = 10
65
+ enterprise_batch: str | None = None
66
+ monitor_dashboard: str | None = None
67
+ coverage_status: bool = False
68
+ coverage_goal: float | None = None
69
+ no_coverage_ratchet: bool = False
70
+
71
+ @classmethod
72
+ @field_validator("publish", "bump", mode="before")
73
+ def validate_bump_options(cls, value: t.Any) -> BumpOption | None:
74
+ if value is None:
75
+ return None
76
+ if value == "":
77
+ return BumpOption.interactive
78
+ try:
79
+ return BumpOption(value.lower())
80
+ except ValueError:
81
+ valid_options = ", ".join([o.value for o in BumpOption])
82
+ msg = f"Invalid bump option: {value}. Must be one of: {valid_options}"
83
+ raise ValueError(
84
+ msg,
85
+ )
86
+
87
+
88
+ CLI_OPTIONS = {
89
+ "commit": typer.Option(
90
+ False,
91
+ "-c",
92
+ "--commit",
93
+ help="Commit and push changes to Git.",
94
+ ),
95
+ "interactive": typer.Option(
96
+ False,
97
+ "-i",
98
+ "--interactive",
99
+ help="Use the interactive Rich UI for a better experience.",
100
+ ),
101
+ "no_config_updates": typer.Option(
102
+ False,
103
+ "-n",
104
+ "--no-config-updates",
105
+ help="Do not update configuration files.",
106
+ ),
107
+ "update_precommit": typer.Option(
108
+ False,
109
+ "-u",
110
+ "--update-precommit",
111
+ help="Update pre-commit hooks configuration.",
112
+ ),
113
+ "verbose": typer.Option(False, "-v", "--verbose", help="Enable verbose output."),
114
+ "publish": typer.Option(
115
+ None,
116
+ "-p",
117
+ "--publish",
118
+ help="Bump version and publish to PyPI (patch, minor, major). Use 'interactive' to be prompted for version selection.",
119
+ case_sensitive=False,
120
+ ),
121
+ "bump": typer.Option(
122
+ None,
123
+ "-b",
124
+ "--bump",
125
+ help="Bump version (patch, minor, major).",
126
+ case_sensitive=False,
127
+ ),
128
+ "clean": typer.Option(
129
+ False,
130
+ "-x",
131
+ "--clean",
132
+ help="Remove docstrings, line comments, and unnecessary whitespace from source code (doesn't affect test files).",
133
+ ),
134
+ "test": typer.Option(False, "-t", "--test", help="Run tests."),
135
+ "benchmark": typer.Option(
136
+ False,
137
+ "--benchmark",
138
+ help="Run tests in benchmark mode (disables parallel execution).",
139
+ ),
140
+ "test_workers": typer.Option(
141
+ 0,
142
+ "--test-workers",
143
+ help="Number of parallel workers for running tests (0 = auto-detect, 1 = disable parallelization).",
144
+ ),
145
+ "test_timeout": typer.Option(
146
+ 0,
147
+ "--test-timeout",
148
+ help="Timeout in seconds for individual tests (0 = use default based on project size).",
149
+ ),
150
+ "skip_hooks": typer.Option(
151
+ False,
152
+ "-s",
153
+ "--skip-hooks",
154
+ help="Skip running pre-commit hooks (useful with -t).",
155
+ ),
156
+ "fast": typer.Option(
157
+ False,
158
+ "--fast",
159
+ help="Run only fast hooks (formatting and basic checks).",
160
+ ),
161
+ "comp": typer.Option(
162
+ False,
163
+ "--comp",
164
+ help="Run only comprehensive hooks (type checking, security, complexity analysis).",
165
+ ),
166
+ "all": typer.Option(
167
+ None,
168
+ "-a",
169
+ "--all",
170
+ help="Run with `-x -t -p <patch|minor|major> -c` development options).",
171
+ case_sensitive=False,
172
+ ),
173
+ "create_pr": typer.Option(
174
+ False,
175
+ "-r",
176
+ "--pr",
177
+ "--new-pull-request",
178
+ help="Create a new pull request to the upstream repository.",
179
+ ),
180
+ "ai_agent": typer.Option(
181
+ False,
182
+ "--ai-agent",
183
+ help="Enable AI agent mode with autonomous auto-fixing.",
184
+ ),
185
+ "start_mcp_server": typer.Option(
186
+ False,
187
+ "--start-mcp-server",
188
+ help="Start MCP server for AI agent integration.",
189
+ ),
190
+ "stop_mcp_server": typer.Option(
191
+ False,
192
+ "--stop-mcp-server",
193
+ help="Stop all running MCP servers.",
194
+ ),
195
+ "restart_mcp_server": typer.Option(
196
+ False,
197
+ "--restart-mcp-server",
198
+ help="Restart MCP server (stop and start again).",
199
+ ),
200
+ "async_mode": typer.Option(
201
+ False,
202
+ "--async",
203
+ help="Enable async mode for faster file operations (experimental).",
204
+ hidden=True,
205
+ ),
206
+ "experimental_hooks": typer.Option(
207
+ False,
208
+ "--experimental-hooks",
209
+ help="Enable experimental pre-commit hooks (includes pyrefly and ty).",
210
+ ),
211
+ "enable_pyrefly": typer.Option(
212
+ False,
213
+ "--enable-pyrefly",
214
+ help="Enable pyrefly experimental type checking (requires experimental hooks mode).",
215
+ ),
216
+ "enable_ty": typer.Option(
217
+ False,
218
+ "--enable-ty",
219
+ help="Enable ty experimental type verification (requires experimental hooks mode).",
220
+ ),
221
+ "no_git_tags": typer.Option(
222
+ False,
223
+ "--no-git-tags",
224
+ help="Skip creating git tags during version bumping (tags are created by default).",
225
+ ),
226
+ "skip_version_check": typer.Option(
227
+ False,
228
+ "--skip-version-check",
229
+ help="Skip version consistency verification between pyproject.toml and git tags.",
230
+ ),
231
+ "start_websocket_server": typer.Option(
232
+ False,
233
+ "--start-websocket-server",
234
+ help="Start WebSocket progress server on port 8675.",
235
+ ),
236
+ "stop_websocket_server": typer.Option(
237
+ False,
238
+ "--stop-websocket-server",
239
+ help="Stop all running WebSocket servers.",
240
+ ),
241
+ "restart_websocket_server": typer.Option(
242
+ False,
243
+ "--restart-websocket-server",
244
+ help="Restart WebSocket server (stop and start again).",
245
+ ),
246
+ "websocket_port": typer.Option(
247
+ None,
248
+ "--websocket-port",
249
+ help="Port for WebSocket server when using --start-mcp-server (e.g., 8675).",
250
+ ),
251
+ "watchdog": typer.Option(
252
+ False,
253
+ "--watchdog",
254
+ help="Start service watchdog to monitor and auto-restart MCP and WebSocket servers.",
255
+ ),
256
+ "monitor": typer.Option(
257
+ False,
258
+ "--monitor",
259
+ help="Start multi-project progress monitor with WebSocket polling, watchdog services, and autodiscovery.",
260
+ ),
261
+ "enhanced_monitor": typer.Option(
262
+ False,
263
+ "--enhanced-monitor",
264
+ help="Start enhanced progress monitor with advanced MetricCard widgets and modern web UI patterns.",
265
+ ),
266
+ "dev": typer.Option(
267
+ False,
268
+ "--dev",
269
+ help="Enable development mode for progress monitors (enables textual --dev mode).",
270
+ ),
271
+ "dashboard": typer.Option(
272
+ False,
273
+ "--dashboard",
274
+ help="Start the comprehensive dashboard with system metrics, job tracking, and performance monitoring.",
275
+ ),
276
+ "max_iterations": typer.Option(
277
+ 10,
278
+ "--max-iterations",
279
+ help="Maximum number of iterations for AI agent auto-fixing workflows (default: 10).",
280
+ ),
281
+ "ai_debug": typer.Option(
282
+ False,
283
+ "--ai-debug",
284
+ help="Enable verbose debugging for AI agent mode (implies --ai-agent).",
285
+ ),
286
+ "job_id": typer.Option(
287
+ None,
288
+ "--job-id",
289
+ help="Job ID for WebSocket progress tracking (internal use).",
290
+ hidden=True,
291
+ ),
292
+ "orchestrated": typer.Option(
293
+ False,
294
+ "--orchestrated",
295
+ help="Enable advanced orchestrated workflow mode with intelligent execution strategies, granular progress tracking, and multi-agent AI coordination.",
296
+ ),
297
+ "orchestration_strategy": typer.Option(
298
+ "adaptive",
299
+ "--orchestration-strategy",
300
+ help="Execution strategy for orchestrated mode: batch, individual, adaptive, selective (default: adaptive).",
301
+ ),
302
+ "orchestration_progress": typer.Option(
303
+ "granular",
304
+ "--orchestration-progress",
305
+ help="Progress tracking level: basic, detailed, granular, streaming (default: granular).",
306
+ ),
307
+ "orchestration_ai_mode": typer.Option(
308
+ "single-agent",
309
+ "--orchestration-ai-mode",
310
+ help="AI coordination mode: single-agent, multi-agent, coordinator (default: single-agent).",
311
+ ),
312
+ "coverage_status": typer.Option(
313
+ False,
314
+ "--coverage-status",
315
+ help="Show current coverage ratchet status and progress toward 100%.",
316
+ ),
317
+ "coverage_goal": typer.Option(
318
+ None,
319
+ "--coverage-goal",
320
+ help="Set explicit coverage target for this session (e.g., 15.0).",
321
+ ),
322
+ "no_coverage_ratchet": typer.Option(
323
+ False,
324
+ "--no-coverage-ratchet",
325
+ help="Disable coverage ratchet system temporarily (for experiments).",
326
+ ),
327
+ }
328
+
329
+
330
+ def create_options(
331
+ commit: bool,
332
+ interactive: bool,
333
+ no_config_updates: bool,
334
+ update_precommit: bool,
335
+ verbose: bool,
336
+ publish: BumpOption | None,
337
+ all: BumpOption | None,
338
+ bump: BumpOption | None,
339
+ clean: bool,
340
+ test: bool,
341
+ benchmark: bool,
342
+ test_workers: int,
343
+ test_timeout: int,
344
+ skip_hooks: bool,
345
+ fast: bool,
346
+ comp: bool,
347
+ create_pr: bool,
348
+ ai_agent: bool,
349
+ async_mode: bool,
350
+ experimental_hooks: bool,
351
+ enable_pyrefly: bool,
352
+ enable_ty: bool,
353
+ no_git_tags: bool,
354
+ skip_version_check: bool,
355
+ orchestrated: bool,
356
+ orchestration_strategy: str,
357
+ orchestration_progress: str,
358
+ orchestration_ai_mode: str,
359
+ dev: bool,
360
+ dashboard: bool,
361
+ max_iterations: int,
362
+ coverage_status: bool,
363
+ coverage_goal: float | None,
364
+ no_coverage_ratchet: bool,
365
+ ) -> Options:
366
+ return Options(
367
+ commit=commit,
368
+ interactive=interactive,
369
+ no_config_updates=no_config_updates,
370
+ update_precommit=update_precommit,
371
+ verbose=verbose,
372
+ publish=publish,
373
+ bump=bump,
374
+ clean=clean,
375
+ test=test,
376
+ benchmark=benchmark,
377
+ test_workers=test_workers,
378
+ test_timeout=test_timeout,
379
+ skip_hooks=skip_hooks,
380
+ fast=fast,
381
+ comp=comp,
382
+ all=all,
383
+ ai_agent=ai_agent,
384
+ create_pr=create_pr,
385
+ async_mode=async_mode,
386
+ experimental_hooks=experimental_hooks,
387
+ enable_pyrefly=enable_pyrefly,
388
+ enable_ty=enable_ty,
389
+ no_git_tags=no_git_tags,
390
+ skip_version_check=skip_version_check,
391
+ orchestrated=orchestrated,
392
+ orchestration_strategy=orchestration_strategy,
393
+ orchestration_progress=orchestration_progress,
394
+ orchestration_ai_mode=orchestration_ai_mode,
395
+ dev=dev,
396
+ dashboard=dashboard,
397
+ max_iterations=max_iterations,
398
+ coverage_status=coverage_status,
399
+ coverage_goal=coverage_goal,
400
+ no_coverage_ratchet=no_coverage_ratchet,
401
+ )
@@ -0,0 +1,18 @@
1
+ from pathlib import Path
2
+
3
+
4
+ def get_package_version() -> str:
5
+ try:
6
+ from importlib.metadata import version
7
+
8
+ return version("crackerjack")
9
+ except (ImportError, ModuleNotFoundError):
10
+ try:
11
+ import tomllib
12
+
13
+ pyproject_path = Path(__file__).parent.parent.parent / "pyproject.toml"
14
+ with pyproject_path.open("rb") as f:
15
+ pyproject_data = tomllib.load(f)
16
+ return pyproject_data["project"]["version"]
17
+ except Exception:
18
+ return "unknown"