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.
- devsync-0.12.0/PKG-INFO +136 -0
- devsync-0.12.0/README.md +97 -0
- devsync-0.12.0/devsync/ai_tools/anteroom.py +216 -0
- {devsync-0.10.0 → devsync-0.12.0}/devsync/ai_tools/capability_registry.py +18 -0
- {devsync-0.10.0 → devsync-0.12.0}/devsync/ai_tools/detector.py +3 -0
- {devsync-0.10.0 → devsync-0.12.0}/devsync/ai_tools/translator.py +29 -0
- devsync-0.12.0/devsync/cli/extract.py +232 -0
- devsync-0.12.0/devsync/cli/install_v2.py +274 -0
- devsync-0.12.0/devsync/cli/list_v2.py +92 -0
- devsync-0.12.0/devsync/cli/main.py +251 -0
- devsync-0.12.0/devsync/cli/setup.py +69 -0
- devsync-0.12.0/devsync/core/adapter.py +181 -0
- {devsync-0.10.0 → devsync-0.12.0}/devsync/core/component_detector.py +1 -0
- devsync-0.12.0/devsync/core/extractor.py +178 -0
- devsync-0.12.0/devsync/core/mcp_credential_prompter.py +129 -0
- {devsync-0.10.0 → devsync-0.12.0}/devsync/core/models.py +2 -0
- devsync-0.12.0/devsync/core/package_manifest_v2.py +240 -0
- devsync-0.12.0/devsync/core/practice.py +174 -0
- devsync-0.12.0/devsync/llm/__init__.py +13 -0
- devsync-0.12.0/devsync/llm/anthropic.py +97 -0
- devsync-0.12.0/devsync/llm/config.py +91 -0
- devsync-0.12.0/devsync/llm/openai_provider.py +98 -0
- devsync-0.12.0/devsync/llm/openrouter.py +100 -0
- devsync-0.12.0/devsync/llm/prompts.py +116 -0
- devsync-0.12.0/devsync/llm/provider.py +137 -0
- devsync-0.12.0/devsync/llm/response_models.py +186 -0
- devsync-0.12.0/devsync/tui/__init__.py +1 -0
- {devsync-0.10.0 → devsync-0.12.0}/devsync/utils/paths.py +8 -7
- devsync-0.12.0/devsync.egg-info/PKG-INFO +136 -0
- {devsync-0.10.0 → devsync-0.12.0}/devsync.egg-info/SOURCES.txt +18 -25
- {devsync-0.10.0 → devsync-0.12.0}/devsync.egg-info/requires.txt +1 -0
- {devsync-0.10.0 → devsync-0.12.0}/pyproject.toml +3 -2
- devsync-0.10.0/PKG-INFO +0 -490
- devsync-0.10.0/README.md +0 -452
- devsync-0.10.0/devsync/cli/delete.py +0 -118
- devsync-0.10.0/devsync/cli/download.py +0 -274
- devsync-0.10.0/devsync/cli/install.py +0 -237
- devsync-0.10.0/devsync/cli/install_new.py +0 -937
- devsync-0.10.0/devsync/cli/list.py +0 -275
- devsync-0.10.0/devsync/cli/main.py +0 -454
- devsync-0.10.0/devsync/cli/mcp_configure.py +0 -233
- devsync-0.10.0/devsync/cli/mcp_install.py +0 -167
- devsync-0.10.0/devsync/cli/mcp_sync.py +0 -166
- devsync-0.10.0/devsync/cli/package.py +0 -386
- devsync-0.10.0/devsync/cli/package_create.py +0 -323
- devsync-0.10.0/devsync/cli/package_install.py +0 -474
- devsync-0.10.0/devsync/cli/template.py +0 -19
- devsync-0.10.0/devsync/cli/template_backup.py +0 -262
- devsync-0.10.0/devsync/cli/template_init.py +0 -499
- devsync-0.10.0/devsync/cli/template_install.py +0 -263
- devsync-0.10.0/devsync/cli/template_list.py +0 -172
- devsync-0.10.0/devsync/cli/template_uninstall.py +0 -146
- devsync-0.10.0/devsync/cli/template_update.py +0 -225
- devsync-0.10.0/devsync/cli/template_validate.py +0 -234
- devsync-0.10.0/devsync/cli/update.py +0 -309
- devsync-0.10.0/devsync/core/template_manifest.py +0 -283
- devsync-0.10.0/devsync/storage/library.py +0 -429
- devsync-0.10.0/devsync/storage/template_library.py +0 -231
- devsync-0.10.0/devsync/storage/template_tracker.py +0 -297
- devsync-0.10.0/devsync/tui/__init__.py +0 -5
- devsync-0.10.0/devsync/tui/installer.py +0 -511
- devsync-0.10.0/devsync.egg-info/PKG-INFO +0 -490
- {devsync-0.10.0 → devsync-0.12.0}/LICENSE +0 -0
- {devsync-0.10.0 → devsync-0.12.0}/devsync/__init__.py +0 -0
- {devsync-0.10.0 → devsync-0.12.0}/devsync/__main__.py +0 -0
- {devsync-0.10.0 → devsync-0.12.0}/devsync/ai_tools/__init__.py +0 -0
- {devsync-0.10.0 → devsync-0.12.0}/devsync/ai_tools/aider.py +0 -0
- {devsync-0.10.0 → devsync-0.12.0}/devsync/ai_tools/amazonq.py +0 -0
- {devsync-0.10.0 → devsync-0.12.0}/devsync/ai_tools/amp.py +0 -0
- {devsync-0.10.0 → devsync-0.12.0}/devsync/ai_tools/antigravity.py +0 -0
- {devsync-0.10.0 → devsync-0.12.0}/devsync/ai_tools/augment.py +0 -0
- {devsync-0.10.0 → devsync-0.12.0}/devsync/ai_tools/base.py +0 -0
- {devsync-0.10.0 → devsync-0.12.0}/devsync/ai_tools/claude.py +0 -0
- {devsync-0.10.0 → devsync-0.12.0}/devsync/ai_tools/claude_desktop.py +0 -0
- {devsync-0.10.0 → devsync-0.12.0}/devsync/ai_tools/cline.py +0 -0
- {devsync-0.10.0 → devsync-0.12.0}/devsync/ai_tools/codex.py +0 -0
- {devsync-0.10.0 → devsync-0.12.0}/devsync/ai_tools/continuedev.py +0 -0
- {devsync-0.10.0 → devsync-0.12.0}/devsync/ai_tools/copilot.py +0 -0
- {devsync-0.10.0 → devsync-0.12.0}/devsync/ai_tools/cursor.py +0 -0
- {devsync-0.10.0 → devsync-0.12.0}/devsync/ai_tools/gemini.py +0 -0
- {devsync-0.10.0 → devsync-0.12.0}/devsync/ai_tools/jetbrains.py +0 -0
- {devsync-0.10.0 → devsync-0.12.0}/devsync/ai_tools/junie.py +0 -0
- {devsync-0.10.0 → devsync-0.12.0}/devsync/ai_tools/kiro.py +0 -0
- {devsync-0.10.0 → devsync-0.12.0}/devsync/ai_tools/mcp_syncer.py +0 -0
- {devsync-0.10.0 → devsync-0.12.0}/devsync/ai_tools/opencode.py +0 -0
- {devsync-0.10.0 → devsync-0.12.0}/devsync/ai_tools/openhands.py +0 -0
- {devsync-0.10.0 → devsync-0.12.0}/devsync/ai_tools/roo.py +0 -0
- {devsync-0.10.0 → devsync-0.12.0}/devsync/ai_tools/tabnine.py +0 -0
- {devsync-0.10.0 → devsync-0.12.0}/devsync/ai_tools/trae.py +0 -0
- {devsync-0.10.0 → devsync-0.12.0}/devsync/ai_tools/winsurf.py +0 -0
- {devsync-0.10.0 → devsync-0.12.0}/devsync/ai_tools/zed.py +0 -0
- {devsync-0.10.0 → devsync-0.12.0}/devsync/cli/__init__.py +0 -0
- {devsync-0.10.0 → devsync-0.12.0}/devsync/cli/tools.py +0 -0
- {devsync-0.10.0 → devsync-0.12.0}/devsync/cli/uninstall.py +0 -0
- {devsync-0.10.0 → devsync-0.12.0}/devsync/core/__init__.py +0 -0
- {devsync-0.10.0 → devsync-0.12.0}/devsync/core/checksum.py +0 -0
- {devsync-0.10.0 → devsync-0.12.0}/devsync/core/conflict_resolution.py +0 -0
- {devsync-0.10.0 → devsync-0.12.0}/devsync/core/git_operations.py +0 -0
- {devsync-0.10.0 → devsync-0.12.0}/devsync/core/mcp/__init__.py +0 -0
- {devsync-0.10.0 → devsync-0.12.0}/devsync/core/mcp/credentials.py +0 -0
- {devsync-0.10.0 → devsync-0.12.0}/devsync/core/mcp/manager.py +0 -0
- {devsync-0.10.0 → devsync-0.12.0}/devsync/core/mcp/set_manager.py +0 -0
- {devsync-0.10.0 → devsync-0.12.0}/devsync/core/mcp/validator.py +0 -0
- {devsync-0.10.0 → devsync-0.12.0}/devsync/core/package_creator.py +0 -0
- {devsync-0.10.0 → devsync-0.12.0}/devsync/core/package_manifest.py +0 -0
- {devsync-0.10.0 → devsync-0.12.0}/devsync/core/repository.py +0 -0
- {devsync-0.10.0 → devsync-0.12.0}/devsync/core/secret_detector.py +0 -0
- {devsync-0.10.0 → devsync-0.12.0}/devsync/core/version.py +0 -0
- {devsync-0.10.0 → devsync-0.12.0}/devsync/storage/__init__.py +0 -0
- {devsync-0.10.0 → devsync-0.12.0}/devsync/storage/mcp_tracker.py +0 -0
- {devsync-0.10.0 → devsync-0.12.0}/devsync/storage/package_tracker.py +0 -0
- {devsync-0.10.0 → devsync-0.12.0}/devsync/storage/tracker.py +0 -0
- {devsync-0.10.0 → devsync-0.12.0}/devsync/utils/__init__.py +0 -0
- {devsync-0.10.0 → devsync-0.12.0}/devsync/utils/atomic_write.py +0 -0
- {devsync-0.10.0 → devsync-0.12.0}/devsync/utils/backup.py +0 -0
- {devsync-0.10.0 → devsync-0.12.0}/devsync/utils/dotenv.py +0 -0
- {devsync-0.10.0 → devsync-0.12.0}/devsync/utils/git_helpers.py +0 -0
- {devsync-0.10.0 → devsync-0.12.0}/devsync/utils/logging.py +0 -0
- {devsync-0.10.0 → devsync-0.12.0}/devsync/utils/namespace.py +0 -0
- {devsync-0.10.0 → devsync-0.12.0}/devsync/utils/project.py +0 -0
- {devsync-0.10.0 → devsync-0.12.0}/devsync/utils/streaming.py +0 -0
- {devsync-0.10.0 → devsync-0.12.0}/devsync/utils/ui.py +0 -0
- {devsync-0.10.0 → devsync-0.12.0}/devsync/utils/validation.py +0 -0
- {devsync-0.10.0 → devsync-0.12.0}/devsync.egg-info/dependency_links.txt +0 -0
- {devsync-0.10.0 → devsync-0.12.0}/devsync.egg-info/entry_points.txt +0 -0
- {devsync-0.10.0 → devsync-0.12.0}/devsync.egg-info/top_level.txt +0 -0
- {devsync-0.10.0 → devsync-0.12.0}/setup.cfg +0 -0
devsync-0.12.0/PKG-INFO
ADDED
|
@@ -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
|
+
[](https://github.com/troylar/devsync/actions/workflows/ci.yml)
|
|
47
|
+
[](https://devsync.readthedocs.io)
|
|
48
|
+
[](https://pypi.org/project/devsync/)
|
|
49
|
+
[](https://codecov.io/gh/troylar/devsync)
|
|
50
|
+
[](https://www.python.org/downloads/)
|
|
51
|
+
[](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).
|
devsync-0.12.0/README.md
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
# DevSync
|
|
4
|
+
|
|
5
|
+
**AI-powered config distribution for AI coding assistants**
|
|
6
|
+
|
|
7
|
+
[](https://github.com/troylar/devsync/actions/workflows/ci.yml)
|
|
8
|
+
[](https://devsync.readthedocs.io)
|
|
9
|
+
[](https://pypi.org/project/devsync/)
|
|
10
|
+
[](https://codecov.io/gh/troylar/devsync)
|
|
11
|
+
[](https://www.python.org/downloads/)
|
|
12
|
+
[](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)
|