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.
@@ -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.28.0",
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": "^4.0.0",
132
+ "which": "^5.0.0",
132
133
  "yaml": "^2.8.1",
133
- "yargs": "^17.7.2"
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.40",
155
+ "@playwright/mcp": "^0.0.41",
155
156
  "@upstash/context7-mcp": "^1.0.0"
156
157
  },
157
158
  "publishConfig": {