speccore 8.3.121 → 8.3.127
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/cli.js +1 -1
- package/dist/cli.js.map +1 -1
- package/dist/commands/about.d.ts.map +1 -1
- package/dist/commands/about.js +16 -0
- package/dist/commands/about.js.map +1 -1
- package/dist/commands/analyze.d.ts +1 -0
- package/dist/commands/analyze.d.ts.map +1 -1
- package/dist/commands/analyze.js +198 -70
- package/dist/commands/analyze.js.map +1 -1
- package/dist/commands/change.js +14 -7
- package/dist/commands/change.js.map +1 -1
- package/dist/commands/context.js +25 -31
- package/dist/commands/context.js.map +1 -1
- package/dist/commands/deploy.d.ts.map +1 -1
- package/dist/commands/deploy.js +6 -1
- package/dist/commands/deploy.js.map +1 -1
- package/dist/commands/done.js +29 -28
- package/dist/commands/done.js.map +1 -1
- package/dist/commands/execute.d.ts.map +1 -1
- package/dist/commands/execute.js +281 -20
- package/dist/commands/execute.js.map +1 -1
- package/dist/commands/import.js +1 -1
- package/dist/commands/import.js.map +1 -1
- package/dist/commands/init.d.ts +1 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +513 -28
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/iteration/create.js +1 -1
- package/dist/commands/iteration/split.d.ts.map +1 -1
- package/dist/commands/iteration/split.js +158 -70
- package/dist/commands/iteration/split.js.map +1 -1
- package/dist/commands/iteration-from-global.js +1 -2
- package/dist/commands/iteration-from-global.js.map +1 -1
- package/dist/commands/pattern.js +30 -16
- package/dist/commands/pattern.js.map +1 -1
- package/dist/commands/pipeline.d.ts.map +1 -1
- package/dist/commands/pipeline.js +15 -4
- package/dist/commands/pipeline.js.map +1 -1
- package/dist/commands/retro.d.ts.map +1 -1
- package/dist/commands/retro.js +15 -8
- package/dist/commands/retro.js.map +1 -1
- package/dist/commands/status-panel.js +1 -1
- package/dist/commands/status-panel.js.map +1 -1
- package/dist/commands/sync.js +15 -8
- package/dist/commands/sync.js.map +1 -1
- package/dist/commands/trace.js +27 -24
- package/dist/commands/trace.js.map +1 -1
- package/dist/commands/update-env-configs.d.ts.map +1 -1
- package/dist/commands/update-env-configs.js +33 -5
- package/dist/commands/update-env-configs.js.map +1 -1
- package/dist/commands/update.d.ts.map +1 -1
- package/dist/commands/update.js +35 -0
- package/dist/commands/update.js.map +1 -1
- package/dist/commands/verify.d.ts +1 -1
- package/dist/commands/verify.d.ts.map +1 -1
- package/dist/commands/verify.js +25 -0
- package/dist/commands/verify.js.map +1 -1
- package/dist/core/ai-context-generator.js +35 -22
- package/dist/core/ai-context-generator.js.map +1 -1
- package/dist/core/ai-impact-analyzer.d.ts.map +1 -1
- package/dist/core/ai-impact-analyzer.js +1 -1
- package/dist/core/ai-impact-analyzer.js.map +1 -1
- package/dist/core/analyze-engine.d.ts +2 -1
- package/dist/core/analyze-engine.d.ts.map +1 -1
- package/dist/core/analyze-engine.js +217 -53
- package/dist/core/analyze-engine.js.map +1 -1
- package/dist/core/ask-engine.d.ts.map +1 -1
- package/dist/core/ask-engine.js +31 -13
- package/dist/core/ask-engine.js.map +1 -1
- package/dist/core/change-detection.d.ts.map +1 -1
- package/dist/core/change-detection.js +21 -5
- package/dist/core/change-detection.js.map +1 -1
- package/dist/core/code-scanner.d.ts +12 -3
- package/dist/core/code-scanner.d.ts.map +1 -1
- package/dist/core/code-scanner.js +376 -10
- package/dist/core/code-scanner.js.map +1 -1
- package/dist/core/context-builder.d.ts +2 -1
- package/dist/core/context-builder.d.ts.map +1 -1
- package/dist/core/context-builder.js +104 -25
- package/dist/core/context-builder.js.map +1 -1
- package/dist/core/deploy/engine.d.ts.map +1 -1
- package/dist/core/deploy/engine.js +21 -6
- package/dist/core/deploy/engine.js.map +1 -1
- package/dist/core/git-integration.d.ts +5 -1
- package/dist/core/git-integration.d.ts.map +1 -1
- package/dist/core/git-integration.js +52 -15
- package/dist/core/git-integration.js.map +1 -1
- package/dist/core/knowledge-graph.d.ts +2 -0
- package/dist/core/knowledge-graph.d.ts.map +1 -1
- package/dist/core/knowledge-graph.js +246 -232
- package/dist/core/knowledge-graph.js.map +1 -1
- package/dist/core/name-validator.js +1 -1
- package/dist/core/name-validator.js.map +1 -1
- package/dist/core/pipeline-test.d.ts.map +1 -1
- package/dist/core/pipeline-test.js +7 -4
- package/dist/core/pipeline-test.js.map +1 -1
- package/dist/core/prompt-builder.d.ts +3 -0
- package/dist/core/prompt-builder.d.ts.map +1 -1
- package/dist/core/prompt-builder.js +629 -77
- package/dist/core/prompt-builder.js.map +1 -1
- package/dist/core/rag-engine.d.ts +10 -5
- package/dist/core/rag-engine.d.ts.map +1 -1
- package/dist/core/rag-engine.js +134 -16
- package/dist/core/rag-engine.js.map +1 -1
- package/dist/core/spec-paths.d.ts +42 -2
- package/dist/core/spec-paths.d.ts.map +1 -1
- package/dist/core/spec-paths.js +207 -11
- package/dist/core/spec-paths.js.map +1 -1
- package/dist/core/structured-extractor.d.ts +67 -0
- package/dist/core/structured-extractor.d.ts.map +1 -1
- package/dist/core/structured-extractor.js +1298 -23
- package/dist/core/structured-extractor.js.map +1 -1
- package/dist/core/test-config.d.ts +1 -1
- package/dist/core/test-config.d.ts.map +1 -1
- package/dist/core/test-config.js +85 -65
- package/dist/core/test-config.js.map +1 -1
- package/dist/core/ui-verify/report-generator.d.ts.map +1 -1
- package/dist/core/ui-verify/report-generator.js +7 -3
- package/dist/core/ui-verify/report-generator.js.map +1 -1
- package/dist/core/ui-verify/smoke-engine.js +7 -1
- package/dist/core/ui-verify/smoke-engine.js.map +1 -1
- package/dist/core/unified-config.d.ts +43 -3
- package/dist/core/unified-config.d.ts.map +1 -1
- package/dist/core/unified-config.js +133 -13
- package/dist/core/unified-config.js.map +1 -1
- package/dist/core/unified-retrieval.d.ts.map +1 -1
- package/dist/core/unified-retrieval.js +41 -15
- package/dist/core/unified-retrieval.js.map +1 -1
- package/dist/core/unit-context-assembler.js +2 -2
- package/dist/core/unit-context-assembler.js.map +1 -1
- package/dist/core/validator.d.ts.map +1 -1
- package/dist/core/validator.js +59 -44
- package/dist/core/validator.js.map +1 -1
- package/dist/core/verify-engine.d.ts +9 -2
- package/dist/core/verify-engine.d.ts.map +1 -1
- package/dist/core/verify-engine.js +274 -108
- package/dist/core/verify-engine.js.map +1 -1
- package/dist/core/verify-report-aggregator.js +9 -20
- package/dist/core/verify-report-aggregator.js.map +1 -1
- package/dist/utils/synonyms.d.ts +24 -0
- package/dist/utils/synonyms.d.ts.map +1 -0
- package/dist/utils/synonyms.js +114 -0
- package/dist/utils/synonyms.js.map +1 -0
- package/package.json +1 -1
|
@@ -46,6 +46,7 @@ exports.getTaskContext = getTaskContext;
|
|
|
46
46
|
exports.isGraphStale = isGraphStale;
|
|
47
47
|
exports.incrementalUpdateKnowledgeGraph = incrementalUpdateKnowledgeGraph;
|
|
48
48
|
exports.refreshKnowledgeGraph = refreshKnowledgeGraph;
|
|
49
|
+
exports.loadFreshKnowledgeGraph = loadFreshKnowledgeGraph;
|
|
49
50
|
exports.traceDependencyChain = traceDependencyChain;
|
|
50
51
|
exports.getFullTaskContext = getFullTaskContext;
|
|
51
52
|
const fs_extra_1 = require("fs-extra");
|
|
@@ -309,113 +310,136 @@ async function scanBusinessCodeMappings(iterDir) {
|
|
|
309
310
|
const specsDir = (0, path_1.join)(iterDir, '020-specs');
|
|
310
311
|
if (!(await (0, fs_extra_1.pathExists)(specsDir)))
|
|
311
312
|
return { entities, relations };
|
|
312
|
-
//
|
|
313
|
-
|
|
313
|
+
// v8.3.122+: 同时兼容新旧两种路径结构
|
|
314
|
+
// 新结构: 020-specs/{feature}/{platform}/TECH.md
|
|
315
|
+
// 旧结构: 020-specs/{platform}/TECH.md
|
|
316
|
+
const knownNonPlatformDirs = new Set(['sources', 'assets', 'prototypes', 'converted', 'features', 'bugs', 'refactors', 'research', 'staging', 'platforms', 'snapshots', 'global', 'overview', 'requirements']);
|
|
314
317
|
const specsEntries = await (0, fs_extra_1.readdir)(specsDir, { withFileTypes: true });
|
|
315
|
-
for (const
|
|
316
|
-
if (!
|
|
318
|
+
for (const featureEntry of specsEntries) {
|
|
319
|
+
if (!featureEntry.isDirectory() || featureEntry.name.startsWith('_') || featureEntry.name.startsWith('.') || knownNonPlatformDirs.has(featureEntry.name))
|
|
317
320
|
continue;
|
|
318
|
-
const
|
|
319
|
-
const
|
|
320
|
-
|
|
321
|
+
const featureName = featureEntry.name;
|
|
322
|
+
const featureDir = (0, path_1.join)(specsDir, featureName);
|
|
323
|
+
// 检测是旧结构(直接有 TECH.md)还是新结构(有子目录)
|
|
324
|
+
const hasTechMdDirectly = await (0, fs_extra_1.pathExists)((0, path_1.join)(featureDir, 'TECH.md'));
|
|
325
|
+
if (hasTechMdDirectly) {
|
|
326
|
+
// 旧结构: 020-specs/{platform}/TECH.md — 把 featureName 当成 platformName
|
|
327
|
+
await processTechMdForMappings(featureDir, featureName, '', entities, relations);
|
|
328
|
+
}
|
|
329
|
+
else {
|
|
330
|
+
// 新结构: 020-specs/{feature}/{platform}/TECH.md
|
|
331
|
+
const platformEntries = await (0, fs_extra_1.readdir)(featureDir, { withFileTypes: true }).catch(() => []);
|
|
332
|
+
for (const platformEntry of platformEntries) {
|
|
333
|
+
if (!platformEntry.isDirectory() || platformEntry.name.startsWith('.') || knownNonPlatformDirs.has(platformEntry.name))
|
|
334
|
+
continue;
|
|
335
|
+
const platformDir = (0, path_1.join)(featureDir, platformEntry.name);
|
|
336
|
+
await processTechMdForMappings(platformDir, platformEntry.name, featureName, entities, relations);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
return { entities, relations };
|
|
341
|
+
}
|
|
342
|
+
/** 从单个 TECH.md 中提取业务-代码映射 */
|
|
343
|
+
async function processTechMdForMappings(dir, platformName, featureName, entities, relations) {
|
|
344
|
+
const techMdPath = (0, path_1.join)(dir, 'TECH.md');
|
|
345
|
+
if (!(await (0, fs_extra_1.pathExists)(techMdPath)))
|
|
346
|
+
return;
|
|
347
|
+
const content = await (0, fs_extra_1.readFile)(techMdPath, 'utf-8').catch(() => '');
|
|
348
|
+
if (!content)
|
|
349
|
+
return;
|
|
350
|
+
// 查找「业务-代码映射」章节
|
|
351
|
+
const mappingSectionMatch = content.match(/##\s+业务-代码映射[\s\S]*?(?=\n##\s|$)/i);
|
|
352
|
+
if (!mappingSectionMatch)
|
|
353
|
+
return;
|
|
354
|
+
const sectionContent = mappingSectionMatch[0];
|
|
355
|
+
const lines = sectionContent.split(/\r?\n/);
|
|
356
|
+
let inTable = false;
|
|
357
|
+
let headerParsed = false;
|
|
358
|
+
let colIndices = { module: -1, entity: -1, relation: -1, desc: -1 };
|
|
359
|
+
const seenModules = new Set();
|
|
360
|
+
for (const line of lines) {
|
|
361
|
+
const cells = line.split('|').map(c => c.trim()).filter(Boolean);
|
|
362
|
+
if (cells.length < 2)
|
|
321
363
|
continue;
|
|
322
|
-
|
|
323
|
-
|
|
364
|
+
if (!headerParsed && cells.some(c => c.includes('业务模块') || c.includes('代码实体'))) {
|
|
365
|
+
colIndices = {
|
|
366
|
+
module: cells.findIndex(c => c.includes('业务模块')),
|
|
367
|
+
entity: cells.findIndex(c => c.includes('代码实体')),
|
|
368
|
+
relation: cells.findIndex(c => c.includes('关系类型')),
|
|
369
|
+
desc: cells.findIndex(c => c.includes('说明')),
|
|
370
|
+
};
|
|
371
|
+
if (colIndices.module < 0)
|
|
372
|
+
colIndices.module = 0;
|
|
373
|
+
if (colIndices.entity < 0)
|
|
374
|
+
colIndices.entity = 1;
|
|
375
|
+
headerParsed = true;
|
|
376
|
+
inTable = true;
|
|
324
377
|
continue;
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
if (!mappingSectionMatch)
|
|
378
|
+
}
|
|
379
|
+
if (cells.every(c => /^[-:]+$/.test(c)))
|
|
328
380
|
continue;
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
const
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
// 数据行
|
|
363
|
-
const moduleName = cells[colIndices.module];
|
|
364
|
-
const codeEntity = cells[colIndices.entity];
|
|
365
|
-
const relationType = colIndices.relation >= 0 && colIndices.relation < cells.length ? cells[colIndices.relation] : 'maps_to';
|
|
366
|
-
const desc = colIndices.desc >= 0 && colIndices.desc < cells.length ? cells[colIndices.desc] : '';
|
|
367
|
-
if (!moduleName || !codeEntity)
|
|
368
|
-
continue;
|
|
369
|
-
if (moduleName === '业务模块' || moduleName === '#')
|
|
370
|
-
continue;
|
|
371
|
-
// 创建业务模块实体(去重)
|
|
372
|
-
const bizModuleId = `biz:${platformName}:${moduleName}`;
|
|
373
|
-
if (!seenModules.has(bizModuleId)) {
|
|
374
|
-
seenModules.add(bizModuleId);
|
|
375
|
-
entities.push({
|
|
376
|
-
id: bizModuleId,
|
|
377
|
-
type: 'business_module',
|
|
378
|
-
title: moduleName,
|
|
379
|
-
file: `020-specs/${platformName}/TECH.md`,
|
|
380
|
-
hash: '',
|
|
381
|
-
mtime: '',
|
|
382
|
-
platform: platformName,
|
|
383
|
-
businessModule: moduleName,
|
|
384
|
-
tags: ['business-mapping'],
|
|
385
|
-
});
|
|
386
|
-
// 关联到对应的 spec 实体
|
|
387
|
-
const specId = `SPEC:${platformName}/TECH`;
|
|
388
|
-
relations.push({
|
|
389
|
-
from: bizModuleId,
|
|
390
|
-
to: specId,
|
|
391
|
-
type: 'elaborates',
|
|
392
|
-
metadata: { source: 'tech-md-mapping' },
|
|
393
|
-
});
|
|
394
|
-
}
|
|
395
|
-
// 创建代码实体(作为 business_module 的关联目标)
|
|
396
|
-
const codeEntityId = `code:${platformName}:${codeEntity.replace(/[/\\]/g, '-')}`;
|
|
397
|
-
if (!entities.find(e => e.id === codeEntityId)) {
|
|
398
|
-
entities.push({
|
|
399
|
-
id: codeEntityId,
|
|
400
|
-
type: 'source-file',
|
|
401
|
-
title: codeEntity,
|
|
402
|
-
file: codeEntity,
|
|
403
|
-
hash: '',
|
|
404
|
-
mtime: '',
|
|
405
|
-
platform: platformName,
|
|
406
|
-
tags: ['code-entity', relationType],
|
|
407
|
-
});
|
|
408
|
-
}
|
|
409
|
-
// 创建关系
|
|
381
|
+
if (!inTable || !headerParsed)
|
|
382
|
+
continue;
|
|
383
|
+
const moduleName = cells[colIndices.module];
|
|
384
|
+
const codeEntity = cells[colIndices.entity];
|
|
385
|
+
const relationType = colIndices.relation >= 0 && colIndices.relation < cells.length ? cells[colIndices.relation] : 'maps_to';
|
|
386
|
+
const desc = colIndices.desc >= 0 && colIndices.desc < cells.length ? cells[colIndices.desc] : '';
|
|
387
|
+
if (!moduleName || !codeEntity)
|
|
388
|
+
continue;
|
|
389
|
+
if (moduleName === '业务模块' || moduleName === '#')
|
|
390
|
+
continue;
|
|
391
|
+
// v8.3.122+: bizModuleId 包含 featureName 避免跨模块冲突
|
|
392
|
+
const bizModuleId = featureName
|
|
393
|
+
? `biz:${featureName}:${platformName}:${moduleName}`
|
|
394
|
+
: `biz:${platformName}:${moduleName}`;
|
|
395
|
+
const filePath = featureName
|
|
396
|
+
? `020-specs/${featureName}/${platformName}/TECH.md`
|
|
397
|
+
: `020-specs/${platformName}/TECH.md`;
|
|
398
|
+
const specId = featureName
|
|
399
|
+
? `SPEC:${featureName}/${platformName}/TECH`
|
|
400
|
+
: `SPEC:${platformName}/TECH`;
|
|
401
|
+
if (!seenModules.has(bizModuleId)) {
|
|
402
|
+
seenModules.add(bizModuleId);
|
|
403
|
+
entities.push({
|
|
404
|
+
id: bizModuleId,
|
|
405
|
+
type: 'business_module',
|
|
406
|
+
title: moduleName,
|
|
407
|
+
file: filePath,
|
|
408
|
+
hash: '',
|
|
409
|
+
mtime: '',
|
|
410
|
+
platform: platformName,
|
|
411
|
+
businessModule: moduleName,
|
|
412
|
+
tags: ['business-mapping'],
|
|
413
|
+
});
|
|
410
414
|
relations.push({
|
|
411
415
|
from: bizModuleId,
|
|
412
|
-
to:
|
|
413
|
-
type:
|
|
414
|
-
metadata: {
|
|
416
|
+
to: specId,
|
|
417
|
+
type: 'elaborates',
|
|
418
|
+
metadata: { source: 'tech-md-mapping' },
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
const codeEntityId = featureName
|
|
422
|
+
? `code:${featureName}:${platformName}:${codeEntity.replace(/[/\\]/g, '-')}`
|
|
423
|
+
: `code:${platformName}:${codeEntity.replace(/[/\\]/g, '-')}`;
|
|
424
|
+
if (!entities.find(e => e.id === codeEntityId)) {
|
|
425
|
+
entities.push({
|
|
426
|
+
id: codeEntityId,
|
|
427
|
+
type: 'source-file',
|
|
428
|
+
title: codeEntity,
|
|
429
|
+
file: codeEntity,
|
|
430
|
+
hash: '',
|
|
431
|
+
mtime: '',
|
|
432
|
+
platform: platformName,
|
|
433
|
+
tags: ['code-entity', relationType],
|
|
415
434
|
});
|
|
416
435
|
}
|
|
436
|
+
relations.push({
|
|
437
|
+
from: bizModuleId,
|
|
438
|
+
to: codeEntityId,
|
|
439
|
+
type: relationType || 'maps_to',
|
|
440
|
+
metadata: { description: desc, source: 'tech-md-mapping' },
|
|
441
|
+
});
|
|
417
442
|
}
|
|
418
|
-
return { entities, relations };
|
|
419
443
|
}
|
|
420
444
|
// ═══════════════════════════════════════════════════
|
|
421
445
|
// 任务扫描(含子任务)
|
|
@@ -509,94 +533,84 @@ async function scanTasks(iterDir) {
|
|
|
509
533
|
status,
|
|
510
534
|
tags: [type],
|
|
511
535
|
});
|
|
512
|
-
//
|
|
536
|
+
// v8.3.121+: 端平铺结构扫描 — {端名}/{子任务}/
|
|
537
|
+
const EXCLUDE_DIRS = new Set(['00-specs', '_shared', '99-artifacts', '.meta']);
|
|
513
538
|
const dirEntries = await (0, fs_extra_1.readdir)(taskPath, { withFileTypes: true }).catch(() => []);
|
|
514
|
-
const
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
539
|
+
for (const de of dirEntries) {
|
|
540
|
+
if (!de.isDirectory() || de.name.startsWith('.') || EXCLUDE_DIRS.has(de.name))
|
|
541
|
+
continue;
|
|
542
|
+
if ((0, task_utils_1.isTimestampBackup)(de.name))
|
|
543
|
+
continue;
|
|
544
|
+
const platformPath = (0, path_1.join)(taskPath, de.name);
|
|
545
|
+
const subtaskEntries = await (0, fs_extra_1.readdir)(platformPath, { withFileTypes: true }).catch(() => []);
|
|
546
|
+
const hasSubtasks = subtaskEntries.some((se) => se.isDirectory() && !se.name.startsWith('.') && !(0, task_utils_1.isTimestampBackup)(se.name));
|
|
547
|
+
if (hasSubtasks) {
|
|
548
|
+
// 标准端平铺结构:{端名}/{子任务}/
|
|
549
|
+
for (const sub of subtaskEntries) {
|
|
550
|
+
if (!sub.isDirectory() || sub.name.startsWith('.') || (0, task_utils_1.isTimestampBackup)(sub.name))
|
|
524
551
|
continue;
|
|
525
|
-
const
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
if (
|
|
542
|
-
|
|
543
|
-
if (raw.includes('已完成') || raw.includes('completed'))
|
|
544
|
-
subStatus = 'completed';
|
|
545
|
-
else if (raw.includes('进行中') || raw.includes('in_progress'))
|
|
546
|
-
subStatus = 'in_progress';
|
|
547
|
-
}
|
|
548
|
-
const subIdMatch = subContent.match(/子任务 ID\*\*[:\s]*`(Task-[^`]+)`/);
|
|
549
|
-
if (subIdMatch)
|
|
550
|
-
subTaskId = subIdMatch[1];
|
|
552
|
+
const subtaskPath = (0, path_1.join)(platformPath, sub.name);
|
|
553
|
+
const platformLabel = `${de.name}/${sub.name}`;
|
|
554
|
+
const subtaskTaskMd = (0, path_1.join)(subtaskPath, 'TASK.md');
|
|
555
|
+
let subTitle = `${title} — ${platformLabel}`;
|
|
556
|
+
let subStatus = 'pending';
|
|
557
|
+
let subTaskId = `${taskId}-${de.name}-${sub.name}`;
|
|
558
|
+
if (await (0, fs_extra_1.pathExists)(subtaskTaskMd)) {
|
|
559
|
+
const subContent = await (0, fs_extra_1.readFile)(subtaskTaskMd, 'utf-8');
|
|
560
|
+
const titleMatch = subContent.match(/^#\s+(.+)/m);
|
|
561
|
+
if (titleMatch)
|
|
562
|
+
subTitle = titleMatch[1].trim().slice(0, 80);
|
|
563
|
+
const statusMatch = subContent.match(/\*\*状态\*\*[:\s]*(.+)/);
|
|
564
|
+
if (statusMatch) {
|
|
565
|
+
const raw = statusMatch[1].trim();
|
|
566
|
+
if (raw.includes('已完成') || raw.includes('completed'))
|
|
567
|
+
subStatus = 'completed';
|
|
568
|
+
else if (raw.includes('进行中') || raw.includes('in_progress'))
|
|
569
|
+
subStatus = 'in_progress';
|
|
551
570
|
}
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
571
|
+
const subIdMatch = subContent.match(/子任务 ID\*\*[:\s]*`(Task-[^`]+)`/);
|
|
572
|
+
if (subIdMatch)
|
|
573
|
+
subTaskId = subIdMatch[1];
|
|
574
|
+
}
|
|
575
|
+
// hash 回退:TASK.md → .meta/status → src/ → 空
|
|
576
|
+
let subHash = { hash: '', mtime: '' };
|
|
577
|
+
if (await (0, fs_extra_1.pathExists)(subtaskTaskMd)) {
|
|
578
|
+
subHash = await fileHash(subtaskTaskMd);
|
|
579
|
+
}
|
|
580
|
+
else {
|
|
581
|
+
const metaStatus = (0, path_1.join)(subtaskPath, '.meta', 'status');
|
|
582
|
+
if (await (0, fs_extra_1.pathExists)(metaStatus)) {
|
|
583
|
+
subHash = await fileHash(metaStatus);
|
|
556
584
|
}
|
|
557
585
|
else {
|
|
558
|
-
const
|
|
559
|
-
if (await (0, fs_extra_1.pathExists)(
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
if (await (0, fs_extra_1.pathExists)(srcDir)) {
|
|
565
|
-
try {
|
|
566
|
-
const srcFiles = await (0, fs_extra_1.readdir)(srcDir);
|
|
567
|
-
if (srcFiles.length > 0)
|
|
568
|
-
subHash = await fileHash((0, path_1.join)(srcDir, srcFiles[0]));
|
|
569
|
-
}
|
|
570
|
-
catch { /* 忽略 */ }
|
|
586
|
+
const srcDir = (0, path_1.join)(subtaskPath, 'src');
|
|
587
|
+
if (await (0, fs_extra_1.pathExists)(srcDir)) {
|
|
588
|
+
try {
|
|
589
|
+
const srcFiles = await (0, fs_extra_1.readdir)(srcDir);
|
|
590
|
+
if (srcFiles.length > 0)
|
|
591
|
+
subHash = await fileHash((0, path_1.join)(srcDir, srcFiles[0]));
|
|
571
592
|
}
|
|
593
|
+
catch { /* 忽略 */ }
|
|
572
594
|
}
|
|
573
595
|
}
|
|
574
|
-
entities.push({
|
|
575
|
-
id: subTaskId,
|
|
576
|
-
type: 'subtask',
|
|
577
|
-
title: subTitle,
|
|
578
|
-
file: `030-tasks/${type === 'feature' ? '' : type + '/'}${taskId}/${catDir}/${svc.name}/${sub.name}`,
|
|
579
|
-
hash: subHash.hash,
|
|
580
|
-
mtime: subHash.mtime,
|
|
581
|
-
status: subStatus,
|
|
582
|
-
platform: platformLabel,
|
|
583
|
-
parentTaskId: taskId,
|
|
584
|
-
});
|
|
585
|
-
relations.push({ from: subTaskId, to: taskId, type: 'subtask_of' });
|
|
586
596
|
}
|
|
597
|
+
entities.push({
|
|
598
|
+
id: subTaskId,
|
|
599
|
+
type: 'subtask',
|
|
600
|
+
title: subTitle,
|
|
601
|
+
file: `030-tasks/${type === 'feature' ? '' : type + '/'}${taskId}/${de.name}/${sub.name}`,
|
|
602
|
+
hash: subHash.hash,
|
|
603
|
+
mtime: subHash.mtime,
|
|
604
|
+
status: subStatus,
|
|
605
|
+
platform: platformLabel,
|
|
606
|
+
parentTaskId: taskId,
|
|
607
|
+
});
|
|
608
|
+
relations.push({ from: subTaskId, to: taskId, type: 'subtask_of' });
|
|
587
609
|
}
|
|
588
610
|
}
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
for (const de of dirEntries) {
|
|
593
|
-
if (!de.isDirectory())
|
|
594
|
-
continue;
|
|
595
|
-
if (de.name.startsWith('.') || de.name.startsWith('0') || de.name === '_shared' || de.name === '99-artifacts')
|
|
596
|
-
continue;
|
|
597
|
-
if ((0, task_utils_1.isTimestampBackup)(de.name))
|
|
598
|
-
continue;
|
|
599
|
-
const platformTaskMd = (0, path_1.join)(taskPath, de.name, 'TASK.md');
|
|
611
|
+
else {
|
|
612
|
+
// 兼容:端目录下无子任务,端本身作为子任务(如旧数据的单端任务)
|
|
613
|
+
const platformTaskMd = (0, path_1.join)(platformPath, 'TASK.md');
|
|
600
614
|
let subTitle = `${title} — ${de.name}`;
|
|
601
615
|
let subStatus = 'pending';
|
|
602
616
|
let subTaskId = `${taskId}-${de.name}`;
|
|
@@ -622,7 +636,7 @@ async function scanTasks(iterDir) {
|
|
|
622
636
|
subHash = await fileHash(platformTaskMd);
|
|
623
637
|
}
|
|
624
638
|
else {
|
|
625
|
-
const srcDir = (0, path_1.join)(
|
|
639
|
+
const srcDir = (0, path_1.join)(platformPath, 'src');
|
|
626
640
|
if (await (0, fs_extra_1.pathExists)(srcDir)) {
|
|
627
641
|
try {
|
|
628
642
|
const srcFiles = await (0, fs_extra_1.readdir)(srcDir);
|
|
@@ -1017,41 +1031,10 @@ async function scanTaskSpecs(iterDir) {
|
|
|
1017
1031
|
const taskDirs = await findTaskDirs(tasksDir);
|
|
1018
1032
|
for (const taskDir of taskDirs) {
|
|
1019
1033
|
const taskId = (0, path_1.basename)(taskDir) || '';
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
const entries = await (0, fs_extra_1.readdir)(taskDir, { withFileTypes: true });
|
|
1025
|
-
for (const e of entries) {
|
|
1026
|
-
if (!e.isDirectory() || e.name.startsWith('.') || (0, task_utils_1.isTimestampBackup)(e.name))
|
|
1027
|
-
continue;
|
|
1028
|
-
if (e.name === '10-backend' || e.name === '20-frontend') {
|
|
1029
|
-
// 新结构:深入两层(service/platform → subtask)
|
|
1030
|
-
const catPath = (0, path_1.join)(taskDir, e.name);
|
|
1031
|
-
try {
|
|
1032
|
-
const svcEntries = await (0, fs_extra_1.readdir)(catPath, { withFileTypes: true });
|
|
1033
|
-
for (const svc of svcEntries) {
|
|
1034
|
-
if (!svc.isDirectory())
|
|
1035
|
-
continue;
|
|
1036
|
-
const subEntries = await (0, fs_extra_1.readdir)((0, path_1.join)(catPath, svc.name), { withFileTypes: true });
|
|
1037
|
-
for (const sub of subEntries) {
|
|
1038
|
-
if (!sub.isDirectory() || sub.name.startsWith('.'))
|
|
1039
|
-
continue;
|
|
1040
|
-
nestedSubDirs.push(`${e.name}/${svc.name}/${sub.name}`);
|
|
1041
|
-
}
|
|
1042
|
-
}
|
|
1043
|
-
}
|
|
1044
|
-
catch { /* 跳过 */ }
|
|
1045
|
-
}
|
|
1046
|
-
else if (!e.name.startsWith('0') && e.name !== '_shared' && e.name !== '99-artifacts') {
|
|
1047
|
-
// 旧结构:扁平平台目录
|
|
1048
|
-
subDirs.push(e.name);
|
|
1049
|
-
}
|
|
1050
|
-
}
|
|
1051
|
-
}
|
|
1052
|
-
catch { /* 跳过 */ }
|
|
1053
|
-
// 扫描任务根下的子目录(_shared/、00-specs/、旧平台目录)
|
|
1054
|
-
for (const subDir of subDirs) {
|
|
1034
|
+
// v8.3.121+: 端平铺结构扫描 — {端名}/{子任务}/
|
|
1035
|
+
const EXCLUDE_DIRS = new Set(['00-specs', '_shared', '99-artifacts', '.meta']);
|
|
1036
|
+
// 扫描任务根下的规格目录(_shared/、00-specs/)
|
|
1037
|
+
for (const subDir of ['_shared', '00-specs']) {
|
|
1055
1038
|
const specDir = (0, path_1.join)(taskDir, subDir);
|
|
1056
1039
|
if (!(await (0, fs_extra_1.pathExists)(specDir)))
|
|
1057
1040
|
continue;
|
|
@@ -1074,31 +1057,44 @@ async function scanTaskSpecs(iterDir) {
|
|
|
1074
1057
|
relations.push({ from: `TSPEC:${taskId}-${subDir}-${specType}`, to: taskId, type: 'elaborates' });
|
|
1075
1058
|
}
|
|
1076
1059
|
}
|
|
1077
|
-
//
|
|
1078
|
-
|
|
1079
|
-
const
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
for (const specFile of TASK_SPEC_FILES) {
|
|
1083
|
-
const specPath = (0, path_1.join)(specDir, specFile);
|
|
1084
|
-
if (!(await (0, fs_extra_1.pathExists)(specPath)))
|
|
1060
|
+
// 扫描端平铺结构下的子任务规格文件
|
|
1061
|
+
try {
|
|
1062
|
+
const platformEntries = await (0, fs_extra_1.readdir)(taskDir, { withFileTypes: true });
|
|
1063
|
+
for (const pe of platformEntries) {
|
|
1064
|
+
if (!pe.isDirectory() || pe.name.startsWith('.') || EXCLUDE_DIRS.has(pe.name))
|
|
1085
1065
|
continue;
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
const
|
|
1089
|
-
const
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1066
|
+
if ((0, task_utils_1.isTimestampBackup)(pe.name))
|
|
1067
|
+
continue;
|
|
1068
|
+
const platformPath = (0, path_1.join)(taskDir, pe.name);
|
|
1069
|
+
const subtaskEntries = await (0, fs_extra_1.readdir)(platformPath, { withFileTypes: true });
|
|
1070
|
+
for (const sub of subtaskEntries) {
|
|
1071
|
+
if (!sub.isDirectory() || sub.name.startsWith('.') || (0, task_utils_1.isTimestampBackup)(sub.name))
|
|
1072
|
+
continue;
|
|
1073
|
+
const subtaskPath = (0, path_1.join)(platformPath, sub.name);
|
|
1074
|
+
const nestedDir = `${pe.name}/${sub.name}`;
|
|
1075
|
+
const label = `${pe.name}-${sub.name}`;
|
|
1076
|
+
for (const specFile of TASK_SPEC_FILES) {
|
|
1077
|
+
const specPath = (0, path_1.join)(subtaskPath, specFile);
|
|
1078
|
+
if (!(await (0, fs_extra_1.pathExists)(specPath)))
|
|
1079
|
+
continue;
|
|
1080
|
+
const { hash, mtime } = await fileHash(specPath);
|
|
1081
|
+
const title = await extractTitle(specPath);
|
|
1082
|
+
const specType = specFile.replace('.md', '').toLowerCase();
|
|
1083
|
+
entities.push({
|
|
1084
|
+
id: `TSPEC:${taskId}-${label}-${specType}`,
|
|
1085
|
+
type: 'task-spec',
|
|
1086
|
+
title: title || `${taskId}/${nestedDir}/${specFile}`,
|
|
1087
|
+
file: `030-tasks/**/${taskId}/${nestedDir}/${specFile}`,
|
|
1088
|
+
hash,
|
|
1089
|
+
mtime,
|
|
1090
|
+
tags: ['task-spec', specType, label],
|
|
1091
|
+
});
|
|
1092
|
+
relations.push({ from: `TSPEC:${taskId}-${label}-${specType}`, to: taskId, type: 'elaborates' });
|
|
1093
|
+
}
|
|
1094
|
+
}
|
|
1100
1095
|
}
|
|
1101
1096
|
}
|
|
1097
|
+
catch { /* 跳过 */ }
|
|
1102
1098
|
}
|
|
1103
1099
|
return { entities, relations };
|
|
1104
1100
|
}
|
|
@@ -1812,6 +1808,24 @@ async function incrementalUpdateKnowledgeGraph(cwd, iteration) {
|
|
|
1812
1808
|
async function refreshKnowledgeGraph(cwd, iteration) {
|
|
1813
1809
|
return incrementalUpdateKnowledgeGraph(cwd, iteration);
|
|
1814
1810
|
}
|
|
1811
|
+
/** v8.3.125+: 加载知识图谱(自动检查过期并刷新) */
|
|
1812
|
+
async function loadFreshKnowledgeGraph(cwd, iteration) {
|
|
1813
|
+
let graph = await loadKnowledgeGraph(cwd);
|
|
1814
|
+
if (!graph)
|
|
1815
|
+
return null;
|
|
1816
|
+
try {
|
|
1817
|
+
const stale = await isGraphStale(cwd, iteration);
|
|
1818
|
+
if (stale) {
|
|
1819
|
+
graph = await refreshKnowledgeGraph(cwd, iteration);
|
|
1820
|
+
if (graph)
|
|
1821
|
+
logger_1.logger?.info?.('🧠 知识图谱已自动刷新(检测到变更)');
|
|
1822
|
+
}
|
|
1823
|
+
}
|
|
1824
|
+
catch {
|
|
1825
|
+
// 过期检查失败不阻断,使用现有图谱
|
|
1826
|
+
}
|
|
1827
|
+
return graph;
|
|
1828
|
+
}
|
|
1815
1829
|
/**
|
|
1816
1830
|
* 追踪任务的完整依赖链路(向上追溯)
|
|
1817
1831
|
* 递归追踪 implements → references → depends_on 关系
|