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
@@ -1,176 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * Batch Orchestrator - Main control script for Stage 2 loop.
5
- *
6
- * This script simplifies the Stage 2 loop in SKILL.md by merging
7
- * "get batch" and "process previous batch results" steps into a single call.
8
- *
9
- * Modes:
10
- * 1. get-batch: Get the next batch of features to process
11
- * 2. process-results: Process the previous batch results
12
- */
13
-
14
- const { execFileSync } = require('child_process');
15
- const path = require('path');
16
-
17
- // Parse command line arguments
18
- function parseArgs() {
19
- const args = process.argv.slice(2);
20
- const result = {
21
- mode: null,
22
- syncStatePath: null,
23
- batchSize: 5,
24
- graphRoot: null,
25
- graphWriteScript: null,
26
- platformId: null
27
- };
28
-
29
- // First non-option argument is the mode
30
- let modeFound = false;
31
-
32
- for (let i = 0; i < args.length; i++) {
33
- const arg = args[i];
34
-
35
- // Skip if it's a flag
36
- if (arg.startsWith('--')) {
37
- switch (arg) {
38
- case '--syncStatePath':
39
- case '-SyncStatePath':
40
- result.syncStatePath = args[++i];
41
- break;
42
- case '--batchSize':
43
- case '-BatchSize':
44
- result.batchSize = parseInt(args[++i], 10) || 5;
45
- break;
46
- case '--graphRoot':
47
- case '-GraphRoot':
48
- result.graphRoot = args[++i];
49
- break;
50
- case '--graphWriteScript':
51
- case '-GraphWriteScript':
52
- result.graphWriteScript = args[++i];
53
- break;
54
- case '--platformId':
55
- case '-PlatformId':
56
- result.platformId = args[++i];
57
- break;
58
- }
59
- } else if (!modeFound) {
60
- // First non-flag argument is the mode
61
- result.mode = arg;
62
- modeFound = true;
63
- }
64
- }
65
-
66
- return result;
67
- }
68
-
69
- // Get the path to a script in the same directory
70
- function getScriptPath(scriptName) {
71
- return path.join(__dirname, scriptName);
72
- }
73
-
74
- // Mode 1: Get the next batch of features
75
- function getBatch(args) {
76
- if (!args.syncStatePath) {
77
- throw new Error('--syncStatePath is required for get-batch mode');
78
- }
79
-
80
- const getNextBatchScript = getScriptPath('get-next-batch.js');
81
-
82
- const commandArgs = [
83
- getNextBatchScript,
84
- '--syncStatePath', args.syncStatePath,
85
- '--batchSize', String(args.batchSize)
86
- ];
87
-
88
- // Execute get-next-batch.js and capture output
89
- const output = execFileSync('node', commandArgs, { encoding: 'utf8' });
90
-
91
- // Parse the output (JSON array of features)
92
- const batch = JSON.parse(output);
93
-
94
- // Determine the response based on whether there are features
95
- if (batch.length === 0) {
96
- return {
97
- action: 'done',
98
- message: 'All features have been processed'
99
- };
100
- } else {
101
- return {
102
- action: 'process',
103
- batch: batch,
104
- batchSize: batch.length,
105
- iteration: null // Could be enhanced with state tracking
106
- };
107
- }
108
- }
109
-
110
- // Mode 2: Process the previous batch results
111
- function processResults(args) {
112
- if (!args.syncStatePath) {
113
- throw new Error('--syncStatePath is required for process-results mode');
114
- }
115
- if (!args.graphRoot) {
116
- throw new Error('--graphRoot is required for process-results mode');
117
- }
118
- if (!args.graphWriteScript) {
119
- throw new Error('--graphWriteScript is required for process-results mode');
120
- }
121
- if (!args.platformId) {
122
- throw new Error('--platformId is required for process-results mode');
123
- }
124
-
125
- const processBatchResultsScript = getScriptPath('process-batch-results.js');
126
-
127
- const commandArgs = [
128
- processBatchResultsScript,
129
- '--syncStatePath', args.syncStatePath,
130
- '--graphRoot', args.graphRoot,
131
- '--graphWriteScript', args.graphWriteScript,
132
- '--platformId', args.platformId
133
- ];
134
-
135
- // Execute process-batch-results.js and capture output
136
- const output = execFileSync('node', commandArgs, { encoding: 'utf8' });
137
-
138
- // Parse and return the output (JSON result)
139
- return JSON.parse(output);
140
- }
141
-
142
- function main() {
143
- try {
144
- const args = parseArgs();
145
-
146
- if (!args.mode) {
147
- console.error('Error: Mode is required. Use "get-batch" or "process-results"');
148
- console.error('Usage:');
149
- console.error(' node batch-orchestrator.js get-batch --syncStatePath <path> [--batchSize 5]');
150
- console.error(' node batch-orchestrator.js process-results --syncStatePath <path> --graphRoot <path> --graphWriteScript <path> --platformId <id>');
151
- process.exit(1);
152
- }
153
-
154
- let result;
155
-
156
- switch (args.mode) {
157
- case 'get-batch':
158
- result = getBatch(args);
159
- break;
160
- case 'process-results':
161
- result = processResults(args);
162
- break;
163
- default:
164
- throw new Error(`Unknown mode: ${args.mode}. Use "get-batch" or "process-results"`);
165
- }
166
-
167
- // Output result as JSON
168
- console.log(JSON.stringify(result, null, 2));
169
-
170
- } catch (error) {
171
- console.error(`Error: ${error.message}`);
172
- process.exit(1);
173
- }
174
- }
175
-
176
- main();
@@ -1,150 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * Get the next batch of features to process from features-*.json files.
5
- *
6
- * Scans the sync-state directory for all features-*.json files and extracts
7
- * features where analyzed=false or analyzed field is missing.
8
- * Additionally excludes features that have a corresponding .done.json file in the
9
- * completed/ directory (indicating Worker has finished but results not yet processed).
10
- * Returns a JSON array limited to BatchSize items.
11
- */
12
-
13
- const fs = require('fs');
14
- const path = require('path');
15
-
16
- // Parse command line arguments
17
- function parseArgs() {
18
- const args = process.argv.slice(2);
19
- const result = {
20
- syncStatePath: null,
21
- batchSize: 5
22
- };
23
-
24
- for (let i = 0; i < args.length; i++) {
25
- const arg = args[i];
26
- switch (arg) {
27
- case '--syncStatePath':
28
- case '-SyncStatePath':
29
- result.syncStatePath = args[++i];
30
- break;
31
- case '--batchSize':
32
- case '-BatchSize':
33
- result.batchSize = parseInt(args[++i], 10) || 5;
34
- break;
35
- }
36
- }
37
-
38
- return result;
39
- }
40
-
41
- function main() {
42
- try {
43
- const args = parseArgs();
44
-
45
- if (!args.syncStatePath) {
46
- console.error('Error: --syncStatePath is required');
47
- process.exit(1);
48
- }
49
-
50
- // Resolve full path
51
- const fullPath = path.resolve(args.syncStatePath);
52
- if (!fs.existsSync(fullPath)) {
53
- console.error(`SyncStatePath not found: ${args.syncStatePath}`);
54
- process.exit(1);
55
- }
56
-
57
- // Path to completed directory
58
- const completedDir = path.join(fullPath, 'completed');
59
-
60
- // Build set of completed feature fileNames (for fast lookup)
61
- const completedFeatureFileNames = new Set();
62
- const completedFeatureIds = new Set();
63
- if (fs.existsSync(completedDir) && fs.statSync(completedDir).isDirectory()) {
64
- const doneFiles = fs.readdirSync(completedDir).filter(f => f.endsWith('.done.json'));
65
- for (const doneFile of doneFiles) {
66
- // Read .done.json file content to extract fileName or featureId
67
- const doneFilePath = path.join(completedDir, doneFile);
68
- try {
69
- const doneRawContent = fs.readFileSync(doneFilePath, 'utf8');
70
- const doneContent = JSON.parse(doneRawContent);
71
- // Use fileName field if available, fallback to featureId
72
- if (doneContent.fileName) {
73
- completedFeatureFileNames.add(doneContent.fileName);
74
- }
75
- if (doneContent.featureId) {
76
- completedFeatureIds.add(doneContent.featureId);
77
- }
78
- } catch (error) {
79
- console.warn(`Warning: Failed to read .done.json file ${doneFile}: ${error.message}`);
80
- // Fallback: use filename without extension (legacy behavior)
81
- const baseName = path.basename(doneFile, '.done.json');
82
- completedFeatureIds.add(baseName);
83
- }
84
- }
85
- }
86
-
87
- // Find all features-*.json files
88
- const featureFiles = fs.readdirSync(fullPath).filter(f => {
89
- return f.startsWith('features-') && f.endsWith('.json') && fs.statSync(path.join(fullPath, f)).isFile();
90
- });
91
-
92
- const candidateFeatures = [];
93
-
94
- for (const fileName of featureFiles) {
95
- const filePath = path.join(fullPath, fileName);
96
- const rawContent = fs.readFileSync(filePath, 'utf8');
97
- const content = JSON.parse(rawContent);
98
-
99
- const platformType = content.platformType;
100
- const platformSubtype = content.platformSubtype || null;
101
- const sourcePath = content.sourcePath;
102
- const techStack = content.techStack;
103
- const platformName = content.platformName;
104
-
105
- if (!content.features || !Array.isArray(content.features)) {
106
- continue;
107
- }
108
-
109
- for (const feature of content.features) {
110
- // Check if feature needs analysis: analyzed=false or analyzed field missing
111
- const needsAnalysis = !('analyzed' in feature) || feature.analyzed === false;
112
-
113
- if (!needsAnalysis) {
114
- continue;
115
- }
116
-
117
- // Use feature's id and fileName fields
118
- const featureId = feature.id;
119
- const featureFileName = feature.fileName;
120
-
121
- // Skip if already completed (has .done.json file)
122
- // Check both fileName (new format) and featureId (legacy format)
123
- if (completedFeatureFileNames.has(featureFileName) || completedFeatureIds.has(featureId)) {
124
- continue;
125
- }
126
-
127
- candidateFeatures.push({
128
- sourceFile: fileName,
129
- platformName: platformName,
130
- platformType: platformType,
131
- platformSubtype: platformSubtype,
132
- sourcePath: sourcePath,
133
- techStack: techStack,
134
- feature: feature
135
- });
136
- }
137
- }
138
-
139
- // Take only the first BatchSize items
140
- const batchFeatures = candidateFeatures.slice(0, args.batchSize);
141
-
142
- // Output as JSON
143
- console.log(JSON.stringify(batchFeatures, null, 2));
144
- } catch (error) {
145
- console.error(`Error: ${error.message}`);
146
- process.exit(1);
147
- }
148
- }
149
-
150
- main();
@@ -1,106 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * Get all pending features from features-*.json files.
5
- *
6
- * Scans the sync-state directory for all features-*.json files and extracts
7
- * features where status='pending' or status field is missing (backward compatibility).
8
- * Returns a flat JSON array with platform metadata attached to each feature for easy dispatch.
9
- */
10
-
11
- const fs = require('fs');
12
- const path = require('path');
13
-
14
- // Parse command line arguments
15
- function parseArgs() {
16
- const args = process.argv.slice(2);
17
- const result = {
18
- syncStatePath: null,
19
- platformId: null
20
- };
21
-
22
- for (let i = 0; i < args.length; i++) {
23
- const arg = args[i];
24
- switch (arg) {
25
- case '--syncStatePath':
26
- case '-SyncStatePath':
27
- result.syncStatePath = args[++i];
28
- break;
29
- case '--platformId':
30
- case '-platformId':
31
- result.platformId = args[++i];
32
- break;
33
- }
34
- }
35
-
36
- return result;
37
- }
38
-
39
- function main() {
40
- try {
41
- const args = parseArgs();
42
-
43
- if (!args.syncStatePath) {
44
- console.error('Error: --syncStatePath is required');
45
- process.exit(1);
46
- }
47
-
48
- // Resolve full path
49
- const fullPath = path.resolve(args.syncStatePath);
50
- if (!fs.existsSync(fullPath)) {
51
- console.error(`SyncStatePath not found: ${args.syncStatePath}`);
52
- process.exit(1);
53
- }
54
-
55
- // Find features-*.json files, filtered by platformId if specified
56
- let featureFiles = fs.readdirSync(fullPath).filter(f => {
57
- return f.startsWith('features-') && f.endsWith('.json') && fs.statSync(path.join(fullPath, f)).isFile();
58
- });
59
-
60
- // If platformId is specified, filter to only the matching file
61
- if (args.platformId) {
62
- const targetFile = `features-${args.platformId}.json`;
63
- featureFiles = featureFiles.filter(f => f === targetFile);
64
- }
65
-
66
- const pendingFeatures = [];
67
-
68
- for (const fileName of featureFiles) {
69
- const filePath = path.join(fullPath, fileName);
70
- const rawContent = fs.readFileSync(filePath, 'utf8');
71
- const content = JSON.parse(rawContent);
72
-
73
- const platformType = content.platformType;
74
- const platformSubtype = content.platformSubtype || null;
75
- const sourcePath = content.sourcePath;
76
- const techStack = content.techStack;
77
- const platformName = content.platformName;
78
-
79
- if (!content.features || !Array.isArray(content.features)) {
80
- continue;
81
- }
82
-
83
- for (const feature of content.features) {
84
- if (!('status' in feature) || feature.status === 'pending') {
85
- pendingFeatures.push({
86
- sourceFile: fileName,
87
- platformName: platformName,
88
- platformType: platformType,
89
- platformSubtype: platformSubtype,
90
- sourcePath: sourcePath,
91
- techStack: techStack,
92
- feature: feature
93
- });
94
- }
95
- }
96
- }
97
-
98
- // Output as JSON
99
- console.log(JSON.stringify(pendingFeatures, null, 2));
100
- } catch (error) {
101
- console.error(`Error: ${error.message}`);
102
- process.exit(1);
103
- }
104
- }
105
-
106
- main();
@@ -1,249 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * Mark features as stale (pending re-analysis) for specified paths.
5
- *
6
- * Scans all features-*.json files in the SyncStatePath directory and marks
7
- * features as stale when their sourcePath matches one of the provided paths.
8
- * Matching supports exact path match or directory prefix match (when a
9
- * directory path is provided, all features under that directory are marked).
10
- *
11
- * Resets the following fields for matched features:
12
- * - analyzed = false
13
- * - status = "pending" (if the field exists)
14
- * - startedAt = null
15
- * - completedAt = null
16
- * - analysisNotes = null
17
- */
18
-
19
- const fs = require('fs');
20
- const path = require('path');
21
-
22
- // Parse command line arguments
23
- function parseArgs() {
24
- const args = process.argv.slice(2);
25
- const result = {
26
- syncStatePath: null,
27
- paths: []
28
- };
29
-
30
- for (let i = 0; i < args.length; i++) {
31
- const arg = args[i];
32
- switch (arg) {
33
- case '--syncStatePath':
34
- case '-SyncStatePath':
35
- result.syncStatePath = args[++i];
36
- break;
37
- case '--paths':
38
- case '-Paths':
39
- // Support comma-separated paths or multiple --paths arguments
40
- const pathsValue = args[++i];
41
- if (pathsValue) {
42
- result.paths.push(...pathsValue.split(',').map(p => p.trim()).filter(p => p));
43
- }
44
- break;
45
- }
46
- }
47
-
48
- return result;
49
- }
50
-
51
- // Helper function to safely set property on object
52
- function setFeatureProperty(obj, propertyName, value) {
53
- obj[propertyName] = value;
54
- }
55
-
56
- // Helper function to safely remove property from object
57
- function removeFeatureProperty(obj, propertyName) {
58
- if (propertyName in obj) {
59
- delete obj[propertyName];
60
- }
61
- }
62
-
63
- // Helper function to normalize path for comparison
64
- // - Convert backslashes to forward slashes
65
- // - Remove trailing slashes
66
- // - Convert to lowercase for case-insensitive comparison (Windows)
67
- function normalizePath(inputPath) {
68
- if (!inputPath) {
69
- return '';
70
- }
71
- let normalized = inputPath.replace(/\\/g, '/');
72
- normalized = normalized.replace(/\/$/, '');
73
- return normalized.toLowerCase();
74
- }
75
-
76
- function main() {
77
- try {
78
- const args = parseArgs();
79
-
80
- if (!args.syncStatePath) {
81
- console.error('Error: --syncStatePath is required');
82
- process.exit(1);
83
- }
84
-
85
- if (args.paths.length === 0) {
86
- console.error('Error: --paths is required');
87
- process.exit(1);
88
- }
89
-
90
- // Validate SyncStatePath exists
91
- const resolvedSyncStatePath = path.resolve(args.syncStatePath);
92
- if (!fs.existsSync(resolvedSyncStatePath)) {
93
- console.error(`SyncStatePath not found: ${args.syncStatePath}`);
94
- process.exit(1);
95
- }
96
-
97
- // Normalize input paths for matching
98
- const normalizedInputPaths = args.paths.map(p => normalizePath(p));
99
-
100
- // Find all features-*.json files
101
- const featureFiles = fs.readdirSync(resolvedSyncStatePath).filter(f => {
102
- return f.startsWith('features-') && f.endsWith('.json') && fs.statSync(path.join(resolvedSyncStatePath, f)).isFile();
103
- });
104
-
105
- if (featureFiles.length === 0) {
106
- // No feature files found, output empty result
107
- const result = {
108
- totalAffected: 0,
109
- features: []
110
- };
111
- console.log(JSON.stringify(result, null, 2));
112
- process.exit(0);
113
- }
114
-
115
- const affectedFeatures = [];
116
-
117
- for (const fileName of featureFiles) {
118
- const filePath = path.join(resolvedSyncStatePath, fileName);
119
- const lockPath = `${filePath}.lock`;
120
- const maxRetries = 30;
121
- let retryCount = 0;
122
- let lockAcquired = false;
123
-
124
- // Acquire file lock
125
- while (!lockAcquired && retryCount < maxRetries) {
126
- try {
127
- // Try to create lock file exclusively
128
- const fd = fs.openSync(lockPath, 'wx');
129
- fs.closeSync(fd);
130
- lockAcquired = true;
131
- } catch (error) {
132
- retryCount++;
133
- if (retryCount >= maxRetries) {
134
- const errorMsg = `Failed to acquire file lock for '${fileName}' after ${maxRetries} attempts (waited ${maxRetries} seconds). The file may be locked by another process.`;
135
- console.warn(errorMsg);
136
- // Continue to next file instead of exiting
137
- break;
138
- }
139
- if (retryCount % 5 === 0) {
140
- console.warn(`Waiting for file lock on '${fileName}'... (attempt ${retryCount} of ${maxRetries})`);
141
- }
142
- // Wait 1 second before retry
143
- const start = Date.now();
144
- while (Date.now() - start < 1000) {
145
- // Busy wait
146
- }
147
- }
148
- }
149
-
150
- // Skip this file if lock could not be acquired
151
- if (!lockAcquired) {
152
- continue;
153
- }
154
-
155
- try {
156
- // Read the JSON file
157
- const rawContent = fs.readFileSync(filePath, 'utf8');
158
- const content = JSON.parse(rawContent);
159
- let fileModified = false;
160
-
161
- if (content.features && Array.isArray(content.features)) {
162
- for (let i = 0; i < content.features.length; i++) {
163
- const feature = content.features[i];
164
- const featureSourcePath = feature.sourcePath;
165
-
166
- if (!featureSourcePath) {
167
- continue;
168
- }
169
-
170
- const normalizedFeaturePath = normalizePath(featureSourcePath);
171
-
172
- // Check if any input path matches this feature
173
- let matched = false;
174
- for (const inputPath of normalizedInputPaths) {
175
- // Exact match or directory prefix match
176
- if (normalizedFeaturePath === inputPath) {
177
- matched = true;
178
- break;
179
- }
180
- // Directory prefix match: input path is a directory and feature path starts with it
181
- if (normalizedFeaturePath.startsWith(`${inputPath}/`)) {
182
- matched = true;
183
- break;
184
- }
185
- }
186
-
187
- if (matched) {
188
- // Reset fields
189
- setFeatureProperty(feature, 'analyzed', false);
190
-
191
- // Only set status if the field already exists
192
- if ('status' in feature) {
193
- setFeatureProperty(feature, 'status', 'pending');
194
- }
195
-
196
- // Clear timestamp fields
197
- removeFeatureProperty(feature, 'startedAt');
198
- removeFeatureProperty(feature, 'completedAt');
199
- removeFeatureProperty(feature, 'analysisNotes');
200
-
201
- content.features[i] = feature;
202
- fileModified = true;
203
-
204
- // Add to affected list
205
- affectedFeatures.push({
206
- sourcePath: featureSourcePath,
207
- module: feature.module,
208
- sourceFile: fileName
209
- });
210
- }
211
- }
212
- }
213
-
214
- // Update counters and write back if modified
215
- if (fileModified) {
216
- content.analyzedCount = content.features.filter(f => f.analyzed === true).length;
217
- content.pendingCount = content.features.filter(f => f.analyzed === false).length;
218
-
219
- // Atomic write: temp file + rename
220
- const tempFile = `${filePath}.tmp`;
221
- fs.writeFileSync(tempFile, JSON.stringify(content, null, 2), 'utf8');
222
- fs.renameSync(tempFile, filePath);
223
- }
224
- } finally {
225
- // Release lock - remove lock file
226
- try {
227
- if (fs.existsSync(lockPath)) {
228
- fs.unlinkSync(lockPath);
229
- }
230
- } catch (e) {
231
- // Ignore cleanup errors
232
- }
233
- }
234
- }
235
-
236
- // Output result as JSON
237
- const result = {
238
- totalAffected: affectedFeatures.length,
239
- features: affectedFeatures
240
- };
241
-
242
- console.log(JSON.stringify(result, null, 2));
243
- } catch (error) {
244
- console.error(`Error: ${error.message}`);
245
- process.exit(1);
246
- }
247
- }
248
-
249
- main();