claude-mpm 4.15.6__py3-none-any.whl → 4.21.3__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 claude-mpm might be problematic. Click here for more details.
- claude_mpm/VERSION +1 -1
- claude_mpm/agents/BASE_ENGINEER.md +286 -0
- claude_mpm/agents/BASE_PM.md +272 -23
- claude_mpm/agents/PM_INSTRUCTIONS.md +49 -0
- claude_mpm/agents/agent_loader.py +4 -4
- claude_mpm/agents/templates/engineer.json +5 -1
- claude_mpm/agents/templates/php-engineer.json +10 -4
- claude_mpm/agents/templates/python_engineer.json +8 -3
- claude_mpm/agents/templates/rust_engineer.json +12 -7
- claude_mpm/agents/templates/svelte-engineer.json +225 -0
- claude_mpm/cli/commands/__init__.py +2 -0
- claude_mpm/cli/commands/mpm_init/__init__.py +73 -0
- claude_mpm/cli/commands/mpm_init/core.py +525 -0
- claude_mpm/cli/commands/mpm_init/display.py +341 -0
- claude_mpm/cli/commands/mpm_init/git_activity.py +427 -0
- claude_mpm/cli/commands/mpm_init/modes.py +397 -0
- claude_mpm/cli/commands/mpm_init/prompts.py +442 -0
- claude_mpm/cli/commands/mpm_init_cli.py +396 -0
- claude_mpm/cli/commands/mpm_init_handler.py +67 -1
- claude_mpm/cli/commands/skills.py +488 -0
- claude_mpm/cli/executor.py +2 -0
- claude_mpm/cli/parsers/base_parser.py +7 -0
- claude_mpm/cli/parsers/mpm_init_parser.py +42 -0
- claude_mpm/cli/parsers/skills_parser.py +137 -0
- claude_mpm/cli/startup.py +57 -0
- claude_mpm/commands/mpm-auto-configure.md +52 -0
- claude_mpm/commands/mpm-help.md +6 -0
- claude_mpm/commands/mpm-init.md +112 -6
- claude_mpm/commands/mpm-resume.md +372 -0
- claude_mpm/commands/mpm-version.md +113 -0
- claude_mpm/commands/mpm.md +2 -0
- claude_mpm/config/agent_config.py +2 -2
- claude_mpm/constants.py +12 -0
- claude_mpm/core/config.py +42 -0
- claude_mpm/core/factories.py +1 -1
- claude_mpm/core/interfaces.py +56 -1
- claude_mpm/core/optimized_agent_loader.py +3 -3
- claude_mpm/hooks/__init__.py +8 -0
- claude_mpm/hooks/claude_hooks/response_tracking.py +35 -1
- claude_mpm/hooks/session_resume_hook.py +121 -0
- claude_mpm/models/resume_log.py +340 -0
- claude_mpm/services/agents/auto_config_manager.py +1 -1
- claude_mpm/services/agents/deployment/agent_configuration_manager.py +1 -1
- claude_mpm/services/agents/deployment/agent_record_service.py +1 -1
- claude_mpm/services/agents/deployment/agent_validator.py +17 -1
- claude_mpm/services/agents/deployment/async_agent_deployment.py +1 -1
- claude_mpm/services/agents/deployment/local_template_deployment.py +1 -1
- claude_mpm/services/agents/local_template_manager.py +1 -1
- claude_mpm/services/agents/recommender.py +47 -0
- claude_mpm/services/cli/resume_service.py +617 -0
- claude_mpm/services/cli/session_manager.py +87 -0
- claude_mpm/services/cli/session_pause_manager.py +504 -0
- claude_mpm/services/cli/session_resume_helper.py +372 -0
- claude_mpm/services/core/base.py +26 -11
- claude_mpm/services/core/interfaces.py +56 -1
- claude_mpm/services/core/models/agent_config.py +3 -0
- claude_mpm/services/core/models/process.py +4 -0
- claude_mpm/services/core/path_resolver.py +1 -1
- claude_mpm/services/diagnostics/models.py +21 -0
- claude_mpm/services/event_bus/relay.py +23 -7
- claude_mpm/services/infrastructure/resume_log_generator.py +439 -0
- claude_mpm/services/local_ops/__init__.py +2 -0
- claude_mpm/services/mcp_config_manager.py +7 -131
- claude_mpm/services/mcp_gateway/auto_configure.py +31 -25
- claude_mpm/services/mcp_gateway/core/process_pool.py +19 -10
- claude_mpm/services/mcp_gateway/tools/external_mcp_services.py +26 -21
- claude_mpm/services/memory/failure_tracker.py +19 -4
- claude_mpm/services/session_manager.py +205 -1
- claude_mpm/services/unified/deployment_strategies/local.py +1 -1
- claude_mpm/services/version_service.py +104 -1
- claude_mpm/skills/__init__.py +21 -0
- claude_mpm/skills/agent_skills_injector.py +324 -0
- claude_mpm/skills/bundled/LICENSE_ATTRIBUTIONS.md +79 -0
- claude_mpm/skills/bundled/api-documentation.md +393 -0
- claude_mpm/skills/bundled/async-testing.md +571 -0
- claude_mpm/skills/bundled/code-review.md +143 -0
- claude_mpm/skills/bundled/collaboration/brainstorming/SKILL.md +79 -0
- claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/SKILL.md +178 -0
- claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/agent-prompts.md +577 -0
- claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/coordination-patterns.md +467 -0
- claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/examples.md +537 -0
- claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/troubleshooting.md +730 -0
- claude_mpm/skills/bundled/collaboration/requesting-code-review/SKILL.md +112 -0
- claude_mpm/skills/bundled/collaboration/requesting-code-review/references/code-reviewer-template.md +146 -0
- claude_mpm/skills/bundled/collaboration/requesting-code-review/references/review-examples.md +412 -0
- claude_mpm/skills/bundled/collaboration/writing-plans/SKILL.md +81 -0
- claude_mpm/skills/bundled/collaboration/writing-plans/references/best-practices.md +362 -0
- claude_mpm/skills/bundled/collaboration/writing-plans/references/plan-structure-templates.md +312 -0
- claude_mpm/skills/bundled/database-migration.md +199 -0
- claude_mpm/skills/bundled/debugging/root-cause-tracing/SKILL.md +152 -0
- claude_mpm/skills/bundled/debugging/root-cause-tracing/references/advanced-techniques.md +668 -0
- claude_mpm/skills/bundled/debugging/root-cause-tracing/references/examples.md +587 -0
- claude_mpm/skills/bundled/debugging/root-cause-tracing/references/integration.md +438 -0
- claude_mpm/skills/bundled/debugging/root-cause-tracing/references/tracing-techniques.md +391 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/CREATION-LOG.md +119 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/SKILL.md +148 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/references/anti-patterns.md +483 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/references/examples.md +452 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/references/troubleshooting.md +449 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/references/workflow.md +411 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/test-academic.md +14 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/test-pressure-1.md +58 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/test-pressure-2.md +68 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/test-pressure-3.md +69 -0
- claude_mpm/skills/bundled/debugging/verification-before-completion/SKILL.md +131 -0
- claude_mpm/skills/bundled/debugging/verification-before-completion/references/gate-function.md +325 -0
- claude_mpm/skills/bundled/debugging/verification-before-completion/references/integration-and-workflows.md +490 -0
- claude_mpm/skills/bundled/debugging/verification-before-completion/references/red-flags-and-failures.md +425 -0
- claude_mpm/skills/bundled/debugging/verification-before-completion/references/verification-patterns.md +499 -0
- claude_mpm/skills/bundled/docker-containerization.md +194 -0
- claude_mpm/skills/bundled/express-local-dev.md +1429 -0
- claude_mpm/skills/bundled/fastapi-local-dev.md +1199 -0
- claude_mpm/skills/bundled/git-workflow.md +414 -0
- claude_mpm/skills/bundled/imagemagick.md +204 -0
- claude_mpm/skills/bundled/json-data-handling.md +223 -0
- claude_mpm/skills/bundled/main/artifacts-builder/SKILL.md +86 -0
- claude_mpm/skills/bundled/main/internal-comms/SKILL.md +43 -0
- claude_mpm/skills/bundled/main/internal-comms/examples/3p-updates.md +47 -0
- claude_mpm/skills/bundled/main/internal-comms/examples/company-newsletter.md +65 -0
- claude_mpm/skills/bundled/main/internal-comms/examples/faq-answers.md +30 -0
- claude_mpm/skills/bundled/main/internal-comms/examples/general-comms.md +16 -0
- claude_mpm/skills/bundled/main/mcp-builder/SKILL.md +160 -0
- claude_mpm/skills/bundled/main/mcp-builder/reference/design_principles.md +412 -0
- claude_mpm/skills/bundled/main/mcp-builder/reference/evaluation.md +602 -0
- claude_mpm/skills/bundled/main/mcp-builder/reference/mcp_best_practices.md +915 -0
- claude_mpm/skills/bundled/main/mcp-builder/reference/node_mcp_server.md +916 -0
- claude_mpm/skills/bundled/main/mcp-builder/reference/python_mcp_server.md +752 -0
- claude_mpm/skills/bundled/main/mcp-builder/reference/workflow.md +1237 -0
- claude_mpm/skills/bundled/main/mcp-builder/scripts/connections.py +157 -0
- claude_mpm/skills/bundled/main/mcp-builder/scripts/evaluation.py +425 -0
- claude_mpm/skills/bundled/main/skill-creator/SKILL.md +189 -0
- claude_mpm/skills/bundled/main/skill-creator/references/best-practices.md +500 -0
- claude_mpm/skills/bundled/main/skill-creator/references/creation-workflow.md +464 -0
- claude_mpm/skills/bundled/main/skill-creator/references/examples.md +619 -0
- claude_mpm/skills/bundled/main/skill-creator/references/progressive-disclosure.md +437 -0
- claude_mpm/skills/bundled/main/skill-creator/references/skill-structure.md +231 -0
- claude_mpm/skills/bundled/main/skill-creator/scripts/init_skill.py +303 -0
- claude_mpm/skills/bundled/main/skill-creator/scripts/package_skill.py +113 -0
- claude_mpm/skills/bundled/main/skill-creator/scripts/quick_validate.py +72 -0
- claude_mpm/skills/bundled/nextjs-local-dev.md +807 -0
- claude_mpm/skills/bundled/pdf.md +141 -0
- claude_mpm/skills/bundled/performance-profiling.md +567 -0
- claude_mpm/skills/bundled/php/espocrm-development/SKILL.md +170 -0
- claude_mpm/skills/bundled/php/espocrm-development/references/architecture.md +602 -0
- claude_mpm/skills/bundled/php/espocrm-development/references/common-tasks.md +821 -0
- claude_mpm/skills/bundled/php/espocrm-development/references/development-workflow.md +742 -0
- claude_mpm/skills/bundled/php/espocrm-development/references/frontend-customization.md +726 -0
- claude_mpm/skills/bundled/php/espocrm-development/references/hooks-and-services.md +764 -0
- claude_mpm/skills/bundled/php/espocrm-development/references/testing-debugging.md +831 -0
- claude_mpm/skills/bundled/refactoring-patterns.md +180 -0
- claude_mpm/skills/bundled/rust/desktop-applications/SKILL.md +226 -0
- claude_mpm/skills/bundled/rust/desktop-applications/references/architecture-patterns.md +901 -0
- claude_mpm/skills/bundled/rust/desktop-applications/references/native-gui-frameworks.md +901 -0
- claude_mpm/skills/bundled/rust/desktop-applications/references/platform-integration.md +775 -0
- claude_mpm/skills/bundled/rust/desktop-applications/references/state-management.md +937 -0
- claude_mpm/skills/bundled/rust/desktop-applications/references/tauri-framework.md +770 -0
- claude_mpm/skills/bundled/rust/desktop-applications/references/testing-deployment.md +961 -0
- claude_mpm/skills/bundled/security-scanning.md +327 -0
- claude_mpm/skills/bundled/systematic-debugging.md +473 -0
- claude_mpm/skills/bundled/test-driven-development.md +378 -0
- claude_mpm/skills/bundled/testing/condition-based-waiting/SKILL.md +119 -0
- claude_mpm/skills/bundled/testing/condition-based-waiting/references/patterns-and-implementation.md +253 -0
- claude_mpm/skills/bundled/testing/test-driven-development/SKILL.md +145 -0
- claude_mpm/skills/bundled/testing/test-driven-development/references/anti-patterns.md +543 -0
- claude_mpm/skills/bundled/testing/test-driven-development/references/examples.md +741 -0
- claude_mpm/skills/bundled/testing/test-driven-development/references/integration.md +470 -0
- claude_mpm/skills/bundled/testing/test-driven-development/references/philosophy.md +458 -0
- claude_mpm/skills/bundled/testing/test-driven-development/references/workflow.md +639 -0
- claude_mpm/skills/bundled/testing/testing-anti-patterns/SKILL.md +140 -0
- claude_mpm/skills/bundled/testing/testing-anti-patterns/references/completeness-anti-patterns.md +572 -0
- claude_mpm/skills/bundled/testing/testing-anti-patterns/references/core-anti-patterns.md +411 -0
- claude_mpm/skills/bundled/testing/testing-anti-patterns/references/detection-guide.md +569 -0
- claude_mpm/skills/bundled/testing/testing-anti-patterns/references/tdd-connection.md +695 -0
- claude_mpm/skills/bundled/testing/webapp-testing/SKILL.md +184 -0
- claude_mpm/skills/bundled/testing/webapp-testing/decision-tree.md +459 -0
- claude_mpm/skills/bundled/testing/webapp-testing/examples/console_logging.py +35 -0
- claude_mpm/skills/bundled/testing/webapp-testing/examples/element_discovery.py +44 -0
- claude_mpm/skills/bundled/testing/webapp-testing/examples/static_html_automation.py +34 -0
- claude_mpm/skills/bundled/testing/webapp-testing/playwright-patterns.md +479 -0
- claude_mpm/skills/bundled/testing/webapp-testing/reconnaissance-pattern.md +687 -0
- claude_mpm/skills/bundled/testing/webapp-testing/scripts/with_server.py +129 -0
- claude_mpm/skills/bundled/testing/webapp-testing/server-management.md +758 -0
- claude_mpm/skills/bundled/testing/webapp-testing/troubleshooting.md +868 -0
- claude_mpm/skills/bundled/vite-local-dev.md +1061 -0
- claude_mpm/skills/bundled/web-performance-optimization.md +2305 -0
- claude_mpm/skills/bundled/xlsx.md +157 -0
- claude_mpm/skills/registry.py +97 -9
- claude_mpm/skills/skills_registry.py +348 -0
- claude_mpm/skills/skills_service.py +739 -0
- claude_mpm/tools/code_tree_analyzer/__init__.py +45 -0
- claude_mpm/tools/code_tree_analyzer/analysis.py +299 -0
- claude_mpm/tools/code_tree_analyzer/cache.py +131 -0
- claude_mpm/tools/code_tree_analyzer/core.py +380 -0
- claude_mpm/tools/code_tree_analyzer/discovery.py +403 -0
- claude_mpm/tools/code_tree_analyzer/events.py +168 -0
- claude_mpm/tools/code_tree_analyzer/gitignore.py +308 -0
- claude_mpm/tools/code_tree_analyzer/models.py +39 -0
- claude_mpm/tools/code_tree_analyzer/multilang_analyzer.py +224 -0
- claude_mpm/tools/code_tree_analyzer/python_analyzer.py +284 -0
- claude_mpm/utils/agent_dependency_loader.py +2 -2
- {claude_mpm-4.15.6.dist-info → claude_mpm-4.21.3.dist-info}/METADATA +211 -33
- {claude_mpm-4.15.6.dist-info → claude_mpm-4.21.3.dist-info}/RECORD +206 -64
- claude_mpm/agents/INSTRUCTIONS_OLD_DEPRECATED.md +0 -602
- claude_mpm/cli/commands/mpm_init.py +0 -2008
- claude_mpm/tools/code_tree_analyzer.py +0 -1825
- {claude_mpm-4.15.6.dist-info → claude_mpm-4.21.3.dist-info}/WHEEL +0 -0
- {claude_mpm-4.15.6.dist-info → claude_mpm-4.21.3.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.15.6.dist-info → claude_mpm-4.21.3.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.15.6.dist-info → claude_mpm-4.21.3.dist-info}/top_level.txt +0 -0
|
@@ -163,17 +163,21 @@ class MCPAutoConfigurator:
|
|
|
163
163
|
Returns:
|
|
164
164
|
True if user agrees, False if declines, None if timeout
|
|
165
165
|
"""
|
|
166
|
-
print("\n" + "=" * 60)
|
|
167
|
-
print("🔧 MCP Gateway Configuration")
|
|
168
|
-
print("=" * 60)
|
|
169
|
-
print(
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
print(
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
print("
|
|
176
|
-
print(
|
|
166
|
+
print("\n" + "=" * 60, file=sys.stderr)
|
|
167
|
+
print("🔧 MCP Gateway Configuration", file=sys.stderr)
|
|
168
|
+
print("=" * 60, file=sys.stderr)
|
|
169
|
+
print(
|
|
170
|
+
"\nClaude MPM can automatically configure MCP Gateway for", file=sys.stderr
|
|
171
|
+
)
|
|
172
|
+
print(
|
|
173
|
+
"Claude Code integration. This enables advanced features:", file=sys.stderr
|
|
174
|
+
)
|
|
175
|
+
print(" • File analysis and summarization", file=sys.stderr)
|
|
176
|
+
print(" • System diagnostics", file=sys.stderr)
|
|
177
|
+
print(" • Ticket management", file=sys.stderr)
|
|
178
|
+
print(" • And more...", file=sys.stderr)
|
|
179
|
+
print("\nWould you like to configure it now? (y/n)", file=sys.stderr)
|
|
180
|
+
print(f"(Auto-declining in {timeout} seconds)", file=sys.stderr)
|
|
177
181
|
|
|
178
182
|
# Use threading for cross-platform timeout support
|
|
179
183
|
# Python 3.7+ has queue built-in - no need to check, we require 3.10+
|
|
@@ -203,7 +207,7 @@ class MCPAutoConfigurator:
|
|
|
203
207
|
|
|
204
208
|
if input_thread.is_alive():
|
|
205
209
|
# Timed out
|
|
206
|
-
print("\n(Timed out - declining)")
|
|
210
|
+
print("\n(Timed out - declining)", file=sys.stderr)
|
|
207
211
|
return None
|
|
208
212
|
# Got input
|
|
209
213
|
return user_input in ["y", "yes"]
|
|
@@ -220,7 +224,7 @@ class MCPAutoConfigurator:
|
|
|
220
224
|
if self.claude_config_path.exists():
|
|
221
225
|
backup_path = self._create_backup()
|
|
222
226
|
if backup_path:
|
|
223
|
-
print(f"✅ Backup created: {backup_path}")
|
|
227
|
+
print(f"✅ Backup created: {backup_path}", file=sys.stderr)
|
|
224
228
|
|
|
225
229
|
# Load or create configuration
|
|
226
230
|
config = self._load_or_create_config()
|
|
@@ -232,7 +236,7 @@ class MCPAutoConfigurator:
|
|
|
232
236
|
# Find claude-mpm executable
|
|
233
237
|
executable = self._find_claude_mpm_executable()
|
|
234
238
|
if not executable:
|
|
235
|
-
print("❌ Could not find claude-mpm executable")
|
|
239
|
+
print("❌ Could not find claude-mpm executable", file=sys.stderr)
|
|
236
240
|
return False
|
|
237
241
|
|
|
238
242
|
# Configure MCP server
|
|
@@ -246,20 +250,22 @@ class MCPAutoConfigurator:
|
|
|
246
250
|
with self.claude_config_path.open("w") as f:
|
|
247
251
|
json.dump(config, f, indent=2)
|
|
248
252
|
|
|
249
|
-
print(
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
print("
|
|
253
|
-
print("
|
|
254
|
-
print("
|
|
253
|
+
print(
|
|
254
|
+
f"✅ Configuration saved to: {self.claude_config_path}", file=sys.stderr
|
|
255
|
+
)
|
|
256
|
+
print("\n🎉 MCP Gateway configured successfully!", file=sys.stderr)
|
|
257
|
+
print("\nNext steps:", file=sys.stderr)
|
|
258
|
+
print("1. Restart Claude Code (if running)", file=sys.stderr)
|
|
259
|
+
print("2. Look for the MCP icon in the interface", file=sys.stderr)
|
|
260
|
+
print("3. Try @claude-mpm-gateway in a conversation", file=sys.stderr)
|
|
255
261
|
|
|
256
262
|
return True
|
|
257
263
|
|
|
258
264
|
except Exception as e:
|
|
259
265
|
self.logger.error(f"Auto-configuration failed: {e}")
|
|
260
|
-
print(f"❌ Configuration failed: {e}")
|
|
261
|
-
print("\nYou can configure manually with:")
|
|
262
|
-
print(" claude-mpm mcp install")
|
|
266
|
+
print(f"❌ Configuration failed: {e}", file=sys.stderr)
|
|
267
|
+
print("\nYou can configure manually with:", file=sys.stderr)
|
|
268
|
+
print(" claude-mpm mcp install", file=sys.stderr)
|
|
263
269
|
return False
|
|
264
270
|
|
|
265
271
|
def _create_backup(self) -> Optional[Path]:
|
|
@@ -344,8 +350,8 @@ class MCPAutoConfigurator:
|
|
|
344
350
|
if user_choice:
|
|
345
351
|
return self.auto_configure()
|
|
346
352
|
if user_choice is False: # User explicitly said no
|
|
347
|
-
print("\n📝 You can configure MCP later with:")
|
|
348
|
-
print(" claude-mpm mcp install")
|
|
353
|
+
print("\n📝 You can configure MCP later with:", file=sys.stderr)
|
|
354
|
+
print(" claude-mpm mcp install", file=sys.stderr)
|
|
349
355
|
# If timeout (None), don't show additional message
|
|
350
356
|
return False
|
|
351
357
|
|
|
@@ -864,7 +864,7 @@ def _prompt_kuzu_update(current: str, latest: str) -> None:
|
|
|
864
864
|
# Check if running in a non-interactive context
|
|
865
865
|
try:
|
|
866
866
|
if confirm_operation(message):
|
|
867
|
-
print("🚀 Updating kuzu-memory...")
|
|
867
|
+
print("🚀 Updating kuzu-memory...", file=sys.stderr)
|
|
868
868
|
try:
|
|
869
869
|
result = subprocess.run(
|
|
870
870
|
["pipx", "upgrade", "kuzu-memory"],
|
|
@@ -874,28 +874,37 @@ def _prompt_kuzu_update(current: str, latest: str) -> None:
|
|
|
874
874
|
check=False,
|
|
875
875
|
)
|
|
876
876
|
if result.returncode == 0:
|
|
877
|
-
print("✅ Successfully updated kuzu-memory!")
|
|
877
|
+
print("✅ Successfully updated kuzu-memory!", file=sys.stderr)
|
|
878
878
|
logger.info(f"Updated kuzu-memory from {current} to {latest}")
|
|
879
879
|
else:
|
|
880
|
-
print(f"⚠️ Update failed: {result.stderr}")
|
|
880
|
+
print(f"⚠️ Update failed: {result.stderr}", file=sys.stderr)
|
|
881
881
|
logger.warning(f"kuzu-memory update failed: {result.stderr}")
|
|
882
882
|
except subprocess.TimeoutExpired:
|
|
883
|
-
print("⚠️ Update timed out. Please try again later.")
|
|
883
|
+
print("⚠️ Update timed out. Please try again later.", file=sys.stderr)
|
|
884
884
|
logger.warning("kuzu-memory update timed out")
|
|
885
885
|
except Exception as e:
|
|
886
|
-
print(f"⚠️ Update failed: {e}")
|
|
886
|
+
print(f"⚠️ Update failed: {e}", file=sys.stderr)
|
|
887
887
|
logger.warning(f"kuzu-memory update error: {e}")
|
|
888
888
|
else:
|
|
889
889
|
# User declined update
|
|
890
|
-
print("\n To skip this version permanently, run:")
|
|
891
|
-
print(
|
|
892
|
-
|
|
893
|
-
|
|
890
|
+
print("\n To skip this version permanently, run:", file=sys.stderr)
|
|
891
|
+
print(
|
|
892
|
+
f" claude-mpm config set-skip-version kuzu-memory {latest}",
|
|
893
|
+
file=sys.stderr,
|
|
894
|
+
)
|
|
895
|
+
print(" To disable update checks for kuzu-memory:", file=sys.stderr)
|
|
896
|
+
print(
|
|
897
|
+
" claude-mpm config disable-update-checks kuzu-memory",
|
|
898
|
+
file=sys.stderr,
|
|
899
|
+
)
|
|
894
900
|
|
|
895
901
|
# Ask if user wants to skip this version
|
|
896
902
|
if confirm_operation("\n Skip this version in future checks?"):
|
|
897
903
|
UpdatePreferences.set_skip_version("kuzu-memory", latest)
|
|
898
|
-
print(
|
|
904
|
+
print(
|
|
905
|
+
f" Version {latest} will be skipped in future checks.",
|
|
906
|
+
file=sys.stderr,
|
|
907
|
+
)
|
|
899
908
|
except (KeyboardInterrupt, EOFError):
|
|
900
909
|
# User interrupted or input not available
|
|
901
910
|
pass
|
|
@@ -161,12 +161,17 @@ class ExternalMCPService(BaseToolAdapter):
|
|
|
161
161
|
|
|
162
162
|
if interactive:
|
|
163
163
|
# Show user-friendly installation prompt
|
|
164
|
-
print(f"\n⚠️ {self.package_name} not found")
|
|
165
|
-
print(
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
print("
|
|
164
|
+
print(f"\n⚠️ {self.package_name} not found", file=sys.stderr)
|
|
165
|
+
print(
|
|
166
|
+
"This package enables enhanced functionality (optional).",
|
|
167
|
+
file=sys.stderr,
|
|
168
|
+
)
|
|
169
|
+
print("\nInstallation options:", file=sys.stderr)
|
|
170
|
+
print(
|
|
171
|
+
"1. Install via pip (recommended for this project)", file=sys.stderr
|
|
172
|
+
)
|
|
173
|
+
print("2. Install via pipx (isolated, system-wide)", file=sys.stderr)
|
|
174
|
+
print("3. Skip (continue without this package)", file=sys.stderr)
|
|
170
175
|
|
|
171
176
|
try:
|
|
172
177
|
choice = input("\nChoose option (1/2/3) [1]: ").strip() or "1"
|
|
@@ -180,7 +185,7 @@ class ExternalMCPService(BaseToolAdapter):
|
|
|
180
185
|
)
|
|
181
186
|
return False
|
|
182
187
|
except (EOFError, KeyboardInterrupt):
|
|
183
|
-
print("\nInstallation cancelled")
|
|
188
|
+
print("\nInstallation cancelled", file=sys.stderr)
|
|
184
189
|
return False
|
|
185
190
|
else:
|
|
186
191
|
# Non-interactive: default to pip
|
|
@@ -201,7 +206,7 @@ class ExternalMCPService(BaseToolAdapter):
|
|
|
201
206
|
async def _install_via_pip(self) -> bool:
|
|
202
207
|
"""Install package via pip."""
|
|
203
208
|
try:
|
|
204
|
-
print(f"\n📦 Installing {self.package_name} via pip...")
|
|
209
|
+
print(f"\n📦 Installing {self.package_name} via pip...", file=sys.stderr)
|
|
205
210
|
result = subprocess.run(
|
|
206
211
|
[sys.executable, "-m", "pip", "install", self.package_name],
|
|
207
212
|
capture_output=True,
|
|
@@ -211,21 +216,21 @@ class ExternalMCPService(BaseToolAdapter):
|
|
|
211
216
|
)
|
|
212
217
|
|
|
213
218
|
if result.returncode == 0:
|
|
214
|
-
print(f"✓ Successfully installed {self.package_name}")
|
|
219
|
+
print(f"✓ Successfully installed {self.package_name}", file=sys.stderr)
|
|
215
220
|
self.logger.info(f"Successfully installed {self.package_name} via pip")
|
|
216
221
|
return True
|
|
217
222
|
|
|
218
223
|
error_msg = result.stderr.strip() if result.stderr else "Unknown error"
|
|
219
|
-
print(f"✗ Installation failed: {error_msg}")
|
|
224
|
+
print(f"✗ Installation failed: {error_msg}", file=sys.stderr)
|
|
220
225
|
self.logger.error(f"Failed to install {self.package_name}: {error_msg}")
|
|
221
226
|
return False
|
|
222
227
|
|
|
223
228
|
except subprocess.TimeoutExpired:
|
|
224
|
-
print("✗ Installation timed out")
|
|
229
|
+
print("✗ Installation timed out", file=sys.stderr)
|
|
225
230
|
self.logger.error(f"Installation of {self.package_name} timed out")
|
|
226
231
|
return False
|
|
227
232
|
except Exception as e:
|
|
228
|
-
print(f"✗ Installation error: {e}")
|
|
233
|
+
print(f"✗ Installation error: {e}", file=sys.stderr)
|
|
229
234
|
self.logger.error(f"Error installing {self.package_name}: {e}")
|
|
230
235
|
return False
|
|
231
236
|
|
|
@@ -242,12 +247,12 @@ class ExternalMCPService(BaseToolAdapter):
|
|
|
242
247
|
)
|
|
243
248
|
|
|
244
249
|
if pipx_check.returncode != 0:
|
|
245
|
-
print("✗ pipx is not installed")
|
|
246
|
-
print("Install pipx first: python -m pip install pipx")
|
|
250
|
+
print("✗ pipx is not installed", file=sys.stderr)
|
|
251
|
+
print("Install pipx first: python -m pip install pipx", file=sys.stderr)
|
|
247
252
|
self.logger.error("pipx not available for installation")
|
|
248
253
|
return False
|
|
249
254
|
|
|
250
|
-
print(f"\n📦 Installing {self.package_name} via pipx...")
|
|
255
|
+
print(f"\n📦 Installing {self.package_name} via pipx...", file=sys.stderr)
|
|
251
256
|
result = subprocess.run(
|
|
252
257
|
["pipx", "install", self.package_name],
|
|
253
258
|
capture_output=True,
|
|
@@ -257,26 +262,26 @@ class ExternalMCPService(BaseToolAdapter):
|
|
|
257
262
|
)
|
|
258
263
|
|
|
259
264
|
if result.returncode == 0:
|
|
260
|
-
print(f"✓ Successfully installed {self.package_name}")
|
|
265
|
+
print(f"✓ Successfully installed {self.package_name}", file=sys.stderr)
|
|
261
266
|
self.logger.info(f"Successfully installed {self.package_name} via pipx")
|
|
262
267
|
return True
|
|
263
268
|
|
|
264
269
|
error_msg = result.stderr.strip() if result.stderr else "Unknown error"
|
|
265
|
-
print(f"✗ Installation failed: {error_msg}")
|
|
270
|
+
print(f"✗ Installation failed: {error_msg}", file=sys.stderr)
|
|
266
271
|
self.logger.error(f"Failed to install {self.package_name}: {error_msg}")
|
|
267
272
|
return False
|
|
268
273
|
|
|
269
274
|
except FileNotFoundError:
|
|
270
|
-
print("✗ pipx command not found")
|
|
271
|
-
print("Install pipx first: python -m pip install pipx")
|
|
275
|
+
print("✗ pipx command not found", file=sys.stderr)
|
|
276
|
+
print("Install pipx first: python -m pip install pipx", file=sys.stderr)
|
|
272
277
|
self.logger.error("pipx command not found")
|
|
273
278
|
return False
|
|
274
279
|
except subprocess.TimeoutExpired:
|
|
275
|
-
print("✗ Installation timed out")
|
|
280
|
+
print("✗ Installation timed out", file=sys.stderr)
|
|
276
281
|
self.logger.error(f"Installation of {self.package_name} timed out")
|
|
277
282
|
return False
|
|
278
283
|
except Exception as e:
|
|
279
|
-
print(f"✗ Installation error: {e}")
|
|
284
|
+
print(f"✗ Installation error: {e}", file=sys.stderr)
|
|
280
285
|
self.logger.error(f"Error installing {self.package_name}: {e}")
|
|
281
286
|
return False
|
|
282
287
|
|
|
@@ -29,6 +29,7 @@ Example flow:
|
|
|
29
29
|
|
|
30
30
|
import logging
|
|
31
31
|
import re
|
|
32
|
+
import threading
|
|
32
33
|
from dataclasses import dataclass, field
|
|
33
34
|
from datetime import datetime, timezone
|
|
34
35
|
from typing import Dict, List, Optional, Tuple
|
|
@@ -536,6 +537,7 @@ class FailureTracker:
|
|
|
536
537
|
|
|
537
538
|
# Singleton instance for session-level tracking
|
|
538
539
|
_tracker_instance: Optional[FailureTracker] = None
|
|
540
|
+
_tracker_lock = threading.Lock()
|
|
539
541
|
|
|
540
542
|
|
|
541
543
|
def get_failure_tracker() -> FailureTracker:
|
|
@@ -544,13 +546,23 @@ def get_failure_tracker() -> FailureTracker:
|
|
|
544
546
|
WHY: Session-level tracking requires a singleton to maintain state
|
|
545
547
|
across multiple hook invocations during the same session.
|
|
546
548
|
|
|
549
|
+
Thread-safe implementation using double-checked locking pattern to
|
|
550
|
+
prevent race conditions during concurrent initialization.
|
|
551
|
+
|
|
547
552
|
Returns:
|
|
548
553
|
The FailureTracker singleton instance
|
|
549
554
|
"""
|
|
550
555
|
global _tracker_instance
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
556
|
+
|
|
557
|
+
# Fast path - check without lock
|
|
558
|
+
if _tracker_instance is not None:
|
|
559
|
+
return _tracker_instance
|
|
560
|
+
|
|
561
|
+
# Slow path - acquire lock and double-check
|
|
562
|
+
with _tracker_lock:
|
|
563
|
+
if _tracker_instance is None:
|
|
564
|
+
_tracker_instance = FailureTracker()
|
|
565
|
+
return _tracker_instance
|
|
554
566
|
|
|
555
567
|
|
|
556
568
|
def reset_failure_tracker() -> None:
|
|
@@ -558,6 +570,9 @@ def reset_failure_tracker() -> None:
|
|
|
558
570
|
|
|
559
571
|
WHY: Tests need to reset state between runs. Also useful for
|
|
560
572
|
explicitly starting a new tracking session.
|
|
573
|
+
|
|
574
|
+
Thread-safe implementation ensures proper cleanup.
|
|
561
575
|
"""
|
|
562
576
|
global _tracker_instance
|
|
563
|
-
|
|
577
|
+
with _tracker_lock:
|
|
578
|
+
_tracker_instance = None
|
|
@@ -6,12 +6,19 @@ Ensures a single session ID is generated and used across all components.
|
|
|
6
6
|
|
|
7
7
|
This service addresses race conditions and duplicate session ID generation
|
|
8
8
|
by providing a single source of truth for session identifiers.
|
|
9
|
+
|
|
10
|
+
Extended with:
|
|
11
|
+
- Token usage tracking and monitoring
|
|
12
|
+
- Resume log generation on session end
|
|
13
|
+
- Context metrics persistence
|
|
14
|
+
- Automatic resume log injection on session startup
|
|
9
15
|
"""
|
|
10
16
|
|
|
11
17
|
import os
|
|
12
18
|
from datetime import datetime, timezone
|
|
19
|
+
from pathlib import Path
|
|
13
20
|
from threading import Lock
|
|
14
|
-
from typing import Optional
|
|
21
|
+
from typing import Any, Dict, Optional
|
|
15
22
|
|
|
16
23
|
from claude_mpm.core.logging_utils import get_logger
|
|
17
24
|
|
|
@@ -64,6 +71,24 @@ class SessionManager:
|
|
|
64
71
|
self._session_id = self._generate_session_id()
|
|
65
72
|
self._session_start_time = datetime.now(timezone.utc)
|
|
66
73
|
|
|
74
|
+
# Token usage tracking
|
|
75
|
+
self._cumulative_tokens = 0
|
|
76
|
+
self._total_budget = 200000 # Default Claude Code budget
|
|
77
|
+
self._last_stop_reason: Optional[str] = None
|
|
78
|
+
|
|
79
|
+
# Context metrics storage
|
|
80
|
+
self._context_metrics: Dict[str, Any] = {
|
|
81
|
+
"total_budget": self._total_budget,
|
|
82
|
+
"used_tokens": 0,
|
|
83
|
+
"remaining_tokens": self._total_budget,
|
|
84
|
+
"percentage_used": 0.0,
|
|
85
|
+
"stop_reason": None,
|
|
86
|
+
"model": "claude-sonnet-4.5",
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
# Resume log reference (loaded on startup if exists)
|
|
90
|
+
self._resume_log_content: Optional[str] = None
|
|
91
|
+
|
|
67
92
|
# Mark as initialized
|
|
68
93
|
self.__class__._initialized = True
|
|
69
94
|
|
|
@@ -71,6 +96,9 @@ class SessionManager:
|
|
|
71
96
|
f"SessionManager initialized with session ID: {self._session_id}"
|
|
72
97
|
)
|
|
73
98
|
|
|
99
|
+
# Check for resume log from previous session
|
|
100
|
+
self._load_resume_log()
|
|
101
|
+
|
|
74
102
|
def _generate_session_id(self) -> str:
|
|
75
103
|
"""
|
|
76
104
|
Generate or retrieve a session ID.
|
|
@@ -134,6 +162,182 @@ class SessionManager:
|
|
|
134
162
|
f"Session ID already set to {session_id}, no change needed"
|
|
135
163
|
)
|
|
136
164
|
|
|
165
|
+
def update_token_usage(
|
|
166
|
+
self,
|
|
167
|
+
input_tokens: int = 0,
|
|
168
|
+
output_tokens: int = 0,
|
|
169
|
+
stop_reason: Optional[str] = None,
|
|
170
|
+
) -> Dict[str, Any]:
|
|
171
|
+
"""
|
|
172
|
+
Update cumulative token usage for the session.
|
|
173
|
+
|
|
174
|
+
Args:
|
|
175
|
+
input_tokens: Input tokens from latest API call
|
|
176
|
+
output_tokens: Output tokens from latest API call
|
|
177
|
+
stop_reason: Stop reason from Claude API
|
|
178
|
+
|
|
179
|
+
Returns:
|
|
180
|
+
Updated context metrics
|
|
181
|
+
"""
|
|
182
|
+
with self.__class__._lock:
|
|
183
|
+
# Update cumulative usage
|
|
184
|
+
tokens_used = input_tokens + output_tokens
|
|
185
|
+
self._cumulative_tokens += tokens_used
|
|
186
|
+
|
|
187
|
+
# Update stop reason if provided
|
|
188
|
+
if stop_reason:
|
|
189
|
+
self._last_stop_reason = stop_reason
|
|
190
|
+
|
|
191
|
+
# Calculate metrics
|
|
192
|
+
remaining = max(0, self._total_budget - self._cumulative_tokens)
|
|
193
|
+
percentage = (self._cumulative_tokens / self._total_budget) * 100
|
|
194
|
+
|
|
195
|
+
# Update context metrics
|
|
196
|
+
self._context_metrics = {
|
|
197
|
+
"total_budget": self._total_budget,
|
|
198
|
+
"used_tokens": self._cumulative_tokens,
|
|
199
|
+
"remaining_tokens": remaining,
|
|
200
|
+
"percentage_used": percentage,
|
|
201
|
+
"stop_reason": self._last_stop_reason,
|
|
202
|
+
"model": "claude-sonnet-4.5",
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
logger.debug(
|
|
206
|
+
f"Token usage updated: {self._cumulative_tokens}/{self._total_budget} "
|
|
207
|
+
f"({percentage:.1f}%) - Stop reason: {stop_reason}"
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
return self._context_metrics.copy()
|
|
211
|
+
|
|
212
|
+
def get_context_metrics(self) -> Dict[str, Any]:
|
|
213
|
+
"""
|
|
214
|
+
Get current context metrics.
|
|
215
|
+
|
|
216
|
+
Returns:
|
|
217
|
+
Dictionary containing token usage and context metrics
|
|
218
|
+
"""
|
|
219
|
+
with self.__class__._lock:
|
|
220
|
+
return self._context_metrics.copy()
|
|
221
|
+
|
|
222
|
+
def get_token_usage_percentage(self) -> float:
|
|
223
|
+
"""
|
|
224
|
+
Get current token usage as a percentage (0.0 to 1.0).
|
|
225
|
+
|
|
226
|
+
Returns:
|
|
227
|
+
Token usage percentage
|
|
228
|
+
"""
|
|
229
|
+
with self.__class__._lock:
|
|
230
|
+
return self._context_metrics["percentage_used"] / 100.0
|
|
231
|
+
|
|
232
|
+
def should_warn_context_limit(self, threshold: float = 0.70) -> bool:
|
|
233
|
+
"""
|
|
234
|
+
Check if context usage has reached warning threshold.
|
|
235
|
+
|
|
236
|
+
Args:
|
|
237
|
+
threshold: Warning threshold (0.0 to 1.0)
|
|
238
|
+
|
|
239
|
+
Returns:
|
|
240
|
+
True if threshold reached
|
|
241
|
+
"""
|
|
242
|
+
return self.get_token_usage_percentage() >= threshold
|
|
243
|
+
|
|
244
|
+
def _load_resume_log(self) -> None:
|
|
245
|
+
"""
|
|
246
|
+
Load resume log from previous session if it exists.
|
|
247
|
+
|
|
248
|
+
This is called during initialization to check for session continuity.
|
|
249
|
+
"""
|
|
250
|
+
try:
|
|
251
|
+
# Lazy import to avoid circular dependencies
|
|
252
|
+
from claude_mpm.services.infrastructure.resume_log_generator import (
|
|
253
|
+
ResumeLogGenerator,
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
generator = ResumeLogGenerator()
|
|
257
|
+
|
|
258
|
+
# Check if there's a resume log for this session
|
|
259
|
+
# (Could be from a previous interrupted session with same ID)
|
|
260
|
+
resume_content = generator.load_resume_log(self._session_id)
|
|
261
|
+
|
|
262
|
+
if resume_content:
|
|
263
|
+
self._resume_log_content = resume_content
|
|
264
|
+
logger.info(f"Loaded resume log for session {self._session_id}")
|
|
265
|
+
else:
|
|
266
|
+
logger.debug("No resume log found for current session")
|
|
267
|
+
|
|
268
|
+
except Exception as e:
|
|
269
|
+
logger.warning(f"Failed to load resume log: {e}")
|
|
270
|
+
# Non-critical error, continue without resume log
|
|
271
|
+
|
|
272
|
+
def get_resume_log_content(self) -> Optional[str]:
|
|
273
|
+
"""
|
|
274
|
+
Get resume log content if loaded.
|
|
275
|
+
|
|
276
|
+
Returns:
|
|
277
|
+
Resume log markdown content or None
|
|
278
|
+
"""
|
|
279
|
+
with self.__class__._lock:
|
|
280
|
+
return self._resume_log_content
|
|
281
|
+
|
|
282
|
+
def generate_resume_log(
|
|
283
|
+
self,
|
|
284
|
+
session_state: Optional[Dict[str, Any]] = None,
|
|
285
|
+
) -> Optional[Path]:
|
|
286
|
+
"""
|
|
287
|
+
Generate and save resume log for current session.
|
|
288
|
+
|
|
289
|
+
Args:
|
|
290
|
+
session_state: Optional session state data to include
|
|
291
|
+
|
|
292
|
+
Returns:
|
|
293
|
+
Path to saved resume log or None if generation failed
|
|
294
|
+
"""
|
|
295
|
+
try:
|
|
296
|
+
# Lazy import to avoid circular dependencies
|
|
297
|
+
from claude_mpm.models.resume_log import ContextMetrics, ResumeLog
|
|
298
|
+
from claude_mpm.services.infrastructure.resume_log_generator import (
|
|
299
|
+
ResumeLogGenerator,
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
generator = ResumeLogGenerator()
|
|
303
|
+
|
|
304
|
+
# Create context metrics from current state
|
|
305
|
+
context_metrics = ContextMetrics(
|
|
306
|
+
total_budget=self._total_budget,
|
|
307
|
+
used_tokens=self._cumulative_tokens,
|
|
308
|
+
remaining_tokens=self._context_metrics["remaining_tokens"],
|
|
309
|
+
percentage_used=self._context_metrics["percentage_used"],
|
|
310
|
+
stop_reason=self._last_stop_reason,
|
|
311
|
+
model=self._context_metrics["model"],
|
|
312
|
+
session_id=self._session_id,
|
|
313
|
+
)
|
|
314
|
+
|
|
315
|
+
if session_state:
|
|
316
|
+
# Generate from provided session state
|
|
317
|
+
resume_log = generator.generate_from_session_state(
|
|
318
|
+
session_id=self._session_id,
|
|
319
|
+
session_state=session_state,
|
|
320
|
+
stop_reason=self._last_stop_reason,
|
|
321
|
+
)
|
|
322
|
+
else:
|
|
323
|
+
# Create minimal resume log
|
|
324
|
+
resume_log = ResumeLog(
|
|
325
|
+
session_id=self._session_id,
|
|
326
|
+
context_metrics=context_metrics,
|
|
327
|
+
mission_summary="Session ended - resume log auto-generated.",
|
|
328
|
+
)
|
|
329
|
+
|
|
330
|
+
if resume_log:
|
|
331
|
+
file_path = generator.save_resume_log(resume_log)
|
|
332
|
+
logger.info(f"Resume log generated and saved: {file_path}")
|
|
333
|
+
return file_path
|
|
334
|
+
logger.warning("Resume log generation returned None")
|
|
335
|
+
return None
|
|
336
|
+
|
|
337
|
+
except Exception as e:
|
|
338
|
+
logger.error(f"Failed to generate resume log: {e}", exc_info=True)
|
|
339
|
+
return None
|
|
340
|
+
|
|
137
341
|
@classmethod
|
|
138
342
|
def reset(cls) -> None:
|
|
139
343
|
"""
|