speccrew 0.6.69 → 0.7.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.
Files changed (134) hide show
  1. package/.speccrew/agents/speccrew-task-worker.md +1 -1
  2. package/.speccrew/agents/speccrew-team-leader.md +336 -189
  3. package/.speccrew/skills/speccrew-agentflow-manager/SKILL.md +161 -0
  4. package/.speccrew/skills/speccrew-agentflow-manager/workflow.agentflow.xml +347 -0
  5. package/.speccrew/skills/speccrew-deploy-build/SKILL.md +3 -56
  6. package/.speccrew/skills/speccrew-deploy-build/workflow.agentflow.xml +125 -0
  7. package/.speccrew/skills/speccrew-deploy-migrate/SKILL.md +3 -64
  8. package/.speccrew/skills/speccrew-deploy-migrate/workflow.agentflow.xml +135 -0
  9. package/.speccrew/skills/speccrew-deploy-smoke-test/SKILL.md +4 -156
  10. package/.speccrew/skills/speccrew-deploy-smoke-test/workflow.agentflow.xml +178 -0
  11. package/.speccrew/skills/speccrew-deploy-startup/SKILL.md +3 -135
  12. package/.speccrew/skills/speccrew-deploy-startup/workflow.agentflow.xml +223 -0
  13. package/.speccrew/skills/speccrew-dev-backend/SKILL.md +10 -2
  14. package/.speccrew/skills/speccrew-dev-backend/workflow.agentflow.xml +254 -0
  15. package/.speccrew/skills/speccrew-dev-desktop-electron/SKILL.md +10 -2
  16. package/.speccrew/skills/speccrew-dev-desktop-electron/workflow.agentflow.xml +259 -0
  17. package/.speccrew/skills/speccrew-dev-desktop-tauri/SKILL.md +10 -2
  18. package/.speccrew/skills/speccrew-dev-desktop-tauri/workflow.agentflow.xml +245 -0
  19. package/.speccrew/skills/speccrew-dev-frontend/SKILL.md +10 -2
  20. package/.speccrew/skills/speccrew-dev-frontend/workflow.agentflow.xml +262 -0
  21. package/.speccrew/skills/speccrew-dev-mobile/SKILL.md +10 -2
  22. package/.speccrew/skills/speccrew-dev-mobile/workflow.agentflow.xml +244 -0
  23. package/.speccrew/skills/speccrew-dev-review-backend/SKILL.md +10 -2
  24. package/.speccrew/skills/speccrew-dev-review-backend/workflow.agentflow.xml +251 -0
  25. package/.speccrew/skills/speccrew-dev-review-desktop/SKILL.md +10 -2
  26. package/.speccrew/skills/speccrew-dev-review-desktop/workflow.agentflow.xml +214 -0
  27. package/.speccrew/skills/speccrew-dev-review-frontend/SKILL.md +10 -2
  28. package/.speccrew/skills/speccrew-dev-review-frontend/workflow.agentflow.xml +213 -0
  29. package/.speccrew/skills/speccrew-dev-review-mobile/SKILL.md +10 -2
  30. package/.speccrew/skills/speccrew-dev-review-mobile/workflow.agentflow.xml +214 -0
  31. package/.speccrew/skills/speccrew-fd-api-contract/SKILL.md +7 -1
  32. package/.speccrew/skills/speccrew-fd-api-contract/workflow.agentflow.xml +222 -0
  33. package/.speccrew/skills/speccrew-fd-feature-analyze/SKILL.md +7 -1
  34. package/.speccrew/skills/speccrew-fd-feature-analyze/workflow.agentflow.xml +223 -0
  35. package/.speccrew/skills/speccrew-fd-feature-design/SKILL.md +7 -1
  36. package/.speccrew/skills/speccrew-fd-feature-design/workflow.agentflow.xml +322 -0
  37. package/.speccrew/skills/speccrew-get-timestamp/SKILL.md +3 -39
  38. package/.speccrew/skills/speccrew-get-timestamp/workflow.agentflow.xml +43 -0
  39. package/.speccrew/skills/speccrew-knowledge-bizs-api-analyze/SKILL.md +57 -508
  40. package/.speccrew/skills/{speccrew-knowledge-bizs-api-analyze-xml/SKILL.md → speccrew-knowledge-bizs-api-analyze/workflow.agentflow.xml} +1 -154
  41. package/.speccrew/skills/speccrew-knowledge-bizs-api-graph/SKILL.md +73 -283
  42. package/.speccrew/skills/{speccrew-knowledge-bizs-api-graph-xml/SKILL.md → speccrew-knowledge-bizs-api-graph/workflow.agentflow.xml} +0 -298
  43. package/.speccrew/skills/speccrew-knowledge-bizs-dispatch/SKILL.md +931 -801
  44. package/.speccrew/skills/{speccrew-knowledge-bizs-dispatch-xml/SKILL.md → speccrew-knowledge-bizs-dispatch/workflow.agentflow.xml} +42 -272
  45. package/.speccrew/skills/speccrew-knowledge-bizs-identify-entries/SKILL.md +263 -71
  46. package/.speccrew/skills/{speccrew-knowledge-bizs-identify-entries-xml/SKILL.md → speccrew-knowledge-bizs-identify-entries/workflow.agentflow.xml} +8 -184
  47. package/.speccrew/skills/speccrew-knowledge-bizs-init-features/SKILL.md +200 -181
  48. package/.speccrew/skills/{speccrew-knowledge-bizs-init-features-xml/SKILL.md → speccrew-knowledge-bizs-init-features/workflow.agentflow.xml} +7 -134
  49. package/.speccrew/skills/speccrew-knowledge-bizs-module-classify/SKILL.md +5 -89
  50. package/.speccrew/skills/speccrew-knowledge-bizs-module-classify/workflow.agentflow.xml +129 -0
  51. package/.speccrew/skills/speccrew-knowledge-bizs-ui-analyze/SKILL.md +454 -326
  52. package/.speccrew/skills/{speccrew-knowledge-bizs-ui-analyze-xml/SKILL.md → speccrew-knowledge-bizs-ui-analyze/workflow.agentflow.xml} +8 -128
  53. package/.speccrew/skills/speccrew-knowledge-bizs-ui-graph/SKILL.md +302 -247
  54. package/.speccrew/skills/{speccrew-knowledge-bizs-ui-graph-xml/SKILL.md → speccrew-knowledge-bizs-ui-graph/workflow.agentflow.xml} +7 -199
  55. package/.speccrew/skills/speccrew-knowledge-bizs-ui-style-extract/SKILL.md +267 -156
  56. package/.speccrew/skills/{speccrew-knowledge-bizs-ui-style-extract-xml/SKILL.md → speccrew-knowledge-bizs-ui-style-extract/workflow.agentflow.xml} +7 -151
  57. package/.speccrew/skills/speccrew-knowledge-graph-query/SKILL.md +3 -122
  58. package/.speccrew/skills/speccrew-knowledge-graph-query/workflow.agentflow.xml +106 -0
  59. package/.speccrew/skills/speccrew-knowledge-graph-write/SKILL.md +3 -80
  60. package/.speccrew/skills/speccrew-knowledge-graph-write/workflow.agentflow.xml +152 -0
  61. package/.speccrew/skills/speccrew-knowledge-module-summarize/SKILL.md +371 -265
  62. package/.speccrew/skills/{speccrew-knowledge-module-summarize-xml/SKILL.md → speccrew-knowledge-module-summarize/workflow.agentflow.xml} +7 -197
  63. package/.speccrew/skills/speccrew-knowledge-system-summarize/SKILL.md +45 -333
  64. package/.speccrew/skills/{speccrew-knowledge-system-summarize-xml/SKILL.md → speccrew-knowledge-system-summarize/workflow.agentflow.xml} +0 -177
  65. package/.speccrew/skills/speccrew-knowledge-techs-dispatch/SKILL.md +174 -727
  66. package/.speccrew/skills/{speccrew-knowledge-techs-dispatch-xml/SKILL.md → speccrew-knowledge-techs-dispatch/workflow.agentflow.xml} +10 -351
  67. package/.speccrew/skills/speccrew-knowledge-techs-generate/SKILL.md +20 -150
  68. package/.speccrew/skills/{speccrew-knowledge-techs-generate-xml/SKILL.md → speccrew-knowledge-techs-generate/workflow.agentflow.xml} +0 -169
  69. package/.speccrew/skills/speccrew-knowledge-techs-generate-conventions/SKILL.md +75 -587
  70. package/.speccrew/skills/{speccrew-knowledge-techs-generate-conventions-xml/SKILL.md → speccrew-knowledge-techs-generate-conventions/workflow.agentflow.xml} +0 -153
  71. package/.speccrew/skills/speccrew-knowledge-techs-generate-quality/SKILL.md +463 -297
  72. package/.speccrew/skills/{speccrew-knowledge-techs-generate-quality-xml/SKILL.md → speccrew-knowledge-techs-generate-quality/workflow.agentflow.xml} +0 -164
  73. package/.speccrew/skills/speccrew-knowledge-techs-generate-ui-style/SKILL.md +57 -292
  74. package/.speccrew/skills/{speccrew-knowledge-techs-generate-ui-style-xml/SKILL.md → speccrew-knowledge-techs-generate-ui-style/workflow.agentflow.xml} +2 -193
  75. package/.speccrew/skills/speccrew-knowledge-techs-index/SKILL.md +49 -335
  76. package/.speccrew/skills/{speccrew-knowledge-techs-index-xml/SKILL.md → speccrew-knowledge-techs-index/workflow.agentflow.xml} +0 -167
  77. package/.speccrew/skills/speccrew-knowledge-techs-init/SKILL.md +28 -109
  78. package/.speccrew/skills/{speccrew-knowledge-techs-init-xml/SKILL.md → speccrew-knowledge-techs-init/workflow.agentflow.xml} +0 -189
  79. package/.speccrew/skills/speccrew-knowledge-techs-ui-analyze/SKILL.md +3 -487
  80. package/.speccrew/skills/speccrew-knowledge-techs-ui-analyze/workflow.agentflow.xml +278 -0
  81. package/.speccrew/skills/speccrew-pm-knowledge-detector/SKILL.md +3 -71
  82. package/.speccrew/skills/speccrew-pm-knowledge-detector/workflow.agentflow.xml +108 -0
  83. package/.speccrew/skills/speccrew-pm-module-initializer/SKILL.md +3 -107
  84. package/.speccrew/skills/speccrew-pm-module-initializer/workflow.agentflow.xml +139 -0
  85. package/.speccrew/skills/speccrew-pm-module-matcher/SKILL.md +3 -115
  86. package/.speccrew/skills/speccrew-pm-module-matcher/workflow.agentflow.xml +146 -0
  87. package/.speccrew/skills/speccrew-pm-requirement-analysis/SKILL.md +3 -343
  88. package/.speccrew/skills/speccrew-pm-requirement-analysis/workflow.agentflow.xml +174 -0
  89. package/.speccrew/skills/speccrew-pm-requirement-assess/SKILL.md +3 -91
  90. package/.speccrew/skills/speccrew-pm-requirement-assess/workflow.agentflow.xml +173 -0
  91. package/.speccrew/skills/speccrew-pm-requirement-clarify/SKILL.md +3 -224
  92. package/.speccrew/skills/speccrew-pm-requirement-clarify/workflow.agentflow.xml +159 -0
  93. package/.speccrew/skills/speccrew-pm-requirement-model/SKILL.md +3 -275
  94. package/.speccrew/skills/speccrew-pm-requirement-model/workflow.agentflow.xml +210 -0
  95. package/.speccrew/skills/speccrew-pm-requirement-simple/SKILL.md +3 -76
  96. package/.speccrew/skills/speccrew-pm-requirement-simple/workflow.agentflow.xml +120 -0
  97. package/.speccrew/skills/speccrew-pm-sub-prd-generate/SKILL.md +7 -1
  98. package/.speccrew/skills/speccrew-pm-sub-prd-generate/workflow.agentflow.xml +218 -0
  99. package/.speccrew/skills/speccrew-sd-backend/SKILL.md +7 -1
  100. package/.speccrew/skills/speccrew-sd-backend/workflow.agentflow.xml +264 -0
  101. package/.speccrew/skills/speccrew-sd-desktop/SKILL.md +7 -1
  102. package/.speccrew/skills/speccrew-sd-desktop/workflow.agentflow.xml +288 -0
  103. package/.speccrew/skills/speccrew-sd-framework-evaluate/SKILL.md +7 -1
  104. package/.speccrew/skills/speccrew-sd-framework-evaluate/workflow.agentflow.xml +235 -0
  105. package/.speccrew/skills/speccrew-sd-frontend/SKILL.md +7 -1
  106. package/.speccrew/skills/speccrew-sd-frontend/workflow.agentflow.xml +299 -0
  107. package/.speccrew/skills/speccrew-sd-mobile/SKILL.md +7 -1
  108. package/.speccrew/skills/speccrew-sd-mobile/workflow.agentflow.xml +301 -0
  109. package/.speccrew/skills/speccrew-test-case-design/SKILL.md +165 -284
  110. package/.speccrew/skills/speccrew-test-case-design/workflow.agentflow.xml +210 -0
  111. package/.speccrew/skills/speccrew-test-code-gen/SKILL.md +204 -324
  112. package/.speccrew/skills/speccrew-test-code-gen/workflow.agentflow.xml +265 -0
  113. package/.speccrew/skills/speccrew-test-reporter/SKILL.md +205 -184
  114. package/.speccrew/skills/speccrew-test-reporter/workflow.agentflow.xml +284 -0
  115. package/.speccrew/skills/speccrew-test-runner/SKILL.md +242 -241
  116. package/.speccrew/skills/speccrew-test-runner/workflow.agentflow.xml +314 -0
  117. package/bin/cli.js +8 -1
  118. package/lib/commands/validate.js +565 -0
  119. package/package.json +1 -1
  120. package/workspace-template/docs/rules/{xml-workflow-spec.md → agentflow-spec.md} +5 -5
  121. package/workspace-template/scripts/validate-agentflow.js +637 -0
  122. package/.speccrew/agents/speccrew-team-leader-xml.md +0 -480
  123. package/.speccrew/skills/speccrew-knowledge-bizs-dispatch/STATUS-FORMATS.md +0 -99
  124. package/.speccrew/skills/speccrew-knowledge-bizs-dispatch/scripts/batch-orchestrator.js +0 -176
  125. package/.speccrew/skills/speccrew-knowledge-bizs-dispatch/scripts/get-next-batch.js +0 -150
  126. package/.speccrew/skills/speccrew-knowledge-bizs-dispatch/scripts/get-pending-features.js +0 -106
  127. package/.speccrew/skills/speccrew-knowledge-bizs-dispatch/scripts/mark-stale.js +0 -249
  128. package/.speccrew/skills/speccrew-knowledge-bizs-dispatch/scripts/merge-features.js +0 -300
  129. package/.speccrew/skills/speccrew-knowledge-bizs-dispatch/scripts/process-batch-results.js +0 -915
  130. package/.speccrew/skills/speccrew-knowledge-bizs-dispatch/scripts/update-feature-status.js +0 -226
  131. package/.speccrew/skills/speccrew-knowledge-bizs-init-features/examples/features.json +0 -34
  132. package/.speccrew/skills/speccrew-knowledge-bizs-init-features/scripts/generate-inventory.js +0 -1087
  133. package/.speccrew/skills/speccrew-knowledge-bizs-init-features/scripts/test-inventory.js +0 -26
  134. package/.speccrew/skills/speccrew-knowledge-techs-dispatch/STATUS-FORMATS.md +0 -550
@@ -0,0 +1,565 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const { getWorkspaceTemplatePath } = require('../utils');
4
+
5
+ /**
6
+ * Validate command - Validates AgentFlow XML files
7
+ * Usage: speccrew validate [path] [--format json|text] [--strict]
8
+ */
9
+
10
+ // Valid block types per specification
11
+ const VALID_BLOCK_TYPES = [
12
+ 'input',
13
+ 'output',
14
+ 'task',
15
+ 'gateway',
16
+ 'loop',
17
+ 'event',
18
+ 'error-handler',
19
+ 'checkpoint',
20
+ 'rule'
21
+ ];
22
+
23
+ // Built-in variables that don't need prior definition
24
+ const BUILTIN_VARIABLES = [
25
+ 'workspace',
26
+ 'platform',
27
+ 'timestamp',
28
+ 'workflow.id',
29
+ 'workflow.status'
30
+ ];
31
+
32
+ /**
33
+ * Get line number for a position in content
34
+ * @param {string} content - File content
35
+ * @param {number} position - Character position
36
+ * @returns {number} Line number (1-based)
37
+ */
38
+ function getLineNumber(content, position) {
39
+ const lines = content.substring(0, position).split('\n');
40
+ return lines.length;
41
+ }
42
+
43
+ /**
44
+ * Extract all block elements from XML content
45
+ * @param {string} content - XML content
46
+ * @returns {Array} Array of block objects with metadata
47
+ */
48
+ function extractBlocks(content) {
49
+ const blocks = [];
50
+ const blockRegex = /<block\s+([^>]+)>/g;
51
+ let match;
52
+
53
+ while ((match = blockRegex.exec(content)) !== null) {
54
+ const attrsString = match[1];
55
+ const startPos = match.index;
56
+ const line = getLineNumber(content, startPos);
57
+
58
+ // Parse attributes
59
+ const attrs = {};
60
+ const attrRegex = /(\w+)=["']([^"']*)["']/g;
61
+ let attrMatch;
62
+ while ((attrMatch = attrRegex.exec(attrsString)) !== null) {
63
+ attrs[attrMatch[1]] = attrMatch[2];
64
+ }
65
+
66
+ blocks.push({
67
+ line,
68
+ position: startPos,
69
+ attributes: attrs,
70
+ raw: match[0]
71
+ });
72
+ }
73
+
74
+ return blocks;
75
+ }
76
+
77
+ /**
78
+ * Extract branch elements from XML content
79
+ * @param {string} content - XML content
80
+ * @returns {Array} Array of branch objects
81
+ */
82
+ function extractBranches(content) {
83
+ const branches = [];
84
+ const branchRegex = /<branch\s+([^>]*)>/g;
85
+ let match;
86
+
87
+ while ((match = branchRegex.exec(content)) !== null) {
88
+ const attrsString = match[1];
89
+ const line = getLineNumber(content, match.index);
90
+
91
+ const attrs = {};
92
+ const attrRegex = /(\w+)=["']([^"']*)["']/g;
93
+ let attrMatch;
94
+ while ((attrMatch = attrRegex.exec(attrsString)) !== null) {
95
+ attrs[attrMatch[1]] = attrMatch[2];
96
+ }
97
+
98
+ branches.push({
99
+ line,
100
+ attributes: attrs,
101
+ raw: match[0]
102
+ });
103
+ }
104
+
105
+ return branches;
106
+ }
107
+
108
+ /**
109
+ * Check if XML has proper structure
110
+ * @param {string} content - XML content
111
+ * @returns {Array} Array of error objects
112
+ */
113
+ function validateXmlStructure(content) {
114
+ const errors = [];
115
+
116
+ // Check for workflow root element
117
+ const workflowMatch = content.match(/<workflow\s+[^>]*>/);
118
+ if (!workflowMatch) {
119
+ errors.push({
120
+ line: 1,
121
+ rule: 'root-element',
122
+ message: 'Missing root element <workflow>'
123
+ });
124
+ }
125
+
126
+ // Check for unclosed tags (basic check)
127
+ const openTags = content.match(/<block\s+[^>]*>/g) || [];
128
+ const closeTags = content.match(/<\/block>/g) || [];
129
+ const selfClosingBlocks = content.match(/<block\s+[^>]*\/>/g) || [];
130
+ const nonSelfClosingBlocks = openTags.length - selfClosingBlocks.length;
131
+
132
+ if (nonSelfClosingBlocks !== closeTags.length) {
133
+ errors.push({
134
+ line: 1,
135
+ rule: 'unclosed-tags',
136
+ message: `Block tag mismatch: ${openTags.length} opening, ${closeTags.length} closing tags`
137
+ });
138
+ }
139
+
140
+ // Check for unquoted attributes
141
+ const unquotedAttrRegex = /<block\s+[^>]*\w+=[^"'][^>]*>/g;
142
+ let match;
143
+ while ((match = unquotedAttrRegex.exec(content)) !== null) {
144
+ const line = getLineNumber(content, match.index);
145
+ errors.push({
146
+ line,
147
+ rule: 'unquoted-attribute',
148
+ message: 'Attribute values must be quoted'
149
+ });
150
+ }
151
+
152
+ return errors;
153
+ }
154
+
155
+ /**
156
+ * Validate block types
157
+ * @param {Array} blocks - Extracted blocks
158
+ * @returns {Array} Array of error objects
159
+ */
160
+ function validateBlockTypes(blocks) {
161
+ const errors = [];
162
+
163
+ for (const block of blocks) {
164
+ const type = block.attributes.type;
165
+ if (!type) {
166
+ errors.push({
167
+ line: block.line,
168
+ rule: 'missing-type',
169
+ message: 'Block is missing required "type" attribute'
170
+ });
171
+ } else if (!VALID_BLOCK_TYPES.includes(type)) {
172
+ errors.push({
173
+ line: block.line,
174
+ rule: 'invalid-type',
175
+ message: `Invalid block type "${type}". Valid types: ${VALID_BLOCK_TYPES.join(', ')}`
176
+ });
177
+ }
178
+ }
179
+
180
+ return errors;
181
+ }
182
+
183
+ /**
184
+ * Validate block ID uniqueness
185
+ * @param {Array} blocks - Extracted blocks
186
+ * @returns {Array} Array of error objects
187
+ */
188
+ function validateUniqueIds(blocks) {
189
+ const errors = [];
190
+ const idMap = new Map();
191
+
192
+ for (const block of blocks) {
193
+ const id = block.attributes.id;
194
+ if (id) {
195
+ if (idMap.has(id)) {
196
+ errors.push({
197
+ line: block.line,
198
+ rule: 'unique-id',
199
+ message: `Duplicate block id "${id}" (first defined at line ${idMap.get(id)})`
200
+ });
201
+ } else {
202
+ idMap.set(id, block.line);
203
+ }
204
+ }
205
+ }
206
+
207
+ return errors;
208
+ }
209
+
210
+ /**
211
+ * Validate required attributes for each block type
212
+ * @param {Array} blocks - Extracted blocks
213
+ * @returns {Array} Array of error objects
214
+ */
215
+ function validateRequiredAttributes(blocks) {
216
+ const errors = [];
217
+
218
+ for (const block of blocks) {
219
+ const type = block.attributes.type;
220
+ const id = block.attributes.id;
221
+
222
+ // All blocks must have id
223
+ if (!id) {
224
+ errors.push({
225
+ line: block.line,
226
+ rule: 'missing-id',
227
+ message: 'Block is missing required "id" attribute'
228
+ });
229
+ }
230
+
231
+ // Type-specific required attributes
232
+ if (type === 'task') {
233
+ if (!block.attributes.action) {
234
+ errors.push({
235
+ line: block.line,
236
+ rule: 'missing-action',
237
+ message: 'Task block is missing required "action" attribute'
238
+ });
239
+ }
240
+ if (!block.attributes.desc) {
241
+ errors.push({
242
+ line: block.line,
243
+ rule: 'missing-desc',
244
+ message: 'Task block is missing required "desc" attribute'
245
+ });
246
+ }
247
+ }
248
+
249
+ if (type === 'input' || type === 'output') {
250
+ if (!block.attributes.desc) {
251
+ errors.push({
252
+ line: block.line,
253
+ rule: 'missing-desc',
254
+ message: `${type} block is missing required "desc" attribute`
255
+ });
256
+ }
257
+ }
258
+ }
259
+
260
+ return errors;
261
+ }
262
+
263
+ /**
264
+ * Validate next references
265
+ * @param {string} content - XML content
266
+ * @param {Array} blocks - Extracted blocks
267
+ * @returns {Array} Array of error objects
268
+ */
269
+ function validateNextReferences(content, blocks) {
270
+ const errors = [];
271
+ const validIds = new Set(blocks.map(b => b.attributes.id).filter(Boolean));
272
+
273
+ // Check field name="next" references
274
+ const fieldRegex = /<field\s+name=["']next["'][^>]*>([^<]*)<\/field>/g;
275
+ let match;
276
+
277
+ while ((match = fieldRegex.exec(content)) !== null) {
278
+ const nextId = match[1].trim();
279
+ const line = getLineNumber(content, match.index);
280
+
281
+ if (nextId && !validIds.has(nextId)) {
282
+ errors.push({
283
+ line,
284
+ rule: 'invalid-next-ref',
285
+ message: `Reference to undefined block id "${nextId}"`
286
+ });
287
+ }
288
+ }
289
+
290
+ return errors;
291
+ }
292
+
293
+ /**
294
+ * Validate variable references
295
+ * @param {string} content - XML content
296
+ * @param {Array} blocks - Extracted blocks
297
+ * @returns {Array} Array of warning objects
298
+ */
299
+ function validateVariableRefs(content, blocks) {
300
+ const warnings = [];
301
+ const definedVars = new Set(BUILTIN_VARIABLES);
302
+
303
+ // Collect output variables from blocks
304
+ for (const block of blocks) {
305
+ const blockEndPos = content.indexOf('</block>', block.position);
306
+ const blockContent = content.substring(block.position, blockEndPos > 0 ? blockEndPos : block.position + 500);
307
+
308
+ // Check for output var attribute
309
+ const outputMatch = blockContent.match(/<field[^>]*\s+var=["']([^"']+)["']/);
310
+ if (outputMatch) {
311
+ definedVars.add(outputMatch[1]);
312
+ }
313
+
314
+ // Check for output field in task blocks
315
+ const outputFieldMatch = blockContent.match(/<field\s+name=["']output["'][^>]*\s+var=["']([^"']+)["']/);
316
+ if (outputFieldMatch) {
317
+ definedVars.add(outputFieldMatch[1]);
318
+ }
319
+
320
+ // Loop variables
321
+ if (block.attributes.type === 'loop') {
322
+ const asAttr = block.attributes.as;
323
+ if (asAttr) {
324
+ definedVars.add(asAttr);
325
+ }
326
+ }
327
+ }
328
+
329
+ // Check all variable references
330
+ const varRegex = /\$\{([^}]+)\}/g;
331
+ let match;
332
+
333
+ while ((match = varRegex.exec(content)) !== null) {
334
+ const fullVar = match[1];
335
+ const baseVar = fullVar.split('.')[0].split('[')[0];
336
+ const line = getLineNumber(content, match.index);
337
+
338
+ if (!definedVars.has(baseVar) && !BUILTIN_VARIABLES.includes(baseVar)) {
339
+ const parentVar = fullVar.split('.')[0];
340
+ if (!definedVars.has(parentVar)) {
341
+ warnings.push({
342
+ line,
343
+ rule: 'var-ref',
344
+ message: `Variable "${baseVar}" may not be defined before use`
345
+ });
346
+ }
347
+ }
348
+ }
349
+
350
+ return warnings;
351
+ }
352
+
353
+ /**
354
+ * Validate a single AgentFlow XML file
355
+ * @param {string} filePath - Path to the XML file
356
+ * @returns {Object} Validation result
357
+ */
358
+ function validateFile(filePath) {
359
+ const result = {
360
+ file: filePath,
361
+ errors: [],
362
+ warnings: [],
363
+ summary: {
364
+ blocks: 0,
365
+ errors: 0,
366
+ warnings: 0
367
+ }
368
+ };
369
+
370
+ try {
371
+ const content = fs.readFileSync(filePath, 'utf8');
372
+
373
+ // Extract all blocks
374
+ const blocks = extractBlocks(content);
375
+ result.summary.blocks = blocks.length;
376
+
377
+ // Run all validations
378
+ result.errors.push(...validateXmlStructure(content));
379
+ result.errors.push(...validateBlockTypes(blocks));
380
+ result.errors.push(...validateUniqueIds(blocks));
381
+ result.errors.push(...validateRequiredAttributes(blocks));
382
+ result.errors.push(...validateNextReferences(content, blocks));
383
+ result.warnings.push(...validateVariableRefs(content, blocks));
384
+
385
+ // Update summary
386
+ result.summary.errors = result.errors.length;
387
+ result.summary.warnings = result.warnings.length;
388
+
389
+ } catch (error) {
390
+ result.errors.push({
391
+ line: 0,
392
+ rule: 'file-error',
393
+ message: `Failed to read file: ${error.message}`
394
+ });
395
+ result.summary.errors = 1;
396
+ }
397
+
398
+ return result;
399
+ }
400
+
401
+ /**
402
+ * Find all .agentflow.xml files in a directory recursively
403
+ * @param {string} dir - Directory to search
404
+ * @returns {Array} Array of file paths
405
+ */
406
+ function findAgentFlowFiles(dir) {
407
+ const files = [];
408
+
409
+ function scan(directory) {
410
+ if (!fs.existsSync(directory)) return;
411
+
412
+ const entries = fs.readdirSync(directory, { withFileTypes: true });
413
+ for (const entry of entries) {
414
+ const fullPath = path.join(directory, entry.name);
415
+ if (entry.isDirectory()) {
416
+ scan(fullPath);
417
+ } else if (entry.name.endsWith('.agentflow.xml')) {
418
+ files.push(fullPath);
419
+ }
420
+ }
421
+ }
422
+
423
+ scan(dir);
424
+ return files;
425
+ }
426
+
427
+ /**
428
+ * Parse command arguments
429
+ * @param {Array} args - Command arguments
430
+ * @returns {Object} Parsed options
431
+ */
432
+ function parseOptions(args) {
433
+ const options = {
434
+ format: 'text',
435
+ strict: false
436
+ };
437
+
438
+ for (let i = 0; i < args.length; i++) {
439
+ const arg = args[i];
440
+ if (arg === '--format' && i + 1 < args.length) {
441
+ options.format = args[i + 1];
442
+ i++;
443
+ } else if (arg === '--strict') {
444
+ options.strict = true;
445
+ }
446
+ }
447
+
448
+ return options;
449
+ }
450
+
451
+ /**
452
+ * Format output as text
453
+ * @param {Array} results - Validation results
454
+ * @param {boolean} strict - Treat warnings as errors
455
+ * @returns {string} Formatted text output
456
+ */
457
+ function formatTextOutput(results, strict) {
458
+ const lines = [];
459
+ let totalErrors = 0;
460
+ let totalWarnings = 0;
461
+ let totalBlocks = 0;
462
+
463
+ for (const result of results) {
464
+ lines.push('');
465
+ lines.push(result.file);
466
+
467
+ if (result.errors.length === 0 && result.warnings.length === 0) {
468
+ lines.push(' PASS No issues found');
469
+ } else {
470
+ for (const error of result.errors) {
471
+ lines.push(` ERROR Line ${error.line}: [${error.rule}] ${error.message}`);
472
+ }
473
+ for (const warning of result.warnings) {
474
+ const label = strict ? 'ERROR' : 'WARN';
475
+ lines.push(` ${label} Line ${warning.line}: [${warning.rule}] ${warning.message}`);
476
+ }
477
+ }
478
+
479
+ totalErrors += result.summary.errors;
480
+ totalWarnings += result.summary.warnings;
481
+ totalBlocks += result.summary.blocks;
482
+ }
483
+
484
+ lines.push('');
485
+ lines.push('─'.repeat(50));
486
+ lines.push(`Summary: ${results.length} file(s), ${totalBlocks} block(s), ${totalErrors} error(s), ${totalWarnings} warning(s)`);
487
+
488
+ if (strict) {
489
+ const totalIssues = totalErrors + totalWarnings;
490
+ lines.push(`Strict mode: ${totalIssues} total issue(s)`);
491
+ }
492
+
493
+ return lines.join('\n');
494
+ }
495
+
496
+ /**
497
+ * Main run function
498
+ * @param {string} projectRoot - Project root directory
499
+ * @param {Array} args - Command arguments
500
+ */
501
+ function run(projectRoot, args) {
502
+ console.log('SpecCrew AgentFlow Validator\n');
503
+
504
+ const options = parseOptions(args);
505
+
506
+ // Determine target path
507
+ let targetPath = null;
508
+ for (const arg of args) {
509
+ if (!arg.startsWith('--')) {
510
+ targetPath = arg;
511
+ break;
512
+ }
513
+ }
514
+
515
+ // Default to speccrew-workspace
516
+ if (!targetPath) {
517
+ targetPath = path.join(projectRoot, 'speccrew-workspace');
518
+ } else {
519
+ targetPath = path.resolve(projectRoot, targetPath);
520
+ }
521
+
522
+ // Check if target exists
523
+ if (!fs.existsSync(targetPath)) {
524
+ console.error(`Error: Path not found: ${targetPath}`);
525
+ process.exit(1);
526
+ }
527
+
528
+ // Collect files to validate
529
+ let files = [];
530
+ const stats = fs.statSync(targetPath);
531
+ if (stats.isDirectory()) {
532
+ files = findAgentFlowFiles(targetPath);
533
+ } else if (targetPath.endsWith('.agentflow.xml')) {
534
+ files = [targetPath];
535
+ }
536
+
537
+ if (files.length === 0) {
538
+ console.log(`No .agentflow.xml files found in ${targetPath}`);
539
+ return true;
540
+ }
541
+
542
+ // Validate all files
543
+ const results = files.map(file => validateFile(file));
544
+
545
+ // Calculate totals
546
+ let totalErrors = 0;
547
+ let totalWarnings = 0;
548
+ for (const result of results) {
549
+ totalErrors += result.summary.errors;
550
+ totalWarnings += result.summary.warnings;
551
+ }
552
+
553
+ // Output results
554
+ if (options.format === 'json') {
555
+ console.log(JSON.stringify(results, null, 2));
556
+ } else {
557
+ console.log(formatTextOutput(results, options.strict));
558
+ }
559
+
560
+ // Return success/failure
561
+ const hasErrors = totalErrors > 0 || (options.strict && totalWarnings > 0);
562
+ return !hasErrors;
563
+ }
564
+
565
+ module.exports = { run };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "speccrew",
3
- "version": "0.6.69",
3
+ "version": "0.7.0",
4
4
  "description": "Spec-Driven Development toolkit for AI-powered IDEs",
5
5
  "author": "charlesmu99",
6
6
  "repository": {
@@ -1,6 +1,6 @@
1
- # XML Workflow Specification
1
+ # AgentFlow Specification
2
2
 
3
- > This document defines the XML Block workflow format used in SpecCrew skill and agent definitions.
3
+ > This document defines the AgentFlow XML Block workflow format used in SpecCrew skill and agent definitions.
4
4
  > **MANDATORY**: Any agent or worker that encounters XML `<workflow>` blocks MUST read and understand this specification before execution.
5
5
 
6
6
  ## Execution Model
@@ -346,7 +346,7 @@ Dispatch skills (naming pattern: `*-dispatch-xml`) are **orchestration playbooks
346
346
  When Team Leader loads a dispatch skill via `action="run-skill"`:
347
347
 
348
348
  1. **Load**: Team Leader invokes the Skill tool with the dispatch skill name (e.g., `speccrew-knowledge-bizs-dispatch-xml`)
349
- 2. **Parse**: Read and understand the complete XML workflow in the loaded SKILL.md
349
+ 2. **Parse**: Read and understand the complete AgentFlow XML workflow in the loaded SKILL.md
350
350
  3. **Execute block-by-block**: Walk through each Stage's blocks in document order, mapping each `action` attribute to the correct IDE tool:
351
351
 
352
352
  | Block Action | What Team Leader MUST Do |
@@ -484,7 +484,7 @@ Team Leader executes:
484
484
 
485
485
  ### Strict Block Adherence
486
486
 
487
- When executing an XML workflow, the Agent MUST strictly follow the block sequence defined in the workflow:
487
+ When executing an AgentFlow XML workflow, the Agent MUST strictly follow the block sequence defined in the workflow:
488
488
 
489
489
  1. **Only perform actions defined by blocks** — Every action the Agent takes must correspond to a specific block in the workflow. The Agent MUST NOT perform any actions not defined by the blocks.
490
490
  2. **Prohibited extra actions** include but are not limited to:
@@ -509,5 +509,5 @@ Before executing each `<block>`, the agent MUST announce the block being execute
509
509
  3. For `<block type="loop">` blocks, announce the loop entry with iteration context (e.g., "Loop [S2-L2] — Iterating over ${batch}, 5 items, parallel=true").
510
510
  4. For `<block type="checkpoint">` blocks, announce the checkpoint and its validation result.
511
511
  5. For `<block type="rule">` blocks, announce the rule being applied.
512
- 6. This protocol applies to ALL agents executing XML workflows — both orchestrating agents (Team Leader) and worker agents.
512
+ 6. This protocol applies to ALL agents executing AgentFlow XML workflows — both orchestrating agents (Team Leader) and worker agents.
513
513
  7. Nested blocks inside loops should also be announced for each iteration.