speccore 6.5.1 → 6.10.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 (108) hide show
  1. package/.agents/skills/spec-ask/SKILL.md +6 -0
  2. package/.agents/skills/spec-reindex/SKILL.md +40 -0
  3. package/.agents/skills/speccore-router/SKILL.md +9 -0
  4. package/dist/cli.js +9 -0
  5. package/dist/cli.js.map +1 -1
  6. package/dist/commands/about.d.ts.map +1 -1
  7. package/dist/commands/about.js +10 -3
  8. package/dist/commands/about.js.map +1 -1
  9. package/dist/commands/analyze.d.ts +2 -0
  10. package/dist/commands/analyze.d.ts.map +1 -1
  11. package/dist/commands/analyze.js +141 -0
  12. package/dist/commands/analyze.js.map +1 -1
  13. package/dist/commands/change.d.ts.map +1 -1
  14. package/dist/commands/change.js +3 -0
  15. package/dist/commands/change.js.map +1 -1
  16. package/dist/commands/doc2spec.d.ts +1 -0
  17. package/dist/commands/doc2spec.d.ts.map +1 -1
  18. package/dist/commands/doc2spec.js +175 -0
  19. package/dist/commands/doc2spec.js.map +1 -1
  20. package/dist/commands/execute.d.ts.map +1 -1
  21. package/dist/commands/execute.js +20 -6
  22. package/dist/commands/execute.js.map +1 -1
  23. package/dist/commands/help.js +32 -5
  24. package/dist/commands/help.js.map +1 -1
  25. package/dist/commands/iteration/create.d.ts.map +1 -1
  26. package/dist/commands/iteration/create.js +38 -10
  27. package/dist/commands/iteration/create.js.map +1 -1
  28. package/dist/commands/iteration/split.d.ts.map +1 -1
  29. package/dist/commands/iteration/split.js +125 -9
  30. package/dist/commands/iteration/split.js.map +1 -1
  31. package/dist/commands/rag-index.d.ts +19 -0
  32. package/dist/commands/rag-index.d.ts.map +1 -0
  33. package/dist/commands/rag-index.js +272 -0
  34. package/dist/commands/rag-index.js.map +1 -0
  35. package/dist/commands/refresh.d.ts +21 -0
  36. package/dist/commands/refresh.d.ts.map +1 -0
  37. package/dist/commands/refresh.js +185 -0
  38. package/dist/commands/refresh.js.map +1 -0
  39. package/dist/commands/sync-global.js +41 -0
  40. package/dist/commands/sync-global.js.map +1 -1
  41. package/dist/core/ai-context-generator.d.ts +2 -0
  42. package/dist/core/ai-context-generator.d.ts.map +1 -1
  43. package/dist/core/ai-context-generator.js +8 -6
  44. package/dist/core/ai-context-generator.js.map +1 -1
  45. package/dist/core/analyze-engine.d.ts +19 -0
  46. package/dist/core/analyze-engine.d.ts.map +1 -1
  47. package/dist/core/analyze-engine.js +326 -9
  48. package/dist/core/analyze-engine.js.map +1 -1
  49. package/dist/core/ask-engine.d.ts.map +1 -1
  50. package/dist/core/ask-engine.js +113 -4
  51. package/dist/core/ask-engine.js.map +1 -1
  52. package/dist/core/ask-host-ai.d.ts.map +1 -1
  53. package/dist/core/ask-host-ai.js +9 -6
  54. package/dist/core/ask-host-ai.js.map +1 -1
  55. package/dist/core/code-scanner.d.ts +24 -2
  56. package/dist/core/code-scanner.d.ts.map +1 -1
  57. package/dist/core/code-scanner.js +235 -5
  58. package/dist/core/code-scanner.js.map +1 -1
  59. package/dist/core/context-builder.d.ts.map +1 -1
  60. package/dist/core/context-builder.js +29 -7
  61. package/dist/core/context-builder.js.map +1 -1
  62. package/dist/core/decay-detector.d.ts +2 -2
  63. package/dist/core/decay-detector.d.ts.map +1 -1
  64. package/dist/core/decay-detector.js +149 -8
  65. package/dist/core/decay-detector.js.map +1 -1
  66. package/dist/core/git-integration.d.ts +50 -2
  67. package/dist/core/git-integration.d.ts.map +1 -1
  68. package/dist/core/git-integration.js +217 -27
  69. package/dist/core/git-integration.js.map +1 -1
  70. package/dist/core/global-knowledge.d.ts +25 -0
  71. package/dist/core/global-knowledge.d.ts.map +1 -0
  72. package/dist/core/global-knowledge.js +228 -0
  73. package/dist/core/global-knowledge.js.map +1 -0
  74. package/dist/core/index-guard.d.ts +57 -0
  75. package/dist/core/index-guard.d.ts.map +1 -0
  76. package/dist/core/index-guard.js +202 -0
  77. package/dist/core/index-guard.js.map +1 -0
  78. package/dist/core/intent-cache.d.ts +3 -1
  79. package/dist/core/intent-cache.d.ts.map +1 -1
  80. package/dist/core/intent-cache.js +33 -3
  81. package/dist/core/intent-cache.js.map +1 -1
  82. package/dist/core/knowledge-graph.d.ts +7 -1
  83. package/dist/core/knowledge-graph.d.ts.map +1 -1
  84. package/dist/core/knowledge-graph.js +265 -32
  85. package/dist/core/knowledge-graph.js.map +1 -1
  86. package/dist/core/platform-registry.d.ts +7 -2
  87. package/dist/core/platform-registry.d.ts.map +1 -1
  88. package/dist/core/platform-registry.js +57 -4
  89. package/dist/core/platform-registry.js.map +1 -1
  90. package/dist/core/prompt-builder.d.ts +3 -2
  91. package/dist/core/prompt-builder.d.ts.map +1 -1
  92. package/dist/core/prompt-builder.js +231 -71
  93. package/dist/core/prompt-builder.js.map +1 -1
  94. package/dist/core/rag-engine.d.ts +152 -0
  95. package/dist/core/rag-engine.d.ts.map +1 -0
  96. package/dist/core/rag-engine.js +816 -0
  97. package/dist/core/rag-engine.js.map +1 -0
  98. package/dist/core/reindex-engine.js +5 -5
  99. package/dist/core/reindex-engine.js.map +1 -1
  100. package/dist/core/task-paths.d.ts +2 -2
  101. package/dist/core/task-paths.d.ts.map +1 -1
  102. package/dist/core/task-paths.js +6 -4
  103. package/dist/core/task-paths.js.map +1 -1
  104. package/dist/core/unified-retrieval.d.ts +140 -0
  105. package/dist/core/unified-retrieval.d.ts.map +1 -0
  106. package/dist/core/unified-retrieval.js +466 -0
  107. package/dist/core/unified-retrieval.js.map +1 -0
  108. package/package.json +1 -1
@@ -20,9 +20,25 @@ const logger_1 = require("../utils/logger");
20
20
  // 常量
21
21
  // ═══════════════════════════════════════════════════════════
22
22
  const CACHE_PATH = '.speccore/local/intent-cache.json';
23
- const CACHE_VERSION = '1.0';
23
+ const CACHE_VERSION = '1.1'; // 升级到 1.1 支持 normalizedInput
24
24
  const MAX_ENTRIES = 200;
25
25
  const FUZZY_THRESHOLD = 2; // 编辑距离阈值
26
+ /** 停用词表 */
27
+ const NORMALIZE_STOP_WORDS = new Set([
28
+ '的', '与', '和', '及', '在', '中', '对', '为', '是', '有', '从', '到',
29
+ 'the', 'a', 'an', 'and', 'or', 'to', 'of', 'in', 'for', 'on', 'with', 'by',
30
+ '请', '帮我', '给我', '一下', '一个', '这个', '那个',
31
+ ]);
32
+ /** 将输入归一化为语义键(去停用词 + 排序) */
33
+ function normalizeInput(input) {
34
+ const tokens = input
35
+ .toLowerCase()
36
+ .replace(/[^\u4e00-\u9fa5a-z0-9\s]/g, ' ')
37
+ .split(/\s+/)
38
+ .filter(t => t.length >= 2 && !NORMALIZE_STOP_WORDS.has(t))
39
+ .sort();
40
+ return tokens.slice(0, 6).join('|'); // 取前6个关键词,用 | 分隔
41
+ }
26
42
  // ═══════════════════════════════════════════════════════════
27
43
  // 缓存读写
28
44
  // ═══════════════════════════════════════════════════════════
@@ -54,7 +70,7 @@ async function saveCache(cache) {
54
70
  // 公共 API
55
71
  // ═══════════════════════════════════════════════════════════
56
72
  /**
57
- * 查询缓存:先精确匹配,再模糊匹配
73
+ * 查询缓存:精确 → 归一化语义 → 编辑距离模糊
58
74
  */
59
75
  async function getCachedIntent(input) {
60
76
  const cache = await loadCache();
@@ -67,7 +83,20 @@ async function getCachedIntent(input) {
67
83
  logger_1.logger.info(`💾 意图缓存命中(精确): "${input.slice(0, 30)}..." → ${exact.result.summary.slice(0, 30)}`);
68
84
  return exact.result;
69
85
  }
70
- // 2. 模糊匹配(编辑距离 ≤ 2,且长度比例合理)
86
+ // 2. 归一化语义匹配("分析一下登录" ≈ "分析登录功能")
87
+ const normalized = normalizeInput(input);
88
+ if (normalized) {
89
+ for (const entry of Object.values(cache.entries)) {
90
+ if (entry.normalizedInput === normalized) {
91
+ entry.hitCount++;
92
+ entry.lastUsed = new Date().toISOString();
93
+ await saveCache(cache);
94
+ logger_1.logger.info(`💾 意图缓存命中(语义): "${input.slice(0, 30)}..." ≈ "${entry.input.slice(0, 30)}..."`);
95
+ return entry.result;
96
+ }
97
+ }
98
+ }
99
+ // 3. 模糊匹配(编辑距离 ≤ 2,且长度比例合理)
71
100
  // 防止短字符串误匹配:如"补充测试"匹配"补充分析"(编辑距离 2 但语义完全不同)
72
101
  for (const [cachedInput, entry] of Object.entries(cache.entries)) {
73
102
  const dist = levenshtein(input, cachedInput);
@@ -92,6 +121,7 @@ async function cacheIntent(input, result, source = 'host-ai') {
92
121
  const cache = await loadCache();
93
122
  cache.entries[input] = {
94
123
  input,
124
+ normalizedInput: normalizeInput(input),
95
125
  result,
96
126
  source,
97
127
  hitCount: 1,
@@ -1 +1 @@
1
- {"version":3,"file":"intent-cache.js","sourceRoot":"","sources":["../../src/core/intent-cache.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"intent-cache.js","sourceRoot":"","sources":["../../src/core/intent-cache.ts"],"names":[],"mappings":"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"}
@@ -41,14 +41,20 @@ export declare function buildKnowledgeGraph(cwd: string, iteration?: string): Pr
41
41
  /** 保存知识图谱到 cache 目录 */
42
42
  export declare function saveKnowledgeGraph(cwd: string, graph: KnowledgeGraph): Promise<string>;
43
43
  /** 加载已存在的知识图谱 */
44
+ /**
45
+ * 加载知识图谱(带进程缓存)
46
+ * 文件 mtime 未变时直接返回缓存,避免重复 JSON 解析
47
+ */
44
48
  export declare function loadKnowledgeGraph(cwd: string): Promise<KnowledgeGraph | null>;
45
49
  /** 获取指定任务的关联链(上游需求 + 关联子任务) */
46
50
  export declare function getTaskContext(graph: KnowledgeGraph, taskId: string): {
47
51
  requirement: GraphEntity | null;
48
52
  siblingSubtasks: GraphEntity[];
49
53
  parentTask: GraphEntity | null;
54
+ relatedSpecs: GraphEntity[];
55
+ dependsOn: GraphEntity[];
50
56
  };
51
- /** 检查知识图谱是否已过期 */
57
+ /** 检查知识图谱是否已过期(带 500ms 超时保护) */
52
58
  export declare function isGraphStale(cwd: string, iteration?: string): Promise<boolean>;
53
59
  /** 刷新知识图谱(重建 + 保存)—— 供命令完成后调用 */
54
60
  export declare function refreshKnowledgeGraph(cwd: string, iteration?: string): Promise<KnowledgeGraph | null>;
@@ -1 +1 @@
1
- {"version":3,"file":"knowledge-graph.d.ts","sourceRoot":"","sources":["../../src/core/knowledge-graph.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAaH,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACtC,SAAS,EAAE,aAAa,EAAE,CAAC;IAC3B,KAAK,EAAE,UAAU,CAAC;CACnB;AAED,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,aAAa,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,GAAG,WAAW,CAAC;IAChE,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,YAAY,GAAG,WAAW,GAAG,YAAY,GAAG,YAAY,GAAG,YAAY,CAAC;CAC/E;AAED,MAAM,WAAW,UAAU;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAiVD,wBAAsB,mBAAmB,CACvC,GAAG,EAAE,MAAM,EACX,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,cAAc,CAAC,CAoDzB;AAED,uBAAuB;AACvB,wBAAsB,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAM5F;AAED,iBAAiB;AACjB,wBAAsB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CASpF;AAED,+BAA+B;AAC/B,wBAAgB,cAAc,CAAC,KAAK,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,GAAG;IACrE,WAAW,EAAE,WAAW,GAAG,IAAI,CAAC;IAChC,eAAe,EAAE,WAAW,EAAE,CAAC;IAC/B,UAAU,EAAE,WAAW,GAAG,IAAI,CAAC;CAChC,CA+BA;AAMD,kBAAkB;AAClB,wBAAsB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CA2BpF;AAED,iCAAiC;AACjC,wBAAsB,qBAAqB,CACzC,GAAG,EAAE,MAAM,EACX,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAShC"}
1
+ {"version":3,"file":"knowledge-graph.d.ts","sourceRoot":"","sources":["../../src/core/knowledge-graph.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAyBH,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACtC,SAAS,EAAE,aAAa,EAAE,CAAC;IAC3B,KAAK,EAAE,UAAU,CAAC;CACnB;AAED,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,aAAa,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,GAAG,WAAW,CAAC;IAChE,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,YAAY,GAAG,WAAW,GAAG,YAAY,GAAG,YAAY,GAAG,YAAY,CAAC;CAC/E;AAED,MAAM,WAAW,UAAU;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAmeD,wBAAsB,mBAAmB,CACvC,GAAG,EAAE,MAAM,EACX,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,cAAc,CAAC,CAkEzB;AAED,uBAAuB;AACvB,wBAAsB,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAM5F;AAED,iBAAiB;AACjB;;;GAGG;AACH,wBAAsB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAkBpF;AAED,+BAA+B;AAC/B,wBAAgB,cAAc,CAAC,KAAK,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,GAAG;IACrE,WAAW,EAAE,WAAW,GAAG,IAAI,CAAC;IAChC,eAAe,EAAE,WAAW,EAAE,CAAC;IAC/B,UAAU,EAAE,WAAW,GAAG,IAAI,CAAC;IAC/B,YAAY,EAAE,WAAW,EAAE,CAAC;IAC5B,SAAS,EAAE,WAAW,EAAE,CAAC;CAC1B,CAwDA;AAiCD,gCAAgC;AAChC,wBAAsB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CA6BpF;AAED,iCAAiC;AACjC,wBAAsB,qBAAqB,CACzC,GAAG,EAAE,MAAM,EACX,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAShC"}
@@ -51,6 +51,8 @@ const crypto_1 = require("crypto");
51
51
  const context_1 = require("./context");
52
52
  const task_paths_1 = require("./task-paths");
53
53
  const task_utils_1 = require("../utils/task-utils");
54
+ const logger_1 = require("../utils/logger");
55
+ const kgCache = new Map();
54
56
  // ═══════════════════════════════════════════════
55
57
  // 文件扫描辅助
56
58
  // ═══════════════════════════════════════════════
@@ -104,11 +106,13 @@ async function scanRequirements(iterDir, iterName) {
104
106
  if (item.isDirectory()) {
105
107
  await scanDir(fullPath, `${relPath}/`);
106
108
  }
107
- else if (item.name.endsWith('.md') && item.name !== 'INDEX.md') {
109
+ else if (item.name.endsWith('.md') && item.name !== 'INDEX.md' && !(0, task_utils_1.isTimestampBackup)(item.name)) {
108
110
  const { hash, mtime } = await fileHash(fullPath);
109
111
  const title = await extractTitle(fullPath);
110
112
  const content = await (0, fs_extra_1.readFile)(fullPath, 'utf-8').catch(() => '');
111
- const reqId = extractReqId(content, item.name.replace('.md', ''));
113
+ // 用路径前缀确保 ID 唯一性,避免不同文件引用同一 REQ-xxx 时覆盖
114
+ const pathPrefix = relPath.replace(/\.md$/, '').replace(/\//g, '-');
115
+ const reqId = extractReqId(content, pathPrefix);
112
116
  entities.push({
113
117
  id: reqId,
114
118
  type: 'requirement',
@@ -116,7 +120,13 @@ async function scanRequirements(iterDir, iterName) {
116
120
  file: `010-requirements/${relPath}`,
117
121
  hash,
118
122
  mtime,
119
- tags: [prefix.includes('features/') ? 'feature' : 'requirement'],
123
+ tags: [
124
+ prefix.includes('features/') ? 'feature'
125
+ : prefix.includes('bugs/') ? 'bug'
126
+ : prefix.includes('refactors/') ? 'refactor'
127
+ : prefix.includes('research/') ? 'research'
128
+ : 'requirement'
129
+ ],
120
130
  });
121
131
  }
122
132
  }
@@ -177,6 +187,74 @@ async function scanSpecs(iterDir) {
177
187
  }
178
188
  }
179
189
  }
190
+ // 扫描 features/ 子目录(按功能模块的分析产出)
191
+ const featuresDir = (0, path_1.join)(specsDir, 'features');
192
+ if (await (0, fs_extra_1.pathExists)(featuresDir)) {
193
+ const featureEntries = await (0, fs_extra_1.readdir)(featuresDir, { withFileTypes: true });
194
+ for (const fe of featureEntries) {
195
+ if (fe.name.startsWith('.') || (0, task_utils_1.isTimestampBackup)(fe.name))
196
+ continue;
197
+ if (!fe.name.endsWith('.md'))
198
+ continue;
199
+ const fullPath = (0, path_1.join)(featuresDir, fe.name);
200
+ const { hash, mtime } = await fileHash(fullPath);
201
+ const title = await extractTitle(fullPath);
202
+ const featureName = fe.name.replace('.md', '');
203
+ entities.push({
204
+ id: `SPEC:features/${featureName}`,
205
+ type: 'spec',
206
+ title: title || featureName,
207
+ file: `020-specs/features/${fe.name}`,
208
+ hash,
209
+ mtime,
210
+ tags: ['feature-spec'],
211
+ });
212
+ // 关联到对应的需求 feature(同名匹配)
213
+ const reqFeaturePath = (0, path_1.join)('010-requirements', 'features', featureName);
214
+ const matchedReq = entities.find(e => e.file?.startsWith(reqFeaturePath));
215
+ if (matchedReq) {
216
+ relations.push({
217
+ from: `SPEC:features/${featureName}`,
218
+ to: matchedReq.id,
219
+ type: 'references',
220
+ });
221
+ }
222
+ }
223
+ }
224
+ // 扫描类型目录(bugs/refactors/research — 扁平文件)
225
+ for (const typeDir of ['bugs', 'refactors', 'research']) {
226
+ const typeDirPath = (0, path_1.join)(specsDir, typeDir);
227
+ if (!(await (0, fs_extra_1.pathExists)(typeDirPath)))
228
+ continue;
229
+ const typeEntries = await (0, fs_extra_1.readdir)(typeDirPath, { withFileTypes: true });
230
+ for (const te of typeEntries) {
231
+ if (!te.isFile() || !te.name.endsWith('.md') || (0, task_utils_1.isTimestampBackup)(te.name))
232
+ continue;
233
+ const fullPath = (0, path_1.join)(typeDirPath, te.name);
234
+ const { hash, mtime } = await fileHash(fullPath);
235
+ const title = await extractTitle(fullPath);
236
+ const slug = te.name.replace('.md', '');
237
+ entities.push({
238
+ id: `SPEC:${typeDir}/${slug}`,
239
+ type: 'spec',
240
+ title: title || slug,
241
+ file: `020-specs/${typeDir}/${te.name}`,
242
+ hash,
243
+ mtime,
244
+ tags: [`${typeDir}-spec`],
245
+ });
246
+ // 关联到对应的需求文件(同名匹配)
247
+ const reqPath = (0, path_1.join)('010-requirements', typeDir, te.name);
248
+ const matchedReq = entities.find(e => e.file === reqPath);
249
+ if (matchedReq) {
250
+ relations.push({
251
+ from: `SPEC:${typeDir}/${slug}`,
252
+ to: matchedReq.id,
253
+ type: 'references',
254
+ });
255
+ }
256
+ }
257
+ }
180
258
  return { entities, relations };
181
259
  }
182
260
  // ═══════════════════════════════════════════════
@@ -248,6 +326,8 @@ async function scanTasks(iterDir) {
248
326
  continue;
249
327
  if (de.name.startsWith('.') || de.name.startsWith('0') || de.name === '_shared' || de.name === '99-artifacts')
250
328
  continue;
329
+ if ((0, task_utils_1.isTimestampBackup)(de.name))
330
+ continue;
251
331
  const platformTaskMd = (0, path_1.join)(taskPath, de.name, 'TASK.md');
252
332
  if (!(await (0, fs_extra_1.pathExists)(platformTaskMd)))
253
333
  continue;
@@ -329,11 +409,12 @@ async function scanUserFiles(iterDir) {
329
409
  // ═══════════════════════════════════════════════
330
410
  // 关联推断
331
411
  // ═══════════════════════════════════════════════
332
- function inferRelations(entities) {
412
+ async function inferRelations(entities, iterDir) {
333
413
  const relations = [];
334
414
  const reqs = entities.filter(e => e.type === 'requirement');
335
415
  const tasks = entities.filter(e => e.type === 'task');
336
416
  const specs = entities.filter(e => e.type === 'spec');
417
+ const entityMap = new Map(entities.map(e => [e.id, e]));
337
418
  // 需求 → 任务:按编号匹配(REQ-001 → Task-001)
338
419
  for (const req of reqs) {
339
420
  const reqNum = req.id.match(/\d+/)?.[0];
@@ -346,8 +427,76 @@ function inferRelations(entities) {
346
427
  }
347
428
  }
348
429
  }
349
- // 规格 → 需求:spec 文件内容中引用了 REQ-xxx
350
- // (这里只做结构推断,实际内容分析在 CONTEXT.md 生成时做)
430
+ // 规格 → 需求:读取 spec 文件内容,提取 REQ-xxx 引用
431
+ for (const spec of specs) {
432
+ const specPath = (0, path_1.join)(iterDir, spec.file);
433
+ try {
434
+ const content = await (0, fs_extra_1.readFile)(specPath, 'utf-8');
435
+ const reqRefs = content.match(/REQ-\d+/g);
436
+ if (reqRefs) {
437
+ for (const ref of [...new Set(reqRefs)]) {
438
+ if (entityMap.has(ref)) {
439
+ relations.push({ from: spec.id, to: ref, type: 'specifies' });
440
+ }
441
+ }
442
+ }
443
+ }
444
+ catch { /* 文件不存在或无法读取,跳过 */ }
445
+ }
446
+ // 任务 → 需求 / 任务 → 规格:读取任务目录下的 REQ.md 和 TECH.md
447
+ for (const task of tasks) {
448
+ const taskAbsDir = (0, path_1.join)(iterDir, task.file);
449
+ const reqPaths = [
450
+ (0, path_1.join)(taskAbsDir, '_shared', 'REQ.md'),
451
+ (0, path_1.join)(taskAbsDir, '00-specs', 'REQ.md'),
452
+ (0, path_1.join)(taskAbsDir, 'REQ.md'),
453
+ ];
454
+ for (const reqPath of reqPaths) {
455
+ if (!(await (0, fs_extra_1.pathExists)(reqPath)))
456
+ continue;
457
+ try {
458
+ const content = await (0, fs_extra_1.readFile)(reqPath, 'utf-8');
459
+ // 提取 REQ-xxx 引用 → task implements req
460
+ const reqRefs = content.match(/REQ-\d+/g);
461
+ if (reqRefs) {
462
+ for (const ref of [...new Set(reqRefs)]) {
463
+ if (entityMap.has(ref) && !relations.some(r => r.from === task.id && r.to === ref && r.type === 'implements')) {
464
+ relations.push({ from: task.id, to: ref, type: 'implements' });
465
+ }
466
+ }
467
+ }
468
+ // 提取 SPEC:xxx 引用 → task references spec
469
+ const specRefs = content.match(/SPEC:[\w\/\-]+/g);
470
+ if (specRefs) {
471
+ for (const ref of [...new Set(specRefs)]) {
472
+ if (entityMap.has(ref) && !relations.some(r => r.from === task.id && r.to === ref && r.type === 'references')) {
473
+ relations.push({ from: task.id, to: ref, type: 'references' });
474
+ }
475
+ }
476
+ }
477
+ }
478
+ catch { /* 跳过 */ }
479
+ break; // 只读第一个存在的
480
+ }
481
+ // 读取 API_CONTRACT.yaml 解析依赖
482
+ const contractPath = (0, path_1.join)(taskAbsDir, '_shared', 'API_CONTRACT.yaml');
483
+ if (await (0, fs_extra_1.pathExists)(contractPath)) {
484
+ try {
485
+ const content = await (0, fs_extra_1.readFile)(contractPath, 'utf-8');
486
+ // 提取依赖的任务引用: dependsOn: Task-xxx 或 # Depends on Task-xxx
487
+ const depRefs = content.match(/(?:dependsOn|depends on|依赖)\s*[::]\s*(Task-\S+)/gi);
488
+ if (depRefs) {
489
+ for (const raw of depRefs) {
490
+ const m = raw.match(/(Task-\S+)/i);
491
+ if (m && entityMap.has(m[1]) && !relations.some(r => r.from === task.id && r.to === m[1] && r.type === 'depends_on')) {
492
+ relations.push({ from: task.id, to: m[1], type: 'depends_on' });
493
+ }
494
+ }
495
+ }
496
+ }
497
+ catch { /* 跳过 */ }
498
+ }
499
+ }
351
500
  return relations;
352
501
  }
353
502
  // ═══════════════════════════════════════════════
@@ -371,22 +520,35 @@ async function buildKnowledgeGraph(cwd, iteration) {
371
520
  const specResult = await scanSpecs(iterDir);
372
521
  const taskResult = await scanTasks(iterDir);
373
522
  const userFiles = await scanUserFiles(iterDir);
374
- // 合并实体
523
+ // 合并实体(处理 ID 冲突:同名实体用路径前缀去重)
375
524
  const allEntities = [
376
525
  ...reqResult.entities,
377
526
  ...specResult.entities,
378
527
  ...taskResult.entities,
379
528
  ...userFiles,
380
529
  ];
530
+ const idRemap = new Map();
381
531
  for (const e of allEntities) {
532
+ let finalId = e.id;
533
+ if (graph.entities[e.id]) {
534
+ finalId = `${e.id}@${e.file.replace(/\//g, '-')}`;
535
+ idRemap.set(e.id, finalId);
536
+ }
537
+ e.id = finalId;
382
538
  graph.entities[e.id] = e;
383
539
  }
540
+ // 同步更新已有关系中的旧 ID
541
+ const remapId = (id) => idRemap.get(id) || id;
542
+ for (const r of [...reqResult.relations, ...specResult.relations, ...taskResult.relations]) {
543
+ r.from = remapId(r.from);
544
+ r.to = remapId(r.to);
545
+ }
384
546
  // 合并关系
385
547
  graph.relations = [
386
548
  ...reqResult.relations,
387
549
  ...specResult.relations,
388
550
  ...taskResult.relations,
389
- ...inferRelations(allEntities),
551
+ ...(await inferRelations(allEntities, iterDir)),
390
552
  ];
391
553
  // 统计
392
554
  graph.stats = {
@@ -408,13 +570,24 @@ async function saveKnowledgeGraph(cwd, graph) {
408
570
  return filePath;
409
571
  }
410
572
  /** 加载已存在的知识图谱 */
573
+ /**
574
+ * 加载知识图谱(带进程缓存)
575
+ * 文件 mtime 未变时直接返回缓存,避免重复 JSON 解析
576
+ */
411
577
  async function loadKnowledgeGraph(cwd) {
412
578
  const filePath = (0, path_1.join)(cwd, '.speccore', 'cache', 'knowledge-graph.json');
413
579
  if (!(await (0, fs_extra_1.pathExists)(filePath)))
414
580
  return null;
415
581
  try {
582
+ const st = await (0, fs_extra_1.stat)(filePath);
583
+ const cached = kgCache.get(filePath);
584
+ if (cached && cached.mtime >= st.mtimeMs) {
585
+ return cached.graph;
586
+ }
416
587
  const content = await (0, fs_extra_1.readFile)(filePath, 'utf-8');
417
- return JSON.parse(content);
588
+ const graph = JSON.parse(content);
589
+ kgCache.set(filePath, { graph, mtime: st.mtimeMs });
590
+ return graph;
418
591
  }
419
592
  catch {
420
593
  return null;
@@ -424,35 +597,95 @@ async function loadKnowledgeGraph(cwd) {
424
597
  function getTaskContext(graph, taskId) {
425
598
  const entity = graph.entities[taskId];
426
599
  if (!entity) {
427
- return { requirement: null, siblingSubtasks: [], parentTask: null };
600
+ return { requirement: null, siblingSubtasks: [], parentTask: null, relatedSpecs: [], dependsOn: [] };
428
601
  }
429
602
  let requirement = null;
430
603
  let parentTask = null;
431
- const siblingSubtasks = [];
432
- // 找上游需求
604
+ const relatedSpecs = [];
605
+ const dependsOn = [];
606
+ // 预建索引:避免多次遍历全量关系
607
+ const parentIndex = new Map();
608
+ for (const e of Object.values(graph.entities)) {
609
+ if (e.type === 'subtask' && e.parentTaskId) {
610
+ const list = parentIndex.get(e.parentTaskId) || [];
611
+ list.push(e);
612
+ parentIndex.set(e.parentTaskId, list);
613
+ }
614
+ }
615
+ // 找上游需求、关联规格、依赖任务
433
616
  for (const rel of graph.relations) {
434
- if (rel.from === taskId && rel.type === 'implements') {
435
- requirement = graph.entities[rel.to] || null;
617
+ if (rel.from === taskId) {
618
+ if (rel.type === 'implements')
619
+ requirement = graph.entities[rel.to] || null;
620
+ if (rel.type === 'references') {
621
+ const spec = graph.entities[rel.to];
622
+ if (spec)
623
+ relatedSpecs.push(spec);
624
+ }
625
+ if (rel.type === 'depends_on') {
626
+ const dep = graph.entities[rel.to];
627
+ if (dep)
628
+ dependsOn.push(dep);
629
+ }
630
+ if (rel.type === 'subtask_of')
631
+ parentTask = graph.entities[rel.to] || null;
436
632
  }
437
- if (rel.from === taskId && rel.type === 'subtask_of') {
438
- parentTask = graph.entities[rel.to] || null;
633
+ }
634
+ // 子任务没有直接 implements 关系 → 通过父任务找上游需求
635
+ if (!requirement && parentTask) {
636
+ for (const rel of graph.relations) {
637
+ if (rel.from === parentTask.id && rel.type === 'implements') {
638
+ requirement = graph.entities[rel.to] || null;
639
+ break;
640
+ }
439
641
  }
440
642
  }
441
- // 如果是子任务,找兄弟子任务
643
+ // 如果是子任务,找兄弟子任务(用索引,O(1))
644
+ const siblingSubtasks = [];
442
645
  const parentId = entity.parentTaskId || parentTask?.id;
443
646
  if (parentId) {
444
- for (const e of Object.values(graph.entities)) {
445
- if (e.type === 'subtask' && e.parentTaskId === parentId) {
446
- siblingSubtasks.push(e);
447
- }
448
- }
647
+ const siblings = parentIndex.get(parentId);
648
+ if (siblings)
649
+ siblingSubtasks.push(...siblings);
449
650
  }
450
- return { requirement, siblingSubtasks, parentTask };
651
+ return { requirement, siblingSubtasks, parentTask, relatedSpecs, dependsOn };
451
652
  }
452
653
  // ═══════════════════════════════════════════════
453
654
  // 自动更新机制
454
655
  // ═══════════════════════════════════════════════
455
- /** 检查知识图谱是否已过期 */
656
+ /**
657
+ * 递归获取目录下所有文件的最新 mtime
658
+ * 限制:每个目录最多检查 MAX_FILES_PER_DIR 个文件,避免大型项目阻塞
659
+ */
660
+ const MAX_FILES_PER_DIR = 100;
661
+ async function getLatestMtime(dir, fileCount = { value: 0 }) {
662
+ if (!(await (0, fs_extra_1.pathExists)(dir)))
663
+ return 0;
664
+ let latest = 0;
665
+ const items = await (0, fs_extra_1.readdir)(dir, { withFileTypes: true });
666
+ for (const item of items) {
667
+ if (item.name.startsWith('.') || (0, task_utils_1.isTimestampBackup)(item.name))
668
+ continue;
669
+ if (fileCount.value >= MAX_FILES_PER_DIR)
670
+ break;
671
+ const fullPath = (0, path_1.join)(dir, item.name);
672
+ const st = await (0, fs_extra_1.stat)(fullPath);
673
+ if (item.isDirectory()) {
674
+ if (item.name === 'node_modules' || item.name === 'cache')
675
+ continue;
676
+ const subLatest = await getLatestMtime(fullPath, fileCount);
677
+ if (subLatest > latest)
678
+ latest = subLatest;
679
+ }
680
+ else {
681
+ fileCount.value++;
682
+ if (st.mtimeMs > latest)
683
+ latest = st.mtimeMs;
684
+ }
685
+ }
686
+ return latest;
687
+ }
688
+ /** 检查知识图谱是否已过期(带 500ms 超时保护) */
456
689
  async function isGraphStale(cwd, iteration) {
457
690
  const graph = await loadKnowledgeGraph(cwd);
458
691
  if (!graph)
@@ -461,23 +694,23 @@ async function isGraphStale(cwd, iteration) {
461
694
  const iterDir = await (0, context_1.getIterationDir)(iterName);
462
695
  if (!iterDir)
463
696
  return false;
464
- const { stat } = await Promise.resolve().then(() => __importStar(require('fs-extra')));
465
697
  const graphTime = new Date(graph.generated).getTime();
466
- // 检查关键目录的最新修改时间
698
+ // 递归检查目录下所有文件的最新 mtime(目录 mtime 只在增删文件时变)
467
699
  const dirsToCheck = [
468
700
  (0, path_1.join)(iterDir, '010-requirements'),
469
701
  (0, path_1.join)(iterDir, '020-specs'),
470
702
  (0, path_1.join)(iterDir, '030-tasks'),
471
703
  ];
472
704
  for (const dir of dirsToCheck) {
473
- if (!(await Promise.resolve().then(() => __importStar(require('fs-extra')))).pathExists(dir))
705
+ // 超时保护:单目录扫描超过 200ms 就放弃
706
+ const start = Date.now();
707
+ const latestMtime = await getLatestMtime(dir);
708
+ if (Date.now() - start > 200) {
709
+ logger_1.logger.debug(`isGraphStale: ${dir} 扫描超时,假设未过期`);
474
710
  continue;
475
- try {
476
- const st = await stat(dir);
477
- if (st.mtimeMs > graphTime)
478
- return true;
479
711
  }
480
- catch { /* ignore */ }
712
+ if (latestMtime > graphTime)
713
+ return true;
481
714
  }
482
715
  return false;
483
716
  }