ai-sdlc 0.2.0-alpha.60 → 0.2.0-alpha.61
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 +19 -6
- package/dist/agents/review.d.ts +13 -0
- package/dist/agents/review.d.ts.map +1 -1
- package/dist/agents/review.js +103 -33
- package/dist/agents/review.js.map +1 -1
- package/dist/cli/batch-processor.d.ts +64 -0
- package/dist/cli/batch-processor.d.ts.map +1 -0
- package/dist/cli/batch-processor.js +85 -0
- package/dist/cli/batch-processor.js.map +1 -0
- package/dist/cli/batch-validator.d.ts +80 -0
- package/dist/cli/batch-validator.d.ts.map +1 -0
- package/dist/cli/batch-validator.js +121 -0
- package/dist/cli/batch-validator.js.map +1 -0
- package/dist/cli/commands.d.ts +1 -0
- package/dist/cli/commands.d.ts.map +1 -1
- package/dist/cli/commands.js +217 -0
- package/dist/cli/commands.js.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validation error detail
|
|
3
|
+
*/
|
|
4
|
+
export interface ValidationError {
|
|
5
|
+
/** Story ID that failed validation */
|
|
6
|
+
storyId: string;
|
|
7
|
+
/** Error message describing the issue */
|
|
8
|
+
message: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Result of batch validation
|
|
12
|
+
*/
|
|
13
|
+
export interface BatchValidationResult {
|
|
14
|
+
/** Whether all validations passed */
|
|
15
|
+
valid: boolean;
|
|
16
|
+
/** List of valid story IDs that passed validation */
|
|
17
|
+
validStoryIds: string[];
|
|
18
|
+
/** List of validation errors */
|
|
19
|
+
errors: ValidationError[];
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Parse comma-separated story ID list
|
|
23
|
+
* Handles whitespace and filters empty strings
|
|
24
|
+
*
|
|
25
|
+
* @param input - Raw comma-separated string (e.g., "S-001, S-002 , S-003")
|
|
26
|
+
* @returns Array of trimmed story IDs
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* parseStoryIdList("S-001,S-002,S-003") // => ["S-001", "S-002", "S-003"]
|
|
30
|
+
* parseStoryIdList("S-001, S-002 , S-003") // => ["S-001", "S-002", "S-003"]
|
|
31
|
+
* parseStoryIdList("") // => []
|
|
32
|
+
*/
|
|
33
|
+
export declare function parseStoryIdList(input: string): string[];
|
|
34
|
+
/**
|
|
35
|
+
* Deduplicate story IDs while preserving order
|
|
36
|
+
* First occurrence is kept, subsequent duplicates are removed
|
|
37
|
+
*
|
|
38
|
+
* @param storyIds - Array of story IDs (may contain duplicates)
|
|
39
|
+
* @returns Array with duplicates removed
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* deduplicateStoryIds(["S-001", "S-002", "S-001"]) // => ["S-001", "S-002"]
|
|
43
|
+
* deduplicateStoryIds(["S-001", "S-001", "S-001"]) // => ["S-001"]
|
|
44
|
+
*/
|
|
45
|
+
export declare function deduplicateStoryIds(storyIds: string[]): string[];
|
|
46
|
+
/**
|
|
47
|
+
* Validate story ID format
|
|
48
|
+
* Story IDs must match pattern: S-\d+ (e.g., S-001, S-123)
|
|
49
|
+
*
|
|
50
|
+
* @param storyId - Story ID to validate
|
|
51
|
+
* @returns true if format is valid, false otherwise
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* validateStoryIdFormat("S-001") // => true
|
|
55
|
+
* validateStoryIdFormat("S-123") // => true
|
|
56
|
+
* validateStoryIdFormat("s-001") // => true (case-insensitive)
|
|
57
|
+
* validateStoryIdFormat("INVALID") // => false
|
|
58
|
+
* validateStoryIdFormat("S-") // => false
|
|
59
|
+
*/
|
|
60
|
+
export declare function validateStoryIdFormat(storyId: string): boolean;
|
|
61
|
+
/**
|
|
62
|
+
* Validate all story IDs in a batch
|
|
63
|
+
* Checks format and existence for each story
|
|
64
|
+
*
|
|
65
|
+
* @param storyIds - Array of story IDs to validate
|
|
66
|
+
* @param sdlcRoot - Root directory of .ai-sdlc
|
|
67
|
+
* @returns BatchValidationResult with valid IDs and errors
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* const result = validateStoryIds(["S-001", "S-002", "INVALID"], sdlcRoot);
|
|
71
|
+
* if (result.valid) {
|
|
72
|
+
* // All stories exist and are valid
|
|
73
|
+
* processStories(result.validStoryIds);
|
|
74
|
+
* } else {
|
|
75
|
+
* // Show errors
|
|
76
|
+
* result.errors.forEach(err => console.log(err.message));
|
|
77
|
+
* }
|
|
78
|
+
*/
|
|
79
|
+
export declare function validateStoryIds(storyIds: string[], sdlcRoot: string): BatchValidationResult;
|
|
80
|
+
//# sourceMappingURL=batch-validator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"batch-validator.d.ts","sourceRoot":"","sources":["../../src/cli/batch-validator.ts"],"names":[],"mappings":"AAGA;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,sCAAsC;IACtC,OAAO,EAAE,MAAM,CAAC;IAChB,yCAAyC;IACzC,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,qCAAqC;IACrC,KAAK,EAAE,OAAO,CAAC;IACf,qDAAqD;IACrD,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,gCAAgC;IAChC,MAAM,EAAE,eAAe,EAAE,CAAC;CAC3B;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CASxD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAahE;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAE9D;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,EAAE,EAClB,QAAQ,EAAE,MAAM,GACf,qBAAqB,CAwCvB"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { findStoryById } from '../core/story.js';
|
|
2
|
+
/**
|
|
3
|
+
* Parse comma-separated story ID list
|
|
4
|
+
* Handles whitespace and filters empty strings
|
|
5
|
+
*
|
|
6
|
+
* @param input - Raw comma-separated string (e.g., "S-001, S-002 , S-003")
|
|
7
|
+
* @returns Array of trimmed story IDs
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* parseStoryIdList("S-001,S-002,S-003") // => ["S-001", "S-002", "S-003"]
|
|
11
|
+
* parseStoryIdList("S-001, S-002 , S-003") // => ["S-001", "S-002", "S-003"]
|
|
12
|
+
* parseStoryIdList("") // => []
|
|
13
|
+
*/
|
|
14
|
+
export function parseStoryIdList(input) {
|
|
15
|
+
if (!input || input.trim() === '') {
|
|
16
|
+
return [];
|
|
17
|
+
}
|
|
18
|
+
return input
|
|
19
|
+
.split(',')
|
|
20
|
+
.map(id => id.trim())
|
|
21
|
+
.filter(id => id.length > 0);
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Deduplicate story IDs while preserving order
|
|
25
|
+
* First occurrence is kept, subsequent duplicates are removed
|
|
26
|
+
*
|
|
27
|
+
* @param storyIds - Array of story IDs (may contain duplicates)
|
|
28
|
+
* @returns Array with duplicates removed
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* deduplicateStoryIds(["S-001", "S-002", "S-001"]) // => ["S-001", "S-002"]
|
|
32
|
+
* deduplicateStoryIds(["S-001", "S-001", "S-001"]) // => ["S-001"]
|
|
33
|
+
*/
|
|
34
|
+
export function deduplicateStoryIds(storyIds) {
|
|
35
|
+
const seen = new Set();
|
|
36
|
+
const result = [];
|
|
37
|
+
for (const id of storyIds) {
|
|
38
|
+
const normalized = id.toUpperCase(); // Case-insensitive deduplication
|
|
39
|
+
if (!seen.has(normalized)) {
|
|
40
|
+
seen.add(normalized);
|
|
41
|
+
result.push(id);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return result;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Validate story ID format
|
|
48
|
+
* Story IDs must match pattern: S-\d+ (e.g., S-001, S-123)
|
|
49
|
+
*
|
|
50
|
+
* @param storyId - Story ID to validate
|
|
51
|
+
* @returns true if format is valid, false otherwise
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* validateStoryIdFormat("S-001") // => true
|
|
55
|
+
* validateStoryIdFormat("S-123") // => true
|
|
56
|
+
* validateStoryIdFormat("s-001") // => true (case-insensitive)
|
|
57
|
+
* validateStoryIdFormat("INVALID") // => false
|
|
58
|
+
* validateStoryIdFormat("S-") // => false
|
|
59
|
+
*/
|
|
60
|
+
export function validateStoryIdFormat(storyId) {
|
|
61
|
+
return /^S-\d+$/i.test(storyId);
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Validate all story IDs in a batch
|
|
65
|
+
* Checks format and existence for each story
|
|
66
|
+
*
|
|
67
|
+
* @param storyIds - Array of story IDs to validate
|
|
68
|
+
* @param sdlcRoot - Root directory of .ai-sdlc
|
|
69
|
+
* @returns BatchValidationResult with valid IDs and errors
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* const result = validateStoryIds(["S-001", "S-002", "INVALID"], sdlcRoot);
|
|
73
|
+
* if (result.valid) {
|
|
74
|
+
* // All stories exist and are valid
|
|
75
|
+
* processStories(result.validStoryIds);
|
|
76
|
+
* } else {
|
|
77
|
+
* // Show errors
|
|
78
|
+
* result.errors.forEach(err => console.log(err.message));
|
|
79
|
+
* }
|
|
80
|
+
*/
|
|
81
|
+
export function validateStoryIds(storyIds, sdlcRoot) {
|
|
82
|
+
const result = {
|
|
83
|
+
valid: true,
|
|
84
|
+
validStoryIds: [],
|
|
85
|
+
errors: [],
|
|
86
|
+
};
|
|
87
|
+
for (const storyId of storyIds) {
|
|
88
|
+
// Check format
|
|
89
|
+
if (!validateStoryIdFormat(storyId)) {
|
|
90
|
+
result.valid = false;
|
|
91
|
+
result.errors.push({
|
|
92
|
+
storyId,
|
|
93
|
+
message: `Invalid story ID format: ${storyId} (expected format: S-001, S-123, etc.)`,
|
|
94
|
+
});
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
// Check existence
|
|
98
|
+
try {
|
|
99
|
+
const story = findStoryById(sdlcRoot, storyId);
|
|
100
|
+
if (!story) {
|
|
101
|
+
result.valid = false;
|
|
102
|
+
result.errors.push({
|
|
103
|
+
storyId,
|
|
104
|
+
message: `Story not found: ${storyId}`,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
result.validStoryIds.push(storyId);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
result.valid = false;
|
|
113
|
+
result.errors.push({
|
|
114
|
+
storyId,
|
|
115
|
+
message: `Error validating story ${storyId}: ${error instanceof Error ? error.message : String(error)}`,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return result;
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=batch-validator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"batch-validator.js","sourceRoot":"","sources":["../../src/cli/batch-validator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAyBjD;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAa;IAC5C,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QAClC,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,OAAO,KAAK;SACT,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC;SACpB,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;AACjC,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,mBAAmB,CAAC,QAAkB;IACpD,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,MAAM,MAAM,GAAa,EAAE,CAAC;IAE5B,KAAK,MAAM,EAAE,IAAI,QAAQ,EAAE,CAAC;QAC1B,MAAM,UAAU,GAAG,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC,iCAAiC;QACtE,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC;YAC1B,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;YACrB,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAClB,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,qBAAqB,CAAC,OAAe;IACnD,OAAO,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;AAClC,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,gBAAgB,CAC9B,QAAkB,EAClB,QAAgB;IAEhB,MAAM,MAAM,GAA0B;QACpC,KAAK,EAAE,IAAI;QACX,aAAa,EAAE,EAAE;QACjB,MAAM,EAAE,EAAE;KACX,CAAC;IAEF,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,eAAe;QACf,IAAI,CAAC,qBAAqB,CAAC,OAAO,CAAC,EAAE,CAAC;YACpC,MAAM,CAAC,KAAK,GAAG,KAAK,CAAC;YACrB,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC;gBACjB,OAAO;gBACP,OAAO,EAAE,4BAA4B,OAAO,wCAAwC;aACrF,CAAC,CAAC;YACH,SAAS;QACX,CAAC;QAED,kBAAkB;QAClB,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,aAAa,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YAC/C,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,MAAM,CAAC,KAAK,GAAG,KAAK,CAAC;gBACrB,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC;oBACjB,OAAO;oBACP,OAAO,EAAE,oBAAoB,OAAO,EAAE;iBACvC,CAAC,CAAC;YACL,CAAC;iBAAM,CAAC;gBACN,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACrC,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,CAAC,KAAK,GAAG,KAAK,CAAC;YACrB,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC;gBACjB,OAAO;gBACP,OAAO,EAAE,0BAA0B,OAAO,KAAK,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE;aACxG,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
package/dist/cli/commands.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../src/cli/commands.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,KAAK,EAAU,UAAU,EAA0H,eAAe,EAAE,MAAM,mBAAmB,CAAC;AA4BvM;;GAEG;AACH,wBAAsB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAyB1C;AAED;;GAEG;AACH,wBAAsB,MAAM,CAAC,OAAO,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAmF1E;AA2DD;;GAEG;AACH,wBAAsB,GAAG,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAyGpF;
|
|
1
|
+
{"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../src/cli/commands.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,KAAK,EAAU,UAAU,EAA0H,eAAe,EAAE,MAAM,mBAAmB,CAAC;AA4BvM;;GAEG;AACH,wBAAsB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAyB1C;AAED;;GAEG;AACH,wBAAsB,MAAM,CAAC,OAAO,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAmF1E;AA2DD;;GAEG;AACH,wBAAsB,GAAG,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAyGpF;AA6HD;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE;IAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,EAC/B,cAAc,EAAE;IAAE,OAAO,EAAE,OAAO,CAAA;CAAE,EACpC,WAAW,EAAE,KAAK,GAAG,IAAI,GACxB,OAAO,CAKT;AA0GD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,sBAAsB,CAC1C,WAAW,EAAE,KAAK,EAClB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,GAC3B,OAAO,CAAC,eAAe,CAAC,CA8H1B;AA+JD;;GAEG;AACH,wBAAsB,GAAG,CAAC,OAAO,EAAE;IAAE,IAAI,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CA4oCxP;AAgXD;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAC;CAClC;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,UAAU,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,GAAG,SAAS,GAAG,IAAI,CAiDlF;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,KAAK,GAAG;IACpD,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB,CAgCA;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,GAAG,MAAM,CAsBtE;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAgB3E;AAED;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAYtD;AA6DD;;GAEG;AACH,wBAAsB,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAkH7D;AA8GD;;GAEG;AACH,wBAAsB,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;IAAE,YAAY,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAgClG;AAED,wBAAsB,OAAO,CAAC,OAAO,EAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CA8G7G;AAqFD;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAGlD;AAED;;GAEG;AACH,wBAAsB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CA8DnD;AAED;;GAEG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAyEhE;AAED;;GAEG;AACH,wBAAsB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAuElG"}
|
package/dist/cli/commands.js
CHANGED
|
@@ -287,6 +287,35 @@ function validateAutoStoryOptions(options) {
|
|
|
287
287
|
' - ai-sdlc run --story <id> --step <phase> (single phase)');
|
|
288
288
|
}
|
|
289
289
|
}
|
|
290
|
+
/**
|
|
291
|
+
* Validates flag combinations for --batch conflicts
|
|
292
|
+
* @throws Error if conflicting flags are detected
|
|
293
|
+
*/
|
|
294
|
+
function validateBatchOptions(options) {
|
|
295
|
+
if (!options.batch) {
|
|
296
|
+
return; // No batch flag, nothing to validate
|
|
297
|
+
}
|
|
298
|
+
// --batch and --story are mutually exclusive
|
|
299
|
+
if (options.story) {
|
|
300
|
+
throw new Error('Cannot combine --batch with --story flag.\n' +
|
|
301
|
+
'Use either:\n' +
|
|
302
|
+
' - ai-sdlc run --batch S-001,S-002,S-003 (batch processing)\n' +
|
|
303
|
+
' - ai-sdlc run --auto --story <id> (single story)');
|
|
304
|
+
}
|
|
305
|
+
// --batch and --watch are mutually exclusive
|
|
306
|
+
if (options.watch) {
|
|
307
|
+
throw new Error('Cannot combine --batch with --watch flag.\n' +
|
|
308
|
+
'Use either:\n' +
|
|
309
|
+
' - ai-sdlc run --batch S-001,S-002,S-003 (batch processing)\n' +
|
|
310
|
+
' - ai-sdlc run --watch (daemon mode)');
|
|
311
|
+
}
|
|
312
|
+
// --batch and --continue are mutually exclusive
|
|
313
|
+
if (options.continue) {
|
|
314
|
+
throw new Error('Cannot combine --batch with --continue flag.\n' +
|
|
315
|
+
'Batch mode does not support resuming from checkpoints.\n' +
|
|
316
|
+
'Use: ai-sdlc run --batch S-001,S-002,S-003');
|
|
317
|
+
}
|
|
318
|
+
}
|
|
290
319
|
/**
|
|
291
320
|
* Determines if a specific phase should be executed based on story state
|
|
292
321
|
* @param story The story to check
|
|
@@ -587,6 +616,149 @@ export async function preFlightConflictCheck(targetStory, sdlcRoot, options) {
|
|
|
587
616
|
return { proceed: true, warnings: ['Conflict detection failed'] };
|
|
588
617
|
}
|
|
589
618
|
}
|
|
619
|
+
/**
|
|
620
|
+
* Process multiple stories sequentially through full SDLC
|
|
621
|
+
* Internal function used by batch mode
|
|
622
|
+
*/
|
|
623
|
+
async function processBatchInternal(storyIds, sdlcRoot, options) {
|
|
624
|
+
const startTime = Date.now();
|
|
625
|
+
const config = loadConfig();
|
|
626
|
+
const c = getThemedChalk(config);
|
|
627
|
+
const { formatBatchProgress, formatBatchSummary, logStoryCompletion, promptContinueOnError } = await import('./batch-processor.js');
|
|
628
|
+
const result = {
|
|
629
|
+
total: storyIds.length,
|
|
630
|
+
succeeded: 0,
|
|
631
|
+
failed: 0,
|
|
632
|
+
skipped: 0,
|
|
633
|
+
errors: [],
|
|
634
|
+
duration: 0,
|
|
635
|
+
};
|
|
636
|
+
console.log();
|
|
637
|
+
console.log(c.bold('═══ Starting Batch Processing ═══'));
|
|
638
|
+
console.log(c.dim(` Stories: ${storyIds.join(', ')}`));
|
|
639
|
+
console.log(c.dim(` Dry run: ${options.dryRun ? 'yes' : 'no'}`));
|
|
640
|
+
console.log();
|
|
641
|
+
// Process each story sequentially
|
|
642
|
+
for (let i = 0; i < storyIds.length; i++) {
|
|
643
|
+
const storyId = storyIds[i];
|
|
644
|
+
// Get story and check status
|
|
645
|
+
let story;
|
|
646
|
+
try {
|
|
647
|
+
story = getStory(sdlcRoot, storyId);
|
|
648
|
+
}
|
|
649
|
+
catch (error) {
|
|
650
|
+
result.failed++;
|
|
651
|
+
result.errors.push({
|
|
652
|
+
storyId,
|
|
653
|
+
error: `Story not found: ${error instanceof Error ? error.message : String(error)}`,
|
|
654
|
+
});
|
|
655
|
+
console.log(c.error(`[${i + 1}/${storyIds.length}] ✗ Story not found: ${storyId}`));
|
|
656
|
+
console.log();
|
|
657
|
+
// Ask if user wants to continue (or abort in non-interactive)
|
|
658
|
+
const shouldContinue = await promptContinueOnError(storyId, c);
|
|
659
|
+
if (!shouldContinue) {
|
|
660
|
+
console.log(c.warning('Batch processing aborted.'));
|
|
661
|
+
break;
|
|
662
|
+
}
|
|
663
|
+
continue;
|
|
664
|
+
}
|
|
665
|
+
// Skip if already done
|
|
666
|
+
if (story.frontmatter.status === 'done') {
|
|
667
|
+
result.skipped++;
|
|
668
|
+
console.log(c.dim(`[${i + 1}/${storyIds.length}] ⊘ Skipping ${storyId} (already completed)`));
|
|
669
|
+
console.log();
|
|
670
|
+
continue;
|
|
671
|
+
}
|
|
672
|
+
// Show progress header
|
|
673
|
+
const progress = {
|
|
674
|
+
currentIndex: i,
|
|
675
|
+
total: storyIds.length,
|
|
676
|
+
currentStory: story,
|
|
677
|
+
};
|
|
678
|
+
console.log(c.info(formatBatchProgress(progress)));
|
|
679
|
+
console.log();
|
|
680
|
+
// Dry-run mode: just show what would be done
|
|
681
|
+
if (options.dryRun) {
|
|
682
|
+
console.log(c.dim(' Would process story through full SDLC'));
|
|
683
|
+
console.log(c.dim(` Status: ${story.frontmatter.status}`));
|
|
684
|
+
console.log();
|
|
685
|
+
result.succeeded++;
|
|
686
|
+
continue;
|
|
687
|
+
}
|
|
688
|
+
// Process story through full SDLC by recursively calling run()
|
|
689
|
+
// We set auto: true to ensure full SDLC execution
|
|
690
|
+
try {
|
|
691
|
+
await run({
|
|
692
|
+
auto: true,
|
|
693
|
+
story: storyId,
|
|
694
|
+
dryRun: false,
|
|
695
|
+
worktree: options.worktree,
|
|
696
|
+
force: options.force,
|
|
697
|
+
});
|
|
698
|
+
// Check if story completed successfully (moved to done)
|
|
699
|
+
const finalStory = getStory(sdlcRoot, storyId);
|
|
700
|
+
if (finalStory.frontmatter.status === 'done') {
|
|
701
|
+
result.succeeded++;
|
|
702
|
+
logStoryCompletion(storyId, true, c);
|
|
703
|
+
}
|
|
704
|
+
else {
|
|
705
|
+
// Story didn't reach done state - treat as failure
|
|
706
|
+
result.failed++;
|
|
707
|
+
result.errors.push({
|
|
708
|
+
storyId,
|
|
709
|
+
error: `Story did not complete (status: ${finalStory.frontmatter.status})`,
|
|
710
|
+
});
|
|
711
|
+
logStoryCompletion(storyId, false, c);
|
|
712
|
+
// Ask if user wants to continue (or abort in non-interactive)
|
|
713
|
+
const shouldContinue = await promptContinueOnError(storyId, c);
|
|
714
|
+
if (!shouldContinue) {
|
|
715
|
+
console.log(c.warning('Batch processing aborted.'));
|
|
716
|
+
break;
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
catch (error) {
|
|
721
|
+
result.failed++;
|
|
722
|
+
result.errors.push({
|
|
723
|
+
storyId,
|
|
724
|
+
error: error instanceof Error ? error.message : String(error),
|
|
725
|
+
});
|
|
726
|
+
logStoryCompletion(storyId, false, c);
|
|
727
|
+
// Ask if user wants to continue (or abort in non-interactive)
|
|
728
|
+
const shouldContinue = await promptContinueOnError(storyId, c);
|
|
729
|
+
if (!shouldContinue) {
|
|
730
|
+
console.log(c.warning('Batch processing aborted.'));
|
|
731
|
+
break;
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
console.log();
|
|
735
|
+
}
|
|
736
|
+
// Display final summary
|
|
737
|
+
result.duration = Date.now() - startTime;
|
|
738
|
+
const summaryLines = formatBatchSummary(result);
|
|
739
|
+
summaryLines.forEach((line) => {
|
|
740
|
+
if (line.includes('✓')) {
|
|
741
|
+
console.log(c.success(line));
|
|
742
|
+
}
|
|
743
|
+
else if (line.includes('✗')) {
|
|
744
|
+
console.log(c.error(line));
|
|
745
|
+
}
|
|
746
|
+
else if (line.includes('⊘')) {
|
|
747
|
+
console.log(c.warning(line));
|
|
748
|
+
}
|
|
749
|
+
else if (line.startsWith(' -')) {
|
|
750
|
+
console.log(c.dim(line));
|
|
751
|
+
}
|
|
752
|
+
else {
|
|
753
|
+
console.log(line);
|
|
754
|
+
}
|
|
755
|
+
});
|
|
756
|
+
// Return non-zero exit code if any failures occurred
|
|
757
|
+
if (result.failed > 0) {
|
|
758
|
+
process.exitCode = 1;
|
|
759
|
+
}
|
|
760
|
+
return result;
|
|
761
|
+
}
|
|
590
762
|
/**
|
|
591
763
|
* Run the workflow (process one action or all)
|
|
592
764
|
*/
|
|
@@ -626,6 +798,51 @@ export async function run(options) {
|
|
|
626
798
|
await startDaemon({ maxIterations: maxIterationsOverride });
|
|
627
799
|
return; // Daemon runs indefinitely
|
|
628
800
|
}
|
|
801
|
+
// Handle batch mode
|
|
802
|
+
if (options.batch) {
|
|
803
|
+
// Validate batch options first
|
|
804
|
+
try {
|
|
805
|
+
validateBatchOptions(options);
|
|
806
|
+
}
|
|
807
|
+
catch (error) {
|
|
808
|
+
console.log(c.error(`Error: ${error instanceof Error ? error.message : String(error)}`));
|
|
809
|
+
return;
|
|
810
|
+
}
|
|
811
|
+
// Import batch validation modules
|
|
812
|
+
const { parseStoryIdList, deduplicateStoryIds, validateStoryIds } = await import('./batch-validator.js');
|
|
813
|
+
// Parse and validate story IDs
|
|
814
|
+
const rawStoryIds = parseStoryIdList(options.batch);
|
|
815
|
+
if (rawStoryIds.length === 0) {
|
|
816
|
+
console.log(c.error('Error: Empty batch - no story IDs provided'));
|
|
817
|
+
console.log(c.dim('Usage: ai-sdlc run --batch S-001,S-002,S-003'));
|
|
818
|
+
return;
|
|
819
|
+
}
|
|
820
|
+
// Deduplicate story IDs
|
|
821
|
+
const storyIds = deduplicateStoryIds(rawStoryIds);
|
|
822
|
+
if (storyIds.length < rawStoryIds.length) {
|
|
823
|
+
const duplicateCount = rawStoryIds.length - storyIds.length;
|
|
824
|
+
console.log(c.dim(`Note: Removed ${duplicateCount} duplicate story ID(s)`));
|
|
825
|
+
}
|
|
826
|
+
// Validate all stories exist before processing
|
|
827
|
+
const validation = validateStoryIds(storyIds, sdlcRoot);
|
|
828
|
+
if (!validation.valid) {
|
|
829
|
+
console.log(c.error('Error: Batch validation failed'));
|
|
830
|
+
console.log();
|
|
831
|
+
for (const error of validation.errors) {
|
|
832
|
+
console.log(c.error(` - ${error.message}`));
|
|
833
|
+
}
|
|
834
|
+
console.log();
|
|
835
|
+
console.log(c.dim('Fix the errors above and try again.'));
|
|
836
|
+
return;
|
|
837
|
+
}
|
|
838
|
+
// Process the batch using internal function
|
|
839
|
+
await processBatchInternal(storyIds, sdlcRoot, {
|
|
840
|
+
dryRun: options.dryRun,
|
|
841
|
+
worktree: options.worktree,
|
|
842
|
+
force: options.force,
|
|
843
|
+
});
|
|
844
|
+
return; // Batch processing complete
|
|
845
|
+
}
|
|
629
846
|
// Valid step names for --step option
|
|
630
847
|
const validSteps = ['refine', 'research', 'plan', 'implement', 'review'];
|
|
631
848
|
// Validate --step option early
|