claude-autopm 1.28.0 → 1.29.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 +47 -15
- package/autopm/.claude/scripts/pm/analytics.js +425 -0
- package/autopm/.claude/scripts/pm/sync-batch.js +337 -0
- package/lib/README-FILTER-SEARCH.md +285 -0
- package/lib/analytics-engine.js +689 -0
- package/lib/batch-processor-integration.js +366 -0
- package/lib/batch-processor.js +278 -0
- package/lib/burndown-chart.js +415 -0
- package/lib/dependency-analyzer.js +466 -0
- package/lib/filter-engine.js +414 -0
- package/lib/query-parser.js +322 -0
- package/package.json +5 -4
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QueryParser - Parse command-line filter arguments into structured queries
|
|
3
|
+
*
|
|
4
|
+
* Converts CLI-style filter arguments into structured query objects for
|
|
5
|
+
* filtering PRDs, Epics, and Tasks.
|
|
6
|
+
*
|
|
7
|
+
* @example Basic Usage
|
|
8
|
+
* ```javascript
|
|
9
|
+
* const QueryParser = require('./lib/query-parser');
|
|
10
|
+
* const parser = new QueryParser();
|
|
11
|
+
*
|
|
12
|
+
* // Parse CLI arguments
|
|
13
|
+
* const query = parser.parse(['--status', 'active', '--priority', 'high']);
|
|
14
|
+
* // Returns: { status: 'active', priority: 'high' }
|
|
15
|
+
*
|
|
16
|
+
* // Validate query
|
|
17
|
+
* const validation = parser.validate(query);
|
|
18
|
+
* // Returns: { valid: true, errors: [] }
|
|
19
|
+
* ```
|
|
20
|
+
*
|
|
21
|
+
* @example Supported Filters
|
|
22
|
+
* ```javascript
|
|
23
|
+
* // Status filter
|
|
24
|
+
* parser.parse(['--status', 'active']);
|
|
25
|
+
*
|
|
26
|
+
* // Priority filter
|
|
27
|
+
* parser.parse(['--priority', 'P0']);
|
|
28
|
+
*
|
|
29
|
+
* // Date range filter
|
|
30
|
+
* parser.parse(['--created-after', '2025-01-01', '--created-before', '2025-12-31']);
|
|
31
|
+
*
|
|
32
|
+
* // Full-text search
|
|
33
|
+
* parser.parse(['--search', 'authentication API']);
|
|
34
|
+
*
|
|
35
|
+
* // Combined filters
|
|
36
|
+
* parser.parse([
|
|
37
|
+
* '--status', 'active',
|
|
38
|
+
* '--priority', 'high',
|
|
39
|
+
* '--created-after', '2025-01-01',
|
|
40
|
+
* '--search', 'OAuth2'
|
|
41
|
+
* ]);
|
|
42
|
+
* ```
|
|
43
|
+
*
|
|
44
|
+
* @module QueryParser
|
|
45
|
+
* @version 1.0.0
|
|
46
|
+
* @since v1.28.0
|
|
47
|
+
*/
|
|
48
|
+
|
|
49
|
+
class QueryParser {
|
|
50
|
+
constructor() {
|
|
51
|
+
/**
|
|
52
|
+
* Supported filter names
|
|
53
|
+
* @type {string[]}
|
|
54
|
+
*/
|
|
55
|
+
this.supportedFilters = [
|
|
56
|
+
'status',
|
|
57
|
+
'priority',
|
|
58
|
+
'epic',
|
|
59
|
+
'author',
|
|
60
|
+
'assignee',
|
|
61
|
+
'created-after',
|
|
62
|
+
'created-before',
|
|
63
|
+
'updated-after',
|
|
64
|
+
'updated-before',
|
|
65
|
+
'search'
|
|
66
|
+
];
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Valid status values
|
|
70
|
+
* @type {string[]}
|
|
71
|
+
*/
|
|
72
|
+
this.validStatuses = [
|
|
73
|
+
'draft',
|
|
74
|
+
'active',
|
|
75
|
+
'in_progress',
|
|
76
|
+
'completed',
|
|
77
|
+
'blocked',
|
|
78
|
+
'archived'
|
|
79
|
+
];
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Valid priority values
|
|
83
|
+
* @type {string[]}
|
|
84
|
+
*/
|
|
85
|
+
this.validPriorities = [
|
|
86
|
+
'P0', 'P1', 'P2', 'P3',
|
|
87
|
+
'p0', 'p1', 'p2', 'p3',
|
|
88
|
+
'high', 'medium', 'low',
|
|
89
|
+
'High', 'Medium', 'Low',
|
|
90
|
+
'HIGH', 'MEDIUM', 'LOW'
|
|
91
|
+
];
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Date filter fields
|
|
95
|
+
* @type {string[]}
|
|
96
|
+
*/
|
|
97
|
+
this.dateFilters = [
|
|
98
|
+
'created-after',
|
|
99
|
+
'created-before',
|
|
100
|
+
'updated-after',
|
|
101
|
+
'updated-before'
|
|
102
|
+
];
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Parse command-line filter arguments into structured query
|
|
107
|
+
*
|
|
108
|
+
* @param {string[]} args - Command-line arguments (e.g., ['--status', 'active'])
|
|
109
|
+
* @returns {Object} - Parsed query object
|
|
110
|
+
*
|
|
111
|
+
* @example
|
|
112
|
+
* const query = parser.parse(['--status', 'active', '--priority', 'high']);
|
|
113
|
+
* // Returns: { status: 'active', priority: 'high' }
|
|
114
|
+
*/
|
|
115
|
+
parse(args) {
|
|
116
|
+
const query = {};
|
|
117
|
+
|
|
118
|
+
if (!Array.isArray(args) || args.length === 0) {
|
|
119
|
+
return query;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
for (let i = 0; i < args.length; i++) {
|
|
123
|
+
const arg = args[i];
|
|
124
|
+
|
|
125
|
+
// Check if this is a filter flag (starts with --)
|
|
126
|
+
if (!arg.startsWith('--')) {
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Extract filter name (remove -- prefix)
|
|
131
|
+
const filterName = arg.substring(2);
|
|
132
|
+
|
|
133
|
+
// Check if this is a supported filter
|
|
134
|
+
if (!this.supportedFilters.includes(filterName)) {
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Get the next argument as the value
|
|
139
|
+
const value = args[i + 1];
|
|
140
|
+
|
|
141
|
+
// Skip if no value or value is another flag
|
|
142
|
+
if (!value || value.startsWith('--')) {
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Trim whitespace
|
|
147
|
+
const trimmedValue = value.trim();
|
|
148
|
+
|
|
149
|
+
// Skip empty values
|
|
150
|
+
if (trimmedValue === '') {
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Add to query
|
|
155
|
+
query[filterName] = trimmedValue;
|
|
156
|
+
|
|
157
|
+
// Skip the next argument (we just consumed it as a value)
|
|
158
|
+
i++;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return query;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Validate query object
|
|
166
|
+
*
|
|
167
|
+
* Checks for valid date formats and other constraints.
|
|
168
|
+
*
|
|
169
|
+
* @param {Object} query - Query object to validate
|
|
170
|
+
* @returns {Object} - { valid: boolean, errors: string[] }
|
|
171
|
+
*
|
|
172
|
+
* @example
|
|
173
|
+
* const result = parser.validate({ status: 'active', 'created-after': '2025-01-01' });
|
|
174
|
+
* // Returns: { valid: true, errors: [] }
|
|
175
|
+
*
|
|
176
|
+
* const result2 = parser.validate({ 'created-after': 'invalid-date' });
|
|
177
|
+
* // Returns: { valid: false, errors: ['Invalid date format...'] }
|
|
178
|
+
*/
|
|
179
|
+
validate(query) {
|
|
180
|
+
const errors = [];
|
|
181
|
+
|
|
182
|
+
if (!query || typeof query !== 'object') {
|
|
183
|
+
return { valid: true, errors: [] };
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Validate date filters
|
|
187
|
+
for (const dateFilter of this.dateFilters) {
|
|
188
|
+
if (query[dateFilter]) {
|
|
189
|
+
const value = query[dateFilter];
|
|
190
|
+
|
|
191
|
+
// Check YYYY-MM-DD format
|
|
192
|
+
if (!this.isValidDateFormat(value)) {
|
|
193
|
+
errors.push(`Invalid date format for ${dateFilter}: ${value} (expected YYYY-MM-DD)`);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return {
|
|
199
|
+
valid: errors.length === 0,
|
|
200
|
+
errors
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Check if a string is a valid date in YYYY-MM-DD format
|
|
206
|
+
*
|
|
207
|
+
* @param {string} dateString - Date string to validate
|
|
208
|
+
* @returns {boolean} - True if valid
|
|
209
|
+
* @private
|
|
210
|
+
*/
|
|
211
|
+
isValidDateFormat(dateString) {
|
|
212
|
+
// Check format: YYYY-MM-DD
|
|
213
|
+
const regex = /^\d{4}-\d{2}-\d{2}$/;
|
|
214
|
+
if (!regex.test(dateString)) {
|
|
215
|
+
return false;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Parse and validate actual date
|
|
219
|
+
const parts = dateString.split('-');
|
|
220
|
+
const year = parseInt(parts[0], 10);
|
|
221
|
+
const month = parseInt(parts[1], 10);
|
|
222
|
+
const day = parseInt(parts[2], 10);
|
|
223
|
+
|
|
224
|
+
// Check ranges
|
|
225
|
+
if (month < 1 || month > 12) {
|
|
226
|
+
return false;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (day < 1 || day > 31) {
|
|
230
|
+
return false;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Create date and verify it matches input
|
|
234
|
+
// (this catches invalid dates like 2025-02-31)
|
|
235
|
+
const date = new Date(year, month - 1, day);
|
|
236
|
+
return (
|
|
237
|
+
date.getFullYear() === year &&
|
|
238
|
+
date.getMonth() === month - 1 &&
|
|
239
|
+
date.getDate() === day
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Get list of supported filters
|
|
245
|
+
*
|
|
246
|
+
* @returns {string[]} - Array of supported filter names
|
|
247
|
+
*
|
|
248
|
+
* @example
|
|
249
|
+
* const filters = parser.getSupportedFilters();
|
|
250
|
+
* // Returns: ['status', 'priority', 'epic', ...]
|
|
251
|
+
*/
|
|
252
|
+
getSupportedFilters() {
|
|
253
|
+
return [...this.supportedFilters];
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Get help text for filters
|
|
258
|
+
*
|
|
259
|
+
* Returns formatted help text describing all available filters
|
|
260
|
+
* with examples.
|
|
261
|
+
*
|
|
262
|
+
* @returns {string} - Formatted help text
|
|
263
|
+
*
|
|
264
|
+
* @example
|
|
265
|
+
* console.log(parser.getFilterHelp());
|
|
266
|
+
* // Outputs:
|
|
267
|
+
* // Available Filters:
|
|
268
|
+
* // --status <value> Filter by status (draft, active, ...)
|
|
269
|
+
* // --priority <value> Filter by priority (P0, P1, high, ...)
|
|
270
|
+
* // ...
|
|
271
|
+
*/
|
|
272
|
+
getFilterHelp() {
|
|
273
|
+
return `
|
|
274
|
+
Available Filters:
|
|
275
|
+
|
|
276
|
+
--status <value> Filter by status
|
|
277
|
+
Values: draft, active, in_progress, completed, blocked, archived
|
|
278
|
+
Example: --status active
|
|
279
|
+
|
|
280
|
+
--priority <value> Filter by priority
|
|
281
|
+
Values: P0, P1, P2, P3, high, medium, low
|
|
282
|
+
Example: --priority high
|
|
283
|
+
|
|
284
|
+
--epic <id> Filter by epic ID
|
|
285
|
+
Example: --epic epic-001
|
|
286
|
+
|
|
287
|
+
--author <name> Filter by author name
|
|
288
|
+
Example: --author john
|
|
289
|
+
|
|
290
|
+
--assignee <name> Filter by assignee name
|
|
291
|
+
Example: --assignee jane
|
|
292
|
+
|
|
293
|
+
--created-after <date> Created after date (YYYY-MM-DD)
|
|
294
|
+
Example: --created-after 2025-01-01
|
|
295
|
+
|
|
296
|
+
--created-before <date> Created before date (YYYY-MM-DD)
|
|
297
|
+
Example: --created-before 2025-12-31
|
|
298
|
+
|
|
299
|
+
--updated-after <date> Updated after date (YYYY-MM-DD)
|
|
300
|
+
Example: --updated-after 2025-06-01
|
|
301
|
+
|
|
302
|
+
--updated-before <date> Updated before date (YYYY-MM-DD)
|
|
303
|
+
Example: --updated-before 2025-06-30
|
|
304
|
+
|
|
305
|
+
--search <text> Full-text search in content and frontmatter
|
|
306
|
+
Example: --search "authentication API"
|
|
307
|
+
|
|
308
|
+
Examples:
|
|
309
|
+
|
|
310
|
+
# Filter by status and priority
|
|
311
|
+
--status active --priority high
|
|
312
|
+
|
|
313
|
+
# Filter by date range
|
|
314
|
+
--created-after 2025-01-01 --created-before 2025-12-31
|
|
315
|
+
|
|
316
|
+
# Combine filters
|
|
317
|
+
--status active --priority P0 --epic epic-001 --search "OAuth2"
|
|
318
|
+
`.trim();
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
module.exports = QueryParser;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-autopm",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.29.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": {
|
|
@@ -114,6 +114,7 @@
|
|
|
114
114
|
"README.md"
|
|
115
115
|
],
|
|
116
116
|
"dependencies": {
|
|
117
|
+
"@octokit/rest": "^22.0.0",
|
|
117
118
|
"azure-devops-node-api": "^15.1.1",
|
|
118
119
|
"chalk": "^5.3.0",
|
|
119
120
|
"dotenv": "^16.6.1",
|
|
@@ -128,9 +129,9 @@
|
|
|
128
129
|
"ora": "^5.4.1",
|
|
129
130
|
"simple-git": "^3.20.0",
|
|
130
131
|
"table": "^6.8.1",
|
|
131
|
-
"which": "^
|
|
132
|
+
"which": "^5.0.0",
|
|
132
133
|
"yaml": "^2.8.1",
|
|
133
|
-
"yargs": "^
|
|
134
|
+
"yargs": "^18.0.0"
|
|
134
135
|
},
|
|
135
136
|
"devDependencies": {
|
|
136
137
|
"@jest/globals": "^30.1.2",
|
|
@@ -151,7 +152,7 @@
|
|
|
151
152
|
"sinon": "^21.0.0"
|
|
152
153
|
},
|
|
153
154
|
"optionalDependencies": {
|
|
154
|
-
"@playwright/mcp": "^0.0.
|
|
155
|
+
"@playwright/mcp": "^0.0.41",
|
|
155
156
|
"@upstash/context7-mcp": "^1.0.0"
|
|
156
157
|
},
|
|
157
158
|
"publishConfig": {
|