codrsync 1.0.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.
- codrsync-1.0.0/PKG-INFO +166 -0
- codrsync-1.0.0/README.md +127 -0
- codrsync-1.0.0/codrsync/__init__.py +9 -0
- codrsync-1.0.0/codrsync/__main__.py +6 -0
- codrsync-1.0.0/codrsync/ai/__init__.py +9 -0
- codrsync-1.0.0/codrsync/ai/backend.py +252 -0
- codrsync-1.0.0/codrsync/ai/build.py +582 -0
- codrsync-1.0.0/codrsync/ai/kickstart.py +416 -0
- codrsync-1.0.0/codrsync/ai/prp.py +545 -0
- codrsync-1.0.0/codrsync/auth.py +164 -0
- codrsync-1.0.0/codrsync/cli.py +427 -0
- codrsync-1.0.0/codrsync/cloud/__init__.py +5 -0
- codrsync-1.0.0/codrsync/cloud/storage.py +301 -0
- codrsync-1.0.0/codrsync/cloud_auth.py +155 -0
- codrsync-1.0.0/codrsync/config.py +182 -0
- codrsync-1.0.0/codrsync/connect/__init__.py +13 -0
- codrsync-1.0.0/codrsync/connect/base.py +44 -0
- codrsync-1.0.0/codrsync/connect/connect.py +155 -0
- codrsync-1.0.0/codrsync/connect/digitalocean.py +42 -0
- codrsync-1.0.0/codrsync/connect/github.py +92 -0
- codrsync-1.0.0/codrsync/connect/mcp.py +82 -0
- codrsync-1.0.0/codrsync/connect/supabase.py +75 -0
- codrsync-1.0.0/codrsync/connect/tailwind.py +109 -0
- codrsync-1.0.0/codrsync/connect/vercel.py +82 -0
- codrsync-1.0.0/codrsync/i18n/__init__.py +104 -0
- codrsync-1.0.0/codrsync/i18n/lazy.py +38 -0
- codrsync-1.0.0/codrsync/i18n/loader.py +53 -0
- codrsync-1.0.0/codrsync/i18n/strings.py +1268 -0
- codrsync-1.0.0/codrsync/i18n/translator.py +95 -0
- codrsync-1.0.0/codrsync/local/__init__.py +10 -0
- codrsync-1.0.0/codrsync/local/export.py +283 -0
- codrsync-1.0.0/codrsync/local/roadmap.py +226 -0
- codrsync-1.0.0/codrsync/local/sprint.py +324 -0
- codrsync-1.0.0/codrsync/local/status.py +220 -0
- codrsync-1.0.0/codrsync/next_steps.py +33 -0
- codrsync-1.0.0/codrsync/onboarding.py +122 -0
- codrsync-1.0.0/codrsync/scanner/__init__.py +21 -0
- codrsync-1.0.0/codrsync/scanner/context_generator.py +117 -0
- codrsync-1.0.0/codrsync/scanner/detector.py +282 -0
- codrsync-1.0.0/codrsync/scanner/doc_scanner.py +79 -0
- codrsync-1.0.0/codrsync/scanner/github_scanner.py +101 -0
- codrsync-1.0.0/codrsync/scanner/scan.py +160 -0
- codrsync-1.0.0/codrsync/utils/__init__.py +7 -0
- codrsync-1.0.0/codrsync/utils/doctor.py +128 -0
- codrsync-1.0.0/codrsync.egg-info/PKG-INFO +166 -0
- codrsync-1.0.0/codrsync.egg-info/SOURCES.txt +55 -0
- codrsync-1.0.0/codrsync.egg-info/dependency_links.txt +1 -0
- codrsync-1.0.0/codrsync.egg-info/entry_points.txt +2 -0
- codrsync-1.0.0/codrsync.egg-info/requires.txt +14 -0
- codrsync-1.0.0/codrsync.egg-info/top_level.txt +3 -0
- codrsync-1.0.0/pyproject.toml +72 -0
- codrsync-1.0.0/setup.cfg +4 -0
- codrsync-1.0.0/tests/__init__.py +1 -0
- codrsync-1.0.0/tests/conftest.py +85 -0
- codrsync-1.0.0/tests/test_cli.py +141 -0
- codrsync-1.0.0/tests/test_config.py +90 -0
- codrsync-1.0.0/tests/test_detector.py +108 -0
codrsync-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: codrsync
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Turn any dev into jedi ninja codr
|
|
5
|
+
Author-email: Ciro Arendt <ciro@ciroarendt.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://codrsync.dev
|
|
8
|
+
Project-URL: Documentation, https://codrsync.dev/docs
|
|
9
|
+
Project-URL: Repository, https://github.com/ciroarendt/codrsync
|
|
10
|
+
Project-URL: Issues, https://github.com/ciroarendt/codrsync/issues
|
|
11
|
+
Keywords: cli,ai,development,orchestrator,claude,prp,codrsync
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Topic :: Software Development
|
|
23
|
+
Classifier: Topic :: Software Development :: Code Generators
|
|
24
|
+
Requires-Python: >=3.10
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
Requires-Dist: typer>=0.9.0
|
|
27
|
+
Requires-Dist: rich>=13.0.0
|
|
28
|
+
Requires-Dist: anthropic>=0.18.0
|
|
29
|
+
Requires-Dist: pydantic>=2.0.0
|
|
30
|
+
Requires-Dist: python-dotenv>=1.0.0
|
|
31
|
+
Requires-Dist: openpyxl>=3.1.0
|
|
32
|
+
Requires-Dist: questionary>=2.0.0
|
|
33
|
+
Provides-Extra: dev
|
|
34
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
35
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
|
|
36
|
+
Requires-Dist: black>=23.0.0; extra == "dev"
|
|
37
|
+
Requires-Dist: ruff>=0.1.0; extra == "dev"
|
|
38
|
+
Requires-Dist: mypy>=1.0.0; extra == "dev"
|
|
39
|
+
|
|
40
|
+
# codrsync CLI
|
|
41
|
+
|
|
42
|
+
**Turn any dev into jedi ninja codr** - AI-powered development orchestrator.
|
|
43
|
+
|
|
44
|
+
## Installation
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
pip install codrsync
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Or from source:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
cd cli
|
|
54
|
+
pip install -e .
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Quick Start
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
# Configure authentication (uses YOUR Claude Code or API key)
|
|
61
|
+
codrsync auth
|
|
62
|
+
|
|
63
|
+
# Create a new project
|
|
64
|
+
codrsync init
|
|
65
|
+
|
|
66
|
+
# Check project status
|
|
67
|
+
codrsync status
|
|
68
|
+
|
|
69
|
+
# View roadmap
|
|
70
|
+
codrsync roadmap
|
|
71
|
+
|
|
72
|
+
# Start a sprint
|
|
73
|
+
codrsync sprint start
|
|
74
|
+
|
|
75
|
+
# Export to Excel/Jira/Trello
|
|
76
|
+
codrsync export excel
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Commands
|
|
80
|
+
|
|
81
|
+
### Local (work offline)
|
|
82
|
+
|
|
83
|
+
| Command | Description |
|
|
84
|
+
|---------|-------------|
|
|
85
|
+
| `codrsync status` | Show project dashboard |
|
|
86
|
+
| `codrsync roadmap` | Show timeline and dependencies |
|
|
87
|
+
| `codrsync sprint` | Manage sprints |
|
|
88
|
+
| `codrsync export` | Export to Excel, Jira, Trello, Notion |
|
|
89
|
+
|
|
90
|
+
### AI-powered (use your Claude)
|
|
91
|
+
|
|
92
|
+
| Command | Description |
|
|
93
|
+
|---------|-------------|
|
|
94
|
+
| `codrsync init` | Interactive project kickstart |
|
|
95
|
+
| `codrsync build` | AI-guided development |
|
|
96
|
+
| `codrsync prp` | Manage PRPs |
|
|
97
|
+
|
|
98
|
+
## Authentication
|
|
99
|
+
|
|
100
|
+
codrsync uses YOUR Claude Code installation or Anthropic API key.
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
# Configure once
|
|
104
|
+
codrsync auth
|
|
105
|
+
|
|
106
|
+
# Options:
|
|
107
|
+
# 1. Use Claude Code (if installed) - recommended
|
|
108
|
+
# 2. Use Anthropic API key
|
|
109
|
+
# 3. Offline mode (limited features)
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
**You pay for your own AI usage.** codrsync provides the intelligence through prompts.
|
|
113
|
+
|
|
114
|
+
## How It Works
|
|
115
|
+
|
|
116
|
+
```
|
|
117
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
118
|
+
│ CODRSYNC CLI │
|
|
119
|
+
├─────────────────────────────────────────────────────────────┤
|
|
120
|
+
│ │
|
|
121
|
+
│ LOCAL (free, offline) AI (your Claude/API) │
|
|
122
|
+
│ ──────────────────── ────────────────── │
|
|
123
|
+
│ • status • init (kickstart) │
|
|
124
|
+
│ • roadmap • build (implement) │
|
|
125
|
+
│ • sprint • prp generate │
|
|
126
|
+
│ • export • research │
|
|
127
|
+
│ │
|
|
128
|
+
│ Reads JSON files Uses YOUR account │
|
|
129
|
+
│ No API calls You pay for usage │
|
|
130
|
+
│ │
|
|
131
|
+
└─────────────────────────────────────────────────────────────┘
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Project Structure
|
|
135
|
+
|
|
136
|
+
When you run `codrsync init`, it creates:
|
|
137
|
+
|
|
138
|
+
```
|
|
139
|
+
your-project/
|
|
140
|
+
├── doc/project/
|
|
141
|
+
│ ├── manifest.json # Project metadata
|
|
142
|
+
│ └── progress.json # Progress tracking
|
|
143
|
+
├── doc/task/
|
|
144
|
+
│ └── context-session.md # Current context
|
|
145
|
+
├── PRPs/ # Product Requirement Prompts
|
|
146
|
+
├── src/ # Your code
|
|
147
|
+
├── tests/ # Your tests
|
|
148
|
+
└── exports/ # Exported reports
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## Diagnostics
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
codrsync doctor
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
Shows:
|
|
158
|
+
- Python version
|
|
159
|
+
- Claude Code installation
|
|
160
|
+
- API key status
|
|
161
|
+
- Current project
|
|
162
|
+
- Dependencies
|
|
163
|
+
|
|
164
|
+
## License
|
|
165
|
+
|
|
166
|
+
MIT
|
codrsync-1.0.0/README.md
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# codrsync CLI
|
|
2
|
+
|
|
3
|
+
**Turn any dev into jedi ninja codr** - AI-powered development orchestrator.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install codrsync
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Or from source:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
cd cli
|
|
15
|
+
pip install -e .
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Quick Start
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
# Configure authentication (uses YOUR Claude Code or API key)
|
|
22
|
+
codrsync auth
|
|
23
|
+
|
|
24
|
+
# Create a new project
|
|
25
|
+
codrsync init
|
|
26
|
+
|
|
27
|
+
# Check project status
|
|
28
|
+
codrsync status
|
|
29
|
+
|
|
30
|
+
# View roadmap
|
|
31
|
+
codrsync roadmap
|
|
32
|
+
|
|
33
|
+
# Start a sprint
|
|
34
|
+
codrsync sprint start
|
|
35
|
+
|
|
36
|
+
# Export to Excel/Jira/Trello
|
|
37
|
+
codrsync export excel
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Commands
|
|
41
|
+
|
|
42
|
+
### Local (work offline)
|
|
43
|
+
|
|
44
|
+
| Command | Description |
|
|
45
|
+
|---------|-------------|
|
|
46
|
+
| `codrsync status` | Show project dashboard |
|
|
47
|
+
| `codrsync roadmap` | Show timeline and dependencies |
|
|
48
|
+
| `codrsync sprint` | Manage sprints |
|
|
49
|
+
| `codrsync export` | Export to Excel, Jira, Trello, Notion |
|
|
50
|
+
|
|
51
|
+
### AI-powered (use your Claude)
|
|
52
|
+
|
|
53
|
+
| Command | Description |
|
|
54
|
+
|---------|-------------|
|
|
55
|
+
| `codrsync init` | Interactive project kickstart |
|
|
56
|
+
| `codrsync build` | AI-guided development |
|
|
57
|
+
| `codrsync prp` | Manage PRPs |
|
|
58
|
+
|
|
59
|
+
## Authentication
|
|
60
|
+
|
|
61
|
+
codrsync uses YOUR Claude Code installation or Anthropic API key.
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
# Configure once
|
|
65
|
+
codrsync auth
|
|
66
|
+
|
|
67
|
+
# Options:
|
|
68
|
+
# 1. Use Claude Code (if installed) - recommended
|
|
69
|
+
# 2. Use Anthropic API key
|
|
70
|
+
# 3. Offline mode (limited features)
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
**You pay for your own AI usage.** codrsync provides the intelligence through prompts.
|
|
74
|
+
|
|
75
|
+
## How It Works
|
|
76
|
+
|
|
77
|
+
```
|
|
78
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
79
|
+
│ CODRSYNC CLI │
|
|
80
|
+
├─────────────────────────────────────────────────────────────┤
|
|
81
|
+
│ │
|
|
82
|
+
│ LOCAL (free, offline) AI (your Claude/API) │
|
|
83
|
+
│ ──────────────────── ────────────────── │
|
|
84
|
+
│ • status • init (kickstart) │
|
|
85
|
+
│ • roadmap • build (implement) │
|
|
86
|
+
│ • sprint • prp generate │
|
|
87
|
+
│ • export • research │
|
|
88
|
+
│ │
|
|
89
|
+
│ Reads JSON files Uses YOUR account │
|
|
90
|
+
│ No API calls You pay for usage │
|
|
91
|
+
│ │
|
|
92
|
+
└─────────────────────────────────────────────────────────────┘
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Project Structure
|
|
96
|
+
|
|
97
|
+
When you run `codrsync init`, it creates:
|
|
98
|
+
|
|
99
|
+
```
|
|
100
|
+
your-project/
|
|
101
|
+
├── doc/project/
|
|
102
|
+
│ ├── manifest.json # Project metadata
|
|
103
|
+
│ └── progress.json # Progress tracking
|
|
104
|
+
├── doc/task/
|
|
105
|
+
│ └── context-session.md # Current context
|
|
106
|
+
├── PRPs/ # Product Requirement Prompts
|
|
107
|
+
├── src/ # Your code
|
|
108
|
+
├── tests/ # Your tests
|
|
109
|
+
└── exports/ # Exported reports
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Diagnostics
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
codrsync doctor
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Shows:
|
|
119
|
+
- Python version
|
|
120
|
+
- Claude Code installation
|
|
121
|
+
- API key status
|
|
122
|
+
- Current project
|
|
123
|
+
- Dependencies
|
|
124
|
+
|
|
125
|
+
## License
|
|
126
|
+
|
|
127
|
+
MIT
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
"""
|
|
2
|
+
AI Backend abstraction - supports Claude Code CLI or Anthropic API
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
import subprocess
|
|
7
|
+
import json
|
|
8
|
+
from abc import ABC, abstractmethod
|
|
9
|
+
from typing import Optional, Generator
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
from rich.console import Console
|
|
13
|
+
|
|
14
|
+
from codrsync.auth import AIBackend
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
console = Console()
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class AIBackendBase(ABC):
|
|
21
|
+
"""Abstract base for AI backends"""
|
|
22
|
+
|
|
23
|
+
@abstractmethod
|
|
24
|
+
def run_prompt(
|
|
25
|
+
self,
|
|
26
|
+
prompt: str,
|
|
27
|
+
context: Optional[dict] = None,
|
|
28
|
+
system: Optional[str] = None,
|
|
29
|
+
) -> str:
|
|
30
|
+
"""Run a prompt and return the response"""
|
|
31
|
+
pass
|
|
32
|
+
|
|
33
|
+
@abstractmethod
|
|
34
|
+
def run_interactive(
|
|
35
|
+
self,
|
|
36
|
+
prompt: str,
|
|
37
|
+
context: Optional[dict] = None,
|
|
38
|
+
system: Optional[str] = None,
|
|
39
|
+
) -> Generator[str, None, None]:
|
|
40
|
+
"""Run an interactive session, yielding responses"""
|
|
41
|
+
pass
|
|
42
|
+
|
|
43
|
+
def run_conversation(
|
|
44
|
+
self,
|
|
45
|
+
messages: list[dict],
|
|
46
|
+
system: Optional[str] = None,
|
|
47
|
+
) -> str:
|
|
48
|
+
"""Run a multi-turn conversation. Default: use last user message as prompt."""
|
|
49
|
+
last_user = ""
|
|
50
|
+
for msg in reversed(messages):
|
|
51
|
+
if msg.get("role") == "user":
|
|
52
|
+
last_user = msg["content"]
|
|
53
|
+
break
|
|
54
|
+
return self.run_prompt(last_user, system=system)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class ClaudeCodeBackend(AIBackendBase):
|
|
58
|
+
"""Uses Claude Code CLI"""
|
|
59
|
+
|
|
60
|
+
def __init__(self):
|
|
61
|
+
self.claude_path = "claude"
|
|
62
|
+
|
|
63
|
+
def run_prompt(
|
|
64
|
+
self,
|
|
65
|
+
prompt: str,
|
|
66
|
+
context: Optional[dict] = None,
|
|
67
|
+
system: Optional[str] = None,
|
|
68
|
+
) -> str:
|
|
69
|
+
"""Run prompt via Claude Code CLI"""
|
|
70
|
+
full_prompt = self._build_prompt(prompt, context, system)
|
|
71
|
+
cmd = [self.claude_path, "-p", full_prompt]
|
|
72
|
+
|
|
73
|
+
try:
|
|
74
|
+
result = subprocess.run(
|
|
75
|
+
cmd,
|
|
76
|
+
capture_output=True,
|
|
77
|
+
text=True,
|
|
78
|
+
timeout=300 # 5 min timeout
|
|
79
|
+
)
|
|
80
|
+
return result.stdout
|
|
81
|
+
except subprocess.TimeoutExpired:
|
|
82
|
+
return "Error: Command timed out"
|
|
83
|
+
except Exception as e:
|
|
84
|
+
return f"Error: {str(e)}"
|
|
85
|
+
|
|
86
|
+
def run_interactive(
|
|
87
|
+
self,
|
|
88
|
+
prompt: str,
|
|
89
|
+
context: Optional[dict] = None,
|
|
90
|
+
system: Optional[str] = None,
|
|
91
|
+
) -> Generator[str, None, None]:
|
|
92
|
+
"""Run interactive Claude Code session"""
|
|
93
|
+
yield self.run_prompt(prompt, context, system)
|
|
94
|
+
|
|
95
|
+
def run_conversation(
|
|
96
|
+
self,
|
|
97
|
+
messages: list[dict],
|
|
98
|
+
system: Optional[str] = None,
|
|
99
|
+
) -> str:
|
|
100
|
+
"""Run conversation via Claude Code CLI using --continue flag"""
|
|
101
|
+
# Claude Code CLI doesn't support multi-turn natively via -p,
|
|
102
|
+
# so we concatenate the conversation into a single prompt.
|
|
103
|
+
parts = []
|
|
104
|
+
if system:
|
|
105
|
+
parts.append(f"<system>\n{system}\n</system>\n")
|
|
106
|
+
|
|
107
|
+
for msg in messages:
|
|
108
|
+
role = msg["role"].upper()
|
|
109
|
+
parts.append(f"[{role}]\n{msg['content']}\n")
|
|
110
|
+
|
|
111
|
+
full_prompt = "\n".join(parts)
|
|
112
|
+
cmd = [self.claude_path, "-p", full_prompt]
|
|
113
|
+
|
|
114
|
+
try:
|
|
115
|
+
result = subprocess.run(
|
|
116
|
+
cmd,
|
|
117
|
+
capture_output=True,
|
|
118
|
+
text=True,
|
|
119
|
+
timeout=300,
|
|
120
|
+
)
|
|
121
|
+
return result.stdout
|
|
122
|
+
except subprocess.TimeoutExpired:
|
|
123
|
+
return "Error: Command timed out"
|
|
124
|
+
except Exception as e:
|
|
125
|
+
return f"Error: {str(e)}"
|
|
126
|
+
|
|
127
|
+
@staticmethod
|
|
128
|
+
def _build_prompt(
|
|
129
|
+
prompt: str,
|
|
130
|
+
context: Optional[dict] = None,
|
|
131
|
+
system: Optional[str] = None,
|
|
132
|
+
) -> str:
|
|
133
|
+
"""Combine system, context, and prompt into a single string for CLI mode."""
|
|
134
|
+
parts = []
|
|
135
|
+
|
|
136
|
+
if system:
|
|
137
|
+
parts.append(f"<system>\n{system}\n</system>\n")
|
|
138
|
+
|
|
139
|
+
if context:
|
|
140
|
+
context_str = json.dumps(context, indent=2, ensure_ascii=False)
|
|
141
|
+
parts.append(f"<context>\n{context_str}\n</context>\n")
|
|
142
|
+
|
|
143
|
+
parts.append(prompt)
|
|
144
|
+
return "\n".join(parts)
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
class AnthropicAPIBackend(AIBackendBase):
|
|
148
|
+
"""Uses Anthropic API directly"""
|
|
149
|
+
|
|
150
|
+
def __init__(self, api_key: Optional[str] = None):
|
|
151
|
+
self.api_key = api_key or os.getenv("ANTHROPIC_API_KEY")
|
|
152
|
+
|
|
153
|
+
if not self.api_key:
|
|
154
|
+
raise ValueError("ANTHROPIC_API_KEY not set")
|
|
155
|
+
|
|
156
|
+
try:
|
|
157
|
+
from anthropic import Anthropic
|
|
158
|
+
self.client = Anthropic(api_key=self.api_key)
|
|
159
|
+
except ImportError:
|
|
160
|
+
raise ImportError("anthropic package not installed")
|
|
161
|
+
|
|
162
|
+
def run_prompt(
|
|
163
|
+
self,
|
|
164
|
+
prompt: str,
|
|
165
|
+
context: Optional[dict] = None,
|
|
166
|
+
system: Optional[str] = None,
|
|
167
|
+
) -> str:
|
|
168
|
+
"""Run prompt via Anthropic API"""
|
|
169
|
+
messages = []
|
|
170
|
+
|
|
171
|
+
if context:
|
|
172
|
+
context_str = json.dumps(context, indent=2, ensure_ascii=False)
|
|
173
|
+
messages.append({
|
|
174
|
+
"role": "user",
|
|
175
|
+
"content": f"Context:\n```json\n{context_str}\n```\n\n{prompt}"
|
|
176
|
+
})
|
|
177
|
+
else:
|
|
178
|
+
messages.append({
|
|
179
|
+
"role": "user",
|
|
180
|
+
"content": prompt
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
kwargs = {
|
|
184
|
+
"model": "claude-sonnet-4-20250514",
|
|
185
|
+
"max_tokens": 4096,
|
|
186
|
+
"messages": messages,
|
|
187
|
+
}
|
|
188
|
+
if system:
|
|
189
|
+
kwargs["system"] = system
|
|
190
|
+
|
|
191
|
+
response = self.client.messages.create(**kwargs)
|
|
192
|
+
return response.content[0].text
|
|
193
|
+
|
|
194
|
+
def run_interactive(
|
|
195
|
+
self,
|
|
196
|
+
prompt: str,
|
|
197
|
+
context: Optional[dict] = None,
|
|
198
|
+
system: Optional[str] = None,
|
|
199
|
+
) -> Generator[str, None, None]:
|
|
200
|
+
"""Run with streaming"""
|
|
201
|
+
messages = []
|
|
202
|
+
|
|
203
|
+
if context:
|
|
204
|
+
context_str = json.dumps(context, indent=2, ensure_ascii=False)
|
|
205
|
+
messages.append({
|
|
206
|
+
"role": "user",
|
|
207
|
+
"content": f"Context:\n```json\n{context_str}\n```\n\n{prompt}"
|
|
208
|
+
})
|
|
209
|
+
else:
|
|
210
|
+
messages.append({
|
|
211
|
+
"role": "user",
|
|
212
|
+
"content": prompt
|
|
213
|
+
})
|
|
214
|
+
|
|
215
|
+
kwargs = {
|
|
216
|
+
"model": "claude-sonnet-4-20250514",
|
|
217
|
+
"max_tokens": 4096,
|
|
218
|
+
"messages": messages,
|
|
219
|
+
}
|
|
220
|
+
if system:
|
|
221
|
+
kwargs["system"] = system
|
|
222
|
+
|
|
223
|
+
with self.client.messages.stream(**kwargs) as stream:
|
|
224
|
+
for text in stream.text_stream:
|
|
225
|
+
yield text
|
|
226
|
+
|
|
227
|
+
def run_conversation(
|
|
228
|
+
self,
|
|
229
|
+
messages: list[dict],
|
|
230
|
+
system: Optional[str] = None,
|
|
231
|
+
) -> str:
|
|
232
|
+
"""Run a multi-turn conversation via API"""
|
|
233
|
+
kwargs = {
|
|
234
|
+
"model": "claude-sonnet-4-20250514",
|
|
235
|
+
"max_tokens": 4096,
|
|
236
|
+
"messages": messages,
|
|
237
|
+
}
|
|
238
|
+
if system:
|
|
239
|
+
kwargs["system"] = system
|
|
240
|
+
|
|
241
|
+
response = self.client.messages.create(**kwargs)
|
|
242
|
+
return response.content[0].text
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def get_backend_instance(backend: AIBackend) -> AIBackendBase:
|
|
246
|
+
"""Get appropriate backend instance"""
|
|
247
|
+
if backend == AIBackend.CLAUDE_CODE:
|
|
248
|
+
return ClaudeCodeBackend()
|
|
249
|
+
elif backend == AIBackend.ANTHROPIC_API:
|
|
250
|
+
return AnthropicAPIBackend()
|
|
251
|
+
else:
|
|
252
|
+
raise ValueError("No AI backend available")
|