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
File without changes
@@ -0,0 +1,6 @@
1
+ """Entry point for running instructionkit as a module."""
2
+
3
+ from aiconfigkit.cli.main import app
4
+
5
+ if __name__ == "__main__":
6
+ app()
File without changes
@@ -0,0 +1,236 @@
1
+ """Base interface for AI coding tool integrations."""
2
+
3
+ import os
4
+ from abc import ABC, abstractmethod
5
+ from pathlib import Path
6
+ from typing import Optional
7
+
8
+ from aiconfigkit.core.models import AIToolType, InstallationScope, Instruction
9
+
10
+
11
+ class AITool(ABC):
12
+ """
13
+ Abstract base class for AI coding tool integrations.
14
+
15
+ Each AI tool (Cline, Cursor, Copilot, Kiro, Roo Code, Winsurf, Claude) implements this interface
16
+ to provide tool-specific installation logic.
17
+ """
18
+
19
+ @property
20
+ @abstractmethod
21
+ def tool_type(self) -> AIToolType:
22
+ """Return the AI tool type identifier."""
23
+ pass
24
+
25
+ @property
26
+ @abstractmethod
27
+ def tool_name(self) -> str:
28
+ """Return human-readable tool name."""
29
+ pass
30
+
31
+ @abstractmethod
32
+ def is_installed(self) -> bool:
33
+ """
34
+ Check if this AI tool is installed on the system.
35
+
36
+ Returns:
37
+ True if tool is detected, False otherwise
38
+ """
39
+ pass
40
+
41
+ @abstractmethod
42
+ def get_instructions_directory(self) -> Path:
43
+ """
44
+ Get the directory where instructions should be installed.
45
+
46
+ Returns:
47
+ Path to instructions directory
48
+
49
+ Raises:
50
+ FileNotFoundError: If tool is not installed
51
+ """
52
+ pass
53
+
54
+ @abstractmethod
55
+ def get_instruction_file_extension(self) -> str:
56
+ """
57
+ Get the file extension for instructions (e.g., '.md', '.txt').
58
+
59
+ Returns:
60
+ File extension including the dot
61
+ """
62
+ pass
63
+
64
+ @abstractmethod
65
+ def get_project_instructions_directory(self, project_root: Path) -> Path:
66
+ """
67
+ Get the directory where project-specific instructions should be installed.
68
+
69
+ Args:
70
+ project_root: Path to the project root directory
71
+
72
+ Returns:
73
+ Path to project instructions directory
74
+ """
75
+ pass
76
+
77
+ def get_mcp_config_path(self) -> Path:
78
+ """
79
+ Get the path to the MCP configuration file for this AI tool.
80
+
81
+ This is an optional method - tools that support MCP server configuration
82
+ should override this method.
83
+
84
+ Returns:
85
+ Path to MCP configuration file
86
+
87
+ Raises:
88
+ NotImplementedError: If tool doesn't support MCP configuration
89
+ """
90
+ raise NotImplementedError(f"{self.tool_name} does not support MCP server configuration")
91
+
92
+ def get_instruction_path(
93
+ self,
94
+ instruction_name: str,
95
+ scope: InstallationScope = InstallationScope.GLOBAL,
96
+ project_root: Optional[Path] = None,
97
+ ) -> Path:
98
+ """
99
+ Get the full path where an instruction file should be installed.
100
+
101
+ Args:
102
+ instruction_name: Name of the instruction
103
+ scope: Installation scope (global or project)
104
+ project_root: Project root path (required if scope is PROJECT)
105
+
106
+ Returns:
107
+ Full path to instruction file
108
+
109
+ Raises:
110
+ ValueError: If scope is PROJECT but project_root is None
111
+ """
112
+ if scope == InstallationScope.PROJECT:
113
+ if project_root is None:
114
+ raise ValueError("project_root is required for PROJECT scope")
115
+ directory = self.get_project_instructions_directory(project_root)
116
+ else:
117
+ directory = self.get_instructions_directory()
118
+
119
+ extension = self.get_instruction_file_extension()
120
+ filename = f"{instruction_name}{extension}"
121
+ return directory / filename
122
+
123
+ def instruction_exists(
124
+ self,
125
+ instruction_name: str,
126
+ scope: InstallationScope = InstallationScope.GLOBAL,
127
+ project_root: Optional[Path] = None,
128
+ ) -> bool:
129
+ """
130
+ Check if an instruction is already installed.
131
+
132
+ Args:
133
+ instruction_name: Name of the instruction
134
+ scope: Installation scope (global or project)
135
+ project_root: Project root path (required if scope is PROJECT)
136
+
137
+ Returns:
138
+ True if instruction file exists
139
+ """
140
+ try:
141
+ path = self.get_instruction_path(instruction_name, scope, project_root)
142
+ return path.exists()
143
+ except (FileNotFoundError, ValueError):
144
+ return False
145
+
146
+ def install_instruction(
147
+ self,
148
+ instruction: Instruction,
149
+ overwrite: bool = False,
150
+ scope: InstallationScope = InstallationScope.GLOBAL,
151
+ project_root: Optional[Path] = None,
152
+ ) -> Path:
153
+ """
154
+ Install an instruction to this AI tool.
155
+
156
+ Args:
157
+ instruction: Instruction to install
158
+ overwrite: Whether to overwrite existing file
159
+ scope: Installation scope (global or project)
160
+ project_root: Project root path (required if scope is PROJECT)
161
+
162
+ Returns:
163
+ Path where instruction was installed
164
+
165
+ Raises:
166
+ FileExistsError: If instruction exists and overwrite=False
167
+ FileNotFoundError: If tool is not installed
168
+ ValueError: If scope is PROJECT but project_root is None
169
+ """
170
+ path = self.get_instruction_path(instruction.name, scope, project_root)
171
+
172
+ if path.exists() and not overwrite:
173
+ raise FileExistsError(f"Instruction already exists: {path}")
174
+
175
+ # Ensure directory exists
176
+ path.parent.mkdir(parents=True, exist_ok=True)
177
+
178
+ # Write instruction content
179
+ path.write_text(instruction.content, encoding="utf-8")
180
+
181
+ return path
182
+
183
+ def uninstall_instruction(
184
+ self,
185
+ instruction_name: str,
186
+ scope: InstallationScope = InstallationScope.GLOBAL,
187
+ project_root: Optional[Path] = None,
188
+ ) -> bool:
189
+ """
190
+ Uninstall an instruction from this AI tool.
191
+
192
+ Args:
193
+ instruction_name: Name of instruction to remove
194
+ scope: Installation scope (global or project)
195
+ project_root: Project root path (required if scope is PROJECT)
196
+
197
+ Returns:
198
+ True if file was removed, False if it didn't exist
199
+ """
200
+ try:
201
+ path = self.get_instruction_path(instruction_name, scope, project_root)
202
+ if path.exists():
203
+ path.unlink()
204
+ return True
205
+ return False
206
+ except (FileNotFoundError, ValueError):
207
+ return False
208
+
209
+ def validate_installation(self) -> Optional[str]:
210
+ """
211
+ Validate that tool installation is correct and accessible.
212
+
213
+ Returns:
214
+ None if valid, error message if invalid
215
+ """
216
+ if not self.is_installed():
217
+ return f"{self.tool_name} is not installed or not found"
218
+
219
+ try:
220
+ directory = self.get_instructions_directory()
221
+ if not directory.exists():
222
+ # Try to create it
223
+ directory.mkdir(parents=True, exist_ok=True)
224
+
225
+ # Check write permissions
226
+ if not os.access(directory, os.W_OK):
227
+ return f"No write permission for {directory}"
228
+
229
+ except Exception as e:
230
+ return f"Error accessing {self.tool_name} directory: {str(e)}"
231
+
232
+ return None
233
+
234
+ def __repr__(self) -> str:
235
+ """String representation."""
236
+ return f"<{self.__class__.__name__} tool_type={self.tool_type.value}>"
@@ -0,0 +1,262 @@
1
+ """IDE capability registry for package component support."""
2
+
3
+ from dataclasses import dataclass
4
+
5
+ from aiconfigkit.core.models import AIToolType, ComponentType
6
+
7
+
8
+ @dataclass
9
+ class IDECapability:
10
+ """
11
+ Defines what component types an IDE supports.
12
+
13
+ Tracks which package components (instructions, MCP servers, hooks, etc.)
14
+ can be installed to each AI coding tool.
15
+ """
16
+
17
+ tool_type: AIToolType
18
+ tool_name: str
19
+ supported_components: set[ComponentType]
20
+ instructions_directory: str
21
+ instruction_file_extension: str
22
+ supports_project_scope: bool = True
23
+ supports_global_scope: bool = False
24
+ mcp_config_path: str | None = None
25
+ mcp_project_config_path: str | None = None # Project-level MCP config
26
+ hooks_directory: str | None = None
27
+ commands_directory: str | None = None
28
+ skills_directory: str | None = None # Claude skills
29
+ workflows_directory: str | None = None # Windsurf workflows
30
+ memory_file_name: str | None = None # CLAUDE.md
31
+ notes: str = ""
32
+
33
+ def supports_component(self, component_type: ComponentType) -> bool:
34
+ """
35
+ Check if IDE supports a specific component type.
36
+
37
+ Args:
38
+ component_type: Component type to check
39
+
40
+ Returns:
41
+ True if supported, False otherwise
42
+ """
43
+ return component_type in self.supported_components
44
+
45
+
46
+ # IDE Capability Registry
47
+ # Maps each AI tool to its supported component types and installation paths
48
+
49
+ CAPABILITY_REGISTRY: dict[AIToolType, IDECapability] = {
50
+ AIToolType.CURSOR: IDECapability(
51
+ tool_type=AIToolType.CURSOR,
52
+ tool_name="Cursor",
53
+ supported_components={
54
+ ComponentType.INSTRUCTION,
55
+ ComponentType.MCP_SERVER,
56
+ ComponentType.RESOURCE,
57
+ },
58
+ instructions_directory=".cursor/rules/",
59
+ instruction_file_extension=".mdc",
60
+ supports_project_scope=True,
61
+ supports_global_scope=True,
62
+ mcp_config_path="~/.cursor/mcp.json",
63
+ mcp_project_config_path=".cursor/mcp.json",
64
+ hooks_directory=None, # Hooks not supported
65
+ commands_directory=None, # Commands not supported
66
+ notes=(
67
+ "Cursor uses .mdc (markdown with metadata) files in .cursor/rules/. "
68
+ "MCP servers configured via ~/.cursor/mcp.json (global) or .cursor/mcp.json (project). "
69
+ "Supports up to 40 MCP tools. Resources not yet supported in MCP."
70
+ ),
71
+ ),
72
+ AIToolType.CLAUDE: IDECapability(
73
+ tool_type=AIToolType.CLAUDE,
74
+ tool_name="Claude Code",
75
+ supported_components={
76
+ ComponentType.INSTRUCTION,
77
+ ComponentType.MCP_SERVER,
78
+ ComponentType.HOOK,
79
+ ComponentType.COMMAND,
80
+ ComponentType.SKILL,
81
+ ComponentType.MEMORY_FILE,
82
+ ComponentType.RESOURCE,
83
+ },
84
+ instructions_directory=".claude/rules/",
85
+ instruction_file_extension=".md",
86
+ supports_project_scope=True,
87
+ supports_global_scope=False,
88
+ mcp_config_path="~/.claude/settings.json",
89
+ mcp_project_config_path=".claude/settings.local.json",
90
+ hooks_directory=".claude/hooks/",
91
+ commands_directory=".claude/commands/",
92
+ skills_directory=".claude/skills/",
93
+ memory_file_name="CLAUDE.md",
94
+ notes=(
95
+ "Claude Code uses .md files in .claude/rules/. "
96
+ "Full support for MCP servers, hooks, slash commands, and skills. "
97
+ "CLAUDE.md files persist context across sessions."
98
+ ),
99
+ ),
100
+ AIToolType.WINSURF: IDECapability(
101
+ tool_type=AIToolType.WINSURF,
102
+ tool_name="Windsurf",
103
+ supported_components={
104
+ ComponentType.INSTRUCTION,
105
+ ComponentType.MCP_SERVER,
106
+ ComponentType.WORKFLOW,
107
+ ComponentType.RESOURCE,
108
+ },
109
+ instructions_directory=".windsurf/rules/",
110
+ instruction_file_extension=".md",
111
+ supports_project_scope=True,
112
+ supports_global_scope=True,
113
+ mcp_config_path="~/.codeium/windsurf/mcp_config.json",
114
+ mcp_project_config_path=None, # Windsurf uses global MCP config only
115
+ hooks_directory=None, # Hooks not supported
116
+ commands_directory=None, # Commands not supported
117
+ workflows_directory=".windsurf/workflows/",
118
+ notes=(
119
+ "Windsurf uses .md files in .windsurf/rules/ with 4 activation modes. "
120
+ "MCP servers configured via ~/.codeium/windsurf/mcp_config.json. "
121
+ "Supports workflows in .windsurf/workflows/. Limit of 100 MCP tools."
122
+ ),
123
+ ),
124
+ AIToolType.KIRO: IDECapability(
125
+ tool_type=AIToolType.KIRO,
126
+ tool_name="Kiro",
127
+ supported_components={
128
+ ComponentType.INSTRUCTION,
129
+ ComponentType.RESOURCE,
130
+ },
131
+ instructions_directory=".kiro/steering/",
132
+ instruction_file_extension=".md",
133
+ supports_project_scope=True,
134
+ supports_global_scope=False,
135
+ mcp_config_path=None, # Kiro uses .kiro/settings/mcp.json but not yet supported
136
+ mcp_project_config_path=None,
137
+ hooks_directory=None, # Kiro hooks use .kiro.hook JSON format, not yet supported
138
+ commands_directory=None,
139
+ notes=(
140
+ "Kiro uses .md files in .kiro/steering/ with optional YAML front matter "
141
+ "for inclusion modes (always, fileMatch, manual, auto). "
142
+ "MCP and hooks support deferred to future release."
143
+ ),
144
+ ),
145
+ AIToolType.CLINE: IDECapability(
146
+ tool_type=AIToolType.CLINE,
147
+ tool_name="Cline",
148
+ supported_components={
149
+ ComponentType.INSTRUCTION,
150
+ ComponentType.RESOURCE,
151
+ },
152
+ instructions_directory=".clinerules/",
153
+ instruction_file_extension=".md",
154
+ supports_project_scope=True,
155
+ supports_global_scope=False,
156
+ mcp_config_path=None, # Cline MCP is global-only via cline_mcp_settings.json in VS Code globalStorage
157
+ mcp_project_config_path=None,
158
+ hooks_directory=None,
159
+ commands_directory=None,
160
+ notes=(
161
+ "Cline uses .md files in .clinerules/ directory (recursive). "
162
+ "Supports optional YAML frontmatter with paths: for conditional activation. "
163
+ "MCP configured globally via cline_mcp_settings.json, no project-level MCP support."
164
+ ),
165
+ ),
166
+ AIToolType.ROO: IDECapability(
167
+ tool_type=AIToolType.ROO,
168
+ tool_name="Roo Code",
169
+ supported_components={
170
+ ComponentType.INSTRUCTION,
171
+ ComponentType.MCP_SERVER,
172
+ ComponentType.COMMAND,
173
+ ComponentType.RESOURCE,
174
+ },
175
+ instructions_directory=".roo/rules/",
176
+ instruction_file_extension=".md",
177
+ supports_project_scope=True,
178
+ supports_global_scope=True,
179
+ mcp_config_path=None, # Global MCP via globalStorage cline_mcp_settings.json
180
+ mcp_project_config_path=".roo/mcp.json",
181
+ hooks_directory=None, # Hooks not supported
182
+ commands_directory=".roo/commands/",
183
+ notes=(
184
+ "Roo Code uses .md files in .roo/rules/ directory (recursive). "
185
+ "Mode-specific rules in .roo/rules-{mode-slug}/. "
186
+ "MCP configured via .roo/mcp.json (project) or globalStorage (global). "
187
+ "Slash commands in .roo/commands/. Global rules at ~/.roo/rules/."
188
+ ),
189
+ ),
190
+ AIToolType.COPILOT: IDECapability(
191
+ tool_type=AIToolType.COPILOT,
192
+ tool_name="GitHub Copilot",
193
+ supported_components={
194
+ ComponentType.INSTRUCTION,
195
+ ComponentType.MCP_SERVER,
196
+ },
197
+ instructions_directory=".github/instructions/",
198
+ instruction_file_extension=".instructions.md",
199
+ supports_project_scope=True,
200
+ supports_global_scope=True,
201
+ mcp_config_path="~/.vscode/mcp.json", # User-level MCP config
202
+ mcp_project_config_path=".vscode/mcp.json", # Workspace-level MCP config
203
+ hooks_directory=None, # Hooks not supported
204
+ commands_directory=None, # Commands not supported
205
+ notes=(
206
+ "GitHub Copilot uses .github/copilot-instructions.md (main) and "
207
+ ".github/instructions/**/*.instructions.md (file-specific with globs). "
208
+ "MCP servers configured via .vscode/mcp.json. Limit of 128 tools."
209
+ ),
210
+ ),
211
+ }
212
+
213
+
214
+ def get_capability(tool_type: AIToolType) -> IDECapability:
215
+ """
216
+ Get capability information for an IDE.
217
+
218
+ Args:
219
+ tool_type: AI tool type
220
+
221
+ Returns:
222
+ IDECapability for the tool
223
+
224
+ Raises:
225
+ KeyError: If tool type is not in registry
226
+ """
227
+ return CAPABILITY_REGISTRY[tool_type]
228
+
229
+
230
+ def get_supported_tools_for_component(component_type: ComponentType) -> list[AIToolType]:
231
+ """
232
+ Get list of IDEs that support a specific component type.
233
+
234
+ Args:
235
+ component_type: Component type to check
236
+
237
+ Returns:
238
+ List of AI tool types that support this component
239
+ """
240
+ supported_tools = []
241
+ for tool_type, capability in CAPABILITY_REGISTRY.items():
242
+ if capability.supports_component(component_type):
243
+ supported_tools.append(tool_type)
244
+ return supported_tools
245
+
246
+
247
+ def validate_component_support(tool_type: AIToolType, component_type: ComponentType) -> bool:
248
+ """
249
+ Validate that an IDE supports a component type.
250
+
251
+ Args:
252
+ tool_type: AI tool type
253
+ component_type: Component type to validate
254
+
255
+ Returns:
256
+ True if supported, False otherwise
257
+ """
258
+ try:
259
+ capability = get_capability(tool_type)
260
+ return capability.supports_component(component_type)
261
+ except KeyError:
262
+ return False
@@ -0,0 +1,91 @@
1
+ """Claude 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_claude_config_dir
8
+
9
+
10
+ class ClaudeTool(AITool):
11
+ """Integration for Claude Code AI coding tool."""
12
+
13
+ @property
14
+ def tool_type(self) -> AIToolType:
15
+ """Return the AI tool type identifier."""
16
+ return AIToolType.CLAUDE
17
+
18
+ @property
19
+ def tool_name(self) -> str:
20
+ """Return human-readable tool name."""
21
+ return "Claude Code"
22
+
23
+ def is_installed(self) -> bool:
24
+ """
25
+ Check if Claude Code is installed on the system.
26
+
27
+ Checks for existence of Claude configuration directory.
28
+
29
+ Returns:
30
+ True if Claude Code is detected
31
+ """
32
+ try:
33
+ config_dir = get_claude_config_dir()
34
+ # Check if parent directory exists
35
+ # Claude Code config dir structure: ~/.claude/rules/
36
+ claude_base = config_dir.parent
37
+ return claude_base.exists()
38
+ except Exception:
39
+ return False
40
+
41
+ def get_instructions_directory(self) -> Path:
42
+ """
43
+ Get the directory where Claude Code instructions should be installed.
44
+
45
+ Note: Global installation is not supported. Claude Code supports both
46
+ single-file (~/.claude/rules.md) and multi-file approaches, but for
47
+ consistency with our multi-file installation pattern, we only support
48
+ project-level installations.
49
+
50
+ Returns:
51
+ Path to Claude Code instructions directory
52
+
53
+ Raises:
54
+ NotImplementedError: Global installation not supported for Claude Code
55
+ """
56
+ raise NotImplementedError(
57
+ f"{self.tool_name} global installation is not supported. "
58
+ "Please use project-level installation instead (--scope project)."
59
+ )
60
+
61
+ def get_instruction_file_extension(self) -> str:
62
+ """
63
+ Get the file extension for Claude Code instructions.
64
+
65
+ Claude Code uses markdown (.md) files for rules.
66
+
67
+ Returns:
68
+ File extension including the dot
69
+ """
70
+ return ".md"
71
+
72
+ def get_project_instructions_directory(self, project_root: Path) -> Path:
73
+ """
74
+ Get the directory for project-specific Claude Code instructions.
75
+
76
+ Claude Code stores project-specific rules in .claude/rules/ directory
77
+ in the project root. It supports multiple .md files in this directory.
78
+
79
+ Reference:
80
+ - Recommended: .claude/rules/*.md (multiple files)
81
+ - Alternative: .claude/rules.md (single file, not used by this tool)
82
+
83
+ Args:
84
+ project_root: Path to the project root directory
85
+
86
+ Returns:
87
+ Path to project instructions directory (.claude/rules/)
88
+ """
89
+ instructions_dir = project_root / ".claude" / "rules"
90
+ instructions_dir.mkdir(parents=True, exist_ok=True)
91
+ return instructions_dir
@@ -0,0 +1,97 @@
1
+ """Claude Desktop MCP 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_claude_desktop_config_path
8
+
9
+
10
+ class ClaudeDesktopTool(AITool):
11
+ """Integration for Claude Desktop MCP server configuration."""
12
+
13
+ @property
14
+ def tool_type(self) -> AIToolType:
15
+ """Return the AI tool type identifier."""
16
+ return AIToolType.CLAUDE
17
+
18
+ @property
19
+ def tool_name(self) -> str:
20
+ """Return human-readable tool name."""
21
+ return "Claude Desktop"
22
+
23
+ def is_installed(self) -> bool:
24
+ """
25
+ Check if Claude Desktop is installed on the system.
26
+
27
+ Checks for existence of Claude Desktop config directory.
28
+
29
+ Returns:
30
+ True if Claude Desktop is detected
31
+ """
32
+ try:
33
+ config_path = get_claude_desktop_config_path()
34
+ # Check if parent directory exists (Claude Desktop install directory)
35
+ return config_path.parent.exists()
36
+ except Exception:
37
+ return False
38
+
39
+ def get_instructions_directory(self) -> Path:
40
+ """
41
+ Get the directory where Claude Desktop instructions should be installed.
42
+
43
+ Note: Claude Desktop doesn't use instruction files like Claude Code.
44
+ This method is not applicable for MCP configuration.
45
+
46
+ Returns:
47
+ Path to instructions directory
48
+
49
+ Raises:
50
+ NotImplementedError: Claude Desktop uses MCP config, not instruction files
51
+ """
52
+ raise NotImplementedError(
53
+ f"{self.tool_name} uses MCP configuration instead of instruction files. "
54
+ "Use get_mcp_config_path() instead."
55
+ )
56
+
57
+ def get_instruction_file_extension(self) -> str:
58
+ """
59
+ Get the file extension for Claude Desktop instructions.
60
+
61
+ Returns:
62
+ File extension including the dot
63
+
64
+ Raises:
65
+ NotImplementedError: Claude Desktop uses MCP config, not instruction files
66
+ """
67
+ raise NotImplementedError(f"{self.tool_name} uses MCP configuration instead of instruction files.")
68
+
69
+ def get_project_instructions_directory(self, project_root: Path) -> Path:
70
+ """
71
+ Get the directory for project-specific Claude Desktop instructions.
72
+
73
+ Args:
74
+ project_root: Path to the project root directory
75
+
76
+ Returns:
77
+ Path to project instructions directory
78
+
79
+ Raises:
80
+ NotImplementedError: Claude Desktop uses MCP config, not instruction files
81
+ """
82
+ raise NotImplementedError(f"{self.tool_name} uses MCP configuration instead of instruction files.")
83
+
84
+ def get_mcp_config_path(self) -> Path:
85
+ """
86
+ Get the path to the Claude Desktop MCP configuration file.
87
+
88
+ Claude Desktop stores MCP server configurations in claude_desktop_config.json.
89
+ The file location is platform-specific:
90
+ - macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
91
+ - Linux: ~/.config/Claude/claude_desktop_config.json
92
+ - Windows: %APPDATA%\\Claude\\claude_desktop_config.json
93
+
94
+ Returns:
95
+ Path to claude_desktop_config.json
96
+ """
97
+ return get_claude_desktop_config_path()