devsync 0.6.0__tar.gz → 0.8.0__tar.gz
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.
- {devsync-0.6.0/devsync.egg-info → devsync-0.8.0}/PKG-INFO +14 -14
- {devsync-0.6.0 → devsync-0.8.0}/README.md +12 -12
- {devsync-0.6.0/aiconfigkit → devsync-0.8.0/devsync}/__main__.py +1 -1
- {devsync-0.6.0/aiconfigkit → devsync-0.8.0/devsync}/ai_tools/base.py +2 -2
- {devsync-0.6.0/aiconfigkit → devsync-0.8.0/devsync}/ai_tools/capability_registry.py +22 -1
- {devsync-0.6.0/aiconfigkit → devsync-0.8.0/devsync}/ai_tools/claude.py +3 -3
- {devsync-0.6.0/aiconfigkit → devsync-0.8.0/devsync}/ai_tools/claude_desktop.py +3 -3
- {devsync-0.6.0/aiconfigkit → devsync-0.8.0/devsync}/ai_tools/cline.py +3 -3
- devsync-0.8.0/devsync/ai_tools/codex.py +219 -0
- {devsync-0.6.0/aiconfigkit → devsync-0.8.0/devsync}/ai_tools/copilot.py +3 -3
- {devsync-0.6.0/aiconfigkit → devsync-0.8.0/devsync}/ai_tools/cursor.py +3 -3
- {devsync-0.6.0/aiconfigkit → devsync-0.8.0/devsync}/ai_tools/detector.py +12 -9
- {devsync-0.6.0/aiconfigkit → devsync-0.8.0/devsync}/ai_tools/kiro.py +3 -3
- {devsync-0.6.0/aiconfigkit → devsync-0.8.0/devsync}/ai_tools/mcp_syncer.py +9 -8
- {devsync-0.6.0/aiconfigkit → devsync-0.8.0/devsync}/ai_tools/roo.py +4 -4
- {devsync-0.6.0/aiconfigkit → devsync-0.8.0/devsync}/ai_tools/translator.py +1 -1
- {devsync-0.6.0/aiconfigkit → devsync-0.8.0/devsync}/ai_tools/winsurf.py +3 -3
- {devsync-0.6.0/aiconfigkit → devsync-0.8.0/devsync}/cli/delete.py +3 -3
- {devsync-0.6.0/aiconfigkit → devsync-0.8.0/devsync}/cli/download.py +6 -6
- {devsync-0.6.0/aiconfigkit → devsync-0.8.0/devsync}/cli/install.py +10 -10
- {devsync-0.6.0/aiconfigkit → devsync-0.8.0/devsync}/cli/install_new.py +10 -10
- {devsync-0.6.0/aiconfigkit → devsync-0.8.0/devsync}/cli/list.py +8 -8
- {devsync-0.6.0/aiconfigkit → devsync-0.8.0/devsync}/cli/main.py +19 -19
- {devsync-0.6.0/aiconfigkit → devsync-0.8.0/devsync}/cli/mcp_configure.py +9 -8
- {devsync-0.6.0/aiconfigkit → devsync-0.8.0/devsync}/cli/mcp_install.py +5 -4
- {devsync-0.6.0/aiconfigkit → devsync-0.8.0/devsync}/cli/mcp_sync.py +5 -4
- {devsync-0.6.0/aiconfigkit → devsync-0.8.0/devsync}/cli/package.py +10 -7
- {devsync-0.6.0/aiconfigkit → devsync-0.8.0/devsync}/cli/package_create.py +4 -4
- {devsync-0.6.0/aiconfigkit → devsync-0.8.0/devsync}/cli/package_install.py +13 -11
- {devsync-0.6.0/aiconfigkit → devsync-0.8.0/devsync}/cli/template_backup.py +9 -8
- {devsync-0.6.0/aiconfigkit → devsync-0.8.0/devsync}/cli/template_init.py +1 -1
- {devsync-0.6.0/aiconfigkit → devsync-0.8.0/devsync}/cli/template_install.py +12 -10
- {devsync-0.6.0/aiconfigkit → devsync-0.8.0/devsync}/cli/template_list.py +2 -2
- {devsync-0.6.0/aiconfigkit → devsync-0.8.0/devsync}/cli/template_uninstall.py +3 -3
- {devsync-0.6.0/aiconfigkit → devsync-0.8.0/devsync}/cli/template_update.py +8 -8
- {devsync-0.6.0/aiconfigkit → devsync-0.8.0/devsync}/cli/template_validate.py +4 -4
- {devsync-0.6.0/aiconfigkit → devsync-0.8.0/devsync}/cli/tools.py +1 -1
- {devsync-0.6.0/aiconfigkit → devsync-0.8.0/devsync}/cli/uninstall.py +5 -5
- {devsync-0.6.0/aiconfigkit → devsync-0.8.0/devsync}/cli/update.py +8 -8
- {devsync-0.6.0/aiconfigkit → devsync-0.8.0/devsync}/core/component_detector.py +7 -6
- {devsync-0.6.0/aiconfigkit → devsync-0.8.0/devsync}/core/conflict_resolution.py +5 -5
- {devsync-0.6.0/aiconfigkit → devsync-0.8.0/devsync}/core/git_operations.py +2 -2
- {devsync-0.6.0/aiconfigkit → devsync-0.8.0/devsync}/core/mcp/credentials.py +3 -3
- {devsync-0.6.0/aiconfigkit → devsync-0.8.0/devsync}/core/mcp/manager.py +5 -5
- {devsync-0.6.0/aiconfigkit → devsync-0.8.0/devsync}/core/models.py +3 -2
- {devsync-0.6.0/aiconfigkit → devsync-0.8.0/devsync}/core/package_creator.py +3 -3
- {devsync-0.6.0/aiconfigkit → devsync-0.8.0/devsync}/core/package_manifest.py +1 -1
- {devsync-0.6.0/aiconfigkit → devsync-0.8.0/devsync}/core/repository.py +1 -1
- {devsync-0.6.0/aiconfigkit → devsync-0.8.0/devsync}/core/secret_detector.py +1 -1
- {devsync-0.6.0/aiconfigkit → devsync-0.8.0/devsync}/core/template_manifest.py +1 -1
- {devsync-0.6.0/aiconfigkit → devsync-0.8.0/devsync}/storage/library.py +4 -4
- {devsync-0.6.0/aiconfigkit → devsync-0.8.0/devsync}/storage/package_tracker.py +2 -2
- {devsync-0.6.0/aiconfigkit → devsync-0.8.0/devsync}/storage/template_library.py +9 -7
- {devsync-0.6.0/aiconfigkit → devsync-0.8.0/devsync}/storage/template_tracker.py +5 -4
- {devsync-0.6.0/aiconfigkit → devsync-0.8.0/devsync}/storage/tracker.py +5 -5
- {devsync-0.6.0/aiconfigkit → devsync-0.8.0/devsync}/tui/__init__.py +1 -1
- {devsync-0.6.0/aiconfigkit → devsync-0.8.0/devsync}/tui/installer.py +4 -4
- {devsync-0.6.0/aiconfigkit → devsync-0.8.0/devsync}/utils/backup.py +14 -14
- {devsync-0.6.0/aiconfigkit → devsync-0.8.0/devsync}/utils/dotenv.py +2 -2
- {devsync-0.6.0/aiconfigkit → devsync-0.8.0/devsync}/utils/git_helpers.py +2 -2
- {devsync-0.6.0/aiconfigkit → devsync-0.8.0/devsync}/utils/paths.py +22 -1
- {devsync-0.6.0/aiconfigkit → devsync-0.8.0/devsync}/utils/project.py +4 -2
- {devsync-0.6.0/aiconfigkit → devsync-0.8.0/devsync}/utils/streaming.py +1 -1
- {devsync-0.6.0/aiconfigkit → devsync-0.8.0/devsync}/utils/ui.py +1 -1
- {devsync-0.6.0 → devsync-0.8.0/devsync.egg-info}/PKG-INFO +14 -14
- devsync-0.8.0/devsync.egg-info/SOURCES.txt +88 -0
- devsync-0.8.0/devsync.egg-info/entry_points.txt +2 -0
- devsync-0.8.0/devsync.egg-info/top_level.txt +1 -0
- {devsync-0.6.0 → devsync-0.8.0}/pyproject.toml +6 -6
- devsync-0.6.0/devsync.egg-info/SOURCES.txt +0 -87
- devsync-0.6.0/devsync.egg-info/entry_points.txt +0 -2
- devsync-0.6.0/devsync.egg-info/top_level.txt +0 -1
- {devsync-0.6.0 → devsync-0.8.0}/LICENSE +0 -0
- {devsync-0.6.0/aiconfigkit → devsync-0.8.0/devsync}/__init__.py +0 -0
- {devsync-0.6.0/aiconfigkit → devsync-0.8.0/devsync}/ai_tools/__init__.py +0 -0
- {devsync-0.6.0/aiconfigkit → devsync-0.8.0/devsync}/cli/__init__.py +0 -0
- {devsync-0.6.0/aiconfigkit → devsync-0.8.0/devsync}/cli/template.py +0 -0
- {devsync-0.6.0/aiconfigkit → devsync-0.8.0/devsync}/core/__init__.py +0 -0
- {devsync-0.6.0/aiconfigkit → devsync-0.8.0/devsync}/core/checksum.py +0 -0
- {devsync-0.6.0/aiconfigkit → devsync-0.8.0/devsync}/core/mcp/__init__.py +0 -0
- {devsync-0.6.0/aiconfigkit → devsync-0.8.0/devsync}/core/mcp/set_manager.py +0 -0
- {devsync-0.6.0/aiconfigkit → devsync-0.8.0/devsync}/core/mcp/validator.py +0 -0
- {devsync-0.6.0/aiconfigkit → devsync-0.8.0/devsync}/core/version.py +0 -0
- {devsync-0.6.0/aiconfigkit → devsync-0.8.0/devsync}/storage/__init__.py +0 -0
- {devsync-0.6.0/aiconfigkit → devsync-0.8.0/devsync}/storage/mcp_tracker.py +0 -0
- {devsync-0.6.0/aiconfigkit → devsync-0.8.0/devsync}/utils/__init__.py +0 -0
- {devsync-0.6.0/aiconfigkit → devsync-0.8.0/devsync}/utils/atomic_write.py +0 -0
- {devsync-0.6.0/aiconfigkit → devsync-0.8.0/devsync}/utils/logging.py +0 -0
- {devsync-0.6.0/aiconfigkit → devsync-0.8.0/devsync}/utils/namespace.py +0 -0
- {devsync-0.6.0/aiconfigkit → devsync-0.8.0/devsync}/utils/validation.py +0 -0
- {devsync-0.6.0 → devsync-0.8.0}/devsync.egg-info/dependency_links.txt +0 -0
- {devsync-0.6.0 → devsync-0.8.0}/devsync.egg-info/requires.txt +0 -0
- {devsync-0.6.0 → devsync-0.8.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: devsync
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.8.0
|
|
4
4
|
Summary: Distribute and sync dev tool configurations across teams
|
|
5
5
|
Author-email: Troy Larson <troy@calvinware.com>
|
|
6
6
|
License: MIT License
|
|
@@ -8,7 +8,7 @@ Project-URL: Homepage, https://github.com/troylar/devsync
|
|
|
8
8
|
Project-URL: Documentation, https://github.com/troylar/devsync#readme
|
|
9
9
|
Project-URL: Repository, https://github.com/troylar/devsync
|
|
10
10
|
Project-URL: Issues, https://github.com/troylar/devsync/issues
|
|
11
|
-
Keywords: cli,ai,config,mcp,cursor,copilot,claude,cline,kiro,roo,windsurf
|
|
11
|
+
Keywords: cli,ai,config,mcp,cursor,copilot,claude,cline,codex,kiro,roo,windsurf
|
|
12
12
|
Classifier: Development Status :: 3 - Alpha
|
|
13
13
|
Classifier: Intended Audience :: Developers
|
|
14
14
|
Classifier: Programming Language :: Python :: 3
|
|
@@ -48,7 +48,7 @@ Requires-Dist: types-PyYAML>=6.0.12.20240808; extra == "dev"
|
|
|
48
48
|
[](https://www.python.org/downloads/)
|
|
49
49
|
[](https://opensource.org/licenses/MIT)
|
|
50
50
|
|
|
51
|
-
**Works with:** Claude Code • Claude Desktop • Cline • Cursor • GitHub Copilot • Kiro • Roo Code • Windsurf
|
|
51
|
+
**Works with:** Claude Code • Claude Desktop • Cline • Codex CLI • Cursor • GitHub Copilot • Kiro • Roo Code • Windsurf
|
|
52
52
|
|
|
53
53
|
</div>
|
|
54
54
|
|
|
@@ -298,16 +298,16 @@ Any IDE-specific content from Git repositories:
|
|
|
298
298
|
|
|
299
299
|
Complete configuration bundles with multiple component types:
|
|
300
300
|
|
|
301
|
-
| Component | Claude | Cline | Cursor | Kiro | Roo Code | Windsurf | Copilot |
|
|
302
|
-
|
|
303
|
-
| Instructions | `.claude/rules/` | `.clinerules/` | `.cursor/rules/` | `.kiro/steering/` | `.roo/rules/` | `.windsurf/rules/` | `.github/instructions/` |
|
|
304
|
-
| MCP Servers | ✅ | ❌ | ✅ | ❌ | ✅ | ✅ | ✅ |
|
|
305
|
-
| Hooks | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
|
306
|
-
| Commands | ✅ | ❌ | ❌ | ❌ | ✅ | ❌ | ❌ |
|
|
307
|
-
| Skills | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
|
308
|
-
| Workflows | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ |
|
|
309
|
-
| Memory Files | ✅ (CLAUDE.md) | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
|
310
|
-
| Resources | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ |
|
|
301
|
+
| Component | Claude | Cline | Codex CLI | Cursor | Kiro | Roo Code | Windsurf | Copilot |
|
|
302
|
+
|-----------|--------|-------|----------|--------|------|----------|----------|---------|
|
|
303
|
+
| Instructions | `.claude/rules/` | `.clinerules/` | `AGENTS.md` | `.cursor/rules/` | `.kiro/steering/` | `.roo/rules/` | `.windsurf/rules/` | `.github/instructions/` |
|
|
304
|
+
| MCP Servers | ✅ | ❌ | ❌ | ✅ | ❌ | ✅ | ✅ | ✅ |
|
|
305
|
+
| Hooks | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
|
306
|
+
| Commands | ✅ | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ | ❌ |
|
|
307
|
+
| Skills | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
|
308
|
+
| Workflows | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ |
|
|
309
|
+
| Memory Files | ✅ (CLAUDE.md) | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
|
310
|
+
| Resources | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ |
|
|
311
311
|
|
|
312
312
|
### MCP Server Configurations
|
|
313
313
|
|
|
@@ -327,7 +327,7 @@ Model Context Protocol server setups for enhanced AI capabilities:
|
|
|
327
327
|
|
|
328
328
|
- Python 3.10 or higher
|
|
329
329
|
- Git (for cloning template repositories)
|
|
330
|
-
- One of: Claude Code, Claude Desktop, Cline, Cursor, GitHub Copilot, Kiro, Roo Code, or Windsurf
|
|
330
|
+
- One of: Claude Code, Claude Desktop, Cline, Codex CLI, Cursor, GitHub Copilot, Kiro, Roo Code, or Windsurf
|
|
331
331
|
|
|
332
332
|
### Install DevSync
|
|
333
333
|
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
[](https://www.python.org/downloads/)
|
|
11
11
|
[](https://opensource.org/licenses/MIT)
|
|
12
12
|
|
|
13
|
-
**Works with:** Claude Code • Claude Desktop • Cline • Cursor • GitHub Copilot • Kiro • Roo Code • Windsurf
|
|
13
|
+
**Works with:** Claude Code • Claude Desktop • Cline • Codex CLI • Cursor • GitHub Copilot • Kiro • Roo Code • Windsurf
|
|
14
14
|
|
|
15
15
|
</div>
|
|
16
16
|
|
|
@@ -260,16 +260,16 @@ Any IDE-specific content from Git repositories:
|
|
|
260
260
|
|
|
261
261
|
Complete configuration bundles with multiple component types:
|
|
262
262
|
|
|
263
|
-
| Component | Claude | Cline | Cursor | Kiro | Roo Code | Windsurf | Copilot |
|
|
264
|
-
|
|
265
|
-
| Instructions | `.claude/rules/` | `.clinerules/` | `.cursor/rules/` | `.kiro/steering/` | `.roo/rules/` | `.windsurf/rules/` | `.github/instructions/` |
|
|
266
|
-
| MCP Servers | ✅ | ❌ | ✅ | ❌ | ✅ | ✅ | ✅ |
|
|
267
|
-
| Hooks | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
|
268
|
-
| Commands | ✅ | ❌ | ❌ | ❌ | ✅ | ❌ | ❌ |
|
|
269
|
-
| Skills | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
|
270
|
-
| Workflows | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ |
|
|
271
|
-
| Memory Files | ✅ (CLAUDE.md) | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
|
272
|
-
| Resources | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ |
|
|
263
|
+
| Component | Claude | Cline | Codex CLI | Cursor | Kiro | Roo Code | Windsurf | Copilot |
|
|
264
|
+
|-----------|--------|-------|----------|--------|------|----------|----------|---------|
|
|
265
|
+
| Instructions | `.claude/rules/` | `.clinerules/` | `AGENTS.md` | `.cursor/rules/` | `.kiro/steering/` | `.roo/rules/` | `.windsurf/rules/` | `.github/instructions/` |
|
|
266
|
+
| MCP Servers | ✅ | ❌ | ❌ | ✅ | ❌ | ✅ | ✅ | ✅ |
|
|
267
|
+
| Hooks | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
|
268
|
+
| Commands | ✅ | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ | ❌ |
|
|
269
|
+
| Skills | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
|
270
|
+
| Workflows | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ |
|
|
271
|
+
| Memory Files | ✅ (CLAUDE.md) | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
|
272
|
+
| Resources | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ |
|
|
273
273
|
|
|
274
274
|
### MCP Server Configurations
|
|
275
275
|
|
|
@@ -289,7 +289,7 @@ Model Context Protocol server setups for enhanced AI capabilities:
|
|
|
289
289
|
|
|
290
290
|
- Python 3.10 or higher
|
|
291
291
|
- Git (for cloning template repositories)
|
|
292
|
-
- One of: Claude Code, Claude Desktop, Cline, Cursor, GitHub Copilot, Kiro, Roo Code, or Windsurf
|
|
292
|
+
- One of: Claude Code, Claude Desktop, Cline, Codex CLI, Cursor, GitHub Copilot, Kiro, Roo Code, or Windsurf
|
|
293
293
|
|
|
294
294
|
### Install DevSync
|
|
295
295
|
|
|
@@ -5,14 +5,14 @@ from abc import ABC, abstractmethod
|
|
|
5
5
|
from pathlib import Path
|
|
6
6
|
from typing import Optional
|
|
7
7
|
|
|
8
|
-
from
|
|
8
|
+
from devsync.core.models import AIToolType, InstallationScope, Instruction
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
class AITool(ABC):
|
|
12
12
|
"""
|
|
13
13
|
Abstract base class for AI coding tool integrations.
|
|
14
14
|
|
|
15
|
-
Each AI tool (Cline, Cursor, Copilot, Kiro, Roo Code, Winsurf, Claude) implements this interface
|
|
15
|
+
Each AI tool (Cline, Codex, Cursor, Copilot, Kiro, Roo Code, Winsurf, Claude) implements this interface
|
|
16
16
|
to provide tool-specific installation logic.
|
|
17
17
|
"""
|
|
18
18
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from dataclasses import dataclass
|
|
4
4
|
|
|
5
|
-
from
|
|
5
|
+
from devsync.core.models import AIToolType, ComponentType
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
@dataclass
|
|
@@ -187,6 +187,27 @@ CAPABILITY_REGISTRY: dict[AIToolType, IDECapability] = {
|
|
|
187
187
|
"Slash commands in .roo/commands/. Global rules at ~/.roo/rules/."
|
|
188
188
|
),
|
|
189
189
|
),
|
|
190
|
+
AIToolType.CODEX: IDECapability(
|
|
191
|
+
tool_type=AIToolType.CODEX,
|
|
192
|
+
tool_name="OpenAI Codex CLI",
|
|
193
|
+
supported_components={
|
|
194
|
+
ComponentType.INSTRUCTION,
|
|
195
|
+
ComponentType.RESOURCE,
|
|
196
|
+
},
|
|
197
|
+
instructions_directory="", # AGENTS.md at project root
|
|
198
|
+
instruction_file_extension=".md",
|
|
199
|
+
supports_project_scope=True,
|
|
200
|
+
supports_global_scope=False,
|
|
201
|
+
mcp_config_path=None,
|
|
202
|
+
mcp_project_config_path=None,
|
|
203
|
+
hooks_directory=None,
|
|
204
|
+
commands_directory=None,
|
|
205
|
+
notes=(
|
|
206
|
+
"OpenAI Codex CLI uses a single AGENTS.md file at the project root. "
|
|
207
|
+
"DevSync manages sections within this file using HTML comment markers. "
|
|
208
|
+
"No MCP, hooks, or commands support."
|
|
209
|
+
),
|
|
210
|
+
),
|
|
190
211
|
AIToolType.COPILOT: IDECapability(
|
|
191
212
|
tool_type=AIToolType.COPILOT,
|
|
192
213
|
tool_name="GitHub Copilot",
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
|
|
5
|
-
from
|
|
6
|
-
from
|
|
7
|
-
from
|
|
5
|
+
from devsync.ai_tools.base import AITool
|
|
6
|
+
from devsync.core.models import AIToolType
|
|
7
|
+
from devsync.utils.paths import get_claude_config_dir
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
class ClaudeTool(AITool):
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
|
|
5
|
-
from
|
|
6
|
-
from
|
|
7
|
-
from
|
|
5
|
+
from devsync.ai_tools.base import AITool
|
|
6
|
+
from devsync.core.models import AIToolType
|
|
7
|
+
from devsync.utils.paths import get_claude_desktop_config_path
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
class ClaudeDesktopTool(AITool):
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
|
|
5
|
-
from
|
|
6
|
-
from
|
|
7
|
-
from
|
|
5
|
+
from devsync.ai_tools.base import AITool
|
|
6
|
+
from devsync.core.models import AIToolType
|
|
7
|
+
from devsync.utils.paths import get_cline_config_dir
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
class ClineTool(AITool):
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
"""OpenAI Codex CLI AI tool integration."""
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
import shutil
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Optional
|
|
7
|
+
|
|
8
|
+
from devsync.ai_tools.base import AITool
|
|
9
|
+
from devsync.core.models import AIToolType, InstallationScope, Instruction
|
|
10
|
+
|
|
11
|
+
START_MARKER = "<!-- devsync:start:{name} -->"
|
|
12
|
+
END_MARKER = "<!-- devsync:end:{name} -->"
|
|
13
|
+
SECTION_PATTERN = r"<!-- devsync:start:{name} -->\n.*?\n<!-- devsync:end:{name} -->"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class CodexTool(AITool):
|
|
17
|
+
"""Integration for OpenAI Codex CLI.
|
|
18
|
+
|
|
19
|
+
Codex CLI reads a single AGENTS.md file at the project root.
|
|
20
|
+
DevSync manages individual instruction sections using HTML comment markers:
|
|
21
|
+
|
|
22
|
+
<!-- devsync:start:instruction-name -->
|
|
23
|
+
... instruction content ...
|
|
24
|
+
<!-- devsync:end:instruction-name -->
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
@property
|
|
28
|
+
def tool_type(self) -> AIToolType:
|
|
29
|
+
"""Return the AI tool type identifier."""
|
|
30
|
+
return AIToolType.CODEX
|
|
31
|
+
|
|
32
|
+
@property
|
|
33
|
+
def tool_name(self) -> str:
|
|
34
|
+
"""Return human-readable tool name."""
|
|
35
|
+
return "OpenAI Codex CLI"
|
|
36
|
+
|
|
37
|
+
def is_installed(self) -> bool:
|
|
38
|
+
"""Check if OpenAI Codex CLI is installed on the system.
|
|
39
|
+
|
|
40
|
+
Returns:
|
|
41
|
+
True if codex binary is found on PATH
|
|
42
|
+
"""
|
|
43
|
+
return shutil.which("codex") is not None
|
|
44
|
+
|
|
45
|
+
def get_instructions_directory(self) -> Path:
|
|
46
|
+
"""Get the directory where instructions should be installed.
|
|
47
|
+
|
|
48
|
+
Raises:
|
|
49
|
+
NotImplementedError: Codex CLI only supports project-level installation
|
|
50
|
+
"""
|
|
51
|
+
raise NotImplementedError(
|
|
52
|
+
f"{self.tool_name} global installation is not supported. "
|
|
53
|
+
"OpenAI Codex CLI uses project-level AGENTS.md only. "
|
|
54
|
+
"Please use project-level installation instead (--scope project)."
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
def get_instruction_file_extension(self) -> str:
|
|
58
|
+
"""Get the file extension for Codex instructions.
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
File extension including the dot
|
|
62
|
+
"""
|
|
63
|
+
return ".md"
|
|
64
|
+
|
|
65
|
+
def get_project_instructions_directory(self, project_root: Path) -> Path:
|
|
66
|
+
"""Get the directory for project-specific Codex instructions.
|
|
67
|
+
|
|
68
|
+
AGENTS.md lives at the project root, so the directory is the root itself.
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
project_root: Path to the project root directory
|
|
72
|
+
|
|
73
|
+
Returns:
|
|
74
|
+
Path to project root (AGENTS.md lives at root)
|
|
75
|
+
"""
|
|
76
|
+
return project_root
|
|
77
|
+
|
|
78
|
+
def get_instruction_path(
|
|
79
|
+
self,
|
|
80
|
+
instruction_name: str,
|
|
81
|
+
scope: InstallationScope = InstallationScope.GLOBAL,
|
|
82
|
+
project_root: Optional[Path] = None,
|
|
83
|
+
) -> Path:
|
|
84
|
+
"""Get the path to AGENTS.md.
|
|
85
|
+
|
|
86
|
+
Always returns project_root / AGENTS.md regardless of instruction name.
|
|
87
|
+
|
|
88
|
+
Args:
|
|
89
|
+
instruction_name: Name of the instruction (unused for path)
|
|
90
|
+
scope: Installation scope (must be PROJECT)
|
|
91
|
+
project_root: Project root path
|
|
92
|
+
|
|
93
|
+
Returns:
|
|
94
|
+
Path to AGENTS.md
|
|
95
|
+
|
|
96
|
+
Raises:
|
|
97
|
+
ValueError: If scope is PROJECT but project_root is None
|
|
98
|
+
NotImplementedError: If scope is GLOBAL
|
|
99
|
+
"""
|
|
100
|
+
if scope == InstallationScope.GLOBAL:
|
|
101
|
+
raise NotImplementedError(
|
|
102
|
+
f"{self.tool_name} global installation is not supported. "
|
|
103
|
+
"Please use project-level installation instead (--scope project)."
|
|
104
|
+
)
|
|
105
|
+
if project_root is None:
|
|
106
|
+
raise ValueError("project_root is required for PROJECT scope")
|
|
107
|
+
return project_root / "AGENTS.md"
|
|
108
|
+
|
|
109
|
+
def instruction_exists(
|
|
110
|
+
self,
|
|
111
|
+
instruction_name: str,
|
|
112
|
+
scope: InstallationScope = InstallationScope.GLOBAL,
|
|
113
|
+
project_root: Optional[Path] = None,
|
|
114
|
+
) -> bool:
|
|
115
|
+
"""Check if an instruction section exists in AGENTS.md.
|
|
116
|
+
|
|
117
|
+
Args:
|
|
118
|
+
instruction_name: Name of the instruction
|
|
119
|
+
scope: Installation scope
|
|
120
|
+
project_root: Project root path
|
|
121
|
+
|
|
122
|
+
Returns:
|
|
123
|
+
True if the instruction's section markers exist in AGENTS.md
|
|
124
|
+
"""
|
|
125
|
+
try:
|
|
126
|
+
path = self.get_instruction_path(instruction_name, scope, project_root)
|
|
127
|
+
if not path.exists():
|
|
128
|
+
return False
|
|
129
|
+
content = path.read_text(encoding="utf-8")
|
|
130
|
+
start = START_MARKER.format(name=instruction_name)
|
|
131
|
+
return start in content
|
|
132
|
+
except (FileNotFoundError, ValueError, NotImplementedError):
|
|
133
|
+
return False
|
|
134
|
+
|
|
135
|
+
def install_instruction(
|
|
136
|
+
self,
|
|
137
|
+
instruction: Instruction,
|
|
138
|
+
overwrite: bool = False,
|
|
139
|
+
scope: InstallationScope = InstallationScope.GLOBAL,
|
|
140
|
+
project_root: Optional[Path] = None,
|
|
141
|
+
) -> Path:
|
|
142
|
+
"""Install an instruction as a section in AGENTS.md.
|
|
143
|
+
|
|
144
|
+
Appends a new section with markers, or replaces an existing section
|
|
145
|
+
if overwrite is True.
|
|
146
|
+
|
|
147
|
+
Args:
|
|
148
|
+
instruction: Instruction to install
|
|
149
|
+
overwrite: Whether to overwrite existing section
|
|
150
|
+
scope: Installation scope
|
|
151
|
+
project_root: Project root path
|
|
152
|
+
|
|
153
|
+
Returns:
|
|
154
|
+
Path to AGENTS.md
|
|
155
|
+
|
|
156
|
+
Raises:
|
|
157
|
+
FileExistsError: If instruction section exists and overwrite=False
|
|
158
|
+
"""
|
|
159
|
+
path = self.get_instruction_path(instruction.name, scope, project_root)
|
|
160
|
+
|
|
161
|
+
start = START_MARKER.format(name=instruction.name)
|
|
162
|
+
end = END_MARKER.format(name=instruction.name)
|
|
163
|
+
section = f"{start}\n{instruction.content}\n{end}"
|
|
164
|
+
|
|
165
|
+
if path.exists():
|
|
166
|
+
content = path.read_text(encoding="utf-8")
|
|
167
|
+
if start in content:
|
|
168
|
+
if not overwrite:
|
|
169
|
+
raise FileExistsError(f"Instruction already exists in AGENTS.md: {instruction.name}")
|
|
170
|
+
pattern = SECTION_PATTERN.format(name=re.escape(instruction.name))
|
|
171
|
+
content = re.sub(pattern, section, content, flags=re.DOTALL)
|
|
172
|
+
path.write_text(content, encoding="utf-8")
|
|
173
|
+
return path
|
|
174
|
+
if content and not content.endswith("\n"):
|
|
175
|
+
content += "\n"
|
|
176
|
+
content += "\n" + section + "\n"
|
|
177
|
+
path.write_text(content, encoding="utf-8")
|
|
178
|
+
else:
|
|
179
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
180
|
+
path.write_text(section + "\n", encoding="utf-8")
|
|
181
|
+
|
|
182
|
+
return path
|
|
183
|
+
|
|
184
|
+
def uninstall_instruction(
|
|
185
|
+
self,
|
|
186
|
+
instruction_name: str,
|
|
187
|
+
scope: InstallationScope = InstallationScope.GLOBAL,
|
|
188
|
+
project_root: Optional[Path] = None,
|
|
189
|
+
) -> bool:
|
|
190
|
+
"""Remove an instruction section from AGENTS.md.
|
|
191
|
+
|
|
192
|
+
Args:
|
|
193
|
+
instruction_name: Name of instruction to remove
|
|
194
|
+
scope: Installation scope
|
|
195
|
+
project_root: Project root path
|
|
196
|
+
|
|
197
|
+
Returns:
|
|
198
|
+
True if section was removed, False if it didn't exist
|
|
199
|
+
"""
|
|
200
|
+
try:
|
|
201
|
+
path = self.get_instruction_path(instruction_name, scope, project_root)
|
|
202
|
+
if not path.exists():
|
|
203
|
+
return False
|
|
204
|
+
|
|
205
|
+
content = path.read_text(encoding="utf-8")
|
|
206
|
+
start = START_MARKER.format(name=instruction_name)
|
|
207
|
+
if start not in content:
|
|
208
|
+
return False
|
|
209
|
+
|
|
210
|
+
pattern = SECTION_PATTERN.format(name=re.escape(instruction_name))
|
|
211
|
+
new_content = re.sub(pattern, "", content, flags=re.DOTALL)
|
|
212
|
+
# Clean up extra blank lines
|
|
213
|
+
new_content = re.sub(r"\n{3,}", "\n\n", new_content).strip()
|
|
214
|
+
if new_content:
|
|
215
|
+
new_content += "\n"
|
|
216
|
+
path.write_text(new_content, encoding="utf-8")
|
|
217
|
+
return True
|
|
218
|
+
except (FileNotFoundError, ValueError, NotImplementedError):
|
|
219
|
+
return False
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
|
|
5
|
-
from
|
|
6
|
-
from
|
|
7
|
-
from
|
|
5
|
+
from devsync.ai_tools.base import AITool
|
|
6
|
+
from devsync.core.models import AIToolType
|
|
7
|
+
from devsync.utils.paths import get_copilot_config_dir
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
class CopilotTool(AITool):
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
|
|
5
|
-
from
|
|
6
|
-
from
|
|
7
|
-
from
|
|
5
|
+
from devsync.ai_tools.base import AITool
|
|
6
|
+
from devsync.core.models import AIToolType
|
|
7
|
+
from devsync.utils.paths import get_cursor_config_dir, get_cursor_mcp_config_path
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
class CursorTool(AITool):
|
|
@@ -2,15 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
from typing import Optional
|
|
4
4
|
|
|
5
|
-
from
|
|
6
|
-
from
|
|
7
|
-
from
|
|
8
|
-
from
|
|
9
|
-
from
|
|
10
|
-
from
|
|
11
|
-
from
|
|
12
|
-
from
|
|
13
|
-
from
|
|
5
|
+
from devsync.ai_tools.base import AITool
|
|
6
|
+
from devsync.ai_tools.claude import ClaudeTool
|
|
7
|
+
from devsync.ai_tools.cline import ClineTool
|
|
8
|
+
from devsync.ai_tools.codex import CodexTool
|
|
9
|
+
from devsync.ai_tools.copilot import CopilotTool
|
|
10
|
+
from devsync.ai_tools.cursor import CursorTool
|
|
11
|
+
from devsync.ai_tools.kiro import KiroTool
|
|
12
|
+
from devsync.ai_tools.roo import RooTool
|
|
13
|
+
from devsync.ai_tools.winsurf import WinsurfTool
|
|
14
|
+
from devsync.core.models import AIToolType
|
|
14
15
|
|
|
15
16
|
|
|
16
17
|
class AIToolDetector:
|
|
@@ -26,6 +27,7 @@ class AIToolDetector:
|
|
|
26
27
|
AIToolType.KIRO: KiroTool(),
|
|
27
28
|
AIToolType.CLINE: ClineTool(),
|
|
28
29
|
AIToolType.ROO: RooTool(),
|
|
30
|
+
AIToolType.CODEX: CodexTool(),
|
|
29
31
|
}
|
|
30
32
|
|
|
31
33
|
def detect_installed_tools(self) -> list[AITool]:
|
|
@@ -87,6 +89,7 @@ class AIToolDetector:
|
|
|
87
89
|
AIToolType.KIRO,
|
|
88
90
|
AIToolType.CLINE,
|
|
89
91
|
AIToolType.ROO,
|
|
92
|
+
AIToolType.CODEX,
|
|
90
93
|
]
|
|
91
94
|
|
|
92
95
|
for tool_type in priority:
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
|
|
5
|
-
from
|
|
6
|
-
from
|
|
7
|
-
from
|
|
5
|
+
from devsync.ai_tools.base import AITool
|
|
6
|
+
from devsync.core.models import AIToolType
|
|
7
|
+
from devsync.utils.paths import get_kiro_config_dir
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
class KiroTool(AITool):
|
|
@@ -5,12 +5,13 @@ import logging
|
|
|
5
5
|
from pathlib import Path
|
|
6
6
|
from typing import Any, Optional
|
|
7
7
|
|
|
8
|
-
from
|
|
9
|
-
from
|
|
10
|
-
from
|
|
11
|
-
from
|
|
12
|
-
from
|
|
13
|
-
from
|
|
8
|
+
from devsync.ai_tools.base import AITool
|
|
9
|
+
from devsync.ai_tools.detector import AIToolDetector
|
|
10
|
+
from devsync.core.mcp.credentials import CredentialManager
|
|
11
|
+
from devsync.core.mcp.manager import MCPManager
|
|
12
|
+
from devsync.core.models import EnvironmentConfig, InstallationScope, MCPServer, MCPTemplate
|
|
13
|
+
from devsync.utils.atomic_write import atomic_write
|
|
14
|
+
from devsync.utils.paths import _resolve_data_dir
|
|
14
15
|
|
|
15
16
|
logger = logging.getLogger(__name__)
|
|
16
17
|
|
|
@@ -61,10 +62,10 @@ class MCPSyncer:
|
|
|
61
62
|
Initialize MCP syncer.
|
|
62
63
|
|
|
63
64
|
Args:
|
|
64
|
-
library_root: Root directory for MCP library (defaults to ~/.
|
|
65
|
+
library_root: Root directory for MCP library (defaults to ~/.devsync/library/)
|
|
65
66
|
project_root: Project root directory (defaults to current directory)
|
|
66
67
|
"""
|
|
67
|
-
self.library_root = library_root or Path.home()
|
|
68
|
+
self.library_root = library_root or _resolve_data_dir(Path.home(), ".devsync", [".instructionkit"]) / "library"
|
|
68
69
|
self.project_root = project_root or Path.cwd()
|
|
69
70
|
|
|
70
71
|
self.mcp_manager = MCPManager(self.library_root)
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
|
|
5
|
-
from
|
|
6
|
-
from
|
|
7
|
-
from
|
|
5
|
+
from devsync.ai_tools.base import AITool
|
|
6
|
+
from devsync.core.models import AIToolType
|
|
7
|
+
from devsync.utils.paths import get_roo_config_dir
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
class RooTool(AITool):
|
|
@@ -61,7 +61,7 @@ class RooTool(AITool):
|
|
|
61
61
|
"""
|
|
62
62
|
if not self.is_installed():
|
|
63
63
|
raise FileNotFoundError(f"{self.tool_name} is not installed")
|
|
64
|
-
from
|
|
64
|
+
from devsync.utils.paths import get_home_directory
|
|
65
65
|
|
|
66
66
|
global_dir = get_home_directory() / ".roo" / "rules"
|
|
67
67
|
global_dir.mkdir(parents=True, exist_ok=True)
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
|
|
5
|
-
from
|
|
6
|
-
from
|
|
7
|
-
from
|
|
5
|
+
from devsync.ai_tools.base import AITool
|
|
6
|
+
from devsync.core.models import AIToolType
|
|
7
|
+
from devsync.utils.paths import get_windsurf_mcp_config_path, get_winsurf_config_dir
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
class WinsurfTool(AITool):
|
|
@@ -4,9 +4,9 @@ import typer
|
|
|
4
4
|
from rich.console import Console
|
|
5
5
|
from rich.prompt import Confirm
|
|
6
6
|
|
|
7
|
-
from
|
|
8
|
-
from
|
|
9
|
-
from
|
|
7
|
+
from devsync.storage.library import LibraryManager
|
|
8
|
+
from devsync.storage.tracker import InstallationTracker
|
|
9
|
+
from devsync.utils.ui import print_error, print_success, print_warning
|
|
10
10
|
|
|
11
11
|
console = Console()
|
|
12
12
|
|
|
@@ -9,12 +9,12 @@ import typer
|
|
|
9
9
|
from rich.console import Console
|
|
10
10
|
from rich.progress import Progress, SpinnerColumn, TextColumn
|
|
11
11
|
|
|
12
|
-
from
|
|
13
|
-
from
|
|
14
|
-
from
|
|
15
|
-
from
|
|
16
|
-
from
|
|
17
|
-
from
|
|
12
|
+
from devsync.core.checksum import calculate_file_checksum
|
|
13
|
+
from devsync.core.git_operations import GitOperations, RepositoryOperationError
|
|
14
|
+
from devsync.core.models import LibraryInstruction
|
|
15
|
+
from devsync.core.repository import RepositoryParser
|
|
16
|
+
from devsync.storage.library import LibraryManager
|
|
17
|
+
from devsync.utils.ui import print_error, print_success
|
|
18
18
|
|
|
19
19
|
console = Console()
|
|
20
20
|
|
|
@@ -7,22 +7,22 @@ from typing import Optional
|
|
|
7
7
|
from rich.console import Console
|
|
8
8
|
from rich.progress import Progress, SpinnerColumn, TextColumn
|
|
9
9
|
|
|
10
|
-
from
|
|
11
|
-
from
|
|
12
|
-
from
|
|
13
|
-
from
|
|
10
|
+
from devsync.ai_tools.base import AITool
|
|
11
|
+
from devsync.ai_tools.detector import get_detector
|
|
12
|
+
from devsync.core.checksum import ChecksumValidator
|
|
13
|
+
from devsync.core.conflict_resolution import (
|
|
14
14
|
ConflictResolver,
|
|
15
15
|
)
|
|
16
|
-
from
|
|
17
|
-
from
|
|
16
|
+
from devsync.core.git_operations import GitOperations
|
|
17
|
+
from devsync.core.models import (
|
|
18
18
|
ConflictResolution,
|
|
19
19
|
InstallationRecord,
|
|
20
20
|
InstallationScope,
|
|
21
21
|
)
|
|
22
|
-
from
|
|
23
|
-
from
|
|
24
|
-
from
|
|
25
|
-
from
|
|
22
|
+
from devsync.core.repository import RepositoryParser
|
|
23
|
+
from devsync.storage.tracker import InstallationTracker
|
|
24
|
+
from devsync.utils.project import find_project_root
|
|
25
|
+
from devsync.utils.validation import is_valid_git_url, normalize_repo_url
|
|
26
26
|
|
|
27
27
|
console = Console()
|
|
28
28
|
|