@korl3one/ccode 2.3.0 → 3.1.0
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.
- package/README.md +81 -46
- package/dist/ai/claude.d.ts +2 -1
- package/dist/ai/claude.js +15 -12
- package/dist/ai/gemini.d.ts +13 -1
- package/dist/ai/gemini.js +62 -15
- package/dist/ai/manager.d.ts +25 -6
- package/dist/ai/manager.js +83 -21
- package/dist/cli/brand.js +1 -1
- package/dist/cli/index.js +252 -96
- package/dist/core/exports.d.ts +74 -0
- package/dist/core/exports.js +303 -0
- package/package.json +9 -4
- package/dist/ai/deepseek.d.ts +0 -11
- package/dist/ai/deepseek.js +0 -35
- package/dist/ai/groq.d.ts +0 -11
- package/dist/ai/groq.js +0 -35
- package/dist/ai/ollama.d.ts +0 -10
- package/dist/ai/ollama.js +0 -27
- package/dist/ai/openai.d.ts +0 -10
- package/dist/ai/openai.js +0 -34
package/README.md
CHANGED
|
@@ -30,37 +30,39 @@ npm install -g @korl3one/ccode
|
|
|
30
30
|
|
|
31
31
|
Every time you switch sessions, models, or tools when working with AI, **you lose your project context**. You end up re-explaining the architecture, previous decisions, and current state over and over again.
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
And if you use multiple AI tools — Claude Code, Cursor, Gemini CLI, Copilot — you have to configure context files for each one manually.
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
## The solution
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
CCODE generates and maintains your project context **inside the repository**. One command produces professional documentation, architecture, rules, and a verifiable task checklist — all adapted to your project's actual complexity.
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
Then it **syncs that context to every major AI tool** automatically.
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
Any developer or AI can read your project and understand it instantly.
|
|
42
42
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
<div align="center">
|
|
46
|
-
<img src="assets/demo-init.svg" alt="ccode init demo" width="700"/>
|
|
47
|
-
</div>
|
|
43
|
+
---
|
|
48
44
|
|
|
49
|
-
|
|
45
|
+
## Universal Context Sync
|
|
50
46
|
|
|
51
|
-
|
|
47
|
+
This is what makes CCODE different. One command generates context files for **every major AI tool**:
|
|
52
48
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
49
|
+
```bash
|
|
50
|
+
ccode sync
|
|
51
|
+
```
|
|
56
52
|
|
|
57
|
-
|
|
53
|
+
```
|
|
54
|
+
project/
|
|
55
|
+
├── AGENTS.md ← Open Standard (60K+ repos)
|
|
56
|
+
├── CLAUDE.md ← Claude Code
|
|
57
|
+
├── GEMINI.md ← Gemini CLI
|
|
58
|
+
├── .cursorrules ← Cursor
|
|
59
|
+
├── .github/copilot-instructions.md ← GitHub Copilot
|
|
60
|
+
└── .ccode/context-export.md ← Universal (copy/paste to any AI chat)
|
|
61
|
+
```
|
|
58
62
|
|
|
59
|
-
|
|
63
|
+
Each file is adapted to the tool's expected format. Your project context — architecture, rules, tasks, decisions — synced everywhere, from a single source of truth.
|
|
60
64
|
|
|
61
|
-
|
|
62
|
-
<img src="assets/demo-doctor.svg" alt="ccode doctor demo" width="700"/>
|
|
63
|
-
</div>
|
|
65
|
+
No manual setup per tool. No copy-pasting between files. Context stays in sync automatically with `ccode init`, `ccode update`, and `ccode sync`.
|
|
64
66
|
|
|
65
67
|
---
|
|
66
68
|
|
|
@@ -71,12 +73,13 @@ Any developer or AI can read `.ccode/` and understand the project instantly.
|
|
|
71
73
|
| | CCODE | Manual prompts |
|
|
72
74
|
|---|:---:|:---:|
|
|
73
75
|
| Persistent project context | ✅ | ❌ |
|
|
76
|
+
| Universal context sync (5+ AI tools) | ✅ | ❌ |
|
|
74
77
|
| Architecture adapted to complexity | ✅ | ❌ |
|
|
75
78
|
| AI-ready documentation | ✅ | ❌ |
|
|
76
79
|
| Verifiable task checklist | ✅ | ❌ |
|
|
77
80
|
| Auto-detect file changes | ✅ | ❌ |
|
|
78
81
|
| AI-powered task verification | ✅ | ❌ |
|
|
79
|
-
|
|
|
82
|
+
| Zero-config AI provider detection | ✅ | ❌ |
|
|
80
83
|
| Context lives in the repo (Git) | ✅ | ❌ |
|
|
81
84
|
|
|
82
85
|
</div>
|
|
@@ -102,6 +105,8 @@ A step-by-step wizard asks about your project and an AI generates the full conte
|
|
|
102
105
|
|
|
103
106
|
A simple login doesn't need microservice diagrams. CCODE is smart about it.
|
|
104
107
|
|
|
108
|
+
After generation, context is automatically synced to all AI tools.
|
|
109
|
+
|
|
105
110
|
### 2. Persistent session
|
|
106
111
|
|
|
107
112
|
After init, CCODE **stays active** — watching your project in real time:
|
|
@@ -129,21 +134,29 @@ CCODE compares **acceptance criteria** against **actual project files**:
|
|
|
129
134
|
|
|
130
135
|
It doesn't guess — it verifies.
|
|
131
136
|
|
|
132
|
-
### 4. Context
|
|
137
|
+
### 4. Context sync
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
ccode sync
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Regenerates context files for every AI tool. Run it after making significant changes, or let `ccode update` handle it when you re-analyze the project.
|
|
144
|
+
|
|
145
|
+
### 5. Context export
|
|
133
146
|
|
|
134
147
|
```bash
|
|
135
148
|
ccode export
|
|
136
149
|
```
|
|
137
150
|
|
|
138
|
-
|
|
151
|
+
Three options: sync to all tools, export a universal `.md` for copy/paste into any AI chat, or pick specific tools.
|
|
139
152
|
|
|
140
|
-
###
|
|
153
|
+
### 6. Project health check
|
|
141
154
|
|
|
142
155
|
```bash
|
|
143
156
|
ccode doctor
|
|
144
157
|
```
|
|
145
158
|
|
|
146
|
-
Like a linter, but for your project context. Checks files, AI connection, task status, and tells you what needs attention.
|
|
159
|
+
Like a linter, but for your project context. Checks context files, AI connection, task status, export state, and tells you what needs attention.
|
|
147
160
|
|
|
148
161
|
---
|
|
149
162
|
|
|
@@ -153,35 +166,41 @@ Like a linter, but for your project context. Checks files, AI connection, task s
|
|
|
153
166
|
|
|
154
167
|
| Provider | Models | Note |
|
|
155
168
|
|----------|--------|------|
|
|
156
|
-
| **
|
|
157
|
-
| **
|
|
158
|
-
| **Google Gemini** | 2.5 Flash, 2.5 Pro, 2.0 Flash | Free tier available |
|
|
159
|
-
| **DeepSeek** | Chat, Reasoner | Budget-friendly |
|
|
160
|
-
| **Groq** | Llama 3.3 70B, Llama 3.1 8B, Mixtral | Ultra-fast, free tier |
|
|
161
|
-
| **Ollama** | Any local model | Offline, no API key |
|
|
169
|
+
| **Google Gemini** | 2.5 Flash, 2.5 Pro, 2.0 Flash | Free — just a Google account |
|
|
170
|
+
| **Claude** (Anthropic) | Sonnet 4, Haiku 3.5, Opus 4 | Best quality |
|
|
162
171
|
|
|
163
172
|
</div>
|
|
164
173
|
|
|
174
|
+
CCODE auto-detects your provider:
|
|
175
|
+
|
|
176
|
+
1. **Gemini CLI OAuth** — If you have `gemini` CLI installed and authenticated, it works instantly. Zero config.
|
|
177
|
+
2. **Environment variables** — `GOOGLE_API_KEY` or `ANTHROPIC_API_KEY` detected automatically.
|
|
178
|
+
3. **Manual setup** — Guided wizard with browser-based key generation as fallback.
|
|
179
|
+
|
|
165
180
|
---
|
|
166
181
|
|
|
167
182
|
## Available commands
|
|
168
183
|
|
|
169
184
|
| Command | What it does |
|
|
170
185
|
|---------|-------------|
|
|
171
|
-
| `ccode init` | Interactive wizard — generates full project context |
|
|
172
|
-
| `ccode
|
|
173
|
-
| `ccode
|
|
174
|
-
| `ccode
|
|
175
|
-
| `ccode
|
|
176
|
-
| `ccode connect` | Configure AI provider and model |
|
|
186
|
+
| `ccode init` | Interactive wizard — generates full project context + syncs to all AI tools |
|
|
187
|
+
| `ccode sync` | Sync context to all AI tools (AGENTS.md, CLAUDE.md, .cursorrules, ...) |
|
|
188
|
+
| `ccode update` | Re-analyze project with AI and refresh context |
|
|
189
|
+
| `ccode export` | Export context — all tools, universal .md, or pick specific |
|
|
190
|
+
| `ccode verify` | AI-powered task verification against actual project files |
|
|
177
191
|
| `ccode status` | Dashboard with progress bar and stats |
|
|
178
|
-
| `ccode
|
|
192
|
+
| `ccode doctor` | Health check — context files, AI connection, exports, tasks |
|
|
193
|
+
| `ccode connect` | Configure or reconfigure AI provider |
|
|
194
|
+
| `ccode explain` | Quick project summary for onboarding |
|
|
195
|
+
| `ccode plan` | Generate or regenerate task checklist |
|
|
196
|
+
| `ccode next` | Show and start the next pending task |
|
|
197
|
+
| `ccode complete` | Mark active task as completed |
|
|
179
198
|
|
|
180
199
|
---
|
|
181
200
|
|
|
182
201
|
## What gets generated
|
|
183
202
|
|
|
184
|
-
|
|
203
|
+
### Source of truth (`.ccode/`)
|
|
185
204
|
|
|
186
205
|
```
|
|
187
206
|
.ccode/
|
|
@@ -192,8 +211,21 @@ Everything lives in `.ccode/` inside your repository:
|
|
|
192
211
|
├── state.json Active task, workflow stage
|
|
193
212
|
├── context.json Project configuration
|
|
194
213
|
├── memory.md Decision history
|
|
195
|
-
|
|
214
|
+
├── config.json AI provider config
|
|
215
|
+
└── context-export.md Universal export
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### Synced context files (project root)
|
|
219
|
+
|
|
196
220
|
```
|
|
221
|
+
AGENTS.md Open Standard
|
|
222
|
+
CLAUDE.md Claude Code
|
|
223
|
+
GEMINI.md Gemini CLI
|
|
224
|
+
.cursorrules Cursor
|
|
225
|
+
.github/copilot-instructions.md GitHub Copilot
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
All derived from `.ccode/`. One source, multiple outputs.
|
|
197
229
|
|
|
198
230
|
---
|
|
199
231
|
|
|
@@ -202,8 +234,8 @@ Everything lives in `.ccode/` inside your repository:
|
|
|
202
234
|
```
|
|
203
235
|
src/
|
|
204
236
|
├── cli/ Session, branding, file watcher
|
|
205
|
-
├── core/ Context engine, tasks, prompt builder
|
|
206
|
-
├── ai/
|
|
237
|
+
├── core/ Context engine, tasks, prompt builder, exports
|
|
238
|
+
├── ai/ Provider adapters (Adapter pattern)
|
|
207
239
|
└── utils/ File system abstraction
|
|
208
240
|
```
|
|
209
241
|
|
|
@@ -215,7 +247,11 @@ src/
|
|
|
215
247
|
|
|
216
248
|
See [CONTRIBUTING.md](CONTRIBUTING.md) for setup instructions.
|
|
217
249
|
|
|
218
|
-
Adding a new AI provider?
|
|
250
|
+
Adding a new AI provider? Implement `IAIProvider`, add it to the manager, done.
|
|
251
|
+
|
|
252
|
+
Adding a new export format? Add a method to `ContextExporter`, register the format, done.
|
|
253
|
+
|
|
254
|
+
Zero changes to the rest of the system in both cases.
|
|
219
255
|
|
|
220
256
|
---
|
|
221
257
|
|
|
@@ -223,16 +259,15 @@ Adding a new AI provider? Just implement `IAIProvider`, add it to the manager sw
|
|
|
223
259
|
|
|
224
260
|
| Resource | Link |
|
|
225
261
|
|----------|------|
|
|
226
|
-
| Learning guide (6 modules, QP2C) | [docs/learning/](docs/learning/README.md) |
|
|
227
262
|
| Engineering roles | [AGENTS.md](AGENTS.md) |
|
|
228
263
|
| Technical competencies | [SKILLS.md](SKILLS.md) |
|
|
229
|
-
| YouTube
|
|
264
|
+
| YouTube | [@CreativeCode25](https://www.youtube.com/@CreativeCode25) |
|
|
230
265
|
|
|
231
266
|
---
|
|
232
267
|
|
|
233
268
|
<div align="center">
|
|
234
269
|
|
|
235
|
-
### If CCODE helps you, consider giving it a star
|
|
270
|
+
### If CCODE helps you, consider giving it a star
|
|
236
271
|
|
|
237
272
|
It helps the project grow and reach more developers.
|
|
238
273
|
|
package/dist/ai/claude.d.ts
CHANGED
|
@@ -3,7 +3,8 @@ import { IAIProvider, IAIConfig } from './provider.js';
|
|
|
3
3
|
* Adaptador para Claude (Anthropic).
|
|
4
4
|
*/
|
|
5
5
|
export declare class ClaudeAdapter implements IAIProvider {
|
|
6
|
-
private
|
|
6
|
+
private apiKey;
|
|
7
|
+
private model;
|
|
7
8
|
constructor(config: IAIConfig);
|
|
8
9
|
getName(): string;
|
|
9
10
|
generate(prompt: string): Promise<string>;
|
package/dist/ai/claude.js
CHANGED
|
@@ -3,32 +3,35 @@ import axios from 'axios';
|
|
|
3
3
|
* Adaptador para Claude (Anthropic).
|
|
4
4
|
*/
|
|
5
5
|
export class ClaudeAdapter {
|
|
6
|
-
|
|
6
|
+
apiKey;
|
|
7
|
+
model;
|
|
7
8
|
constructor(config) {
|
|
8
|
-
this.
|
|
9
|
-
|
|
10
|
-
model: config.model || 'claude-sonnet-4-20250514',
|
|
11
|
-
baseUrl: config.baseUrl || 'https://api.anthropic.com/v1/messages',
|
|
12
|
-
};
|
|
9
|
+
this.apiKey = config.apiKey || '';
|
|
10
|
+
this.model = config.model || 'claude-sonnet-4-20250514';
|
|
13
11
|
}
|
|
14
12
|
getName() {
|
|
15
|
-
return `Claude (${this.
|
|
13
|
+
return `Claude (${this.model})`;
|
|
16
14
|
}
|
|
17
15
|
async generate(prompt) {
|
|
18
|
-
if (!this.
|
|
16
|
+
if (!this.apiKey) {
|
|
19
17
|
throw new Error('API Key de Anthropic no configurada.');
|
|
20
18
|
}
|
|
21
|
-
const response = await axios.post(
|
|
22
|
-
model: this.
|
|
19
|
+
const response = await axios.post('https://api.anthropic.com/v1/messages', {
|
|
20
|
+
model: this.model,
|
|
23
21
|
max_tokens: 8096,
|
|
24
22
|
messages: [{ role: 'user', content: prompt }],
|
|
25
23
|
}, {
|
|
26
24
|
headers: {
|
|
27
|
-
'x-api-key': this.
|
|
25
|
+
'x-api-key': this.apiKey,
|
|
28
26
|
'anthropic-version': '2023-06-01',
|
|
29
27
|
'content-type': 'application/json',
|
|
30
28
|
},
|
|
29
|
+
timeout: 120000,
|
|
31
30
|
});
|
|
32
|
-
|
|
31
|
+
const text = response.data?.content?.[0]?.text;
|
|
32
|
+
if (!text) {
|
|
33
|
+
throw new Error('Claude respondio pero sin contenido. Intenta de nuevo.');
|
|
34
|
+
}
|
|
35
|
+
return text;
|
|
33
36
|
}
|
|
34
37
|
}
|
package/dist/ai/gemini.d.ts
CHANGED
|
@@ -1,10 +1,22 @@
|
|
|
1
1
|
import { IAIProvider, IAIConfig } from './provider.js';
|
|
2
2
|
/**
|
|
3
3
|
* Adaptador para Google Gemini.
|
|
4
|
+
* Soporta API Key y OAuth token (de Gemini CLI).
|
|
4
5
|
*/
|
|
5
6
|
export declare class GeminiAdapter implements IAIProvider {
|
|
6
|
-
private
|
|
7
|
+
private apiKey;
|
|
8
|
+
private oauthToken;
|
|
9
|
+
private model;
|
|
7
10
|
constructor(config: IAIConfig);
|
|
8
11
|
getName(): string;
|
|
12
|
+
/**
|
|
13
|
+
* Try to read OAuth token from Gemini CLI installation.
|
|
14
|
+
*/
|
|
15
|
+
static readOAuthToken(): string | null;
|
|
16
|
+
/**
|
|
17
|
+
* Check if Gemini CLI is installed and authenticated.
|
|
18
|
+
*/
|
|
19
|
+
static isAvailable(): boolean;
|
|
20
|
+
setOAuthToken(token: string): void;
|
|
9
21
|
generate(prompt: string): Promise<string>;
|
|
10
22
|
}
|
package/dist/ai/gemini.js
CHANGED
|
@@ -1,31 +1,78 @@
|
|
|
1
1
|
import axios from 'axios';
|
|
2
|
+
import * as fs from 'fs';
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
import * as os from 'os';
|
|
2
5
|
/**
|
|
3
6
|
* Adaptador para Google Gemini.
|
|
7
|
+
* Soporta API Key y OAuth token (de Gemini CLI).
|
|
4
8
|
*/
|
|
5
9
|
export class GeminiAdapter {
|
|
6
|
-
|
|
10
|
+
apiKey;
|
|
11
|
+
oauthToken;
|
|
12
|
+
model;
|
|
7
13
|
constructor(config) {
|
|
8
|
-
this.
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
baseUrl: config.baseUrl || 'https://generativelanguage.googleapis.com/v1beta/models',
|
|
12
|
-
};
|
|
14
|
+
this.apiKey = config.apiKey || '';
|
|
15
|
+
this.oauthToken = '';
|
|
16
|
+
this.model = config.model || 'gemini-2.5-flash';
|
|
13
17
|
}
|
|
14
18
|
getName() {
|
|
15
|
-
return `Gemini (${this.
|
|
19
|
+
return `Gemini (${this.model})`;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Try to read OAuth token from Gemini CLI installation.
|
|
23
|
+
*/
|
|
24
|
+
static readOAuthToken() {
|
|
25
|
+
try {
|
|
26
|
+
const credsPath = path.join(os.homedir(), '.gemini', 'oauth_creds.json');
|
|
27
|
+
if (!fs.existsSync(credsPath))
|
|
28
|
+
return null;
|
|
29
|
+
const creds = JSON.parse(fs.readFileSync(credsPath, 'utf-8'));
|
|
30
|
+
if (creds.access_token && typeof creds.access_token === 'string') {
|
|
31
|
+
return creds.access_token;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
// ignore
|
|
36
|
+
}
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Check if Gemini CLI is installed and authenticated.
|
|
41
|
+
*/
|
|
42
|
+
static isAvailable() {
|
|
43
|
+
return GeminiAdapter.readOAuthToken() !== null;
|
|
44
|
+
}
|
|
45
|
+
setOAuthToken(token) {
|
|
46
|
+
this.oauthToken = token;
|
|
16
47
|
}
|
|
17
48
|
async generate(prompt) {
|
|
18
|
-
|
|
19
|
-
|
|
49
|
+
const baseUrl = `https://generativelanguage.googleapis.com/v1beta/models/${this.model}:generateContent`;
|
|
50
|
+
let url;
|
|
51
|
+
let headers;
|
|
52
|
+
if (this.oauthToken) {
|
|
53
|
+
// Use OAuth token from Gemini CLI
|
|
54
|
+
url = baseUrl;
|
|
55
|
+
headers = {
|
|
56
|
+
'Content-Type': 'application/json',
|
|
57
|
+
'Authorization': `Bearer ${this.oauthToken}`,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
else if (this.apiKey) {
|
|
61
|
+
// Use API key
|
|
62
|
+
url = `${baseUrl}?key=${this.apiKey}`;
|
|
63
|
+
headers = { 'Content-Type': 'application/json' };
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
throw new Error('No hay credenciales de Gemini configuradas.');
|
|
20
67
|
}
|
|
21
|
-
const url = `${this.config.baseUrl}/${this.config.model}:generateContent?key=${this.config.apiKey}`;
|
|
22
68
|
const response = await axios.post(url, {
|
|
23
69
|
contents: [{ parts: [{ text: prompt }] }],
|
|
24
70
|
generationConfig: { maxOutputTokens: 8096 },
|
|
25
|
-
}, {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
71
|
+
}, { headers, timeout: 120000 });
|
|
72
|
+
const text = response.data?.candidates?.[0]?.content?.parts?.[0]?.text;
|
|
73
|
+
if (!text) {
|
|
74
|
+
throw new Error('Gemini respondio pero sin contenido. Intenta de nuevo.');
|
|
75
|
+
}
|
|
76
|
+
return text;
|
|
30
77
|
}
|
|
31
78
|
}
|
package/dist/ai/manager.d.ts
CHANGED
|
@@ -1,18 +1,37 @@
|
|
|
1
1
|
import { IAIProvider } from './provider.js';
|
|
2
|
-
export type ProviderName = 'claude' | '
|
|
2
|
+
export type ProviderName = 'claude' | 'gemini';
|
|
3
3
|
export interface ICCODEConfig {
|
|
4
4
|
provider: ProviderName;
|
|
5
5
|
apiKey?: string;
|
|
6
6
|
model?: string;
|
|
7
|
-
|
|
7
|
+
authType?: 'api-key' | 'oauth';
|
|
8
8
|
}
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
export declare const PROVIDER_INFO: Record<ProviderName, {
|
|
10
|
+
name: string;
|
|
11
|
+
keyUrl: string;
|
|
12
|
+
envVars: string[];
|
|
13
|
+
models: Array<{
|
|
14
|
+
name: string;
|
|
15
|
+
value: string;
|
|
16
|
+
}>;
|
|
17
|
+
}>;
|
|
12
18
|
export declare class AIManager {
|
|
13
19
|
private static readonly CONFIG_FILE;
|
|
14
20
|
static loadConfig(): Promise<ICCODEConfig | null>;
|
|
15
21
|
static saveConfig(config: ICCODEConfig): Promise<void>;
|
|
16
22
|
static getProvider(config: ICCODEConfig): IAIProvider;
|
|
17
|
-
static testConnection(config: ICCODEConfig): Promise<
|
|
23
|
+
static testConnection(config: ICCODEConfig): Promise<{
|
|
24
|
+
ok: boolean;
|
|
25
|
+
error?: string;
|
|
26
|
+
}>;
|
|
27
|
+
/**
|
|
28
|
+
* Auto-detect the best available provider.
|
|
29
|
+
* Priority: 1) Gemini CLI OAuth, 2) env vars, 3) null
|
|
30
|
+
*/
|
|
31
|
+
static autoDetect(): {
|
|
32
|
+
provider: ProviderName;
|
|
33
|
+
authType: 'oauth' | 'api-key';
|
|
34
|
+
apiKey?: string;
|
|
35
|
+
source: string;
|
|
36
|
+
} | null;
|
|
18
37
|
}
|
package/dist/ai/manager.js
CHANGED
|
@@ -1,14 +1,30 @@
|
|
|
1
1
|
import * as path from 'path';
|
|
2
|
+
import axios from 'axios';
|
|
2
3
|
import { ClaudeAdapter } from './claude.js';
|
|
3
|
-
import { OpenAIAdapter } from './openai.js';
|
|
4
4
|
import { GeminiAdapter } from './gemini.js';
|
|
5
|
-
import { DeepSeekAdapter } from './deepseek.js';
|
|
6
|
-
import { GroqAdapter } from './groq.js';
|
|
7
|
-
import { OllamaAdapter } from './ollama.js';
|
|
8
5
|
import { FileUtils } from '../utils/files.js';
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
6
|
+
export const PROVIDER_INFO = {
|
|
7
|
+
gemini: {
|
|
8
|
+
name: 'Google Gemini',
|
|
9
|
+
keyUrl: 'https://aistudio.google.com/apikey',
|
|
10
|
+
envVars: ['GOOGLE_API_KEY', 'GEMINI_API_KEY'],
|
|
11
|
+
models: [
|
|
12
|
+
{ name: 'Gemini 2.5 Flash (recomendado, gratis)', value: 'gemini-2.5-flash' },
|
|
13
|
+
{ name: 'Gemini 2.5 Pro (maxima calidad)', value: 'gemini-2.5-pro' },
|
|
14
|
+
{ name: 'Gemini 2.0 Flash (rapido)', value: 'gemini-2.0-flash' },
|
|
15
|
+
],
|
|
16
|
+
},
|
|
17
|
+
claude: {
|
|
18
|
+
name: 'Claude (Anthropic)',
|
|
19
|
+
keyUrl: 'https://console.anthropic.com/settings/keys',
|
|
20
|
+
envVars: ['ANTHROPIC_API_KEY'],
|
|
21
|
+
models: [
|
|
22
|
+
{ name: 'Claude Sonnet 4 (recomendado)', value: 'claude-sonnet-4-20250514' },
|
|
23
|
+
{ name: 'Claude Haiku 3.5 (rapido)', value: 'claude-haiku-4-5-20251001' },
|
|
24
|
+
{ name: 'Claude Opus 4 (maxima calidad)', value: 'claude-opus-4-20250514' },
|
|
25
|
+
],
|
|
26
|
+
},
|
|
27
|
+
};
|
|
12
28
|
export class AIManager {
|
|
13
29
|
static CONFIG_FILE = '.ccode/config.json';
|
|
14
30
|
static async loadConfig() {
|
|
@@ -24,18 +40,18 @@ export class AIManager {
|
|
|
24
40
|
}
|
|
25
41
|
static getProvider(config) {
|
|
26
42
|
switch (config.provider) {
|
|
43
|
+
case 'gemini': {
|
|
44
|
+
const adapter = new GeminiAdapter({ apiKey: config.apiKey, model: config.model });
|
|
45
|
+
// If using OAuth from Gemini CLI
|
|
46
|
+
if (config.authType === 'oauth') {
|
|
47
|
+
const token = GeminiAdapter.readOAuthToken();
|
|
48
|
+
if (token)
|
|
49
|
+
adapter.setOAuthToken(token);
|
|
50
|
+
}
|
|
51
|
+
return adapter;
|
|
52
|
+
}
|
|
27
53
|
case 'claude':
|
|
28
54
|
return new ClaudeAdapter({ apiKey: config.apiKey, model: config.model });
|
|
29
|
-
case 'openai':
|
|
30
|
-
return new OpenAIAdapter({ apiKey: config.apiKey, model: config.model });
|
|
31
|
-
case 'gemini':
|
|
32
|
-
return new GeminiAdapter({ apiKey: config.apiKey, model: config.model });
|
|
33
|
-
case 'deepseek':
|
|
34
|
-
return new DeepSeekAdapter({ apiKey: config.apiKey, model: config.model });
|
|
35
|
-
case 'groq':
|
|
36
|
-
return new GroqAdapter({ apiKey: config.apiKey, model: config.model });
|
|
37
|
-
case 'ollama':
|
|
38
|
-
return new OllamaAdapter({ model: config.model, baseUrl: config.baseUrl });
|
|
39
55
|
default:
|
|
40
56
|
throw new Error(`Proveedor desconocido: ${config.provider}`);
|
|
41
57
|
}
|
|
@@ -43,11 +59,57 @@ export class AIManager {
|
|
|
43
59
|
static async testConnection(config) {
|
|
44
60
|
try {
|
|
45
61
|
const provider = this.getProvider(config);
|
|
46
|
-
await provider.generate('Responde
|
|
47
|
-
|
|
62
|
+
const result = await provider.generate('Responde unicamente con la palabra "ok".');
|
|
63
|
+
if (!result || result.trim().length === 0) {
|
|
64
|
+
return { ok: false, error: 'La IA respondio pero el contenido esta vacio.' };
|
|
65
|
+
}
|
|
66
|
+
return { ok: true };
|
|
48
67
|
}
|
|
49
|
-
catch {
|
|
50
|
-
|
|
68
|
+
catch (err) {
|
|
69
|
+
if (axios.isAxiosError(err)) {
|
|
70
|
+
const status = err.response?.status;
|
|
71
|
+
const data = err.response?.data;
|
|
72
|
+
if (status === 401 || status === 403) {
|
|
73
|
+
return { ok: false, error: `Credenciales invalidas (HTTP ${status}). Verifica tu autenticacion.` };
|
|
74
|
+
}
|
|
75
|
+
if (status === 404) {
|
|
76
|
+
return { ok: false, error: 'Modelo no encontrado. Verifica el nombre del modelo.' };
|
|
77
|
+
}
|
|
78
|
+
if (status === 429) {
|
|
79
|
+
return { ok: false, error: 'Rate limit. Espera unos segundos e intenta de nuevo.' };
|
|
80
|
+
}
|
|
81
|
+
if (err.code === 'ECONNREFUSED' || err.code === 'ENOTFOUND') {
|
|
82
|
+
return { ok: false, error: 'Sin conexion a internet.' };
|
|
83
|
+
}
|
|
84
|
+
if (err.code === 'ETIMEDOUT' || err.message?.includes('timeout')) {
|
|
85
|
+
return { ok: false, error: 'Timeout: la IA no respondio a tiempo.' };
|
|
86
|
+
}
|
|
87
|
+
const msg = typeof data === 'object' && data?.error?.message
|
|
88
|
+
? data.error.message
|
|
89
|
+
: `Error HTTP ${status || 'desconocido'}`;
|
|
90
|
+
return { ok: false, error: msg };
|
|
91
|
+
}
|
|
92
|
+
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
51
93
|
}
|
|
52
94
|
}
|
|
95
|
+
/**
|
|
96
|
+
* Auto-detect the best available provider.
|
|
97
|
+
* Priority: 1) Gemini CLI OAuth, 2) env vars, 3) null
|
|
98
|
+
*/
|
|
99
|
+
static autoDetect() {
|
|
100
|
+
// 1. Gemini CLI installed and authenticated
|
|
101
|
+
if (GeminiAdapter.isAvailable()) {
|
|
102
|
+
return { provider: 'gemini', authType: 'oauth', source: 'Gemini CLI' };
|
|
103
|
+
}
|
|
104
|
+
// 2. Environment variables
|
|
105
|
+
for (const [provider, info] of Object.entries(PROVIDER_INFO)) {
|
|
106
|
+
for (const envVar of info.envVars) {
|
|
107
|
+
const value = process.env[envVar];
|
|
108
|
+
if (value && value.length > 10) {
|
|
109
|
+
return { provider: provider, authType: 'api-key', apiKey: value, source: envVar };
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
53
115
|
}
|
package/dist/cli/brand.js
CHANGED
|
@@ -27,7 +27,7 @@ export function showLogo() {
|
|
|
27
27
|
LOGO_LINES.forEach((line, i) => {
|
|
28
28
|
console.log(chalk.hex(GRADIENT[i])(line));
|
|
29
29
|
});
|
|
30
|
-
console.log(c.dim(' Context-Persistent AI Development
|
|
30
|
+
console.log(c.dim(' Context-Persistent AI Development v3.0'));
|
|
31
31
|
console.log('');
|
|
32
32
|
}
|
|
33
33
|
// ─── Componentes UI ─────────────────────────────────────────────────
|