@mycodemap/mycodemap 0.4.2 → 0.5.1

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 (288) hide show
  1. package/CHANGELOG.md +105 -3
  2. package/README.md +192 -53
  3. package/dist/ai/claude.d.ts +38 -0
  4. package/dist/ai/claude.d.ts.map +1 -0
  5. package/dist/ai/claude.js +169 -0
  6. package/dist/ai/claude.js.map +1 -0
  7. package/dist/ai/codex.d.ts +38 -0
  8. package/dist/ai/codex.d.ts.map +1 -0
  9. package/dist/ai/codex.js +169 -0
  10. package/dist/ai/codex.js.map +1 -0
  11. package/dist/ai/factory.d.ts +48 -0
  12. package/dist/ai/factory.d.ts.map +1 -0
  13. package/dist/ai/factory.js +95 -0
  14. package/dist/ai/factory.js.map +1 -0
  15. package/dist/ai/index.d.ts +12 -0
  16. package/dist/ai/index.d.ts.map +1 -0
  17. package/dist/ai/index.js +29 -0
  18. package/dist/ai/index.js.map +1 -0
  19. package/dist/ai/provider.d.ts +70 -0
  20. package/dist/ai/provider.d.ts.map +1 -0
  21. package/dist/ai/provider.js +31 -0
  22. package/dist/ai/provider.js.map +1 -0
  23. package/dist/ai/subagent-caller.d.ts +90 -0
  24. package/dist/ai/subagent-caller.d.ts.map +1 -0
  25. package/dist/ai/subagent-caller.js +280 -0
  26. package/dist/ai/subagent-caller.js.map +1 -0
  27. package/dist/ai/types.d.ts +70 -0
  28. package/dist/ai/types.d.ts.map +1 -0
  29. package/dist/ai/types.js +5 -0
  30. package/dist/ai/types.js.map +1 -0
  31. package/dist/cli/commands/analyze.d.ts +18 -0
  32. package/dist/cli/commands/analyze.d.ts.map +1 -1
  33. package/dist/cli/commands/analyze.js +239 -6
  34. package/dist/cli/commands/analyze.js.map +1 -1
  35. package/dist/cli/commands/check.d.ts +22 -0
  36. package/dist/cli/commands/check.d.ts.map +1 -0
  37. package/dist/cli/commands/check.js +168 -0
  38. package/dist/cli/commands/check.js.map +1 -0
  39. package/dist/cli/commands/ci.d.ts +25 -0
  40. package/dist/cli/commands/ci.d.ts.map +1 -1
  41. package/dist/cli/commands/ci.js +139 -36
  42. package/dist/cli/commands/ci.js.map +1 -1
  43. package/dist/cli/commands/complexity.d.ts.map +1 -1
  44. package/dist/cli/commands/complexity.js +6 -0
  45. package/dist/cli/commands/complexity.js.map +1 -1
  46. package/dist/cli/commands/design.d.ts +52 -0
  47. package/dist/cli/commands/design.d.ts.map +1 -0
  48. package/dist/cli/commands/design.js +274 -0
  49. package/dist/cli/commands/design.js.map +1 -0
  50. package/dist/cli/commands/generate.d.ts +1 -0
  51. package/dist/cli/commands/generate.d.ts.map +1 -1
  52. package/dist/cli/commands/generate.js +121 -8
  53. package/dist/cli/commands/generate.js.map +1 -1
  54. package/dist/cli/commands/history.d.ts +26 -0
  55. package/dist/cli/commands/history.d.ts.map +1 -0
  56. package/dist/cli/commands/history.js +92 -0
  57. package/dist/cli/commands/history.js.map +1 -0
  58. package/dist/cli/commands/mcp.d.ts +13 -0
  59. package/dist/cli/commands/mcp.d.ts.map +1 -0
  60. package/dist/cli/commands/mcp.js +108 -0
  61. package/dist/cli/commands/mcp.js.map +1 -0
  62. package/dist/cli/commands/server.d.ts +9 -0
  63. package/dist/cli/commands/server.d.ts.map +1 -0
  64. package/dist/cli/commands/server.js +65 -0
  65. package/dist/cli/commands/server.js.map +1 -0
  66. package/dist/cli/commands/ship/pipeline.d.ts.map +1 -1
  67. package/dist/cli/commands/ship/pipeline.js +8 -1
  68. package/dist/cli/commands/ship/pipeline.js.map +1 -1
  69. package/dist/cli/commands/ship/publisher.d.ts +9 -1
  70. package/dist/cli/commands/ship/publisher.d.ts.map +1 -1
  71. package/dist/cli/commands/ship/publisher.js +149 -6
  72. package/dist/cli/commands/ship/publisher.js.map +1 -1
  73. package/dist/cli/commands/workflow.d.ts.map +1 -1
  74. package/dist/cli/commands/workflow.js +22 -2
  75. package/dist/cli/commands/workflow.js.map +1 -1
  76. package/dist/cli/config-loader.d.ts.map +1 -1
  77. package/dist/cli/config-loader.js +3 -2
  78. package/dist/cli/config-loader.js.map +1 -1
  79. package/dist/cli/contract-checker.d.ts +33 -0
  80. package/dist/cli/contract-checker.d.ts.map +1 -0
  81. package/dist/cli/contract-checker.js +719 -0
  82. package/dist/cli/contract-checker.js.map +1 -0
  83. package/dist/cli/contract-diff-scope.d.ts +14 -0
  84. package/dist/cli/contract-diff-scope.d.ts.map +1 -0
  85. package/dist/cli/contract-diff-scope.js +127 -0
  86. package/dist/cli/contract-diff-scope.js.map +1 -0
  87. package/dist/cli/contract-gate-thresholds.d.ts +14 -0
  88. package/dist/cli/contract-gate-thresholds.d.ts.map +1 -0
  89. package/dist/cli/contract-gate-thresholds.js +19 -0
  90. package/dist/cli/contract-gate-thresholds.js.map +1 -0
  91. package/dist/cli/design-contract-loader.d.ts +15 -0
  92. package/dist/cli/design-contract-loader.d.ts.map +1 -0
  93. package/dist/cli/design-contract-loader.js +527 -0
  94. package/dist/cli/design-contract-loader.js.map +1 -0
  95. package/dist/cli/design-contract-schema.d.ts +11 -0
  96. package/dist/cli/design-contract-schema.d.ts.map +1 -0
  97. package/dist/cli/design-contract-schema.js +75 -0
  98. package/dist/cli/design-contract-schema.js.map +1 -0
  99. package/dist/cli/design-handoff-builder.d.ts +15 -0
  100. package/dist/cli/design-handoff-builder.d.ts.map +1 -0
  101. package/dist/cli/design-handoff-builder.js +345 -0
  102. package/dist/cli/design-handoff-builder.js.map +1 -0
  103. package/dist/cli/design-scope-resolver.d.ts +8 -0
  104. package/dist/cli/design-scope-resolver.d.ts.map +1 -0
  105. package/dist/cli/design-scope-resolver.js +760 -0
  106. package/dist/cli/design-scope-resolver.js.map +1 -0
  107. package/dist/cli/design-verification-builder.d.ts +8 -0
  108. package/dist/cli/design-verification-builder.d.ts.map +1 -0
  109. package/dist/cli/design-verification-builder.js +369 -0
  110. package/dist/cli/design-verification-builder.js.map +1 -0
  111. package/dist/cli/index.js +20 -6
  112. package/dist/cli/index.js.map +1 -1
  113. package/dist/cli/paths.d.ts.map +1 -1
  114. package/dist/cli/paths.js +30 -7
  115. package/dist/cli/paths.js.map +1 -1
  116. package/dist/cli-new/commands/server.d.ts +13 -0
  117. package/dist/cli-new/commands/server.d.ts.map +1 -0
  118. package/dist/cli-new/commands/server.js +90 -0
  119. package/dist/cli-new/commands/server.js.map +1 -0
  120. package/dist/core/analyzer.d.ts.map +1 -1
  121. package/dist/core/analyzer.js +16 -0
  122. package/dist/core/analyzer.js.map +1 -1
  123. package/dist/domain/entities/CodeGraph.d.ts +5 -1
  124. package/dist/domain/entities/CodeGraph.d.ts.map +1 -1
  125. package/dist/domain/entities/CodeGraph.js +29 -12
  126. package/dist/domain/entities/CodeGraph.js.map +1 -1
  127. package/dist/domain/entities/Dependency.d.ts +8 -1
  128. package/dist/domain/entities/Dependency.d.ts.map +1 -1
  129. package/dist/domain/entities/Dependency.js +19 -4
  130. package/dist/domain/entities/Dependency.js.map +1 -1
  131. package/dist/domain/entities/Symbol.d.ts +2 -1
  132. package/dist/domain/entities/Symbol.d.ts.map +1 -1
  133. package/dist/domain/entities/Symbol.js +6 -3
  134. package/dist/domain/entities/Symbol.js.map +1 -1
  135. package/dist/generator/ai-overview.d.ts +51 -0
  136. package/dist/generator/ai-overview.d.ts.map +1 -0
  137. package/dist/generator/ai-overview.js +160 -0
  138. package/dist/generator/ai-overview.js.map +1 -0
  139. package/dist/infrastructure/storage/StorageFactory.d.ts +13 -5
  140. package/dist/infrastructure/storage/StorageFactory.d.ts.map +1 -1
  141. package/dist/infrastructure/storage/StorageFactory.js +62 -16
  142. package/dist/infrastructure/storage/StorageFactory.js.map +1 -1
  143. package/dist/infrastructure/storage/adapters/FileSystemStorage.d.ts +3 -1
  144. package/dist/infrastructure/storage/adapters/FileSystemStorage.d.ts.map +1 -1
  145. package/dist/infrastructure/storage/adapters/FileSystemStorage.js +10 -2
  146. package/dist/infrastructure/storage/adapters/FileSystemStorage.js.map +1 -1
  147. package/dist/infrastructure/storage/adapters/KuzuDBStorage.d.ts +3 -1
  148. package/dist/infrastructure/storage/adapters/KuzuDBStorage.d.ts.map +1 -1
  149. package/dist/infrastructure/storage/adapters/KuzuDBStorage.js +9 -1
  150. package/dist/infrastructure/storage/adapters/KuzuDBStorage.js.map +1 -1
  151. package/dist/infrastructure/storage/adapters/MemoryStorage.d.ts +3 -1
  152. package/dist/infrastructure/storage/adapters/MemoryStorage.d.ts.map +1 -1
  153. package/dist/infrastructure/storage/adapters/MemoryStorage.js +9 -1
  154. package/dist/infrastructure/storage/adapters/MemoryStorage.js.map +1 -1
  155. package/dist/infrastructure/storage/adapters/Neo4jStorage.d.ts +41 -0
  156. package/dist/infrastructure/storage/adapters/Neo4jStorage.d.ts.map +1 -0
  157. package/dist/infrastructure/storage/adapters/Neo4jStorage.js +162 -0
  158. package/dist/infrastructure/storage/adapters/Neo4jStorage.js.map +1 -0
  159. package/dist/infrastructure/storage/adapters/SQLiteStorage.d.ts +53 -0
  160. package/dist/infrastructure/storage/adapters/SQLiteStorage.d.ts.map +1 -0
  161. package/dist/infrastructure/storage/adapters/SQLiteStorage.js +879 -0
  162. package/dist/infrastructure/storage/adapters/SQLiteStorage.js.map +1 -0
  163. package/dist/infrastructure/storage/graph-helpers.d.ts +3 -1
  164. package/dist/infrastructure/storage/graph-helpers.d.ts.map +1 -1
  165. package/dist/infrastructure/storage/graph-helpers.js +90 -0
  166. package/dist/infrastructure/storage/graph-helpers.js.map +1 -1
  167. package/dist/infrastructure/storage/index.d.ts +1 -1
  168. package/dist/infrastructure/storage/index.d.ts.map +1 -1
  169. package/dist/infrastructure/storage/interfaces/StorageBase.d.ts +3 -1
  170. package/dist/infrastructure/storage/interfaces/StorageBase.d.ts.map +1 -1
  171. package/dist/infrastructure/storage/interfaces/StorageBase.js.map +1 -1
  172. package/dist/infrastructure/storage/sqlite/GovernanceGraphCache.d.ts +27 -0
  173. package/dist/infrastructure/storage/sqlite/GovernanceGraphCache.d.ts.map +1 -0
  174. package/dist/infrastructure/storage/sqlite/GovernanceGraphCache.js +246 -0
  175. package/dist/infrastructure/storage/sqlite/GovernanceGraphCache.js.map +1 -0
  176. package/dist/infrastructure/storage/sqlite/perf-thresholds.d.ts +25 -0
  177. package/dist/infrastructure/storage/sqlite/perf-thresholds.d.ts.map +1 -0
  178. package/dist/infrastructure/storage/sqlite/perf-thresholds.js +25 -0
  179. package/dist/infrastructure/storage/sqlite/perf-thresholds.js.map +1 -0
  180. package/dist/infrastructure/storage/sqlite/schema.d.ts +4 -0
  181. package/dist/infrastructure/storage/sqlite/schema.d.ts.map +1 -0
  182. package/dist/infrastructure/storage/sqlite/schema.js +111 -0
  183. package/dist/infrastructure/storage/sqlite/schema.js.map +1 -0
  184. package/dist/interface/types/design-check.d.ts +73 -0
  185. package/dist/interface/types/design-check.d.ts.map +1 -0
  186. package/dist/interface/types/design-check.js +4 -0
  187. package/dist/interface/types/design-check.js.map +1 -0
  188. package/dist/interface/types/design-contract.d.ts +123 -0
  189. package/dist/interface/types/design-contract.d.ts.map +1 -0
  190. package/dist/interface/types/design-contract.js +7 -0
  191. package/dist/interface/types/design-contract.js.map +1 -0
  192. package/dist/interface/types/design-handoff.d.ts +68 -0
  193. package/dist/interface/types/design-handoff.d.ts.map +1 -0
  194. package/dist/interface/types/design-handoff.js +4 -0
  195. package/dist/interface/types/design-handoff.js.map +1 -0
  196. package/dist/interface/types/design-mapping.d.ts +51 -0
  197. package/dist/interface/types/design-mapping.d.ts.map +1 -0
  198. package/dist/interface/types/design-mapping.js +4 -0
  199. package/dist/interface/types/design-mapping.js.map +1 -0
  200. package/dist/interface/types/design-verification.d.ts +49 -0
  201. package/dist/interface/types/design-verification.d.ts.map +1 -0
  202. package/dist/interface/types/design-verification.js +4 -0
  203. package/dist/interface/types/design-verification.js.map +1 -0
  204. package/dist/interface/types/history-risk.d.ts +90 -0
  205. package/dist/interface/types/history-risk.d.ts.map +1 -0
  206. package/dist/interface/types/history-risk.js +4 -0
  207. package/dist/interface/types/history-risk.js.map +1 -0
  208. package/dist/interface/types/index.d.ts +20 -1
  209. package/dist/interface/types/index.d.ts.map +1 -1
  210. package/dist/interface/types/storage.d.ts +28 -1
  211. package/dist/interface/types/storage.d.ts.map +1 -1
  212. package/dist/orchestrator/adapters/ast-grep-adapter.d.ts +10 -0
  213. package/dist/orchestrator/adapters/ast-grep-adapter.d.ts.map +1 -1
  214. package/dist/orchestrator/adapters/ast-grep-adapter.js +46 -17
  215. package/dist/orchestrator/adapters/ast-grep-adapter.js.map +1 -1
  216. package/dist/orchestrator/adapters/codemap-adapter.d.ts.map +1 -1
  217. package/dist/orchestrator/adapters/codemap-adapter.js +2 -22
  218. package/dist/orchestrator/adapters/codemap-adapter.js.map +1 -1
  219. package/dist/orchestrator/ai-feed-generator.d.ts +210 -0
  220. package/dist/orchestrator/ai-feed-generator.d.ts.map +1 -0
  221. package/dist/orchestrator/ai-feed-generator.js +377 -0
  222. package/dist/orchestrator/ai-feed-generator.js.map +1 -0
  223. package/dist/orchestrator/history-risk-service.d.ts +55 -0
  224. package/dist/orchestrator/history-risk-service.d.ts.map +1 -0
  225. package/dist/orchestrator/history-risk-service.js +680 -0
  226. package/dist/orchestrator/history-risk-service.js.map +1 -0
  227. package/dist/orchestrator/types.d.ts +19 -1
  228. package/dist/orchestrator/types.d.ts.map +1 -1
  229. package/dist/orchestrator/types.js +19 -0
  230. package/dist/orchestrator/types.js.map +1 -1
  231. package/dist/server/mcp/index.d.ts +4 -0
  232. package/dist/server/mcp/index.d.ts.map +1 -0
  233. package/dist/server/mcp/index.js +5 -0
  234. package/dist/server/mcp/index.js.map +1 -0
  235. package/dist/server/mcp/server.d.ts +17 -0
  236. package/dist/server/mcp/server.d.ts.map +1 -0
  237. package/dist/server/mcp/server.js +84 -0
  238. package/dist/server/mcp/server.js.map +1 -0
  239. package/dist/server/mcp/service.d.ts +22 -0
  240. package/dist/server/mcp/service.d.ts.map +1 -0
  241. package/dist/server/mcp/service.js +177 -0
  242. package/dist/server/mcp/service.js.map +1 -0
  243. package/dist/server/mcp/types.d.ts +56 -0
  244. package/dist/server/mcp/types.d.ts.map +1 -0
  245. package/dist/server/mcp/types.js +4 -0
  246. package/dist/server/mcp/types.js.map +1 -0
  247. package/docs/AI_ASSISTANT_SETUP.md +1 -1
  248. package/docs/SETUP_GUIDE.md +6 -6
  249. package/docs/ai-guide/COMMANDS.md +171 -4
  250. package/docs/ai-guide/INTEGRATION.md +137 -433
  251. package/docs/ai-guide/OUTPUT.md +890 -5
  252. package/docs/ai-guide/PATTERNS.md +54 -14
  253. package/docs/ai-guide/PROMPTS.md +17 -6
  254. package/docs/archive/test-report-symbol-search.md +384 -0
  255. package/docs/archive/test-scenario-4-complexity-analysis.md +460 -0
  256. package/docs/archive/test_report_scenario5.md +615 -0
  257. package/docs/archive/test_scenario_3_impact_analysis_report.md +520 -0
  258. package/docs/backlog.md +177 -0
  259. package/docs/eatdogfood-reports/2026-04-17-eatdogfood-agent-experience.md +231 -0
  260. package/docs/exec-plans/completed/2026-04-17-eatdogfood-codemap-cli.md +103 -0
  261. package/docs/ideation/2026-04-15-executable-architecture-constitution-ideation.md +102 -0
  262. package/docs/product-specs/DESIGN_CONTRACT_TEMPLATE.md +126 -0
  263. package/docs/product-specs/MVP3-ARCHITECTURE-COMPARISON.md +11 -10
  264. package/docs/product-specs/MVP3-ARCHITECTURE-REDESIGN-PRD.md +10 -10
  265. package/docs/product-specs/MVP3-ARCHITECTURE-REDESIGN-TECH-PRD.md +17 -12
  266. package/docs/product-specs/README.md +2 -1
  267. package/docs/rules/README.md +16 -11
  268. package/docs/rules/architecture-guardrails.md +24 -336
  269. package/docs/rules/code-quality-redlines.md +25 -311
  270. package/docs/rules/engineering-with-codex-openai.md +20 -3
  271. package/docs/rules/validation.md +90 -37
  272. package/mycodemap.config.schema.json +3 -3
  273. package/package.json +7 -2
  274. package/scripts/benchmark-governance-graph.mjs +132 -0
  275. package/scripts/calibrate-contract-gate.mjs +221 -0
  276. package/scripts/capability-report.py +255 -0
  277. package/scripts/experiments/arcadedb-http-smoke.mjs +90 -0
  278. package/scripts/qa-rule-control.sh +254 -0
  279. package/scripts/report-high-risk-files.mjs +395 -0
  280. package/scripts/rule-context.mjs +155 -0
  281. package/scripts/smoke-sqlite-impact.mjs +85 -0
  282. package/scripts/sync-analyze-docs.js +1 -0
  283. package/scripts/tests/test_capability_report.py +89 -0
  284. package/scripts/tests/test_rule_control_workflow.py +51 -0
  285. package/scripts/tests/test_validate_rules.py +81 -0
  286. package/scripts/validate-ai-docs.js +283 -1
  287. package/scripts/validate-docs.js +479 -25
  288. package/scripts/validate-rules.py +254 -0
@@ -0,0 +1,760 @@
1
+ // [META] since:2026-03-25 | owner:cli-team | stable:false
2
+ // [WHY] Translate validated design contracts into auditable candidate code scope with reason chains and blocker diagnostics
3
+ import { existsSync, readFileSync } from 'node:fs';
4
+ import { readFile } from 'node:fs/promises';
5
+ import path from 'node:path';
6
+ import { cwd } from 'node:process';
7
+ import { globby } from 'globby';
8
+ import { calculateConfidenceLevel } from '../orchestrator/types.js';
9
+ import { resolveTestFile, resolveTestFiles } from '../orchestrator/test-linker.js';
10
+ import { AnalyzeCommand } from './commands/analyze.js';
11
+ import { loadDesignContract } from './design-contract-loader.js';
12
+ import { resolveDataPath } from './paths.js';
13
+ const POSITIVE_SECTION_IDS = [
14
+ 'goal',
15
+ 'constraints',
16
+ 'acceptanceCriteria',
17
+ ];
18
+ const HIGH_RISK_PATHS = new Set([
19
+ 'src/cli/index.ts',
20
+ 'src/cli/commands/analyze.ts',
21
+ 'src/orchestrator/workflow/workflow-orchestrator.ts',
22
+ ]);
23
+ const SOURCE_GLOBS = ['src/**/*.{ts,tsx,js,jsx,mjs,cjs}', 'scripts/**/*.js'];
24
+ const SOURCE_IGNORE = ['dist/**', 'node_modules/**', '.mycodemap/**', '.codemap/**'];
25
+ const TEST_GLOBS = ['src/**/*.{test,spec}.{ts,tsx,js,jsx}', 'tests/**/*.{test,spec}.{ts,tsx,js,jsx}'];
26
+ const FIND_KEYWORD_LIMIT = 6;
27
+ const FIND_RESULTS_PER_KEYWORD = 6;
28
+ const OVER_BROAD_PATH_LIMIT = 6;
29
+ const OVER_BROAD_SUBSYSTEM_LIMIT = 3;
30
+ const EXTENSIONS = ['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs'];
31
+ const KEYWORD_STOPWORDS = new Set([
32
+ 'acceptance',
33
+ 'criteria',
34
+ 'constraint',
35
+ 'constraints',
36
+ 'feature',
37
+ 'goal',
38
+ 'goals',
39
+ 'human',
40
+ 'machine',
41
+ 'provide',
42
+ 'provides',
43
+ 'return',
44
+ 'returns',
45
+ 'section',
46
+ 'sections',
47
+ 'should',
48
+ 'system',
49
+ 'with',
50
+ ]);
51
+ const codeMapSnapshotCache = new Map();
52
+ const sourceSnapshotCache = new Map();
53
+ const designScopeResultCache = new Map();
54
+ function normalizeSlashes(value) {
55
+ return value.replace(/\\/gu, '/');
56
+ }
57
+ function isTestLikePath(filePath) {
58
+ const normalizedPath = normalizeSlashes(filePath).replace(/^\.\//u, '');
59
+ return normalizedPath.includes('/__tests__/') || /\.(test|spec)\.[cm]?[jt]sx?$/iu.test(normalizedPath);
60
+ }
61
+ function stripKnownExtension(filePath) {
62
+ return filePath.replace(/\.(ts|tsx|js|jsx|mjs|cjs)$/iu, '');
63
+ }
64
+ function toRelativePath(filePath, rootDir) {
65
+ if (!path.isAbsolute(filePath)) {
66
+ return normalizeSlashes(filePath).replace(/^\.\//u, '');
67
+ }
68
+ return normalizeSlashes(path.relative(rootDir, filePath));
69
+ }
70
+ function toAbsolutePath(filePath, rootDir) {
71
+ if (path.isAbsolute(filePath)) {
72
+ return normalizeSlashes(filePath);
73
+ }
74
+ return normalizeSlashes(path.join(rootDir, filePath));
75
+ }
76
+ function escapeRegExp(value) {
77
+ return value.replace(/[.*+?^${}()|[\]\\]/gu, '\\$&');
78
+ }
79
+ function uniq(items) {
80
+ return Array.from(new Set(items));
81
+ }
82
+ function collectCodeSpans(text) {
83
+ const matches = text.match(/`([^`]+)`/gu) ?? [];
84
+ return matches
85
+ .map((match) => match.slice(1, -1).trim())
86
+ .filter((match) => match.length > 0);
87
+ }
88
+ function collectPathAnchors(text) {
89
+ const matches = text.match(/(?:\.planning|src|tests|docs|scripts)\/[A-Za-z0-9_./-]+/gu) ?? [];
90
+ return uniq(matches.map((match) => match.replace(/[),.:;!?]+$/u, '')));
91
+ }
92
+ function looksLikeIdentifier(value) {
93
+ return /^[A-Za-z_$][A-Za-z0-9_$]*$/u.test(value);
94
+ }
95
+ function looksLikeModuleAnchor(value) {
96
+ return value.includes('/') && !/\.[A-Za-z0-9]+$/u.test(value);
97
+ }
98
+ function collectKeywordSignals(section, text) {
99
+ const sanitized = text.replace(/`[^`]+`/gu, ' ');
100
+ const matches = sanitized.match(/[A-Za-z][A-Za-z0-9_-]{2,}/gu) ?? [];
101
+ return uniq(matches.map((match) => match.toLowerCase()))
102
+ .filter((match) => match.length >= 4)
103
+ .filter((match) => !KEYWORD_STOPWORDS.has(match))
104
+ .map((match) => ({
105
+ section: section.id,
106
+ matchedText: match,
107
+ value: match,
108
+ evidenceType: 'keyword-match',
109
+ }));
110
+ }
111
+ function collectSectionSignals(section) {
112
+ const signals = [];
113
+ for (const line of section.content) {
114
+ const codeSpans = collectCodeSpans(line);
115
+ const pathAnchors = uniq([...collectPathAnchors(line), ...codeSpans.filter(looksLikeModuleAnchor)]);
116
+ for (const pathAnchor of pathAnchors) {
117
+ const evidenceType = /\.[A-Za-z0-9]+$/u.test(pathAnchor) ? 'path-anchor' : 'module-anchor';
118
+ signals.push({
119
+ section: section.id,
120
+ matchedText: pathAnchor,
121
+ value: pathAnchor,
122
+ evidenceType,
123
+ });
124
+ }
125
+ for (const codeSpan of codeSpans.filter(looksLikeIdentifier)) {
126
+ signals.push({
127
+ section: section.id,
128
+ matchedText: codeSpan,
129
+ value: codeSpan,
130
+ evidenceType: 'symbol-anchor',
131
+ });
132
+ }
133
+ if (pathAnchors.length === 0 && codeSpans.length === 0) {
134
+ signals.push(...collectKeywordSignals(section, line));
135
+ }
136
+ }
137
+ return signals;
138
+ }
139
+ function toMappingDiagnostic(diagnostic) {
140
+ return {
141
+ code: diagnostic.code,
142
+ severity: diagnostic.severity,
143
+ blocker: diagnostic.severity === 'error',
144
+ message: diagnostic.message,
145
+ section: diagnostic.section,
146
+ suggestion: diagnostic.suggestion,
147
+ };
148
+ }
149
+ function createMappingDiagnostic(code, message, options = {}) {
150
+ return {
151
+ code,
152
+ severity: options.severity ?? 'error',
153
+ blocker: options.blocker ?? true,
154
+ message,
155
+ section: options.section,
156
+ suggestion: options.suggestion,
157
+ candidatePaths: options.candidatePaths,
158
+ };
159
+ }
160
+ function createSummary(candidates, diagnostics) {
161
+ const blocked = diagnostics.some((diagnostic) => diagnostic.blocker);
162
+ const unknownCount = candidates.reduce((total, candidate) => total + candidate.unknowns.length, 0);
163
+ return {
164
+ candidateCount: candidates.length,
165
+ blocked,
166
+ unknownCount,
167
+ diagnosticCount: diagnostics.length,
168
+ };
169
+ }
170
+ function defaultConfidence(reasons) {
171
+ const baseScore = reasons.reduce((score, reason) => {
172
+ if (reason.evidenceType === 'path-anchor') {
173
+ return Math.max(score, 0.92);
174
+ }
175
+ if (reason.evidenceType === 'module-anchor') {
176
+ return Math.max(score, 0.82);
177
+ }
178
+ if (reason.evidenceType === 'symbol-anchor') {
179
+ return Math.max(score, 0.86);
180
+ }
181
+ if (reason.evidenceType === 'analyze-find') {
182
+ return Math.max(score, 0.55);
183
+ }
184
+ return Math.max(score, 0.45);
185
+ }, 0.35);
186
+ const reasonBonus = Math.min(0.15, Math.max(0, reasons.length - 1) * 0.05);
187
+ const score = Math.min(0.97, Math.round((baseScore + reasonBonus) * 100) / 100);
188
+ return {
189
+ score,
190
+ level: calculateConfidenceLevel(score),
191
+ };
192
+ }
193
+ function createCandidate(kind, relativePath, reason, absolutePath, moduleName, symbolName) {
194
+ return {
195
+ kind,
196
+ path: relativePath,
197
+ moduleName,
198
+ symbolName,
199
+ reasons: [reason],
200
+ dependencies: [],
201
+ testImpact: [],
202
+ risk: HIGH_RISK_PATHS.has(relativePath) ? 'high' : 'low',
203
+ confidence: defaultConfidence([reason]),
204
+ unknowns: [],
205
+ absolutePath,
206
+ };
207
+ }
208
+ function mergeReason(existing, reason) {
209
+ const hasReason = existing.reasons.some((item) => item.section === reason.section &&
210
+ item.matchedText === reason.matchedText &&
211
+ item.evidenceType === reason.evidenceType);
212
+ if (!hasReason) {
213
+ existing.reasons.push(reason);
214
+ existing.confidence = defaultConfidence(existing.reasons);
215
+ }
216
+ return existing;
217
+ }
218
+ function candidateKey(candidate) {
219
+ return [candidate.kind, candidate.path, candidate.moduleName ?? '', candidate.symbolName ?? ''].join('::');
220
+ }
221
+ function addCandidate(candidates, candidate) {
222
+ const key = candidateKey(candidate);
223
+ const existing = candidates.get(key);
224
+ if (!existing) {
225
+ candidates.set(key, candidate);
226
+ return;
227
+ }
228
+ for (const reason of candidate.reasons) {
229
+ mergeReason(existing, reason);
230
+ }
231
+ }
232
+ function loadCodeMapSnapshot(rootDir) {
233
+ if (codeMapSnapshotCache.has(rootDir)) {
234
+ return codeMapSnapshotCache.get(rootDir) ?? null;
235
+ }
236
+ const dataPath = resolveDataPath(rootDir);
237
+ if (!existsSync(dataPath)) {
238
+ codeMapSnapshotCache.set(rootDir, null);
239
+ return null;
240
+ }
241
+ try {
242
+ const snapshot = JSON.parse(readFileSync(dataPath, 'utf8'));
243
+ codeMapSnapshotCache.set(rootDir, snapshot);
244
+ return snapshot;
245
+ }
246
+ catch {
247
+ codeMapSnapshotCache.set(rootDir, null);
248
+ return null;
249
+ }
250
+ }
251
+ function discoverModuleCandidates(anchor, rootDir) {
252
+ const normalizedAnchor = normalizeSlashes(anchor).replace(/^\.\//u, '');
253
+ const absoluteBase = path.isAbsolute(normalizedAnchor)
254
+ ? normalizeSlashes(normalizedAnchor)
255
+ : normalizeSlashes(path.join(rootDir, normalizedAnchor));
256
+ const candidates = EXTENSIONS.map((extension) => `${absoluteBase}${extension}`);
257
+ candidates.push(...EXTENSIONS.map((extension) => normalizeSlashes(path.join(absoluteBase, `index${extension}`))));
258
+ return candidates.filter((candidate) => existsSync(candidate));
259
+ }
260
+ function buildPathReason(signal) {
261
+ return {
262
+ section: signal.section,
263
+ matchedText: signal.matchedText,
264
+ evidenceType: signal.evidenceType,
265
+ };
266
+ }
267
+ function resolveExactPathSignals(candidates, signals, rootDir) {
268
+ for (const signal of signals.filter((item) => item.evidenceType === 'path-anchor')) {
269
+ for (const absolutePath of discoverModuleCandidates(signal.value, rootDir).concat(existsSync(toAbsolutePath(signal.value, rootDir)) ? [toAbsolutePath(signal.value, rootDir)] : [])) {
270
+ const relativePath = toRelativePath(absolutePath, rootDir);
271
+ addCandidate(candidates, createCandidate('file', relativePath, buildPathReason(signal), absolutePath));
272
+ }
273
+ }
274
+ }
275
+ function resolveExactModuleSignals(candidates, signals, rootDir, codeMap) {
276
+ for (const signal of signals.filter((item) => item.evidenceType === 'module-anchor')) {
277
+ const exactMatches = discoverModuleCandidates(signal.value, rootDir);
278
+ for (const absolutePath of exactMatches) {
279
+ const relativePath = toRelativePath(absolutePath, rootDir);
280
+ addCandidate(candidates, createCandidate('module', relativePath, buildPathReason(signal), absolutePath, stripKnownExtension(relativePath)));
281
+ }
282
+ if (!codeMap) {
283
+ continue;
284
+ }
285
+ for (const module of codeMap.modules) {
286
+ const relativePath = toRelativePath(module.absolutePath || module.path, rootDir);
287
+ const moduleName = stripKnownExtension(relativePath);
288
+ const matched = moduleName === signal.value || moduleName.endsWith(`/${signal.value}`);
289
+ if (matched) {
290
+ addCandidate(candidates, createCandidate('module', relativePath, buildPathReason(signal), module.absolutePath, moduleName));
291
+ }
292
+ }
293
+ }
294
+ }
295
+ async function discoverSourceFiles(rootDir) {
296
+ const files = await globby(SOURCE_GLOBS, {
297
+ cwd: rootDir,
298
+ absolute: true,
299
+ gitignore: true,
300
+ ignore: SOURCE_IGNORE,
301
+ });
302
+ return files.filter((filePath) => !isTestLikePath(toRelativePath(filePath, rootDir)));
303
+ }
304
+ function indexedPathsFrom(codeMap, rootDir) {
305
+ if (!codeMap) {
306
+ return new Set();
307
+ }
308
+ return new Set(codeMap.modules
309
+ .map((module) => toRelativePath(module.absolutePath || module.path, rootDir))
310
+ .filter((filePath) => !isTestLikePath(filePath)));
311
+ }
312
+ async function buildSourceSnapshot(rootDir, codeMap) {
313
+ const cachedSnapshot = sourceSnapshotCache.get(rootDir);
314
+ if (cachedSnapshot) {
315
+ return cachedSnapshot;
316
+ }
317
+ const snapshotPromise = (async () => {
318
+ const files = await discoverSourceFiles(rootDir);
319
+ const testFiles = await globby(TEST_GLOBS, {
320
+ cwd: rootDir,
321
+ absolute: true,
322
+ gitignore: true,
323
+ ignore: SOURCE_IGNORE,
324
+ });
325
+ const entries = await Promise.all(files.map(async (filePath) => [filePath, await readFile(filePath, 'utf8')]));
326
+ return {
327
+ files,
328
+ contents: new Map(entries),
329
+ indexedPaths: indexedPathsFrom(codeMap, rootDir),
330
+ testFiles,
331
+ };
332
+ })();
333
+ sourceSnapshotCache.set(rootDir, snapshotPromise);
334
+ try {
335
+ return await snapshotPromise;
336
+ }
337
+ catch (error) {
338
+ sourceSnapshotCache.delete(rootDir);
339
+ throw error;
340
+ }
341
+ }
342
+ async function resolveSymbolSignals(candidates, signals, rootDir, codeMap, sourceSnapshot) {
343
+ for (const signal of signals.filter((item) => item.evidenceType === 'symbol-anchor')) {
344
+ const matchedFiles = new Set();
345
+ if (codeMap) {
346
+ for (const module of codeMap.modules) {
347
+ const relativePath = toRelativePath(module.absolutePath || module.path, rootDir);
348
+ if (isTestLikePath(relativePath)) {
349
+ continue;
350
+ }
351
+ if (module.symbols.some((symbol) => symbol.name === signal.value)) {
352
+ matchedFiles.add(module.absolutePath || module.path);
353
+ }
354
+ }
355
+ }
356
+ const declarationPattern = new RegExp(`\\b(?:function|class|interface|type|enum|const|let|var)\\s+${escapeRegExp(signal.value)}\\b`, 'u');
357
+ const identifierPattern = new RegExp(`\\b${escapeRegExp(signal.value)}\\b`, 'u');
358
+ for (const sourceFile of sourceSnapshot.files) {
359
+ const content = sourceSnapshot.contents.get(sourceFile) ?? '';
360
+ if (declarationPattern.test(content) || identifierPattern.test(content)) {
361
+ matchedFiles.add(sourceFile);
362
+ }
363
+ }
364
+ for (const matchedFile of Array.from(matchedFiles).slice(0, FIND_RESULTS_PER_KEYWORD)) {
365
+ const relativePath = toRelativePath(matchedFile, rootDir);
366
+ addCandidate(candidates, createCandidate('symbol', relativePath, buildPathReason(signal), matchedFile, stripKnownExtension(relativePath), signal.value));
367
+ }
368
+ }
369
+ }
370
+ function normalizeAnalyzePaths(output, rootDir) {
371
+ return uniq(output.results
372
+ .map((result) => result.file)
373
+ .filter((result) => typeof result === 'string' && result.length > 0)
374
+ .map((result) => toRelativePath(result, rootDir)));
375
+ }
376
+ async function runAnalyzeFind(keyword) {
377
+ try {
378
+ const command = new AnalyzeCommand({
379
+ intent: 'find',
380
+ keywords: [keyword],
381
+ includeTests: false,
382
+ json: true,
383
+ structured: true,
384
+ });
385
+ const output = await command.execute();
386
+ return typeof output === 'object' && output !== null && 'results' in output
387
+ ? output
388
+ : null;
389
+ }
390
+ catch {
391
+ return null;
392
+ }
393
+ }
394
+ async function fallbackKeywordPaths(keyword, rootDir, sourceSnapshot) {
395
+ const pattern = new RegExp(`\\b${escapeRegExp(keyword)}\\b`, 'iu');
396
+ const matches = [];
397
+ for (const sourceFile of sourceSnapshot.files) {
398
+ const relativePath = toRelativePath(sourceFile, rootDir);
399
+ if (relativePath.toLowerCase().includes(keyword.toLowerCase())) {
400
+ matches.push(relativePath);
401
+ continue;
402
+ }
403
+ const content = sourceSnapshot.contents.get(sourceFile) ?? '';
404
+ if (pattern.test(content)) {
405
+ matches.push(relativePath);
406
+ }
407
+ }
408
+ return uniq(matches).slice(0, FIND_RESULTS_PER_KEYWORD);
409
+ }
410
+ async function resolveKeywordSignals(candidates, signals, rootDir, sourceSnapshot) {
411
+ if (candidates.size > 0) {
412
+ return;
413
+ }
414
+ for (const signal of signals.slice(0, FIND_KEYWORD_LIMIT)) {
415
+ const fallbackPaths = await fallbackKeywordPaths(signal.value, rootDir, sourceSnapshot);
416
+ const analyzeOutput = fallbackPaths.length > 0 ? null : await runAnalyzeFind(signal.value);
417
+ const matchedPaths = analyzeOutput ? normalizeAnalyzePaths(analyzeOutput, rootDir) : [];
418
+ const paths = matchedPaths.length > 0 ? matchedPaths : fallbackPaths;
419
+ for (const relativePath of paths) {
420
+ const absolutePath = toAbsolutePath(relativePath, rootDir);
421
+ addCandidate(candidates, createCandidate('file', relativePath, {
422
+ section: signal.section,
423
+ matchedText: signal.matchedText,
424
+ evidenceType: matchedPaths.length > 0 ? 'analyze-find' : 'keyword-match',
425
+ }, absolutePath));
426
+ }
427
+ }
428
+ }
429
+ function matchesNegativeSignal(candidate, signal) {
430
+ const haystacks = [
431
+ candidate.path,
432
+ candidate.moduleName ?? '',
433
+ candidate.symbolName ?? '',
434
+ ].map((item) => item.toLowerCase());
435
+ const needle = signal.value.toLowerCase();
436
+ return haystacks.some((item) => item.includes(needle));
437
+ }
438
+ function applyNegativeFiltering(candidates, negativeSignals) {
439
+ return candidates.filter((candidate) => !negativeSignals.some((signal) => matchesNegativeSignal(candidate, signal)));
440
+ }
441
+ async function readEnrichment(candidate) {
442
+ try {
443
+ const command = new AnalyzeCommand({
444
+ intent: 'read',
445
+ targets: [candidate.path],
446
+ includeTests: true,
447
+ json: true,
448
+ structured: true,
449
+ });
450
+ const output = await command.execute();
451
+ return typeof output === 'object' && output !== null && 'results' in output
452
+ ? output
453
+ : null;
454
+ }
455
+ catch {
456
+ return null;
457
+ }
458
+ }
459
+ async function linkEnrichment(candidate) {
460
+ try {
461
+ const command = new AnalyzeCommand({
462
+ intent: 'link',
463
+ targets: [candidate.path],
464
+ includeTests: true,
465
+ json: true,
466
+ structured: true,
467
+ });
468
+ const output = await command.execute();
469
+ return typeof output === 'object' && output !== null && 'results' in output
470
+ ? output
471
+ : null;
472
+ }
473
+ catch {
474
+ return null;
475
+ }
476
+ }
477
+ function resolveImportTarget(importerPath, importPath, rootDir) {
478
+ if (!importPath.startsWith('./') && !importPath.startsWith('../')) {
479
+ return null;
480
+ }
481
+ const absoluteBase = normalizeSlashes(path.resolve(path.dirname(importerPath), importPath));
482
+ const possiblePaths = [
483
+ absoluteBase,
484
+ ...EXTENSIONS.map((extension) => `${absoluteBase}${extension}`),
485
+ ...EXTENSIONS.map((extension) => normalizeSlashes(path.join(absoluteBase, `index${extension}`))),
486
+ ];
487
+ const existingPath = possiblePaths.find((candidate) => existsSync(candidate));
488
+ return existingPath ? toRelativePath(existingPath, rootDir) : null;
489
+ }
490
+ function localDependencies(candidate, rootDir, sourceSnapshot, codeMap) {
491
+ if (!candidate.absolutePath) {
492
+ return [];
493
+ }
494
+ const content = sourceSnapshot.contents.get(candidate.absolutePath) ?? '';
495
+ const imports = Array.from(content.matchAll(/(?:import|export)\s+[^'"]*from\s+['"](.+?)['"]/gu))
496
+ .map((match) => match[1] ?? '')
497
+ .map((match) => resolveImportTarget(candidate.absolutePath, match, rootDir))
498
+ .filter((match) => typeof match === 'string' && match.length > 0);
499
+ const bareImports = Array.from(content.matchAll(/import\s+['"](.+?)['"]/gu))
500
+ .map((match) => match[1] ?? '')
501
+ .map((match) => resolveImportTarget(candidate.absolutePath, match, rootDir))
502
+ .filter((match) => typeof match === 'string' && match.length > 0);
503
+ const dependents = !codeMap
504
+ ? []
505
+ : codeMap.modules
506
+ .filter((module) => module.dependencies.some((dependency) => resolveImportTarget(module.absolutePath || module.path, dependency, rootDir) === candidate.path))
507
+ .map((module) => toRelativePath(module.absolutePath || module.path, rootDir));
508
+ return uniq([...imports, ...bareImports, ...dependents]).filter((item) => item !== candidate.path);
509
+ }
510
+ function localRisk(candidate, dependencies) {
511
+ if (HIGH_RISK_PATHS.has(candidate.path)) {
512
+ return 'high';
513
+ }
514
+ if (dependencies.length > 8) {
515
+ return 'high';
516
+ }
517
+ if (dependencies.length > 3) {
518
+ return 'medium';
519
+ }
520
+ return candidate.risk;
521
+ }
522
+ function mergeDependencies(candidate, readOutput, linkOutput, rootDir, fallbackDependencies) {
523
+ const fromRead = readOutput?.results
524
+ .find((result) => toRelativePath(result.file, rootDir) === candidate.path)
525
+ ?.metadata?.dependencies ?? [];
526
+ const fromLinkAnalysis = linkOutput?.analysis && 'dependency' in linkOutput.analysis
527
+ ? (linkOutput.analysis.dependency ?? []).flatMap((item) => [...item.imports, ...item.importedBy])
528
+ : [];
529
+ const fromLinkMetadata = linkOutput?.results
530
+ .find((result) => toRelativePath(result.file, rootDir) === candidate.path)
531
+ ?.metadata?.dependencies ?? [];
532
+ return uniq([...fallbackDependencies, ...fromRead, ...fromLinkAnalysis, ...fromLinkMetadata]
533
+ .filter((item) => typeof item === 'string' && item.length > 0)
534
+ .map((item) => toRelativePath(item, rootDir))).filter((item) => item !== candidate.path);
535
+ }
536
+ function resolveRisk(candidate, readOutput, rootDir, fallbackRisk) {
537
+ if (HIGH_RISK_PATHS.has(candidate.path)) {
538
+ return 'high';
539
+ }
540
+ const fromAnalysis = readOutput?.analysis && 'impact' in readOutput.analysis
541
+ ? (readOutput.analysis.impact ?? []).find((item) => toRelativePath(item.file, rootDir) === candidate.path)?.risk
542
+ : undefined;
543
+ const fromMetadata = readOutput?.results
544
+ .find((result) => toRelativePath(result.file, rootDir) === candidate.path)
545
+ ?.metadata?.riskLevel;
546
+ return fromAnalysis ?? fromMetadata ?? fallbackRisk;
547
+ }
548
+ function resolveConfidence(candidate, readOutput, linkOutput) {
549
+ const scores = [candidate.confidence.score];
550
+ if (readOutput?.confidence?.score) {
551
+ scores.push(readOutput.confidence.score);
552
+ }
553
+ if (linkOutput?.confidence?.score) {
554
+ scores.push(linkOutput.confidence.score);
555
+ }
556
+ const score = Math.max(...scores);
557
+ return {
558
+ score,
559
+ level: calculateConfidenceLevel(score),
560
+ };
561
+ }
562
+ function localTestImpact(candidate, rootDir, sourceSnapshot) {
563
+ const rawBase = path.basename(stripKnownExtension(candidate.path)).toLowerCase();
564
+ const kebabBase = rawBase.replace(/([a-z0-9])([A-Z])/gu, '$1-$2').toLowerCase();
565
+ const needles = uniq([rawBase, kebabBase]);
566
+ return sourceSnapshot.testFiles
567
+ .map((filePath) => toRelativePath(filePath, rootDir))
568
+ .filter((filePath) => {
569
+ const fileName = path.basename(filePath).toLowerCase();
570
+ return needles.some((needle) => fileName === `${needle}.test.ts` ||
571
+ fileName === `${needle}.spec.ts` ||
572
+ fileName.includes(`${needle}.test`) ||
573
+ fileName.includes(`${needle}.spec`) ||
574
+ fileName.includes(`${needle}-`) ||
575
+ fileName.includes(`${needle}_`));
576
+ });
577
+ }
578
+ async function resolveTestImpact(candidate, rootDir, sourceSnapshot) {
579
+ const localMatches = localTestImpact(candidate, rootDir, sourceSnapshot);
580
+ if (localMatches.length > 0 || !sourceSnapshot.indexedPaths.has(candidate.path)) {
581
+ return uniq(localMatches);
582
+ }
583
+ const direct = await resolveTestFile(candidate.path);
584
+ const mappings = await resolveTestFiles([candidate.path]);
585
+ const mappedTests = mappings
586
+ .map((mapping) => mapping.testFile)
587
+ .filter((item) => typeof item === 'string' && item.length > 0);
588
+ return uniq([
589
+ ...localMatches,
590
+ ...[direct, ...mappedTests].filter((item) => typeof item === 'string'),
591
+ ]);
592
+ }
593
+ function detectPartialImplementation(candidate, sourceSnapshot) {
594
+ if (!candidate.absolutePath) {
595
+ return false;
596
+ }
597
+ const content = sourceSnapshot.contents.get(candidate.absolutePath) ?? '';
598
+ return /TODO-DEBT|throw new Error\(|Not implemented|stub/iu.test(content);
599
+ }
600
+ function synthesizeUnknowns(candidate, testImpact, hasOpenQuestions, sourceSnapshot) {
601
+ const unknowns = [];
602
+ if (testImpact.length === 0) {
603
+ unknowns.push(`未找到与 ${candidate.path} 对应的测试文件`);
604
+ }
605
+ if (candidate.confidence.level === 'low') {
606
+ unknowns.push(`候选 ${candidate.path} 置信度偏低,建议补充更明确的 design anchors`);
607
+ }
608
+ if (hasOpenQuestions) {
609
+ unknowns.push('design contract 仍包含 Open Questions,需要人工确认');
610
+ }
611
+ if (detectPartialImplementation(candidate, sourceSnapshot)) {
612
+ unknowns.push(`候选 ${candidate.path} 可能仍处于 partial/stubbed implementation 状态`);
613
+ }
614
+ return uniq(unknowns);
615
+ }
616
+ function hasWeakEvidence(candidate) {
617
+ return candidate.reasons.some((reason) => reason.evidenceType === 'keyword-match' || reason.evidenceType === 'analyze-find');
618
+ }
619
+ async function enrichCandidate(candidate, rootDir, hasOpenQuestions, sourceSnapshot, codeMap) {
620
+ const fallbackDependencies = localDependencies(candidate, rootDir, sourceSnapshot, codeMap);
621
+ const fallbackRisk = localRisk(candidate, fallbackDependencies);
622
+ const shouldRunAnalyze = sourceSnapshot.indexedPaths.has(candidate.path) && hasWeakEvidence(candidate);
623
+ const [readOutput, linkOutput, testImpact] = await Promise.all([
624
+ shouldRunAnalyze ? readEnrichment(candidate) : Promise.resolve(null),
625
+ shouldRunAnalyze ? linkEnrichment(candidate) : Promise.resolve(null),
626
+ resolveTestImpact(candidate, rootDir, sourceSnapshot),
627
+ ]);
628
+ const confidence = shouldRunAnalyze
629
+ ? resolveConfidence(candidate, readOutput, linkOutput)
630
+ : candidate.confidence;
631
+ const enriched = {
632
+ dependencies: shouldRunAnalyze
633
+ ? mergeDependencies(candidate, readOutput, linkOutput, rootDir, fallbackDependencies)
634
+ : fallbackDependencies,
635
+ risk: shouldRunAnalyze
636
+ ? resolveRisk(candidate, readOutput, rootDir, fallbackRisk)
637
+ : fallbackRisk,
638
+ confidence,
639
+ testImpact,
640
+ unknowns: synthesizeUnknowns({ ...candidate, confidence }, testImpact, hasOpenQuestions, sourceSnapshot),
641
+ };
642
+ return {
643
+ ...candidate,
644
+ dependencies: enriched.dependencies,
645
+ risk: enriched.risk,
646
+ confidence: enriched.confidence,
647
+ testImpact: enriched.testImpact,
648
+ unknowns: enriched.unknowns,
649
+ };
650
+ }
651
+ async function enrichCandidates(candidates, rootDir, hasOpenQuestions, sourceSnapshot, codeMap) {
652
+ const enriched = [];
653
+ for (const candidate of candidates) {
654
+ enriched.push(await enrichCandidate(candidate, rootDir, hasOpenQuestions, sourceSnapshot, codeMap));
655
+ }
656
+ return enriched;
657
+ }
658
+ function subsystemOf(candidatePath) {
659
+ const parts = candidatePath.split('/');
660
+ if (parts.length >= 2) {
661
+ return `${parts[0]}/${parts[1]}`;
662
+ }
663
+ return candidatePath;
664
+ }
665
+ function buildScopeDiagnostics(candidates) {
666
+ const uniquePaths = uniq(candidates.map((candidate) => candidate.path));
667
+ const diagnostics = [];
668
+ if (uniquePaths.length === 0) {
669
+ diagnostics.push(createMappingDiagnostic('no-candidates', '未能从当前 design contract 中解析出可信的候选范围;请补充更明确的路径、模块或符号 anchors。', { candidatePaths: [] }));
670
+ return diagnostics;
671
+ }
672
+ const highRiskMatches = uniquePaths.filter((candidatePath) => HIGH_RISK_PATHS.has(candidatePath));
673
+ if (highRiskMatches.length > 0) {
674
+ diagnostics.push(createMappingDiagnostic('high-risk-scope', '候选范围命中了高 blast-radius 文件;请先补充更具体的 design scope,再继续执行。', { candidatePaths: highRiskMatches }));
675
+ }
676
+ const subsystemCount = new Set(uniquePaths.map((candidatePath) => subsystemOf(candidatePath))).size;
677
+ const hasBroadEvidence = candidates.some((candidate) => candidate.reasons.some((reason) => reason.evidenceType === 'keyword-match' || reason.evidenceType === 'analyze-find'));
678
+ if (uniquePaths.length > OVER_BROAD_PATH_LIMIT || (hasBroadEvidence && subsystemCount > OVER_BROAD_SUBSYSTEM_LIMIT)) {
679
+ diagnostics.push(createMappingDiagnostic('over-broad-scope', '当前 design contract 解析出的候选范围过宽,已跨越多个子系统;请补充更具体的路径、模块或符号 anchors。', { candidatePaths: uniquePaths }));
680
+ }
681
+ return diagnostics;
682
+ }
683
+ function sortCandidates(candidates) {
684
+ const kindOrder = {
685
+ file: 0,
686
+ module: 1,
687
+ symbol: 2,
688
+ };
689
+ return [...candidates].sort((left, right) => {
690
+ if (left.path !== right.path) {
691
+ return left.path.localeCompare(right.path);
692
+ }
693
+ return kindOrder[left.kind] - kindOrder[right.kind];
694
+ });
695
+ }
696
+ function positiveSignalsFrom(contractSections) {
697
+ return POSITIVE_SECTION_IDS.flatMap((sectionId) => {
698
+ const section = contractSections[sectionId];
699
+ return section ? collectSectionSignals(section) : [];
700
+ });
701
+ }
702
+ function negativeSignalsFrom(contractSections) {
703
+ const section = contractSections.nonGoals;
704
+ return section ? collectSectionSignals(section) : [];
705
+ }
706
+ function toResult(filePath, candidates, diagnostics) {
707
+ const summary = createSummary(candidates, diagnostics);
708
+ return {
709
+ ok: !summary.blocked,
710
+ filePath,
711
+ summary,
712
+ candidates: sortCandidates(candidates),
713
+ diagnostics: [...diagnostics],
714
+ };
715
+ }
716
+ export async function resolveDesignScope(options = {}) {
717
+ const rootDir = options.rootDir ?? cwd();
718
+ const loadedContract = await loadDesignContract({
719
+ filePath: options.filePath,
720
+ rootDir,
721
+ });
722
+ const cacheKey = `${rootDir}::${loadedContract.filePath}`;
723
+ const cachedResult = designScopeResultCache.get(cacheKey);
724
+ if (cachedResult) {
725
+ return cachedResult;
726
+ }
727
+ const resultPromise = (async () => {
728
+ const baseDiagnostics = loadedContract.diagnostics.map(toMappingDiagnostic);
729
+ if (!loadedContract.ok) {
730
+ return toResult(loadedContract.filePath, [], baseDiagnostics);
731
+ }
732
+ const codeMap = loadCodeMapSnapshot(rootDir);
733
+ const sourceSnapshot = await buildSourceSnapshot(rootDir, codeMap);
734
+ const positiveSignals = positiveSignalsFrom(loadedContract.contract.sections);
735
+ const negativeSignals = negativeSignalsFrom(loadedContract.contract.sections);
736
+ const keywordSignals = positiveSignals.filter((signal) => signal.evidenceType === 'keyword-match');
737
+ const candidates = new Map();
738
+ resolveExactPathSignals(candidates, positiveSignals, rootDir);
739
+ resolveExactModuleSignals(candidates, positiveSignals, rootDir, codeMap);
740
+ await resolveSymbolSignals(candidates, positiveSignals, rootDir, codeMap, sourceSnapshot);
741
+ await resolveKeywordSignals(candidates, keywordSignals, rootDir, sourceSnapshot);
742
+ const filteredCandidates = applyNegativeFiltering(Array.from(candidates.values()), negativeSignals);
743
+ const earlyDiagnostics = buildScopeDiagnostics(filteredCandidates);
744
+ if (earlyDiagnostics.some((diagnostic) => diagnostic.blocker)) {
745
+ return toResult(loadedContract.filePath, filteredCandidates, [...baseDiagnostics, ...earlyDiagnostics]);
746
+ }
747
+ const enrichedCandidates = await enrichCandidates(filteredCandidates, rootDir, Boolean(loadedContract.contract.sections.openQuestions), sourceSnapshot, codeMap);
748
+ const scopeDiagnostics = buildScopeDiagnostics(enrichedCandidates);
749
+ return toResult(loadedContract.filePath, enrichedCandidates, [...baseDiagnostics, ...scopeDiagnostics]);
750
+ })();
751
+ designScopeResultCache.set(cacheKey, resultPromise);
752
+ try {
753
+ return await resultPromise;
754
+ }
755
+ catch (error) {
756
+ designScopeResultCache.delete(cacheKey);
757
+ throw error;
758
+ }
759
+ }
760
+ //# sourceMappingURL=design-scope-resolver.js.map