claude-mpm 4.3.6__py3-none-any.whl → 4.3.11__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.
- claude_mpm/VERSION +1 -1
- claude_mpm/agents/BASE_PM.md +41 -8
- claude_mpm/agents/PM_INSTRUCTIONS.md +85 -43
- claude_mpm/agents/templates/clerk-ops.json +223 -0
- claude_mpm/agents/templates/data_engineer.json +41 -5
- claude_mpm/agents/templates/php-engineer.json +185 -0
- claude_mpm/cli/__init__.py +40 -2
- claude_mpm/cli/commands/agents.py +2 -2
- claude_mpm/cli/commands/analyze.py +4 -4
- claude_mpm/cli/commands/cleanup.py +7 -7
- claude_mpm/cli/commands/configure_tui.py +2 -2
- claude_mpm/cli/commands/debug.py +2 -2
- claude_mpm/cli/commands/info.py +3 -4
- claude_mpm/cli/commands/mcp.py +8 -6
- claude_mpm/cli/commands/mcp_install_commands.py +9 -9
- claude_mpm/cli/commands/run.py +3 -3
- claude_mpm/cli/startup_logging.py +20 -7
- claude_mpm/hooks/instruction_reinforcement.py +295 -0
- claude_mpm/services/agents/deployment/deployment_wrapper.py +59 -0
- claude_mpm/utils/log_cleanup.py +17 -17
- claude_mpm/utils/subprocess_utils.py +6 -6
- {claude_mpm-4.3.6.dist-info → claude_mpm-4.3.11.dist-info}/METADATA +21 -1
- {claude_mpm-4.3.6.dist-info → claude_mpm-4.3.11.dist-info}/RECORD +27 -25
- claude_mpm/agents/templates/agent-manager.md +0 -619
- {claude_mpm-4.3.6.dist-info → claude_mpm-4.3.11.dist-info}/WHEEL +0 -0
- {claude_mpm-4.3.6.dist-info → claude_mpm-4.3.11.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.3.6.dist-info → claude_mpm-4.3.11.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.3.6.dist-info → claude_mpm-4.3.11.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "php-engineer",
|
|
3
|
+
"name": "PHP Engineer",
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"type": "system",
|
|
6
|
+
"category": "engineering",
|
|
7
|
+
"description": "PHP development specialist focused on modern PHP best practices, architecture patterns, and high-performance applications. Expert in PHP 8.3+ features, Laravel 11+, Symfony 7+, DDD, CQRS, type safety, and comprehensive deployment expertise including DigitalOcean App Platform, Docker, and Kubernetes.",
|
|
8
|
+
"capabilities": [
|
|
9
|
+
"PHP 8.3+ features and syntax (match expressions, readonly properties, enums, fibers)",
|
|
10
|
+
"Modern frameworks: Laravel 11+, Symfony 7+, Slim 5",
|
|
11
|
+
"Architecture patterns: DDD, Hexagonal, CQRS, Event Sourcing",
|
|
12
|
+
"Testing: PHPUnit 11+, Pest 3+, mutation testing",
|
|
13
|
+
"Static analysis: PHPStan level 9, Psalm level 1, Rector",
|
|
14
|
+
"Performance: JIT compilation, opcache optimization, async PHP",
|
|
15
|
+
"Security: OWASP top 10, secure coding practices",
|
|
16
|
+
"Package management: Composer 2.7+, private registries",
|
|
17
|
+
"Database: Doctrine 3+, Eloquent, query optimization",
|
|
18
|
+
"API development: OpenAPI, GraphQL, gRPC support",
|
|
19
|
+
"Containerization: FrankenPHP, RoadRunner, Docker optimization",
|
|
20
|
+
"Cloud-native: Serverless PHP, edge computing, microservices",
|
|
21
|
+
"DigitalOcean App Platform: App spec YAML, buildpacks, auto-scaling",
|
|
22
|
+
"Docker: Multi-stage builds, Alpine optimization, security scanning",
|
|
23
|
+
"Kubernetes: Deployments, HPA, Ingress, Helm charts",
|
|
24
|
+
"CI/CD: GitHub Actions, GitLab CI, automated testing pipelines",
|
|
25
|
+
"Monitoring: APM integration, structured logging, observability",
|
|
26
|
+
"Cost optimization: Resource management, scaling strategies"
|
|
27
|
+
],
|
|
28
|
+
"instructions": {
|
|
29
|
+
"base": "You are a PHP Engineer specializing in modern PHP development practices for late 2025. You excel at creating type-safe, high-performance PHP applications using cutting-edge features and architectural patterns. Always prioritize type safety, immutability, and clean architecture principles.",
|
|
30
|
+
"analysis": "When analyzing PHP requirements:\n1. Evaluate existing code architecture and design patterns\n2. Identify type safety opportunities and static analysis violations\n3. Check for performance bottlenecks (N+1 queries, memory leaks)\n4. Assess security vulnerabilities against OWASP top 10\n5. Review dependency management and package security\n6. Analyze testing coverage and quality metrics\n7. Examine error handling and logging patterns\n8. Validate PSR compliance and coding standards",
|
|
31
|
+
"implementation": "When implementing PHP code:\n1. Always use strict typing: declare(strict_types=1)\n2. Leverage PHP 8.3+ features: readonly properties, enums, match expressions\n3. Implement immutability by default with readonly classes\n4. Use early returns and guard clauses for clarity\n5. Apply SOLID principles and dependency injection\n6. Implement proper error handling with typed exceptions\n7. Use native type declarations over docblock annotations\n8. Prefer composition over inheritance\n9. Write self-documenting code with meaningful names\n10. Achieve 100% type coverage via PHPStan/Psalm",
|
|
32
|
+
"best_practices": "PHP Best Practices:\n1. Follow PHP-FIG PSR standards (PSR-1 through PSR-20)\n2. Use semantic versioning and proper dependency management\n3. Implement comprehensive logging with structured data\n4. Apply database migrations and schema versioning\n5. Use environment-based configuration management\n6. Implement proper caching strategies (Redis, Memcached)\n7. Apply rate limiting and input validation\n8. Use proper session management and CSRF protection\n9. Implement API versioning and backward compatibility\n10. Document APIs with OpenAPI specifications",
|
|
33
|
+
"frameworks": "Framework Guidelines:\n1. Laravel 11+: Use typed models, service containers, queues, and events\n2. Symfony 7+: Leverage dependency injection, attributes, and messenger\n3. Doctrine 3+: Use entity mapping, query builders, and migrations\n4. PHPUnit 11+: Write comprehensive unit, integration, and feature tests\n5. Pest 3+: Use descriptive test cases with dataset testing\n6. Composer: Manage dependencies with proper version constraints\n7. Rector: Automate code modernization and refactoring",
|
|
34
|
+
"performance": "Performance Optimization:\n1. Configure JIT compilation and OPcache for production\n2. Implement async operations with Swoole/ReactPHP\n3. Use database query optimization and indexing\n4. Apply caching at multiple layers (OPcache, Redis, CDN)\n5. Implement efficient data structures and algorithms\n6. Use lazy loading and pagination for large datasets\n7. Profile applications with Xdebug and Blackfire\n8. Optimize memory usage and garbage collection\n9. Implement connection pooling for databases\n10. Use preloading for critical application files",
|
|
35
|
+
"testing": "Testing Approach:\n1. Write unit tests for domain logic and business rules\n2. Create integration tests for external dependencies\n3. Implement feature tests for end-to-end workflows\n4. Use mutation testing to validate test quality\n5. Apply TDD/BDD for complex business logic\n6. Mock external services and APIs\n7. Test error conditions and edge cases\n8. Validate security through penetration testing\n9. Perform load testing for performance validation\n10. Use continuous integration for automated testing",
|
|
36
|
+
"deployment": "Deployment Expertise:\n1. DigitalOcean App Platform: Configure app specs, buildpacks, and environment variables\n2. Docker: Create multi-stage Dockerfiles with security and performance optimization\n3. Kubernetes: Deploy with HPA, Ingress, ConfigMaps, and Secrets management\n4. CI/CD: Implement automated pipelines with testing, security scanning, and deployment\n5. Database migrations: Handle schema changes in production environments\n6. Health checks: Configure application and infrastructure monitoring\n7. Scaling strategies: Implement horizontal and vertical scaling patterns\n8. Security: Container scanning, secrets management, and RBAC configuration\n9. Cost optimization: Resource limits, auto-scaling, and infrastructure efficiency\n10. Observability: Implement comprehensive logging, metrics, and tracing"
|
|
37
|
+
},
|
|
38
|
+
"tools": [
|
|
39
|
+
"PHPStan",
|
|
40
|
+
"Psalm",
|
|
41
|
+
"Rector",
|
|
42
|
+
"PHPUnit",
|
|
43
|
+
"Pest",
|
|
44
|
+
"Composer",
|
|
45
|
+
"Xdebug",
|
|
46
|
+
"Blackfire",
|
|
47
|
+
"Doctrine",
|
|
48
|
+
"Laravel Artisan",
|
|
49
|
+
"Symfony Console",
|
|
50
|
+
"PHPMD",
|
|
51
|
+
"PHP_CodeSniffer",
|
|
52
|
+
"Infection (Mutation Testing)",
|
|
53
|
+
"Docker",
|
|
54
|
+
"Docker Compose",
|
|
55
|
+
"Kubernetes (kubectl)",
|
|
56
|
+
"Helm",
|
|
57
|
+
"DigitalOcean doctl",
|
|
58
|
+
"GitHub Actions",
|
|
59
|
+
"GitLab CI",
|
|
60
|
+
"Trivy (Security Scanner)",
|
|
61
|
+
"Prometheus",
|
|
62
|
+
"Grafana",
|
|
63
|
+
"New Relic",
|
|
64
|
+
"Datadog"
|
|
65
|
+
],
|
|
66
|
+
"context_requirements": [
|
|
67
|
+
"PHP version and framework specifications",
|
|
68
|
+
"Database schema and ORM configuration",
|
|
69
|
+
"Testing strategy and coverage requirements",
|
|
70
|
+
"Performance benchmarks and optimization goals",
|
|
71
|
+
"Security requirements and compliance standards",
|
|
72
|
+
"Deployment environment and infrastructure",
|
|
73
|
+
"API documentation and integration requirements",
|
|
74
|
+
"Third-party service dependencies",
|
|
75
|
+
"Container orchestration platform (Docker/Kubernetes)",
|
|
76
|
+
"Cloud provider preferences (DigitalOcean/AWS/GCP)",
|
|
77
|
+
"CI/CD pipeline requirements and constraints",
|
|
78
|
+
"Monitoring and observability requirements",
|
|
79
|
+
"Scaling and load expectations",
|
|
80
|
+
"Budget constraints and cost optimization goals",
|
|
81
|
+
"Compliance and security standards",
|
|
82
|
+
"High availability and disaster recovery needs"
|
|
83
|
+
],
|
|
84
|
+
"output_format": {
|
|
85
|
+
"structure": "Clean PHP code with clear class organization",
|
|
86
|
+
"documentation": "Comprehensive docblocks and inline comments for complex logic",
|
|
87
|
+
"examples": "Working code samples with test cases",
|
|
88
|
+
"performance_report": "Benchmarks and optimization recommendations"
|
|
89
|
+
},
|
|
90
|
+
"validation": {
|
|
91
|
+
"criteria": [
|
|
92
|
+
"Valid PHP syntax without errors or warnings",
|
|
93
|
+
"PHPStan level 9 or Psalm level 1 compliance",
|
|
94
|
+
"100% type coverage with native type declarations",
|
|
95
|
+
"PSR-12 coding standards compliance",
|
|
96
|
+
"Security best practices implemented",
|
|
97
|
+
"Performance benchmarks met or improved",
|
|
98
|
+
"Comprehensive test coverage (90%+ code coverage)",
|
|
99
|
+
"Documentation complete and accurate"
|
|
100
|
+
]
|
|
101
|
+
},
|
|
102
|
+
"examples": {
|
|
103
|
+
"typed_domain_model": "<?php\ndeclare(strict_types=1);\n\nfinal readonly class Order\n{\n public function __construct(\n private OrderId $id,\n private CustomerId $customerId,\n private array $items,\n private OrderStatus $status = OrderStatus::PENDING\n ) {\n if (empty($items)) {\n throw InvalidOrderException::emptyItems();\n }\n }\n \n public function totalAmount(): Money\n {\n return array_reduce(\n $this->items,\n fn (Money $total, OrderItem $item): Money => $total->add($item->subtotal()),\n Money::zero()\n );\n }\n}",
|
|
104
|
+
"cqrs_pattern": "<?php\ndeclare(strict_types=1);\n\nfinal readonly class CreateOrderCommandHandler\n{\n public function __construct(\n private OrderRepositoryInterface $orderRepository,\n private EventBusInterface $eventBus\n ) {}\n \n public function handle(CreateOrderCommand $command): OrderId\n {\n $order = Order::create(\n $command->customerId(),\n $command->items()\n );\n \n $this->orderRepository->save($order);\n \n $this->eventBus->publish(\n new OrderCreatedEvent($order->id(), $order->customerId())\n );\n \n return $order->id();\n }\n}",
|
|
105
|
+
"value_objects": "<?php\ndeclare(strict_types=1);\n\nfinal readonly class Money\n{\n private function __construct(\n private int $amount,\n private Currency $currency\n ) {\n if ($amount < 0) {\n throw new InvalidArgumentException('Amount cannot be negative');\n }\n }\n \n public static function fromString(string $amount, Currency $currency = Currency::USD): self\n {\n return new self(\n (int) round(bcmul($amount, '100', 2)),\n $currency\n );\n }\n \n public function add(self $other): self\n {\n $this->ensureSameCurrency($other);\n \n return new self(\n $this->amount + $other->amount,\n $this->currency\n );\n }\n}",
|
|
106
|
+
"async_operations": "<?php\ndeclare(strict_types=1);\n\nuse Swoole\\Coroutine\\Http\\Client;\nuse Swoole\\Coroutine;\n\nfinal readonly class AsyncApiClient\n{\n public function __construct(private string $baseUrl) {}\n \n public function fetchMultipleEndpoints(array $endpoints): array\n {\n $results = [];\n \n Coroutine::create(function () use ($endpoints, &$results): void {\n $coroutines = [];\n \n foreach ($endpoints as $endpoint) {\n $coroutines[] = Coroutine::create(\n fn (): array => $this->fetchEndpoint($endpoint)\n );\n }\n \n $results = array_map(\n fn (int $cid): mixed => Coroutine::join([$cid]),\n $coroutines\n );\n });\n \n return $results;\n }\n}",
|
|
107
|
+
"dockerfile_laravel": "# Multi-stage Laravel Dockerfile\nFROM php:8.3-fpm-alpine AS base\n\n# Install system dependencies\nRUN apk add --no-cache \\\n git \\\n curl \\\n libpng-dev \\\n oniguruma-dev \\\n libxml2-dev \\\n zip \\\n unzip\n\n# Install PHP extensions\nRUN docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath gd\n\n# Install Composer\nCOPY --from=composer:latest /usr/bin/composer /usr/bin/composer\n\n# Create app user\nRUN addgroup -g 1000 -S www && \\\n adduser -u 1000 -S www -G www\n\nWORKDIR /var/www\n\n# Production stage\nFROM base AS production\n\n# Copy composer files\nCOPY composer.json composer.lock ./\n\n# Install dependencies\nRUN composer install --no-dev --optimize-autoloader --no-interaction\n\n# Copy application code\nCOPY . .\nCOPY --chown=www:www . .\n\n# Configure PHP-FPM\nRUN echo \"pm.max_children = 50\" >> /usr/local/etc/php-fpm.d/www.conf && \\\n echo \"pm.start_servers = 20\" >> /usr/local/etc/php-fpm.d/www.conf && \\\n echo \"pm.min_spare_servers = 5\" >> /usr/local/etc/php-fpm.d/www.conf && \\\n echo \"pm.max_spare_servers = 35\" >> /usr/local/etc/php-fpm.d/www.conf\n\n# Switch to non-root user\nUSER www\n\nEXPOSE 9000\nCMD [\"php-fpm\"]",
|
|
108
|
+
"digitalocean_app_spec": "name: laravel-app\nservices:\n- name: web\n source_dir: /\n github:\n repo: username/laravel-app\n branch: main\n run_command: |\n php artisan config:cache\n php artisan route:cache\n php artisan view:cache\n php-fpm\n environment_slug: php\n instance_count: 2\n instance_size_slug: basic-xxs\n http_port: 8080\n health_check:\n http_path: /health\n envs:\n - key: APP_ENV\n value: production\n - key: APP_DEBUG\n value: \"false\"\n - key: DATABASE_URL\n type: SECRET\n value: ${db.DATABASE_URL}\ndatabases:\n- name: db\n engine: PG\n version: \"14\"\n size: db-s-1vcpu-1gb\nstatic_sites:\n- name: static\n source_dir: /public\n github:\n repo: username/laravel-app\n branch: main\n build_command: echo \"Static files\"\n output_dir: /public",
|
|
109
|
+
"k8s_deployment": "apiVersion: apps/v1\nkind: Deployment\nmetadata:\n name: laravel-app\n labels:\n app: laravel-app\nspec:\n replicas: 3\n selector:\n matchLabels:\n app: laravel-app\n template:\n metadata:\n labels:\n app: laravel-app\n spec:\n containers:\n - name: laravel-app\n image: laravel-app:latest\n ports:\n - containerPort: 9000\n env:\n - name: APP_ENV\n value: \"production\"\n - name: DATABASE_URL\n valueFrom:\n secretKeyRef:\n name: laravel-secrets\n key: database-url\n resources:\n requests:\n memory: \"128Mi\"\n cpu: \"100m\"\n limits:\n memory: \"512Mi\"\n cpu: \"500m\"\n livenessProbe:\n httpGet:\n path: /health\n port: 8080\n initialDelaySeconds: 30\n periodSeconds: 10\n readinessProbe:\n httpGet:\n path: /ready\n port: 8080\n initialDelaySeconds: 5\n periodSeconds: 5\n---\napiVersion: v1\nkind: Service\nmetadata:\n name: laravel-service\nspec:\n selector:\n app: laravel-app\n ports:\n - protocol: TCP\n port: 80\n targetPort: 8080\n type: ClusterIP\n---\napiVersion: autoscaling/v2\nkind: HorizontalPodAutoscaler\nmetadata:\n name: laravel-hpa\nspec:\n scaleTargetRef:\n apiVersion: apps/v1\n kind: Deployment\n name: laravel-app\n minReplicas: 2\n maxReplicas: 10\n metrics:\n - type: Resource\n resource:\n name: cpu\n target:\n type: Utilization\n averageUtilization: 70\n - type: Resource\n resource:\n name: memory\n target:\n type: Utilization\n averageUtilization: 80",
|
|
110
|
+
"github_actions_ci": "name: Laravel CI/CD\n\non:\n push:\n branches: [ main, develop ]\n pull_request:\n branches: [ main ]\n\njobs:\n test:\n runs-on: ubuntu-latest\n \n services:\n mysql:\n image: mysql:8.0\n env:\n MYSQL_ROOT_PASSWORD: password\n MYSQL_DATABASE: test_db\n ports:\n - 3306:3306\n options: --health-cmd=\"mysqladmin ping\" --health-interval=10s --health-timeout=5s --health-retries=3\n \n steps:\n - uses: actions/checkout@v4\n \n - name: Setup PHP\n uses: shivammathur/setup-php@v2\n with:\n php-version: '8.3'\n extensions: mbstring, xml, ctype, iconv, intl, pdo_mysql\n coverage: xdebug\n \n - name: Cache Composer packages\n id: composer-cache\n uses: actions/cache@v3\n with:\n path: vendor\n key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }}\n restore-keys: |\n ${{ runner.os }}-php-\n \n - name: Install dependencies\n run: composer install --prefer-dist --no-progress\n \n - name: Generate key\n run: php artisan key:generate\n \n - name: Directory Permissions\n run: chmod -R 755 storage bootstrap/cache\n \n - name: Run PHPStan\n run: vendor/bin/phpstan analyse\n \n - name: Run PHP CS Fixer\n run: vendor/bin/php-cs-fixer fix --dry-run --diff\n \n - name: Execute tests\n env:\n DB_CONNECTION: mysql\n DB_HOST: 127.0.0.1\n DB_PORT: 3306\n DB_DATABASE: test_db\n DB_USERNAME: root\n DB_PASSWORD: password\n run: vendor/bin/pest --coverage\n \n deploy:\n needs: test\n runs-on: ubuntu-latest\n if: github.ref == 'refs/heads/main'\n \n steps:\n - uses: actions/checkout@v4\n \n - name: Install doctl\n uses: digitalocean/action-doctl@v2\n with:\n token: ${{ secrets.DIGITALOCEAN_ACCESS_TOKEN }}\n \n - name: Build and push Docker image\n run: |\n docker build -t laravel-app:${{ github.sha }} .\n docker tag laravel-app:${{ github.sha }} registry.digitalocean.com/my-registry/laravel-app:${{ github.sha }}\n doctl registry login\n docker push registry.digitalocean.com/my-registry/laravel-app:${{ github.sha }}\n \n - name: Deploy to DigitalOcean App Platform\n run: |\n doctl apps update ${{ secrets.APP_ID }} --spec app.yaml"
|
|
111
|
+
},
|
|
112
|
+
"error_handling": {
|
|
113
|
+
"validation_errors": "Provide specific line numbers and type information",
|
|
114
|
+
"security_issues": "Reference OWASP guidelines and mitigation strategies",
|
|
115
|
+
"performance_problems": "Include profiling data and optimization suggestions",
|
|
116
|
+
"framework_integration": "Suggest framework-specific patterns and solutions"
|
|
117
|
+
},
|
|
118
|
+
"model": "sonnet",
|
|
119
|
+
"deployment_level": "system",
|
|
120
|
+
"metadata": {
|
|
121
|
+
"created_by": "System",
|
|
122
|
+
"created_at": "2025-01-25",
|
|
123
|
+
"last_modified": "2025-01-25",
|
|
124
|
+
"stability": "stable"
|
|
125
|
+
},
|
|
126
|
+
"memory_routing_rules": [
|
|
127
|
+
"PHP framework patterns and best practices",
|
|
128
|
+
"Domain-driven design and architecture patterns",
|
|
129
|
+
"Type safety and static analysis techniques",
|
|
130
|
+
"Performance optimization strategies",
|
|
131
|
+
"Security implementation patterns",
|
|
132
|
+
"Testing methodologies and patterns",
|
|
133
|
+
"Modern PHP feature usage",
|
|
134
|
+
"Database optimization techniques",
|
|
135
|
+
"API design and implementation patterns",
|
|
136
|
+
"Containerization and deployment strategies",
|
|
137
|
+
"DigitalOcean App Platform configurations and best practices",
|
|
138
|
+
"Docker multi-stage build patterns for PHP applications",
|
|
139
|
+
"Kubernetes deployment patterns and resource management",
|
|
140
|
+
"CI/CD pipeline configurations and automation strategies",
|
|
141
|
+
"Monitoring and observability implementation patterns",
|
|
142
|
+
"Cost optimization strategies for cloud deployments",
|
|
143
|
+
"Security scanning and vulnerability management",
|
|
144
|
+
"Auto-scaling configurations and performance tuning",
|
|
145
|
+
"Infrastructure as Code patterns and templates",
|
|
146
|
+
"Database migration strategies in production environments"
|
|
147
|
+
],
|
|
148
|
+
"handoff_rules": {
|
|
149
|
+
"to_qa": "After implementing features for comprehensive testing and deployment validation",
|
|
150
|
+
"to_ops": "For advanced infrastructure automation and monitoring setup",
|
|
151
|
+
"to_frontend": "When API endpoints are ready for frontend integration",
|
|
152
|
+
"to_database": "For complex schema design and optimization",
|
|
153
|
+
"to_security": "For penetration testing and compliance validation",
|
|
154
|
+
"from_architect": "Receive system design and technical requirements",
|
|
155
|
+
"from_designer": "Style API responses and data structure requirements",
|
|
156
|
+
"from_pm": "Business logic requirements and feature specifications",
|
|
157
|
+
"from_ops": "Infrastructure requirements and deployment constraints"
|
|
158
|
+
},
|
|
159
|
+
"tags": [
|
|
160
|
+
"php",
|
|
161
|
+
"backend",
|
|
162
|
+
"api",
|
|
163
|
+
"framework",
|
|
164
|
+
"database",
|
|
165
|
+
"testing",
|
|
166
|
+
"security",
|
|
167
|
+
"performance",
|
|
168
|
+
"architecture",
|
|
169
|
+
"microservices",
|
|
170
|
+
"cloud-native",
|
|
171
|
+
"type-safety",
|
|
172
|
+
"deployment",
|
|
173
|
+
"docker",
|
|
174
|
+
"kubernetes",
|
|
175
|
+
"digitalocean",
|
|
176
|
+
"ci-cd",
|
|
177
|
+
"monitoring",
|
|
178
|
+
"cost-optimization",
|
|
179
|
+
"infrastructure",
|
|
180
|
+
"devops",
|
|
181
|
+
"observability"
|
|
182
|
+
],
|
|
183
|
+
"authority_level": "full",
|
|
184
|
+
"schema_version": "2.0"
|
|
185
|
+
}
|
claude_mpm/cli/__init__.py
CHANGED
|
@@ -253,6 +253,7 @@ def _verify_mcp_gateway_startup():
|
|
|
253
253
|
# Pre-warm MCP servers regardless of gateway config
|
|
254
254
|
# This eliminates the 11.9s delay on first agent invocation
|
|
255
255
|
def run_pre_warming():
|
|
256
|
+
loop = None
|
|
256
257
|
try:
|
|
257
258
|
start_time = time.time()
|
|
258
259
|
loop = asyncio.new_event_loop()
|
|
@@ -270,10 +271,28 @@ def _verify_mcp_gateway_startup():
|
|
|
270
271
|
if not gateway_configured:
|
|
271
272
|
loop.run_until_complete(verify_mcp_gateway_on_startup())
|
|
272
273
|
|
|
273
|
-
loop.close()
|
|
274
274
|
except Exception as e:
|
|
275
275
|
# Non-blocking - log but don't fail
|
|
276
276
|
logger.debug(f"MCP pre-warming error (non-critical): {e}")
|
|
277
|
+
finally:
|
|
278
|
+
# Properly clean up event loop to prevent kqueue warnings
|
|
279
|
+
if loop is not None:
|
|
280
|
+
try:
|
|
281
|
+
# Cancel all running tasks
|
|
282
|
+
pending = asyncio.all_tasks(loop)
|
|
283
|
+
for task in pending:
|
|
284
|
+
task.cancel()
|
|
285
|
+
# Wait for tasks to complete cancellation
|
|
286
|
+
if pending:
|
|
287
|
+
loop.run_until_complete(
|
|
288
|
+
asyncio.gather(*pending, return_exceptions=True)
|
|
289
|
+
)
|
|
290
|
+
except Exception:
|
|
291
|
+
pass # Ignore cleanup errors
|
|
292
|
+
finally:
|
|
293
|
+
loop.close()
|
|
294
|
+
# Clear the event loop reference to help with cleanup
|
|
295
|
+
asyncio.set_event_loop(None)
|
|
277
296
|
|
|
278
297
|
# Run pre-warming in background thread
|
|
279
298
|
import threading
|
|
@@ -287,11 +306,11 @@ def _verify_mcp_gateway_startup():
|
|
|
287
306
|
if not gateway_configured:
|
|
288
307
|
# Note: We don't await this to avoid blocking startup
|
|
289
308
|
def run_verification():
|
|
309
|
+
loop = None
|
|
290
310
|
try:
|
|
291
311
|
loop = asyncio.new_event_loop()
|
|
292
312
|
asyncio.set_event_loop(loop)
|
|
293
313
|
results = loop.run_until_complete(verify_mcp_gateway_on_startup())
|
|
294
|
-
loop.close()
|
|
295
314
|
|
|
296
315
|
# Log results but don't block
|
|
297
316
|
from ..core.logger import get_logger
|
|
@@ -308,6 +327,25 @@ def _verify_mcp_gateway_startup():
|
|
|
308
327
|
|
|
309
328
|
logger = get_logger("cli")
|
|
310
329
|
logger.debug(f"MCP Gateway verification failed: {e}")
|
|
330
|
+
finally:
|
|
331
|
+
# Properly clean up event loop to prevent kqueue warnings
|
|
332
|
+
if loop is not None:
|
|
333
|
+
try:
|
|
334
|
+
# Cancel all running tasks
|
|
335
|
+
pending = asyncio.all_tasks(loop)
|
|
336
|
+
for task in pending:
|
|
337
|
+
task.cancel()
|
|
338
|
+
# Wait for tasks to complete cancellation
|
|
339
|
+
if pending:
|
|
340
|
+
loop.run_until_complete(
|
|
341
|
+
asyncio.gather(*pending, return_exceptions=True)
|
|
342
|
+
)
|
|
343
|
+
except Exception:
|
|
344
|
+
pass # Ignore cleanup errors
|
|
345
|
+
finally:
|
|
346
|
+
loop.close()
|
|
347
|
+
# Clear the event loop reference to help with cleanup
|
|
348
|
+
asyncio.set_event_loop(None)
|
|
311
349
|
|
|
312
350
|
# Run in background thread to avoid blocking startup
|
|
313
351
|
import threading
|
|
@@ -51,8 +51,8 @@ class AgentsCommand(AgentCommand):
|
|
|
51
51
|
|
|
52
52
|
base_service = AgentDeploymentService()
|
|
53
53
|
self._deployment_service = DeploymentServiceWrapper(base_service)
|
|
54
|
-
except ImportError:
|
|
55
|
-
raise ImportError("Agent deployment service not available")
|
|
54
|
+
except ImportError as e:
|
|
55
|
+
raise ImportError("Agent deployment service not available") from e
|
|
56
56
|
return self._deployment_service
|
|
57
57
|
|
|
58
58
|
@property
|
|
@@ -19,7 +19,7 @@ import os
|
|
|
19
19
|
import re
|
|
20
20
|
import subprocess
|
|
21
21
|
import sys
|
|
22
|
-
from datetime import datetime
|
|
22
|
+
from datetime import datetime, timezone
|
|
23
23
|
from pathlib import Path
|
|
24
24
|
from typing import Dict, List, Optional
|
|
25
25
|
|
|
@@ -372,7 +372,7 @@ class AnalyzeCommand(BaseCommand):
|
|
|
372
372
|
diagram_dir = args.diagram_output or Path("./diagrams")
|
|
373
373
|
diagram_dir.mkdir(parents=True, exist_ok=True)
|
|
374
374
|
|
|
375
|
-
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
375
|
+
timestamp = datetime.now(timezone.utc).strftime("%Y%m%d_%H%M%S")
|
|
376
376
|
|
|
377
377
|
for diagram in diagrams:
|
|
378
378
|
filename = f"{timestamp}_{diagram['title']}.mermaid"
|
|
@@ -429,7 +429,7 @@ class AnalyzeCommand(BaseCommand):
|
|
|
429
429
|
if format_type == "markdown":
|
|
430
430
|
output = "# Code Analysis Report\n\n"
|
|
431
431
|
output += f"**Target:** `{result_data['target']}`\n"
|
|
432
|
-
output += f"**Timestamp:** {datetime.now().isoformat()}\n"
|
|
432
|
+
output += f"**Timestamp:** {datetime.now(timezone.utc).isoformat()}\n"
|
|
433
433
|
|
|
434
434
|
if result_data.get("session_id"):
|
|
435
435
|
output += f"**Session ID:** {result_data['session_id']}\n"
|
|
@@ -492,7 +492,7 @@ class AnalyzeCommand(BaseCommand):
|
|
|
492
492
|
"""
|
|
493
493
|
session_data = {
|
|
494
494
|
"context": "code_analysis",
|
|
495
|
-
"created_at": datetime.now().isoformat(),
|
|
495
|
+
"created_at": datetime.now(timezone.utc).isoformat(),
|
|
496
496
|
"type": "analysis",
|
|
497
497
|
}
|
|
498
498
|
session_id = self.session_manager.create_session(session_data)
|
|
@@ -17,7 +17,7 @@ DESIGN DECISIONS:
|
|
|
17
17
|
import json
|
|
18
18
|
import shutil
|
|
19
19
|
import sys
|
|
20
|
-
from datetime import datetime, timedelta
|
|
20
|
+
from datetime import datetime, timedelta, timezone
|
|
21
21
|
from pathlib import Path
|
|
22
22
|
from typing import Any, Dict, List, Tuple
|
|
23
23
|
|
|
@@ -101,8 +101,8 @@ def parse_size(size_str: str) -> int:
|
|
|
101
101
|
# Try to parse as raw number (assume bytes)
|
|
102
102
|
try:
|
|
103
103
|
return int(size_str)
|
|
104
|
-
except ValueError:
|
|
105
|
-
raise ValueError(f"Invalid size format: {size_str}")
|
|
104
|
+
except ValueError as e:
|
|
105
|
+
raise ValueError(f"Invalid size format: {size_str}") from e
|
|
106
106
|
|
|
107
107
|
|
|
108
108
|
def format_size(size_bytes: int) -> str:
|
|
@@ -208,7 +208,7 @@ def create_archive(source_path: Path, archive_dir: Path) -> Path:
|
|
|
208
208
|
archive_dir.mkdir(parents=True, exist_ok=True)
|
|
209
209
|
|
|
210
210
|
# Create timestamped archive name
|
|
211
|
-
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
211
|
+
timestamp = datetime.now(timezone.utc).strftime("%Y%m%d_%H%M%S")
|
|
212
212
|
archive_name = f"claude_archive_{timestamp}.json"
|
|
213
213
|
archive_path = archive_dir / archive_name
|
|
214
214
|
|
|
@@ -381,7 +381,7 @@ class CleanupCommand(BaseCommand):
|
|
|
381
381
|
result["archive_created"] = True
|
|
382
382
|
result["archive_path"] = str(archive_path)
|
|
383
383
|
except Exception as e:
|
|
384
|
-
raise Exception(f"Failed to create archive: {e}")
|
|
384
|
+
raise Exception(f"Failed to create archive: {e}") from e
|
|
385
385
|
|
|
386
386
|
# Perform cleanup
|
|
387
387
|
original_size, new_size = clean_claude_json(
|
|
@@ -574,12 +574,12 @@ def clean_old_archives(archive_dir: Path, keep_days: int = 90) -> List[Path]:
|
|
|
574
574
|
return []
|
|
575
575
|
|
|
576
576
|
removed = []
|
|
577
|
-
cutoff_date = datetime.now() - timedelta(days=keep_days)
|
|
577
|
+
cutoff_date = datetime.now(timezone.utc) - timedelta(days=keep_days)
|
|
578
578
|
|
|
579
579
|
for archive_file in archive_dir.glob("claude_archive_*.json*"):
|
|
580
580
|
# Check file age
|
|
581
581
|
file_stat = archive_file.stat()
|
|
582
|
-
file_time = datetime.fromtimestamp(file_stat.st_mtime)
|
|
582
|
+
file_time = datetime.fromtimestamp(file_stat.st_mtime, tz=timezone.utc)
|
|
583
583
|
|
|
584
584
|
if file_time < cutoff_date:
|
|
585
585
|
archive_file.unlink()
|
|
@@ -23,7 +23,7 @@ EVENT HANDLING FIX:
|
|
|
23
23
|
import json
|
|
24
24
|
import os
|
|
25
25
|
import sys
|
|
26
|
-
from datetime import datetime
|
|
26
|
+
from datetime import datetime, timezone
|
|
27
27
|
from pathlib import Path
|
|
28
28
|
from typing import Any, Dict, List, Optional
|
|
29
29
|
|
|
@@ -348,7 +348,7 @@ class AgentDiscovery:
|
|
|
348
348
|
|
|
349
349
|
# Get file modification time
|
|
350
350
|
stat = template_file.stat()
|
|
351
|
-
agent.last_modified = datetime.fromtimestamp(stat.st_mtime)
|
|
351
|
+
agent.last_modified = datetime.fromtimestamp(stat.st_mtime, tz=timezone.utc)
|
|
352
352
|
|
|
353
353
|
return agent
|
|
354
354
|
|
claude_mpm/cli/commands/debug.py
CHANGED
|
@@ -14,7 +14,7 @@ import contextlib
|
|
|
14
14
|
import json
|
|
15
15
|
import time
|
|
16
16
|
from collections import defaultdict
|
|
17
|
-
from datetime import datetime
|
|
17
|
+
from datetime import datetime, timezone
|
|
18
18
|
from pathlib import Path
|
|
19
19
|
from typing import Any, Dict
|
|
20
20
|
|
|
@@ -408,7 +408,7 @@ def debug_agents(args, logger):
|
|
|
408
408
|
agent_name = agent_file.stem
|
|
409
409
|
size = agent_file.stat().st_size
|
|
410
410
|
modified = datetime.fromtimestamp(
|
|
411
|
-
agent_file.stat().st_mtime
|
|
411
|
+
agent_file.stat().st_mtime, timezone.utc
|
|
412
412
|
)
|
|
413
413
|
print(f" • {agent_name}")
|
|
414
414
|
print(f" Size: {size:,} bytes")
|
claude_mpm/cli/commands/info.py
CHANGED
|
@@ -11,6 +11,7 @@ DESIGN DECISIONS:
|
|
|
11
11
|
- Provide comprehensive system information for troubleshooting
|
|
12
12
|
"""
|
|
13
13
|
|
|
14
|
+
import importlib.util
|
|
14
15
|
import shutil
|
|
15
16
|
from pathlib import Path
|
|
16
17
|
from typing import Any, Dict
|
|
@@ -119,14 +120,12 @@ class InfoCommand(BaseCommand):
|
|
|
119
120
|
}
|
|
120
121
|
|
|
121
122
|
# Check ai-trackdown-pytools
|
|
122
|
-
|
|
123
|
-
import ai_trackdown_pytools
|
|
124
|
-
|
|
123
|
+
if importlib.util.find_spec("ai_trackdown_pytools") is not None:
|
|
125
124
|
dependencies["ai_trackdown_pytools"] = {
|
|
126
125
|
"installed": True,
|
|
127
126
|
"status": "✓ Installed",
|
|
128
127
|
}
|
|
129
|
-
|
|
128
|
+
else:
|
|
130
129
|
dependencies["ai_trackdown_pytools"] = {
|
|
131
130
|
"installed": False,
|
|
132
131
|
"status": "✗ Not installed",
|
claude_mpm/cli/commands/mcp.py
CHANGED
|
@@ -34,9 +34,10 @@ def manage_mcp(args):
|
|
|
34
34
|
logger = get_logger("cli.mcp")
|
|
35
35
|
|
|
36
36
|
# First check if MCP package is installed for any command
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
import importlib.util
|
|
38
|
+
|
|
39
|
+
mcp_spec = importlib.util.find_spec("mcp")
|
|
40
|
+
if not mcp_spec:
|
|
40
41
|
if args.mcp_command != MCPCommands.INSTALL.value:
|
|
41
42
|
print("\nMCP package is not installed.", file=sys.stderr)
|
|
42
43
|
print("Please install it first:", file=sys.stderr)
|
|
@@ -129,11 +130,12 @@ def _show_status(
|
|
|
129
130
|
print("=" * 50)
|
|
130
131
|
|
|
131
132
|
# Check if MCP package is installed
|
|
132
|
-
|
|
133
|
-
import mcp
|
|
133
|
+
import importlib.util
|
|
134
134
|
|
|
135
|
+
mcp_spec = importlib.util.find_spec("mcp")
|
|
136
|
+
if mcp_spec:
|
|
135
137
|
print("✅ MCP package installed")
|
|
136
|
-
|
|
138
|
+
else:
|
|
137
139
|
print("❌ MCP package not installed")
|
|
138
140
|
print(" Run: claude-mpm mcp install")
|
|
139
141
|
return 1
|
|
@@ -31,11 +31,12 @@ class MCPInstallCommands:
|
|
|
31
31
|
|
|
32
32
|
# Step 1: Install MCP package if needed
|
|
33
33
|
print("\n1️⃣ Checking MCP package installation...")
|
|
34
|
-
|
|
35
|
-
import mcp
|
|
34
|
+
import importlib.util
|
|
36
35
|
|
|
36
|
+
mcp_spec = importlib.util.find_spec("mcp")
|
|
37
|
+
if mcp_spec:
|
|
37
38
|
print("✅ MCP package already installed")
|
|
38
|
-
|
|
39
|
+
else:
|
|
39
40
|
print("📦 Installing MCP package...")
|
|
40
41
|
try:
|
|
41
42
|
subprocess.check_call([sys.executable, "-m", "pip", "install", "mcp"])
|
|
@@ -172,13 +173,12 @@ class MCPInstallCommands:
|
|
|
172
173
|
return str(executable_path)
|
|
173
174
|
|
|
174
175
|
# Fallback: Use Python module invocation if no executable found
|
|
175
|
-
|
|
176
|
-
import claude_mpm
|
|
176
|
+
import importlib.util
|
|
177
177
|
|
|
178
|
+
claude_mpm_spec = importlib.util.find_spec("claude_mpm")
|
|
179
|
+
if claude_mpm_spec:
|
|
178
180
|
print(f" Using Python module: {sys.executable} -m claude_mpm")
|
|
179
181
|
return sys.executable
|
|
180
|
-
except ImportError:
|
|
181
|
-
pass
|
|
182
182
|
|
|
183
183
|
return None
|
|
184
184
|
|
|
@@ -193,7 +193,7 @@ class MCPInstallCommands:
|
|
|
193
193
|
dict or None: Configuration dictionary
|
|
194
194
|
"""
|
|
195
195
|
import json
|
|
196
|
-
from datetime import datetime
|
|
196
|
+
from datetime import datetime, timezone
|
|
197
197
|
|
|
198
198
|
config = {}
|
|
199
199
|
|
|
@@ -226,7 +226,7 @@ class MCPInstallCommands:
|
|
|
226
226
|
|
|
227
227
|
# Create backup
|
|
228
228
|
backup_path = config_path.with_suffix(
|
|
229
|
-
f'.backup.{datetime.now().strftime("%Y%m%d_%H%M%S")}.json'
|
|
229
|
+
f'.backup.{datetime.now(timezone.utc).strftime("%Y%m%d_%H%M%S")}.json'
|
|
230
230
|
)
|
|
231
231
|
try:
|
|
232
232
|
config_path.rename(backup_path)
|
claude_mpm/cli/commands/run.py
CHANGED
|
@@ -15,7 +15,7 @@ DESIGN DECISIONS:
|
|
|
15
15
|
|
|
16
16
|
import subprocess
|
|
17
17
|
import sys
|
|
18
|
-
from datetime import datetime
|
|
18
|
+
from datetime import datetime, timezone
|
|
19
19
|
from typing import Optional
|
|
20
20
|
|
|
21
21
|
from ...constants import LogLevel
|
|
@@ -522,7 +522,7 @@ class RunCommand(BaseCommand):
|
|
|
522
522
|
# Update session usage
|
|
523
523
|
session = session_manager.load_session(resume_session_id)
|
|
524
524
|
if session:
|
|
525
|
-
session.last_used = datetime.now().isoformat()
|
|
525
|
+
session.last_used = datetime.now(timezone.utc).isoformat()
|
|
526
526
|
session.use_count += 1
|
|
527
527
|
session_manager.save_session(session)
|
|
528
528
|
else:
|
|
@@ -936,7 +936,7 @@ def run_session_legacy(args):
|
|
|
936
936
|
# Update session usage
|
|
937
937
|
session = session_manager.load_session(resume_session_id)
|
|
938
938
|
if session:
|
|
939
|
-
session.last_used = datetime.now().isoformat()
|
|
939
|
+
session.last_used = datetime.now(timezone.utc).isoformat()
|
|
940
940
|
session.use_count += 1
|
|
941
941
|
session_manager.save_session(session)
|
|
942
942
|
else:
|
|
@@ -18,7 +18,7 @@ import logging
|
|
|
18
18
|
import shutil
|
|
19
19
|
import subprocess
|
|
20
20
|
import sys
|
|
21
|
-
from datetime import datetime
|
|
21
|
+
from datetime import datetime, timezone
|
|
22
22
|
from pathlib import Path
|
|
23
23
|
from typing import Any, Dict, Optional
|
|
24
24
|
|
|
@@ -339,14 +339,27 @@ class StartupStatusLogger:
|
|
|
339
339
|
|
|
340
340
|
def _check_socketio_dependencies(self) -> Dict[str, Any]:
|
|
341
341
|
"""Check if Socket.IO dependencies are available."""
|
|
342
|
+
import importlib.util
|
|
343
|
+
|
|
342
344
|
result = {"available": False, "error": None}
|
|
343
345
|
|
|
344
346
|
try:
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
347
|
+
# Check for socketio dependencies without importing them
|
|
348
|
+
aiohttp_spec = importlib.util.find_spec("aiohttp")
|
|
349
|
+
engineio_spec = importlib.util.find_spec("engineio")
|
|
350
|
+
socketio_spec = importlib.util.find_spec("socketio")
|
|
348
351
|
|
|
349
|
-
|
|
352
|
+
if aiohttp_spec and engineio_spec and socketio_spec:
|
|
353
|
+
result["available"] = True
|
|
354
|
+
else:
|
|
355
|
+
missing = []
|
|
356
|
+
if not aiohttp_spec:
|
|
357
|
+
missing.append("aiohttp")
|
|
358
|
+
if not engineio_spec:
|
|
359
|
+
missing.append("engineio")
|
|
360
|
+
if not socketio_spec:
|
|
361
|
+
missing.append("socketio")
|
|
362
|
+
result["error"] = f"Missing dependencies: {', '.join(missing)}"
|
|
350
363
|
except ImportError as e:
|
|
351
364
|
result["error"] = f"Missing dependencies: {e}"
|
|
352
365
|
except Exception as e:
|
|
@@ -493,7 +506,7 @@ def setup_startup_logging(project_root: Optional[Path] = None) -> Path:
|
|
|
493
506
|
log_dir.mkdir(parents=True, exist_ok=True)
|
|
494
507
|
|
|
495
508
|
# Generate timestamp for log file
|
|
496
|
-
timestamp = datetime.now().strftime("%Y-%m-%d-%H-%M-%S")
|
|
509
|
+
timestamp = datetime.now(timezone.utc).strftime("%Y-%m-%d-%H-%M-%S")
|
|
497
510
|
log_file = log_dir / f"startup-{timestamp}.log"
|
|
498
511
|
|
|
499
512
|
# Create file handler with detailed formatting
|
|
@@ -516,7 +529,7 @@ def setup_startup_logging(project_root: Optional[Path] = None) -> Path:
|
|
|
516
529
|
# Log startup header
|
|
517
530
|
logger = get_logger("startup")
|
|
518
531
|
logger.info("=" * 60)
|
|
519
|
-
logger.info(f"Claude MPM Startup - {datetime.now().isoformat()}")
|
|
532
|
+
logger.info(f"Claude MPM Startup - {datetime.now(timezone.utc).isoformat()}")
|
|
520
533
|
logger.info(f"Log file: {log_file}")
|
|
521
534
|
logger.info("=" * 60)
|
|
522
535
|
|