@memberjunction/db-auto-doc 2.117.0 → 2.119.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 (263) hide show
  1. package/README.md +803 -165
  2. package/bin/run.js +7 -0
  3. package/dist/api/DBAutoDocAPI.d.ts +252 -0
  4. package/dist/api/DBAutoDocAPI.d.ts.map +1 -0
  5. package/dist/api/DBAutoDocAPI.js +530 -0
  6. package/dist/api/DBAutoDocAPI.js.map +1 -0
  7. package/dist/api/index.d.ts +7 -0
  8. package/dist/api/index.d.ts.map +1 -0
  9. package/dist/api/index.js +10 -0
  10. package/dist/api/index.js.map +1 -0
  11. package/dist/commands/analyze.d.ts +6 -4
  12. package/dist/commands/analyze.d.ts.map +1 -1
  13. package/dist/commands/analyze.js +58 -71
  14. package/dist/commands/analyze.js.map +1 -1
  15. package/dist/commands/export.d.ts +14 -4
  16. package/dist/commands/export.d.ts.map +1 -1
  17. package/dist/commands/export.js +156 -61
  18. package/dist/commands/export.js.map +1 -1
  19. package/dist/commands/generate-queries.d.ts +17 -0
  20. package/dist/commands/generate-queries.d.ts.map +1 -0
  21. package/dist/commands/generate-queries.js +182 -0
  22. package/dist/commands/generate-queries.js.map +1 -0
  23. package/dist/commands/init.d.ts +3 -4
  24. package/dist/commands/init.d.ts.map +1 -1
  25. package/dist/commands/init.js +206 -144
  26. package/dist/commands/init.js.map +1 -1
  27. package/dist/commands/reset.d.ts +4 -1
  28. package/dist/commands/reset.d.ts.map +1 -1
  29. package/dist/commands/reset.js +33 -19
  30. package/dist/commands/reset.js.map +1 -1
  31. package/dist/commands/status.d.ts +10 -0
  32. package/dist/commands/status.d.ts.map +1 -0
  33. package/dist/commands/status.js +66 -0
  34. package/dist/commands/status.js.map +1 -0
  35. package/dist/core/AnalysisEngine.d.ts +108 -0
  36. package/dist/core/AnalysisEngine.d.ts.map +1 -0
  37. package/dist/core/AnalysisEngine.js +716 -0
  38. package/dist/core/AnalysisEngine.js.map +1 -0
  39. package/dist/core/AnalysisOrchestrator.d.ts +41 -0
  40. package/dist/core/AnalysisOrchestrator.d.ts.map +1 -0
  41. package/dist/core/AnalysisOrchestrator.js +377 -0
  42. package/dist/core/AnalysisOrchestrator.js.map +1 -0
  43. package/dist/core/BackpropagationEngine.d.ts +32 -0
  44. package/dist/core/BackpropagationEngine.d.ts.map +1 -0
  45. package/dist/core/BackpropagationEngine.js +121 -0
  46. package/dist/core/BackpropagationEngine.js.map +1 -0
  47. package/dist/core/ConvergenceDetector.d.ts +27 -0
  48. package/dist/core/ConvergenceDetector.d.ts.map +1 -0
  49. package/dist/core/ConvergenceDetector.js +92 -0
  50. package/dist/core/ConvergenceDetector.js.map +1 -0
  51. package/dist/core/GuardrailsManager.d.ts +78 -0
  52. package/dist/core/GuardrailsManager.d.ts.map +1 -0
  53. package/dist/core/GuardrailsManager.js +367 -0
  54. package/dist/core/GuardrailsManager.js.map +1 -0
  55. package/dist/core/index.d.ts +7 -0
  56. package/dist/core/index.d.ts.map +1 -0
  57. package/dist/core/index.js +13 -0
  58. package/dist/core/index.js.map +1 -0
  59. package/dist/database/Database.d.ts +56 -0
  60. package/dist/database/Database.d.ts.map +1 -0
  61. package/dist/database/Database.js +172 -0
  62. package/dist/database/Database.js.map +1 -0
  63. package/dist/database/TopologicalSorter.d.ts +25 -0
  64. package/dist/database/TopologicalSorter.d.ts.map +1 -0
  65. package/dist/database/TopologicalSorter.js +150 -0
  66. package/dist/database/TopologicalSorter.js.map +1 -0
  67. package/dist/database/index.d.ts +6 -0
  68. package/dist/database/index.d.ts.map +1 -0
  69. package/dist/database/index.js +14 -0
  70. package/dist/database/index.js.map +1 -0
  71. package/dist/discovery/ColumnStatsCache.d.ts +91 -0
  72. package/dist/discovery/ColumnStatsCache.d.ts.map +1 -0
  73. package/dist/discovery/ColumnStatsCache.js +231 -0
  74. package/dist/discovery/ColumnStatsCache.js.map +1 -0
  75. package/dist/discovery/DiscoveryEngine.d.ts +100 -0
  76. package/dist/discovery/DiscoveryEngine.d.ts.map +1 -0
  77. package/dist/discovery/DiscoveryEngine.js +726 -0
  78. package/dist/discovery/DiscoveryEngine.js.map +1 -0
  79. package/dist/discovery/DiscoveryTriggerAnalyzer.d.ts +57 -0
  80. package/dist/discovery/DiscoveryTriggerAnalyzer.d.ts.map +1 -0
  81. package/dist/discovery/DiscoveryTriggerAnalyzer.js +186 -0
  82. package/dist/discovery/DiscoveryTriggerAnalyzer.js.map +1 -0
  83. package/dist/discovery/FKDetector.d.ts +47 -0
  84. package/dist/discovery/FKDetector.d.ts.map +1 -0
  85. package/dist/discovery/FKDetector.js +317 -0
  86. package/dist/discovery/FKDetector.js.map +1 -0
  87. package/dist/discovery/LLMDiscoveryValidator.d.ts +64 -0
  88. package/dist/discovery/LLMDiscoveryValidator.d.ts.map +1 -0
  89. package/dist/discovery/LLMDiscoveryValidator.js +431 -0
  90. package/dist/discovery/LLMDiscoveryValidator.js.map +1 -0
  91. package/dist/discovery/LLMSanityChecker.d.ts +38 -0
  92. package/dist/discovery/LLMSanityChecker.d.ts.map +1 -0
  93. package/dist/discovery/LLMSanityChecker.js +156 -0
  94. package/dist/discovery/LLMSanityChecker.js.map +1 -0
  95. package/dist/discovery/PKDetector.d.ts +62 -0
  96. package/dist/discovery/PKDetector.d.ts.map +1 -0
  97. package/dist/discovery/PKDetector.js +436 -0
  98. package/dist/discovery/PKDetector.js.map +1 -0
  99. package/dist/discovery/index.d.ts +9 -0
  100. package/dist/discovery/index.d.ts.map +1 -0
  101. package/dist/discovery/index.js +25 -0
  102. package/dist/discovery/index.js.map +1 -0
  103. package/dist/drivers/BaseAutoDocDriver.d.ts +132 -0
  104. package/dist/drivers/BaseAutoDocDriver.d.ts.map +1 -0
  105. package/dist/drivers/BaseAutoDocDriver.js +121 -0
  106. package/dist/drivers/BaseAutoDocDriver.js.map +1 -0
  107. package/dist/drivers/MySQLDriver.d.ts +61 -0
  108. package/dist/drivers/MySQLDriver.d.ts.map +1 -0
  109. package/dist/drivers/MySQLDriver.js +668 -0
  110. package/dist/drivers/MySQLDriver.js.map +1 -0
  111. package/dist/drivers/PostgreSQLDriver.d.ts +65 -0
  112. package/dist/drivers/PostgreSQLDriver.d.ts.map +1 -0
  113. package/dist/drivers/PostgreSQLDriver.js +704 -0
  114. package/dist/drivers/PostgreSQLDriver.js.map +1 -0
  115. package/dist/drivers/SQLServerDriver.d.ts +61 -0
  116. package/dist/drivers/SQLServerDriver.d.ts.map +1 -0
  117. package/dist/drivers/SQLServerDriver.js +667 -0
  118. package/dist/drivers/SQLServerDriver.js.map +1 -0
  119. package/dist/generators/CSVGenerator.d.ts +35 -0
  120. package/dist/generators/CSVGenerator.d.ts.map +1 -0
  121. package/dist/generators/CSVGenerator.js +154 -0
  122. package/dist/generators/CSVGenerator.js.map +1 -0
  123. package/dist/generators/HTMLGenerator.d.ts +29 -0
  124. package/dist/generators/HTMLGenerator.d.ts.map +1 -0
  125. package/dist/generators/HTMLGenerator.js +710 -0
  126. package/dist/generators/HTMLGenerator.js.map +1 -0
  127. package/dist/generators/MarkdownGenerator.d.ts +27 -0
  128. package/dist/generators/MarkdownGenerator.d.ts.map +1 -0
  129. package/dist/generators/MarkdownGenerator.js +361 -0
  130. package/dist/generators/MarkdownGenerator.js.map +1 -0
  131. package/dist/generators/MermaidGenerator.d.ts +35 -0
  132. package/dist/generators/MermaidGenerator.d.ts.map +1 -0
  133. package/dist/generators/MermaidGenerator.js +321 -0
  134. package/dist/generators/MermaidGenerator.js.map +1 -0
  135. package/dist/generators/ReportGenerator.d.ts +22 -0
  136. package/dist/generators/ReportGenerator.d.ts.map +1 -0
  137. package/dist/generators/ReportGenerator.js +176 -0
  138. package/dist/generators/ReportGenerator.js.map +1 -0
  139. package/dist/generators/SQLGenerator.d.ts +31 -0
  140. package/dist/generators/SQLGenerator.d.ts.map +1 -0
  141. package/dist/generators/SQLGenerator.js +168 -0
  142. package/dist/generators/SQLGenerator.js.map +1 -0
  143. package/dist/generators/SampleQueryGenerator.d.ts +64 -0
  144. package/dist/generators/SampleQueryGenerator.d.ts.map +1 -0
  145. package/dist/generators/SampleQueryGenerator.js +500 -0
  146. package/dist/generators/SampleQueryGenerator.js.map +1 -0
  147. package/dist/generators/index.d.ts +10 -0
  148. package/dist/generators/index.d.ts.map +1 -0
  149. package/dist/generators/index.js +19 -0
  150. package/dist/generators/index.js.map +1 -0
  151. package/dist/index.d.ts +11 -20
  152. package/dist/index.d.ts.map +1 -1
  153. package/dist/index.js +19 -20
  154. package/dist/index.js.map +1 -1
  155. package/dist/prompts/PromptEngine.d.ts +65 -0
  156. package/dist/prompts/PromptEngine.d.ts.map +1 -0
  157. package/dist/prompts/PromptEngine.js +305 -0
  158. package/dist/prompts/PromptEngine.js.map +1 -0
  159. package/dist/prompts/PromptFileLoader.d.ts +21 -0
  160. package/dist/prompts/PromptFileLoader.d.ts.map +1 -0
  161. package/dist/prompts/PromptFileLoader.js +74 -0
  162. package/dist/prompts/PromptFileLoader.js.map +1 -0
  163. package/dist/prompts/index.d.ts +6 -0
  164. package/dist/prompts/index.d.ts.map +1 -0
  165. package/dist/prompts/index.js +11 -0
  166. package/dist/prompts/index.js.map +1 -0
  167. package/dist/state/IterationTracker.d.ts +64 -0
  168. package/dist/state/IterationTracker.d.ts.map +1 -0
  169. package/dist/state/IterationTracker.js +136 -0
  170. package/dist/state/IterationTracker.js.map +1 -0
  171. package/dist/state/StateManager.d.ts +79 -0
  172. package/dist/state/StateManager.d.ts.map +1 -0
  173. package/dist/state/StateManager.js +348 -0
  174. package/dist/state/StateManager.js.map +1 -0
  175. package/dist/state/StateValidator.d.ts +24 -0
  176. package/dist/state/StateValidator.d.ts.map +1 -0
  177. package/dist/state/StateValidator.js +147 -0
  178. package/dist/state/StateValidator.js.map +1 -0
  179. package/dist/state/index.d.ts +7 -0
  180. package/dist/state/index.d.ts.map +1 -0
  181. package/dist/state/index.js +13 -0
  182. package/dist/state/index.js.map +1 -0
  183. package/dist/types/analysis.d.ts +76 -0
  184. package/dist/types/analysis.d.ts.map +1 -0
  185. package/dist/types/analysis.js +6 -0
  186. package/dist/types/analysis.js.map +1 -0
  187. package/dist/types/config.d.ts +143 -0
  188. package/dist/types/config.d.ts.map +1 -0
  189. package/dist/types/config.js +7 -0
  190. package/dist/types/config.js.map +1 -0
  191. package/dist/types/discovery.d.ts +277 -0
  192. package/dist/types/discovery.d.ts.map +1 -0
  193. package/dist/types/discovery.js +7 -0
  194. package/dist/types/discovery.js.map +1 -0
  195. package/dist/types/driver.d.ts +148 -0
  196. package/dist/types/driver.d.ts.map +1 -0
  197. package/dist/types/driver.js +7 -0
  198. package/dist/types/driver.js.map +1 -0
  199. package/dist/types/index.d.ts +8 -0
  200. package/dist/types/index.d.ts.map +1 -0
  201. package/dist/types/index.js +24 -0
  202. package/dist/types/index.js.map +1 -0
  203. package/dist/types/prompts.d.ts +158 -0
  204. package/dist/types/prompts.d.ts.map +1 -0
  205. package/dist/types/prompts.js +6 -0
  206. package/dist/types/prompts.js.map +1 -0
  207. package/dist/types/sample-queries.d.ts +172 -0
  208. package/dist/types/sample-queries.d.ts.map +1 -0
  209. package/dist/types/sample-queries.js +7 -0
  210. package/dist/types/sample-queries.js.map +1 -0
  211. package/dist/types/state.d.ts +291 -0
  212. package/dist/types/state.d.ts.map +1 -0
  213. package/dist/types/state.js +7 -0
  214. package/dist/types/state.js.map +1 -0
  215. package/dist/utils/config-loader.d.ts +29 -0
  216. package/dist/utils/config-loader.d.ts.map +1 -0
  217. package/dist/utils/config-loader.js +163 -0
  218. package/dist/utils/config-loader.js.map +1 -0
  219. package/dist/utils/index.d.ts +5 -0
  220. package/dist/utils/index.d.ts.map +1 -0
  221. package/dist/utils/index.js +9 -0
  222. package/dist/utils/index.js.map +1 -0
  223. package/package.json +28 -3
  224. package/dist/ai/simple-ai-client.d.ts +0 -70
  225. package/dist/ai/simple-ai-client.d.ts.map +0 -1
  226. package/dist/ai/simple-ai-client.js +0 -181
  227. package/dist/ai/simple-ai-client.js.map +0 -1
  228. package/dist/analyzers/analyzer.d.ts +0 -23
  229. package/dist/analyzers/analyzer.d.ts.map +0 -1
  230. package/dist/analyzers/analyzer.js +0 -127
  231. package/dist/analyzers/analyzer.js.map +0 -1
  232. package/dist/cli-old/cli.d.ts +0 -3
  233. package/dist/cli-old/cli.d.ts.map +0 -1
  234. package/dist/cli-old/cli.js +0 -388
  235. package/dist/cli-old/cli.js.map +0 -1
  236. package/dist/commands/review.d.ts +0 -11
  237. package/dist/commands/review.d.ts.map +0 -1
  238. package/dist/commands/review.js +0 -82
  239. package/dist/commands/review.js.map +0 -1
  240. package/dist/database/connection.d.ts +0 -40
  241. package/dist/database/connection.d.ts.map +0 -1
  242. package/dist/database/connection.js +0 -136
  243. package/dist/database/connection.js.map +0 -1
  244. package/dist/database/introspection.d.ts +0 -59
  245. package/dist/database/introspection.d.ts.map +0 -1
  246. package/dist/database/introspection.js +0 -124
  247. package/dist/database/introspection.js.map +0 -1
  248. package/dist/generators/markdown-generator.d.ts +0 -8
  249. package/dist/generators/markdown-generator.d.ts.map +0 -1
  250. package/dist/generators/markdown-generator.js +0 -106
  251. package/dist/generators/markdown-generator.js.map +0 -1
  252. package/dist/generators/sql-generator.d.ts +0 -20
  253. package/dist/generators/sql-generator.d.ts.map +0 -1
  254. package/dist/generators/sql-generator.js +0 -83
  255. package/dist/generators/sql-generator.js.map +0 -1
  256. package/dist/state/state-manager.d.ts +0 -95
  257. package/dist/state/state-manager.d.ts.map +0 -1
  258. package/dist/state/state-manager.js +0 -236
  259. package/dist/state/state-manager.js.map +0 -1
  260. package/dist/types/state-file.d.ts +0 -124
  261. package/dist/types/state-file.d.ts.map +0 -1
  262. package/dist/types/state-file.js +0 -79
  263. package/dist/types/state-file.js.map +0 -1
@@ -0,0 +1,716 @@
1
+ "use strict";
2
+ /**
3
+ * Main analysis orchestrator
4
+ * Coordinates the entire documentation generation workflow
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.AnalysisEngine = void 0;
8
+ const BackpropagationEngine_js_1 = require("./BackpropagationEngine.js");
9
+ const ConvergenceDetector_js_1 = require("./ConvergenceDetector.js");
10
+ const GuardrailsManager_js_1 = require("./GuardrailsManager.js");
11
+ class AnalysisEngine {
12
+ constructor(config, promptEngine, stateManager, iterationTracker) {
13
+ this.config = config;
14
+ this.promptEngine = promptEngine;
15
+ this.stateManager = stateManager;
16
+ this.iterationTracker = iterationTracker;
17
+ this.startTime = 0;
18
+ this.backpropagationEngine = new BackpropagationEngine_js_1.BackpropagationEngine(promptEngine, stateManager, iterationTracker, config.analysis.backpropagation.maxDepth);
19
+ this.convergenceDetector = new ConvergenceDetector_js_1.ConvergenceDetector(config.analysis.convergence, stateManager, iterationTracker);
20
+ this.guardrailsManager = new GuardrailsManager_js_1.GuardrailsManager(config.analysis.guardrails);
21
+ // Set up guardrail checking in PromptEngine
22
+ this.promptEngine.setGuardrailCheck(() => {
23
+ if (!this.currentRun) {
24
+ return { canContinue: true };
25
+ }
26
+ const result = this.guardrailsManager.checkGuardrails(this.currentRun);
27
+ this.guardrailsManager.recordEnforcement(this.currentRun, result);
28
+ return result;
29
+ });
30
+ }
31
+ /**
32
+ * Initialize timing for guardrails and set current run
33
+ */
34
+ startAnalysis(run) {
35
+ this.startTime = Date.now();
36
+ this.currentRun = run;
37
+ this.guardrailsManager.startPhase('analysis');
38
+ }
39
+ /**
40
+ * Process a single dependency level
41
+ */
42
+ async processLevel(state, run, level, tables) {
43
+ const triggers = [];
44
+ for (const tableNode of tables) {
45
+ const result = await this.analyzeTable(state, run, tableNode, level);
46
+ // Check if guardrail was exceeded during this table's analysis
47
+ if (result.guardrailExceeded) {
48
+ break; // Stop processing this level
49
+ }
50
+ if (result.triggers) {
51
+ triggers.push(...result.triggers);
52
+ }
53
+ }
54
+ run.levelsProcessed = Math.max(run.levelsProcessed, level + 1);
55
+ return triggers;
56
+ }
57
+ /**
58
+ * Analyze a single table
59
+ */
60
+ async analyzeTable(state, run, tableNode, level) {
61
+ const table = tableNode.tableDefinition;
62
+ if (!table) {
63
+ return {};
64
+ }
65
+ try {
66
+ // Build analysis context
67
+ const context = this.buildTableContext(state, tableNode);
68
+ // Execute analysis prompt
69
+ const result = await this.promptEngine.executePrompt('table-analysis', context, {
70
+ responseFormat: 'JSON',
71
+ temperature: this.config.ai.temperature
72
+ });
73
+ // Check if guardrail was exceeded
74
+ if (result.guardrailExceeded) {
75
+ this.iterationTracker.completeRun(run, false, result.errorMessage || 'Guardrail exceeded');
76
+ return { guardrailExceeded: true };
77
+ }
78
+ if (!result.success || !result.result) {
79
+ this.iterationTracker.addError(run, `Failed to analyze ${tableNode.schema}.${tableNode.table}: ${result.errorMessage}`);
80
+ this.iterationTracker.addLogEntryWithPrompt(run, {
81
+ level,
82
+ schema: tableNode.schema,
83
+ table: tableNode.table,
84
+ action: 'analyze',
85
+ result: 'error',
86
+ message: result.errorMessage
87
+ }, result.promptInput, result.promptOutput);
88
+ return {};
89
+ }
90
+ // Track tokens
91
+ this.iterationTracker.addTokenUsage(run, result.tokensUsed, result.cost);
92
+ // Use semantic comparison to check if description materially changed
93
+ const previousDescription = table.description;
94
+ const comparisonResult = await this.compareDescriptions(run, table, result.result, tableNode.schema, tableNode.table);
95
+ const descriptionChanged = comparisonResult.tableMateriallyChanged;
96
+ // Update table description
97
+ this.stateManager.updateTableDescription(table, result.result.tableDescription, result.result.reasoning, result.result.confidence, run.modelUsed, previousDescription ? 'refinement' : 'initial');
98
+ // Update column descriptions
99
+ for (const colDesc of result.result.columnDescriptions || []) {
100
+ const column = table.columns.find(c => c.name === colDesc.columnName);
101
+ if (column) {
102
+ this.stateManager.updateColumnDescription(column, colDesc.description, colDesc.reasoning, run.modelUsed);
103
+ }
104
+ }
105
+ // Process structured FK insights from LLM and feed back to discovery phase
106
+ if (state.phases.keyDetection && result.result.foreignKeys) {
107
+ this.processFKInsightsFromLLM(state, tableNode.schema, tableNode.table, result.result.foreignKeys);
108
+ }
109
+ // Update inferred business domain
110
+ if (result.result.inferredBusinessDomain) {
111
+ // Could store this in table metadata if needed
112
+ }
113
+ // Log result with prompt I/O and semantic comparison details
114
+ this.iterationTracker.addLogEntryWithPrompt(run, {
115
+ level,
116
+ schema: tableNode.schema,
117
+ table: tableNode.table,
118
+ action: 'analyze',
119
+ result: descriptionChanged ? 'changed' : 'unchanged',
120
+ message: `Confidence: ${result.result.confidence.toFixed(2)}`,
121
+ tokensUsed: result.tokensUsed,
122
+ semanticComparison: comparisonResult
123
+ }, result.promptInput, result.promptOutput);
124
+ // Detect potential insights for parent tables
125
+ const triggers = this.backpropagationEngine.detectParentInsights(table, result.result, tableNode.schema, tableNode.table);
126
+ return { triggers };
127
+ }
128
+ catch (error) {
129
+ this.iterationTracker.addError(run, `Exception analyzing ${tableNode.schema}.${tableNode.table}: ${error.message}`);
130
+ this.iterationTracker.addLogEntry(run, {
131
+ level,
132
+ schema: tableNode.schema,
133
+ table: tableNode.table,
134
+ action: 'analyze',
135
+ result: 'error',
136
+ message: error.message
137
+ });
138
+ return {};
139
+ }
140
+ }
141
+ /**
142
+ * Build context for table analysis
143
+ */
144
+ buildTableContext(state, tableNode) {
145
+ const table = tableNode.tableDefinition;
146
+ // Get parent table descriptions (for context)
147
+ const parentDescriptions = table.dependsOn
148
+ .map(dep => {
149
+ const parentTable = this.stateManager.findTable(state, dep.schema, dep.table);
150
+ if (parentTable && parentTable.description) {
151
+ return {
152
+ schema: dep.schema,
153
+ table: dep.table,
154
+ description: parentTable.description
155
+ };
156
+ }
157
+ return null;
158
+ })
159
+ .filter(p => p !== null);
160
+ // Build list of all tables in the database for FK reference validation
161
+ const allTables = [];
162
+ for (const schema of state.schemas) {
163
+ for (const tbl of schema.tables) {
164
+ allTables.push({ schema: schema.name, name: tbl.name });
165
+ }
166
+ }
167
+ return {
168
+ schema: tableNode.schema,
169
+ table: tableNode.table,
170
+ rowCount: table.rowCount,
171
+ columns: table.columns.map(col => ({
172
+ name: col.name,
173
+ dataType: col.dataType,
174
+ isNullable: col.isNullable,
175
+ isPrimaryKey: col.isPrimaryKey,
176
+ isForeignKey: col.isForeignKey,
177
+ foreignKeyReferences: col.foreignKeyReferences,
178
+ checkConstraint: col.checkConstraint,
179
+ defaultValue: col.defaultValue,
180
+ possibleValues: col.possibleValues,
181
+ statistics: col.statistics
182
+ })),
183
+ dependsOn: table.dependsOn,
184
+ dependents: table.dependents,
185
+ sampleData: [], // Could add sample rows here if needed
186
+ parentDescriptions: parentDescriptions,
187
+ userNotes: table.userNotes,
188
+ seedContext: state.seedContext,
189
+ allTables
190
+ };
191
+ }
192
+ /**
193
+ * Compare descriptions using LLM to determine material changes
194
+ */
195
+ async compareDescriptions(run, table, newResult, schemaName, tableName) {
196
+ // Get previous iteration
197
+ const previousIteration = table.descriptionIterations.length > 0
198
+ ? table.descriptionIterations[table.descriptionIterations.length - 1]
199
+ : null;
200
+ // If no previous iteration, it's initial generation - don't waste tokens on comparison
201
+ // Return minimal result with no column changes (they'll all be logged as "initial" anyway)
202
+ if (!previousIteration) {
203
+ return {
204
+ tableMateriallyChanged: true,
205
+ tableChangeReasoning: 'Initial generation (skipped semantic comparison)',
206
+ columnChanges: [] // Don't log individual column changes for initial generation
207
+ };
208
+ }
209
+ // Build previous column descriptions map
210
+ const previousColumns = table.columns.map(col => ({
211
+ columnName: col.name,
212
+ description: col.description || ''
213
+ }));
214
+ // Build current column descriptions
215
+ const currentColumns = newResult.columnDescriptions.map(col => ({
216
+ columnName: col.columnName,
217
+ description: col.description
218
+ }));
219
+ // Call semantic comparison prompt
220
+ const result = await this.promptEngine.executePrompt('semantic-comparison', {
221
+ schemaName,
222
+ tableName,
223
+ previousIteration: table.descriptionIterations.length,
224
+ currentIteration: table.descriptionIterations.length + 1,
225
+ previousTableDescription: previousIteration.description,
226
+ previousColumns,
227
+ currentTableDescription: newResult.tableDescription,
228
+ currentColumns
229
+ }, {
230
+ responseFormat: 'JSON'
231
+ });
232
+ if (!result.success || !result.result) {
233
+ // If comparison fails, assume changed to be safe
234
+ return {
235
+ tableMateriallyChanged: true,
236
+ tableChangeReasoning: 'Comparison failed - assuming changed for safety',
237
+ columnChanges: newResult.columnDescriptions.map(col => ({
238
+ columnName: col.columnName,
239
+ materiallyChanged: true,
240
+ changeReasoning: 'Comparison failed'
241
+ }))
242
+ };
243
+ }
244
+ // Track tokens for comparison
245
+ this.iterationTracker.addTokenUsage(run, result.tokensUsed, result.cost);
246
+ return result.result;
247
+ }
248
+ /**
249
+ * Perform dependency-level sanity check
250
+ * Checks consistency across tables at the same dependency level
251
+ */
252
+ async performDependencyLevelSanityCheck(state, run, level, tables) {
253
+ try {
254
+ const context = {
255
+ dependencyLevel: level,
256
+ tables: tables.map(t => {
257
+ const tableDef = t.tableDefinition;
258
+ return {
259
+ schema: t.schema,
260
+ table: t.table,
261
+ description: tableDef?.description || 'No description yet',
262
+ columns: tableDef?.columns.map(c => ({
263
+ name: c.name,
264
+ description: c.description || 'No description yet'
265
+ })) || [],
266
+ dependsOn: tableDef?.dependsOn || [],
267
+ dependents: tableDef?.dependents || []
268
+ };
269
+ })
270
+ };
271
+ const result = await this.promptEngine.executePrompt('dependency-level-sanity-check', context, {
272
+ responseFormat: 'JSON'
273
+ });
274
+ if (result.success && result.result) {
275
+ // Track this sanity check
276
+ const sanityCheckRecord = {
277
+ timestamp: new Date().toISOString(),
278
+ checkType: 'dependency_level',
279
+ scope: `level ${level}`,
280
+ hasMaterialIssues: result.result.hasMaterialIssues,
281
+ issuesFound: result.result.tableIssues.length,
282
+ tablesAffected: result.result.tableIssues.map(i => i.tableName),
283
+ result: result.result.hasMaterialIssues ? 'issues_corrected' : 'no_issues',
284
+ tokensUsed: result.tokensUsed,
285
+ promptInput: result.promptInput,
286
+ promptOutput: result.promptOutput
287
+ };
288
+ run.sanityChecks.push(sanityCheckRecord);
289
+ run.sanityCheckCount++;
290
+ // Track tokens
291
+ this.iterationTracker.addTokenUsage(run, result.tokensUsed, result.cost);
292
+ // Log issues
293
+ if (result.result.hasMaterialIssues) {
294
+ for (const issue of result.result.tableIssues) {
295
+ this.iterationTracker.addWarning(run, `[Level ${level}] ${issue.severity.toUpperCase()}: ${issue.tableName} - ${issue.description}`);
296
+ }
297
+ }
298
+ return result.result.hasMaterialIssues;
299
+ }
300
+ return false;
301
+ }
302
+ catch (error) {
303
+ this.iterationTracker.addError(run, `Dependency-level sanity check failed for level ${level}: ${error.message}`);
304
+ return false;
305
+ }
306
+ }
307
+ /**
308
+ * Perform schema-level sanity check
309
+ * Holistic review after entire schema is analyzed
310
+ */
311
+ async performSchemaLevelSanityCheck(state, run, schema) {
312
+ try {
313
+ const context = {
314
+ schemaName: schema.name,
315
+ tables: schema.tables.map(t => ({
316
+ name: t.name,
317
+ description: t.description || 'No description yet',
318
+ rowCount: t.rowCount,
319
+ dependencyLevel: t.dependencyLevel,
320
+ columns: t.columns.map(c => ({
321
+ name: c.name,
322
+ dataType: c.dataType,
323
+ description: c.description || 'No description yet'
324
+ })),
325
+ dependsOn: t.dependsOn,
326
+ dependents: t.dependents
327
+ }))
328
+ };
329
+ const result = await this.promptEngine.executePrompt('schema-level-sanity-check', context, {
330
+ responseFormat: 'JSON'
331
+ });
332
+ if (result.success && result.result) {
333
+ // Update schema description if provided
334
+ if (result.result.schemaLevelIssues.some(i => i.suggestedSchemaDescription)) {
335
+ const suggested = result.result.schemaLevelIssues.find(i => i.suggestedSchemaDescription);
336
+ if (suggested?.suggestedSchemaDescription) {
337
+ this.stateManager.updateSchemaDescription(schema, suggested.suggestedSchemaDescription, 'Generated from schema-level sanity check', run.modelUsed);
338
+ }
339
+ }
340
+ // Track this sanity check
341
+ const sanityCheckRecord = {
342
+ timestamp: new Date().toISOString(),
343
+ checkType: 'schema_level',
344
+ scope: `${schema.name} schema`,
345
+ hasMaterialIssues: result.result.hasMaterialIssues,
346
+ issuesFound: result.result.schemaLevelIssues.length + result.result.tableIssues.length,
347
+ tablesAffected: [
348
+ ...result.result.tableIssues.map(i => i.tableName),
349
+ ...result.result.schemaLevelIssues.flatMap(i => i.affectedTables)
350
+ ],
351
+ result: result.result.hasMaterialIssues ? 'issues_corrected' : 'no_issues',
352
+ tokensUsed: result.tokensUsed,
353
+ promptInput: result.promptInput,
354
+ promptOutput: result.promptOutput
355
+ };
356
+ run.sanityChecks.push(sanityCheckRecord);
357
+ run.sanityCheckCount++;
358
+ // Track tokens
359
+ this.iterationTracker.addTokenUsage(run, result.tokensUsed, result.cost);
360
+ // Log issues
361
+ if (result.result.hasMaterialIssues) {
362
+ for (const issue of result.result.schemaLevelIssues) {
363
+ this.iterationTracker.addWarning(run, `[Schema ${schema.name}] ${issue.severity.toUpperCase()}: ${issue.issueType} - ${issue.description}`);
364
+ }
365
+ for (const issue of result.result.tableIssues) {
366
+ this.iterationTracker.addWarning(run, `[Schema ${schema.name}] ${issue.severity.toUpperCase()}: ${issue.tableName} - ${issue.description}`);
367
+ }
368
+ }
369
+ return result.result.hasMaterialIssues;
370
+ }
371
+ return false;
372
+ }
373
+ catch (error) {
374
+ this.iterationTracker.addError(run, `Schema-level sanity check failed for ${schema.name}: ${error.message}`);
375
+ return false;
376
+ }
377
+ }
378
+ /**
379
+ * Perform cross-schema sanity check
380
+ * Validates consistency across all schemas
381
+ */
382
+ async performCrossSchemaSanityCheck(state, run) {
383
+ if (state.schemas.length <= 1) {
384
+ return false; // No need for cross-schema check
385
+ }
386
+ try {
387
+ const context = {
388
+ schemas: state.schemas.map(s => ({
389
+ schemaName: s.name,
390
+ description: s.description || 'No description yet',
391
+ tableCount: s.tables.length,
392
+ tables: s.tables.map(t => ({
393
+ name: t.name,
394
+ description: t.description || 'No description yet',
395
+ rowCount: t.rowCount
396
+ }))
397
+ }))
398
+ };
399
+ const result = await this.promptEngine.executePrompt('cross-schema-sanity-check', context, {
400
+ responseFormat: 'JSON'
401
+ });
402
+ if (result.success && result.result) {
403
+ // Track this sanity check
404
+ const sanityCheckRecord = {
405
+ timestamp: new Date().toISOString(),
406
+ checkType: 'cross_schema',
407
+ scope: 'all schemas',
408
+ hasMaterialIssues: result.result.hasMaterialIssues,
409
+ issuesFound: result.result.crossSchemaIssues.length + result.result.schemaIssues.length,
410
+ tablesAffected: result.result.crossSchemaIssues.flatMap(i => i.affectedTables.map(t => `${t.schema}.${t.table}`)),
411
+ result: result.result.hasMaterialIssues ? 'issues_corrected' : 'no_issues',
412
+ tokensUsed: result.tokensUsed,
413
+ promptInput: result.promptInput,
414
+ promptOutput: result.promptOutput
415
+ };
416
+ run.sanityChecks.push(sanityCheckRecord);
417
+ run.sanityCheckCount++;
418
+ // Track tokens
419
+ this.iterationTracker.addTokenUsage(run, result.tokensUsed, result.cost);
420
+ // Log issues
421
+ if (result.result.hasMaterialIssues) {
422
+ for (const issue of result.result.crossSchemaIssues) {
423
+ this.iterationTracker.addWarning(run, `[Cross-Schema] ${issue.severity.toUpperCase()}: ${issue.issueType} - ${issue.description}`);
424
+ }
425
+ for (const issue of result.result.schemaIssues) {
426
+ this.iterationTracker.addWarning(run, `[Schema ${issue.schemaName}] ${issue.issueType} - ${issue.description}`);
427
+ }
428
+ }
429
+ return result.result.hasMaterialIssues;
430
+ }
431
+ return false;
432
+ }
433
+ catch (error) {
434
+ this.iterationTracker.addError(run, `Cross-schema sanity check failed: ${error.message}`);
435
+ return false;
436
+ }
437
+ }
438
+ /**
439
+ * Check convergence
440
+ */
441
+ checkConvergence(state, run) {
442
+ const result = this.convergenceDetector.hasConverged(state, run);
443
+ if (result.converged) {
444
+ this.iterationTracker.completeRun(run, true, result.reason);
445
+ return true;
446
+ }
447
+ return false;
448
+ }
449
+ /**
450
+ * Execute backpropagation
451
+ */
452
+ async executeBackpropagation(state, run, triggers) {
453
+ if (!this.config.analysis.backpropagation.enabled) {
454
+ return;
455
+ }
456
+ if (triggers.length === 0) {
457
+ return;
458
+ }
459
+ await this.backpropagationEngine.execute(state, run, triggers);
460
+ }
461
+ /**
462
+ * Extract FK insights from column descriptions and create feedback to discovery phase
463
+ *
464
+ * **DEPRECATED**: This method previously used brittle regex heuristics to parse natural language
465
+ * descriptions. Per architectural decision, we should use LLM for language understanding, not regex.
466
+ *
467
+ * **TODO**: Replace with structured LLM output approach:
468
+ * 1. Update table-analysis prompt to include a "foreignKeys" array in JSON response:
469
+ * {
470
+ * "tableDescription": "...",
471
+ * "columnDescriptions": [...],
472
+ * "foreignKeys": [
473
+ * { "column": "prd_id", "referencesTable": "inv.prd", "referencesColumn": "prd_id" }
474
+ * ]
475
+ * }
476
+ * 2. Process the structured foreignKeys array directly instead of parsing descriptions
477
+ * 3. Use deterministic code only for statistics calculation, LLM for reasoning
478
+ *
479
+ * For now, this method is disabled to prevent brittle regex-based FK detection.
480
+ */
481
+ extractAndFeedbackFKInsights(state, schemaName, tableName, columnDescriptions) {
482
+ // Method disabled - awaiting structured LLM output implementation
483
+ console.log(`[AnalysisEngine] extractAndFeedbackFKInsights disabled - awaiting structured FK output from LLM`);
484
+ return;
485
+ }
486
+ /**
487
+ * Process structured FK insights from LLM and create feedback to discovery phase
488
+ *
489
+ * Uses the foreignKeys array from table-analysis prompt response instead of brittle regex parsing.
490
+ * Per architectural decision: use LLM for language understanding, deterministic code for processing.
491
+ */
492
+ processFKInsightsFromLLM(state, schemaName, tableName, foreignKeys) {
493
+ const discoveryPhase = state.phases.keyDetection;
494
+ if (!discoveryPhase || !foreignKeys || foreignKeys.length === 0)
495
+ return;
496
+ console.log(`[AnalysisEngine] Processing ${foreignKeys.length} structured FK insights from LLM for ${schemaName}.${tableName}`);
497
+ for (const fk of foreignKeys) {
498
+ const { columnName, referencesSchema, referencesTable, referencesColumn, confidence } = fk;
499
+ // Create feedback for this FK
500
+ const feedback = {
501
+ type: 'new_relationship',
502
+ evidence: `LLM-identified FK: ${columnName} -> ${referencesSchema}.${referencesTable}.${referencesColumn} (confidence: ${confidence})`,
503
+ tableName,
504
+ columnName,
505
+ affectedCandidates: [],
506
+ recommendation: 'add_new',
507
+ newRelationship: {
508
+ targetTable: `${referencesSchema}.${referencesTable}`,
509
+ targetColumn: referencesColumn
510
+ }
511
+ };
512
+ // Check if this column was incorrectly marked as a PK - reject it unless it's a surrogate key
513
+ const falsePK = discoveryPhase.discovered.primaryKeys.find(pk => pk.schemaName === schemaName &&
514
+ pk.tableName === tableName &&
515
+ pk.columnNames.includes(columnName));
516
+ if (falsePK) {
517
+ const columnLower = columnName.toLowerCase();
518
+ const tableLower = tableName.toLowerCase();
519
+ const isSurrogateKey = columnLower === `${tableLower}_id` ||
520
+ columnLower === tableLower + 'id' ||
521
+ columnLower === 'id';
522
+ if (!isSurrogateKey) {
523
+ falsePK.status = 'rejected';
524
+ feedback.affectedCandidates.push(`PK:${schemaName}.${tableName}.${columnName}`);
525
+ const column = this.findColumnInState(state, schemaName, tableName, columnName);
526
+ if (column)
527
+ column.isPrimaryKey = false;
528
+ console.log(`[AnalysisEngine] FK from LLM: ${schemaName}.${tableName}.${columnName} -> ${referencesSchema}.${referencesTable}, rejecting as PK`);
529
+ }
530
+ }
531
+ // Check if we already have this FK - boost confidence
532
+ const existingFK = discoveryPhase.discovered.foreignKeys.find(fk => fk.schemaName === schemaName &&
533
+ fk.sourceTable === tableName &&
534
+ fk.sourceColumn === columnName);
535
+ if (existingFK && existingFK.status === 'candidate') {
536
+ existingFK.validatedByLLM = true;
537
+ existingFK.status = 'confirmed';
538
+ existingFK.confidence = Math.min(existingFK.confidence + 20, 100);
539
+ feedback.type = 'confidence_change';
540
+ feedback.newConfidence = existingFK.confidence;
541
+ feedback.affectedCandidates.push(`FK:${schemaName}.${tableName}.${columnName}`);
542
+ const column = this.findColumnInState(state, schemaName, tableName, columnName);
543
+ if (column) {
544
+ column.isForeignKey = true;
545
+ column.foreignKeyReferences = {
546
+ schema: referencesSchema,
547
+ table: referencesTable,
548
+ column: referencesColumn,
549
+ referencedColumn: referencesColumn
550
+ };
551
+ }
552
+ // Update table-level dependsOn and dependents arrays for ERD generation
553
+ this.updateTableDependencies(state, schemaName, tableName, referencesSchema, referencesTable, columnName, referencesColumn);
554
+ console.log(`[AnalysisEngine] Confirmed FK: ${schemaName}.${tableName}.${columnName} -> ${referencesSchema}.${referencesTable}.${referencesColumn}, confidence: ${existingFK.confidence}`);
555
+ }
556
+ else {
557
+ // Create new FK from LLM insight
558
+ const newFK = {
559
+ schemaName,
560
+ sourceTable: tableName,
561
+ sourceColumn: columnName,
562
+ targetSchema: referencesSchema,
563
+ targetTable: referencesTable,
564
+ targetColumn: referencesColumn,
565
+ confidence: Math.round(confidence * 100),
566
+ evidence: {
567
+ namingMatch: 0.9,
568
+ valueOverlap: 0,
569
+ cardinalityRatio: 0,
570
+ dataTypeMatch: true,
571
+ nullPercentage: 0,
572
+ sampleSize: 0,
573
+ orphanCount: 0,
574
+ warnings: ['Created from structured LLM output']
575
+ },
576
+ discoveredInIteration: 1,
577
+ validatedByLLM: true,
578
+ status: 'confirmed'
579
+ };
580
+ discoveryPhase.discovered.foreignKeys.push(newFK);
581
+ feedback.newConfidence = newFK.confidence;
582
+ feedback.affectedCandidates.push(`FK:${schemaName}.${tableName}.${columnName}`);
583
+ const column = this.findColumnInState(state, schemaName, tableName, columnName);
584
+ if (column) {
585
+ column.isForeignKey = true;
586
+ column.foreignKeyReferences = {
587
+ schema: referencesSchema,
588
+ table: referencesTable,
589
+ column: referencesColumn,
590
+ referencedColumn: referencesColumn
591
+ };
592
+ }
593
+ // Update table-level dependsOn and dependents arrays for ERD generation
594
+ this.updateTableDependencies(state, schemaName, tableName, referencesSchema, referencesTable, columnName, referencesColumn);
595
+ console.log(`[AnalysisEngine] Created FK from LLM: ${schemaName}.${tableName}.${columnName} -> ${referencesSchema}.${referencesTable}.${referencesColumn}`);
596
+ }
597
+ discoveryPhase.feedbackFromAnalysis.push(feedback);
598
+ }
599
+ }
600
+ /**
601
+ * Find a column in the state schemas
602
+ */
603
+ findColumnInState(state, schemaName, tableName, columnName) {
604
+ const schema = state.schemas.find(s => s.name === schemaName);
605
+ if (!schema)
606
+ return null;
607
+ const table = schema.tables.find(t => t.name === tableName);
608
+ if (!table)
609
+ return null;
610
+ return table.columns.find(c => c.name === columnName) || null;
611
+ }
612
+ /**
613
+ * Update table-level dependsOn and dependents arrays when creating FK relationships
614
+ * This is required for ERD diagram generation
615
+ */
616
+ updateTableDependencies(state, sourceSchemaName, sourceTableName, targetSchemaName, targetTableName, sourceColumnName, targetColumnName) {
617
+ // Find source table and add to its dependsOn array
618
+ const sourceSchema = state.schemas.find(s => s.name === sourceSchemaName);
619
+ if (sourceSchema) {
620
+ const sourceTable = sourceSchema.tables.find(t => t.name === sourceTableName);
621
+ if (sourceTable) {
622
+ // Check if this dependency already exists
623
+ const existingDep = sourceTable.dependsOn.find(dep => dep.schema === targetSchemaName &&
624
+ dep.table === targetTableName &&
625
+ dep.column === sourceColumnName);
626
+ if (!existingDep) {
627
+ sourceTable.dependsOn.push({
628
+ schema: targetSchemaName,
629
+ table: targetTableName,
630
+ column: sourceColumnName,
631
+ referencedColumn: targetColumnName
632
+ });
633
+ console.log(`[AnalysisEngine] Updated dependsOn for ${sourceSchemaName}.${sourceTableName} -> ${targetSchemaName}.${targetTableName}`);
634
+ }
635
+ }
636
+ }
637
+ // Find target table and add to its dependents array
638
+ const targetSchema = state.schemas.find(s => s.name === targetSchemaName);
639
+ if (targetSchema) {
640
+ const targetTable = targetSchema.tables.find(t => t.name === targetTableName);
641
+ if (targetTable) {
642
+ // Check if this dependent already exists
643
+ const existingDep = targetTable.dependents.find(dep => dep.schema === sourceSchemaName &&
644
+ dep.table === sourceTableName &&
645
+ dep.column === sourceColumnName);
646
+ if (!existingDep) {
647
+ targetTable.dependents.push({
648
+ schema: sourceSchemaName,
649
+ table: sourceTableName,
650
+ column: sourceColumnName,
651
+ referencedColumn: targetColumnName
652
+ });
653
+ console.log(`[AnalysisEngine] Updated dependents for ${targetSchemaName}.${targetTableName} <- ${sourceSchemaName}.${sourceTableName}`);
654
+ }
655
+ }
656
+ }
657
+ }
658
+ /**
659
+ * Resolve target table from LLM hint (e.g., "product", "warehouse", "supplier")
660
+ * Returns the best matching table and its likely PK column
661
+ */
662
+ resolveTargetTable(state, tableHint, currentSchema) {
663
+ const hint = tableHint.toLowerCase().trim();
664
+ // Search all schemas for matching table names
665
+ for (const schema of state.schemas) {
666
+ for (const table of schema.tables) {
667
+ const tableLower = table.name.toLowerCase();
668
+ // Direct match
669
+ if (tableLower === hint || tableLower === hint + 's' || tableLower + 's' === hint) {
670
+ // Find PK column (prefer columns ending in _id or named id)
671
+ const pkColumn = table.columns.find(c => c.isPrimaryKey);
672
+ if (pkColumn) {
673
+ return {
674
+ schema: schema.name,
675
+ table: table.name,
676
+ column: pkColumn.name
677
+ };
678
+ }
679
+ // Fallback: look for column ending in _id or just 'id'
680
+ const idColumn = table.columns.find(c => c.name.toLowerCase() === 'id' ||
681
+ c.name.toLowerCase() === `${tableLower}_id` ||
682
+ c.name.toLowerCase().endsWith('_id'));
683
+ if (idColumn) {
684
+ return {
685
+ schema: schema.name,
686
+ table: table.name,
687
+ column: idColumn.name
688
+ };
689
+ }
690
+ // Fallback: first column
691
+ if (table.columns.length > 0) {
692
+ return {
693
+ schema: schema.name,
694
+ table: table.name,
695
+ column: table.columns[0].name
696
+ };
697
+ }
698
+ }
699
+ // Partial match (e.g., "product" matches "prd", "products")
700
+ if (tableLower.includes(hint) || hint.includes(tableLower)) {
701
+ const pkColumn = table.columns.find(c => c.isPrimaryKey);
702
+ if (pkColumn) {
703
+ return {
704
+ schema: schema.name,
705
+ table: table.name,
706
+ column: pkColumn.name
707
+ };
708
+ }
709
+ }
710
+ }
711
+ }
712
+ return null;
713
+ }
714
+ }
715
+ exports.AnalysisEngine = AnalysisEngine;
716
+ //# sourceMappingURL=AnalysisEngine.js.map