speccrew 0.1.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.
- package/.speccrew/agents/speccrew-feature-designer.md +142 -0
- package/.speccrew/agents/speccrew-product-manager.md +61 -0
- package/.speccrew/agents/speccrew-system-designer.md +200 -0
- package/.speccrew/agents/speccrew-system-developer.md +238 -0
- package/.speccrew/agents/speccrew-task-worker.md +80 -0
- package/.speccrew/agents/speccrew-team-leader.md +92 -0
- package/.speccrew/agents/speccrew-test-manager.md +313 -0
- package/.speccrew/skills/speccrew-create-agents/SKILL.md +98 -0
- package/.speccrew/skills/speccrew-create-agents/templates/agents/designer-agent.md +54 -0
- package/.speccrew/skills/speccrew-create-agents/templates/agents/dev-agent.md +79 -0
- package/.speccrew/skills/speccrew-create-agents/templates/agents/test-agent.md +80 -0
- package/.speccrew/skills/speccrew-dev-backend/SKILL.md +205 -0
- package/.speccrew/skills/speccrew-dev-backend/templates/TASK-RECORD-TEMPLATE.md +118 -0
- package/.speccrew/skills/speccrew-dev-desktop/SKILL.md +258 -0
- package/.speccrew/skills/speccrew-dev-desktop/templates/TASK-RECORD-TEMPLATE.md +161 -0
- package/.speccrew/skills/speccrew-dev-frontend/SKILL.md +202 -0
- package/.speccrew/skills/speccrew-dev-frontend/templates/TASK-RECORD-TEMPLATE.md +115 -0
- package/.speccrew/skills/speccrew-dev-mobile/SKILL.md +200 -0
- package/.speccrew/skills/speccrew-dev-mobile/templates/TASK-RECORD-TEMPLATE.md +125 -0
- package/.speccrew/skills/speccrew-fd-api-contract/SKILL.md +73 -0
- package/.speccrew/skills/speccrew-fd-api-contract/templates/API-CONTRACT-TEMPLATE.md +96 -0
- package/.speccrew/skills/speccrew-fd-feature-design/SKILL.md +395 -0
- package/.speccrew/skills/speccrew-fd-feature-design/templates/FEATURE-SPEC-TEMPLATE.md +387 -0
- package/.speccrew/skills/speccrew-get-timestamp/SKILL.md +80 -0
- package/.speccrew/skills/speccrew-get-timestamp/scripts/get-timestamp.js +35 -0
- package/.speccrew/skills/speccrew-knowledge-bizs-api-analyze/SKILL.md +1116 -0
- package/.speccrew/skills/speccrew-knowledge-bizs-api-analyze/templates/FEATURE-DETAIL-TEMPLATE-FASTAPI.md +462 -0
- package/.speccrew/skills/speccrew-knowledge-bizs-api-analyze/templates/FEATURE-DETAIL-TEMPLATE-JAVA.md +480 -0
- package/.speccrew/skills/speccrew-knowledge-bizs-api-analyze/templates/FEATURE-DETAIL-TEMPLATE-NET.md +464 -0
- package/.speccrew/skills/speccrew-knowledge-bizs-api-analyze/templates/FEATURE-DETAIL-TEMPLATE.md +480 -0
- package/.speccrew/skills/speccrew-knowledge-bizs-api-analyze/templates/MODULE-OVERVIEW-TEMPLATE.md +367 -0
- package/.speccrew/skills/speccrew-knowledge-bizs-dispatch/SKILL.md +667 -0
- package/.speccrew/skills/speccrew-knowledge-bizs-dispatch/STATUS-FORMATS.md +74 -0
- package/.speccrew/skills/speccrew-knowledge-bizs-dispatch/scripts/batch-orchestrator.js +176 -0
- package/.speccrew/skills/speccrew-knowledge-bizs-dispatch/scripts/get-next-batch.js +150 -0
- package/.speccrew/skills/speccrew-knowledge-bizs-dispatch/scripts/get-pending-features.js +106 -0
- package/.speccrew/skills/speccrew-knowledge-bizs-dispatch/scripts/mark-stale.js +249 -0
- package/.speccrew/skills/speccrew-knowledge-bizs-dispatch/scripts/process-batch-results.js +848 -0
- package/.speccrew/skills/speccrew-knowledge-bizs-dispatch/scripts/update-feature-status.js +226 -0
- package/.speccrew/skills/speccrew-knowledge-bizs-init-features/SKILL.md +264 -0
- package/.speccrew/skills/speccrew-knowledge-bizs-init-features/examples/features.json +34 -0
- package/.speccrew/skills/speccrew-knowledge-bizs-init-features/scripts/generate-inventory.js +867 -0
- package/.speccrew/skills/speccrew-knowledge-bizs-init-features/scripts/test-inventory.js +26 -0
- package/.speccrew/skills/speccrew-knowledge-bizs-module-classify/SKILL.md +165 -0
- package/.speccrew/skills/speccrew-knowledge-bizs-module-classify/scripts/apply-module-mapping.js +208 -0
- package/.speccrew/skills/speccrew-knowledge-bizs-module-classify/scripts/extract-module-summary.js +180 -0
- package/.speccrew/skills/speccrew-knowledge-bizs-module-classify/scripts/reindex-modules.js +358 -0
- package/.speccrew/skills/speccrew-knowledge-bizs-ui-analyze/SKILL.md +1055 -0
- package/.speccrew/skills/speccrew-knowledge-bizs-ui-analyze/templates/FEATURE-DETAIL-TEMPLATE-UI-DESKTOP.md +303 -0
- package/.speccrew/skills/speccrew-knowledge-bizs-ui-analyze/templates/FEATURE-DETAIL-TEMPLATE-UI-ELECTRON.md +327 -0
- package/.speccrew/skills/speccrew-knowledge-bizs-ui-analyze/templates/FEATURE-DETAIL-TEMPLATE-UI-MINIAPP.md +292 -0
- package/.speccrew/skills/speccrew-knowledge-bizs-ui-analyze/templates/FEATURE-DETAIL-TEMPLATE-UI-MOBILE.md +281 -0
- package/.speccrew/skills/speccrew-knowledge-bizs-ui-analyze/templates/FEATURE-DETAIL-TEMPLATE-UI.md +324 -0
- package/.speccrew/skills/speccrew-knowledge-bizs-ui-style-extract/SKILL.md +270 -0
- package/.speccrew/skills/speccrew-knowledge-bizs-ui-style-extract/templates/COMPONENT-PATTERN-TEMPLATE.md +33 -0
- package/.speccrew/skills/speccrew-knowledge-bizs-ui-style-extract/templates/LAYOUT-PATTERN-TEMPLATE.md +33 -0
- package/.speccrew/skills/speccrew-knowledge-bizs-ui-style-extract/templates/PAGE-TYPE-TEMPLATE.md +33 -0
- package/.speccrew/skills/speccrew-knowledge-graph-query/SKILL.md +229 -0
- package/.speccrew/skills/speccrew-knowledge-graph-query/scripts/graph-query.js +549 -0
- package/.speccrew/skills/speccrew-knowledge-graph-write/SKILL.md +181 -0
- package/.speccrew/skills/speccrew-knowledge-graph-write/scripts/graph-write.js +651 -0
- package/.speccrew/skills/speccrew-knowledge-module-summarize/SKILL.md +305 -0
- package/.speccrew/skills/speccrew-knowledge-module-summarize/templates/MODULE-OVERVIEW-TEMPLATE.md +400 -0
- package/.speccrew/skills/speccrew-knowledge-system-summarize/SKILL.md +351 -0
- package/.speccrew/skills/speccrew-knowledge-system-summarize/templates/SYSTEM-OVERVIEW-TEMPLATE.md +294 -0
- package/.speccrew/skills/speccrew-knowledge-techs-dispatch/SKILL.md +683 -0
- package/.speccrew/skills/speccrew-knowledge-techs-dispatch/STATUS-FORMATS.md +550 -0
- package/.speccrew/skills/speccrew-knowledge-techs-dispatch/templates/techs-manifest-EXAMPLE.json +35 -0
- package/.speccrew/skills/speccrew-knowledge-techs-generate/SKILL.md +1087 -0
- package/.speccrew/skills/speccrew-knowledge-techs-generate/templates/ARCHITECTURE-TEMPLATE.md +240 -0
- package/.speccrew/skills/speccrew-knowledge-techs-generate/templates/COLOR-SYSTEM-TEMPLATE.md +68 -0
- package/.speccrew/skills/speccrew-knowledge-techs-generate/templates/COMPONENT-LIBRARY-TEMPLATE.md +86 -0
- package/.speccrew/skills/speccrew-knowledge-techs-generate/templates/CONVENTIONS-BUILD-TEMPLATE.md +466 -0
- package/.speccrew/skills/speccrew-knowledge-techs-generate/templates/CONVENTIONS-DATA-TEMPLATE.md +432 -0
- package/.speccrew/skills/speccrew-knowledge-techs-generate/templates/CONVENTIONS-DESIGN-TEMPLATE.md +1209 -0
- package/.speccrew/skills/speccrew-knowledge-techs-generate/templates/CONVENTIONS-DEV-TEMPLATE.md +1433 -0
- package/.speccrew/skills/speccrew-knowledge-techs-generate/templates/CONVENTIONS-SYSTEM-TEST-TEMPLATE.md +1052 -0
- package/.speccrew/skills/speccrew-knowledge-techs-generate/templates/CONVENTIONS-UNIT-TEST-TEMPLATE.md +946 -0
- package/.speccrew/skills/speccrew-knowledge-techs-generate/templates/INDEX-TEMPLATE.md +29 -0
- package/.speccrew/skills/speccrew-knowledge-techs-generate/templates/PAGE-LAYOUTS-TEMPLATE.md +69 -0
- package/.speccrew/skills/speccrew-knowledge-techs-generate/templates/PAGE-TYPE-SUMMARY-TEMPLATE.md +74 -0
- package/.speccrew/skills/speccrew-knowledge-techs-generate/templates/TECH-STACK-TEMPLATE.md +232 -0
- package/.speccrew/skills/speccrew-knowledge-techs-generate-conventions/SKILL.md +628 -0
- package/.speccrew/skills/speccrew-knowledge-techs-generate-ui-style/SKILL.md +392 -0
- package/.speccrew/skills/speccrew-knowledge-techs-index/SKILL.md +489 -0
- package/.speccrew/skills/speccrew-knowledge-techs-index/templates/INDEX-TEMPLATE.md +243 -0
- package/.speccrew/skills/speccrew-knowledge-techs-init/SKILL.md +269 -0
- package/.speccrew/skills/speccrew-knowledge-techs-ui-analyze/SKILL.md +562 -0
- package/.speccrew/skills/speccrew-knowledge-techs-ui-analyze/templates/BUSINESS-COMPONENTS-TEMPLATE.md +171 -0
- package/.speccrew/skills/speccrew-knowledge-techs-ui-analyze/templates/COMMON-COMPONENTS-TEMPLATE.md +177 -0
- package/.speccrew/skills/speccrew-knowledge-techs-ui-analyze/templates/COMPONENT-INDIVIDUAL-TEMPLATE.md +80 -0
- package/.speccrew/skills/speccrew-knowledge-techs-ui-analyze/templates/COMPONENT-LIBRARY-TEMPLATE.md +118 -0
- package/.speccrew/skills/speccrew-knowledge-techs-ui-analyze/templates/LAYOUT-INDIVIDUAL-TEMPLATE.md +97 -0
- package/.speccrew/skills/speccrew-knowledge-techs-ui-analyze/templates/LAYOUT-PATTERNS-TEMPLATE.md +208 -0
- package/.speccrew/skills/speccrew-knowledge-techs-ui-analyze/templates/NAVIGATION-PATTERNS-TEMPLATE.md +157 -0
- package/.speccrew/skills/speccrew-knowledge-techs-ui-analyze/templates/PAGE-TYPE-INDIVIDUAL-TEMPLATE.md +123 -0
- package/.speccrew/skills/speccrew-knowledge-techs-ui-analyze/templates/PAGE-TYPE-SUMMARY-TEMPLATE.md +58 -0
- package/.speccrew/skills/speccrew-knowledge-techs-ui-analyze/templates/SPACING-TEMPLATE.md +119 -0
- package/.speccrew/skills/speccrew-knowledge-techs-ui-analyze/templates/STYLE-SYSTEM-TEMPLATE.md +117 -0
- package/.speccrew/skills/speccrew-knowledge-techs-ui-analyze/templates/TYPOGRAPHY-TEMPLATE.md +107 -0
- package/.speccrew/skills/speccrew-knowledge-techs-ui-analyze/templates/UI-STYLE-GUIDE-TEMPLATE.md +171 -0
- package/.speccrew/skills/speccrew-pm-requirement-analysis/SKILL.md +434 -0
- package/.speccrew/skills/speccrew-pm-requirement-analysis/templates/BIZS-MODELING-TEMPLATE.md +332 -0
- package/.speccrew/skills/speccrew-pm-requirement-analysis/templates/PRD-TEMPLATE.md +200 -0
- package/.speccrew/skills/speccrew-pm-requirement-assess/SKILL.md +195 -0
- package/.speccrew/skills/speccrew-project-diagnosis/SKILL.md +208 -0
- package/.speccrew/skills/speccrew-project-diagnosis/templates/DIAGNOSIS-REPORT-TEMPLATE.md +202 -0
- package/.speccrew/skills/speccrew-sd-backend/SKILL.md +188 -0
- package/.speccrew/skills/speccrew-sd-backend/templates/INDEX-TEMPLATE.md +85 -0
- package/.speccrew/skills/speccrew-sd-backend/templates/SD-BACKEND-TEMPLATE.md +269 -0
- package/.speccrew/skills/speccrew-sd-desktop/SKILL.md +192 -0
- package/.speccrew/skills/speccrew-sd-desktop/templates/INDEX-TEMPLATE.md +271 -0
- package/.speccrew/skills/speccrew-sd-desktop/templates/SD-DESKTOP-TEMPLATE.md +673 -0
- package/.speccrew/skills/speccrew-sd-frontend/SKILL.md +176 -0
- package/.speccrew/skills/speccrew-sd-frontend/templates/INDEX-TEMPLATE.md +184 -0
- package/.speccrew/skills/speccrew-sd-frontend/templates/SD-FRONTEND-TEMPLATE.md +382 -0
- package/.speccrew/skills/speccrew-sd-mobile/SKILL.md +189 -0
- package/.speccrew/skills/speccrew-sd-mobile/templates/INDEX-TEMPLATE.md +219 -0
- package/.speccrew/skills/speccrew-sd-mobile/templates/SD-MOBILE-TEMPLATE.md +534 -0
- package/.speccrew/skills/speccrew-test-case-design/SKILL.md +284 -0
- package/.speccrew/skills/speccrew-test-case-design/templates/TEST-CASE-DESIGN-TEMPLATE.md +263 -0
- package/.speccrew/skills/speccrew-test-code-gen/SKILL.md +313 -0
- package/.speccrew/skills/speccrew-test-code-gen/templates/TEST-CODE-PLAN-TEMPLATE.md +180 -0
- package/.speccrew/skills/speccrew-test-execute/SKILL.md +283 -0
- package/.speccrew/skills/speccrew-test-execute/templates/BUG-REPORT-TEMPLATE.md +50 -0
- package/.speccrew/skills/speccrew-test-execute/templates/TEST-REPORT-TEMPLATE.md +57 -0
- package/.speccrew/skills/speccrew-workflow-diagnose/SKILL.md +155 -0
- package/LICENSE +21 -0
- package/README.ar.md +318 -0
- package/README.en.md +318 -0
- package/README.es.md +318 -0
- package/README.md +340 -0
- package/bin/cli.js +62 -0
- package/lib/commands/doctor.js +138 -0
- package/lib/commands/init.js +231 -0
- package/lib/commands/list.js +114 -0
- package/lib/commands/uninstall.js +117 -0
- package/lib/commands/update.js +351 -0
- package/lib/ide-adapters.js +73 -0
- package/lib/utils.js +104 -0
- package/package.json +28 -0
- package/workspace-template/docs/configs/document-templates.json +667 -0
- package/workspace-template/docs/configs/platform-mapping.json +194 -0
- package/workspace-template/docs/configs/tech-stack-mappings.json +313 -0
- package/workspace-template/docs/configs/validation-rules.json +87 -0
- package/workspace-template/docs/rules/mermaid-rule.md +114 -0
- package/workspace-template/docs/solutions/Agent/346/212/200/350/203/275/345/256/232/344/271/211+/351/234/200/346/261/202/346/226/207/346/241/243+UML/344/275/277/347/224/250/346/250/241/346/235/277/357/274/210ISA-95/345/205/255/346/256/265/345/274/217/350/236/215/345/220/210/347/211/210/357/274/211.md +586 -0
- package/workspace-template/docs/solutions/agent-knowledge-map.md +238 -0
- package/workspace-template/docs/solutions/bizs-knowledge-pipeline.md +678 -0
- package/workspace-template/docs/solutions/harness.md +410 -0
- package/workspace-template/docs/solutions/knowledge-incremental-sync-spec.md +943 -0
- package/workspace-template/docs/solutions/techs-knowledge-pipeline.md +803 -0
- package/workspace-template/docs/solutions/workspace-structure.md +318 -0
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* reindex-modules.js - 确定性脚本,用更新后的 exclude_dirs 对已有 features JSON 重新提取 module 名
|
|
4
|
+
*
|
|
5
|
+
* 调用方式:
|
|
6
|
+
* node reindex-modules.js --featuresFile "path/to/features-backend-system.json" --projectRoot "d:\dev\ruoyi-vue-pro"
|
|
7
|
+
*
|
|
8
|
+
* 可选参数:
|
|
9
|
+
* --platformType "backend" - 如果不提供,从 features JSON 的 platformType 字段读取
|
|
10
|
+
* --techIdentifier "spring" - 如果不提供,尝试从 features JSON 推断(如 techStack 数组)
|
|
11
|
+
* --excludeDirs "controller,admin,api,service" - 如果不提供,从 tech-stack-mappings.json 加载
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const fs = require('fs');
|
|
15
|
+
const path = require('path');
|
|
16
|
+
|
|
17
|
+
// === 工具函数(从 generate-inventory.js 复用) ===
|
|
18
|
+
|
|
19
|
+
function normalizePath(filePath) {
|
|
20
|
+
if (!filePath) return '';
|
|
21
|
+
return filePath.replace(/\\/g, '/');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function parseArrayParam(value) {
|
|
25
|
+
if (!value) return [];
|
|
26
|
+
const trimmed = value.trim();
|
|
27
|
+
if (trimmed.startsWith('[')) {
|
|
28
|
+
try { return JSON.parse(trimmed); } catch (e) {
|
|
29
|
+
return trimmed.slice(1, -1).split(',').map(s => s.trim()).filter(Boolean);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return trimmed.split(',').map(s => s.trim()).filter(Boolean);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Java/Kotlin 标准源码路径前缀
|
|
36
|
+
const STANDARD_SOURCE_PREFIXES = [
|
|
37
|
+
'src/main/java',
|
|
38
|
+
'src/main/kotlin',
|
|
39
|
+
'src/main/scala',
|
|
40
|
+
'src/main/groovy',
|
|
41
|
+
'src/main/resources',
|
|
42
|
+
'src/test/java',
|
|
43
|
+
'src/test/kotlin',
|
|
44
|
+
];
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Fallback exclude_dirs when tech-stack-mappings.json is missing or incomplete.
|
|
48
|
+
* Covers the most common container directories across all platforms.
|
|
49
|
+
*/
|
|
50
|
+
const FALLBACK_EXCLUDE_DIRS = {
|
|
51
|
+
web: ["src", "views", "pages", "components", "composables", "hooks", "utils", "mixins", "directives"],
|
|
52
|
+
mobile: ["src", "views", "pages", "components", "composables", "hooks", "utils", "mixins", "directives"],
|
|
53
|
+
backend: ["controller", "controllers", "admin", "app", "api", "service", "services", "repository", "repositories", "dao", "dal", "mysql", "redis", "dataobject", "entity", "entities", "model", "models", "dto", "dtos", "vo", "vos", "mapper", "mappers", "convert", "converter", "converters", "config", "configs", "util", "utils", "common", "exception", "exceptions", "enums", "framework", "job", "mq", "listener", "listeners", "producer", "consumer"],
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
function getModuleName(dirPath, excludeDirs, fallbackModuleName) {
|
|
57
|
+
let normalized = normalizePath(dirPath);
|
|
58
|
+
|
|
59
|
+
// Strip standard source directory prefixes (Java/Kotlin/etc.)
|
|
60
|
+
for (const prefix of STANDARD_SOURCE_PREFIXES) {
|
|
61
|
+
if (normalized.startsWith(prefix + '/')) {
|
|
62
|
+
normalized = normalized.slice(prefix.length + 1);
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const parts = normalized.split('/').filter(p => p && p !== '.');
|
|
68
|
+
|
|
69
|
+
for (const part of parts) {
|
|
70
|
+
if (!excludeDirs.includes(part)) {
|
|
71
|
+
return part;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// All parts were excluded (e.g., "src/utils/helper.ts" with src and utils both excluded)
|
|
76
|
+
// Return "_root" to indicate framework/root-level files, NOT the original module name
|
|
77
|
+
return '_root';
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// === 辅助函数 ===
|
|
81
|
+
|
|
82
|
+
function parseArgs(argv) {
|
|
83
|
+
const args = {};
|
|
84
|
+
for (let i = 0; i < argv.length; i++) {
|
|
85
|
+
if (argv[i].startsWith('--') && i + 1 < argv.length) {
|
|
86
|
+
const key = argv[i].slice(2);
|
|
87
|
+
args[key] = argv[i + 1];
|
|
88
|
+
i++;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return args;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Find project root by searching upward for speccrew-workspace directory
|
|
96
|
+
*/
|
|
97
|
+
function findProjectRoot(startPath) {
|
|
98
|
+
let currentDir = path.resolve(startPath);
|
|
99
|
+
const root = path.parse(currentDir).root;
|
|
100
|
+
|
|
101
|
+
while (currentDir !== root) {
|
|
102
|
+
const workspaceDir = path.join(currentDir, 'speccrew-workspace');
|
|
103
|
+
if (fs.existsSync(workspaceDir) && fs.statSync(workspaceDir).isDirectory()) {
|
|
104
|
+
return currentDir;
|
|
105
|
+
}
|
|
106
|
+
currentDir = path.dirname(currentDir);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Fallback: return start path's parent directory
|
|
110
|
+
return path.dirname(path.resolve(startPath));
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function loadExcludeDirs(projectRoot, platformType, techIdentifier, featuresData) {
|
|
114
|
+
console.log(`Loading exclude_dirs: platformType="${platformType}", techIdentifier="${techIdentifier || '(auto)'}"`);
|
|
115
|
+
|
|
116
|
+
// 尝试推断 techIdentifier
|
|
117
|
+
if (!techIdentifier) {
|
|
118
|
+
const techStack = featuresData.techStack || [];
|
|
119
|
+
// 常见映射
|
|
120
|
+
const techMap = {
|
|
121
|
+
'spring': 'spring', 'spring-boot': 'spring', 'java': 'spring',
|
|
122
|
+
'vue': 'vue', 'vue3': 'vue', 'vue2': 'vue',
|
|
123
|
+
'react': 'react', 'next': 'nextjs', 'nextjs': 'nextjs',
|
|
124
|
+
'angular': 'angular',
|
|
125
|
+
'uniapp': 'uniapp', 'uni-app': 'uniapp',
|
|
126
|
+
'flutter': 'flutter',
|
|
127
|
+
'react-native': 'react-native',
|
|
128
|
+
};
|
|
129
|
+
for (const tech of techStack) {
|
|
130
|
+
const lower = tech.toLowerCase();
|
|
131
|
+
if (techMap[lower]) {
|
|
132
|
+
techIdentifier = techMap[lower];
|
|
133
|
+
console.log(`Inferred techIdentifier from techStack "${tech}": "${techIdentifier}"`);
|
|
134
|
+
break;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (!platformType || !techIdentifier) {
|
|
140
|
+
console.warn('WARNING: Cannot determine platformType/techIdentifier for exclude_dirs lookup');
|
|
141
|
+
// Return fallback even when platformType/techIdentifier unknown
|
|
142
|
+
const fallbackDirs = FALLBACK_EXCLUDE_DIRS[platformType] || [];
|
|
143
|
+
console.log(`Using fallback exclude_dirs (${fallbackDirs.length}): ${fallbackDirs.slice(0, 5).join(', ')}...`);
|
|
144
|
+
return { excludeDirs: fallbackDirs, stripModulePrefixes: [] };
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Get fallback dirs for this platformType
|
|
148
|
+
const fallback = FALLBACK_EXCLUDE_DIRS[platformType] || [];
|
|
149
|
+
|
|
150
|
+
// 查找 tech-stack-mappings.json
|
|
151
|
+
const configPaths = [
|
|
152
|
+
path.join(projectRoot, 'speccrew-workspace', 'docs', 'configs', 'tech-stack-mappings.json'),
|
|
153
|
+
path.join(projectRoot, 'docs', 'configs', 'tech-stack-mappings.json'),
|
|
154
|
+
];
|
|
155
|
+
|
|
156
|
+
let loadedDirs = [];
|
|
157
|
+
let globalDirs = [];
|
|
158
|
+
let stripModulePrefixes = [];
|
|
159
|
+
for (const configPath of configPaths) {
|
|
160
|
+
try {
|
|
161
|
+
if (fs.existsSync(configPath)) {
|
|
162
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
163
|
+
|
|
164
|
+
// Load tech-stack-specific exclude_dirs
|
|
165
|
+
if (config.tech_stacks &&
|
|
166
|
+
config.tech_stacks[platformType] &&
|
|
167
|
+
config.tech_stacks[platformType][techIdentifier]) {
|
|
168
|
+
const techConfig = config.tech_stacks[platformType][techIdentifier];
|
|
169
|
+
if (techConfig.exclude_dirs) {
|
|
170
|
+
loadedDirs = techConfig.exclude_dirs;
|
|
171
|
+
}
|
|
172
|
+
// Load strip_module_prefixes
|
|
173
|
+
if (techConfig.strip_module_prefixes) {
|
|
174
|
+
stripModulePrefixes = techConfig.strip_module_prefixes;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Load global exclude_dirs (applies to all platforms)
|
|
179
|
+
globalDirs = config.global_exclude_dirs || [];
|
|
180
|
+
|
|
181
|
+
if (loadedDirs.length > 0 || globalDirs.length > 0) {
|
|
182
|
+
console.log(`Loaded exclude_dirs from: ${configPath}`);
|
|
183
|
+
break;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
} catch (e) {
|
|
187
|
+
// continue to next path
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// MERGE: global + tech-specific + fallback — ensure critical container dirs are always included
|
|
192
|
+
const merged = [...new Set([...globalDirs, ...loadedDirs, ...fallback])];
|
|
193
|
+
|
|
194
|
+
if (loadedDirs.length === 0 && globalDirs.length === 0) {
|
|
195
|
+
console.warn(`WARNING: No tech-stack-specific exclude_dirs found for ${platformType}/${techIdentifier}, using fallback`);
|
|
196
|
+
console.log(`Fallback exclude_dirs (${fallback.length}): ${fallback.slice(0, 5).join(', ')}...`);
|
|
197
|
+
} else {
|
|
198
|
+
console.log(`Loaded exclude_dirs: ${globalDirs.length} global + ${loadedDirs.length} tech-specific (${platformType}/${techIdentifier})`);
|
|
199
|
+
console.log(`Merged with fallback = ${merged.length} total dirs`);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (stripModulePrefixes.length > 0) {
|
|
203
|
+
console.log(`Loaded strip_module_prefixes: ${stripModulePrefixes.join(', ')}`);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return { excludeDirs: merged, stripModulePrefixes };
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function extractPlatformId(featuresData) {
|
|
210
|
+
// 从第一个 feature 的 documentPath 提取 platformId
|
|
211
|
+
// 格式: "speccrew-workspace/knowledges/bizs/{platformId}/..."
|
|
212
|
+
for (const feature of (featuresData.features || [])) {
|
|
213
|
+
if (feature.documentPath) {
|
|
214
|
+
const match = normalizePath(feature.documentPath).match(/knowledges\/bizs\/([^/]+)\//);
|
|
215
|
+
if (match) return match[1];
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
// 如果无法提取,用 platformType-platformSubtype 构建
|
|
219
|
+
if (featuresData.platformType && featuresData.platformSubtype) {
|
|
220
|
+
return `${featuresData.platformType}-${featuresData.platformSubtype}`;
|
|
221
|
+
}
|
|
222
|
+
return null;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// === 主逻辑 ===
|
|
226
|
+
|
|
227
|
+
function main() {
|
|
228
|
+
// 1. 解析命令行参数
|
|
229
|
+
const args = parseArgs(process.argv.slice(2));
|
|
230
|
+
const featuresFile = args.featuresFile;
|
|
231
|
+
|
|
232
|
+
if (!featuresFile) {
|
|
233
|
+
console.error('Error: --featuresFile is required');
|
|
234
|
+
process.exit(1);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// 2. 读取 features JSON
|
|
238
|
+
let featuresData;
|
|
239
|
+
try {
|
|
240
|
+
featuresData = JSON.parse(fs.readFileSync(featuresFile, 'utf8'));
|
|
241
|
+
} catch (e) {
|
|
242
|
+
console.error(`Error: Failed to read/parse features file: ${e.message}`);
|
|
243
|
+
process.exit(1);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Auto-detect projectRoot from featuresFile if not provided
|
|
247
|
+
const projectRoot = args.projectRoot || findProjectRoot(featuresFile);
|
|
248
|
+
console.log(`Project root: ${projectRoot}`);
|
|
249
|
+
|
|
250
|
+
const inventorySourcePath = normalizePath(featuresData.sourcePath || '');
|
|
251
|
+
const platformType = args.platformType || featuresData.platformType || '';
|
|
252
|
+
|
|
253
|
+
// 3. 加载 exclude_dirs
|
|
254
|
+
let excludeDirs = [];
|
|
255
|
+
let stripModulePrefixes = [];
|
|
256
|
+
if (args.excludeDirs) {
|
|
257
|
+
excludeDirs = parseArrayParam(args.excludeDirs);
|
|
258
|
+
} else {
|
|
259
|
+
// 从 tech-stack-mappings.json 加载,按优先级确定 techIdentifier:
|
|
260
|
+
// 1. 命令行参数 --techIdentifier
|
|
261
|
+
// 2. features JSON 中的 techIdentifier
|
|
262
|
+
// 3. features JSON 中的 platformSubtype (兼容旧数据)
|
|
263
|
+
const techId = args.techIdentifier || featuresData.techIdentifier || featuresData.platformSubtype;
|
|
264
|
+
const config = loadExcludeDirs(projectRoot, platformType, techId, featuresData);
|
|
265
|
+
excludeDirs = config.excludeDirs;
|
|
266
|
+
stripModulePrefixes = config.stripModulePrefixes;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
if (excludeDirs.length === 0) {
|
|
270
|
+
console.error('Warning: exclude_dirs is empty, module names may not be optimal');
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
console.log(`Platform: ${platformType}`);
|
|
274
|
+
console.log(`Exclude dirs (${excludeDirs.length}): ${excludeDirs.join(', ')}`);
|
|
275
|
+
console.log(`Total features: ${featuresData.features.length}`);
|
|
276
|
+
|
|
277
|
+
// 4. 重新计算每个 feature 的 module
|
|
278
|
+
const modulesBefore = [...new Set(featuresData.features.map(f => f.module))].sort();
|
|
279
|
+
let reclassifiedCount = 0;
|
|
280
|
+
|
|
281
|
+
// 从 features JSON 获取 platformId(用于重建 documentPath)
|
|
282
|
+
// platformId 格式: "{platformType}-{platformSubtype}"
|
|
283
|
+
// 从现有 documentPath 提取: "speccrew-workspace/knowledges/bizs/{platformId}/..."
|
|
284
|
+
const platformId = extractPlatformId(featuresData);
|
|
285
|
+
|
|
286
|
+
featuresData.features.forEach(feature => {
|
|
287
|
+
// 计算 feature 源文件相对于 inventorySourcePath 的路径
|
|
288
|
+
let relativePath = normalizePath(feature.sourcePath || '');
|
|
289
|
+
|
|
290
|
+
// 如果 feature.sourcePath 是绝对路径或相对于项目根,需要去掉 inventorySourcePath 前缀
|
|
291
|
+
if (inventorySourcePath && relativePath.startsWith(inventorySourcePath)) {
|
|
292
|
+
relativePath = relativePath.slice(inventorySourcePath.length);
|
|
293
|
+
if (relativePath.startsWith('/')) relativePath = relativePath.slice(1);
|
|
294
|
+
}
|
|
295
|
+
// 也可能 inventorySourcePath 只是部分匹配
|
|
296
|
+
else if (inventorySourcePath) {
|
|
297
|
+
const invParts = inventorySourcePath.split('/');
|
|
298
|
+
const relParts = relativePath.split('/');
|
|
299
|
+
// 找到重叠部分
|
|
300
|
+
let startIdx = 0;
|
|
301
|
+
for (let i = 0; i < relParts.length; i++) {
|
|
302
|
+
if (relParts.slice(i, i + invParts.length).join('/') === inventorySourcePath) {
|
|
303
|
+
startIdx = i + invParts.length;
|
|
304
|
+
break;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
if (startIdx > 0) {
|
|
308
|
+
relativePath = relParts.slice(startIdx).join('/');
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// 取目录部分(去掉文件名)
|
|
313
|
+
const dirPath = path.dirname(relativePath).replace(/\\/g, '/');
|
|
314
|
+
|
|
315
|
+
// 用 getModuleName 重新提取模块名
|
|
316
|
+
const fallback = feature.module || '_root';
|
|
317
|
+
let newModule = getModuleName(dirPath, excludeDirs, fallback);
|
|
318
|
+
|
|
319
|
+
// 应用 strip_module_prefixes 前缀去除
|
|
320
|
+
for (const prefix of stripModulePrefixes) {
|
|
321
|
+
if (newModule.startsWith(prefix)) {
|
|
322
|
+
newModule = newModule.substring(prefix.length);
|
|
323
|
+
break;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
if (newModule !== feature.module) {
|
|
328
|
+
reclassifiedCount++;
|
|
329
|
+
feature.module = newModule;
|
|
330
|
+
|
|
331
|
+
// 重建 documentPath(使用 fileName 而非 feature.id,避免文件名过长)
|
|
332
|
+
if (platformId) {
|
|
333
|
+
feature.documentPath = `speccrew-workspace/knowledges/bizs/${platformId}/${newModule}/${feature.fileName}.md`;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
// 5. 更新 modules 数组
|
|
339
|
+
const modulesAfter = [...new Set(featuresData.features.map(f => f.module))].sort();
|
|
340
|
+
featuresData.modules = modulesAfter;
|
|
341
|
+
|
|
342
|
+
// 6. 写回文件
|
|
343
|
+
fs.writeFileSync(featuresFile, JSON.stringify(featuresData, null, 2), 'utf8');
|
|
344
|
+
|
|
345
|
+
// 7. 输出结果
|
|
346
|
+
const result = {
|
|
347
|
+
status: 'success',
|
|
348
|
+
modules_before: modulesBefore,
|
|
349
|
+
modules_after: modulesAfter,
|
|
350
|
+
reclassified_count: reclassifiedCount,
|
|
351
|
+
total_features: featuresData.features.length
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
console.log('\n=== Reindex Result ===');
|
|
355
|
+
console.log(JSON.stringify(result, null, 2));
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
main();
|