claude-autopm 1.30.1 → 1.31.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/autopm/.claude/scripts/github/dependency-tracker.js +554 -0
- package/autopm/.claude/scripts/github/dependency-validator.js +545 -0
- package/autopm/.claude/scripts/github/dependency-visualizer.js +477 -0
- package/autopm/.claude/scripts/pm/lib/epic-discovery.js +119 -0
- package/autopm/.claude/scripts/pm/next.js +56 -58
- package/bin/autopm-poc.js +216 -0
- package/lib/ai-providers/ClaudeProvider.js +112 -0
- package/lib/services/PRDService.js +178 -0
- package/package.json +5 -2
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const { logError } = require('./lib/logger');
|
|
4
|
+
const { findAllEpicDirs } = require('./lib/epic-discovery');
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* PM Next Script (Node.js version)
|
|
@@ -106,69 +107,66 @@ function displayTddReminder(addMessage) {
|
|
|
106
107
|
async function findAvailableTasks() {
|
|
107
108
|
const availableTasks = [];
|
|
108
109
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
}
|
|
110
|
+
// Use shared epic discovery utility
|
|
111
|
+
const epicDirs = findAllEpicDirs();
|
|
112
112
|
|
|
113
|
-
|
|
114
|
-
const
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
const
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
const
|
|
143
|
-
const
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
}
|
|
162
|
-
} catch (err) {
|
|
163
|
-
// Skip files we can't read
|
|
113
|
+
for (const epicDir of epicDirs) {
|
|
114
|
+
const { name: epicName, path: epicPath } = epicDir;
|
|
115
|
+
|
|
116
|
+
try {
|
|
117
|
+
const taskFiles = fs.readdirSync(epicPath)
|
|
118
|
+
.filter(file => /^\d+.*\.md$/.test(file))
|
|
119
|
+
.sort();
|
|
120
|
+
|
|
121
|
+
for (const taskFile of taskFiles) {
|
|
122
|
+
const taskPath = path.join(epicPath, taskFile);
|
|
123
|
+
|
|
124
|
+
try {
|
|
125
|
+
const content = fs.readFileSync(taskPath, 'utf8');
|
|
126
|
+
|
|
127
|
+
// Check if task is open (case-insensitive)
|
|
128
|
+
const statusMatch = content.match(/^status:\s*(.+)$/m);
|
|
129
|
+
const status = statusMatch ? statusMatch[1].trim().toLowerCase() : '';
|
|
130
|
+
|
|
131
|
+
// Skip non-open tasks (only open tasks or tasks without status are available)
|
|
132
|
+
if (status !== 'open' && status !== '') {
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Check dependencies
|
|
137
|
+
const depsMatch = content.match(/^depends_on:\s*\[(.*?)\]/m);
|
|
138
|
+
const depsStr = depsMatch ? depsMatch[1].trim() : '';
|
|
139
|
+
|
|
140
|
+
// If no dependencies or empty dependencies, task is available
|
|
141
|
+
if (!depsStr || depsStr === '') {
|
|
142
|
+
const nameMatch = content.match(/^name:\s*(.+)$/m);
|
|
143
|
+
const name = nameMatch ? nameMatch[1].trim() : 'Unnamed Task';
|
|
144
|
+
|
|
145
|
+
const parallelMatch = content.match(/^parallel:\s*(.+)$/m);
|
|
146
|
+
const parallel = parallelMatch ? parallelMatch[1].trim() === 'true' : false;
|
|
147
|
+
|
|
148
|
+
const taskNum = path.basename(taskFile, '.md');
|
|
149
|
+
|
|
150
|
+
availableTasks.push({
|
|
151
|
+
taskNum,
|
|
152
|
+
name,
|
|
153
|
+
epicName,
|
|
154
|
+
parallel
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
} catch (err) {
|
|
158
|
+
// Log file read errors in DEBUG mode
|
|
159
|
+
if (process.env.DEBUG) {
|
|
160
|
+
console.error(`Error reading task file ${taskPath}:`, err.message);
|
|
164
161
|
}
|
|
165
162
|
}
|
|
166
|
-
}
|
|
167
|
-
|
|
163
|
+
}
|
|
164
|
+
} catch (err) {
|
|
165
|
+
// Log directory read errors in DEBUG mode
|
|
166
|
+
if (process.env.DEBUG) {
|
|
167
|
+
console.error(`Error reading epic directory ${epicPath}:`, err.message);
|
|
168
168
|
}
|
|
169
169
|
}
|
|
170
|
-
} catch (err) {
|
|
171
|
-
// Silently handle errors
|
|
172
170
|
}
|
|
173
171
|
|
|
174
172
|
return availableTasks;
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* AutoPM POC - CLI for testing Claude API integration
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* autopm-poc parse <prd-file> - Parse PRD with streaming output
|
|
7
|
+
* autopm-poc parse <prd-file> --json - Parse PRD and output JSON
|
|
8
|
+
* autopm-poc summarize <prd-file> - Get one-paragraph summary
|
|
9
|
+
* autopm-poc test - Test API connection
|
|
10
|
+
*
|
|
11
|
+
* Environment:
|
|
12
|
+
* ANTHROPIC_API_KEY - Required for all operations
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const fs = require('fs');
|
|
16
|
+
const path = require('path');
|
|
17
|
+
const ClaudeProvider = require('../lib/ai-providers/ClaudeProvider');
|
|
18
|
+
const PRDService = require('../lib/services/PRDService');
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Print usage information
|
|
22
|
+
*/
|
|
23
|
+
function printUsage() {
|
|
24
|
+
console.log(`
|
|
25
|
+
AutoPM POC - Claude API Integration Demo
|
|
26
|
+
=========================================
|
|
27
|
+
|
|
28
|
+
Usage:
|
|
29
|
+
autopm-poc parse <prd-file> Parse PRD with streaming output
|
|
30
|
+
autopm-poc parse <prd-file> --json Parse PRD and extract epics as JSON
|
|
31
|
+
autopm-poc summarize <prd-file> Get one-paragraph summary
|
|
32
|
+
autopm-poc test Test API connection
|
|
33
|
+
autopm-poc help Show this help message
|
|
34
|
+
|
|
35
|
+
Environment Variables:
|
|
36
|
+
ANTHROPIC_API_KEY Required - Your Anthropic API key
|
|
37
|
+
|
|
38
|
+
Examples:
|
|
39
|
+
export ANTHROPIC_API_KEY="sk-ant-..."
|
|
40
|
+
autopm-poc parse examples/sample-prd.md
|
|
41
|
+
autopm-poc summarize examples/sample-prd.md
|
|
42
|
+
autopm-poc parse examples/sample-prd.md --json
|
|
43
|
+
`);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Test API connection
|
|
48
|
+
*/
|
|
49
|
+
async function testConnection(provider) {
|
|
50
|
+
console.log('🔍 Testing API connection...\n');
|
|
51
|
+
|
|
52
|
+
try {
|
|
53
|
+
const result = await provider.complete('Say "Connection successful!"', {
|
|
54
|
+
maxTokens: 50
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
console.log('✅ API Connection Test: SUCCESS');
|
|
58
|
+
console.log(`📝 Response: ${result}\n`);
|
|
59
|
+
return true;
|
|
60
|
+
} catch (error) {
|
|
61
|
+
console.error('❌ API Connection Test: FAILED');
|
|
62
|
+
console.error(` Error: ${error.message}\n`);
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Parse PRD with streaming output
|
|
69
|
+
*/
|
|
70
|
+
async function parsePRDStream(service, content) {
|
|
71
|
+
console.log('🔍 Analyzing PRD with Claude AI...\n');
|
|
72
|
+
console.log('📝 Streaming response:\n');
|
|
73
|
+
console.log('─'.repeat(60));
|
|
74
|
+
|
|
75
|
+
let fullResponse = '';
|
|
76
|
+
try {
|
|
77
|
+
for await (const chunk of service.parseStream(content)) {
|
|
78
|
+
process.stdout.write(chunk);
|
|
79
|
+
fullResponse += chunk;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
console.log('\n' + '─'.repeat(60));
|
|
83
|
+
console.log('\n✅ Analysis complete!');
|
|
84
|
+
console.log(`📊 Total response length: ${fullResponse.length} characters\n`);
|
|
85
|
+
} catch (error) {
|
|
86
|
+
console.error('\n❌ Error during parsing:', error.message);
|
|
87
|
+
process.exit(1);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Parse PRD and extract epics as JSON
|
|
93
|
+
*/
|
|
94
|
+
async function parsePRDJson(service, content) {
|
|
95
|
+
console.log('🔍 Extracting epics from PRD...\n');
|
|
96
|
+
|
|
97
|
+
try {
|
|
98
|
+
const epics = await service.extractEpics(content);
|
|
99
|
+
|
|
100
|
+
console.log('✅ Extraction complete!\n');
|
|
101
|
+
console.log('📋 Extracted Epics:\n');
|
|
102
|
+
console.log(JSON.stringify(epics, null, 2));
|
|
103
|
+
console.log(`\n📊 Found ${Array.isArray(epics) ? epics.length : 0} epic(s)\n`);
|
|
104
|
+
} catch (error) {
|
|
105
|
+
console.error('❌ Error during JSON extraction:', error.message);
|
|
106
|
+
process.exit(1);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Summarize PRD
|
|
112
|
+
*/
|
|
113
|
+
async function summarizePRD(service, content) {
|
|
114
|
+
console.log('🔍 Summarizing PRD...\n');
|
|
115
|
+
|
|
116
|
+
try {
|
|
117
|
+
const summary = await service.summarize(content);
|
|
118
|
+
|
|
119
|
+
console.log('✅ Summary complete!\n');
|
|
120
|
+
console.log('📝 Summary:\n');
|
|
121
|
+
console.log(summary);
|
|
122
|
+
console.log();
|
|
123
|
+
} catch (error) {
|
|
124
|
+
console.error('❌ Error during summarization:', error.message);
|
|
125
|
+
process.exit(1);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Main entry point
|
|
131
|
+
*/
|
|
132
|
+
async function main() {
|
|
133
|
+
const args = process.argv.slice(2);
|
|
134
|
+
const command = args[0];
|
|
135
|
+
|
|
136
|
+
// Handle help
|
|
137
|
+
if (!command || command === 'help' || command === '--help' || command === '-h') {
|
|
138
|
+
printUsage();
|
|
139
|
+
process.exit(0);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Check for API key
|
|
143
|
+
const apiKey = process.env.ANTHROPIC_API_KEY;
|
|
144
|
+
if (!apiKey) {
|
|
145
|
+
console.error('❌ Error: ANTHROPIC_API_KEY environment variable required\n');
|
|
146
|
+
console.error('Set it with:');
|
|
147
|
+
console.error(' export ANTHROPIC_API_KEY="sk-ant-..."\n');
|
|
148
|
+
process.exit(1);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Initialize provider and service
|
|
152
|
+
const provider = new ClaudeProvider(apiKey);
|
|
153
|
+
const service = new PRDService(provider);
|
|
154
|
+
|
|
155
|
+
// Handle test command
|
|
156
|
+
if (command === 'test') {
|
|
157
|
+
const success = await testConnection(provider);
|
|
158
|
+
process.exit(success ? 0 : 1);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Handle parse and summarize commands
|
|
162
|
+
if (command === 'parse' || command === 'summarize') {
|
|
163
|
+
const file = args[1];
|
|
164
|
+
|
|
165
|
+
if (!file) {
|
|
166
|
+
console.error(`❌ Error: PRD file required for '${command}' command\n`);
|
|
167
|
+
console.error(`Usage: autopm-poc ${command} <prd-file>\n`);
|
|
168
|
+
process.exit(1);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Check if file exists
|
|
172
|
+
if (!fs.existsSync(file)) {
|
|
173
|
+
console.error(`❌ Error: File not found: ${file}\n`);
|
|
174
|
+
process.exit(1);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Read file content
|
|
178
|
+
const content = fs.readFileSync(file, 'utf8');
|
|
179
|
+
|
|
180
|
+
if (!content.trim()) {
|
|
181
|
+
console.error('❌ Error: PRD file is empty\n');
|
|
182
|
+
process.exit(1);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
console.log(`📄 Reading PRD from: ${file}`);
|
|
186
|
+
console.log(`📏 File size: ${content.length} characters\n`);
|
|
187
|
+
|
|
188
|
+
// Execute command
|
|
189
|
+
if (command === 'parse') {
|
|
190
|
+
const jsonFlag = args[2] === '--json';
|
|
191
|
+
|
|
192
|
+
if (jsonFlag) {
|
|
193
|
+
await parsePRDJson(service, content);
|
|
194
|
+
} else {
|
|
195
|
+
await parsePRDStream(service, content);
|
|
196
|
+
}
|
|
197
|
+
} else if (command === 'summarize') {
|
|
198
|
+
await summarizePRD(service, content);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
process.exit(0);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Unknown command
|
|
205
|
+
console.error(`❌ Error: Unknown command: ${command}\n`);
|
|
206
|
+
printUsage();
|
|
207
|
+
process.exit(1);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Run main with error handling
|
|
211
|
+
main().catch(err => {
|
|
212
|
+
console.error('❌ Fatal error:', err.message);
|
|
213
|
+
console.error('\nStack trace:');
|
|
214
|
+
console.error(err.stack);
|
|
215
|
+
process.exit(1);
|
|
216
|
+
});
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ClaudeProvider - Anthropic Claude API integration
|
|
3
|
+
*
|
|
4
|
+
* Provides both synchronous completion and streaming capabilities
|
|
5
|
+
* for Claude AI interactions.
|
|
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
|
+
|
|
13
|
+
const Anthropic = require('@anthropic-ai/sdk');
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* ClaudeProvider class for Anthropic Claude API integration
|
|
17
|
+
*/
|
|
18
|
+
class ClaudeProvider {
|
|
19
|
+
/**
|
|
20
|
+
* Create a new ClaudeProvider instance
|
|
21
|
+
* @param {string} apiKey - Anthropic API key
|
|
22
|
+
* @throws {Error} If API key is not provided
|
|
23
|
+
*/
|
|
24
|
+
constructor(apiKey) {
|
|
25
|
+
if (!apiKey) {
|
|
26
|
+
throw new Error('API key is required for ClaudeProvider');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
this.apiKey = apiKey;
|
|
30
|
+
this.client = new Anthropic({ apiKey });
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Complete a prompt synchronously (wait for full response)
|
|
35
|
+
* @param {string} prompt - The prompt to complete
|
|
36
|
+
* @param {Object} options - Optional configuration
|
|
37
|
+
* @param {string} options.model - Model to use (default: claude-sonnet-4-20250514)
|
|
38
|
+
* @param {number} options.maxTokens - Maximum tokens to generate (default: 4096)
|
|
39
|
+
* @returns {Promise<string>} The completed text
|
|
40
|
+
*/
|
|
41
|
+
async complete(prompt, options = {}) {
|
|
42
|
+
try {
|
|
43
|
+
const response = await this.client.messages.create({
|
|
44
|
+
model: options.model || 'claude-sonnet-4-20250514',
|
|
45
|
+
max_tokens: options.maxTokens || 4096,
|
|
46
|
+
messages: [{ role: 'user', content: prompt }]
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
// Extract text from response
|
|
50
|
+
if (response.content && response.content.length > 0) {
|
|
51
|
+
return response.content[0].text;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return '';
|
|
55
|
+
} catch (error) {
|
|
56
|
+
throw new Error(`Claude API error: ${error.message}`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Stream a prompt response (async generator for real-time feedback)
|
|
62
|
+
* @param {string} prompt - The prompt to complete
|
|
63
|
+
* @param {Object} options - Optional configuration
|
|
64
|
+
* @param {string} options.model - Model to use (default: claude-sonnet-4-20250514)
|
|
65
|
+
* @param {number} options.maxTokens - Maximum tokens to generate (default: 4096)
|
|
66
|
+
* @yields {string} Text chunks as they arrive
|
|
67
|
+
*/
|
|
68
|
+
async *stream(prompt, options = {}) {
|
|
69
|
+
try {
|
|
70
|
+
const stream = await this.client.messages.create({
|
|
71
|
+
model: options.model || 'claude-sonnet-4-20250514',
|
|
72
|
+
max_tokens: options.maxTokens || 4096,
|
|
73
|
+
stream: true,
|
|
74
|
+
messages: [{ role: 'user', content: prompt }]
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
// Yield text deltas as they arrive
|
|
78
|
+
for await (const event of stream) {
|
|
79
|
+
if (event.type === 'content_block_delta' &&
|
|
80
|
+
event.delta &&
|
|
81
|
+
event.delta.type === 'text_delta') {
|
|
82
|
+
yield event.delta.text;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
} catch (error) {
|
|
86
|
+
throw new Error(`Claude API streaming error: ${error.message}`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Get the current model being used
|
|
92
|
+
* @returns {string} The default model name
|
|
93
|
+
*/
|
|
94
|
+
getModel() {
|
|
95
|
+
return 'claude-sonnet-4-20250514';
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Test the API connection
|
|
100
|
+
* @returns {Promise<boolean>} True if connection is successful
|
|
101
|
+
*/
|
|
102
|
+
async testConnection() {
|
|
103
|
+
try {
|
|
104
|
+
await this.complete('Hello');
|
|
105
|
+
return true;
|
|
106
|
+
} catch (error) {
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
module.exports = ClaudeProvider;
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PRDService - Product Requirements Document parsing with AI
|
|
3
|
+
*
|
|
4
|
+
* Analyzes PRD documents and extracts structured information
|
|
5
|
+
* including epics, features, dependencies, and estimates.
|
|
6
|
+
*
|
|
7
|
+
* Documentation Queries:
|
|
8
|
+
* - mcp://context7/agile/prd-analysis - PRD analysis best practices
|
|
9
|
+
* - mcp://context7/agile/epic-breakdown - Epic decomposition patterns
|
|
10
|
+
* - mcp://context7/project-management/estimation - Estimation techniques
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* PRDService class for analyzing Product Requirements Documents
|
|
15
|
+
*/
|
|
16
|
+
class PRDService {
|
|
17
|
+
/**
|
|
18
|
+
* Create a new PRDService instance
|
|
19
|
+
* @param {Object} aiProvider - AI provider instance (e.g., ClaudeProvider)
|
|
20
|
+
*/
|
|
21
|
+
constructor(aiProvider) {
|
|
22
|
+
if (!aiProvider) {
|
|
23
|
+
throw new Error('AI provider is required for PRDService');
|
|
24
|
+
}
|
|
25
|
+
this.ai = aiProvider;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Build a comprehensive prompt for PRD analysis
|
|
30
|
+
* @param {string} prdContent - The PRD content to analyze
|
|
31
|
+
* @returns {string} Structured prompt for AI
|
|
32
|
+
* @private
|
|
33
|
+
*/
|
|
34
|
+
_buildPrompt(prdContent) {
|
|
35
|
+
return `You are an expert product manager and technical analyst. Analyze the following Product Requirements Document (PRD) and extract structured information.
|
|
36
|
+
|
|
37
|
+
PRD Content:
|
|
38
|
+
${prdContent}
|
|
39
|
+
|
|
40
|
+
Please analyze and provide:
|
|
41
|
+
|
|
42
|
+
1. **Project Overview**
|
|
43
|
+
- Project name
|
|
44
|
+
- Brief description
|
|
45
|
+
- Goals and objectives
|
|
46
|
+
|
|
47
|
+
2. **Epics/Features** (Main features or user stories)
|
|
48
|
+
- Name of each epic
|
|
49
|
+
- Description
|
|
50
|
+
- Rough estimate (if mentioned)
|
|
51
|
+
- Priority (High/Medium/Low)
|
|
52
|
+
|
|
53
|
+
3. **Technical Requirements**
|
|
54
|
+
- Technologies mentioned
|
|
55
|
+
- Infrastructure needs
|
|
56
|
+
- Integration points
|
|
57
|
+
|
|
58
|
+
4. **Dependencies**
|
|
59
|
+
- Dependencies between features
|
|
60
|
+
- External dependencies
|
|
61
|
+
- Blockers or constraints
|
|
62
|
+
|
|
63
|
+
5. **Timeline/Phases** (if mentioned)
|
|
64
|
+
- Phases or milestones
|
|
65
|
+
- Estimated durations
|
|
66
|
+
|
|
67
|
+
Please structure your response clearly with headers and bullet points. Focus on actionable insights that would help with project planning and task breakdown.`;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Build a simpler prompt for streaming analysis
|
|
72
|
+
* @param {string} prdContent - The PRD content to analyze
|
|
73
|
+
* @returns {string} Prompt for streaming
|
|
74
|
+
* @private
|
|
75
|
+
*/
|
|
76
|
+
_buildStreamPrompt(prdContent) {
|
|
77
|
+
return `Analyze this PRD and extract the key epics, features, and technical requirements:
|
|
78
|
+
|
|
79
|
+
${prdContent}
|
|
80
|
+
|
|
81
|
+
Provide a clear, structured breakdown of:
|
|
82
|
+
1. Main features/epics
|
|
83
|
+
2. Technical requirements
|
|
84
|
+
3. Dependencies
|
|
85
|
+
4. Estimated timeline (if mentioned)`;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Parse PRD synchronously (wait for complete analysis)
|
|
90
|
+
* @param {string} prdContent - The PRD content to analyze
|
|
91
|
+
* @param {Object} options - Optional configuration
|
|
92
|
+
* @returns {Promise<string>} Structured analysis of the PRD
|
|
93
|
+
*/
|
|
94
|
+
async parse(prdContent, options = {}) {
|
|
95
|
+
if (!prdContent) {
|
|
96
|
+
return 'No PRD content provided. Please provide a PRD document to analyze.';
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const prompt = this._buildPrompt(prdContent);
|
|
100
|
+
|
|
101
|
+
try {
|
|
102
|
+
return await this.ai.complete(prompt, options);
|
|
103
|
+
} catch (error) {
|
|
104
|
+
throw new Error(`PRD parsing error: ${error.message}`);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Parse PRD with streaming (async generator for real-time feedback)
|
|
110
|
+
* @param {string} prdContent - The PRD content to analyze
|
|
111
|
+
* @param {Object} options - Optional configuration
|
|
112
|
+
* @yields {string} Analysis chunks as they arrive
|
|
113
|
+
*/
|
|
114
|
+
async *parseStream(prdContent, options = {}) {
|
|
115
|
+
if (!prdContent) {
|
|
116
|
+
yield 'No PRD content provided. Please provide a PRD document to analyze.';
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const prompt = this._buildStreamPrompt(prdContent);
|
|
121
|
+
|
|
122
|
+
try {
|
|
123
|
+
for await (const chunk of this.ai.stream(prompt, options)) {
|
|
124
|
+
yield chunk;
|
|
125
|
+
}
|
|
126
|
+
} catch (error) {
|
|
127
|
+
throw new Error(`PRD streaming error: ${error.message}`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Extract epics from PRD (simplified extraction)
|
|
133
|
+
* @param {string} prdContent - The PRD content
|
|
134
|
+
* @returns {Promise<Array>} Array of epic objects
|
|
135
|
+
*/
|
|
136
|
+
async extractEpics(prdContent) {
|
|
137
|
+
const prompt = `Extract all epics/features from this PRD and return them as a JSON array:
|
|
138
|
+
|
|
139
|
+
${prdContent}
|
|
140
|
+
|
|
141
|
+
Format: [{"name": "Epic Name", "description": "Brief description", "estimate": "time estimate if available"}]
|
|
142
|
+
|
|
143
|
+
Return ONLY the JSON array, no other text.`;
|
|
144
|
+
|
|
145
|
+
try {
|
|
146
|
+
const response = await this.ai.complete(prompt, { maxTokens: 2048 });
|
|
147
|
+
|
|
148
|
+
// Try to parse as JSON
|
|
149
|
+
try {
|
|
150
|
+
return JSON.parse(response);
|
|
151
|
+
} catch (parseError) {
|
|
152
|
+
// If parsing fails, return raw response wrapped in array
|
|
153
|
+
return [{ raw: response }];
|
|
154
|
+
}
|
|
155
|
+
} catch (error) {
|
|
156
|
+
throw new Error(`Epic extraction error: ${error.message}`);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Summarize PRD in one paragraph
|
|
162
|
+
* @param {string} prdContent - The PRD content
|
|
163
|
+
* @returns {Promise<string>} One-paragraph summary
|
|
164
|
+
*/
|
|
165
|
+
async summarize(prdContent) {
|
|
166
|
+
const prompt = `Summarize this PRD in one concise paragraph (2-3 sentences):
|
|
167
|
+
|
|
168
|
+
${prdContent}`;
|
|
169
|
+
|
|
170
|
+
try {
|
|
171
|
+
return await this.ai.complete(prompt, { maxTokens: 256 });
|
|
172
|
+
} catch (error) {
|
|
173
|
+
throw new Error(`PRD summarization error: ${error.message}`);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
module.exports = PRDService;
|
package/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-autopm",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.31.0",
|
|
4
4
|
"description": "Autonomous Project Management Framework for Claude Code - Advanced AI-powered development automation",
|
|
5
5
|
"main": "bin/autopm.js",
|
|
6
6
|
"bin": {
|
|
7
|
-
"autopm": "./bin/autopm.js"
|
|
7
|
+
"autopm": "./bin/autopm.js",
|
|
8
|
+
"autopm-poc": "./bin/autopm-poc.js"
|
|
8
9
|
},
|
|
9
10
|
"scripts": {
|
|
10
11
|
"postinstall": "echo '🎉 ClaudeAutoPM installed! Run: autopm --help'",
|
|
@@ -52,6 +53,7 @@
|
|
|
52
53
|
"test:unit:coverage": "c8 node --test $(find test/unit -name '*.test.js' ! -name '*-jest.test.js')",
|
|
53
54
|
"test:e2e": "jest test/e2e",
|
|
54
55
|
"test:e2e:legacy": "node --test test/e2e/*.test.js",
|
|
56
|
+
"test:poc": "jest --config jest.config.poc.js",
|
|
55
57
|
"setup:hooks": "bash scripts/setup-hooks.sh",
|
|
56
58
|
"lint": "markdownlint *.md .claude/**/*.md install/*.md",
|
|
57
59
|
"lint:fix": "markdownlint --fix *.md .claude/**/*.md install/*.md",
|
|
@@ -114,6 +116,7 @@
|
|
|
114
116
|
"README.md"
|
|
115
117
|
],
|
|
116
118
|
"dependencies": {
|
|
119
|
+
"@anthropic-ai/sdk": "^0.32.1",
|
|
117
120
|
"@octokit/rest": "^22.0.0",
|
|
118
121
|
"azure-devops-node-api": "^15.1.1",
|
|
119
122
|
"chalk": "^5.3.0",
|