ctxsync 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.
- ctxsync-0.8.0/LICENSE +21 -0
- ctxsync-0.8.0/PKG-INFO +151 -0
- ctxsync-0.8.0/README.md +97 -0
- ctxsync-0.8.0/pyproject.toml +74 -0
- ctxsync-0.8.0/setup.cfg +4 -0
- ctxsync-0.8.0/setup.py +6 -0
- ctxsync-0.8.0/src/ctxsync/__init__.py +0 -0
- ctxsync-0.8.0/src/ctxsync/chat_sync.py +186 -0
- ctxsync-0.8.0/src/ctxsync/cli/__init__.py +3 -0
- ctxsync-0.8.0/src/ctxsync/cli/auth.py +77 -0
- ctxsync-0.8.0/src/ctxsync/cli/category.py +71 -0
- ctxsync-0.8.0/src/ctxsync/cli/chat.py +357 -0
- ctxsync-0.8.0/src/ctxsync/cli/config.py +72 -0
- ctxsync-0.8.0/src/ctxsync/cli/file.py +29 -0
- ctxsync-0.8.0/src/ctxsync/cli/main.py +257 -0
- ctxsync-0.8.0/src/ctxsync/cli/organization.py +98 -0
- ctxsync-0.8.0/src/ctxsync/cli/project.py +422 -0
- ctxsync-0.8.0/src/ctxsync/cli/session.py +626 -0
- ctxsync-0.8.0/src/ctxsync/cli/submodule.py +148 -0
- ctxsync-0.8.0/src/ctxsync/cli/sync.py +79 -0
- ctxsync-0.8.0/src/ctxsync/compression.py +302 -0
- ctxsync-0.8.0/src/ctxsync/configmanager/__init__.py +5 -0
- ctxsync-0.8.0/src/ctxsync/configmanager/base_config_manager.py +255 -0
- ctxsync-0.8.0/src/ctxsync/configmanager/file_config_manager.py +362 -0
- ctxsync-0.8.0/src/ctxsync/configmanager/inmemory_config_manager.py +134 -0
- ctxsync-0.8.0/src/ctxsync/exceptions.py +22 -0
- ctxsync-0.8.0/src/ctxsync/provider_factory.py +38 -0
- ctxsync-0.8.0/src/ctxsync/providers/__init__.py +0 -0
- ctxsync-0.8.0/src/ctxsync/providers/base_claude_ai.py +537 -0
- ctxsync-0.8.0/src/ctxsync/providers/base_provider.py +109 -0
- ctxsync-0.8.0/src/ctxsync/providers/claude_ai.py +192 -0
- ctxsync-0.8.0/src/ctxsync/session_key_manager.py +129 -0
- ctxsync-0.8.0/src/ctxsync/syncmanager.py +328 -0
- ctxsync-0.8.0/src/ctxsync/utils.py +416 -0
- ctxsync-0.8.0/src/ctxsync.egg-info/PKG-INFO +151 -0
- ctxsync-0.8.0/src/ctxsync.egg-info/SOURCES.txt +42 -0
- ctxsync-0.8.0/src/ctxsync.egg-info/dependency_links.txt +1 -0
- ctxsync-0.8.0/src/ctxsync.egg-info/entry_points.txt +2 -0
- ctxsync-0.8.0/src/ctxsync.egg-info/requires.txt +17 -0
- ctxsync-0.8.0/src/ctxsync.egg-info/top_level.txt +1 -0
- ctxsync-0.8.0/tests/test_chat_happy_path.py +74 -0
- ctxsync-0.8.0/tests/test_claude_ai.py +253 -0
- ctxsync-0.8.0/tests/test_happy_path.py +77 -0
- ctxsync-0.8.0/tests/test_session_key_manager.py +66 -0
ctxsync-0.8.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Jahziah Wagner
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
ctxsync-0.8.0/PKG-INFO
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ctxsync
|
|
3
|
+
Version: 0.8.0
|
|
4
|
+
Summary: A tool to synchronize local files with Claude.ai projects
|
|
5
|
+
Author-email: Jahziah Wagner <540380+jahwag@users.noreply.github.com>
|
|
6
|
+
License: MIT License
|
|
7
|
+
|
|
8
|
+
Copyright (c) 2024 Jahziah Wagner
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
+
in the Software without restriction, including without limitation the rights
|
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
+
furnished to do so, subject to the following conditions:
|
|
16
|
+
|
|
17
|
+
The above copyright notice and this permission notice shall be included in all
|
|
18
|
+
copies or substantial portions of the Software.
|
|
19
|
+
|
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
+
SOFTWARE.
|
|
27
|
+
|
|
28
|
+
Project-URL: Homepage, https://github.com/jahwag/ctxsync
|
|
29
|
+
Project-URL: Bug Tracker, https://github.com/jahwag/ctxsync/issues
|
|
30
|
+
Keywords: sync,files,Claude.ai,automation,synchronization,project management,file management,cloud sync,cli tool,command line,productivity,development tools,file synchronization,continuous integration,devops,version control
|
|
31
|
+
Classifier: Programming Language :: Python :: 3
|
|
32
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
33
|
+
Classifier: Operating System :: OS Independent
|
|
34
|
+
Requires-Python: >=3.10
|
|
35
|
+
Description-Content-Type: text/markdown
|
|
36
|
+
License-File: LICENSE
|
|
37
|
+
Requires-Dist: click>=8.1.7
|
|
38
|
+
Requires-Dist: click_completion>=0.5.2
|
|
39
|
+
Requires-Dist: pathspec>=0.12.1
|
|
40
|
+
Requires-Dist: pytest>=8.3.2
|
|
41
|
+
Requires-Dist: python_crontab>=3.2.0
|
|
42
|
+
Requires-Dist: setuptools>=73.0.1
|
|
43
|
+
Requires-Dist: sseclient_py>=1.8.0
|
|
44
|
+
Requires-Dist: tqdm>=4.66.5
|
|
45
|
+
Requires-Dist: pytest-cov>=5.0.0
|
|
46
|
+
Requires-Dist: crontab>=1.0.1
|
|
47
|
+
Requires-Dist: python-crontab>=3.2.0
|
|
48
|
+
Requires-Dist: Brotli>=1.1.0
|
|
49
|
+
Requires-Dist: cryptography>=42.0.4
|
|
50
|
+
Provides-Extra: test
|
|
51
|
+
Requires-Dist: pytest>=8.2.2; extra == "test"
|
|
52
|
+
Requires-Dist: pytest-cov>=5.0.0; extra == "test"
|
|
53
|
+
Dynamic: license-file
|
|
54
|
+
|
|
55
|
+
# ctxsync
|
|
56
|
+
|
|
57
|
+
[](https://opensource.org/licenses/MIT)
|
|
58
|
+
[](https://pypi.org/project/ctxsync/)
|
|
59
|
+
[](https://github.com/jahwag/ctxsync/releases)
|
|
60
|
+
[](https://github.com/jahwag/ctxsync/actions/workflows/python-package.yml)
|
|
61
|
+
[](https://github.com/jahwag/ctxsync/issues)
|
|
62
|
+
[](https://github.com/psf/black)
|
|
63
|
+
[](https://github.com/jahwag/ctxsync/network/dependencies)
|
|
64
|
+
[](https://github.com/jahwag/ctxsync/commits/main)
|
|
65
|
+
[](https://github.com/sponsors/jahwag)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
ctxsync (formerly known as ClaudeSync) bridges your local development environment with Claude.ai projects, enabling seamless synchronization to enhance your AI-powered workflow.
|
|
69
|
+
|
|
70
|
+
> **Renamed from ClaudeSync**: the `claudesync` PyPI package is deprecated — install `ctxsync` instead. Your existing configuration is picked up automatically: `~/.claudesync` is migrated on first run and project-local `.claudesync` directories keep working.
|
|
71
|
+
|
|
72
|
+

|
|
73
|
+
|
|
74
|
+
## ⚠️ Disclaimer
|
|
75
|
+
|
|
76
|
+
ctxsync is an independent, open-source project **not affiliated** with Anthropic or Claude.ai. By using ctxsync, you agree to:
|
|
77
|
+
|
|
78
|
+
1. Use it at your own risk.
|
|
79
|
+
2. Acknowledge potential violation of Anthropic's Terms of Service.
|
|
80
|
+
3. Assume responsibility for any consequences.
|
|
81
|
+
4. Understand that Anthropic does not support this tool.
|
|
82
|
+
|
|
83
|
+
Please review [Anthropic's Terms of Service](https://www.anthropic.com/legal/consumer-terms) before using ctxsync.
|
|
84
|
+
|
|
85
|
+
## 🌟 Features
|
|
86
|
+
|
|
87
|
+
- **File sync**: Synchronize local files with [Claude.ai projects](https://www.anthropic.com/news/projects).
|
|
88
|
+
- **Cross-Platform**: Compatible with [Windows, macOS, and Linux](https://github.com/jahwag/ctxsync/releases).
|
|
89
|
+
- **Configurable**: Plenty of [configuration options](https://github.com/jahwag/ctxsync/wiki/Quick-reference).
|
|
90
|
+
- **Integrate**: Designed to be easy to integrate into your pipelines.
|
|
91
|
+
- **Secure**: Ensures data privacy and security.
|
|
92
|
+
|
|
93
|
+
## ⚙️ Prerequisites
|
|
94
|
+
|
|
95
|
+
### 📄 Supported Claude.ai plans
|
|
96
|
+
|
|
97
|
+
| [Plan](https://www.anthropic.com/pricing) | Supported |
|
|
98
|
+
|--------|-----------|
|
|
99
|
+
| Pro | ✅ |
|
|
100
|
+
| Team | ✅ |
|
|
101
|
+
| Free | ❌ |
|
|
102
|
+
|
|
103
|
+
### 🔑 SSH Key
|
|
104
|
+
|
|
105
|
+
Ensure you have an SSH key for secure credential storage. Follow [GitHub's guide](https://docs.github.com/en/authentication/connecting-to-github-with-ssh) to generate and add your SSH key.
|
|
106
|
+
|
|
107
|
+
### 💻 Software
|
|
108
|
+
|
|
109
|
+
- **Python**: ≥ [3.10](https://www.python.org/downloads/)
|
|
110
|
+
- **pip**: [Python package installer](https://pip.pypa.io/en/stable/installation/)
|
|
111
|
+
|
|
112
|
+
## 🚀 Quick Start
|
|
113
|
+
|
|
114
|
+
1. **Install ctxsync**
|
|
115
|
+
```shell
|
|
116
|
+
pip install ctxsync
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
2. **Authenticate**
|
|
120
|
+
```shell
|
|
121
|
+
ctxsync auth login
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
3. **Create a Project**
|
|
125
|
+
```shell
|
|
126
|
+
ctxsync project create
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
4. **Start Syncing***
|
|
130
|
+
```shell
|
|
131
|
+
ctxsync push
|
|
132
|
+
```
|
|
133
|
+
**This is a one-way sync. Files not present locally will be removed from the Claude.ai project unless pruning is [disabled](https://github.com/jahwag/ctxsync/wiki/Quick-reference#pruning-remote).*
|
|
134
|
+
|
|
135
|
+
📚 [Detailed Guides & FAQs](https://github.com/jahwag/ctxsync/wiki)
|
|
136
|
+
|
|
137
|
+
## 🤝 Support & Contribute
|
|
138
|
+
|
|
139
|
+
Enjoying ctxsync? Support us by:
|
|
140
|
+
|
|
141
|
+
- ⭐ [Starring the Repository](https://github.com/jahwag/ctxsync)
|
|
142
|
+
- 🐛 [Reporting Issues](https://github.com/jahwag/ctxsync/issues)
|
|
143
|
+
- 🌍 [Contributing](CONTRIBUTING.md)
|
|
144
|
+
- 💬 [Join Our Discord](https://discord.gg/pR4qeMH4u4)
|
|
145
|
+
- 💖 [Sponsor Us](https://github.com/sponsors/jahwag)
|
|
146
|
+
|
|
147
|
+
Your contributions help improve ctxsync!
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
[Contributors](https://github.com/jahwag/ctxsync/graphs/contributors) • [License](https://github.com/jahwag/ctxsync/blob/master/LICENSE) • [Report Bug](https://github.com/jahwag/ctxsync/issues) • [Request Feature](https://github.com/jahwag/ctxsync/issues/new?labels=enhancement&template=feature_request.md)• [Sponsor](https://github.com/sponsors/jahwag)
|
ctxsync-0.8.0/README.md
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# ctxsync
|
|
2
|
+
|
|
3
|
+
[](https://opensource.org/licenses/MIT)
|
|
4
|
+
[](https://pypi.org/project/ctxsync/)
|
|
5
|
+
[](https://github.com/jahwag/ctxsync/releases)
|
|
6
|
+
[](https://github.com/jahwag/ctxsync/actions/workflows/python-package.yml)
|
|
7
|
+
[](https://github.com/jahwag/ctxsync/issues)
|
|
8
|
+
[](https://github.com/psf/black)
|
|
9
|
+
[](https://github.com/jahwag/ctxsync/network/dependencies)
|
|
10
|
+
[](https://github.com/jahwag/ctxsync/commits/main)
|
|
11
|
+
[](https://github.com/sponsors/jahwag)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
ctxsync (formerly known as ClaudeSync) bridges your local development environment with Claude.ai projects, enabling seamless synchronization to enhance your AI-powered workflow.
|
|
15
|
+
|
|
16
|
+
> **Renamed from ClaudeSync**: the `claudesync` PyPI package is deprecated — install `ctxsync` instead. Your existing configuration is picked up automatically: `~/.claudesync` is migrated on first run and project-local `.claudesync` directories keep working.
|
|
17
|
+
|
|
18
|
+

|
|
19
|
+
|
|
20
|
+
## ⚠️ Disclaimer
|
|
21
|
+
|
|
22
|
+
ctxsync is an independent, open-source project **not affiliated** with Anthropic or Claude.ai. By using ctxsync, you agree to:
|
|
23
|
+
|
|
24
|
+
1. Use it at your own risk.
|
|
25
|
+
2. Acknowledge potential violation of Anthropic's Terms of Service.
|
|
26
|
+
3. Assume responsibility for any consequences.
|
|
27
|
+
4. Understand that Anthropic does not support this tool.
|
|
28
|
+
|
|
29
|
+
Please review [Anthropic's Terms of Service](https://www.anthropic.com/legal/consumer-terms) before using ctxsync.
|
|
30
|
+
|
|
31
|
+
## 🌟 Features
|
|
32
|
+
|
|
33
|
+
- **File sync**: Synchronize local files with [Claude.ai projects](https://www.anthropic.com/news/projects).
|
|
34
|
+
- **Cross-Platform**: Compatible with [Windows, macOS, and Linux](https://github.com/jahwag/ctxsync/releases).
|
|
35
|
+
- **Configurable**: Plenty of [configuration options](https://github.com/jahwag/ctxsync/wiki/Quick-reference).
|
|
36
|
+
- **Integrate**: Designed to be easy to integrate into your pipelines.
|
|
37
|
+
- **Secure**: Ensures data privacy and security.
|
|
38
|
+
|
|
39
|
+
## ⚙️ Prerequisites
|
|
40
|
+
|
|
41
|
+
### 📄 Supported Claude.ai plans
|
|
42
|
+
|
|
43
|
+
| [Plan](https://www.anthropic.com/pricing) | Supported |
|
|
44
|
+
|--------|-----------|
|
|
45
|
+
| Pro | ✅ |
|
|
46
|
+
| Team | ✅ |
|
|
47
|
+
| Free | ❌ |
|
|
48
|
+
|
|
49
|
+
### 🔑 SSH Key
|
|
50
|
+
|
|
51
|
+
Ensure you have an SSH key for secure credential storage. Follow [GitHub's guide](https://docs.github.com/en/authentication/connecting-to-github-with-ssh) to generate and add your SSH key.
|
|
52
|
+
|
|
53
|
+
### 💻 Software
|
|
54
|
+
|
|
55
|
+
- **Python**: ≥ [3.10](https://www.python.org/downloads/)
|
|
56
|
+
- **pip**: [Python package installer](https://pip.pypa.io/en/stable/installation/)
|
|
57
|
+
|
|
58
|
+
## 🚀 Quick Start
|
|
59
|
+
|
|
60
|
+
1. **Install ctxsync**
|
|
61
|
+
```shell
|
|
62
|
+
pip install ctxsync
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
2. **Authenticate**
|
|
66
|
+
```shell
|
|
67
|
+
ctxsync auth login
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
3. **Create a Project**
|
|
71
|
+
```shell
|
|
72
|
+
ctxsync project create
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
4. **Start Syncing***
|
|
76
|
+
```shell
|
|
77
|
+
ctxsync push
|
|
78
|
+
```
|
|
79
|
+
**This is a one-way sync. Files not present locally will be removed from the Claude.ai project unless pruning is [disabled](https://github.com/jahwag/ctxsync/wiki/Quick-reference#pruning-remote).*
|
|
80
|
+
|
|
81
|
+
📚 [Detailed Guides & FAQs](https://github.com/jahwag/ctxsync/wiki)
|
|
82
|
+
|
|
83
|
+
## 🤝 Support & Contribute
|
|
84
|
+
|
|
85
|
+
Enjoying ctxsync? Support us by:
|
|
86
|
+
|
|
87
|
+
- ⭐ [Starring the Repository](https://github.com/jahwag/ctxsync)
|
|
88
|
+
- 🐛 [Reporting Issues](https://github.com/jahwag/ctxsync/issues)
|
|
89
|
+
- 🌍 [Contributing](CONTRIBUTING.md)
|
|
90
|
+
- 💬 [Join Our Discord](https://discord.gg/pR4qeMH4u4)
|
|
91
|
+
- 💖 [Sponsor Us](https://github.com/sponsors/jahwag)
|
|
92
|
+
|
|
93
|
+
Your contributions help improve ctxsync!
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
[Contributors](https://github.com/jahwag/ctxsync/graphs/contributors) • [License](https://github.com/jahwag/ctxsync/blob/master/LICENSE) • [Report Bug](https://github.com/jahwag/ctxsync/issues) • [Request Feature](https://github.com/jahwag/ctxsync/issues/new?labels=enhancement&template=feature_request.md)• [Sponsor](https://github.com/sponsors/jahwag)
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "ctxsync"
|
|
3
|
+
version = "0.8.0"
|
|
4
|
+
authors = [
|
|
5
|
+
{name = "Jahziah Wagner", email = "540380+jahwag@users.noreply.github.com"},
|
|
6
|
+
]
|
|
7
|
+
description = "A tool to synchronize local files with Claude.ai projects"
|
|
8
|
+
license = {file = "LICENSE"}
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
classifiers = [
|
|
12
|
+
"Programming Language :: Python :: 3",
|
|
13
|
+
"License :: OSI Approved :: MIT License",
|
|
14
|
+
"Operating System :: OS Independent",
|
|
15
|
+
]
|
|
16
|
+
dependencies = [
|
|
17
|
+
"click>=8.1.7",
|
|
18
|
+
"click_completion>=0.5.2",
|
|
19
|
+
"pathspec>=0.12.1",
|
|
20
|
+
"pytest>=8.3.2",
|
|
21
|
+
"python_crontab>=3.2.0",
|
|
22
|
+
"setuptools>=73.0.1",
|
|
23
|
+
"sseclient_py>=1.8.0",
|
|
24
|
+
"tqdm>=4.66.5",
|
|
25
|
+
"pytest-cov>=5.0.0",
|
|
26
|
+
"crontab>=1.0.1",
|
|
27
|
+
"python-crontab>=3.2.0",
|
|
28
|
+
"Brotli>=1.1.0",
|
|
29
|
+
"cryptography>=42.0.4",
|
|
30
|
+
]
|
|
31
|
+
keywords = [
|
|
32
|
+
"sync",
|
|
33
|
+
"files",
|
|
34
|
+
"Claude.ai",
|
|
35
|
+
"automation",
|
|
36
|
+
"synchronization",
|
|
37
|
+
"project management",
|
|
38
|
+
"file management",
|
|
39
|
+
"cloud sync",
|
|
40
|
+
"cli tool",
|
|
41
|
+
"command line",
|
|
42
|
+
"productivity",
|
|
43
|
+
"development tools",
|
|
44
|
+
"file synchronization",
|
|
45
|
+
"continuous integration",
|
|
46
|
+
"devops",
|
|
47
|
+
"version control"
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
[project.optional-dependencies]
|
|
51
|
+
test = [
|
|
52
|
+
"pytest>=8.2.2",
|
|
53
|
+
"pytest-cov>=5.0.0",
|
|
54
|
+
]
|
|
55
|
+
|
|
56
|
+
[project.urls]
|
|
57
|
+
"Homepage" = "https://github.com/jahwag/ctxsync"
|
|
58
|
+
"Bug Tracker" = "https://github.com/jahwag/ctxsync/issues"
|
|
59
|
+
|
|
60
|
+
[project.scripts]
|
|
61
|
+
ctxsync = "ctxsync.cli.main:cli"
|
|
62
|
+
|
|
63
|
+
[build-system]
|
|
64
|
+
requires = ["setuptools>=42", "wheel"]
|
|
65
|
+
build-backend = "setuptools.build_meta"
|
|
66
|
+
|
|
67
|
+
[tool.setuptools.packages.find]
|
|
68
|
+
where = ["src"]
|
|
69
|
+
include = ["ctxsync*"]
|
|
70
|
+
|
|
71
|
+
[tool.pytest.ini_options]
|
|
72
|
+
testpaths = ["tests"]
|
|
73
|
+
python_files = "test_*.py"
|
|
74
|
+
addopts = "-v --cov=ctxsync --cov-report=term-missing"
|
ctxsync-0.8.0/setup.cfg
ADDED
ctxsync-0.8.0/setup.py
ADDED
|
File without changes
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import logging
|
|
3
|
+
import os
|
|
4
|
+
import re
|
|
5
|
+
|
|
6
|
+
from tqdm import tqdm
|
|
7
|
+
|
|
8
|
+
from .exceptions import ConfigurationError
|
|
9
|
+
|
|
10
|
+
logger = logging.getLogger(__name__)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def sync_chats(provider, config, sync_all=False):
|
|
14
|
+
"""
|
|
15
|
+
Synchronize chats and their artifacts from the remote source.
|
|
16
|
+
|
|
17
|
+
This function fetches all chats for the active organization, saves their metadata,
|
|
18
|
+
messages, and extracts any artifacts found in the assistant's messages.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
provider: The API provider instance.
|
|
22
|
+
config: The configuration manager instance.
|
|
23
|
+
sync_all (bool): If True, sync all chats regardless of project. If False, only sync chats for the active project.
|
|
24
|
+
|
|
25
|
+
Raises:
|
|
26
|
+
ConfigurationError: If required configuration settings are missing.
|
|
27
|
+
"""
|
|
28
|
+
# Get the local_path for chats
|
|
29
|
+
local_path = config.get("local_path")
|
|
30
|
+
if not local_path:
|
|
31
|
+
raise ConfigurationError(
|
|
32
|
+
"Local path not set. Use 'ctxsync project set' or 'ctxsync project create' to set it."
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
# Create chats directory within local_path
|
|
36
|
+
chat_destination = os.path.join(local_path, "claude_chats")
|
|
37
|
+
os.makedirs(chat_destination, exist_ok=True)
|
|
38
|
+
|
|
39
|
+
# Get the active organization ID
|
|
40
|
+
organization_id = config.get("active_organization_id")
|
|
41
|
+
if not organization_id:
|
|
42
|
+
raise ConfigurationError(
|
|
43
|
+
"No active organization set. Please set an organization."
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
# Get the active project ID
|
|
47
|
+
active_project_id = config.get("active_project_id")
|
|
48
|
+
if not active_project_id and not sync_all:
|
|
49
|
+
raise ConfigurationError(
|
|
50
|
+
"No active project set. Please set a project or use the -a flag to sync all chats."
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
# Fetch all chats for the organization
|
|
54
|
+
logger.debug(f"Fetching chats for organization {organization_id}")
|
|
55
|
+
chats = provider.get_chat_conversations(organization_id)
|
|
56
|
+
logger.debug(f"Found {len(chats)} chats")
|
|
57
|
+
|
|
58
|
+
# Process each chat
|
|
59
|
+
for chat in tqdm(chats, desc="Chats"):
|
|
60
|
+
sync_chat(
|
|
61
|
+
active_project_id,
|
|
62
|
+
chat,
|
|
63
|
+
chat_destination,
|
|
64
|
+
organization_id,
|
|
65
|
+
provider,
|
|
66
|
+
sync_all,
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
logger.debug(f"Chats and artifacts synchronized to {chat_destination}")
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def sync_chat(
|
|
73
|
+
active_project_id, chat, chat_destination, organization_id, provider, sync_all
|
|
74
|
+
):
|
|
75
|
+
# Check if the chat belongs to the active project or if we're syncing all chats
|
|
76
|
+
if sync_all or (
|
|
77
|
+
chat.get("project") and chat["project"].get("uuid") == active_project_id
|
|
78
|
+
):
|
|
79
|
+
logger.debug(f"Processing chat {chat['uuid']}")
|
|
80
|
+
chat_folder = os.path.join(chat_destination, chat["uuid"])
|
|
81
|
+
os.makedirs(chat_folder, exist_ok=True)
|
|
82
|
+
|
|
83
|
+
# Save chat metadata
|
|
84
|
+
metadata_file = os.path.join(chat_folder, "metadata.json")
|
|
85
|
+
if not os.path.exists(metadata_file):
|
|
86
|
+
with open(metadata_file, "w") as f:
|
|
87
|
+
json.dump(chat, f, indent=2)
|
|
88
|
+
|
|
89
|
+
# Fetch full chat conversation
|
|
90
|
+
logger.debug(f"Fetching full conversation for chat {chat['uuid']}")
|
|
91
|
+
full_chat = provider.get_chat_conversation(organization_id, chat["uuid"])
|
|
92
|
+
|
|
93
|
+
# Process each message in the chat
|
|
94
|
+
for message in full_chat["chat_messages"]:
|
|
95
|
+
message_file = os.path.join(chat_folder, f"{message['uuid']}.json")
|
|
96
|
+
|
|
97
|
+
# Skip processing if the message file already exists
|
|
98
|
+
if os.path.exists(message_file):
|
|
99
|
+
logger.debug(f"Skipping existing message {message['uuid']}")
|
|
100
|
+
continue
|
|
101
|
+
|
|
102
|
+
# Save the message
|
|
103
|
+
with open(message_file, "w") as f:
|
|
104
|
+
json.dump(message, f, indent=2)
|
|
105
|
+
|
|
106
|
+
# Handle artifacts in assistant messages
|
|
107
|
+
if message["sender"] == "assistant":
|
|
108
|
+
artifacts = extract_artifacts(message["text"])
|
|
109
|
+
if artifacts:
|
|
110
|
+
save_artifacts(artifacts, chat_folder, message)
|
|
111
|
+
else:
|
|
112
|
+
logger.debug(
|
|
113
|
+
f"Skipping chat {chat['uuid']} as it doesn't belong to the active project"
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def save_artifacts(artifacts, chat_folder, message):
|
|
118
|
+
logger.info(f"Found {len(artifacts)} artifacts in message {message['uuid']}")
|
|
119
|
+
artifact_folder = os.path.join(chat_folder, "artifacts")
|
|
120
|
+
os.makedirs(artifact_folder, exist_ok=True)
|
|
121
|
+
for artifact in artifacts:
|
|
122
|
+
# Save each artifact
|
|
123
|
+
artifact_file = os.path.join(
|
|
124
|
+
artifact_folder,
|
|
125
|
+
f"{artifact['identifier']}.{get_file_extension(artifact['type'])}",
|
|
126
|
+
)
|
|
127
|
+
if not os.path.exists(artifact_file):
|
|
128
|
+
with open(artifact_file, "w") as f:
|
|
129
|
+
f.write(artifact["content"])
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def get_file_extension(artifact_type):
|
|
133
|
+
"""
|
|
134
|
+
Get the appropriate file extension for a given artifact type.
|
|
135
|
+
|
|
136
|
+
Args:
|
|
137
|
+
artifact_type (str): The MIME type of the artifact.
|
|
138
|
+
|
|
139
|
+
Returns:
|
|
140
|
+
str: The corresponding file extension.
|
|
141
|
+
"""
|
|
142
|
+
type_to_extension = {
|
|
143
|
+
"text/html": "html",
|
|
144
|
+
"application/vnd.ant.code": "txt",
|
|
145
|
+
"image/svg+xml": "svg",
|
|
146
|
+
"application/vnd.ant.mermaid": "mmd",
|
|
147
|
+
"application/vnd.ant.react": "jsx",
|
|
148
|
+
}
|
|
149
|
+
return type_to_extension.get(artifact_type, "txt")
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def extract_artifacts(text):
|
|
153
|
+
"""
|
|
154
|
+
Extract artifacts from the given text.
|
|
155
|
+
|
|
156
|
+
This function searches for antArtifact tags in the text and extracts
|
|
157
|
+
the artifact information, including identifier, type, and content.
|
|
158
|
+
|
|
159
|
+
Args:
|
|
160
|
+
text (str): The text to search for artifacts.
|
|
161
|
+
|
|
162
|
+
Returns:
|
|
163
|
+
list: A list of dictionaries containing artifact information.
|
|
164
|
+
"""
|
|
165
|
+
artifacts = []
|
|
166
|
+
|
|
167
|
+
# Regular expression to match the <antArtifact> tags and extract their attributes and content
|
|
168
|
+
pattern = re.compile(
|
|
169
|
+
r'<antArtifact\s+identifier="([^"]+)"\s+type="([^"]+)"\s+title="([^"]+)">([\s\S]*?)</antArtifact>',
|
|
170
|
+
re.MULTILINE,
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
# Find all matches in the text
|
|
174
|
+
matches = pattern.findall(text)
|
|
175
|
+
|
|
176
|
+
for match in matches:
|
|
177
|
+
identifier, artifact_type, title, content = match
|
|
178
|
+
artifacts.append(
|
|
179
|
+
{
|
|
180
|
+
"identifier": identifier,
|
|
181
|
+
"type": artifact_type,
|
|
182
|
+
"content": content.strip(),
|
|
183
|
+
}
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
return artifacts
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import click
|
|
2
|
+
|
|
3
|
+
from ctxsync.provider_factory import get_provider
|
|
4
|
+
from ..exceptions import ProviderError
|
|
5
|
+
from ..utils import handle_errors
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@click.group()
|
|
9
|
+
def auth():
|
|
10
|
+
"""Manage authentication."""
|
|
11
|
+
pass
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@auth.command()
|
|
15
|
+
@click.option(
|
|
16
|
+
"--provider",
|
|
17
|
+
prompt="Choose provider",
|
|
18
|
+
type=click.Choice(["claude.ai"], case_sensitive=False),
|
|
19
|
+
default="claude.ai",
|
|
20
|
+
help="The provider to use for this project",
|
|
21
|
+
)
|
|
22
|
+
@click.option(
|
|
23
|
+
"--session-key",
|
|
24
|
+
help="Directly provide the Claude.ai session key",
|
|
25
|
+
envvar="CLAUDE_SESSION_KEY",
|
|
26
|
+
)
|
|
27
|
+
@click.option(
|
|
28
|
+
"--auto-approve",
|
|
29
|
+
is_flag=True,
|
|
30
|
+
help="Automatically approve the suggested expiry time",
|
|
31
|
+
)
|
|
32
|
+
@click.pass_context
|
|
33
|
+
@handle_errors
|
|
34
|
+
def login(ctx, provider, session_key, auto_approve):
|
|
35
|
+
"""Authenticate with an AI provider."""
|
|
36
|
+
config = ctx.obj
|
|
37
|
+
provider_instance = get_provider(config, provider)
|
|
38
|
+
|
|
39
|
+
try:
|
|
40
|
+
if session_key:
|
|
41
|
+
# If session key is provided, bypass the interactive prompt
|
|
42
|
+
if not session_key.startswith("sk-ant"):
|
|
43
|
+
raise ProviderError(
|
|
44
|
+
"Invalid sessionKey format. Must start with 'sk-ant'"
|
|
45
|
+
)
|
|
46
|
+
# Set auto_approve to True when session key is provided
|
|
47
|
+
provider_instance._auto_approve_expiry = auto_approve
|
|
48
|
+
provider_instance._provided_session_key = session_key
|
|
49
|
+
|
|
50
|
+
session_key, expiry = provider_instance.login()
|
|
51
|
+
config.set_session_key(provider, session_key, expiry)
|
|
52
|
+
click.echo(
|
|
53
|
+
f"Successfully authenticated with {provider}. Session key stored globally."
|
|
54
|
+
)
|
|
55
|
+
except ProviderError as e:
|
|
56
|
+
click.echo(f"Authentication failed: {str(e)}")
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@auth.command()
|
|
60
|
+
@click.pass_obj
|
|
61
|
+
def logout(config):
|
|
62
|
+
"""Log out from all AI providers."""
|
|
63
|
+
config.clear_all_session_keys()
|
|
64
|
+
click.echo("Logged out from all providers successfully.")
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
@auth.command()
|
|
68
|
+
@click.pass_obj
|
|
69
|
+
def ls(config):
|
|
70
|
+
"""List all authenticated providers."""
|
|
71
|
+
authenticated_providers = config.get_providers_with_session_keys()
|
|
72
|
+
if authenticated_providers:
|
|
73
|
+
click.echo("Authenticated providers:")
|
|
74
|
+
for provider in authenticated_providers:
|
|
75
|
+
click.echo(f" - {provider}")
|
|
76
|
+
else:
|
|
77
|
+
click.echo("No authenticated providers found.")
|