speccrew 0.6.67 → 0.6.69
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/.speccrew/skills/speccrew-knowledge-bizs-init-features/scripts/generate-inventory.js
CHANGED
|
@@ -66,6 +66,16 @@
|
|
|
66
66
|
const fs = require('fs');
|
|
67
67
|
const path = require('path');
|
|
68
68
|
|
|
69
|
+
// Platform type to tech-stack-mappings category mapping
|
|
70
|
+
const PLATFORM_TYPE_TO_CATEGORY = {
|
|
71
|
+
'frontend': 'web',
|
|
72
|
+
'web': 'web',
|
|
73
|
+
'backend': 'backend',
|
|
74
|
+
'mobile': 'mobile',
|
|
75
|
+
'desktop': 'desktop',
|
|
76
|
+
'api': 'api'
|
|
77
|
+
};
|
|
78
|
+
|
|
69
79
|
/**
|
|
70
80
|
* Parse array parameter that supports both JSON format and comma-separated format.
|
|
71
81
|
* JSON format: '["vue","typescript"]'
|
|
@@ -264,12 +274,15 @@ function loadTechStackConfig(platformType, framework, projectRoot) {
|
|
|
264
274
|
const configContent = fs.readFileSync(configPath, 'utf8');
|
|
265
275
|
const config = JSON.parse(configContent);
|
|
266
276
|
|
|
277
|
+
// Map platformType to tech-stack-mappings category
|
|
278
|
+
const techCategory = PLATFORM_TYPE_TO_CATEGORY[platformType] || platformType;
|
|
279
|
+
|
|
267
280
|
// Try exact match first
|
|
268
281
|
let techConfig = null;
|
|
269
282
|
if (config.tech_stacks &&
|
|
270
|
-
config.tech_stacks[
|
|
271
|
-
config.tech_stacks[
|
|
272
|
-
techConfig = config.tech_stacks[
|
|
283
|
+
config.tech_stacks[techCategory] &&
|
|
284
|
+
config.tech_stacks[techCategory][framework]) {
|
|
285
|
+
techConfig = config.tech_stacks[techCategory][framework];
|
|
273
286
|
}
|
|
274
287
|
|
|
275
288
|
// If not found, try normalized identifier (remove language prefix)
|
|
@@ -277,9 +290,9 @@ function loadTechStackConfig(platformType, framework, projectRoot) {
|
|
|
277
290
|
const normalizedFramework = normalizeTechIdentifier(framework);
|
|
278
291
|
if (normalizedFramework !== framework &&
|
|
279
292
|
config.tech_stacks &&
|
|
280
|
-
config.tech_stacks[
|
|
281
|
-
config.tech_stacks[
|
|
282
|
-
techConfig = config.tech_stacks[
|
|
293
|
+
config.tech_stacks[techCategory] &&
|
|
294
|
+
config.tech_stacks[techCategory][normalizedFramework]) {
|
|
295
|
+
techConfig = config.tech_stacks[techCategory][normalizedFramework];
|
|
283
296
|
console.log(`Using normalized tech identifier: ${framework} → ${normalizedFramework}`);
|
|
284
297
|
}
|
|
285
298
|
}
|
|
@@ -295,6 +308,37 @@ function loadTechStackConfig(platformType, framework, projectRoot) {
|
|
|
295
308
|
return { extensions: [], exclude_file_suffixes: [], exclude_file_names: [] };
|
|
296
309
|
}
|
|
297
310
|
|
|
311
|
+
/**
|
|
312
|
+
* Validate entry-dirs JSON schema
|
|
313
|
+
* @param {object} data - Parsed entry-dirs JSON data
|
|
314
|
+
* @returns {string[]|null} Array of error messages or null if valid
|
|
315
|
+
*/
|
|
316
|
+
function validateEntryDirsSchema(data) {
|
|
317
|
+
const errors = [];
|
|
318
|
+
if (!data.platformId || typeof data.platformId !== 'string') {
|
|
319
|
+
errors.push('platformId must be a non-empty string');
|
|
320
|
+
} else if (!/^[a-zA-Z0-9_-]+$/.test(data.platformId)) {
|
|
321
|
+
errors.push(`platformId format invalid: "${data.platformId}" (only alphanumeric, hyphen, underscore allowed)`);
|
|
322
|
+
}
|
|
323
|
+
if (!data.sourcePath || typeof data.sourcePath !== 'string') {
|
|
324
|
+
errors.push('sourcePath must be a non-empty string');
|
|
325
|
+
}
|
|
326
|
+
if (!Array.isArray(data.modules) || data.modules.length === 0) {
|
|
327
|
+
errors.push('modules must be a non-empty array');
|
|
328
|
+
} else {
|
|
329
|
+
for (let i = 0; i < data.modules.length; i++) {
|
|
330
|
+
const mod = data.modules[i];
|
|
331
|
+
if (!mod.name || typeof mod.name !== 'string') {
|
|
332
|
+
errors.push(`modules[${i}].name must be a non-empty string`);
|
|
333
|
+
}
|
|
334
|
+
if (!Array.isArray(mod.entryDirs) || mod.entryDirs.length === 0) {
|
|
335
|
+
errors.push(`modules[${i}].entryDirs must be a non-empty array`);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
return errors.length > 0 ? errors : null;
|
|
340
|
+
}
|
|
341
|
+
|
|
298
342
|
/**
|
|
299
343
|
* Infer platform info from platformId
|
|
300
344
|
* @param {string} platformId - Platform ID like "backend-ai", "web-vue", "mobile-uniapp"
|
|
@@ -533,7 +577,7 @@ function generateFromEntryDirs(entryDirsData, platformConfig, projectRoot, outpu
|
|
|
533
577
|
platformName: platformConfig.platformName,
|
|
534
578
|
platformType: platformType,
|
|
535
579
|
sourcePath: sourcePath,
|
|
536
|
-
techStack: [framework],
|
|
580
|
+
techStack: entryDirsData.techStack || [framework],
|
|
537
581
|
modules: [...new Set(moduleNames)].sort(),
|
|
538
582
|
totalFiles: features.length,
|
|
539
583
|
analyzedCount: 0,
|
|
@@ -667,7 +711,15 @@ function main() {
|
|
|
667
711
|
console.error(`Found top-level keys: ${foundKeys}`);
|
|
668
712
|
process.exit(1);
|
|
669
713
|
}
|
|
670
|
-
|
|
714
|
+
|
|
715
|
+
// Validate entry-dirs JSON schema
|
|
716
|
+
const schemaErrors = validateEntryDirsSchema(entryDirsData);
|
|
717
|
+
if (schemaErrors) {
|
|
718
|
+
console.error('Error: entry-dirs JSON schema validation failed:');
|
|
719
|
+
schemaErrors.forEach(err => console.error(` - ${err}`));
|
|
720
|
+
process.exit(1);
|
|
721
|
+
}
|
|
722
|
+
|
|
671
723
|
// Find project root (use current directory or entryDirsFile directory)
|
|
672
724
|
const projectRoot = findProjectRoot(path.dirname(entryDirsFilePath));
|
|
673
725
|
|
|
@@ -718,7 +770,10 @@ function main() {
|
|
|
718
770
|
outputDir = path.resolve(params.outputDir);
|
|
719
771
|
console.log(`Using outputDir from parameter: ${outputDir}`);
|
|
720
772
|
} else {
|
|
773
|
+
console.warn('WARNING: --outputDir not specified, falling back to default path.');
|
|
774
|
+
console.warn(' Recommended: explicitly pass --outputDir to avoid incorrect output location.');
|
|
721
775
|
outputDir = path.join(projectRoot, 'speccrew-workspace', 'knowledges', 'base', 'sync-state', 'knowledge-bizs');
|
|
776
|
+
console.warn(` Using fallback outputDir: ${outputDir}`);
|
|
722
777
|
}
|
|
723
778
|
|
|
724
779
|
// Generate features from entry dirs
|
|
@@ -762,25 +817,28 @@ function main() {
|
|
|
762
817
|
const configContent = fs.readFileSync(configPath, 'utf8');
|
|
763
818
|
const config = JSON.parse(configContent);
|
|
764
819
|
|
|
820
|
+
// Map platformType to tech-stack-mappings category
|
|
821
|
+
const techCategory = PLATFORM_TYPE_TO_CATEGORY[platformType] || platformType;
|
|
822
|
+
|
|
765
823
|
// Load tech-stack-specific exclude_dirs
|
|
766
824
|
let techExcludeDirs = [];
|
|
767
825
|
let effectiveTechIdentifier = techIdentifier;
|
|
768
826
|
|
|
769
827
|
// Try exact match first
|
|
770
828
|
if (config.tech_stacks &&
|
|
771
|
-
config.tech_stacks[
|
|
772
|
-
config.tech_stacks[
|
|
773
|
-
config.tech_stacks[
|
|
774
|
-
techExcludeDirs = config.tech_stacks[
|
|
829
|
+
config.tech_stacks[techCategory] &&
|
|
830
|
+
config.tech_stacks[techCategory][techIdentifier] &&
|
|
831
|
+
config.tech_stacks[techCategory][techIdentifier].exclude_dirs) {
|
|
832
|
+
techExcludeDirs = config.tech_stacks[techCategory][techIdentifier].exclude_dirs;
|
|
775
833
|
} else {
|
|
776
834
|
// Try normalized identifier (remove language prefix like "python-fastapi" → "fastapi")
|
|
777
835
|
const normalizedIdentifier = normalizeTechIdentifier(techIdentifier);
|
|
778
836
|
if (normalizedIdentifier !== techIdentifier &&
|
|
779
837
|
config.tech_stacks &&
|
|
780
|
-
config.tech_stacks[
|
|
781
|
-
config.tech_stacks[
|
|
782
|
-
config.tech_stacks[
|
|
783
|
-
techExcludeDirs = config.tech_stacks[
|
|
838
|
+
config.tech_stacks[techCategory] &&
|
|
839
|
+
config.tech_stacks[techCategory][normalizedIdentifier] &&
|
|
840
|
+
config.tech_stacks[techCategory][normalizedIdentifier].exclude_dirs) {
|
|
841
|
+
techExcludeDirs = config.tech_stacks[techCategory][normalizedIdentifier].exclude_dirs;
|
|
784
842
|
effectiveTechIdentifier = normalizedIdentifier;
|
|
785
843
|
console.log(`Using normalized tech identifier for exclude_dirs: ${techIdentifier} → ${normalizedIdentifier}`);
|
|
786
844
|
}
|
|
@@ -796,10 +854,10 @@ function main() {
|
|
|
796
854
|
|
|
797
855
|
// Load tech-stack-specific exclude_file_suffixes
|
|
798
856
|
if (config.tech_stacks &&
|
|
799
|
-
config.tech_stacks[
|
|
800
|
-
config.tech_stacks[
|
|
801
|
-
config.tech_stacks[
|
|
802
|
-
excludeFileSuffixes = config.tech_stacks[
|
|
857
|
+
config.tech_stacks[techCategory] &&
|
|
858
|
+
config.tech_stacks[techCategory][effectiveTechIdentifier] &&
|
|
859
|
+
config.tech_stacks[techCategory][effectiveTechIdentifier].exclude_file_suffixes) {
|
|
860
|
+
excludeFileSuffixes = config.tech_stacks[techCategory][effectiveTechIdentifier].exclude_file_suffixes;
|
|
803
861
|
if (excludeFileSuffixes.length > 0) {
|
|
804
862
|
console.log(`Loaded exclude_file_suffixes from tech-stack-mappings.json: ${excludeFileSuffixes.join(', ')}`);
|
|
805
863
|
}
|
|
@@ -807,10 +865,10 @@ function main() {
|
|
|
807
865
|
|
|
808
866
|
// Load tech-stack-specific exclude_file_names
|
|
809
867
|
if (config.tech_stacks &&
|
|
810
|
-
config.tech_stacks[
|
|
811
|
-
config.tech_stacks[
|
|
812
|
-
config.tech_stacks[
|
|
813
|
-
excludeFileNames = config.tech_stacks[
|
|
868
|
+
config.tech_stacks[techCategory] &&
|
|
869
|
+
config.tech_stacks[techCategory][effectiveTechIdentifier] &&
|
|
870
|
+
config.tech_stacks[techCategory][effectiveTechIdentifier].exclude_file_names) {
|
|
871
|
+
excludeFileNames = config.tech_stacks[techCategory][effectiveTechIdentifier].exclude_file_names;
|
|
814
872
|
if (excludeFileNames.length > 0) {
|
|
815
873
|
console.log(`Loaded exclude_file_names from tech-stack-mappings.json: ${excludeFileNames.join(', ')}`);
|
|
816
874
|
}
|
|
@@ -840,7 +898,10 @@ function main() {
|
|
|
840
898
|
syncStateDir = path.resolve(params.outputDir);
|
|
841
899
|
console.log(`Using outputDir from parameter: ${syncStateDir}`);
|
|
842
900
|
} else {
|
|
901
|
+
console.warn('WARNING: --outputDir not specified, falling back to default path.');
|
|
902
|
+
console.warn(' Recommended: explicitly pass --outputDir to avoid incorrect output location.');
|
|
843
903
|
syncStateDir = path.join(projectRoot, 'speccrew-workspace', 'knowledges', 'base', 'sync-state', 'knowledge-bizs');
|
|
904
|
+
console.warn(` Using fallback outputDir: ${syncStateDir}`);
|
|
844
905
|
}
|
|
845
906
|
const outputPath = path.join(syncStateDir, outputFileName);
|
|
846
907
|
|