devsync 0.10.0__tar.gz → 0.12.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.
Files changed (127) hide show
  1. devsync-0.12.0/PKG-INFO +136 -0
  2. devsync-0.12.0/README.md +97 -0
  3. devsync-0.12.0/devsync/ai_tools/anteroom.py +216 -0
  4. {devsync-0.10.0 → devsync-0.12.0}/devsync/ai_tools/capability_registry.py +18 -0
  5. {devsync-0.10.0 → devsync-0.12.0}/devsync/ai_tools/detector.py +3 -0
  6. {devsync-0.10.0 → devsync-0.12.0}/devsync/ai_tools/translator.py +29 -0
  7. devsync-0.12.0/devsync/cli/extract.py +232 -0
  8. devsync-0.12.0/devsync/cli/install_v2.py +274 -0
  9. devsync-0.12.0/devsync/cli/list_v2.py +92 -0
  10. devsync-0.12.0/devsync/cli/main.py +251 -0
  11. devsync-0.12.0/devsync/cli/setup.py +69 -0
  12. devsync-0.12.0/devsync/core/adapter.py +181 -0
  13. {devsync-0.10.0 → devsync-0.12.0}/devsync/core/component_detector.py +1 -0
  14. devsync-0.12.0/devsync/core/extractor.py +178 -0
  15. devsync-0.12.0/devsync/core/mcp_credential_prompter.py +129 -0
  16. {devsync-0.10.0 → devsync-0.12.0}/devsync/core/models.py +2 -0
  17. devsync-0.12.0/devsync/core/package_manifest_v2.py +240 -0
  18. devsync-0.12.0/devsync/core/practice.py +174 -0
  19. devsync-0.12.0/devsync/llm/__init__.py +13 -0
  20. devsync-0.12.0/devsync/llm/anthropic.py +97 -0
  21. devsync-0.12.0/devsync/llm/config.py +91 -0
  22. devsync-0.12.0/devsync/llm/openai_provider.py +98 -0
  23. devsync-0.12.0/devsync/llm/openrouter.py +100 -0
  24. devsync-0.12.0/devsync/llm/prompts.py +116 -0
  25. devsync-0.12.0/devsync/llm/provider.py +137 -0
  26. devsync-0.12.0/devsync/llm/response_models.py +186 -0
  27. devsync-0.12.0/devsync/tui/__init__.py +1 -0
  28. {devsync-0.10.0 → devsync-0.12.0}/devsync/utils/paths.py +8 -7
  29. devsync-0.12.0/devsync.egg-info/PKG-INFO +136 -0
  30. {devsync-0.10.0 → devsync-0.12.0}/devsync.egg-info/SOURCES.txt +18 -25
  31. {devsync-0.10.0 → devsync-0.12.0}/devsync.egg-info/requires.txt +1 -0
  32. {devsync-0.10.0 → devsync-0.12.0}/pyproject.toml +3 -2
  33. devsync-0.10.0/PKG-INFO +0 -490
  34. devsync-0.10.0/README.md +0 -452
  35. devsync-0.10.0/devsync/cli/delete.py +0 -118
  36. devsync-0.10.0/devsync/cli/download.py +0 -274
  37. devsync-0.10.0/devsync/cli/install.py +0 -237
  38. devsync-0.10.0/devsync/cli/install_new.py +0 -937
  39. devsync-0.10.0/devsync/cli/list.py +0 -275
  40. devsync-0.10.0/devsync/cli/main.py +0 -454
  41. devsync-0.10.0/devsync/cli/mcp_configure.py +0 -233
  42. devsync-0.10.0/devsync/cli/mcp_install.py +0 -167
  43. devsync-0.10.0/devsync/cli/mcp_sync.py +0 -166
  44. devsync-0.10.0/devsync/cli/package.py +0 -386
  45. devsync-0.10.0/devsync/cli/package_create.py +0 -323
  46. devsync-0.10.0/devsync/cli/package_install.py +0 -474
  47. devsync-0.10.0/devsync/cli/template.py +0 -19
  48. devsync-0.10.0/devsync/cli/template_backup.py +0 -262
  49. devsync-0.10.0/devsync/cli/template_init.py +0 -499
  50. devsync-0.10.0/devsync/cli/template_install.py +0 -263
  51. devsync-0.10.0/devsync/cli/template_list.py +0 -172
  52. devsync-0.10.0/devsync/cli/template_uninstall.py +0 -146
  53. devsync-0.10.0/devsync/cli/template_update.py +0 -225
  54. devsync-0.10.0/devsync/cli/template_validate.py +0 -234
  55. devsync-0.10.0/devsync/cli/update.py +0 -309
  56. devsync-0.10.0/devsync/core/template_manifest.py +0 -283
  57. devsync-0.10.0/devsync/storage/library.py +0 -429
  58. devsync-0.10.0/devsync/storage/template_library.py +0 -231
  59. devsync-0.10.0/devsync/storage/template_tracker.py +0 -297
  60. devsync-0.10.0/devsync/tui/__init__.py +0 -5
  61. devsync-0.10.0/devsync/tui/installer.py +0 -511
  62. devsync-0.10.0/devsync.egg-info/PKG-INFO +0 -490
  63. {devsync-0.10.0 → devsync-0.12.0}/LICENSE +0 -0
  64. {devsync-0.10.0 → devsync-0.12.0}/devsync/__init__.py +0 -0
  65. {devsync-0.10.0 → devsync-0.12.0}/devsync/__main__.py +0 -0
  66. {devsync-0.10.0 → devsync-0.12.0}/devsync/ai_tools/__init__.py +0 -0
  67. {devsync-0.10.0 → devsync-0.12.0}/devsync/ai_tools/aider.py +0 -0
  68. {devsync-0.10.0 → devsync-0.12.0}/devsync/ai_tools/amazonq.py +0 -0
  69. {devsync-0.10.0 → devsync-0.12.0}/devsync/ai_tools/amp.py +0 -0
  70. {devsync-0.10.0 → devsync-0.12.0}/devsync/ai_tools/antigravity.py +0 -0
  71. {devsync-0.10.0 → devsync-0.12.0}/devsync/ai_tools/augment.py +0 -0
  72. {devsync-0.10.0 → devsync-0.12.0}/devsync/ai_tools/base.py +0 -0
  73. {devsync-0.10.0 → devsync-0.12.0}/devsync/ai_tools/claude.py +0 -0
  74. {devsync-0.10.0 → devsync-0.12.0}/devsync/ai_tools/claude_desktop.py +0 -0
  75. {devsync-0.10.0 → devsync-0.12.0}/devsync/ai_tools/cline.py +0 -0
  76. {devsync-0.10.0 → devsync-0.12.0}/devsync/ai_tools/codex.py +0 -0
  77. {devsync-0.10.0 → devsync-0.12.0}/devsync/ai_tools/continuedev.py +0 -0
  78. {devsync-0.10.0 → devsync-0.12.0}/devsync/ai_tools/copilot.py +0 -0
  79. {devsync-0.10.0 → devsync-0.12.0}/devsync/ai_tools/cursor.py +0 -0
  80. {devsync-0.10.0 → devsync-0.12.0}/devsync/ai_tools/gemini.py +0 -0
  81. {devsync-0.10.0 → devsync-0.12.0}/devsync/ai_tools/jetbrains.py +0 -0
  82. {devsync-0.10.0 → devsync-0.12.0}/devsync/ai_tools/junie.py +0 -0
  83. {devsync-0.10.0 → devsync-0.12.0}/devsync/ai_tools/kiro.py +0 -0
  84. {devsync-0.10.0 → devsync-0.12.0}/devsync/ai_tools/mcp_syncer.py +0 -0
  85. {devsync-0.10.0 → devsync-0.12.0}/devsync/ai_tools/opencode.py +0 -0
  86. {devsync-0.10.0 → devsync-0.12.0}/devsync/ai_tools/openhands.py +0 -0
  87. {devsync-0.10.0 → devsync-0.12.0}/devsync/ai_tools/roo.py +0 -0
  88. {devsync-0.10.0 → devsync-0.12.0}/devsync/ai_tools/tabnine.py +0 -0
  89. {devsync-0.10.0 → devsync-0.12.0}/devsync/ai_tools/trae.py +0 -0
  90. {devsync-0.10.0 → devsync-0.12.0}/devsync/ai_tools/winsurf.py +0 -0
  91. {devsync-0.10.0 → devsync-0.12.0}/devsync/ai_tools/zed.py +0 -0
  92. {devsync-0.10.0 → devsync-0.12.0}/devsync/cli/__init__.py +0 -0
  93. {devsync-0.10.0 → devsync-0.12.0}/devsync/cli/tools.py +0 -0
  94. {devsync-0.10.0 → devsync-0.12.0}/devsync/cli/uninstall.py +0 -0
  95. {devsync-0.10.0 → devsync-0.12.0}/devsync/core/__init__.py +0 -0
  96. {devsync-0.10.0 → devsync-0.12.0}/devsync/core/checksum.py +0 -0
  97. {devsync-0.10.0 → devsync-0.12.0}/devsync/core/conflict_resolution.py +0 -0
  98. {devsync-0.10.0 → devsync-0.12.0}/devsync/core/git_operations.py +0 -0
  99. {devsync-0.10.0 → devsync-0.12.0}/devsync/core/mcp/__init__.py +0 -0
  100. {devsync-0.10.0 → devsync-0.12.0}/devsync/core/mcp/credentials.py +0 -0
  101. {devsync-0.10.0 → devsync-0.12.0}/devsync/core/mcp/manager.py +0 -0
  102. {devsync-0.10.0 → devsync-0.12.0}/devsync/core/mcp/set_manager.py +0 -0
  103. {devsync-0.10.0 → devsync-0.12.0}/devsync/core/mcp/validator.py +0 -0
  104. {devsync-0.10.0 → devsync-0.12.0}/devsync/core/package_creator.py +0 -0
  105. {devsync-0.10.0 → devsync-0.12.0}/devsync/core/package_manifest.py +0 -0
  106. {devsync-0.10.0 → devsync-0.12.0}/devsync/core/repository.py +0 -0
  107. {devsync-0.10.0 → devsync-0.12.0}/devsync/core/secret_detector.py +0 -0
  108. {devsync-0.10.0 → devsync-0.12.0}/devsync/core/version.py +0 -0
  109. {devsync-0.10.0 → devsync-0.12.0}/devsync/storage/__init__.py +0 -0
  110. {devsync-0.10.0 → devsync-0.12.0}/devsync/storage/mcp_tracker.py +0 -0
  111. {devsync-0.10.0 → devsync-0.12.0}/devsync/storage/package_tracker.py +0 -0
  112. {devsync-0.10.0 → devsync-0.12.0}/devsync/storage/tracker.py +0 -0
  113. {devsync-0.10.0 → devsync-0.12.0}/devsync/utils/__init__.py +0 -0
  114. {devsync-0.10.0 → devsync-0.12.0}/devsync/utils/atomic_write.py +0 -0
  115. {devsync-0.10.0 → devsync-0.12.0}/devsync/utils/backup.py +0 -0
  116. {devsync-0.10.0 → devsync-0.12.0}/devsync/utils/dotenv.py +0 -0
  117. {devsync-0.10.0 → devsync-0.12.0}/devsync/utils/git_helpers.py +0 -0
  118. {devsync-0.10.0 → devsync-0.12.0}/devsync/utils/logging.py +0 -0
  119. {devsync-0.10.0 → devsync-0.12.0}/devsync/utils/namespace.py +0 -0
  120. {devsync-0.10.0 → devsync-0.12.0}/devsync/utils/project.py +0 -0
  121. {devsync-0.10.0 → devsync-0.12.0}/devsync/utils/streaming.py +0 -0
  122. {devsync-0.10.0 → devsync-0.12.0}/devsync/utils/ui.py +0 -0
  123. {devsync-0.10.0 → devsync-0.12.0}/devsync/utils/validation.py +0 -0
  124. {devsync-0.10.0 → devsync-0.12.0}/devsync.egg-info/dependency_links.txt +0 -0
  125. {devsync-0.10.0 → devsync-0.12.0}/devsync.egg-info/entry_points.txt +0 -0
  126. {devsync-0.10.0 → devsync-0.12.0}/devsync.egg-info/top_level.txt +0 -0
  127. {devsync-0.10.0 → devsync-0.12.0}/setup.cfg +0 -0
@@ -0,0 +1,136 @@
1
+ Metadata-Version: 2.1
2
+ Name: devsync
3
+ Version: 0.12.0
4
+ Summary: Distribute and sync dev tool configurations across teams
5
+ Author-email: Troy Larson <troy@calvinware.com>
6
+ License: MIT License
7
+ Project-URL: Homepage, https://github.com/troylar/devsync
8
+ Project-URL: Documentation, https://devsync.readthedocs.io
9
+ Project-URL: Repository, https://github.com/troylar/devsync
10
+ Project-URL: Issues, https://github.com/troylar/devsync/issues
11
+ Keywords: cli,ai,config,mcp,cursor,copilot,claude,cline,codex,kiro,roo,windsurf
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Requires-Python: >=3.10
20
+ Description-Content-Type: text/markdown
21
+ License-File: LICENSE
22
+ Requires-Dist: typer[all]>=0.15.0
23
+ Requires-Dist: rich>=14.0.0
24
+ Requires-Dist: pyyaml>=6.0
25
+ Requires-Dist: textual>=6.0.0
26
+ Requires-Dist: GitPython>=3.1.45
27
+ Requires-Dist: python-dotenv>=1.0.0
28
+ Requires-Dist: httpx>=0.27.0
29
+ Provides-Extra: dev
30
+ Requires-Dist: pytest>=8.0.0; extra == "dev"
31
+ Requires-Dist: pytest-cov>=5.0.0; extra == "dev"
32
+ Requires-Dist: black==24.10.0; extra == "dev"
33
+ Requires-Dist: mypy>=1.10.0; extra == "dev"
34
+ Requires-Dist: ruff>=0.9.0; extra == "dev"
35
+ Requires-Dist: invoke>=2.0.0; extra == "dev"
36
+ Requires-Dist: build>=1.2.1; extra == "dev"
37
+ Requires-Dist: twine>=5.0.0; extra == "dev"
38
+ Requires-Dist: types-PyYAML>=6.0.12.20240808; extra == "dev"
39
+
40
+ <div align="center">
41
+
42
+ # DevSync
43
+
44
+ **AI-powered config distribution for AI coding assistants**
45
+
46
+ [![CI](https://github.com/troylar/devsync/actions/workflows/ci.yml/badge.svg)](https://github.com/troylar/devsync/actions/workflows/ci.yml)
47
+ [![Docs](https://readthedocs.org/projects/devsync/badge/?version=latest)](https://devsync.readthedocs.io)
48
+ [![PyPI version](https://img.shields.io/pypi/v/devsync.svg)](https://pypi.org/project/devsync/)
49
+ [![Coverage](https://codecov.io/gh/troylar/devsync/branch/main/graph/badge.svg)](https://codecov.io/gh/troylar/devsync)
50
+ [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
51
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
52
+
53
+ **Works with:** Aider | Amazon Q | Amp | Antigravity | Augment | Claude Code | Claude Desktop | Cline | Codex CLI | Continue.dev | Cursor | Gemini CLI | GitHub Copilot | JetBrains AI | Junie | Kiro | OpenCode | OpenHands | Roo Code | Tabnine | Trae | Windsurf | Zed
54
+
55
+ </div>
56
+
57
+ ---
58
+
59
+ DevSync uses LLM intelligence to extract coding practices from projects and adapt them to recipients' existing setups -- across 23+ AI coding assistants. Two commands: `extract` and `install`.
60
+
61
+ ## Quick Start
62
+
63
+ ```bash
64
+ pip install devsync
65
+
66
+ # One-time: configure your LLM provider
67
+ devsync setup
68
+
69
+ # Check detected AI tools
70
+ devsync tools
71
+
72
+ # Extract practices from a project
73
+ devsync extract
74
+
75
+ # Install a package into another project
76
+ devsync install ./team-standards
77
+
78
+ # Install from Git
79
+ devsync install https://github.com/company/standards
80
+ ```
81
+
82
+ No API key? DevSync works without one -- it falls back to file-copy mode. Add `--no-ai` to any command to force this.
83
+
84
+ ## Features
85
+
86
+ - **AI-powered extraction** -- LLM reads your project's rules, MCP configs, and commands to produce abstract practice declarations
87
+ - **AI-powered installation** -- LLM adapts incoming practices to your existing setup with intelligent merging
88
+ - **23+ AI tool integrations** -- Claude Code, Cursor, Windsurf, GitHub Copilot, Kiro, Roo Code, Cline, Codex, and more
89
+ - **MCP credential handling** -- prompts for credentials at install time, never stores them in repos
90
+ - **v1 backward compatibility** -- old `ai-config-kit-package.yaml` packages still install via file-copy
91
+ - **Graceful degradation** -- works without an API key, `--no-ai` flag for explicit file-copy mode
92
+
93
+ ## Commands
94
+
95
+ | Command | Description |
96
+ |---------|-------------|
97
+ | `devsync setup` | Configure LLM provider (Anthropic, OpenAI, OpenRouter) |
98
+ | `devsync tools` | Detect installed AI coding tools |
99
+ | `devsync extract` | Extract practices from current project into a shareable package |
100
+ | `devsync install <source>` | Install a package with AI-powered adaptation |
101
+ | `devsync list` | Show installed packages |
102
+ | `devsync uninstall <name>` | Remove an installed package |
103
+
104
+ ## Migrating from v1
105
+
106
+ If you have v1 packages (`ai-config-kit-package.yaml`), they still work with `devsync install`. To upgrade them to v2 format:
107
+
108
+ ```bash
109
+ devsync extract --upgrade ./old-package
110
+ ```
111
+
112
+ ## Documentation
113
+
114
+ Full documentation at **[devsync.readthedocs.io](https://devsync.readthedocs.io)**:
115
+
116
+ - [Getting Started](https://devsync.readthedocs.io/getting-started/installation/) -- installation, quickstart, core concepts
117
+ - [CLI Reference](https://devsync.readthedocs.io/cli/) -- all commands with examples
118
+ - [IDE Integrations](https://devsync.readthedocs.io/ide-integrations/) -- setup guides for each AI tool
119
+ - [Packages](https://devsync.readthedocs.io/packages/) -- creating and installing config packages
120
+ - [MCP Server](https://devsync.readthedocs.io/mcp-server/) -- MCP configuration management
121
+ - [Tutorials](https://devsync.readthedocs.io/tutorials/team-config-repo/) -- step-by-step walkthroughs
122
+
123
+ ## Contributing
124
+
125
+ ```bash
126
+ git clone https://github.com/troylar/devsync.git
127
+ cd devsync
128
+ pip install -e .[dev]
129
+ invoke test
130
+ ```
131
+
132
+ See the [contributing guide](https://devsync.readthedocs.io/advanced/contributing/) for details.
133
+
134
+ ## License
135
+
136
+ MIT -- see [LICENSE](LICENSE).
@@ -0,0 +1,97 @@
1
+ <div align="center">
2
+
3
+ # DevSync
4
+
5
+ **AI-powered config distribution for AI coding assistants**
6
+
7
+ [![CI](https://github.com/troylar/devsync/actions/workflows/ci.yml/badge.svg)](https://github.com/troylar/devsync/actions/workflows/ci.yml)
8
+ [![Docs](https://readthedocs.org/projects/devsync/badge/?version=latest)](https://devsync.readthedocs.io)
9
+ [![PyPI version](https://img.shields.io/pypi/v/devsync.svg)](https://pypi.org/project/devsync/)
10
+ [![Coverage](https://codecov.io/gh/troylar/devsync/branch/main/graph/badge.svg)](https://codecov.io/gh/troylar/devsync)
11
+ [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
12
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
13
+
14
+ **Works with:** Aider | Amazon Q | Amp | Antigravity | Augment | Claude Code | Claude Desktop | Cline | Codex CLI | Continue.dev | Cursor | Gemini CLI | GitHub Copilot | JetBrains AI | Junie | Kiro | OpenCode | OpenHands | Roo Code | Tabnine | Trae | Windsurf | Zed
15
+
16
+ </div>
17
+
18
+ ---
19
+
20
+ DevSync uses LLM intelligence to extract coding practices from projects and adapt them to recipients' existing setups -- across 23+ AI coding assistants. Two commands: `extract` and `install`.
21
+
22
+ ## Quick Start
23
+
24
+ ```bash
25
+ pip install devsync
26
+
27
+ # One-time: configure your LLM provider
28
+ devsync setup
29
+
30
+ # Check detected AI tools
31
+ devsync tools
32
+
33
+ # Extract practices from a project
34
+ devsync extract
35
+
36
+ # Install a package into another project
37
+ devsync install ./team-standards
38
+
39
+ # Install from Git
40
+ devsync install https://github.com/company/standards
41
+ ```
42
+
43
+ No API key? DevSync works without one -- it falls back to file-copy mode. Add `--no-ai` to any command to force this.
44
+
45
+ ## Features
46
+
47
+ - **AI-powered extraction** -- LLM reads your project's rules, MCP configs, and commands to produce abstract practice declarations
48
+ - **AI-powered installation** -- LLM adapts incoming practices to your existing setup with intelligent merging
49
+ - **23+ AI tool integrations** -- Claude Code, Cursor, Windsurf, GitHub Copilot, Kiro, Roo Code, Cline, Codex, and more
50
+ - **MCP credential handling** -- prompts for credentials at install time, never stores them in repos
51
+ - **v1 backward compatibility** -- old `ai-config-kit-package.yaml` packages still install via file-copy
52
+ - **Graceful degradation** -- works without an API key, `--no-ai` flag for explicit file-copy mode
53
+
54
+ ## Commands
55
+
56
+ | Command | Description |
57
+ |---------|-------------|
58
+ | `devsync setup` | Configure LLM provider (Anthropic, OpenAI, OpenRouter) |
59
+ | `devsync tools` | Detect installed AI coding tools |
60
+ | `devsync extract` | Extract practices from current project into a shareable package |
61
+ | `devsync install <source>` | Install a package with AI-powered adaptation |
62
+ | `devsync list` | Show installed packages |
63
+ | `devsync uninstall <name>` | Remove an installed package |
64
+
65
+ ## Migrating from v1
66
+
67
+ If you have v1 packages (`ai-config-kit-package.yaml`), they still work with `devsync install`. To upgrade them to v2 format:
68
+
69
+ ```bash
70
+ devsync extract --upgrade ./old-package
71
+ ```
72
+
73
+ ## Documentation
74
+
75
+ Full documentation at **[devsync.readthedocs.io](https://devsync.readthedocs.io)**:
76
+
77
+ - [Getting Started](https://devsync.readthedocs.io/getting-started/installation/) -- installation, quickstart, core concepts
78
+ - [CLI Reference](https://devsync.readthedocs.io/cli/) -- all commands with examples
79
+ - [IDE Integrations](https://devsync.readthedocs.io/ide-integrations/) -- setup guides for each AI tool
80
+ - [Packages](https://devsync.readthedocs.io/packages/) -- creating and installing config packages
81
+ - [MCP Server](https://devsync.readthedocs.io/mcp-server/) -- MCP configuration management
82
+ - [Tutorials](https://devsync.readthedocs.io/tutorials/team-config-repo/) -- step-by-step walkthroughs
83
+
84
+ ## Contributing
85
+
86
+ ```bash
87
+ git clone https://github.com/troylar/devsync.git
88
+ cd devsync
89
+ pip install -e .[dev]
90
+ invoke test
91
+ ```
92
+
93
+ See the [contributing guide](https://devsync.readthedocs.io/advanced/contributing/) for details.
94
+
95
+ ## License
96
+
97
+ MIT -- see [LICENSE](LICENSE).
@@ -0,0 +1,216 @@
1
+ """Anteroom 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 AnteroomTool(AITool):
17
+ """Integration for Anteroom.
18
+
19
+ Anteroom reads a single ANTEROOM.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.ANTEROOM
31
+
32
+ @property
33
+ def tool_name(self) -> str:
34
+ """Return human-readable tool name."""
35
+ return "Anteroom"
36
+
37
+ def is_installed(self) -> bool:
38
+ """Check if Anteroom is installed on the system.
39
+
40
+ Returns:
41
+ True if aroom binary is found on PATH or ~/.anteroom/ exists
42
+ """
43
+ if shutil.which("aroom") is not None:
44
+ return True
45
+ config_dir = Path.home() / ".anteroom"
46
+ return config_dir.exists()
47
+
48
+ def get_instructions_directory(self) -> Path:
49
+ """Get the directory where instructions should be installed.
50
+
51
+ Raises:
52
+ NotImplementedError: Anteroom only supports project-level installation
53
+ """
54
+ raise NotImplementedError(
55
+ f"{self.tool_name} global installation is not supported. "
56
+ "Anteroom uses project-level ANTEROOM.md only. "
57
+ "Please use project-level installation instead (--scope project)."
58
+ )
59
+
60
+ def get_instruction_file_extension(self) -> str:
61
+ """Get the file extension for Anteroom instructions.
62
+
63
+ Returns:
64
+ File extension including the dot
65
+ """
66
+ return ".md"
67
+
68
+ def get_project_instructions_directory(self, project_root: Path) -> Path:
69
+ """Get the directory for project-specific Anteroom instructions.
70
+
71
+ ANTEROOM.md lives at the project root.
72
+
73
+ Args:
74
+ project_root: Path to the project root directory
75
+
76
+ Returns:
77
+ Path to project root
78
+ """
79
+ return project_root
80
+
81
+ def get_instruction_path(
82
+ self,
83
+ instruction_name: str,
84
+ scope: InstallationScope = InstallationScope.GLOBAL,
85
+ project_root: Optional[Path] = None,
86
+ ) -> Path:
87
+ """Get the path to ANTEROOM.md.
88
+
89
+ Args:
90
+ instruction_name: Name of the instruction (unused for path)
91
+ scope: Installation scope (must be PROJECT)
92
+ project_root: Project root path
93
+
94
+ Returns:
95
+ Path to ANTEROOM.md
96
+
97
+ Raises:
98
+ ValueError: If scope is PROJECT but project_root is None
99
+ NotImplementedError: If scope is GLOBAL
100
+ """
101
+ if scope == InstallationScope.GLOBAL:
102
+ raise NotImplementedError(
103
+ f"{self.tool_name} global installation is not supported. "
104
+ "Please use project-level installation instead (--scope project)."
105
+ )
106
+ if project_root is None:
107
+ raise ValueError("project_root is required for PROJECT scope")
108
+ return project_root / "ANTEROOM.md"
109
+
110
+ def instruction_exists(
111
+ self,
112
+ instruction_name: str,
113
+ scope: InstallationScope = InstallationScope.GLOBAL,
114
+ project_root: Optional[Path] = None,
115
+ ) -> bool:
116
+ """Check if an instruction section exists in ANTEROOM.md.
117
+
118
+ Args:
119
+ instruction_name: Name of the instruction
120
+ scope: Installation scope
121
+ project_root: Project root path
122
+
123
+ Returns:
124
+ True if the instruction's section markers exist in ANTEROOM.md
125
+ """
126
+ try:
127
+ path = self.get_instruction_path(instruction_name, scope, project_root)
128
+ if not path.exists():
129
+ return False
130
+ content = path.read_text(encoding="utf-8")
131
+ start = START_MARKER.format(name=instruction_name)
132
+ return start in content
133
+ except (FileNotFoundError, ValueError, NotImplementedError):
134
+ return False
135
+
136
+ def install_instruction(
137
+ self,
138
+ instruction: Instruction,
139
+ overwrite: bool = False,
140
+ scope: InstallationScope = InstallationScope.GLOBAL,
141
+ project_root: Optional[Path] = None,
142
+ ) -> Path:
143
+ """Install an instruction as a section in ANTEROOM.md.
144
+
145
+ Args:
146
+ instruction: Instruction to install
147
+ overwrite: Whether to overwrite existing section
148
+ scope: Installation scope
149
+ project_root: Project root path
150
+
151
+ Returns:
152
+ Path to ANTEROOM.md
153
+
154
+ Raises:
155
+ FileExistsError: If instruction section exists and overwrite=False
156
+ """
157
+ path = self.get_instruction_path(instruction.name, scope, project_root)
158
+
159
+ start = START_MARKER.format(name=instruction.name)
160
+ end = END_MARKER.format(name=instruction.name)
161
+ section = f"{start}\n{instruction.content}\n{end}"
162
+
163
+ if path.exists():
164
+ content = path.read_text(encoding="utf-8")
165
+ if start in content:
166
+ if not overwrite:
167
+ raise FileExistsError(f"Instruction already exists in ANTEROOM.md: {instruction.name}")
168
+ pattern = SECTION_PATTERN.format(name=re.escape(instruction.name))
169
+ content = re.sub(pattern, section, content, flags=re.DOTALL)
170
+ path.write_text(content, encoding="utf-8")
171
+ return path
172
+ if content and not content.endswith("\n"):
173
+ content += "\n"
174
+ content += "\n" + section + "\n"
175
+ path.write_text(content, encoding="utf-8")
176
+ else:
177
+ path.parent.mkdir(parents=True, exist_ok=True)
178
+ path.write_text(section + "\n", encoding="utf-8")
179
+
180
+ return path
181
+
182
+ def uninstall_instruction(
183
+ self,
184
+ instruction_name: str,
185
+ scope: InstallationScope = InstallationScope.GLOBAL,
186
+ project_root: Optional[Path] = None,
187
+ ) -> bool:
188
+ """Remove an instruction section from ANTEROOM.md.
189
+
190
+ Args:
191
+ instruction_name: Name of instruction to remove
192
+ scope: Installation scope
193
+ project_root: Project root path
194
+
195
+ Returns:
196
+ True if section was removed, False if it didn't exist
197
+ """
198
+ try:
199
+ path = self.get_instruction_path(instruction_name, scope, project_root)
200
+ if not path.exists():
201
+ return False
202
+
203
+ content = path.read_text(encoding="utf-8")
204
+ start = START_MARKER.format(name=instruction_name)
205
+ if start not in content:
206
+ return False
207
+
208
+ pattern = SECTION_PATTERN.format(name=re.escape(instruction_name))
209
+ new_content = re.sub(pattern, "", content, flags=re.DOTALL)
210
+ new_content = re.sub(r"\n{3,}", "\n\n", new_content).strip()
211
+ if new_content:
212
+ new_content += "\n"
213
+ path.write_text(new_content, encoding="utf-8")
214
+ return True
215
+ except (FileNotFoundError, ValueError, NotImplementedError):
216
+ return False
@@ -461,6 +461,24 @@ CAPABILITY_REGISTRY: dict[AIToolType, IDECapability] = {
461
461
  "DevSync manages sections within this file using HTML comment markers."
462
462
  ),
463
463
  ),
464
+ AIToolType.ANTEROOM: IDECapability(
465
+ tool_type=AIToolType.ANTEROOM,
466
+ tool_name="Anteroom",
467
+ supported_components={
468
+ ComponentType.INSTRUCTION,
469
+ ComponentType.RESOURCE,
470
+ },
471
+ instructions_directory="",
472
+ instruction_file_extension=".md",
473
+ supports_project_scope=True,
474
+ supports_global_scope=False,
475
+ mcp_config_path=None,
476
+ mcp_project_config_path=None,
477
+ notes=(
478
+ "Anteroom uses a single ANTEROOM.md file at the project root. "
479
+ "DevSync manages sections within this file using HTML comment markers."
480
+ ),
481
+ ),
464
482
  AIToolType.COPILOT: IDECapability(
465
483
  tool_type=AIToolType.COPILOT,
466
484
  tool_name="GitHub Copilot",
@@ -5,6 +5,7 @@ from typing import Optional
5
5
  from devsync.ai_tools.aider import AiderTool
6
6
  from devsync.ai_tools.amazonq import AmazonQTool
7
7
  from devsync.ai_tools.amp import AmpTool
8
+ from devsync.ai_tools.anteroom import AnteroomTool
8
9
  from devsync.ai_tools.antigravity import AntigravityTool
9
10
  from devsync.ai_tools.augment import AugmentTool
10
11
  from devsync.ai_tools.base import AITool
@@ -56,6 +57,7 @@ class AIToolDetector:
56
57
  AIToolType.OPENHANDS: OpenHandsTool(),
57
58
  AIToolType.AMP: AmpTool(),
58
59
  AIToolType.OPENCODE: OpenCodeTool(),
60
+ AIToolType.ANTEROOM: AnteroomTool(),
59
61
  }
60
62
 
61
63
  def detect_installed_tools(self) -> list[AITool]:
@@ -132,6 +134,7 @@ class AIToolDetector:
132
134
  AIToolType.OPENHANDS,
133
135
  AIToolType.AMP,
134
136
  AIToolType.OPENCODE,
137
+ AIToolType.ANTEROOM,
135
138
  ]
136
139
 
137
140
  for tool_type in priority:
@@ -823,6 +823,34 @@ class OpenCodeTranslator(ComponentTranslator):
823
823
  raise NotImplementedError("OpenCode does not support MCP servers")
824
824
 
825
825
 
826
+ class AnteroomTranslator(ComponentTranslator):
827
+ """Translator for Anteroom (ANTEROOM.md at project root)."""
828
+
829
+ @property
830
+ def tool_type(self) -> AIToolType:
831
+ return AIToolType.ANTEROOM
832
+
833
+ def translate_instruction(self, component: InstructionComponent, package_root: Path) -> TranslatedComponent:
834
+ """Translate instruction to Anteroom format with section markers."""
835
+ instruction_path = package_root / component.file
836
+ with open(instruction_path, "r") as f:
837
+ content = f.read()
838
+
839
+ wrapped = f"<!-- devsync:start:{component.name} -->\n{content}\n<!-- devsync:end:{component.name} -->"
840
+
841
+ return TranslatedComponent(
842
+ component_type=ComponentType.INSTRUCTION,
843
+ component_name=component.name,
844
+ target_path="ANTEROOM.md",
845
+ content=wrapped,
846
+ metadata={"section_based": True},
847
+ )
848
+
849
+ def translate_mcp_server(self, component: MCPServerComponent, package_root: Path) -> TranslatedComponent:
850
+ """Anteroom does not support MCP servers."""
851
+ raise NotImplementedError("Anteroom does not support MCP servers")
852
+
853
+
826
854
  class CopilotTranslator(ComponentTranslator):
827
855
  """Translator for GitHub Copilot (.github/instructions/)."""
828
856
 
@@ -899,6 +927,7 @@ def get_translator(tool_type: AIToolType) -> ComponentTranslator:
899
927
  AIToolType.OPENHANDS: OpenHandsTranslator,
900
928
  AIToolType.AMP: AmpTranslator,
901
929
  AIToolType.OPENCODE: OpenCodeTranslator,
930
+ AIToolType.ANTEROOM: AnteroomTranslator,
902
931
  }
903
932
 
904
933
  translator_class = translators.get(tool_type)