speccore 8.3.16 → 8.3.17
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/dist/commands/analyze.d.ts.map +1 -1
- package/dist/commands/analyze.js +85 -2
- package/dist/commands/analyze.js.map +1 -1
- package/dist/core/spec-paths.d.ts +12 -0
- package/dist/core/spec-paths.d.ts.map +1 -1
- package/dist/core/spec-paths.js +65 -0
- package/dist/core/spec-paths.js.map +1 -1
- package/dist/core/spec-skeleton.d.ts +9 -0
- package/dist/core/spec-skeleton.d.ts.map +1 -1
- package/dist/core/spec-skeleton.js +100 -0
- package/dist/core/spec-skeleton.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"analyze.d.ts","sourceRoot":"","sources":["../../src/commands/analyze.ts"],"names":[],"mappings":"AAoEA,MAAM,WAAW,cAAc;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,QAAQ,GAAG,WAAW,GAAG,MAAM,CAAC;IACxC,KAAK,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAC;IACpC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,YAAY,CAAC,EAAE,OAAO,CAAC;IAEvB,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB,WAAW,CAAC,EAAE,OAAO,CAAC;IAEtB,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,wBAAsB,cAAc,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"analyze.d.ts","sourceRoot":"","sources":["../../src/commands/analyze.ts"],"names":[],"mappings":"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"}
|
package/dist/commands/analyze.js
CHANGED
|
@@ -690,9 +690,28 @@ async function analyzeCommand(options) {
|
|
|
690
690
|
}
|
|
691
691
|
// v7.4.3+ 兼容:先运行一次旧版 sanitize 迁移遗留文件(一次性)
|
|
692
692
|
await sanitizeSpecDirectories(iterDirForSkeleton);
|
|
693
|
-
// v8.
|
|
693
|
+
// v8.3.17+: 检测新旧结构,自动选择 manifest 生成方式
|
|
694
694
|
const phase = options.phase;
|
|
695
|
-
const
|
|
695
|
+
const isLegacy = await (0, spec_paths_1.isLegacySpecDir)(specDir);
|
|
696
|
+
let manifest;
|
|
697
|
+
if (isLegacy) {
|
|
698
|
+
// 旧结构:端平铺(020-specs/{端}/)
|
|
699
|
+
manifest = (0, spec_skeleton_1.computeAnalyzeManifest)(platforms, phase, iterName);
|
|
700
|
+
logger_1.logger.info(`📂 使用旧结构(端平铺): 020-specs/{端}/`);
|
|
701
|
+
}
|
|
702
|
+
else {
|
|
703
|
+
// 新结构:按需求文档名组织(020-specs/{需求名}/)
|
|
704
|
+
const features = await (0, spec_paths_1.parseFeatureList)(iterDirForSkeleton);
|
|
705
|
+
if (features.length > 0) {
|
|
706
|
+
manifest = (0, spec_skeleton_1.computeFeatureBasedAnalyzeManifest)(features, platforms, phase, iterName);
|
|
707
|
+
logger_1.logger.info(`📂 使用新结构(按需求文档组织): ${features.length} 个功能模块`);
|
|
708
|
+
}
|
|
709
|
+
else {
|
|
710
|
+
// 未检测到需求文档,回退到旧结构
|
|
711
|
+
manifest = (0, spec_skeleton_1.computeAnalyzeManifest)(platforms, phase, iterName);
|
|
712
|
+
logger_1.logger.info(`📂 未检测到需求文档,回退到旧结构(端平铺)`);
|
|
713
|
+
}
|
|
714
|
+
}
|
|
696
715
|
const skeletonResult = await (0, spec_skeleton_1.generateSkeleton)(specDir, manifest);
|
|
697
716
|
if (skeletonResult.created.length > 0) {
|
|
698
717
|
logger_1.logger.info(`🦴 已创建 ${skeletonResult.created.length} 个文件骨架: ${skeletonResult.created.join(', ')}`);
|
|
@@ -1079,6 +1098,12 @@ async function analyzeCommand(options) {
|
|
|
1079
1098
|
await (0, fs_extra_1.ensureDir)(specDir);
|
|
1080
1099
|
const globalSet = new Set(spec_paths_1.GLOBAL_SPEC_FILES);
|
|
1081
1100
|
const validPlatforms = new Set([spec_paths_1.GLOBAL_SPECS_DIR, ...(await (0, spec_paths_1.parsePlatformList)())]);
|
|
1101
|
+
// v8.3.17+: 检测是否为新结构(按需求文档组织)
|
|
1102
|
+
const isFeatureBased = !await (0, spec_paths_1.isLegacySpecDir)(specDir);
|
|
1103
|
+
const features = isFeatureBased ? await (0, spec_paths_1.parseFeatureList)(iterDir) : [];
|
|
1104
|
+
if (isFeatureBased && features.length > 0) {
|
|
1105
|
+
logger_1.logger.info(` 📂 新结构检测到 ${features.length} 个功能模块,按需求文档名路由文档`);
|
|
1106
|
+
}
|
|
1082
1107
|
let skippedCount = 0;
|
|
1083
1108
|
for (const [filename, content] of Object.entries(docs)) {
|
|
1084
1109
|
// v8.2.0+: 需求文档写入黄金需求目录 020-specs/requirements/
|
|
@@ -1135,6 +1160,50 @@ async function analyzeCommand(options) {
|
|
|
1135
1160
|
if (cleanFilename.startsWith(`${spec_paths_1.GLOBAL_SPECS_DIR}/`)) {
|
|
1136
1161
|
cleanFilename = cleanFilename.slice(`${spec_paths_1.GLOBAL_SPECS_DIR}/`.length);
|
|
1137
1162
|
}
|
|
1163
|
+
// v8.3.17+: 新结构路由 — 按需求文档名组织的目录结构
|
|
1164
|
+
if (isFeatureBased && cleanFilename.includes('/')) {
|
|
1165
|
+
const parts = cleanFilename.split('/');
|
|
1166
|
+
const firstPart = parts[0];
|
|
1167
|
+
// 全局文档:overview/xxx → 写入 020-specs/overview/
|
|
1168
|
+
if (firstPart === spec_paths_1.GLOBAL_SPECS_DIR) {
|
|
1169
|
+
const targetDir = (0, path_1.join)(specDir, spec_paths_1.GLOBAL_SPECS_DIR);
|
|
1170
|
+
await (0, fs_extra_1.ensureDir)(targetDir);
|
|
1171
|
+
const fp = (0, path_1.join)(targetDir, parts.slice(1).join('/'));
|
|
1172
|
+
if (!(await (0, task_utils_1.shouldOverwrite)(fp, !!options.interactive))) {
|
|
1173
|
+
logger_1.logger.info(` ⏭️ 跳过: ${filename}`);
|
|
1174
|
+
continue;
|
|
1175
|
+
}
|
|
1176
|
+
const bk = await (0, task_utils_1.backupWithTimestamp)(fp);
|
|
1177
|
+
if (bk) {
|
|
1178
|
+
backups.push(bk);
|
|
1179
|
+
logger_1.logger.info(` 📦 ${filename} 旧版已备份: ${bk.split('/').pop()}`);
|
|
1180
|
+
}
|
|
1181
|
+
await (0, fs_extra_1.writeFile)(fp, content);
|
|
1182
|
+
count++;
|
|
1183
|
+
continue;
|
|
1184
|
+
}
|
|
1185
|
+
// 功能模块路径:验证第二级是 overview/ 或合法端名
|
|
1186
|
+
if (!validPlatforms.has(firstPart) && parts.length >= 2) {
|
|
1187
|
+
const secondPart = parts[1];
|
|
1188
|
+
if (secondPart === 'overview' || validPlatforms.has(secondPart)) {
|
|
1189
|
+
const targetDir = (0, path_1.join)(specDir, ...parts.slice(0, -1));
|
|
1190
|
+
await (0, fs_extra_1.ensureDir)(targetDir);
|
|
1191
|
+
const fp = (0, path_1.join)(targetDir, parts[parts.length - 1]);
|
|
1192
|
+
if (!(await (0, task_utils_1.shouldOverwrite)(fp, !!options.interactive))) {
|
|
1193
|
+
logger_1.logger.info(` ⏭️ 跳过: ${filename}`);
|
|
1194
|
+
continue;
|
|
1195
|
+
}
|
|
1196
|
+
const bk = await (0, task_utils_1.backupWithTimestamp)(fp);
|
|
1197
|
+
if (bk) {
|
|
1198
|
+
backups.push(bk);
|
|
1199
|
+
logger_1.logger.info(` 📦 ${filename} 旧版已备份: ${bk.split('/').pop()}`);
|
|
1200
|
+
}
|
|
1201
|
+
await (0, fs_extra_1.writeFile)(fp, content);
|
|
1202
|
+
count++;
|
|
1203
|
+
continue;
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
1138
1207
|
// v7.4.0+: 如果 AI 携带了其他未知目录前缀(非端名),剥离前缀只保留文件名
|
|
1139
1208
|
if (cleanFilename.includes('/')) {
|
|
1140
1209
|
const prefix = cleanFilename.split('/')[0];
|
|
@@ -1878,6 +1947,20 @@ async function sanitizeSpecDirectories(iterDir) {
|
|
|
1878
1947
|
}
|
|
1879
1948
|
// 白名单校验:非 overview/ 且非端名的目录 → 非法
|
|
1880
1949
|
if (!validDirs.has(entry)) {
|
|
1950
|
+
// v8.3.17+: 新结构下允许功能模块目录存在
|
|
1951
|
+
const isLegacy = await (0, spec_paths_1.isLegacySpecDir)(specDir);
|
|
1952
|
+
if (!isLegacy) {
|
|
1953
|
+
// 新结构:只归档明显非法的目录名(纯数字、特殊符号等)
|
|
1954
|
+
if (/^\d+$/.test(entry) || /^[\.\-\*_\s]+$/.test(entry)) {
|
|
1955
|
+
const archivedName = `${entry}.invalid-${Date.now()}`;
|
|
1956
|
+
await (0, fs_extra_1.rename)(entryPath, (0, path_1.join)(specDir, archivedName));
|
|
1957
|
+
logger_1.logger.warn(`⚠️ 非法目录已归档: 020-specs/${entry}/ → ${archivedName}/`);
|
|
1958
|
+
illegalDirCount++;
|
|
1959
|
+
}
|
|
1960
|
+
// 其他目录允许存在(功能模块目录)
|
|
1961
|
+
continue;
|
|
1962
|
+
}
|
|
1963
|
+
// 旧结构:非 overview/ 且非端名的目录 → 非法
|
|
1881
1964
|
const archivedName = `${entry}.invalid-${Date.now()}`;
|
|
1882
1965
|
await (0, fs_extra_1.rename)(entryPath, (0, path_1.join)(specDir, archivedName));
|
|
1883
1966
|
logger_1.logger.warn(`⚠️ 非法目录已归档: 020-specs/${entry}/ → ${archivedName}/`);
|