hanzo-mcp 0.9.0__py3-none-any.whl → 0.9.2__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 hanzo-mcp might be problematic. Click here for more details.

Files changed (135) hide show
  1. hanzo_mcp/__init__.py +1 -1
  2. hanzo_mcp/analytics/posthog_analytics.py +14 -1
  3. hanzo_mcp/cli.py +108 -4
  4. hanzo_mcp/server.py +11 -0
  5. hanzo_mcp/tools/__init__.py +3 -16
  6. hanzo_mcp/tools/agent/__init__.py +5 -0
  7. hanzo_mcp/tools/agent/agent.py +5 -0
  8. hanzo_mcp/tools/agent/agent_tool.py +3 -17
  9. hanzo_mcp/tools/agent/agent_tool_v1_deprecated.py +623 -0
  10. hanzo_mcp/tools/agent/clarification_tool.py +7 -1
  11. hanzo_mcp/tools/agent/claude_desktop_auth.py +16 -6
  12. hanzo_mcp/tools/agent/cli_agent_base.py +5 -0
  13. hanzo_mcp/tools/agent/cli_tools.py +26 -0
  14. hanzo_mcp/tools/agent/code_auth_tool.py +5 -0
  15. hanzo_mcp/tools/agent/critic_tool.py +7 -1
  16. hanzo_mcp/tools/agent/iching_tool.py +5 -0
  17. hanzo_mcp/tools/agent/network_tool.py +5 -0
  18. hanzo_mcp/tools/agent/review_tool.py +7 -1
  19. hanzo_mcp/tools/agent/swarm_alias.py +5 -0
  20. hanzo_mcp/tools/agent/swarm_tool.py +701 -0
  21. hanzo_mcp/tools/agent/swarm_tool_v1_deprecated.py +554 -0
  22. hanzo_mcp/tools/agent/unified_cli_tools.py +5 -0
  23. hanzo_mcp/tools/common/auto_timeout.py +254 -0
  24. hanzo_mcp/tools/common/base.py +4 -0
  25. hanzo_mcp/tools/common/batch_tool.py +5 -0
  26. hanzo_mcp/tools/common/config_tool.py +5 -0
  27. hanzo_mcp/tools/common/critic_tool.py +5 -0
  28. hanzo_mcp/tools/common/paginated_base.py +4 -0
  29. hanzo_mcp/tools/common/permissions.py +38 -12
  30. hanzo_mcp/tools/common/personality.py +673 -980
  31. hanzo_mcp/tools/common/stats.py +5 -0
  32. hanzo_mcp/tools/common/thinking_tool.py +5 -0
  33. hanzo_mcp/tools/common/timeout_parser.py +103 -0
  34. hanzo_mcp/tools/common/tool_disable.py +5 -0
  35. hanzo_mcp/tools/common/tool_enable.py +5 -0
  36. hanzo_mcp/tools/common/tool_list.py +5 -0
  37. hanzo_mcp/tools/config/config_tool.py +5 -0
  38. hanzo_mcp/tools/config/mode_tool.py +5 -0
  39. hanzo_mcp/tools/database/graph.py +5 -0
  40. hanzo_mcp/tools/database/graph_add.py +5 -0
  41. hanzo_mcp/tools/database/graph_query.py +5 -0
  42. hanzo_mcp/tools/database/graph_remove.py +5 -0
  43. hanzo_mcp/tools/database/graph_search.py +5 -0
  44. hanzo_mcp/tools/database/graph_stats.py +5 -0
  45. hanzo_mcp/tools/database/sql.py +5 -0
  46. hanzo_mcp/tools/database/sql_query.py +2 -0
  47. hanzo_mcp/tools/database/sql_search.py +5 -0
  48. hanzo_mcp/tools/database/sql_stats.py +5 -0
  49. hanzo_mcp/tools/editor/neovim_command.py +5 -0
  50. hanzo_mcp/tools/editor/neovim_edit.py +7 -2
  51. hanzo_mcp/tools/editor/neovim_session.py +5 -0
  52. hanzo_mcp/tools/filesystem/__init__.py +23 -26
  53. hanzo_mcp/tools/filesystem/ast_tool.py +3 -4
  54. hanzo_mcp/tools/filesystem/base.py +2 -18
  55. hanzo_mcp/tools/filesystem/batch_search.py +825 -0
  56. hanzo_mcp/tools/filesystem/content_replace.py +5 -3
  57. hanzo_mcp/tools/filesystem/diff.py +5 -0
  58. hanzo_mcp/tools/filesystem/directory_tree.py +34 -281
  59. hanzo_mcp/tools/filesystem/directory_tree_paginated.py +345 -0
  60. hanzo_mcp/tools/filesystem/edit.py +6 -5
  61. hanzo_mcp/tools/filesystem/find.py +177 -311
  62. hanzo_mcp/tools/filesystem/find_files.py +370 -0
  63. hanzo_mcp/tools/filesystem/git_search.py +5 -3
  64. hanzo_mcp/tools/filesystem/grep.py +454 -0
  65. hanzo_mcp/tools/filesystem/multi_edit.py +6 -5
  66. hanzo_mcp/tools/filesystem/read.py +10 -9
  67. hanzo_mcp/tools/filesystem/rules_tool.py +6 -4
  68. hanzo_mcp/tools/filesystem/search_tool.py +728 -0
  69. hanzo_mcp/tools/filesystem/symbols_tool.py +510 -0
  70. hanzo_mcp/tools/filesystem/tree.py +273 -0
  71. hanzo_mcp/tools/filesystem/watch.py +6 -1
  72. hanzo_mcp/tools/filesystem/write.py +13 -7
  73. hanzo_mcp/tools/jupyter/jupyter.py +30 -2
  74. hanzo_mcp/tools/jupyter/notebook_edit.py +298 -0
  75. hanzo_mcp/tools/jupyter/notebook_read.py +148 -0
  76. hanzo_mcp/tools/llm/consensus_tool.py +8 -6
  77. hanzo_mcp/tools/llm/llm_manage.py +5 -0
  78. hanzo_mcp/tools/llm/llm_tool.py +2 -0
  79. hanzo_mcp/tools/llm/llm_unified.py +5 -0
  80. hanzo_mcp/tools/llm/provider_tools.py +5 -0
  81. hanzo_mcp/tools/lsp/lsp_tool.py +475 -622
  82. hanzo_mcp/tools/mcp/mcp_add.py +7 -2
  83. hanzo_mcp/tools/mcp/mcp_remove.py +15 -2
  84. hanzo_mcp/tools/mcp/mcp_stats.py +5 -0
  85. hanzo_mcp/tools/mcp/mcp_tool.py +5 -0
  86. hanzo_mcp/tools/memory/knowledge_tools.py +14 -0
  87. hanzo_mcp/tools/memory/memory_tools.py +17 -0
  88. hanzo_mcp/tools/search/find_tool.py +5 -3
  89. hanzo_mcp/tools/search/unified_search.py +3 -1
  90. hanzo_mcp/tools/shell/__init__.py +2 -14
  91. hanzo_mcp/tools/shell/base_process.py +4 -2
  92. hanzo_mcp/tools/shell/bash_tool.py +2 -0
  93. hanzo_mcp/tools/shell/command_executor.py +7 -7
  94. hanzo_mcp/tools/shell/logs.py +5 -0
  95. hanzo_mcp/tools/shell/npx.py +5 -0
  96. hanzo_mcp/tools/shell/npx_background.py +5 -0
  97. hanzo_mcp/tools/shell/npx_tool.py +5 -0
  98. hanzo_mcp/tools/shell/open.py +5 -0
  99. hanzo_mcp/tools/shell/pkill.py +5 -0
  100. hanzo_mcp/tools/shell/process_tool.py +5 -0
  101. hanzo_mcp/tools/shell/processes.py +5 -0
  102. hanzo_mcp/tools/shell/run_background.py +5 -0
  103. hanzo_mcp/tools/shell/run_command.py +2 -0
  104. hanzo_mcp/tools/shell/run_command_windows.py +5 -0
  105. hanzo_mcp/tools/shell/streaming_command.py +5 -0
  106. hanzo_mcp/tools/shell/uvx.py +5 -0
  107. hanzo_mcp/tools/shell/uvx_background.py +5 -0
  108. hanzo_mcp/tools/shell/uvx_tool.py +5 -0
  109. hanzo_mcp/tools/shell/zsh_tool.py +3 -0
  110. hanzo_mcp/tools/todo/todo.py +5 -0
  111. hanzo_mcp/tools/todo/todo_read.py +142 -0
  112. hanzo_mcp/tools/todo/todo_write.py +367 -0
  113. hanzo_mcp/tools/vector/__init__.py +42 -95
  114. hanzo_mcp/tools/vector/index_tool.py +5 -0
  115. hanzo_mcp/tools/vector/vector.py +5 -0
  116. hanzo_mcp/tools/vector/vector_index.py +5 -0
  117. hanzo_mcp/tools/vector/vector_search.py +5 -0
  118. {hanzo_mcp-0.9.0.dist-info → hanzo_mcp-0.9.2.dist-info}/METADATA +1 -1
  119. hanzo_mcp-0.9.2.dist-info/RECORD +195 -0
  120. hanzo_mcp/tools/common/path_utils.py +0 -34
  121. hanzo_mcp/tools/compiler/__init__.py +0 -8
  122. hanzo_mcp/tools/compiler/sandboxed_compiler.py +0 -681
  123. hanzo_mcp/tools/environment/__init__.py +0 -8
  124. hanzo_mcp/tools/environment/environment_detector.py +0 -594
  125. hanzo_mcp/tools/filesystem/search.py +0 -1160
  126. hanzo_mcp/tools/framework/__init__.py +0 -8
  127. hanzo_mcp/tools/framework/framework_modes.py +0 -714
  128. hanzo_mcp/tools/memory/conversation_memory.py +0 -636
  129. hanzo_mcp/tools/shell/run_tool.py +0 -56
  130. hanzo_mcp/tools/vector/node_tool.py +0 -538
  131. hanzo_mcp/tools/vector/unified_vector.py +0 -384
  132. hanzo_mcp-0.9.0.dist-info/RECORD +0 -191
  133. {hanzo_mcp-0.9.0.dist-info → hanzo_mcp-0.9.2.dist-info}/WHEEL +0 -0
  134. {hanzo_mcp-0.9.0.dist-info → hanzo_mcp-0.9.2.dist-info}/entry_points.txt +0 -0
  135. {hanzo_mcp-0.9.0.dist-info → hanzo_mcp-0.9.2.dist-info}/top_level.txt +0 -0
@@ -1,714 +0,0 @@
1
- """Framework-specific modes for dynamic tool loading.
2
-
3
- This module provides mode management for different development frameworks,
4
- allowing dynamic loading of framework-specific tools and configurations.
5
- """
6
-
7
- import json
8
- import logging
9
- from typing import Dict, List, Any, Optional, Set, Callable
10
- from dataclasses import dataclass, field
11
- from pathlib import Path
12
-
13
- from hanzo_mcp.types import MCPResourceDocument
14
- from hanzo_mcp.tools.common.base import BaseTool
15
-
16
-
17
- @dataclass
18
- class FrameworkMode:
19
- """Represents a framework-specific mode."""
20
- name: str
21
- description: str
22
- category: str # language, web, mobile, data, etc.
23
- tools: List[str] # MCP tools to enable
24
- aliases: List[str] = field(default_factory=list)
25
- environment: Dict[str, str] = field(default_factory=dict)
26
- snippets: Dict[str, str] = field(default_factory=dict) # Code snippets
27
- commands: Dict[str, List[str]] = field(default_factory=dict) # Common commands
28
- dependencies: List[str] = field(default_factory=list) # Required modes
29
-
30
-
31
- # Framework mode definitions
32
- FRAMEWORK_MODES = {
33
- # Python Frameworks
34
- "django": FrameworkMode(
35
- name="django",
36
- description="Django web framework mode",
37
- category="web",
38
- tools=[
39
- "run_command", "python", "pip_install",
40
- "django_manage", "django_shell", "django_migrate",
41
- "django_makemigrations", "django_test", "django_runserver"
42
- ],
43
- aliases=["dj"],
44
- environment={
45
- "DJANGO_SETTINGS_MODULE": "settings",
46
- "PYTHONPATH": ".",
47
- },
48
- snippets={
49
- "model": """from django.db import models
50
-
51
- class {ModelName}(models.Model):
52
- created_at = models.DateTimeField(auto_now_add=True)
53
- updated_at = models.DateTimeField(auto_now=True)
54
-
55
- class Meta:
56
- ordering = ['-created_at']""",
57
- "view": """from django.shortcuts import render
58
- from django.views import View
59
-
60
- class {ViewName}(View):
61
- def get(self, request):
62
- return render(request, 'template.html', {})""",
63
- },
64
- commands={
65
- "serve": ["python", "manage.py", "runserver"],
66
- "migrate": ["python", "manage.py", "migrate"],
67
- "test": ["python", "manage.py", "test"],
68
- },
69
- dependencies=["python"],
70
- ),
71
-
72
- "fastapi": FrameworkMode(
73
- name="fastapi",
74
- description="FastAPI web framework mode",
75
- category="web",
76
- tools=[
77
- "run_command", "python", "uvicorn",
78
- "fastapi_routes", "fastapi_swagger", "fastapi_test"
79
- ],
80
- aliases=["fa"],
81
- environment={
82
- "PYTHONPATH": ".",
83
- },
84
- snippets={
85
- "endpoint": """from fastapi import FastAPI, HTTPException
86
- from pydantic import BaseModel
87
-
88
- app = FastAPI()
89
-
90
- class {ModelName}(BaseModel):
91
- field: str
92
-
93
- @app.post("/{endpoint}")
94
- async def {function_name}(data: {ModelName}):
95
- return {"message": "success", "data": data}""",
96
- },
97
- commands={
98
- "serve": ["uvicorn", "main:app", "--reload"],
99
- "test": ["pytest", "-v"],
100
- },
101
- dependencies=["python"],
102
- ),
103
-
104
- "flask": FrameworkMode(
105
- name="flask",
106
- description="Flask web framework mode",
107
- category="web",
108
- tools=[
109
- "run_command", "python", "flask_run",
110
- "flask_shell", "flask_routes"
111
- ],
112
- aliases=["fl"],
113
- environment={
114
- "FLASK_APP": "app.py",
115
- "FLASK_ENV": "development",
116
- },
117
- snippets={
118
- "route": """from flask import Flask, request, jsonify
119
-
120
- app = Flask(__name__)
121
-
122
- @app.route('/{endpoint}', methods=['GET', 'POST'])
123
- def {function_name}():
124
- if request.method == 'POST':
125
- data = request.json
126
- return jsonify({"message": "success", "data": data})
127
- return jsonify({"message": "GET request"})""",
128
- },
129
- commands={
130
- "serve": ["flask", "run"],
131
- "shell": ["flask", "shell"],
132
- },
133
- dependencies=["python"],
134
- ),
135
-
136
- # JavaScript/TypeScript Frameworks
137
- "nextjs": FrameworkMode(
138
- name="nextjs",
139
- description="Next.js React framework mode",
140
- category="web",
141
- tools=[
142
- "npx", "npm_install", "next_dev", "next_build",
143
- "next_start", "next_lint", "react_component"
144
- ],
145
- aliases=["next"],
146
- environment={
147
- "NODE_ENV": "development",
148
- },
149
- snippets={
150
- "page": """export default function {PageName}() {
151
- return (
152
- <div>
153
- <h1>{PageName}</h1>
154
- </div>
155
- );
156
- }""",
157
- "api": """export default function handler(req, res) {
158
- if (req.method === 'POST') {
159
- const data = req.body;
160
- res.status(200).json({ message: 'Success', data });
161
- } else {
162
- res.status(200).json({ message: 'GET request' });
163
- }
164
- }""",
165
- },
166
- commands={
167
- "dev": ["npm", "run", "dev"],
168
- "build": ["npm", "run", "build"],
169
- "start": ["npm", "run", "start"],
170
- },
171
- dependencies=["javascript", "react"],
172
- ),
173
-
174
- "react": FrameworkMode(
175
- name="react",
176
- description="React framework mode",
177
- category="web",
178
- tools=[
179
- "npx", "npm_install", "react_component",
180
- "react_hook", "react_context"
181
- ],
182
- aliases=["r"],
183
- snippets={
184
- "component": """import React from 'react';
185
-
186
- export const {ComponentName} = () => {
187
- return (
188
- <div>
189
- <h1>{ComponentName}</h1>
190
- </div>
191
- );
192
- };""",
193
- "hook": """import { useState, useEffect } from 'react';
194
-
195
- export const use{HookName} = () => {
196
- const [data, setData] = useState(null);
197
-
198
- useEffect(() => {
199
- // Effect logic
200
- }, []);
201
-
202
- return { data };
203
- };""",
204
- },
205
- commands={
206
- "start": ["npm", "start"],
207
- "build": ["npm", "run", "build"],
208
- "test": ["npm", "test"],
209
- },
210
- dependencies=["javascript"],
211
- ),
212
-
213
- "vue": FrameworkMode(
214
- name="vue",
215
- description="Vue.js framework mode",
216
- category="web",
217
- tools=[
218
- "npx", "npm_install", "vue_component",
219
- "vue_store", "vue_router"
220
- ],
221
- aliases=["v"],
222
- snippets={
223
- "component": """<template>
224
- <div>
225
- <h1>{{ title }}</h1>
226
- </div>
227
- </template>
228
-
229
- <script>
230
- export default {
231
- name: '{ComponentName}',
232
- data() {
233
- return {
234
- title: '{ComponentName}'
235
- };
236
- }
237
- };
238
- </script>""",
239
- },
240
- commands={
241
- "serve": ["npm", "run", "serve"],
242
- "build": ["npm", "run", "build"],
243
- },
244
- dependencies=["javascript"],
245
- ),
246
-
247
- # Rust Frameworks
248
- "actix": FrameworkMode(
249
- name="actix",
250
- description="Actix web framework mode",
251
- category="web",
252
- tools=[
253
- "cargo_build", "cargo_run", "cargo_test",
254
- "actix_handler", "actix_middleware"
255
- ],
256
- aliases=["ax"],
257
- snippets={
258
- "handler": """use actix_web::{web, HttpResponse, Result};
259
- use serde::{Deserialize, Serialize};
260
-
261
- #[derive(Serialize, Deserialize)]
262
- struct {ModelName} {
263
- field: String,
264
- }
265
-
266
- pub async fn {handler_name}(data: web::Json<{ModelName}>) -> Result<HttpResponse> {
267
- Ok(HttpResponse::Ok().json(&data.into_inner()))
268
- }""",
269
- },
270
- commands={
271
- "run": ["cargo", "run"],
272
- "build": ["cargo", "build", "--release"],
273
- "test": ["cargo", "test"],
274
- },
275
- dependencies=["rust"],
276
- ),
277
-
278
- # Go Frameworks
279
- "gin": FrameworkMode(
280
- name="gin",
281
- description="Gin web framework mode",
282
- category="web",
283
- tools=[
284
- "go_build", "go_run", "go_test",
285
- "gin_handler", "gin_middleware"
286
- ],
287
- aliases=["g"],
288
- snippets={
289
- "handler": """func {HandlerName}(c *gin.Context) {
290
- var data struct {
291
- Field string `json:"field"`
292
- }
293
-
294
- if err := c.ShouldBindJSON(&data); err != nil {
295
- c.JSON(400, gin.H{"error": err.Error()})
296
- return
297
- }
298
-
299
- c.JSON(200, gin.H{
300
- "message": "success",
301
- "data": data,
302
- })
303
- }""",
304
- },
305
- commands={
306
- "run": ["go", "run", "."],
307
- "build": ["go", "build"],
308
- "test": ["go", "test", "./..."],
309
- },
310
- dependencies=["go"],
311
- ),
312
-
313
- # Language Modes (Base modes)
314
- "python": FrameworkMode(
315
- name="python",
316
- description="Python language mode",
317
- category="language",
318
- tools=[
319
- "run_command", "python", "pip", "pytest",
320
- "black", "ruff", "mypy", "uvx"
321
- ],
322
- environment={
323
- "PYTHONPATH": ".",
324
- },
325
- commands={
326
- "run": ["python"],
327
- "test": ["pytest"],
328
- "format": ["black", "."],
329
- "lint": ["ruff", "check", "."],
330
- },
331
- ),
332
-
333
- "javascript": FrameworkMode(
334
- name="javascript",
335
- description="JavaScript/TypeScript language mode",
336
- category="language",
337
- tools=[
338
- "npx", "npm", "node", "tsc",
339
- "eslint", "prettier", "jest"
340
- ],
341
- commands={
342
- "run": ["node"],
343
- "test": ["npm", "test"],
344
- "format": ["prettier", "--write", "."],
345
- "lint": ["eslint", "."],
346
- },
347
- ),
348
-
349
- "rust": FrameworkMode(
350
- name="rust",
351
- description="Rust language mode",
352
- category="language",
353
- tools=[
354
- "cargo", "rustc", "rustfmt", "clippy"
355
- ],
356
- commands={
357
- "build": ["cargo", "build"],
358
- "run": ["cargo", "run"],
359
- "test": ["cargo", "test"],
360
- "format": ["cargo", "fmt"],
361
- "lint": ["cargo", "clippy"],
362
- },
363
- ),
364
-
365
- "go": FrameworkMode(
366
- name="go",
367
- description="Go language mode",
368
- category="language",
369
- tools=[
370
- "go", "gofmt", "golint", "gotest"
371
- ],
372
- commands={
373
- "build": ["go", "build"],
374
- "run": ["go", "run", "."],
375
- "test": ["go", "test", "./..."],
376
- "format": ["go", "fmt", "./..."],
377
- },
378
- ),
379
- }
380
-
381
-
382
- class FrameworkModeManager(BaseTool):
383
- """Manager for framework-specific modes."""
384
-
385
- name = "framework_mode"
386
- description = """Manage framework-specific development modes.
387
-
388
- Actions:
389
- - enable: Enable a framework mode
390
- - disable: Disable a framework mode
391
- - list: List available modes
392
- - current: Show currently active modes
393
- - info: Get information about a mode
394
- - snippet: Get code snippet for framework
395
- - command: Get command for framework task
396
-
397
- Modes automatically load framework-specific tools and configurations.
398
- """
399
-
400
- def __init__(self):
401
- super().__init__()
402
- self.logger = logging.getLogger(__name__)
403
- self.active_modes: Set[str] = set()
404
- self.mode_stack: List[str] = [] # For mode history
405
-
406
- def _resolve_dependencies(self, mode_name: str) -> List[str]:
407
- """Resolve mode dependencies."""
408
- if mode_name not in FRAMEWORK_MODES:
409
- return []
410
-
411
- mode = FRAMEWORK_MODES[mode_name]
412
- dependencies = []
413
-
414
- # Add dependencies recursively
415
- for dep in mode.dependencies:
416
- dependencies.extend(self._resolve_dependencies(dep))
417
- dependencies.append(dep)
418
-
419
- return dependencies
420
-
421
- def enable_mode(self, mode_name: str) -> Dict[str, Any]:
422
- """Enable a framework mode."""
423
- # Check if mode exists
424
- if mode_name not in FRAMEWORK_MODES:
425
- # Check aliases
426
- for name, mode in FRAMEWORK_MODES.items():
427
- if mode_name in mode.aliases:
428
- mode_name = name
429
- break
430
- else:
431
- return {
432
- "success": False,
433
- "error": f"Unknown mode: {mode_name}",
434
- "available_modes": list(FRAMEWORK_MODES.keys()),
435
- }
436
-
437
- mode = FRAMEWORK_MODES[mode_name]
438
-
439
- # Resolve dependencies
440
- dependencies = self._resolve_dependencies(mode_name)
441
-
442
- # Enable dependencies first
443
- for dep in dependencies:
444
- if dep not in self.active_modes:
445
- self.active_modes.add(dep)
446
- self.logger.info(f"Enabled dependency mode: {dep}")
447
-
448
- # Enable the mode
449
- self.active_modes.add(mode_name)
450
- self.mode_stack.append(mode_name)
451
-
452
- # Get all tools to enable
453
- tools = set(mode.tools)
454
- for dep in dependencies:
455
- tools.update(FRAMEWORK_MODES[dep].tools)
456
-
457
- return {
458
- "success": True,
459
- "mode": mode_name,
460
- "description": mode.description,
461
- "category": mode.category,
462
- "enabled_tools": sorted(tools),
463
- "dependencies_enabled": dependencies,
464
- "environment": mode.environment,
465
- "active_modes": sorted(self.active_modes),
466
- }
467
-
468
- def disable_mode(self, mode_name: str) -> Dict[str, Any]:
469
- """Disable a framework mode."""
470
- if mode_name not in self.active_modes:
471
- return {
472
- "success": False,
473
- "error": f"Mode not active: {mode_name}",
474
- "active_modes": sorted(self.active_modes),
475
- }
476
-
477
- # Remove from active modes
478
- self.active_modes.discard(mode_name)
479
-
480
- # Remove from stack
481
- if mode_name in self.mode_stack:
482
- self.mode_stack.remove(mode_name)
483
-
484
- # Check if any active modes depend on this
485
- dependent_modes = []
486
- for active in self.active_modes.copy():
487
- mode = FRAMEWORK_MODES[active]
488
- if mode_name in mode.dependencies:
489
- dependent_modes.append(active)
490
- self.active_modes.discard(active)
491
-
492
- return {
493
- "success": True,
494
- "mode": mode_name,
495
- "disabled": True,
496
- "dependent_modes_disabled": dependent_modes,
497
- "active_modes": sorted(self.active_modes),
498
- }
499
-
500
- def get_snippet(self, mode_name: str, snippet_name: str) -> Optional[str]:
501
- """Get code snippet for framework."""
502
- if mode_name not in FRAMEWORK_MODES:
503
- return None
504
-
505
- mode = FRAMEWORK_MODES[mode_name]
506
- return mode.snippets.get(snippet_name)
507
-
508
- def get_command(self, mode_name: str, command_name: str) -> Optional[List[str]]:
509
- """Get command for framework task."""
510
- if mode_name not in FRAMEWORK_MODES:
511
- return None
512
-
513
- mode = FRAMEWORK_MODES[mode_name]
514
- return mode.commands.get(command_name)
515
-
516
- def list_modes(self, category: Optional[str] = None) -> List[Dict[str, Any]]:
517
- """List available modes."""
518
- modes = []
519
-
520
- for name, mode in FRAMEWORK_MODES.items():
521
- if category and mode.category != category:
522
- continue
523
-
524
- modes.append({
525
- "name": name,
526
- "description": mode.description,
527
- "category": mode.category,
528
- "aliases": mode.aliases,
529
- "active": name in self.active_modes,
530
- "tools_count": len(mode.tools),
531
- "has_snippets": bool(mode.snippets),
532
- "has_commands": bool(mode.commands),
533
- })
534
-
535
- return modes
536
-
537
- def get_mode_info(self, mode_name: str) -> Optional[Dict[str, Any]]:
538
- """Get detailed information about a mode."""
539
- if mode_name not in FRAMEWORK_MODES:
540
- return None
541
-
542
- mode = FRAMEWORK_MODES[mode_name]
543
-
544
- return {
545
- "name": mode.name,
546
- "description": mode.description,
547
- "category": mode.category,
548
- "aliases": mode.aliases,
549
- "active": mode.name in self.active_modes,
550
- "tools": mode.tools,
551
- "environment": mode.environment,
552
- "snippets": list(mode.snippets.keys()),
553
- "commands": list(mode.commands.keys()),
554
- "dependencies": mode.dependencies,
555
- }
556
-
557
- def export_configuration(self) -> Dict[str, Any]:
558
- """Export current mode configuration."""
559
- config = {
560
- "active_modes": sorted(self.active_modes),
561
- "mode_stack": self.mode_stack,
562
- "enabled_tools": set(),
563
- "environment": {},
564
- "commands": {},
565
- }
566
-
567
- # Collect all enabled tools and environment
568
- for mode_name in self.active_modes:
569
- mode = FRAMEWORK_MODES[mode_name]
570
- config["enabled_tools"].update(mode.tools)
571
- config["environment"].update(mode.environment)
572
-
573
- # Add commands with mode prefix
574
- for cmd_name, cmd in mode.commands.items():
575
- config["commands"][f"{mode_name}:{cmd_name}"] = cmd
576
-
577
- config["enabled_tools"] = sorted(config["enabled_tools"])
578
-
579
- return config
580
-
581
- async def run(
582
- self,
583
- action: str,
584
- mode: Optional[str] = None,
585
- snippet: Optional[str] = None,
586
- command: Optional[str] = None,
587
- category: Optional[str] = None,
588
- **kwargs,
589
- ) -> MCPResourceDocument:
590
- """Execute framework mode action."""
591
-
592
- if action == "enable":
593
- if not mode:
594
- return MCPResourceDocument(data={"error": "Mode name required"})
595
-
596
- result = self.enable_mode(mode)
597
- return MCPResourceDocument(data=result)
598
-
599
- elif action == "disable":
600
- if not mode:
601
- return MCPResourceDocument(data={"error": "Mode name required"})
602
-
603
- result = self.disable_mode(mode)
604
- return MCPResourceDocument(data=result)
605
-
606
- elif action == "list":
607
- modes = self.list_modes(category)
608
-
609
- return MCPResourceDocument(
610
- data={
611
- "modes": modes,
612
- "total": len(modes),
613
- "active_count": len(self.active_modes),
614
- "categories": list(set(m["category"] for m in modes)),
615
- }
616
- )
617
-
618
- elif action == "current":
619
- config = self.export_configuration()
620
-
621
- return MCPResourceDocument(data=config)
622
-
623
- elif action == "info":
624
- if not mode:
625
- return MCPResourceDocument(data={"error": "Mode name required"})
626
-
627
- info = self.get_mode_info(mode)
628
-
629
- if info:
630
- return MCPResourceDocument(data=info)
631
- else:
632
- return MCPResourceDocument(
633
- data={
634
- "error": f"Unknown mode: {mode}",
635
- "available_modes": list(FRAMEWORK_MODES.keys()),
636
- }
637
- )
638
-
639
- elif action == "snippet":
640
- if not mode or not snippet:
641
- return MCPResourceDocument(
642
- data={"error": "Mode and snippet name required"}
643
- )
644
-
645
- code = self.get_snippet(mode, snippet)
646
-
647
- if code:
648
- return MCPResourceDocument(
649
- data={
650
- "mode": mode,
651
- "snippet": snippet,
652
- "code": code,
653
- }
654
- )
655
- else:
656
- mode_obj = FRAMEWORK_MODES.get(mode)
657
- available = list(mode_obj.snippets.keys()) if mode_obj else []
658
-
659
- return MCPResourceDocument(
660
- data={
661
- "error": f"Snippet '{snippet}' not found for mode '{mode}'",
662
- "available_snippets": available,
663
- }
664
- )
665
-
666
- elif action == "command":
667
- if not mode or not command:
668
- return MCPResourceDocument(
669
- data={"error": "Mode and command name required"}
670
- )
671
-
672
- cmd = self.get_command(mode, command)
673
-
674
- if cmd:
675
- return MCPResourceDocument(
676
- data={
677
- "mode": mode,
678
- "command": command,
679
- "cmd": cmd,
680
- "executable": " ".join(cmd),
681
- }
682
- )
683
- else:
684
- mode_obj = FRAMEWORK_MODES.get(mode)
685
- available = list(mode_obj.commands.keys()) if mode_obj else []
686
-
687
- return MCPResourceDocument(
688
- data={
689
- "error": f"Command '{command}' not found for mode '{mode}'",
690
- "available_commands": available,
691
- }
692
- )
693
-
694
- else:
695
- return MCPResourceDocument(
696
- data={
697
- "error": f"Unknown action: {action}",
698
- "valid_actions": [
699
- "enable", "disable", "list", "current",
700
- "info", "snippet", "command"
701
- ],
702
- }
703
- )
704
-
705
- async def call(self, **kwargs) -> str:
706
- """Tool interface for MCP."""
707
- result = await self.run(**kwargs)
708
- return result.to_json_string()
709
-
710
-
711
- # Factory function
712
- def create_framework_mode_manager():
713
- """Create framework mode manager."""
714
- return FrameworkModeManager()