devloop 0.8.0__tar.gz → 0.9.0__tar.gz
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.
- {devloop-0.8.0 → devloop-0.9.0}/PKG-INFO +2 -1
- {devloop-0.8.0 → devloop-0.9.0}/pyproject.toml +5 -4
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/agents/agent_health_monitor.py +0 -1
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/agents/formatter.py +5 -4
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/agents/git_commit_assistant.py +7 -5
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/agents/linter.py +2 -1
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/agents/test_runner.py +2 -1
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/cli/commands/marketplace.py +0 -1
- devloop-0.9.0/src/devloop/cli/commands/mcp_server.py +186 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/cli/commands/metrics.py +6 -6
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/cli/main.py +388 -198
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/core/agent_template.py +1 -1
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/core/backup_manager.py +3 -3
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/core/claude_adapter.py +5 -8
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/core/config.py +3 -3
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/core/context_store.py +1 -1
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/core/custom_agent.py +2 -1
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/core/event_store.py +20 -40
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/core/learning.py +4 -2
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/core/manager.py +7 -7
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/core/project_context.py +2 -2
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/marketplace/cache.py +3 -3
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/marketplace/installer.py +0 -1
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/marketplace/registry.py +0 -1
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/marketplace/registry_client.py +2 -2
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/marketplace/reviews.py +0 -1
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/marketplace/search.py +4 -4
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/marketplace/signing.py +3 -2
- devloop-0.9.0/src/devloop/mcp/__init__.py +5 -0
- devloop-0.9.0/src/devloop/mcp/resources.py +277 -0
- devloop-0.9.0/src/devloop/mcp/server.py +624 -0
- devloop-0.9.0/src/devloop/mcp/subscriptions.py +187 -0
- devloop-0.9.0/src/devloop/mcp/tools.py +1039 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/providers/artifactory_registry.py +1 -1
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/providers/jenkins_provider.py +1 -1
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/providers/provider_manager.py +3 -3
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/security/factory.py +12 -12
- {devloop-0.8.0 → devloop-0.9.0}/LICENSE +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/README.md +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/__init__.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/agents/__init__.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/agents/ci_monitor.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/agents/code_rabbit.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/agents/doc_lifecycle.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/agents/echo.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/agents/file_logger.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/agents/performance_profiler.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/agents/sandbox_helper.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/agents/security_scanner.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/agents/snyk.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/agents/type_checker.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/cli/__init__.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/cli/agent_rules.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/cli/coderabbit_installer.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/cli/commands/__init__.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/cli/commands/agent_publish.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/cli/commands/audit.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/cli/commands/custom_agents.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/cli/commands/feedback.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/cli/commands/insights.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/cli/commands/marketplace_server.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/cli/commands/release.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/cli/commands/summary.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/cli/commands/telemetry.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/cli/commands/tools.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/cli/main_v1.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/cli/pre_push_check.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/cli/prerequisites.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/cli/pyodide_installer.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/cli/snyk_installer.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/cli/templates/.devloop/tools-registry.json +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/cli/templates/claude_commands/README.md +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/cli/templates/claude_commands/agent-summary.md +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/cli/templates/claude_commands/devloop-findings.md +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/cli/templates/claude_commands/devloop-status.md +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/cli/templates/claude_commands/extract-findings.md +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/cli/templates/claude_commands/verify-work.md +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/cli/templates/devloop_agents_template.md +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/cli/templates/git_hooks/post-commit +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/cli/templates/git_hooks/pre-commit +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/cli/templates/git_hooks/pre-commit-checks +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/cli/templates/git_hooks/pre-push +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/cli/templates/supervisor/devloop.conf +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/cli/templates/systemd/devloop.service +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/collectors/__init__.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/collectors/base.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/collectors/filesystem.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/collectors/git.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/collectors/manager.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/collectors/process.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/collectors/system.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/core/__init__.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/core/action_logger.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/core/agent.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/core/agent_audit_logger.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/core/amp_integration.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/core/amp_thread_mapper.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/core/auto_fix.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/core/config_schema.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/core/context.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/core/contextual_feedback.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/core/daemon_health.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/core/debug_trace.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/core/error_handler.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/core/error_notifier.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/core/event.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/core/event_replayer.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/core/feedback.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/core/file_lock_manager.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/core/operational_health.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/core/pattern_analyzer.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/core/pattern_detector.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/core/performance.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/core/proactive_feedback.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/core/summary_formatter.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/core/summary_generator.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/core/telemetry.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/core/tool_dependencies.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/core/tool_registry.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/core/tool_runner.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/core/transactional_io.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/integrations/__init__.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/integrations/beads_integration.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/lsp/__init__.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/lsp/__main__.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/lsp/mapper.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/lsp/server.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/marketplace/__init__.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/marketplace/api.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/marketplace/http_server.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/marketplace/metadata.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/marketplace/publisher.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/metrics/__init__.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/metrics/dora.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/metrics/value_metrics.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/providers/__init__.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/providers/ci_provider.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/providers/circleci_provider.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/providers/github_actions_provider.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/providers/gitlab_ci_provider.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/providers/pypi_registry.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/providers/registry_provider.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/release/__init__.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/release/release_manager.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/security/__init__.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/security/audit_logger.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/security/bubblewrap_sandbox.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/security/cgroups_helper.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/security/no_sandbox.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/security/package.json +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/security/path_validator.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/security/pyodide_runner.js +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/security/pyodide_sandbox.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/security/sandbox.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/security/token_manager.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/telemetry/__init__.py +0 -0
- {devloop-0.8.0 → devloop-0.9.0}/src/devloop/telemetry/telemetry_manager.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: devloop
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.9.0
|
|
4
4
|
Summary: Intelligent background agents for development workflow automation
|
|
5
5
|
License: MIT
|
|
6
6
|
License-File: LICENSE
|
|
@@ -28,6 +28,7 @@ Provides-Extra: marketplace-api
|
|
|
28
28
|
Provides-Extra: snyk
|
|
29
29
|
Requires-Dist: aiofiles (>=23.2,<24.0)
|
|
30
30
|
Requires-Dist: lsprotocol (>=2023.0.0,<2024.0.0)
|
|
31
|
+
Requires-Dist: mcp (>=1.0.0,<2.0.0)
|
|
31
32
|
Requires-Dist: psutil (>=5.9,<6.0)
|
|
32
33
|
Requires-Dist: pydantic (>=2.5,<3.0)
|
|
33
34
|
Requires-Dist: pygls (>=1.3.0,<2.0.0)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "devloop"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.9.0"
|
|
4
4
|
description = "Intelligent background agents for development workflow automation"
|
|
5
5
|
authors = ["DevLoop Contributors <devloop@example.com>"]
|
|
6
6
|
license = "MIT"
|
|
@@ -49,12 +49,13 @@ aiofiles = "^23.2"
|
|
|
49
49
|
psutil = "^5.9"
|
|
50
50
|
pygls = "^1.3.0"
|
|
51
51
|
lsprotocol = "^2023.0.0"
|
|
52
|
+
mcp = "^1.0.0"
|
|
52
53
|
|
|
53
54
|
[tool.poetry.group.dev.dependencies]
|
|
54
55
|
pytest = "^7.4"
|
|
55
56
|
pytest-asyncio = "^0.21"
|
|
56
57
|
pytest-cov = "^4.1"
|
|
57
|
-
black = "
|
|
58
|
+
black = ">=24.3.0"
|
|
58
59
|
ruff = "^0.1"
|
|
59
60
|
mypy = "^1.8"
|
|
60
61
|
bandit = "^1.7"
|
|
@@ -96,7 +97,7 @@ markers = [
|
|
|
96
97
|
"security: marks tests as security tests",
|
|
97
98
|
"benchmark: marks tests as benchmarks",
|
|
98
99
|
]
|
|
99
|
-
addopts = "--cov=src/devloop --cov-report=term-missing --cov-report=html --cov-report=xml"
|
|
100
|
+
addopts = "--import-mode=importlib --cov=src/devloop --cov-report=term-missing --cov-report=html --cov-report=xml"
|
|
100
101
|
|
|
101
102
|
[tool.coverage.run]
|
|
102
103
|
source = ["src/devloop"]
|
|
@@ -123,7 +124,7 @@ exclude_lines = [
|
|
|
123
124
|
|
|
124
125
|
[tool.mypy]
|
|
125
126
|
python_version = "3.11"
|
|
126
|
-
warn_return_any =
|
|
127
|
+
warn_return_any = true
|
|
127
128
|
warn_unused_configs = true
|
|
128
129
|
disallow_untyped_defs = false
|
|
129
130
|
disallow_incomplete_defs = false
|
|
@@ -46,9 +46,9 @@ class FormatterAgent(Agent):
|
|
|
46
46
|
self.config = FormatterConfig(config or {})
|
|
47
47
|
|
|
48
48
|
# Loop prevention mechanisms
|
|
49
|
-
self._recent_formats: Dict[
|
|
50
|
-
|
|
51
|
-
|
|
49
|
+
self._recent_formats: Dict[str, List[float]] = (
|
|
50
|
+
{}
|
|
51
|
+
) # file_path -> list of timestamps
|
|
52
52
|
self._format_timeout = 30 # seconds
|
|
53
53
|
self._loop_detection_window = 10 # seconds
|
|
54
54
|
self._max_consecutive_formats = 3 # per file per window
|
|
@@ -246,7 +246,8 @@ class FormatterAgent(Agent):
|
|
|
246
246
|
|
|
247
247
|
language = extension_map.get(suffix)
|
|
248
248
|
if language:
|
|
249
|
-
|
|
249
|
+
formatter = self.config.formatters.get(language)
|
|
250
|
+
return str(formatter) if formatter else None
|
|
250
251
|
|
|
251
252
|
return None
|
|
252
253
|
|
|
@@ -318,17 +318,19 @@ class GitCommitAssistantAgent(Agent):
|
|
|
318
318
|
if not self.config.auto_generate_scope:
|
|
319
319
|
return ""
|
|
320
320
|
|
|
321
|
-
modules = analysis.get("affected_modules", [])
|
|
321
|
+
modules: list[str] = analysis.get("affected_modules", [])
|
|
322
322
|
if len(modules) == 1:
|
|
323
|
-
return modules[0].lower().replace(" ", "-")
|
|
323
|
+
return str(modules[0]).lower().replace(" ", "-")
|
|
324
324
|
elif len(modules) > 1:
|
|
325
325
|
# Find common prefix
|
|
326
326
|
common = ""
|
|
327
|
-
|
|
327
|
+
first_module = str(modules[0])
|
|
328
|
+
for i, char in enumerate(first_module):
|
|
328
329
|
if all(
|
|
329
|
-
module.startswith(
|
|
330
|
+
str(module).startswith(first_module[: i + 1])
|
|
331
|
+
for module in modules[1:]
|
|
330
332
|
):
|
|
331
|
-
common =
|
|
333
|
+
common = first_module[: i + 1]
|
|
332
334
|
else:
|
|
333
335
|
break
|
|
334
336
|
return common.lower().replace(" ", "-") if common else ""
|
|
@@ -327,7 +327,8 @@ class TestRunnerAgent(Agent):
|
|
|
327
327
|
|
|
328
328
|
language = extension_map.get(suffix)
|
|
329
329
|
if language:
|
|
330
|
-
|
|
330
|
+
framework = self.config.test_frameworks.get(language)
|
|
331
|
+
return str(framework) if framework else None
|
|
331
332
|
|
|
332
333
|
return None
|
|
333
334
|
|
|
@@ -14,7 +14,6 @@ from devloop.marketplace import RegistryClient, RegistryConfig
|
|
|
14
14
|
from devloop.marketplace.installer import AgentInstaller
|
|
15
15
|
from devloop.marketplace.registry import AgentRegistry
|
|
16
16
|
|
|
17
|
-
|
|
18
17
|
logger = logging.getLogger(__name__)
|
|
19
18
|
app = typer.Typer(help="Agent marketplace commands", add_completion=False)
|
|
20
19
|
console = Console()
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
"""MCP server CLI command.
|
|
2
|
+
|
|
3
|
+
This module provides the CLI entry point for the DevLoop MCP server,
|
|
4
|
+
enabling integration with Claude Code and other MCP-compatible clients.
|
|
5
|
+
|
|
6
|
+
Usage:
|
|
7
|
+
devloop mcp-server # Start server (stdio mode)
|
|
8
|
+
devloop mcp-server --check # Validate server can start
|
|
9
|
+
devloop mcp-server --install # Register in Claude Code settings
|
|
10
|
+
devloop mcp-server --uninstall # Remove from Claude Code settings
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import asyncio
|
|
14
|
+
import json
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
|
|
17
|
+
import typer
|
|
18
|
+
from rich.console import Console
|
|
19
|
+
|
|
20
|
+
app = typer.Typer(name="mcp-server", help="DevLoop MCP server for Claude Code")
|
|
21
|
+
console = Console()
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def get_claude_settings_path() -> Path:
|
|
25
|
+
"""Get the path to Claude Code settings file.
|
|
26
|
+
|
|
27
|
+
Returns:
|
|
28
|
+
Path to ~/.claude/settings.json
|
|
29
|
+
"""
|
|
30
|
+
return Path.home() / ".claude" / "settings.json"
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def install_mcp_server() -> bool:
|
|
34
|
+
"""Install DevLoop MCP server in Claude Code settings.
|
|
35
|
+
|
|
36
|
+
Creates or updates ~/.claude/settings.json to register the devloop
|
|
37
|
+
MCP server.
|
|
38
|
+
|
|
39
|
+
Returns:
|
|
40
|
+
True if installation succeeded, False otherwise.
|
|
41
|
+
"""
|
|
42
|
+
settings_path = get_claude_settings_path()
|
|
43
|
+
|
|
44
|
+
# Create .claude directory if needed
|
|
45
|
+
settings_path.parent.mkdir(parents=True, exist_ok=True)
|
|
46
|
+
|
|
47
|
+
# Load existing settings or create new
|
|
48
|
+
settings = {}
|
|
49
|
+
if settings_path.exists():
|
|
50
|
+
try:
|
|
51
|
+
settings = json.loads(settings_path.read_text())
|
|
52
|
+
except json.JSONDecodeError:
|
|
53
|
+
settings = {}
|
|
54
|
+
|
|
55
|
+
# Ensure mcpServers key exists
|
|
56
|
+
if "mcpServers" not in settings:
|
|
57
|
+
settings["mcpServers"] = {}
|
|
58
|
+
|
|
59
|
+
# Add devloop server configuration
|
|
60
|
+
settings["mcpServers"]["devloop"] = {
|
|
61
|
+
"command": "devloop",
|
|
62
|
+
"args": ["mcp-server"],
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
# Write settings back
|
|
66
|
+
settings_path.write_text(json.dumps(settings, indent=2) + "\n")
|
|
67
|
+
|
|
68
|
+
return True
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def uninstall_mcp_server() -> bool:
|
|
72
|
+
"""Remove DevLoop MCP server from Claude Code settings.
|
|
73
|
+
|
|
74
|
+
Updates ~/.claude/settings.json to remove the devloop MCP server.
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
True if uninstallation succeeded, False otherwise.
|
|
78
|
+
"""
|
|
79
|
+
settings_path = get_claude_settings_path()
|
|
80
|
+
|
|
81
|
+
if not settings_path.exists():
|
|
82
|
+
return True
|
|
83
|
+
|
|
84
|
+
try:
|
|
85
|
+
settings = json.loads(settings_path.read_text())
|
|
86
|
+
except json.JSONDecodeError:
|
|
87
|
+
return True
|
|
88
|
+
|
|
89
|
+
# Remove devloop from mcpServers if present
|
|
90
|
+
if "mcpServers" in settings and "devloop" in settings["mcpServers"]:
|
|
91
|
+
del settings["mcpServers"]["devloop"]
|
|
92
|
+
|
|
93
|
+
# Write settings back
|
|
94
|
+
settings_path.write_text(json.dumps(settings, indent=2) + "\n")
|
|
95
|
+
|
|
96
|
+
return True
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
@app.callback(invoke_without_command=True)
|
|
100
|
+
def main(
|
|
101
|
+
ctx: typer.Context,
|
|
102
|
+
check: bool = typer.Option(
|
|
103
|
+
False, "--check", help="Validate server can start correctly"
|
|
104
|
+
),
|
|
105
|
+
install: bool = typer.Option(
|
|
106
|
+
False, "--install", help="Register in Claude Code settings"
|
|
107
|
+
),
|
|
108
|
+
uninstall: bool = typer.Option(
|
|
109
|
+
False, "--uninstall", help="Remove from Claude Code settings"
|
|
110
|
+
),
|
|
111
|
+
) -> None:
|
|
112
|
+
"""DevLoop MCP server for Claude Code integration.
|
|
113
|
+
|
|
114
|
+
By default, starts the MCP server in stdio mode for Claude Code communication.
|
|
115
|
+
|
|
116
|
+
Use --install to register the server in Claude Code settings, and
|
|
117
|
+
--uninstall to remove it.
|
|
118
|
+
"""
|
|
119
|
+
# Import here to avoid circular imports and for lazy loading
|
|
120
|
+
from devloop.mcp.server import MCPServer
|
|
121
|
+
|
|
122
|
+
# Check for mutually exclusive options
|
|
123
|
+
options_set = sum([check, install, uninstall])
|
|
124
|
+
if options_set > 1:
|
|
125
|
+
console.print(
|
|
126
|
+
"[red]Error:[/red] Only one of --check, --install, or --uninstall can be specified"
|
|
127
|
+
)
|
|
128
|
+
raise typer.Exit(1)
|
|
129
|
+
|
|
130
|
+
if check:
|
|
131
|
+
# Validate server can start
|
|
132
|
+
try:
|
|
133
|
+
server = MCPServer()
|
|
134
|
+
console.print("[green]Server validated successfully.[/green]")
|
|
135
|
+
console.print(f"Project root: {server.project_root}")
|
|
136
|
+
except FileNotFoundError as e:
|
|
137
|
+
console.print(f"[red]Error:[/red] {e}")
|
|
138
|
+
raise typer.Exit(1)
|
|
139
|
+
except Exception as e:
|
|
140
|
+
console.print(f"[red]Error validating server:[/red] {e}")
|
|
141
|
+
raise typer.Exit(1)
|
|
142
|
+
|
|
143
|
+
elif install:
|
|
144
|
+
# Install MCP server in Claude Code settings
|
|
145
|
+
try:
|
|
146
|
+
if install_mcp_server():
|
|
147
|
+
settings_path = get_claude_settings_path()
|
|
148
|
+
console.print(
|
|
149
|
+
"[green]DevLoop MCP server installed successfully.[/green]"
|
|
150
|
+
)
|
|
151
|
+
console.print(f"Settings updated: {settings_path}")
|
|
152
|
+
console.print("\nRestart Claude Code to activate the MCP server.")
|
|
153
|
+
else:
|
|
154
|
+
console.print("[red]Failed to install MCP server.[/red]")
|
|
155
|
+
raise typer.Exit(1)
|
|
156
|
+
except Exception as e:
|
|
157
|
+
console.print(f"[red]Error installing MCP server:[/red] {e}")
|
|
158
|
+
raise typer.Exit(1)
|
|
159
|
+
|
|
160
|
+
elif uninstall:
|
|
161
|
+
# Remove MCP server from Claude Code settings
|
|
162
|
+
try:
|
|
163
|
+
if uninstall_mcp_server():
|
|
164
|
+
console.print(
|
|
165
|
+
"[green]DevLoop MCP server uninstalled successfully.[/green]"
|
|
166
|
+
)
|
|
167
|
+
else:
|
|
168
|
+
console.print("[red]Failed to uninstall MCP server.[/red]")
|
|
169
|
+
raise typer.Exit(1)
|
|
170
|
+
except Exception as e:
|
|
171
|
+
console.print(f"[red]Error uninstalling MCP server:[/red] {e}")
|
|
172
|
+
raise typer.Exit(1)
|
|
173
|
+
|
|
174
|
+
else:
|
|
175
|
+
# Start server in stdio mode (default behavior)
|
|
176
|
+
try:
|
|
177
|
+
server = MCPServer()
|
|
178
|
+
asyncio.run(server.run())
|
|
179
|
+
except FileNotFoundError as e:
|
|
180
|
+
console.print(f"[red]Error:[/red] {e}")
|
|
181
|
+
raise typer.Exit(1)
|
|
182
|
+
except KeyboardInterrupt:
|
|
183
|
+
pass
|
|
184
|
+
except Exception as e:
|
|
185
|
+
console.print(f"[red]Server error:[/red] {e}")
|
|
186
|
+
raise typer.Exit(1)
|
|
@@ -173,15 +173,15 @@ def _calculate_ci_metrics(events: list[dict]) -> dict[str, any]:
|
|
|
173
173
|
"pre_commit_passed": pre_commit_passed,
|
|
174
174
|
"pre_commit_failed": pre_commit_failed,
|
|
175
175
|
"pre_commit_total": total_commits,
|
|
176
|
-
"pre_commit_pass_rate": (
|
|
177
|
-
|
|
178
|
-
|
|
176
|
+
"pre_commit_pass_rate": (
|
|
177
|
+
(pre_commit_passed / total_commits * 100) if total_commits > 0 else 0
|
|
178
|
+
),
|
|
179
179
|
"pre_push_passed": pre_push_passed,
|
|
180
180
|
"pre_push_failed": pre_push_failed,
|
|
181
181
|
"pre_push_total": total_pushes,
|
|
182
|
-
"pre_push_pass_rate": (
|
|
183
|
-
|
|
184
|
-
|
|
182
|
+
"pre_push_pass_rate": (
|
|
183
|
+
(pre_push_passed / total_pushes * 100) if total_pushes > 0 else 0
|
|
184
|
+
),
|
|
185
185
|
}
|
|
186
186
|
|
|
187
187
|
|