devsync 0.5.5__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.
Files changed (84) hide show
  1. aiconfigkit/__init__.py +0 -0
  2. aiconfigkit/__main__.py +6 -0
  3. aiconfigkit/ai_tools/__init__.py +0 -0
  4. aiconfigkit/ai_tools/base.py +236 -0
  5. aiconfigkit/ai_tools/capability_registry.py +262 -0
  6. aiconfigkit/ai_tools/claude.py +91 -0
  7. aiconfigkit/ai_tools/claude_desktop.py +97 -0
  8. aiconfigkit/ai_tools/cline.py +92 -0
  9. aiconfigkit/ai_tools/copilot.py +92 -0
  10. aiconfigkit/ai_tools/cursor.py +109 -0
  11. aiconfigkit/ai_tools/detector.py +169 -0
  12. aiconfigkit/ai_tools/kiro.py +85 -0
  13. aiconfigkit/ai_tools/mcp_syncer.py +291 -0
  14. aiconfigkit/ai_tools/roo.py +110 -0
  15. aiconfigkit/ai_tools/translator.py +390 -0
  16. aiconfigkit/ai_tools/winsurf.py +102 -0
  17. aiconfigkit/cli/__init__.py +0 -0
  18. aiconfigkit/cli/delete.py +118 -0
  19. aiconfigkit/cli/download.py +274 -0
  20. aiconfigkit/cli/install.py +237 -0
  21. aiconfigkit/cli/install_new.py +937 -0
  22. aiconfigkit/cli/list.py +275 -0
  23. aiconfigkit/cli/main.py +454 -0
  24. aiconfigkit/cli/mcp_configure.py +232 -0
  25. aiconfigkit/cli/mcp_install.py +166 -0
  26. aiconfigkit/cli/mcp_sync.py +165 -0
  27. aiconfigkit/cli/package.py +383 -0
  28. aiconfigkit/cli/package_create.py +323 -0
  29. aiconfigkit/cli/package_install.py +472 -0
  30. aiconfigkit/cli/template.py +19 -0
  31. aiconfigkit/cli/template_backup.py +261 -0
  32. aiconfigkit/cli/template_init.py +499 -0
  33. aiconfigkit/cli/template_install.py +261 -0
  34. aiconfigkit/cli/template_list.py +172 -0
  35. aiconfigkit/cli/template_uninstall.py +146 -0
  36. aiconfigkit/cli/template_update.py +225 -0
  37. aiconfigkit/cli/template_validate.py +234 -0
  38. aiconfigkit/cli/tools.py +47 -0
  39. aiconfigkit/cli/uninstall.py +125 -0
  40. aiconfigkit/cli/update.py +309 -0
  41. aiconfigkit/core/__init__.py +0 -0
  42. aiconfigkit/core/checksum.py +211 -0
  43. aiconfigkit/core/component_detector.py +905 -0
  44. aiconfigkit/core/conflict_resolution.py +329 -0
  45. aiconfigkit/core/git_operations.py +539 -0
  46. aiconfigkit/core/mcp/__init__.py +1 -0
  47. aiconfigkit/core/mcp/credentials.py +279 -0
  48. aiconfigkit/core/mcp/manager.py +308 -0
  49. aiconfigkit/core/mcp/set_manager.py +1 -0
  50. aiconfigkit/core/mcp/validator.py +1 -0
  51. aiconfigkit/core/models.py +1661 -0
  52. aiconfigkit/core/package_creator.py +743 -0
  53. aiconfigkit/core/package_manifest.py +248 -0
  54. aiconfigkit/core/repository.py +298 -0
  55. aiconfigkit/core/secret_detector.py +438 -0
  56. aiconfigkit/core/template_manifest.py +283 -0
  57. aiconfigkit/core/version.py +201 -0
  58. aiconfigkit/storage/__init__.py +0 -0
  59. aiconfigkit/storage/library.py +429 -0
  60. aiconfigkit/storage/mcp_tracker.py +1 -0
  61. aiconfigkit/storage/package_tracker.py +234 -0
  62. aiconfigkit/storage/template_library.py +229 -0
  63. aiconfigkit/storage/template_tracker.py +296 -0
  64. aiconfigkit/storage/tracker.py +416 -0
  65. aiconfigkit/tui/__init__.py +5 -0
  66. aiconfigkit/tui/installer.py +511 -0
  67. aiconfigkit/utils/__init__.py +0 -0
  68. aiconfigkit/utils/atomic_write.py +90 -0
  69. aiconfigkit/utils/backup.py +169 -0
  70. aiconfigkit/utils/dotenv.py +128 -0
  71. aiconfigkit/utils/git_helpers.py +187 -0
  72. aiconfigkit/utils/logging.py +60 -0
  73. aiconfigkit/utils/namespace.py +134 -0
  74. aiconfigkit/utils/paths.py +205 -0
  75. aiconfigkit/utils/project.py +109 -0
  76. aiconfigkit/utils/streaming.py +216 -0
  77. aiconfigkit/utils/ui.py +194 -0
  78. aiconfigkit/utils/validation.py +187 -0
  79. devsync-0.5.5.dist-info/LICENSE +21 -0
  80. devsync-0.5.5.dist-info/METADATA +477 -0
  81. devsync-0.5.5.dist-info/RECORD +84 -0
  82. devsync-0.5.5.dist-info/WHEEL +5 -0
  83. devsync-0.5.5.dist-info/entry_points.txt +2 -0
  84. devsync-0.5.5.dist-info/top_level.txt +1 -0
@@ -0,0 +1,291 @@
1
+ """Cross-tool MCP server synchronization orchestration."""
2
+
3
+ import json
4
+ import logging
5
+ from pathlib import Path
6
+ from typing import Any, Optional
7
+
8
+ from aiconfigkit.ai_tools.base import AITool
9
+ from aiconfigkit.ai_tools.detector import AIToolDetector
10
+ from aiconfigkit.core.mcp.credentials import CredentialManager
11
+ from aiconfigkit.core.mcp.manager import MCPManager
12
+ from aiconfigkit.core.models import EnvironmentConfig, InstallationScope, MCPServer, MCPTemplate
13
+ from aiconfigkit.utils.atomic_write import atomic_write
14
+
15
+ logger = logging.getLogger(__name__)
16
+
17
+
18
+ class MCPSyncResult:
19
+ """Result of MCP synchronization operation."""
20
+
21
+ def __init__(self) -> None:
22
+ """Initialize sync result."""
23
+ self.synced_tools: list[str] = []
24
+ self.skipped_tools: list[tuple[str, str]] = [] # (tool_name, reason)
25
+ self.synced_servers: list[str] = []
26
+ self.skipped_servers: list[tuple[str, str]] = [] # (server_name, reason)
27
+
28
+ def add_synced_tool(self, tool_name: str) -> None:
29
+ """Mark tool as successfully synced."""
30
+ self.synced_tools.append(tool_name)
31
+
32
+ def add_skipped_tool(self, tool_name: str, reason: str) -> None:
33
+ """Mark tool as skipped with reason."""
34
+ self.skipped_tools.append((tool_name, reason))
35
+
36
+ def add_synced_server(self, server_name: str) -> None:
37
+ """Mark server as successfully synced."""
38
+ if server_name not in self.synced_servers:
39
+ self.synced_servers.append(server_name)
40
+
41
+ def add_skipped_server(self, server_name: str, reason: str) -> None:
42
+ """Mark server as skipped with reason."""
43
+ if not any(s[0] == server_name for s in self.skipped_servers):
44
+ self.skipped_servers.append((server_name, reason))
45
+
46
+ @property
47
+ def success(self) -> bool:
48
+ """Check if any tools were successfully synced."""
49
+ return len(self.synced_tools) > 0
50
+
51
+
52
+ class MCPSyncer:
53
+ """Orchestrates MCP server synchronization to AI tools."""
54
+
55
+ def __init__(
56
+ self,
57
+ library_root: Optional[Path] = None,
58
+ project_root: Optional[Path] = None,
59
+ ):
60
+ """
61
+ Initialize MCP syncer.
62
+
63
+ Args:
64
+ library_root: Root directory for MCP library (defaults to ~/.instructionkit/library/)
65
+ project_root: Project root directory (defaults to current directory)
66
+ """
67
+ self.library_root = library_root or Path.home() / ".instructionkit" / "library"
68
+ self.project_root = project_root or Path.cwd()
69
+
70
+ self.mcp_manager = MCPManager(self.library_root)
71
+ self.cred_manager = CredentialManager(self.project_root)
72
+ self.tool_detector = AIToolDetector()
73
+
74
+ def sync_all(
75
+ self,
76
+ tool_names: Optional[list[str]] = None,
77
+ scope: InstallationScope = InstallationScope.PROJECT,
78
+ create_backup: bool = True,
79
+ dry_run: bool = False,
80
+ ) -> MCPSyncResult:
81
+ """
82
+ Sync all MCP servers to AI tools.
83
+
84
+ Args:
85
+ tool_names: List of tool names to sync to (None = all detected tools)
86
+ scope: Scope to load configurations from (PROJECT, GLOBAL, or merge)
87
+ create_backup: Create backup of config files before modifying
88
+ dry_run: Don't actually write configs, just report what would be done
89
+
90
+ Returns:
91
+ MCPSyncResult with sync status
92
+ """
93
+ result = MCPSyncResult()
94
+
95
+ # Load all installed MCP templates
96
+ templates = self._load_templates(scope)
97
+
98
+ if not templates:
99
+ logger.info("No MCP templates installed")
100
+ return result
101
+
102
+ # Collect all MCP servers from templates
103
+ all_servers: list[MCPServer] = []
104
+ for template in templates:
105
+ all_servers.extend(template.servers)
106
+
107
+ if not all_servers:
108
+ logger.info("No MCP servers found in installed templates")
109
+ return result
110
+
111
+ # Load environment configuration
112
+ env_config = self.cred_manager.merge_scopes()
113
+
114
+ # Validate servers have required credentials
115
+ validated_servers = []
116
+ for server in all_servers:
117
+ is_valid, missing = self._validate_server_credentials(server, env_config)
118
+ if is_valid:
119
+ validated_servers.append(server)
120
+ result.add_synced_server(server.get_fully_qualified_name())
121
+ else:
122
+ reason = f"Missing credentials: {', '.join(missing)}"
123
+ result.add_skipped_server(server.get_fully_qualified_name(), reason)
124
+ logger.warning(f"Skipping {server.get_fully_qualified_name()}: {reason}")
125
+
126
+ if not validated_servers:
127
+ logger.warning("No servers have complete credentials configured")
128
+ return result
129
+
130
+ # Resolve environment variables in server configurations
131
+ resolved_servers = self._resolve_env_vars(validated_servers, env_config)
132
+
133
+ # Get tools to sync to
134
+ if tool_names and "all" not in tool_names:
135
+ tools = []
136
+ for name in tool_names:
137
+ tool = self.tool_detector.get_tool_by_name(name)
138
+ if tool:
139
+ tools.append(tool)
140
+ else:
141
+ result.add_skipped_tool(name, "Unknown tool")
142
+ else:
143
+ tools = self.tool_detector.detect_installed_tools()
144
+
145
+ if not tools:
146
+ logger.warning("No AI tools detected")
147
+ return result
148
+
149
+ # Sync to each tool
150
+ for tool in tools:
151
+ tool_name = tool.tool_type.value
152
+ try:
153
+ logger.info(f"Syncing MCP servers to {tool.tool_name}")
154
+
155
+ if dry_run:
156
+ logger.info(f"[DRY RUN] Would sync {len(resolved_servers)} servers to {tool.tool_name}")
157
+ result.add_synced_tool(tool_name)
158
+ else:
159
+ # Check if tool has MCP config support
160
+ if not hasattr(tool, "get_mcp_config_path"):
161
+ result.add_skipped_tool(tool_name, "MCP config not supported")
162
+ logger.warning(f"{tool.tool_name} does not support MCP configuration sync")
163
+ continue
164
+
165
+ # Sync servers to tool
166
+ self._sync_to_tool(tool, resolved_servers, create_backup)
167
+ result.add_synced_tool(tool_name)
168
+ logger.info(f"Successfully synced to {tool.tool_name}")
169
+
170
+ except Exception as e:
171
+ result.add_skipped_tool(tool_name, str(e))
172
+ logger.error(f"Failed to sync to {tool.tool_name}: {e}")
173
+
174
+ return result
175
+
176
+ def _load_templates(self, scope: InstallationScope) -> list[MCPTemplate]:
177
+ """
178
+ Load all installed MCP templates.
179
+
180
+ Args:
181
+ scope: Scope to load from
182
+
183
+ Returns:
184
+ List of MCPTemplate objects
185
+ """
186
+ templates = []
187
+
188
+ # Load project templates
189
+ if scope in (InstallationScope.PROJECT, InstallationScope.PROJECT):
190
+ templates.extend(self.mcp_manager.list_templates(InstallationScope.PROJECT))
191
+
192
+ # Load global templates
193
+ if scope == InstallationScope.GLOBAL:
194
+ templates.extend(self.mcp_manager.list_templates(InstallationScope.GLOBAL))
195
+
196
+ return templates
197
+
198
+ def _validate_server_credentials(self, server: MCPServer, env_config: EnvironmentConfig) -> tuple[bool, list[str]]:
199
+ """
200
+ Validate that server has all required credentials.
201
+
202
+ Args:
203
+ server: MCP server to validate
204
+ env_config: Environment configuration
205
+
206
+ Returns:
207
+ Tuple of (is_valid, missing_vars)
208
+ """
209
+ required_vars = server.get_required_env_vars()
210
+ missing_vars = []
211
+
212
+ for var_name in required_vars:
213
+ if not env_config.has(var_name):
214
+ missing_vars.append(var_name)
215
+
216
+ return (len(missing_vars) == 0, missing_vars)
217
+
218
+ def _resolve_env_vars(self, servers: list[MCPServer], env_config: EnvironmentConfig) -> list[dict[str, Any]]:
219
+ """
220
+ Resolve environment variables in server configurations.
221
+
222
+ Args:
223
+ servers: List of MCP servers
224
+ env_config: Environment configuration
225
+
226
+ Returns:
227
+ List of resolved server configurations as dicts
228
+ """
229
+ resolved = []
230
+
231
+ for server in servers:
232
+ # Build server config dict
233
+ config: dict[str, Any] = {
234
+ "command": server.command,
235
+ "args": server.args,
236
+ }
237
+
238
+ # Resolve environment variables
239
+ if server.env:
240
+ resolved_env = {}
241
+ for var_name, var_value in server.env.items():
242
+ # Get value from env_config
243
+ actual_value = env_config.get(var_name)
244
+ if actual_value:
245
+ resolved_env[var_name] = actual_value
246
+
247
+ if resolved_env:
248
+ config["env"] = resolved_env
249
+
250
+ # Add to resolved list with server name
251
+ resolved.append(
252
+ {
253
+ "name": server.get_fully_qualified_name(),
254
+ "config": config,
255
+ }
256
+ )
257
+
258
+ return resolved
259
+
260
+ def _sync_to_tool(self, tool: AITool, servers: list[dict[str, Any]], create_backup: bool) -> None:
261
+ """
262
+ Sync MCP servers to a specific AI tool.
263
+
264
+ Args:
265
+ tool: AITool instance
266
+ servers: List of resolved server configurations
267
+ create_backup: Create backup before modifying
268
+
269
+ Raises:
270
+ RuntimeError: If sync fails
271
+ """
272
+ config_path = tool.get_mcp_config_path()
273
+
274
+ # Load existing config if it exists
275
+ if config_path.exists():
276
+ with open(config_path, "r", encoding="utf-8") as f:
277
+ config = json.load(f)
278
+ else:
279
+ config = {}
280
+
281
+ # Ensure mcpServers section exists
282
+ if "mcpServers" not in config:
283
+ config["mcpServers"] = {}
284
+
285
+ # Update mcpServers with our servers
286
+ for server in servers:
287
+ config["mcpServers"][server["name"]] = server["config"]
288
+
289
+ # Write config atomically
290
+ with atomic_write(config_path, create_backup=create_backup) as f:
291
+ json.dump(config, f, indent=2)
@@ -0,0 +1,110 @@
1
+ """Roo Code AI tool integration."""
2
+
3
+ from pathlib import Path
4
+
5
+ from aiconfigkit.ai_tools.base import AITool
6
+ from aiconfigkit.core.models import AIToolType
7
+ from aiconfigkit.utils.paths import get_roo_config_dir
8
+
9
+
10
+ class RooTool(AITool):
11
+ """Integration for Roo Code AI coding tool (VS Code extension).
12
+
13
+ Roo Code (formerly Roo Cline) uses .roo/rules/ directory at the project root
14
+ for AI instructions. Files are .md (Markdown) and are read recursively in
15
+ alphabetical order by filename. Numeric prefixes control load order.
16
+
17
+ Mode-specific rules can be placed in .roo/rules-{mode-slug}/ directories.
18
+ Project-level MCP config is at .roo/mcp.json.
19
+
20
+ Detection is based on the VS Code extension rooveterinaryinc.roo-cline
21
+ globalStorage directory.
22
+ """
23
+
24
+ @property
25
+ def tool_type(self) -> AIToolType:
26
+ """Return the AI tool type identifier."""
27
+ return AIToolType.ROO
28
+
29
+ @property
30
+ def tool_name(self) -> str:
31
+ """Return human-readable tool name."""
32
+ return "Roo Code"
33
+
34
+ def is_installed(self) -> bool:
35
+ """
36
+ Check if Roo Code is installed on the system.
37
+
38
+ Checks for existence of the Roo Code VS Code extension globalStorage directory
39
+ (rooveterinaryinc.roo-cline).
40
+
41
+ Returns:
42
+ True if Roo Code is detected
43
+ """
44
+ try:
45
+ config_dir = get_roo_config_dir()
46
+ return config_dir.exists()
47
+ except Exception:
48
+ return False
49
+
50
+ def get_instructions_directory(self) -> Path:
51
+ """
52
+ Get the directory where Roo Code global instructions should be installed.
53
+
54
+ Roo Code supports global rules at ~/.roo/rules/.
55
+
56
+ Returns:
57
+ Path to Roo Code global instructions directory
58
+
59
+ Raises:
60
+ FileNotFoundError: If Roo Code is not installed
61
+ """
62
+ if not self.is_installed():
63
+ raise FileNotFoundError(f"{self.tool_name} is not installed")
64
+ from aiconfigkit.utils.paths import get_home_directory
65
+
66
+ global_dir = get_home_directory() / ".roo" / "rules"
67
+ global_dir.mkdir(parents=True, exist_ok=True)
68
+ return global_dir
69
+
70
+ def get_instruction_file_extension(self) -> str:
71
+ """
72
+ Get the file extension for Roo Code instructions.
73
+
74
+ Roo Code uses markdown (.md) files in the .roo/rules/ directory.
75
+
76
+ Returns:
77
+ File extension including the dot
78
+ """
79
+ return ".md"
80
+
81
+ def get_project_instructions_directory(self, project_root: Path) -> Path:
82
+ """
83
+ Get the directory for project-specific Roo Code instructions.
84
+
85
+ Roo Code stores project-specific rules in .roo/rules/ directory at the
86
+ project root. It reads .md files recursively from this directory.
87
+ Numeric prefixes (e.g., 01-coding-standards.md) control load order.
88
+
89
+ Args:
90
+ project_root: Path to the project root directory
91
+
92
+ Returns:
93
+ Path to project instructions directory (.roo/rules/)
94
+ """
95
+ instructions_dir = project_root / ".roo" / "rules"
96
+ instructions_dir.mkdir(parents=True, exist_ok=True)
97
+ return instructions_dir
98
+
99
+ def get_mcp_config_path(self) -> Path:
100
+ """
101
+ Get the path to the MCP configuration file for Roo Code.
102
+
103
+ Roo Code stores project-level MCP config at .roo/mcp.json.
104
+ Global MCP config is at the globalStorage settings directory.
105
+
106
+ Returns:
107
+ Path to global MCP configuration file
108
+ """
109
+ config_dir = get_roo_config_dir()
110
+ return config_dir / "settings" / "cline_mcp_settings.json"