claude-autopm 1.31.0 → 2.1.1

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.
@@ -0,0 +1,165 @@
1
+ /**
2
+ * @fileoverview ServiceFactory - Creates services with ConfigManager integration
3
+ *
4
+ * Provides factory methods for creating AutoPM services with:
5
+ * - Automatic provider creation from ConfigManager
6
+ * - Centralized configuration management
7
+ * - Simplified service instantiation
8
+ *
9
+ * @example
10
+ * const factory = new ServiceFactory(configManager);
11
+ * const prdService = factory.createPRDService();
12
+ * const epicService = factory.createEpicService();
13
+ */
14
+
15
+ const ConfigManager = require('../config/ConfigManager');
16
+
17
+ /**
18
+ * ServiceFactory - Creates services with ConfigManager integration
19
+ */
20
+ class ServiceFactory {
21
+ /**
22
+ * Create a ServiceFactory instance
23
+ *
24
+ * @param {ConfigManager} configManager - ConfigManager instance
25
+ * @throws {Error} If configManager is not provided or invalid
26
+ */
27
+ constructor(configManager) {
28
+ if (!configManager) {
29
+ throw new Error('ConfigManager instance is required');
30
+ }
31
+
32
+ if (!(configManager instanceof ConfigManager)) {
33
+ throw new Error('configManager must be an instance of ConfigManager');
34
+ }
35
+
36
+ this.configManager = configManager;
37
+ }
38
+
39
+ /**
40
+ * Create AI provider from ConfigManager
41
+ *
42
+ * @param {string} [providerName] - Provider name (defaults to defaultProvider)
43
+ * @returns {Object} Provider instance
44
+ * @throws {Error} If provider creation fails
45
+ *
46
+ * @example
47
+ * const provider = factory.createProvider(); // Uses default provider
48
+ * const provider = factory.createProvider('openai'); // Specific provider
49
+ */
50
+ createProvider(providerName = null) {
51
+ // Check master password
52
+ if (!this.configManager.hasMasterPassword()) {
53
+ throw new Error('Master password not set. Call configManager.setMasterPassword() first.');
54
+ }
55
+
56
+ // Get provider name (default if not specified)
57
+ const name = providerName || this.configManager.getDefaultProvider();
58
+
59
+ // Get provider config first to validate provider exists
60
+ const config = this.configManager.getProvider(name);
61
+ if (!config) {
62
+ throw new Error(`Provider configuration not found: ${name}`);
63
+ }
64
+
65
+ // Validate configuration before attempting to get API key
66
+ try {
67
+ this.configManager.validateProvider(config);
68
+ } catch (error) {
69
+ throw new Error(`Invalid configuration for provider '${name}': ${error.message}`);
70
+ }
71
+
72
+ // Get API key
73
+ const apiKey = this.configManager.getApiKey(name);
74
+ if (!apiKey) {
75
+ throw new Error(`API key not found for provider: ${name}`);
76
+ }
77
+
78
+ // Create provider based on name
79
+ if (name === 'claude') {
80
+ const ClaudeProvider = require('../ai-providers/ClaudeProvider');
81
+ return new ClaudeProvider({ apiKey, ...config });
82
+ }
83
+
84
+ throw new Error(`Unknown provider: ${name}`);
85
+ }
86
+
87
+ /**
88
+ * Create PRDService instance
89
+ *
90
+ * @param {Object} [options] - Service options
91
+ * @param {Object} [options.provider] - Custom provider (overrides ConfigManager)
92
+ * @param {number} [options.defaultEffortHours] - Default effort hours
93
+ * @param {number} [options.hoursPerDay] - Hours per day
94
+ * @param {number} [options.hoursPerWeek] - Hours per week
95
+ * @returns {PRDService} PRDService instance
96
+ *
97
+ * @example
98
+ * const service = factory.createPRDService();
99
+ * const service = factory.createPRDService({ defaultEffortHours: 10 });
100
+ */
101
+ createPRDService(options = {}) {
102
+ const PRDService = require('../services/PRDService');
103
+
104
+ // Create provider if not provided
105
+ const provider = options.provider || this.createProvider();
106
+
107
+ return new PRDService({
108
+ provider,
109
+ configManager: this.configManager,
110
+ ...options
111
+ });
112
+ }
113
+
114
+ /**
115
+ * Create EpicService instance
116
+ *
117
+ * @param {Object} [options] - Service options
118
+ * @param {PRDService} [options.prdService] - Custom PRDService (overrides auto-creation)
119
+ * @returns {EpicService} EpicService instance
120
+ *
121
+ * @example
122
+ * const service = factory.createEpicService();
123
+ * const service = factory.createEpicService({ prdService: customPrdService });
124
+ */
125
+ createEpicService(options = {}) {
126
+ const EpicService = require('../services/EpicService');
127
+
128
+ // Create PRDService if not provided
129
+ const prdService = options.prdService || this.createPRDService();
130
+
131
+ return new EpicService({
132
+ prdService,
133
+ configManager: this.configManager,
134
+ ...options
135
+ });
136
+ }
137
+
138
+ /**
139
+ * Create TaskService instance
140
+ *
141
+ * @param {Object} [options] - Service options
142
+ * @param {PRDService} [options.prdService] - Custom PRDService (overrides auto-creation)
143
+ * @param {string} [options.defaultTaskType] - Default task type
144
+ * @param {string} [options.defaultEffort] - Default effort
145
+ * @returns {TaskService} TaskService instance
146
+ *
147
+ * @example
148
+ * const service = factory.createTaskService();
149
+ * const service = factory.createTaskService({ defaultTaskType: 'testing' });
150
+ */
151
+ createTaskService(options = {}) {
152
+ const TaskService = require('../services/TaskService');
153
+
154
+ // Create PRDService if not provided
155
+ const prdService = options.prdService || this.createPRDService();
156
+
157
+ return new TaskService({
158
+ prdService,
159
+ configManager: this.configManager,
160
+ ...options
161
+ });
162
+ }
163
+ }
164
+
165
+ module.exports = ServiceFactory;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-autopm",
3
- "version": "1.31.0",
3
+ "version": "2.1.1",
4
4
  "description": "Autonomous Project Management Framework for Claude Code - Advanced AI-powered development automation",
5
5
  "main": "bin/autopm.js",
6
6
  "bin": {
@@ -116,11 +116,11 @@
116
116
  "README.md"
117
117
  ],
118
118
  "dependencies": {
119
- "@anthropic-ai/sdk": "^0.32.1",
119
+ "@anthropic-ai/sdk": "^0.65.0",
120
120
  "@octokit/rest": "^22.0.0",
121
121
  "azure-devops-node-api": "^15.1.1",
122
- "chalk": "^5.3.0",
123
- "dotenv": "^16.6.1",
122
+ "chalk": "4.1.2",
123
+ "dotenv": "^17.2.3",
124
124
  "execa": "^9.6.0",
125
125
  "fast-glob": "^3.3.2",
126
126
  "fs-extra": "^11.3.2",
@@ -128,6 +128,7 @@
128
128
  "inquirer": "^12.9.6",
129
129
  "js-yaml": "^4.1.0",
130
130
  "markdown-it": "^14.1.0",
131
+ "marked": "^16.4.0",
131
132
  "moment": "^2.29.4",
132
133
  "ora": "^5.4.1",
133
134
  "simple-git": "^3.20.0",
@@ -155,7 +156,7 @@
155
156
  "sinon": "^21.0.0"
156
157
  },
157
158
  "optionalDependencies": {
158
- "@playwright/mcp": "^0.0.41",
159
+ "@playwright/mcp": "^0.0.42",
159
160
  "@upstash/context7-mcp": "^1.0.0"
160
161
  },
161
162
  "publishConfig": {
@@ -0,0 +1,108 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * @fileoverview Config command: get
4
+ * Get configuration value with dot notation support
5
+ *
6
+ * Usage: autopm config:get <key>
7
+ * Example: autopm config:get providers.claude.model
8
+ */
9
+
10
+ const path = require('path');
11
+ const os = require('os');
12
+ const ConfigManager = require('../../lib/config/ConfigManager');
13
+
14
+ // Default config path
15
+ const DEFAULT_CONFIG_PATH = path.join(os.homedir(), '.autopm', 'config.json');
16
+
17
+ /**
18
+ * Mask sensitive API key data
19
+ * @param {*} value - Value to potentially mask
20
+ * @returns {*} Masked value or original value
21
+ */
22
+ function maskSensitiveData(value) {
23
+ if (value && typeof value === 'object') {
24
+ if ('iv' in value && 'encryptedData' in value) {
25
+ // This is an encrypted API key
26
+ return '***encrypted***';
27
+ }
28
+
29
+ // Recursively mask object properties
30
+ const masked = {};
31
+ for (const [key, val] of Object.entries(value)) {
32
+ masked[key] = maskSensitiveData(val);
33
+ }
34
+ return masked;
35
+ }
36
+
37
+ return value;
38
+ }
39
+
40
+ /**
41
+ * Format output value
42
+ * @param {*} value - Value to format
43
+ * @returns {string} Formatted output
44
+ */
45
+ function formatOutput(value) {
46
+ if (value === null || value === undefined) {
47
+ return 'null';
48
+ }
49
+
50
+ if (typeof value === 'object') {
51
+ // Mask sensitive data before printing
52
+ const masked = maskSensitiveData(value);
53
+ return JSON.stringify(masked, null, 2);
54
+ }
55
+
56
+ return String(value);
57
+ }
58
+
59
+ /**
60
+ * Main function
61
+ */
62
+ async function main() {
63
+ try {
64
+ const configPath = process.env.AUTOPM_CONFIG_PATH || DEFAULT_CONFIG_PATH;
65
+
66
+ // Get key from command line arguments
67
+ const key = process.argv[2];
68
+
69
+ if (!key) {
70
+ console.error('Error: Key is required');
71
+ console.error('Usage: autopm config:get <key>');
72
+ console.error('Example: autopm config:get providers.claude.model');
73
+ process.exit(1);
74
+ }
75
+
76
+ // Check if config exists
77
+ const fs = require('fs');
78
+ if (!fs.existsSync(configPath)) {
79
+ console.error('Error: Configuration not found');
80
+ console.error('Run: autopm config:init');
81
+ process.exit(1);
82
+ }
83
+
84
+ // Load configuration
85
+ const manager = new ConfigManager(configPath);
86
+
87
+ // Get value
88
+ const value = manager.getConfig(key);
89
+
90
+ // Output formatted value
91
+ console.log(formatOutput(value));
92
+
93
+ } catch (error) {
94
+ if (error.message.includes('Unexpected token') || error.message.includes('JSON')) {
95
+ console.error('Error reading config: Invalid JSON format');
96
+ } else {
97
+ console.error('Error:', error.message);
98
+ }
99
+ process.exit(1);
100
+ }
101
+ }
102
+
103
+ // Run if called directly
104
+ if (require.main === module) {
105
+ main();
106
+ }
107
+
108
+ module.exports = main;
@@ -0,0 +1,100 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * @fileoverview Config command: init
4
+ * Initialize configuration with interactive prompts for master password
5
+ *
6
+ * Usage: autopm config:init
7
+ */
8
+
9
+ const path = require('path');
10
+ const os = require('os');
11
+ const { password } = require('@inquirer/prompts');
12
+ const ConfigManager = require('../../lib/config/ConfigManager');
13
+
14
+ // Default config path
15
+ const DEFAULT_CONFIG_PATH = path.join(os.homedir(), '.autopm', 'config.json');
16
+
17
+ /**
18
+ * Validate password meets minimum requirements
19
+ * @param {string} pwd - Password to validate
20
+ * @returns {boolean|string} True if valid, error message otherwise
21
+ */
22
+ function validatePassword(pwd) {
23
+ if (!pwd || pwd.length < 8) {
24
+ return 'Password must be at least 8 characters long';
25
+ }
26
+ return true;
27
+ }
28
+
29
+ /**
30
+ * Main initialization function
31
+ */
32
+ async function main() {
33
+ try {
34
+ const configPath = process.env.AUTOPM_CONFIG_PATH || DEFAULT_CONFIG_PATH;
35
+
36
+ console.log('\n🔧 AutoPM Configuration Setup');
37
+ console.log('=============================\n');
38
+
39
+ // Check if config already exists
40
+ const fs = require('fs');
41
+ if (fs.existsSync(configPath)) {
42
+ console.log('⚠️ Configuration already exists at:', configPath);
43
+ console.log('To reconfigure, delete the existing file first.\n');
44
+ return;
45
+ }
46
+
47
+ // Prompt for master password
48
+ const masterPassword = await password({
49
+ message: 'Enter master password (for API key encryption):',
50
+ mask: '*'
51
+ });
52
+
53
+ // Validate password
54
+ const validationResult = validatePassword(masterPassword);
55
+ if (validationResult !== true) {
56
+ console.error(`\nError: ${validationResult}`);
57
+ process.exit(1);
58
+ }
59
+
60
+ // Confirm password
61
+ const confirmPassword = await password({
62
+ message: 'Confirm master password:',
63
+ mask: '*'
64
+ });
65
+
66
+ // Check passwords match
67
+ if (masterPassword !== confirmPassword) {
68
+ console.error('\nError: Passwords do not match');
69
+ process.exit(1);
70
+ }
71
+
72
+ // Create ConfigManager instance
73
+ const manager = new ConfigManager(configPath);
74
+ manager.setMasterPassword(masterPassword);
75
+
76
+ // Save configuration
77
+ manager.save();
78
+
79
+ console.log('\n✓ Configuration initialized at:', configPath);
80
+ console.log('✓ Master password set (not stored, remember it!)');
81
+ console.log('\nNext steps:');
82
+ console.log(' - Set API key: autopm config:set-api-key');
83
+ console.log(' - Configure provider: autopm config:set-provider\n');
84
+
85
+ } catch (error) {
86
+ if (error.message.includes('cancelled') || error.message.includes('User force closed')) {
87
+ console.error('\nError: Setup cancelled by user');
88
+ } else {
89
+ console.error('\nError:', error.message);
90
+ }
91
+ process.exit(1);
92
+ }
93
+ }
94
+
95
+ // Run if called directly
96
+ if (require.main === module) {
97
+ main();
98
+ }
99
+
100
+ module.exports = main;
@@ -0,0 +1,93 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * @fileoverview Config command: list-providers
4
+ * List all configured providers with API key status
5
+ *
6
+ * Usage: autopm config:list-providers
7
+ */
8
+
9
+ const path = require('path');
10
+ const os = require('os');
11
+ const ConfigManager = require('../../lib/config/ConfigManager');
12
+
13
+ // Default config path
14
+ const DEFAULT_CONFIG_PATH = path.join(os.homedir(), '.autopm', 'config.json');
15
+
16
+ /**
17
+ * Main function
18
+ */
19
+ async function main() {
20
+ try {
21
+ const configPath = process.env.AUTOPM_CONFIG_PATH || DEFAULT_CONFIG_PATH;
22
+
23
+ // Check if config exists
24
+ const fs = require('fs');
25
+ if (!fs.existsSync(configPath)) {
26
+ console.error('\nError: Configuration not found');
27
+ console.error('Run: autopm config:init\n');
28
+ process.exit(1);
29
+ }
30
+
31
+ // Load configuration
32
+ const manager = new ConfigManager(configPath);
33
+
34
+ // Get providers
35
+ const providers = manager.listProviders();
36
+ const defaultProvider = manager.getDefaultProvider();
37
+
38
+ if (providers.length === 0) {
39
+ console.log('\nNo providers configured');
40
+ console.log('Run: autopm config:set-provider <name>\n');
41
+ return;
42
+ }
43
+
44
+ // Display header
45
+ console.log('\nConfigured Providers:');
46
+ console.log('=====================\n');
47
+
48
+ // Sort providers alphabetically
49
+ const sortedProviders = providers.sort();
50
+
51
+ // Count providers with API keys
52
+ let apiKeyCount = 0;
53
+
54
+ // Display each provider
55
+ for (const providerName of sortedProviders) {
56
+ const provider = manager.getProvider(providerName);
57
+ const hasApiKey = manager.hasApiKey(providerName);
58
+ const isDefault = providerName === defaultProvider;
59
+
60
+ if (hasApiKey) {
61
+ apiKeyCount++;
62
+ }
63
+
64
+ // Format line
65
+ const defaultMarker = isDefault ? '* ' : ' ';
66
+ const apiKeyStatus = hasApiKey ? '✓ API key set' : '✗ No API key';
67
+ const modelInfo = provider.model || 'not configured';
68
+
69
+ console.log(`${defaultMarker}${providerName.padEnd(18)} ${apiKeyStatus.padEnd(15)} ${modelInfo}`);
70
+ }
71
+
72
+ console.log('\n(* = default provider)\n');
73
+
74
+ // Summary
75
+ console.log(`Total: ${providers.length} provider${providers.length !== 1 ? 's' : ''} configured`);
76
+ console.log(`API keys: ${apiKeyCount} with API keys configured\n`);
77
+
78
+ } catch (error) {
79
+ if (error.message.includes('Unexpected token') || error.message.includes('JSON')) {
80
+ console.error('\nError reading config: Invalid JSON format\n');
81
+ } else {
82
+ console.error('\nError:', error.message, '\n');
83
+ }
84
+ process.exit(1);
85
+ }
86
+ }
87
+
88
+ // Run if called directly
89
+ if (require.main === module) {
90
+ main();
91
+ }
92
+
93
+ module.exports = main;
@@ -0,0 +1,107 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * @fileoverview Config command: set-api-key
4
+ * Set encrypted API key for a provider
5
+ *
6
+ * Usage: autopm config:set-api-key
7
+ */
8
+
9
+ const path = require('path');
10
+ const os = require('os');
11
+ const { select, password } = require('@inquirer/prompts');
12
+ const ConfigManager = require('../../lib/config/ConfigManager');
13
+
14
+ // Default config path
15
+ const DEFAULT_CONFIG_PATH = path.join(os.homedir(), '.autopm', 'config.json');
16
+
17
+ /**
18
+ * Main function
19
+ */
20
+ async function main() {
21
+ try {
22
+ const configPath = process.env.AUTOPM_CONFIG_PATH || DEFAULT_CONFIG_PATH;
23
+
24
+ // Check if config exists
25
+ const fs = require('fs');
26
+ if (!fs.existsSync(configPath)) {
27
+ console.error('\nError: Configuration not initialized');
28
+ console.error('Run: autopm config:init\n');
29
+ process.exit(1);
30
+ }
31
+
32
+ // Load configuration
33
+ const manager = new ConfigManager(configPath);
34
+
35
+ // Get list of providers
36
+ const providers = manager.listProviders();
37
+
38
+ if (providers.length === 0) {
39
+ console.error('\nError: No providers configured');
40
+ console.error('Run: autopm config:set-provider <name>\n');
41
+ process.exit(1);
42
+ }
43
+
44
+ // Build choices with current API key status
45
+ const choices = providers.map(provider => {
46
+ const hasKey = manager.hasApiKey(provider);
47
+ const status = hasKey ? '✓ API key set' : '✗ No API key';
48
+ return {
49
+ name: `${provider.padEnd(20)} ${status}`,
50
+ value: provider
51
+ };
52
+ });
53
+
54
+ // Prompt for provider selection
55
+ const selectedProvider = await select({
56
+ message: 'Select provider:',
57
+ choices
58
+ });
59
+
60
+ // Get master password (from env or prompt)
61
+ let masterPassword = process.env.AUTOPM_MASTER_PASSWORD;
62
+
63
+ if (!masterPassword) {
64
+ masterPassword = await password({
65
+ message: 'Enter master password:',
66
+ mask: '*'
67
+ });
68
+ }
69
+
70
+ manager.setMasterPassword(masterPassword);
71
+
72
+ // Prompt for API key
73
+ const apiKey = await password({
74
+ message: `Enter API key for ${selectedProvider}:`,
75
+ mask: '*'
76
+ });
77
+
78
+ // Validate API key not empty
79
+ if (!apiKey || apiKey.trim().length === 0) {
80
+ console.error('\nError: API key cannot be empty');
81
+ process.exit(1);
82
+ }
83
+
84
+ // Set and save API key
85
+ manager.setApiKey(selectedProvider, apiKey);
86
+ manager.save();
87
+
88
+ console.log(`\n✓ API key encrypted and stored for '${selectedProvider}'\n`);
89
+
90
+ } catch (error) {
91
+ if (error.message.includes('cancelled') || error.message.includes('User force closed')) {
92
+ console.error('\nError: Operation cancelled by user');
93
+ } else if (error.message.includes('incorrect')) {
94
+ console.error('\nError: Incorrect master password');
95
+ } else {
96
+ console.error('\nError:', error.message);
97
+ }
98
+ process.exit(1);
99
+ }
100
+ }
101
+
102
+ // Run if called directly
103
+ if (require.main === module) {
104
+ main();
105
+ }
106
+
107
+ module.exports = main;