codeep 1.0.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.
Files changed (103) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +576 -0
  3. package/dist/api/index.d.ts +8 -0
  4. package/dist/api/index.js +421 -0
  5. package/dist/app.d.ts +2 -0
  6. package/dist/app.js +1406 -0
  7. package/dist/components/AgentProgress.d.ts +33 -0
  8. package/dist/components/AgentProgress.js +97 -0
  9. package/dist/components/Export.d.ts +8 -0
  10. package/dist/components/Export.js +27 -0
  11. package/dist/components/Help.d.ts +2 -0
  12. package/dist/components/Help.js +3 -0
  13. package/dist/components/Input.d.ts +9 -0
  14. package/dist/components/Input.js +89 -0
  15. package/dist/components/Loading.d.ts +9 -0
  16. package/dist/components/Loading.js +31 -0
  17. package/dist/components/Login.d.ts +7 -0
  18. package/dist/components/Login.js +77 -0
  19. package/dist/components/Logo.d.ts +8 -0
  20. package/dist/components/Logo.js +89 -0
  21. package/dist/components/LogoutPicker.d.ts +8 -0
  22. package/dist/components/LogoutPicker.js +61 -0
  23. package/dist/components/Message.d.ts +10 -0
  24. package/dist/components/Message.js +234 -0
  25. package/dist/components/MessageList.d.ts +10 -0
  26. package/dist/components/MessageList.js +8 -0
  27. package/dist/components/ProjectPermission.d.ts +7 -0
  28. package/dist/components/ProjectPermission.js +52 -0
  29. package/dist/components/Search.d.ts +10 -0
  30. package/dist/components/Search.js +30 -0
  31. package/dist/components/SessionPicker.d.ts +9 -0
  32. package/dist/components/SessionPicker.js +88 -0
  33. package/dist/components/Sessions.d.ts +12 -0
  34. package/dist/components/Sessions.js +102 -0
  35. package/dist/components/Settings.d.ts +7 -0
  36. package/dist/components/Settings.js +162 -0
  37. package/dist/components/Status.d.ts +2 -0
  38. package/dist/components/Status.js +12 -0
  39. package/dist/config/config.test.d.ts +1 -0
  40. package/dist/config/config.test.js +157 -0
  41. package/dist/config/index.d.ts +121 -0
  42. package/dist/config/index.js +555 -0
  43. package/dist/config/providers.d.ts +43 -0
  44. package/dist/config/providers.js +82 -0
  45. package/dist/config/providers.test.d.ts +1 -0
  46. package/dist/config/providers.test.js +132 -0
  47. package/dist/index.d.ts +2 -0
  48. package/dist/index.js +38 -0
  49. package/dist/utils/agent.d.ts +37 -0
  50. package/dist/utils/agent.js +627 -0
  51. package/dist/utils/codeReview.d.ts +36 -0
  52. package/dist/utils/codeReview.js +390 -0
  53. package/dist/utils/context.d.ts +49 -0
  54. package/dist/utils/context.js +216 -0
  55. package/dist/utils/diffPreview.d.ts +57 -0
  56. package/dist/utils/diffPreview.js +335 -0
  57. package/dist/utils/export.d.ts +19 -0
  58. package/dist/utils/export.js +94 -0
  59. package/dist/utils/git.d.ts +85 -0
  60. package/dist/utils/git.js +399 -0
  61. package/dist/utils/git.test.d.ts +1 -0
  62. package/dist/utils/git.test.js +193 -0
  63. package/dist/utils/history.d.ts +93 -0
  64. package/dist/utils/history.js +348 -0
  65. package/dist/utils/interactive.d.ts +34 -0
  66. package/dist/utils/interactive.js +206 -0
  67. package/dist/utils/keychain.d.ts +17 -0
  68. package/dist/utils/keychain.js +160 -0
  69. package/dist/utils/learning.d.ts +89 -0
  70. package/dist/utils/learning.js +330 -0
  71. package/dist/utils/logger.d.ts +33 -0
  72. package/dist/utils/logger.js +130 -0
  73. package/dist/utils/project.d.ts +86 -0
  74. package/dist/utils/project.js +415 -0
  75. package/dist/utils/project.test.d.ts +1 -0
  76. package/dist/utils/project.test.js +212 -0
  77. package/dist/utils/ratelimit.d.ts +26 -0
  78. package/dist/utils/ratelimit.js +132 -0
  79. package/dist/utils/ratelimit.test.d.ts +1 -0
  80. package/dist/utils/ratelimit.test.js +131 -0
  81. package/dist/utils/retry.d.ts +28 -0
  82. package/dist/utils/retry.js +109 -0
  83. package/dist/utils/retry.test.d.ts +1 -0
  84. package/dist/utils/retry.test.js +163 -0
  85. package/dist/utils/search.d.ts +11 -0
  86. package/dist/utils/search.js +29 -0
  87. package/dist/utils/shell.d.ts +45 -0
  88. package/dist/utils/shell.js +242 -0
  89. package/dist/utils/skills.d.ts +144 -0
  90. package/dist/utils/skills.js +1137 -0
  91. package/dist/utils/smartContext.d.ts +29 -0
  92. package/dist/utils/smartContext.js +441 -0
  93. package/dist/utils/tools.d.ts +224 -0
  94. package/dist/utils/tools.js +731 -0
  95. package/dist/utils/update.d.ts +22 -0
  96. package/dist/utils/update.js +128 -0
  97. package/dist/utils/validation.d.ts +28 -0
  98. package/dist/utils/validation.js +141 -0
  99. package/dist/utils/validation.test.d.ts +1 -0
  100. package/dist/utils/validation.test.js +164 -0
  101. package/dist/utils/verify.d.ts +78 -0
  102. package/dist/utils/verify.js +464 -0
  103. package/package.json +68 -0
@@ -0,0 +1,421 @@
1
+ import { config, getApiKey } from '../config/index';
2
+ import { withRetry, isNetworkError, isTimeoutError } from '../utils/retry';
3
+ import { getProvider, getProviderBaseUrl, getProviderAuthHeader } from '../config/providers';
4
+ import { logApiRequest, logApiResponse } from '../utils/logger';
5
+ // Error messages by language
6
+ const ERROR_MESSAGES = {
7
+ en: {
8
+ noInternet: 'No internet connection. Please check your network.',
9
+ timeout: 'Request timed out. Please try again.',
10
+ retrying: 'Connection failed, retrying...',
11
+ apiError: 'API error',
12
+ },
13
+ hr: {
14
+ noInternet: 'Nema internet konekcije. Provjerite mrežu.',
15
+ timeout: 'Zahtjev je istekao. Pokušajte ponovo.',
16
+ retrying: 'Konekcija nije uspjela, pokušavam ponovo...',
17
+ apiError: 'API greška',
18
+ },
19
+ };
20
+ function getErrorMessage(key) {
21
+ const lang = config.get('language');
22
+ const messages = ERROR_MESSAGES[lang] || ERROR_MESSAGES['en'];
23
+ return messages[key] || ERROR_MESSAGES['en'][key];
24
+ }
25
+ // Store project context for use in system prompt
26
+ let currentProjectContext = null;
27
+ export function setProjectContext(ctx) {
28
+ currentProjectContext = ctx;
29
+ }
30
+ export async function chat(message, history = [], onChunk, onRetry, projectContext, abortSignal) {
31
+ // Update project context if provided
32
+ if (projectContext !== undefined) {
33
+ currentProjectContext = projectContext;
34
+ }
35
+ const protocol = config.get('protocol');
36
+ const model = config.get('model');
37
+ const apiKey = getApiKey();
38
+ const providerId = config.get('provider');
39
+ if (!apiKey) {
40
+ throw new Error('API key not configured');
41
+ }
42
+ // Log API request
43
+ logApiRequest(providerId, model, history.length + 1);
44
+ const chatFn = protocol === 'anthropic'
45
+ ? () => chatAnthropic(message, history, model, apiKey, onChunk, abortSignal)
46
+ : () => chatOpenAI(message, history, model, apiKey, onChunk, abortSignal);
47
+ try {
48
+ const response = await withRetry(chatFn, {
49
+ maxAttempts: 3,
50
+ baseDelay: 1000,
51
+ onRetry: (attempt, error) => {
52
+ if (onRetry) {
53
+ onRetry(attempt);
54
+ }
55
+ },
56
+ shouldRetry: (error) => {
57
+ // Don't retry on user abort
58
+ if (error.name === 'AbortError')
59
+ return false;
60
+ // Don't retry on 4xx client errors (except 429 rate limit)
61
+ if (error.status >= 400 && error.status < 500 && error.status !== 429)
62
+ return false;
63
+ // Retry on network errors, timeouts, 5xx, and rate limits
64
+ return true;
65
+ },
66
+ });
67
+ // Log successful response
68
+ logApiResponse(providerId, true, response.length);
69
+ return response;
70
+ }
71
+ catch (error) {
72
+ const err = error;
73
+ // Don't log or throw if request was aborted by user
74
+ if (err.name === 'AbortError' || err.message?.includes('aborted')) {
75
+ throw error; // Re-throw abort errors silently
76
+ }
77
+ // Log error
78
+ logApiResponse(providerId, false, undefined, err.message);
79
+ // Translate errors to user-friendly messages
80
+ if (isNetworkError(error)) {
81
+ throw new Error(getErrorMessage('noInternet'));
82
+ }
83
+ if (isTimeoutError(error)) {
84
+ throw new Error(getErrorMessage('timeout'));
85
+ }
86
+ throw error;
87
+ }
88
+ }
89
+ const LANGUAGE_NAMES = {
90
+ 'en': 'English',
91
+ 'zh': 'Chinese (中文)',
92
+ 'es': 'Spanish (Español)',
93
+ 'hi': 'Hindi (हिन्दी)',
94
+ 'ar': 'Arabic (العربية)',
95
+ 'pt': 'Portuguese (Português)',
96
+ 'fr': 'French (Français)',
97
+ 'de': 'German (Deutsch)',
98
+ 'ja': 'Japanese (日本語)',
99
+ 'ru': 'Russian (Русский)',
100
+ 'hr': 'Croatian (Hrvatski)',
101
+ };
102
+ function getSystemPrompt() {
103
+ const language = config.get('language');
104
+ let basePrompt;
105
+ if (language === 'auto') {
106
+ basePrompt = `You are a helpful AI coding assistant. Always respond in the same language as the user's message. Detect the language of the user's input and reply in that same language.`;
107
+ }
108
+ else {
109
+ const langName = LANGUAGE_NAMES[language] || 'English';
110
+ basePrompt = `You are a helpful AI coding assistant. Always respond in ${langName}, regardless of what language the user writes in.`;
111
+ }
112
+ // Important: This is CHAT mode, not agent mode
113
+ // The model should NOT pretend to execute tools or create files
114
+ basePrompt += `
115
+
116
+ IMPORTANT: You are in CHAT mode, NOT agent mode. You do NOT have the ability to:
117
+ - Create, edit, or delete files directly
118
+ - Execute shell commands
119
+ - Use tools or tool_calls
120
+
121
+ If the conversation history contains messages about file creation or tool execution, those were from a previous agent session. In chat mode, you can only provide advice, explanations, and code suggestions that the user must manually apply.`;
122
+ // Add project context if available
123
+ if (currentProjectContext) {
124
+ const writeInfo = currentProjectContext.hasWriteAccess
125
+ ? `
126
+
127
+ **Write Access:** ENABLED - You can suggest file modifications. Format them as:
128
+ \`\`\`filepath:path/to/file.ts
129
+ // modified code here
130
+ \`\`\`
131
+ The user will review and approve changes before they are applied.`
132
+ : `
133
+
134
+ **Write Access:** READ-ONLY - You can analyze code but cannot suggest file modifications.`;
135
+ const projectInfo = `
136
+
137
+ ## Project Context
138
+ You are working with a ${currentProjectContext.type} project called "${currentProjectContext.name}".
139
+
140
+ **Project Structure:**
141
+ \`\`\`
142
+ ${currentProjectContext.structure}
143
+ \`\`\`
144
+
145
+ **Key Files:** ${currentProjectContext.keyFiles.join(', ')}${writeInfo}
146
+
147
+ When the user mentions a file path, the file content will be automatically attached to their message.
148
+ You can analyze, explain, or suggest improvements to the code.`;
149
+ return basePrompt + projectInfo;
150
+ }
151
+ return basePrompt;
152
+ }
153
+ async function chatOpenAI(message, history, model, apiKey, onChunk, abortSignal) {
154
+ const messages = [
155
+ { role: 'system', content: getSystemPrompt() },
156
+ ...history,
157
+ { role: 'user', content: message },
158
+ ];
159
+ const stream = Boolean(onChunk);
160
+ const timeout = config.get('apiTimeout');
161
+ const temperature = config.get('temperature');
162
+ const maxTokens = config.get('maxTokens');
163
+ // Get provider-specific URL and auth
164
+ const providerId = config.get('provider');
165
+ const baseUrl = getProviderBaseUrl(providerId, 'openai');
166
+ const authHeader = getProviderAuthHeader(providerId, 'openai');
167
+ if (!baseUrl) {
168
+ throw new Error(`Provider ${providerId} does not support OpenAI protocol`);
169
+ }
170
+ // Create abort controller for timeout or use provided one
171
+ const controller = abortSignal ? new AbortController() : new AbortController();
172
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
173
+ // Listen to external abort signal if provided
174
+ if (abortSignal) {
175
+ abortSignal.addEventListener('abort', () => controller.abort());
176
+ }
177
+ // Build headers based on auth type
178
+ const headers = {
179
+ 'Content-Type': 'application/json',
180
+ };
181
+ if (authHeader === 'Bearer') {
182
+ headers['Authorization'] = `Bearer ${apiKey}`;
183
+ }
184
+ else {
185
+ headers['x-api-key'] = apiKey;
186
+ }
187
+ try {
188
+ const response = await fetch(`${baseUrl}/chat/completions`, {
189
+ method: 'POST',
190
+ headers,
191
+ body: JSON.stringify({
192
+ model,
193
+ messages,
194
+ stream,
195
+ temperature,
196
+ max_tokens: maxTokens,
197
+ }),
198
+ signal: controller.signal,
199
+ });
200
+ if (!response.ok) {
201
+ const error = await response.text();
202
+ const err = new Error(`${getErrorMessage('apiError')}: ${response.status} - ${error}`);
203
+ err.status = response.status;
204
+ throw err;
205
+ }
206
+ if (stream && response.body) {
207
+ return handleOpenAIStream(response.body, onChunk);
208
+ }
209
+ else {
210
+ const data = await response.json();
211
+ const content = data.choices[0]?.message?.content || '';
212
+ return stripThinkTags(content);
213
+ }
214
+ }
215
+ finally {
216
+ clearTimeout(timeoutId);
217
+ }
218
+ }
219
+ async function handleOpenAIStream(body, onChunk) {
220
+ const reader = body.getReader();
221
+ const decoder = new TextDecoder();
222
+ const chunks = [];
223
+ let buffer = '';
224
+ while (true) {
225
+ const { done, value } = await reader.read();
226
+ if (done)
227
+ break;
228
+ buffer += decoder.decode(value, { stream: true });
229
+ const lines = buffer.split('\n');
230
+ buffer = lines.pop() || '';
231
+ for (const line of lines) {
232
+ if (line.startsWith('data: ')) {
233
+ const data = line.slice(6);
234
+ if (data === '[DONE]')
235
+ continue;
236
+ try {
237
+ const parsed = JSON.parse(data);
238
+ const content = parsed.choices?.[0]?.delta?.content;
239
+ if (content) {
240
+ chunks.push(content);
241
+ onChunk(content);
242
+ }
243
+ }
244
+ catch {
245
+ // Ignore parse errors
246
+ }
247
+ }
248
+ }
249
+ }
250
+ // Strip <think> tags from MiniMax and other providers
251
+ return stripThinkTags(chunks.join(''));
252
+ }
253
+ async function chatAnthropic(message, history, model, apiKey, onChunk, abortSignal) {
254
+ const systemPrompt = getSystemPrompt();
255
+ const messages = [
256
+ { role: 'user', content: systemPrompt },
257
+ { role: 'assistant', content: 'Understood. I will follow your language instructions.' },
258
+ ...history,
259
+ { role: 'user', content: message },
260
+ ];
261
+ const stream = Boolean(onChunk);
262
+ const timeout = config.get('apiTimeout');
263
+ const temperature = config.get('temperature');
264
+ const maxTokens = config.get('maxTokens');
265
+ // Get provider-specific URL and auth
266
+ const providerId = config.get('provider');
267
+ const baseUrl = getProviderBaseUrl(providerId, 'anthropic');
268
+ const authHeader = getProviderAuthHeader(providerId, 'anthropic');
269
+ if (!baseUrl) {
270
+ throw new Error(`Provider ${providerId} does not support Anthropic protocol`);
271
+ }
272
+ // Create abort controller for timeout or use provided one
273
+ const controller = new AbortController();
274
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
275
+ // Listen to external abort signal if provided
276
+ if (abortSignal) {
277
+ abortSignal.addEventListener('abort', () => controller.abort());
278
+ }
279
+ // Build headers based on auth type
280
+ const headers = {
281
+ 'Content-Type': 'application/json',
282
+ 'anthropic-version': '2023-06-01',
283
+ };
284
+ if (authHeader === 'Bearer') {
285
+ headers['Authorization'] = `Bearer ${apiKey}`;
286
+ }
287
+ else {
288
+ headers['x-api-key'] = apiKey;
289
+ }
290
+ try {
291
+ const response = await fetch(`${baseUrl}/v1/messages`, {
292
+ method: 'POST',
293
+ headers,
294
+ body: JSON.stringify({
295
+ model,
296
+ messages,
297
+ max_tokens: maxTokens,
298
+ temperature,
299
+ stream,
300
+ }),
301
+ signal: controller.signal,
302
+ });
303
+ if (!response.ok) {
304
+ const error = await response.text();
305
+ const err = new Error(`${getErrorMessage('apiError')}: ${response.status} - ${error}`);
306
+ err.status = response.status;
307
+ throw err;
308
+ }
309
+ if (stream && response.body) {
310
+ return handleAnthropicStream(response.body, onChunk);
311
+ }
312
+ else {
313
+ const data = await response.json();
314
+ const content = data.content[0]?.text || '';
315
+ return stripThinkTags(content);
316
+ }
317
+ }
318
+ finally {
319
+ clearTimeout(timeoutId);
320
+ }
321
+ }
322
+ /**
323
+ * Remove <think>...</think> tags from response
324
+ * Some providers (MiniMax, DeepSeek) include internal reasoning in these tags
325
+ * which should not be shown to users
326
+ */
327
+ function stripThinkTags(text) {
328
+ return text.replace(/<think>[\s\S]*?<\/think>/gi, '').trim();
329
+ }
330
+ async function handleAnthropicStream(body, onChunk) {
331
+ const reader = body.getReader();
332
+ const decoder = new TextDecoder();
333
+ const chunks = [];
334
+ let buffer = '';
335
+ while (true) {
336
+ const { done, value } = await reader.read();
337
+ if (done)
338
+ break;
339
+ buffer += decoder.decode(value, { stream: true });
340
+ const lines = buffer.split('\n');
341
+ buffer = lines.pop() || '';
342
+ for (const line of lines) {
343
+ if (line.startsWith('data: ')) {
344
+ const data = line.slice(6);
345
+ try {
346
+ const parsed = JSON.parse(data);
347
+ if (parsed.type === 'content_block_delta') {
348
+ const text = parsed.delta?.text;
349
+ if (text) {
350
+ chunks.push(text);
351
+ onChunk(text);
352
+ }
353
+ }
354
+ }
355
+ catch {
356
+ // Ignore parse errors
357
+ }
358
+ }
359
+ }
360
+ }
361
+ // Strip <think> tags from MiniMax responses
362
+ return stripThinkTags(chunks.join(''));
363
+ }
364
+ export async function validateApiKey(apiKey, providerId) {
365
+ const provider = providerId || config.get('provider');
366
+ const providerConfig = getProvider(provider);
367
+ if (!providerConfig) {
368
+ return { valid: false, error: `Unknown provider: ${provider}` };
369
+ }
370
+ // Determine which protocol to use for validation
371
+ const protocol = providerConfig.defaultProtocol;
372
+ const baseUrl = getProviderBaseUrl(provider, protocol);
373
+ const authHeader = getProviderAuthHeader(provider, protocol);
374
+ const model = providerConfig.defaultModel;
375
+ if (!baseUrl) {
376
+ return { valid: false, error: `No endpoint configured for ${provider}` };
377
+ }
378
+ // Build headers
379
+ const headers = {
380
+ 'Content-Type': 'application/json',
381
+ };
382
+ if (authHeader === 'Bearer') {
383
+ headers['Authorization'] = `Bearer ${apiKey}`;
384
+ }
385
+ else {
386
+ headers['x-api-key'] = apiKey;
387
+ }
388
+ if (protocol === 'anthropic') {
389
+ headers['anthropic-version'] = '2023-06-01';
390
+ }
391
+ // Build request based on protocol
392
+ const endpoint = protocol === 'openai' ? '/chat/completions' : '/v1/messages';
393
+ const body = protocol === 'openai'
394
+ ? {
395
+ model,
396
+ messages: [{ role: 'user', content: 'hi' }],
397
+ max_tokens: 5,
398
+ }
399
+ : {
400
+ model,
401
+ messages: [{ role: 'user', content: 'hi' }],
402
+ max_tokens: 5,
403
+ };
404
+ try {
405
+ const response = await fetch(`${baseUrl}${endpoint}`, {
406
+ method: 'POST',
407
+ headers,
408
+ body: JSON.stringify(body),
409
+ });
410
+ if (response.ok) {
411
+ return { valid: true };
412
+ }
413
+ else {
414
+ const errorText = await response.text();
415
+ return { valid: false, error: `${response.status}: ${errorText}` };
416
+ }
417
+ }
418
+ catch (err) {
419
+ return { valid: false, error: err.message };
420
+ }
421
+ }
package/dist/app.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ import React from 'react';
2
+ export declare const App: React.FC;