mindheal 1.0.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 (255) hide show
  1. package/.env.example +48 -0
  2. package/CHANGELOG.md +27 -0
  3. package/LICENSE +21 -0
  4. package/README.md +481 -0
  5. package/dist/cjs/ai/ai-provider.js +46 -0
  6. package/dist/cjs/ai/ai-provider.js.map +1 -0
  7. package/dist/cjs/ai/anthropic-provider.js +106 -0
  8. package/dist/cjs/ai/anthropic-provider.js.map +1 -0
  9. package/dist/cjs/ai/azure-openai-provider.js +130 -0
  10. package/dist/cjs/ai/azure-openai-provider.js.map +1 -0
  11. package/dist/cjs/ai/bedrock-provider.js +183 -0
  12. package/dist/cjs/ai/bedrock-provider.js.map +1 -0
  13. package/dist/cjs/ai/deepseek-provider.js +118 -0
  14. package/dist/cjs/ai/deepseek-provider.js.map +1 -0
  15. package/dist/cjs/ai/gemini-provider.js +129 -0
  16. package/dist/cjs/ai/gemini-provider.js.map +1 -0
  17. package/dist/cjs/ai/groq-provider.js +118 -0
  18. package/dist/cjs/ai/groq-provider.js.map +1 -0
  19. package/dist/cjs/ai/meta-provider.js +118 -0
  20. package/dist/cjs/ai/meta-provider.js.map +1 -0
  21. package/dist/cjs/ai/ollama-provider.js +127 -0
  22. package/dist/cjs/ai/ollama-provider.js.map +1 -0
  23. package/dist/cjs/ai/openai-provider.js +117 -0
  24. package/dist/cjs/ai/openai-provider.js.map +1 -0
  25. package/dist/cjs/ai/perplexity-provider.js +118 -0
  26. package/dist/cjs/ai/perplexity-provider.js.map +1 -0
  27. package/dist/cjs/ai/prompt-templates.js +174 -0
  28. package/dist/cjs/ai/prompt-templates.js.map +1 -0
  29. package/dist/cjs/ai/qwen-provider.js +118 -0
  30. package/dist/cjs/ai/qwen-provider.js.map +1 -0
  31. package/dist/cjs/analytics/healing-analytics.js +263 -0
  32. package/dist/cjs/analytics/healing-analytics.js.map +1 -0
  33. package/dist/cjs/cli/init.js +517 -0
  34. package/dist/cjs/cli/init.js.map +1 -0
  35. package/dist/cjs/config/config-loader.js +135 -0
  36. package/dist/cjs/config/config-loader.js.map +1 -0
  37. package/dist/cjs/config/defaults.js +109 -0
  38. package/dist/cjs/config/defaults.js.map +1 -0
  39. package/dist/cjs/core/dom-snapshot.js +280 -0
  40. package/dist/cjs/core/dom-snapshot.js.map +1 -0
  41. package/dist/cjs/core/enterprise-strategy.js +702 -0
  42. package/dist/cjs/core/enterprise-strategy.js.map +1 -0
  43. package/dist/cjs/core/healer.js +283 -0
  44. package/dist/cjs/core/healer.js.map +1 -0
  45. package/dist/cjs/core/interceptor.js +945 -0
  46. package/dist/cjs/core/interceptor.js.map +1 -0
  47. package/dist/cjs/core/locator-analyzer.js +172 -0
  48. package/dist/cjs/core/locator-analyzer.js.map +1 -0
  49. package/dist/cjs/core/locator-strategies.js +891 -0
  50. package/dist/cjs/core/locator-strategies.js.map +1 -0
  51. package/dist/cjs/core/self-heal-cache.js +178 -0
  52. package/dist/cjs/core/self-heal-cache.js.map +1 -0
  53. package/dist/cjs/core/smart-retry.js +248 -0
  54. package/dist/cjs/core/smart-retry.js.map +1 -0
  55. package/dist/cjs/core/visual-verification.js +262 -0
  56. package/dist/cjs/core/visual-verification.js.map +1 -0
  57. package/dist/cjs/git/code-modifier.js +184 -0
  58. package/dist/cjs/git/code-modifier.js.map +1 -0
  59. package/dist/cjs/git/git-operations.js +145 -0
  60. package/dist/cjs/git/git-operations.js.map +1 -0
  61. package/dist/cjs/git/pr-creator.js +190 -0
  62. package/dist/cjs/git/pr-creator.js.map +1 -0
  63. package/dist/cjs/index.js +97 -0
  64. package/dist/cjs/index.js.map +1 -0
  65. package/dist/cjs/rag/context-retriever.js +289 -0
  66. package/dist/cjs/rag/context-retriever.js.map +1 -0
  67. package/dist/cjs/rag/embeddings.js +82 -0
  68. package/dist/cjs/rag/embeddings.js.map +1 -0
  69. package/dist/cjs/rag/knowledge-store.js +159 -0
  70. package/dist/cjs/rag/knowledge-store.js.map +1 -0
  71. package/dist/cjs/reporters/heal-report.js +279 -0
  72. package/dist/cjs/reporters/heal-report.js.map +1 -0
  73. package/dist/cjs/reporters/heal-reporter.js +294 -0
  74. package/dist/cjs/reporters/heal-reporter.js.map +1 -0
  75. package/dist/cjs/server/review-server.js +166 -0
  76. package/dist/cjs/server/review-server.js.map +1 -0
  77. package/dist/cjs/server/routes.js +92 -0
  78. package/dist/cjs/server/routes.js.map +1 -0
  79. package/dist/cjs/utils/environment.js +57 -0
  80. package/dist/cjs/utils/environment.js.map +1 -0
  81. package/dist/cjs/utils/file-lock.js +136 -0
  82. package/dist/cjs/utils/file-lock.js.map +1 -0
  83. package/dist/cjs/utils/file-utils.js +49 -0
  84. package/dist/cjs/utils/file-utils.js.map +1 -0
  85. package/dist/cjs/utils/logger.js +78 -0
  86. package/dist/cjs/utils/logger.js.map +1 -0
  87. package/dist/esm/ai/ai-provider.js +44 -0
  88. package/dist/esm/ai/ai-provider.js.map +1 -0
  89. package/dist/esm/ai/anthropic-provider.js +104 -0
  90. package/dist/esm/ai/anthropic-provider.js.map +1 -0
  91. package/dist/esm/ai/azure-openai-provider.js +128 -0
  92. package/dist/esm/ai/azure-openai-provider.js.map +1 -0
  93. package/dist/esm/ai/bedrock-provider.js +181 -0
  94. package/dist/esm/ai/bedrock-provider.js.map +1 -0
  95. package/dist/esm/ai/deepseek-provider.js +116 -0
  96. package/dist/esm/ai/deepseek-provider.js.map +1 -0
  97. package/dist/esm/ai/gemini-provider.js +127 -0
  98. package/dist/esm/ai/gemini-provider.js.map +1 -0
  99. package/dist/esm/ai/groq-provider.js +116 -0
  100. package/dist/esm/ai/groq-provider.js.map +1 -0
  101. package/dist/esm/ai/meta-provider.js +116 -0
  102. package/dist/esm/ai/meta-provider.js.map +1 -0
  103. package/dist/esm/ai/ollama-provider.js +125 -0
  104. package/dist/esm/ai/ollama-provider.js.map +1 -0
  105. package/dist/esm/ai/openai-provider.js +115 -0
  106. package/dist/esm/ai/openai-provider.js.map +1 -0
  107. package/dist/esm/ai/perplexity-provider.js +116 -0
  108. package/dist/esm/ai/perplexity-provider.js.map +1 -0
  109. package/dist/esm/ai/prompt-templates.js +171 -0
  110. package/dist/esm/ai/prompt-templates.js.map +1 -0
  111. package/dist/esm/ai/qwen-provider.js +116 -0
  112. package/dist/esm/ai/qwen-provider.js.map +1 -0
  113. package/dist/esm/analytics/healing-analytics.js +261 -0
  114. package/dist/esm/analytics/healing-analytics.js.map +1 -0
  115. package/dist/esm/cli/init.js +495 -0
  116. package/dist/esm/cli/init.js.map +1 -0
  117. package/dist/esm/config/config-loader.js +132 -0
  118. package/dist/esm/config/config-loader.js.map +1 -0
  119. package/dist/esm/config/defaults.js +107 -0
  120. package/dist/esm/config/defaults.js.map +1 -0
  121. package/dist/esm/core/dom-snapshot.js +278 -0
  122. package/dist/esm/core/dom-snapshot.js.map +1 -0
  123. package/dist/esm/core/enterprise-strategy.js +695 -0
  124. package/dist/esm/core/enterprise-strategy.js.map +1 -0
  125. package/dist/esm/core/healer.js +281 -0
  126. package/dist/esm/core/healer.js.map +1 -0
  127. package/dist/esm/core/interceptor.js +940 -0
  128. package/dist/esm/core/interceptor.js.map +1 -0
  129. package/dist/esm/core/locator-analyzer.js +169 -0
  130. package/dist/esm/core/locator-analyzer.js.map +1 -0
  131. package/dist/esm/core/locator-strategies.js +882 -0
  132. package/dist/esm/core/locator-strategies.js.map +1 -0
  133. package/dist/esm/core/self-heal-cache.js +176 -0
  134. package/dist/esm/core/self-heal-cache.js.map +1 -0
  135. package/dist/esm/core/smart-retry.js +246 -0
  136. package/dist/esm/core/smart-retry.js.map +1 -0
  137. package/dist/esm/core/visual-verification.js +260 -0
  138. package/dist/esm/core/visual-verification.js.map +1 -0
  139. package/dist/esm/git/code-modifier.js +182 -0
  140. package/dist/esm/git/code-modifier.js.map +1 -0
  141. package/dist/esm/git/git-operations.js +143 -0
  142. package/dist/esm/git/git-operations.js.map +1 -0
  143. package/dist/esm/git/pr-creator.js +188 -0
  144. package/dist/esm/git/pr-creator.js.map +1 -0
  145. package/dist/esm/index.js +37 -0
  146. package/dist/esm/index.js.map +1 -0
  147. package/dist/esm/rag/context-retriever.js +287 -0
  148. package/dist/esm/rag/context-retriever.js.map +1 -0
  149. package/dist/esm/rag/embeddings.js +77 -0
  150. package/dist/esm/rag/embeddings.js.map +1 -0
  151. package/dist/esm/rag/knowledge-store.js +157 -0
  152. package/dist/esm/rag/knowledge-store.js.map +1 -0
  153. package/dist/esm/reporters/heal-report.js +277 -0
  154. package/dist/esm/reporters/heal-report.js.map +1 -0
  155. package/dist/esm/reporters/heal-reporter.js +290 -0
  156. package/dist/esm/reporters/heal-reporter.js.map +1 -0
  157. package/dist/esm/server/review-server.js +164 -0
  158. package/dist/esm/server/review-server.js.map +1 -0
  159. package/dist/esm/server/routes.js +90 -0
  160. package/dist/esm/server/routes.js.map +1 -0
  161. package/dist/esm/utils/environment.js +53 -0
  162. package/dist/esm/utils/environment.js.map +1 -0
  163. package/dist/esm/utils/file-lock.js +134 -0
  164. package/dist/esm/utils/file-lock.js.map +1 -0
  165. package/dist/esm/utils/file-utils.js +43 -0
  166. package/dist/esm/utils/file-utils.js.map +1 -0
  167. package/dist/esm/utils/logger.js +75 -0
  168. package/dist/esm/utils/logger.js.map +1 -0
  169. package/dist/types/ai/ai-provider.d.ts +4 -0
  170. package/dist/types/ai/ai-provider.d.ts.map +1 -0
  171. package/dist/types/ai/anthropic-provider.d.ts +11 -0
  172. package/dist/types/ai/anthropic-provider.d.ts.map +1 -0
  173. package/dist/types/ai/azure-openai-provider.d.ts +13 -0
  174. package/dist/types/ai/azure-openai-provider.d.ts.map +1 -0
  175. package/dist/types/ai/bedrock-provider.d.ts +14 -0
  176. package/dist/types/ai/bedrock-provider.d.ts.map +1 -0
  177. package/dist/types/ai/deepseek-provider.d.ts +12 -0
  178. package/dist/types/ai/deepseek-provider.d.ts.map +1 -0
  179. package/dist/types/ai/gemini-provider.d.ts +12 -0
  180. package/dist/types/ai/gemini-provider.d.ts.map +1 -0
  181. package/dist/types/ai/groq-provider.d.ts +12 -0
  182. package/dist/types/ai/groq-provider.d.ts.map +1 -0
  183. package/dist/types/ai/meta-provider.d.ts +12 -0
  184. package/dist/types/ai/meta-provider.d.ts.map +1 -0
  185. package/dist/types/ai/ollama-provider.d.ts +10 -0
  186. package/dist/types/ai/ollama-provider.d.ts.map +1 -0
  187. package/dist/types/ai/openai-provider.d.ts +11 -0
  188. package/dist/types/ai/openai-provider.d.ts.map +1 -0
  189. package/dist/types/ai/perplexity-provider.d.ts +12 -0
  190. package/dist/types/ai/perplexity-provider.d.ts.map +1 -0
  191. package/dist/types/ai/prompt-templates.d.ts +11 -0
  192. package/dist/types/ai/prompt-templates.d.ts.map +1 -0
  193. package/dist/types/ai/qwen-provider.d.ts +12 -0
  194. package/dist/types/ai/qwen-provider.d.ts.map +1 -0
  195. package/dist/types/analytics/healing-analytics.d.ts +36 -0
  196. package/dist/types/analytics/healing-analytics.d.ts.map +1 -0
  197. package/dist/types/cli/init.d.ts +15 -0
  198. package/dist/types/cli/init.d.ts.map +1 -0
  199. package/dist/types/config/config-loader.d.ts +4 -0
  200. package/dist/types/config/config-loader.d.ts.map +1 -0
  201. package/dist/types/config/defaults.d.ts +3 -0
  202. package/dist/types/config/defaults.d.ts.map +1 -0
  203. package/dist/types/core/dom-snapshot.d.ts +12 -0
  204. package/dist/types/core/dom-snapshot.d.ts.map +1 -0
  205. package/dist/types/core/enterprise-strategy.d.ts +56 -0
  206. package/dist/types/core/enterprise-strategy.d.ts.map +1 -0
  207. package/dist/types/core/healer.d.ts +52 -0
  208. package/dist/types/core/healer.d.ts.map +1 -0
  209. package/dist/types/core/interceptor.d.ts +64 -0
  210. package/dist/types/core/interceptor.d.ts.map +1 -0
  211. package/dist/types/core/locator-analyzer.d.ts +31 -0
  212. package/dist/types/core/locator-analyzer.d.ts.map +1 -0
  213. package/dist/types/core/locator-strategies.d.ts +45 -0
  214. package/dist/types/core/locator-strategies.d.ts.map +1 -0
  215. package/dist/types/core/self-heal-cache.d.ts +51 -0
  216. package/dist/types/core/self-heal-cache.d.ts.map +1 -0
  217. package/dist/types/core/smart-retry.d.ts +64 -0
  218. package/dist/types/core/smart-retry.d.ts.map +1 -0
  219. package/dist/types/core/visual-verification.d.ts +46 -0
  220. package/dist/types/core/visual-verification.d.ts.map +1 -0
  221. package/dist/types/git/code-modifier.d.ts +51 -0
  222. package/dist/types/git/code-modifier.d.ts.map +1 -0
  223. package/dist/types/git/git-operations.d.ts +40 -0
  224. package/dist/types/git/git-operations.d.ts.map +1 -0
  225. package/dist/types/git/pr-creator.d.ts +27 -0
  226. package/dist/types/git/pr-creator.d.ts.map +1 -0
  227. package/dist/types/index.d.ts +40 -0
  228. package/dist/types/index.d.ts.map +1 -0
  229. package/dist/types/rag/context-retriever.d.ts +69 -0
  230. package/dist/types/rag/context-retriever.d.ts.map +1 -0
  231. package/dist/types/rag/embeddings.d.ts +32 -0
  232. package/dist/types/rag/embeddings.d.ts.map +1 -0
  233. package/dist/types/rag/index.d.ts +12 -0
  234. package/dist/types/rag/index.d.ts.map +1 -0
  235. package/dist/types/rag/knowledge-store.d.ts +38 -0
  236. package/dist/types/rag/knowledge-store.d.ts.map +1 -0
  237. package/dist/types/reporters/heal-report.d.ts +29 -0
  238. package/dist/types/reporters/heal-report.d.ts.map +1 -0
  239. package/dist/types/reporters/heal-reporter.d.ts +49 -0
  240. package/dist/types/reporters/heal-reporter.d.ts.map +1 -0
  241. package/dist/types/server/review-server.d.ts +20 -0
  242. package/dist/types/server/review-server.d.ts.map +1 -0
  243. package/dist/types/server/routes.d.ts +4 -0
  244. package/dist/types/server/routes.d.ts.map +1 -0
  245. package/dist/types/types/index.d.ts +433 -0
  246. package/dist/types/types/index.d.ts.map +1 -0
  247. package/dist/types/utils/environment.d.ts +10 -0
  248. package/dist/types/utils/environment.d.ts.map +1 -0
  249. package/dist/types/utils/file-lock.d.ts +37 -0
  250. package/dist/types/utils/file-lock.d.ts.map +1 -0
  251. package/dist/types/utils/file-utils.d.ts +7 -0
  252. package/dist/types/utils/file-utils.d.ts.map +1 -0
  253. package/dist/types/utils/logger.d.ts +9 -0
  254. package/dist/types/utils/logger.d.ts.map +1 -0
  255. package/package.json +106 -0
@@ -0,0 +1,290 @@
1
+ import { getAllHealingSessions } from '../core/interceptor.js';
2
+ import { HealReportGenerator } from './heal-report.js';
3
+ import { GitOperations } from '../git/git-operations.js';
4
+ import { PRCreator } from '../git/pr-creator.js';
5
+ import { CodeModifier } from '../git/code-modifier.js';
6
+ import { ReviewServer } from '../server/review-server.js';
7
+ import { loadConfig } from '../config/config-loader.js';
8
+ import { isCI } from '../utils/environment.js';
9
+ import { logger } from '../utils/logger.js';
10
+
11
+ /**
12
+ * Custom Playwright reporter that hooks into the test lifecycle to collect
13
+ * healing events, generate reports, and optionally create PRs or launch the
14
+ * interactive review server.
15
+ *
16
+ * Usage in `playwright.config.ts`:
17
+ * ```ts
18
+ * reporter: [['mindheal/reporter', { ai: { provider: 'anthropic', apiKey: '...' } }]]
19
+ * ```
20
+ */
21
+ class HealReporter {
22
+ constructor(options = {}) {
23
+ this.startTime = 0;
24
+ this.totalTests = 0;
25
+ this.config = loadConfig(options);
26
+ this.reportGenerator = new HealReportGenerator();
27
+ }
28
+ /**
29
+ * Called once before tests start running.
30
+ */
31
+ onBegin(config, suite) {
32
+ this.startTime = Date.now();
33
+ this.totalTests = suite.allTests().length;
34
+ logger.info(`MindHeal reporter initialized. Running ${this.totalTests} test(s) with healing ${this.config.healing.enabled ? 'enabled' : 'disabled'}.`);
35
+ }
36
+ /**
37
+ * Called after each test finishes. Used to tag healing events with the
38
+ * test title so the report is more informative.
39
+ */
40
+ onTestEnd(test, result) {
41
+ // Healing events are collected globally by the interceptor. We enrich
42
+ // them with the test title here since the interceptor does not have
43
+ // access to the TestCase object.
44
+ const sessions = getAllHealingSessions();
45
+ for (const session of sessions) {
46
+ for (const event of session.events) {
47
+ if (!event.testTitle && event.timestamp >= this.startTime) {
48
+ event.testTitle = test.title;
49
+ event.testFile = event.testFile || test.location.file;
50
+ }
51
+ }
52
+ }
53
+ }
54
+ /**
55
+ * Called once after all tests have completed. Drives report generation,
56
+ * git operations, and the review server workflow.
57
+ */
58
+ async onEnd(result) {
59
+ const endTime = Date.now();
60
+ // Collect all healing events from every session.
61
+ const sessions = getAllHealingSessions();
62
+ const allEvents = [];
63
+ for (const session of sessions) {
64
+ allEvents.push(...session.events);
65
+ }
66
+ if (allEvents.length === 0) {
67
+ logger.info('No healing events recorded during this run.');
68
+ return;
69
+ }
70
+ const successfulHeals = allEvents.filter((e) => e.status === 'healed').length;
71
+ const failedHeals = allEvents.filter((e) => e.status === 'failed').length;
72
+ logger.info(`Healing summary: ${successfulHeals} healed, ${failedHeals} failed out of ${allEvents.length} total events.`);
73
+ // Build the report data structure.
74
+ const report = {
75
+ sessionId: sessions[0]?.id ?? `report_${Date.now().toString(36)}`,
76
+ startTime: this.startTime,
77
+ endTime,
78
+ totalTests: this.totalTests,
79
+ totalHeals: allEvents.length,
80
+ successfulHeals,
81
+ failedHeals,
82
+ events: allEvents,
83
+ config: {
84
+ healing: this.config.healing,
85
+ reporting: this.config.reporting,
86
+ git: { ...this.config.git, token: '***' },
87
+ ai: { ...this.config.ai, apiKey: '***' },
88
+ },
89
+ };
90
+ // Generate reports.
91
+ const outputDir = this.config.reporting.outputDir ?? '.mindheal/reports';
92
+ try {
93
+ await this.reportGenerator.saveReport(report, outputDir, this.config.reporting.generateHTML, this.config.reporting.generateJSON);
94
+ }
95
+ catch (error) {
96
+ const message = error instanceof Error ? error.message : String(error);
97
+ logger.error(`Failed to save report: ${message}`);
98
+ }
99
+ // Only process healed events for code modification.
100
+ const healedEvents = allEvents.filter((e) => e.status === 'healed');
101
+ if (healedEvents.length === 0) {
102
+ logger.info('No successful heals to apply. Done.');
103
+ return;
104
+ }
105
+ // Determine the workflow based on environment.
106
+ if (isCI() && this.config.git.enabled) {
107
+ await this.handleCIWorkflow(healedEvents);
108
+ }
109
+ else if (!isCI() && this.config.reviewServer.enabled) {
110
+ await this.handleLocalWorkflow(healedEvents);
111
+ }
112
+ else {
113
+ // Fallback: apply approved modifications directly (auto-approve all).
114
+ logger.info('Neither CI/git nor review server enabled. Auto-approving all healed locators.');
115
+ for (const event of healedEvents) {
116
+ event.reviewStatus = 'approved';
117
+ }
118
+ await this.applyApprovedModifications(healedEvents);
119
+ }
120
+ logger.info('MindHeal reporter finished.');
121
+ }
122
+ // ── CI/CD workflow: branch + commit + PR ─────────────────────────────────────
123
+ async handleCIWorkflow(healedEvents) {
124
+ logger.info('CI environment detected with git enabled. Creating branch and PR...');
125
+ const gitOps = new GitOperations(this.config.git);
126
+ const prCreator = new PRCreator(this.config.git);
127
+ const codeModifier = new CodeModifier();
128
+ let originalBranch;
129
+ try {
130
+ // Remember the current branch so we can switch back on failure.
131
+ originalBranch = await gitOps.getCurrentBranch();
132
+ // Create a dedicated branch for the fixes.
133
+ const branchName = gitOps.generateBranchName();
134
+ await gitOps.createBranch(branchName);
135
+ // Auto-approve all events in CI mode (no interactive review).
136
+ for (const event of healedEvents) {
137
+ event.reviewStatus = 'approved';
138
+ }
139
+ // Apply code modifications.
140
+ const modifications = this.buildModifications(healedEvents);
141
+ if (modifications.length === 0) {
142
+ logger.warn('No source locations available for code modifications. Skipping PR.');
143
+ await gitOps.switchBranch(originalBranch);
144
+ return;
145
+ }
146
+ await codeModifier.applyAllModifications(modifications);
147
+ // Commit and push.
148
+ const modifiedFiles = [...new Set(modifications.map((m) => m.filePath))];
149
+ await gitOps.commitChanges(modifiedFiles, `Fix ${modifications.length} broken locator(s) via auto-healing`);
150
+ await gitOps.pushBranch(branchName);
151
+ // Create PR if configured.
152
+ if (this.config.git.autoCreatePR && this.config.git.token) {
153
+ const prBody = this.buildPRBody(modifications);
154
+ const prResult = await prCreator.createPR({
155
+ title: `[MindHeal] Fix ${modifications.length} broken locator(s)`,
156
+ body: prBody,
157
+ sourceBranch: branchName,
158
+ targetBranch: this.config.git.baseBranch ?? 'main',
159
+ labels: this.config.git.prLabels ?? ['auto-heal', 'mindheal'],
160
+ reviewers: this.config.git.prReviewers ?? [],
161
+ });
162
+ logger.info(`Pull request created: ${prResult.url}`);
163
+ }
164
+ // Switch back to the original branch.
165
+ await gitOps.switchBranch(originalBranch);
166
+ }
167
+ catch (error) {
168
+ const message = error instanceof Error ? error.message : String(error);
169
+ logger.error(`CI workflow failed: ${message}`);
170
+ // Best-effort: switch back to the original branch.
171
+ if (originalBranch) {
172
+ try {
173
+ await gitOps.switchBranch(originalBranch);
174
+ }
175
+ catch {
176
+ logger.warn('Failed to switch back to the original branch after CI workflow error.');
177
+ }
178
+ }
179
+ }
180
+ }
181
+ // ── Local workflow: review server ────────────────────────────────────────────
182
+ async handleLocalWorkflow(healedEvents) {
183
+ logger.info('Local environment detected with review server enabled. Starting review server...');
184
+ const reviewServerConfig = typeof this.config.reviewServer.enabled === 'boolean'
185
+ ? this.config.reviewServer
186
+ : { ...this.config.reviewServer, enabled: true };
187
+ const server = new ReviewServer(reviewServerConfig, healedEvents);
188
+ try {
189
+ await server.start();
190
+ logger.info(`Review server running on port ${this.config.reviewServer.port}. Waiting for reviews...`);
191
+ // Block until all events have been reviewed.
192
+ const reviewedEvents = await server.waitForReview();
193
+ // Stop the server if it hasn't auto-closed.
194
+ await server.stop();
195
+ // Apply only approved modifications.
196
+ const approved = reviewedEvents.filter((e) => e.reviewStatus === 'approved');
197
+ if (approved.length > 0) {
198
+ logger.info(`Applying ${approved.length} approved modification(s)...`);
199
+ await this.applyApprovedModifications(approved);
200
+ }
201
+ else {
202
+ logger.info('No modifications were approved.');
203
+ }
204
+ }
205
+ catch (error) {
206
+ const message = error instanceof Error ? error.message : String(error);
207
+ logger.error(`Local review workflow failed: ${message}`);
208
+ // Best-effort: stop the server.
209
+ try {
210
+ await server.stop();
211
+ }
212
+ catch {
213
+ // Ignore stop errors during cleanup.
214
+ }
215
+ }
216
+ }
217
+ // ── Shared helpers ───────────────────────────────────────────────────────────
218
+ /**
219
+ * Apply approved code modifications using the AST-based CodeModifier.
220
+ */
221
+ async applyApprovedModifications(approvedEvents) {
222
+ const modifications = this.buildModifications(approvedEvents);
223
+ if (modifications.length === 0) {
224
+ logger.warn('No source locations available for approved events. Cannot apply modifications.');
225
+ return;
226
+ }
227
+ const codeModifier = new CodeModifier();
228
+ try {
229
+ await codeModifier.applyAllModifications(modifications);
230
+ logger.info(`Successfully applied ${modifications.length} code modification(s).`);
231
+ }
232
+ catch (error) {
233
+ const message = error instanceof Error ? error.message : String(error);
234
+ logger.error(`Failed to apply code modifications: ${message}`);
235
+ }
236
+ }
237
+ /**
238
+ * Convert healing events into CodeModification objects for the CodeModifier.
239
+ * Events without source locations or healed locators are skipped.
240
+ */
241
+ buildModifications(events) {
242
+ const modifications = [];
243
+ for (const event of events) {
244
+ if (!event.sourceLocation || !event.healedLocator) {
245
+ logger.debug(`Skipping event ${event.id}: missing source location or healed locator.`);
246
+ continue;
247
+ }
248
+ modifications.push({
249
+ filePath: event.sourceLocation.filePath,
250
+ line: event.sourceLocation.line,
251
+ column: event.sourceLocation.column,
252
+ originalCode: event.originalLocator.playwrightExpression,
253
+ modifiedCode: event.healedLocator.playwrightExpression,
254
+ healingEvent: event,
255
+ });
256
+ }
257
+ return modifications;
258
+ }
259
+ /**
260
+ * Build a markdown PR description summarizing all modifications.
261
+ */
262
+ buildPRBody(modifications) {
263
+ const rows = modifications
264
+ .map((mod) => `| \`${mod.filePath}:${mod.line}\` | \`${mod.originalCode}\` | \`${mod.modifiedCode}\` | ${Math.round(mod.healingEvent.confidence * 100)}% |`)
265
+ .join('\n');
266
+ return [
267
+ '### Summary',
268
+ '',
269
+ `This PR contains **${modifications.length}** auto-healed locator fix(es) generated by MindHeal.`,
270
+ '',
271
+ '### Changes',
272
+ '',
273
+ '| File | Original Locator | Healed Locator | Confidence |',
274
+ '|------|-----------------|----------------|------------|',
275
+ rows,
276
+ '',
277
+ '### How it works',
278
+ '',
279
+ 'MindHeal intercepts failing Playwright locators at runtime, analyses the DOM,',
280
+ 'and suggests replacement selectors using configurable strategies (attribute,',
281
+ 'text, role, CSS, XPath, AI). Only changes that meet the confidence threshold',
282
+ 'are included in this PR.',
283
+ '',
284
+ 'Please review each change carefully before merging.',
285
+ ].join('\n');
286
+ }
287
+ }
288
+
289
+ export { HealReporter as default };
290
+ //# sourceMappingURL=heal-reporter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"heal-reporter.js","sources":["../../../../src/reporters/heal-reporter.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}
@@ -0,0 +1,164 @@
1
+ import express from 'express';
2
+ import { WebSocketServer } from 'ws';
3
+ import open from 'open';
4
+ import { createServer } from 'http';
5
+ import { readFileSync } from 'fs';
6
+ import { resolve, dirname } from 'path';
7
+ import { createRoutes } from './routes.js';
8
+ import { logger } from '../utils/logger.js';
9
+
10
+ class ReviewServer {
11
+ constructor(config, events) {
12
+ this.httpServer = null;
13
+ this.wss = null;
14
+ this.reviewResolve = null;
15
+ this.config = config;
16
+ this.events = events;
17
+ this.app = express();
18
+ this.setupApp();
19
+ }
20
+ // ─── Public API ──────────────────────────────────────────────────────────────
21
+ async start() {
22
+ return new Promise((resolveStart, rejectStart) => {
23
+ this.httpServer = createServer(this.app);
24
+ this.wss = new WebSocketServer({ server: this.httpServer });
25
+ this.wss.on('connection', (ws) => {
26
+ logger.debug('WebSocket client connected');
27
+ ws.send(JSON.stringify({
28
+ type: 'init',
29
+ events: this.events,
30
+ summary: this.buildSummary(),
31
+ }));
32
+ ws.on('close', () => {
33
+ logger.debug('WebSocket client disconnected');
34
+ });
35
+ });
36
+ this.httpServer.listen(this.config.port, () => {
37
+ const url = `http://localhost:${this.config.port}`;
38
+ logger.info(`Review server started at ${url}`);
39
+ if (this.config.openBrowser) {
40
+ open(url).catch((err) => {
41
+ logger.warn('Failed to open browser', err);
42
+ });
43
+ }
44
+ resolveStart();
45
+ });
46
+ this.httpServer.on('error', (err) => {
47
+ logger.error('Review server failed to start', err);
48
+ rejectStart(err);
49
+ });
50
+ });
51
+ }
52
+ async stop() {
53
+ return new Promise((resolveStop) => {
54
+ logger.info('Shutting down review server...');
55
+ if (this.wss) {
56
+ for (const client of this.wss.clients) {
57
+ client.close(1000, 'Server shutting down');
58
+ }
59
+ this.wss.close();
60
+ this.wss = null;
61
+ }
62
+ if (this.httpServer) {
63
+ this.httpServer.close(() => {
64
+ this.httpServer = null;
65
+ logger.info('Review server stopped');
66
+ resolveStop();
67
+ });
68
+ }
69
+ else {
70
+ resolveStop();
71
+ }
72
+ });
73
+ }
74
+ async waitForReview() {
75
+ if (this.allReviewed()) {
76
+ return this.events;
77
+ }
78
+ return new Promise((resolve) => {
79
+ this.reviewResolve = resolve;
80
+ });
81
+ }
82
+ // ─── Internals ───────────────────────────────────────────────────────────────
83
+ setupApp() {
84
+ const router = createRoutes(this.events, (event) => this.onEventUpdated(event));
85
+ this.app.use(router);
86
+ // Serve the dashboard UI
87
+ this.app.get('/', (_req, res) => {
88
+ try {
89
+ const htmlPath = resolve(dirname(__filename), 'ui', 'index.html');
90
+ let html = readFileSync(htmlPath, 'utf-8');
91
+ // Inject the port so the client-side JS can connect to WebSocket
92
+ html = html.replace('{{WS_PORT}}', String(this.config.port));
93
+ html = html.replace('{{AUTO_CLOSE}}', String(this.config.autoCloseAfterReview));
94
+ res.type('html').send(html);
95
+ }
96
+ catch (err) {
97
+ logger.error('Failed to serve dashboard UI', err);
98
+ res.status(500).send('Failed to load dashboard');
99
+ }
100
+ });
101
+ }
102
+ onEventUpdated(event) {
103
+ logger.info(`Event ${event.id} review status changed to ${event.reviewStatus}`);
104
+ this.broadcastUpdate(event);
105
+ if (this.allReviewed()) {
106
+ logger.info('All events have been reviewed');
107
+ this.broadcastAllReviewed();
108
+ if (this.reviewResolve) {
109
+ const resolve = this.reviewResolve;
110
+ this.reviewResolve = null;
111
+ if (this.config.autoCloseAfterReview) {
112
+ // Give the client a moment to show the completion UI
113
+ setTimeout(() => {
114
+ this.stop().then(() => resolve(this.events));
115
+ }, 3000);
116
+ }
117
+ else {
118
+ resolve(this.events);
119
+ }
120
+ }
121
+ }
122
+ }
123
+ broadcastUpdate(event) {
124
+ if (!this.wss)
125
+ return;
126
+ const message = JSON.stringify({
127
+ type: 'update',
128
+ event,
129
+ summary: this.buildSummary(),
130
+ });
131
+ for (const client of this.wss.clients) {
132
+ if (client.readyState === 1 /* WebSocket.OPEN */) {
133
+ client.send(message);
134
+ }
135
+ }
136
+ }
137
+ broadcastAllReviewed() {
138
+ if (!this.wss)
139
+ return;
140
+ const message = JSON.stringify({
141
+ type: 'all-reviewed',
142
+ summary: this.buildSummary(),
143
+ });
144
+ for (const client of this.wss.clients) {
145
+ if (client.readyState === 1) {
146
+ client.send(message);
147
+ }
148
+ }
149
+ }
150
+ allReviewed() {
151
+ return this.events.every((e) => e.reviewStatus !== 'pending');
152
+ }
153
+ buildSummary() {
154
+ return {
155
+ total: this.events.length,
156
+ approved: this.events.filter((e) => e.reviewStatus === 'approved').length,
157
+ rejected: this.events.filter((e) => e.reviewStatus === 'rejected').length,
158
+ pending: this.events.filter((e) => e.reviewStatus === 'pending').length,
159
+ };
160
+ }
161
+ }
162
+
163
+ export { ReviewServer };
164
+ //# sourceMappingURL=review-server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review-server.js","sources":["../../../../src/server/review-server.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}
@@ -0,0 +1,90 @@
1
+ import express from 'express';
2
+
3
+ function createRoutes(events, onUpdate) {
4
+ const router = express.Router();
5
+ router.use(express.json());
6
+ // ─── GET /api/heals ──────────────────────────────────────────────────────────
7
+ router.get('/api/heals', (_req, res) => {
8
+ res.json(events);
9
+ });
10
+ // ─── GET /api/heals/:id ──────────────────────────────────────────────────────
11
+ router.get('/api/heals/:id', (req, res) => {
12
+ const event = events.find((e) => e.id === req.params.id);
13
+ if (!event) {
14
+ res.status(404).json({ error: 'Healing event not found' });
15
+ return;
16
+ }
17
+ res.json(event);
18
+ });
19
+ // ─── POST /api/heals/:id/approve ─────────────────────────────────────────────
20
+ router.post('/api/heals/:id/approve', (req, res) => {
21
+ const event = events.find((e) => e.id === req.params.id);
22
+ if (!event) {
23
+ res.status(404).json({ error: 'Healing event not found' });
24
+ return;
25
+ }
26
+ event.reviewStatus = 'approved';
27
+ const { editedLocator } = req.body;
28
+ if (editedLocator && event.healedLocator) {
29
+ event.healedLocator = {
30
+ ...event.healedLocator,
31
+ selector: editedLocator,
32
+ playwrightExpression: editedLocator,
33
+ };
34
+ }
35
+ onUpdate(event);
36
+ res.json(event);
37
+ });
38
+ // ─── POST /api/heals/:id/reject ──────────────────────────────────────────────
39
+ router.post('/api/heals/:id/reject', (req, res) => {
40
+ const event = events.find((e) => e.id === req.params.id);
41
+ if (!event) {
42
+ res.status(404).json({ error: 'Healing event not found' });
43
+ return;
44
+ }
45
+ event.reviewStatus = 'rejected';
46
+ onUpdate(event);
47
+ res.json(event);
48
+ });
49
+ // ─── POST /api/approve-all ────────────────────────────────────────────────────
50
+ router.post('/api/approve-all', (_req, res) => {
51
+ const pending = events.filter((e) => e.reviewStatus === 'pending');
52
+ for (const event of pending) {
53
+ event.reviewStatus = 'approved';
54
+ onUpdate(event);
55
+ }
56
+ res.json({ updated: pending.length });
57
+ });
58
+ // ─── POST /api/reject-all ────────────────────────────────────────────────────
59
+ router.post('/api/reject-all', (_req, res) => {
60
+ const pending = events.filter((e) => e.reviewStatus === 'pending');
61
+ for (const event of pending) {
62
+ event.reviewStatus = 'rejected';
63
+ onUpdate(event);
64
+ }
65
+ res.json({ updated: pending.length });
66
+ });
67
+ // ─── POST /api/approve-high-confidence ────────────────────────────────────────
68
+ router.post('/api/approve-high-confidence', (_req, res) => {
69
+ const highConfidence = events.filter((e) => e.reviewStatus === 'pending' && e.confidence > 0.9);
70
+ for (const event of highConfidence) {
71
+ event.reviewStatus = 'approved';
72
+ onUpdate(event);
73
+ }
74
+ res.json({ updated: highConfidence.length });
75
+ });
76
+ // ─── GET /api/summary ─────────────────────────────────────────────────────────
77
+ router.get('/api/summary', (_req, res) => {
78
+ const summary = {
79
+ total: events.length,
80
+ approved: events.filter((e) => e.reviewStatus === 'approved').length,
81
+ rejected: events.filter((e) => e.reviewStatus === 'rejected').length,
82
+ pending: events.filter((e) => e.reviewStatus === 'pending').length,
83
+ };
84
+ res.json(summary);
85
+ });
86
+ return router;
87
+ }
88
+
89
+ export { createRoutes };
90
+ //# sourceMappingURL=routes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes.js","sources":["../../../../src/server/routes.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ import { execSync } from 'child_process';
2
+ import { logger } from './logger.js';
3
+
4
+ function isCI() {
5
+ return !!(process.env.CI ||
6
+ process.env.CONTINUOUS_INTEGRATION ||
7
+ process.env.GITHUB_ACTIONS ||
8
+ process.env.GITLAB_CI ||
9
+ process.env.BITBUCKET_PIPELINES ||
10
+ process.env.JENKINS_URL ||
11
+ process.env.CIRCLECI ||
12
+ process.env.TRAVIS ||
13
+ process.env.BUILD_NUMBER);
14
+ }
15
+ function detectGitProvider() {
16
+ try {
17
+ const remoteUrl = execSync('git remote get-url origin', { encoding: 'utf-8' }).trim();
18
+ if (remoteUrl.includes('github.com'))
19
+ return 'github';
20
+ if (remoteUrl.includes('gitlab.com') || remoteUrl.includes('gitlab'))
21
+ return 'gitlab';
22
+ if (remoteUrl.includes('bitbucket.org') || remoteUrl.includes('bitbucket'))
23
+ return 'bitbucket';
24
+ return 'unknown';
25
+ }
26
+ catch {
27
+ logger.debug('Could not detect git provider from remote URL');
28
+ return 'unknown';
29
+ }
30
+ }
31
+ function getRepoInfo() {
32
+ try {
33
+ const remoteUrl = execSync('git remote get-url origin', { encoding: 'utf-8' }).trim();
34
+ // Handle SSH: git@github.com:owner/repo.git
35
+ const sshMatch = remoteUrl.match(/[:\/]([^/]+)\/([^/]+?)(?:\.git)?$/);
36
+ if (sshMatch) {
37
+ return { owner: sshMatch[1], name: sshMatch[2] };
38
+ }
39
+ // Handle HTTPS: https://github.com/owner/repo.git
40
+ const httpsMatch = remoteUrl.match(/\/([^/]+)\/([^/]+?)(?:\.git)?$/);
41
+ if (httpsMatch) {
42
+ return { owner: httpsMatch[1], name: httpsMatch[2] };
43
+ }
44
+ return null;
45
+ }
46
+ catch {
47
+ logger.debug('Could not detect repo info from remote URL');
48
+ return null;
49
+ }
50
+ }
51
+
52
+ export { detectGitProvider, getRepoInfo, isCI };
53
+ //# sourceMappingURL=environment.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"environment.js","sources":["../../../../src/utils/environment.ts"],"sourcesContent":[null],"names":[],"mappings":";;;SAGgB,IAAI,GAAA;AAClB,IAAA,OAAO,CAAC,EACN,OAAO,CAAC,GAAG,CAAC,EAAE;QACd,OAAO,CAAC,GAAG,CAAC,sBAAsB;QAClC,OAAO,CAAC,GAAG,CAAC,cAAc;QAC1B,OAAO,CAAC,GAAG,CAAC,SAAS;QACrB,OAAO,CAAC,GAAG,CAAC,mBAAmB;QAC/B,OAAO,CAAC,GAAG,CAAC,WAAW;QACvB,OAAO,CAAC,GAAG,CAAC,QAAQ;QACpB,OAAO,CAAC,GAAG,CAAC,MAAM;AAClB,QAAA,OAAO,CAAC,GAAG,CAAC,YAAY,CACzB;AACH;SAIgB,iBAAiB,GAAA;AAC/B,IAAA,IAAI;AACF,QAAA,MAAM,SAAS,GAAG,QAAQ,CAAC,2BAA2B,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC,IAAI,EAAE;AAErF,QAAA,IAAI,SAAS,CAAC,QAAQ,CAAC,YAAY,CAAC;AAAE,YAAA,OAAO,QAAQ;AACrD,QAAA,IAAI,SAAS,CAAC,QAAQ,CAAC,YAAY,CAAC,IAAI,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC;AAAE,YAAA,OAAO,QAAQ;AACrF,QAAA,IAAI,SAAS,CAAC,QAAQ,CAAC,eAAe,CAAC,IAAI,SAAS,CAAC,QAAQ,CAAC,WAAW,CAAC;AAAE,YAAA,OAAO,WAAW;AAE9F,QAAA,OAAO,SAAS;IAClB;AAAE,IAAA,MAAM;AACN,QAAA,MAAM,CAAC,KAAK,CAAC,+CAA+C,CAAC;AAC7D,QAAA,OAAO,SAAS;IAClB;AACF;SAEgB,WAAW,GAAA;AACzB,IAAA,IAAI;AACF,QAAA,MAAM,SAAS,GAAG,QAAQ,CAAC,2BAA2B,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC,IAAI,EAAE;;QAGrF,MAAM,QAAQ,GAAG,SAAS,CAAC,KAAK,CAAC,mCAAmC,CAAC;QACrE,IAAI,QAAQ,EAAE;AACZ,YAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE;QAClD;;QAGA,MAAM,UAAU,GAAG,SAAS,CAAC,KAAK,CAAC,gCAAgC,CAAC;QACpE,IAAI,UAAU,EAAE;AACd,YAAA,OAAO,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE;QACtD;AAEA,QAAA,OAAO,IAAI;IACb;AAAE,IAAA,MAAM;AACN,QAAA,MAAM,CAAC,KAAK,CAAC,4CAA4C,CAAC;AAC1D,QAAA,OAAO,IAAI;IACb;AACF;;;;"}