claude-autopm 1.30.1 → 2.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.
Files changed (36) hide show
  1. package/autopm/.claude/mcp/test-server.md +10 -0
  2. package/autopm/.claude/scripts/github/dependency-tracker.js +554 -0
  3. package/autopm/.claude/scripts/github/dependency-validator.js +545 -0
  4. package/autopm/.claude/scripts/github/dependency-visualizer.js +477 -0
  5. package/autopm/.claude/scripts/pm/lib/epic-discovery.js +119 -0
  6. package/autopm/.claude/scripts/pm/next.js +56 -58
  7. package/bin/autopm-poc.js +348 -0
  8. package/bin/autopm.js +6 -0
  9. package/lib/ai-providers/AbstractAIProvider.js +524 -0
  10. package/lib/ai-providers/ClaudeProvider.js +423 -0
  11. package/lib/ai-providers/TemplateProvider.js +432 -0
  12. package/lib/cli/commands/agent.js +206 -0
  13. package/lib/cli/commands/config.js +488 -0
  14. package/lib/cli/commands/prd.js +345 -0
  15. package/lib/cli/commands/task.js +206 -0
  16. package/lib/config/ConfigManager.js +531 -0
  17. package/lib/errors/AIProviderError.js +164 -0
  18. package/lib/services/AgentService.js +557 -0
  19. package/lib/services/EpicService.js +609 -0
  20. package/lib/services/PRDService.js +1003 -0
  21. package/lib/services/TaskService.js +760 -0
  22. package/lib/services/interfaces.js +753 -0
  23. package/lib/utils/CircuitBreaker.js +165 -0
  24. package/lib/utils/Encryption.js +201 -0
  25. package/lib/utils/RateLimiter.js +241 -0
  26. package/lib/utils/ServiceFactory.js +165 -0
  27. package/package.json +9 -5
  28. package/scripts/config/get.js +108 -0
  29. package/scripts/config/init.js +100 -0
  30. package/scripts/config/list-providers.js +93 -0
  31. package/scripts/config/set-api-key.js +107 -0
  32. package/scripts/config/set-provider.js +201 -0
  33. package/scripts/config/set.js +139 -0
  34. package/scripts/config/show.js +181 -0
  35. package/autopm/.claude/.env +0 -158
  36. package/autopm/.claude/settings.local.json +0 -9
@@ -0,0 +1,423 @@
1
+ /**
2
+ * ClaudeProvider - Anthropic Claude API Integration
3
+ *
4
+ * Extends AbstractAIProvider to provide Claude-specific AI capabilities
5
+ * with full backward compatibility for existing code.
6
+ *
7
+ * Documentation Queries:
8
+ * - mcp://context7/anthropic/sdk - Anthropic SDK patterns
9
+ * - mcp://context7/anthropic/streaming - Streaming best practices
10
+ * - mcp://context7/nodejs/async-generators - Async generator patterns
11
+ *
12
+ * @extends AbstractAIProvider
13
+ *
14
+ * @example
15
+ * // Legacy usage (backward compatible)
16
+ * const provider = new ClaudeProvider('sk-ant-...');
17
+ * const result = await provider.complete('Hello');
18
+ *
19
+ * @example
20
+ * // Enhanced usage with config
21
+ * const provider = new ClaudeProvider({
22
+ * apiKey: 'sk-ant-...',
23
+ * model: 'claude-sonnet-4-20250514',
24
+ * temperature: 0.7,
25
+ * maxTokens: 4096
26
+ * });
27
+ *
28
+ * @example
29
+ * // Using environment variable
30
+ * process.env.ANTHROPIC_API_KEY = 'sk-ant-...';
31
+ * const provider = new ClaudeProvider({});
32
+ */
33
+
34
+ const Anthropic = require('@anthropic-ai/sdk');
35
+ const AbstractAIProvider = require('./AbstractAIProvider');
36
+ const AIProviderError = require('../errors/AIProviderError');
37
+
38
+ /**
39
+ * ClaudeProvider class for Anthropic Claude API integration
40
+ *
41
+ * @class ClaudeProvider
42
+ * @extends AbstractAIProvider
43
+ */
44
+ class ClaudeProvider extends AbstractAIProvider {
45
+ /**
46
+ * Create a new ClaudeProvider instance
47
+ *
48
+ * Supports two constructor signatures for backward compatibility:
49
+ * 1. new ClaudeProvider('api-key') <- Legacy
50
+ * 2. new ClaudeProvider({ apiKey: 'key', ...options }) <- Enhanced
51
+ *
52
+ * @param {string|Object} [config={}] - API key string or configuration object
53
+ * @param {string} [config.apiKey] - Anthropic API key (or use ANTHROPIC_API_KEY env var)
54
+ * @param {string} [config.model] - Model to use (default: claude-sonnet-4-20250514)
55
+ * @param {number} [config.maxTokens] - Maximum tokens (default: 4096)
56
+ * @param {number} [config.temperature] - Temperature (default: 0.7)
57
+ *
58
+ * @throws {Error} If API key is not provided and ANTHROPIC_API_KEY env var is not set
59
+ */
60
+ constructor(config = {}) {
61
+ // Handle backward compatibility: string API key as first parameter
62
+ if (typeof config === 'string') {
63
+ config = { apiKey: config };
64
+ }
65
+
66
+ // Call parent constructor (handles config normalization and defaults)
67
+ super(config);
68
+
69
+ // Validate API key is available after parent constructor
70
+ if (!this.apiKey) {
71
+ throw new Error(
72
+ 'API key is required for ClaudeProvider. ' +
73
+ 'Provide it via constructor or set ANTHROPIC_API_KEY environment variable.'
74
+ );
75
+ }
76
+
77
+ // Initialize Anthropic client with API key
78
+ this.client = new Anthropic({ apiKey: this.apiKey });
79
+ }
80
+
81
+ // ============================================================
82
+ // ABSTRACT METHOD IMPLEMENTATIONS (Required by AbstractAIProvider)
83
+ // ============================================================
84
+
85
+ /**
86
+ * Get the default model identifier for Claude
87
+ *
88
+ * @returns {string} Default Claude model
89
+ */
90
+ getDefaultModel() {
91
+ return 'claude-sonnet-4-20250514';
92
+ }
93
+
94
+ /**
95
+ * Get the environment variable name for API key
96
+ *
97
+ * @returns {string} Environment variable name
98
+ */
99
+ getApiKeyEnvVar() {
100
+ return 'ANTHROPIC_API_KEY';
101
+ }
102
+
103
+ /**
104
+ * Complete a prompt synchronously (wait for full response)
105
+ *
106
+ * Uses parent's _mergeOptions to combine instance config with method options.
107
+ * Method-level options take precedence over instance config.
108
+ *
109
+ * Automatically applies rate limiting if configured in constructor.
110
+ *
111
+ * @param {string} prompt - The prompt to complete
112
+ * @param {Object} [options={}] - Optional configuration
113
+ * @param {string} [options.model] - Model to use (overrides instance model)
114
+ * @param {number} [options.maxTokens] - Maximum tokens (overrides instance maxTokens)
115
+ * @param {number} [options.temperature] - Temperature (overrides instance temperature)
116
+ *
117
+ * @returns {Promise<string>} The completed text
118
+ * @throws {AIProviderError} On API errors
119
+ */
120
+ async complete(prompt, options = {}) {
121
+ return this._withRateLimit(async () => {
122
+ // Merge instance config with method options
123
+ const finalOptions = this._mergeOptions(options);
124
+
125
+ try {
126
+ const response = await this.client.messages.create({
127
+ model: finalOptions.model,
128
+ max_tokens: finalOptions.maxTokens,
129
+ temperature: finalOptions.temperature,
130
+ messages: [{ role: 'user', content: prompt }]
131
+ });
132
+
133
+ // Extract text from response
134
+ if (response.content && response.content.length > 0) {
135
+ return response.content[0].text || '';
136
+ }
137
+
138
+ return '';
139
+ } catch (error) {
140
+ // Format error using parent's formatError + Claude-specific mapping
141
+ throw this.formatError(error);
142
+ }
143
+ });
144
+ }
145
+
146
+ /**
147
+ * Stream a prompt response (async generator for real-time feedback)
148
+ *
149
+ * Uses parent's _mergeOptions to combine instance config with method options.
150
+ * Method-level options take precedence over instance config.
151
+ *
152
+ * Automatically applies rate limiting if configured in constructor.
153
+ *
154
+ * @param {string} prompt - The prompt to complete
155
+ * @param {Object} [options={}] - Optional configuration
156
+ * @param {string} [options.model] - Model to use (overrides instance model)
157
+ * @param {number} [options.maxTokens] - Maximum tokens (overrides instance maxTokens)
158
+ * @param {number} [options.temperature] - Temperature (overrides instance temperature)
159
+ *
160
+ * @yields {string} Text chunks as they arrive
161
+ * @throws {AIProviderError} On API errors
162
+ */
163
+ async *stream(prompt, options = {}) {
164
+ // Apply rate limiting before initiating stream
165
+ if (this.rateLimiter) {
166
+ await this.rateLimiter.removeTokens(1);
167
+ }
168
+
169
+ // Merge instance config with method options
170
+ const finalOptions = this._mergeOptions(options);
171
+
172
+ try {
173
+ const stream = await this.client.messages.create({
174
+ model: finalOptions.model,
175
+ max_tokens: finalOptions.maxTokens,
176
+ temperature: finalOptions.temperature,
177
+ stream: true,
178
+ messages: [{ role: 'user', content: prompt }]
179
+ });
180
+
181
+ // Yield text deltas as they arrive
182
+ for await (const event of stream) {
183
+ if (
184
+ event.type === 'content_block_delta' &&
185
+ event.delta &&
186
+ event.delta.type === 'text_delta'
187
+ ) {
188
+ yield event.delta.text;
189
+ }
190
+ }
191
+ } catch (error) {
192
+ // Format error using parent's formatError + Claude-specific mapping
193
+ throw this.formatError(error);
194
+ }
195
+ }
196
+
197
+ // ============================================================
198
+ // CAPABILITY OVERRIDES
199
+ // ============================================================
200
+
201
+ /**
202
+ * Check if Claude supports streaming
203
+ *
204
+ * @returns {boolean} True (Claude supports streaming)
205
+ */
206
+ supportsStreaming() {
207
+ return true;
208
+ }
209
+
210
+ /**
211
+ * Check if Claude supports function calling
212
+ *
213
+ * @returns {boolean} True (Claude supports tools/function calling)
214
+ */
215
+ supportsFunctionCalling() {
216
+ return true;
217
+ }
218
+
219
+ /**
220
+ * Check if Claude supports chat format
221
+ *
222
+ * @returns {boolean} True (Claude has native chat format)
223
+ */
224
+ supportsChat() {
225
+ return true;
226
+ }
227
+
228
+ /**
229
+ * Check if Claude supports vision/image inputs
230
+ *
231
+ * @returns {boolean} False (not implemented yet)
232
+ */
233
+ supportsVision() {
234
+ return false;
235
+ }
236
+
237
+ // ============================================================
238
+ // ENHANCED ERROR HANDLING
239
+ // ============================================================
240
+
241
+ /**
242
+ * Format Anthropic API errors into AIProviderError
243
+ *
244
+ * Maps Anthropic-specific error codes and HTTP statuses to AIProviderError codes.
245
+ * Falls back to parent's formatError for unknown errors.
246
+ *
247
+ * @param {Error} error - The error to format
248
+ * @returns {AIProviderError} Formatted error with appropriate code and metadata
249
+ */
250
+ formatError(error) {
251
+ // Already an AIProviderError, return as-is
252
+ if (error instanceof AIProviderError) {
253
+ return error;
254
+ }
255
+
256
+ // Map Anthropic HTTP status codes to AIProviderError codes
257
+ if (error.status) {
258
+ switch (error.status) {
259
+ case 401:
260
+ return new AIProviderError(
261
+ AIProviderError.INVALID_API_KEY,
262
+ 'Invalid Anthropic API key or authentication failed',
263
+ true,
264
+ 401
265
+ );
266
+
267
+ case 429:
268
+ return new AIProviderError(
269
+ AIProviderError.RATE_LIMIT,
270
+ 'Claude API rate limit exceeded. Please retry after a delay.',
271
+ true,
272
+ 429
273
+ );
274
+
275
+ case 500:
276
+ case 502:
277
+ case 503:
278
+ case 504:
279
+ return new AIProviderError(
280
+ AIProviderError.SERVICE_UNAVAILABLE,
281
+ `Claude API service temporarily unavailable (${error.status})`,
282
+ true,
283
+ error.status
284
+ );
285
+
286
+ case 400:
287
+ return new AIProviderError(
288
+ AIProviderError.INVALID_REQUEST,
289
+ error.message || 'Invalid request parameters',
290
+ true,
291
+ 400
292
+ );
293
+
294
+ default:
295
+ // Unknown status code, wrap in generic error
296
+ return new AIProviderError(
297
+ 'UNKNOWN_ERROR',
298
+ `Claude API error (${error.status}): ${error.message}`,
299
+ true,
300
+ error.status
301
+ );
302
+ }
303
+ }
304
+
305
+ // No status code, check for specific error types
306
+ if (error.message) {
307
+ const lowerMessage = error.message.toLowerCase();
308
+
309
+ if (lowerMessage.includes('api key') || lowerMessage.includes('authentication')) {
310
+ return new AIProviderError(
311
+ AIProviderError.INVALID_API_KEY,
312
+ error.message,
313
+ true
314
+ );
315
+ }
316
+
317
+ if (lowerMessage.includes('rate limit')) {
318
+ return new AIProviderError(
319
+ AIProviderError.RATE_LIMIT,
320
+ error.message,
321
+ true
322
+ );
323
+ }
324
+
325
+ if (lowerMessage.includes('network') || lowerMessage.includes('connection')) {
326
+ return new AIProviderError(
327
+ AIProviderError.NETWORK_ERROR,
328
+ error.message,
329
+ true
330
+ );
331
+ }
332
+
333
+ if (lowerMessage.includes('context') || lowerMessage.includes('too long')) {
334
+ return new AIProviderError(
335
+ AIProviderError.CONTEXT_LENGTH_EXCEEDED,
336
+ error.message,
337
+ true
338
+ );
339
+ }
340
+ }
341
+
342
+ // Fall back to parent's error handling
343
+ return super.formatError(error);
344
+ }
345
+
346
+ // ============================================================
347
+ // ENHANCED CHAT SUPPORT
348
+ // ============================================================
349
+
350
+ /**
351
+ * Chat completion with message history
352
+ *
353
+ * Overrides parent's fallback implementation to use Claude's native chat format.
354
+ * Converts system role to user role (Claude requirement).
355
+ *
356
+ * Automatically applies rate limiting if configured in constructor.
357
+ *
358
+ * @param {Array<{role: string, content: string}>} messages - Chat messages
359
+ * @param {Object} [options={}] - Optional configuration
360
+ *
361
+ * @returns {Promise<string>} Completion response
362
+ * @throws {AIProviderError} On API errors
363
+ */
364
+ async chat(messages, options = {}) {
365
+ return this._withRateLimit(async () => {
366
+ // Merge instance config with method options
367
+ const finalOptions = this._mergeOptions(options);
368
+
369
+ try {
370
+ // Convert system role to user (Claude doesn't support system role in messages array)
371
+ const claudeMessages = messages.map(msg => ({
372
+ role: msg.role === 'system' ? 'user' : msg.role,
373
+ content: msg.content
374
+ }));
375
+
376
+ const response = await this.client.messages.create({
377
+ model: finalOptions.model,
378
+ max_tokens: finalOptions.maxTokens,
379
+ temperature: finalOptions.temperature,
380
+ messages: claudeMessages
381
+ });
382
+
383
+ // Extract text from response
384
+ if (response.content && response.content.length > 0) {
385
+ return response.content[0].text || '';
386
+ }
387
+
388
+ return '';
389
+ } catch (error) {
390
+ throw this.formatError(error);
391
+ }
392
+ });
393
+ }
394
+
395
+ // ============================================================
396
+ // BACKWARD COMPATIBILITY METHODS
397
+ // ============================================================
398
+
399
+ /**
400
+ * Get the current model being used (legacy method for backward compatibility)
401
+ *
402
+ * Note: This returns the default model, not the instance model.
403
+ * For instance model, use provider.model property.
404
+ *
405
+ * @returns {string} The default model name
406
+ * @deprecated Use getDefaultModel() or access provider.model instead
407
+ */
408
+ getModel() {
409
+ return this.getDefaultModel();
410
+ }
411
+
412
+ /**
413
+ * Test the API connection (inherited from AbstractAIProvider)
414
+ *
415
+ * This method is provided by the parent class and works by making a test
416
+ * complete() call. Included here for documentation completeness.
417
+ *
418
+ * @returns {Promise<boolean>} True if connection is successful
419
+ */
420
+ // testConnection() is inherited from AbstractAIProvider
421
+ }
422
+
423
+ module.exports = ClaudeProvider;