@devran-ai/kit 4.1.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 (231) hide show
  1. package/.agent/CheatSheet.md +350 -0
  2. package/.agent/README.md +76 -0
  3. package/.agent/agents/README.md +155 -0
  4. package/.agent/agents/architect.md +185 -0
  5. package/.agent/agents/backend-specialist.md +276 -0
  6. package/.agent/agents/build-error-resolver.md +207 -0
  7. package/.agent/agents/code-reviewer.md +162 -0
  8. package/.agent/agents/database-architect.md +138 -0
  9. package/.agent/agents/devops-engineer.md +144 -0
  10. package/.agent/agents/doc-updater.md +229 -0
  11. package/.agent/agents/e2e-runner.md +145 -0
  12. package/.agent/agents/explorer-agent.md +143 -0
  13. package/.agent/agents/frontend-specialist.md +144 -0
  14. package/.agent/agents/go-reviewer.md +128 -0
  15. package/.agent/agents/knowledge-agent.md +197 -0
  16. package/.agent/agents/mobile-developer.md +150 -0
  17. package/.agent/agents/performance-optimizer.md +175 -0
  18. package/.agent/agents/planner.md +133 -0
  19. package/.agent/agents/pr-reviewer.md +148 -0
  20. package/.agent/agents/python-reviewer.md +123 -0
  21. package/.agent/agents/refactor-cleaner.md +201 -0
  22. package/.agent/agents/reliability-engineer.md +156 -0
  23. package/.agent/agents/security-reviewer.md +141 -0
  24. package/.agent/agents/sprint-orchestrator.md +124 -0
  25. package/.agent/agents/tdd-guide.md +179 -0
  26. package/.agent/agents/typescript-reviewer.md +110 -0
  27. package/.agent/checklists/README.md +102 -0
  28. package/.agent/checklists/pre-commit.md +93 -0
  29. package/.agent/checklists/session-end.md +99 -0
  30. package/.agent/checklists/session-start.md +102 -0
  31. package/.agent/checklists/task-complete.md +81 -0
  32. package/.agent/commands/README.md +130 -0
  33. package/.agent/commands/adr.md +29 -0
  34. package/.agent/commands/ask.md +28 -0
  35. package/.agent/commands/build.md +30 -0
  36. package/.agent/commands/changelog.md +40 -0
  37. package/.agent/commands/checkpoint.md +28 -0
  38. package/.agent/commands/code-review.md +65 -0
  39. package/.agent/commands/compact.md +28 -0
  40. package/.agent/commands/cook.md +30 -0
  41. package/.agent/commands/db.md +30 -0
  42. package/.agent/commands/debug.md +31 -0
  43. package/.agent/commands/deploy.md +37 -0
  44. package/.agent/commands/design.md +29 -0
  45. package/.agent/commands/doc.md +30 -0
  46. package/.agent/commands/eval.md +30 -0
  47. package/.agent/commands/fix.md +32 -0
  48. package/.agent/commands/git.md +32 -0
  49. package/.agent/commands/help.md +273 -0
  50. package/.agent/commands/implement.md +30 -0
  51. package/.agent/commands/integrate.md +32 -0
  52. package/.agent/commands/learn.md +29 -0
  53. package/.agent/commands/perf.md +31 -0
  54. package/.agent/commands/plan.md +56 -0
  55. package/.agent/commands/pr-describe.md +65 -0
  56. package/.agent/commands/pr-fix.md +45 -0
  57. package/.agent/commands/pr-merge.md +45 -0
  58. package/.agent/commands/pr-review.md +50 -0
  59. package/.agent/commands/pr-split.md +54 -0
  60. package/.agent/commands/pr-status.md +56 -0
  61. package/.agent/commands/pr.md +58 -0
  62. package/.agent/commands/refactor.md +32 -0
  63. package/.agent/commands/research.md +28 -0
  64. package/.agent/commands/scout.md +30 -0
  65. package/.agent/commands/security-scan.md +33 -0
  66. package/.agent/commands/setup.md +31 -0
  67. package/.agent/commands/status.md +59 -0
  68. package/.agent/commands/tdd.md +73 -0
  69. package/.agent/commands/verify.md +58 -0
  70. package/.agent/contexts/brainstorm.md +26 -0
  71. package/.agent/contexts/debug.md +28 -0
  72. package/.agent/contexts/implement.md +29 -0
  73. package/.agent/contexts/plan-quality-log.md +30 -0
  74. package/.agent/contexts/review.md +27 -0
  75. package/.agent/contexts/ship.md +28 -0
  76. package/.agent/decisions/001-trust-grade-governance.md +46 -0
  77. package/.agent/decisions/002-cross-ide-generation.md +15 -0
  78. package/.agent/engine/identity.json +4 -0
  79. package/.agent/engine/loading-rules.json +193 -0
  80. package/.agent/engine/marketplace-index.json +29 -0
  81. package/.agent/engine/mcp-servers/filesystem.json +9 -0
  82. package/.agent/engine/mcp-servers/github.json +11 -0
  83. package/.agent/engine/mcp-servers/postgres.json +11 -0
  84. package/.agent/engine/mcp-servers/supabase.json +11 -0
  85. package/.agent/engine/mcp-servers/vercel.json +11 -0
  86. package/.agent/engine/reliability-config.json +14 -0
  87. package/.agent/engine/sdlc-map.json +50 -0
  88. package/.agent/engine/workflow-state.json +167 -0
  89. package/.agent/hooks/README.md +101 -0
  90. package/.agent/hooks/hooks.json +104 -0
  91. package/.agent/hooks/templates/session-end.md +110 -0
  92. package/.agent/hooks/templates/session-start.md +95 -0
  93. package/.agent/manifest.json +466 -0
  94. package/.agent/rules/agent-upgrade-policy.md +56 -0
  95. package/.agent/rules/architecture.md +111 -0
  96. package/.agent/rules/coding-style.md +75 -0
  97. package/.agent/rules/documentation.md +74 -0
  98. package/.agent/rules/git-workflow.md +140 -0
  99. package/.agent/rules/quality-gate.md +117 -0
  100. package/.agent/rules/security.md +67 -0
  101. package/.agent/rules/sprint-tracking.md +103 -0
  102. package/.agent/rules/testing.md +80 -0
  103. package/.agent/rules/workflow-standards.md +30 -0
  104. package/.agent/rules.md +293 -0
  105. package/.agent/session-context.md +69 -0
  106. package/.agent/session-state.json +27 -0
  107. package/.agent/skills/README.md +135 -0
  108. package/.agent/skills/api-patterns/SKILL.md +117 -0
  109. package/.agent/skills/app-builder/SKILL.md +202 -0
  110. package/.agent/skills/architecture/SKILL.md +101 -0
  111. package/.agent/skills/behavioral-modes/SKILL.md +295 -0
  112. package/.agent/skills/brainstorming/SKILL.md +156 -0
  113. package/.agent/skills/clean-code/SKILL.md +142 -0
  114. package/.agent/skills/context-budget/SKILL.md +78 -0
  115. package/.agent/skills/continuous-learning/SKILL.md +145 -0
  116. package/.agent/skills/database-design/SKILL.md +303 -0
  117. package/.agent/skills/debugging-strategies/SKILL.md +158 -0
  118. package/.agent/skills/deployment-procedures/SKILL.md +191 -0
  119. package/.agent/skills/docker-patterns/SKILL.md +161 -0
  120. package/.agent/skills/eval-harness/SKILL.md +89 -0
  121. package/.agent/skills/frontend-patterns/SKILL.md +141 -0
  122. package/.agent/skills/git-workflow/SKILL.md +159 -0
  123. package/.agent/skills/i18n-localization/SKILL.md +191 -0
  124. package/.agent/skills/intelligent-routing/SKILL.md +180 -0
  125. package/.agent/skills/mcp-integration/SKILL.md +240 -0
  126. package/.agent/skills/mobile-design/SKILL.md +191 -0
  127. package/.agent/skills/nodejs-patterns/SKILL.md +164 -0
  128. package/.agent/skills/parallel-agents/SKILL.md +200 -0
  129. package/.agent/skills/performance-profiling/SKILL.md +134 -0
  130. package/.agent/skills/plan-validation/SKILL.md +192 -0
  131. package/.agent/skills/plan-writing/SKILL.md +183 -0
  132. package/.agent/skills/plan-writing/domain-enhancers.md +184 -0
  133. package/.agent/skills/plan-writing/plan-retrospective.md +116 -0
  134. package/.agent/skills/plan-writing/plan-schema.md +119 -0
  135. package/.agent/skills/pr-toolkit/SKILL.md +174 -0
  136. package/.agent/skills/production-readiness/SKILL.md +126 -0
  137. package/.agent/skills/security-practices/SKILL.md +109 -0
  138. package/.agent/skills/shell-conventions/SKILL.md +92 -0
  139. package/.agent/skills/strategic-compact/SKILL.md +62 -0
  140. package/.agent/skills/testing-patterns/SKILL.md +141 -0
  141. package/.agent/skills/typescript-expert/SKILL.md +160 -0
  142. package/.agent/skills/ui-ux-pro-max/SKILL.md +137 -0
  143. package/.agent/skills/ui-ux-pro-max/data/charts.csv +26 -0
  144. package/.agent/skills/ui-ux-pro-max/data/colors.csv +97 -0
  145. package/.agent/skills/ui-ux-pro-max/data/icons.csv +101 -0
  146. package/.agent/skills/ui-ux-pro-max/data/landing.csv +31 -0
  147. package/.agent/skills/ui-ux-pro-max/data/products.csv +97 -0
  148. package/.agent/skills/ui-ux-pro-max/data/react-performance.csv +45 -0
  149. package/.agent/skills/ui-ux-pro-max/data/stacks/astro.csv +54 -0
  150. package/.agent/skills/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
  151. package/.agent/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
  152. package/.agent/skills/ui-ux-pro-max/data/stacks/jetpack-compose.csv +53 -0
  153. package/.agent/skills/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
  154. package/.agent/skills/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
  155. package/.agent/skills/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
  156. package/.agent/skills/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
  157. package/.agent/skills/ui-ux-pro-max/data/stacks/react.csv +54 -0
  158. package/.agent/skills/ui-ux-pro-max/data/stacks/shadcn.csv +61 -0
  159. package/.agent/skills/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
  160. package/.agent/skills/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
  161. package/.agent/skills/ui-ux-pro-max/data/stacks/vue.csv +50 -0
  162. package/.agent/skills/ui-ux-pro-max/data/styles.csv +68 -0
  163. package/.agent/skills/ui-ux-pro-max/data/typography.csv +58 -0
  164. package/.agent/skills/ui-ux-pro-max/data/ui-reasoning.csv +101 -0
  165. package/.agent/skills/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
  166. package/.agent/skills/ui-ux-pro-max/data/web-interface.csv +31 -0
  167. package/.agent/skills/ui-ux-pro-max/scripts/core.py +253 -0
  168. package/.agent/skills/ui-ux-pro-max/scripts/design_system.py +1067 -0
  169. package/.agent/skills/ui-ux-pro-max/scripts/search.py +114 -0
  170. package/.agent/skills/verification-loop/SKILL.md +89 -0
  171. package/.agent/skills/webapp-testing/SKILL.md +175 -0
  172. package/.agent/templates/adr-template.md +32 -0
  173. package/.agent/templates/bug-report.md +37 -0
  174. package/.agent/templates/feature-request.md +32 -0
  175. package/.agent/workflows/README.md +101 -0
  176. package/.agent/workflows/brainstorm.md +86 -0
  177. package/.agent/workflows/create.md +85 -0
  178. package/.agent/workflows/debug.md +83 -0
  179. package/.agent/workflows/deploy.md +114 -0
  180. package/.agent/workflows/enhance.md +85 -0
  181. package/.agent/workflows/orchestrate.md +106 -0
  182. package/.agent/workflows/plan.md +105 -0
  183. package/.agent/workflows/pr-fix.md +163 -0
  184. package/.agent/workflows/pr-merge.md +117 -0
  185. package/.agent/workflows/pr-review.md +178 -0
  186. package/.agent/workflows/pr-split.md +118 -0
  187. package/.agent/workflows/pr.md +184 -0
  188. package/.agent/workflows/preflight.md +107 -0
  189. package/.agent/workflows/preview.md +95 -0
  190. package/.agent/workflows/quality-gate.md +103 -0
  191. package/.agent/workflows/retrospective.md +100 -0
  192. package/.agent/workflows/review.md +104 -0
  193. package/.agent/workflows/status.md +89 -0
  194. package/.agent/workflows/test.md +98 -0
  195. package/.agent/workflows/ui-ux-pro-max.md +93 -0
  196. package/.agent/workflows/upgrade.md +97 -0
  197. package/LICENSE +21 -0
  198. package/README.md +218 -0
  199. package/bin/kit.js +773 -0
  200. package/lib/agent-registry.js +228 -0
  201. package/lib/agent-reputation.js +343 -0
  202. package/lib/circuit-breaker.js +195 -0
  203. package/lib/cli-commands.js +322 -0
  204. package/lib/config-validator.js +274 -0
  205. package/lib/conflict-detector.js +252 -0
  206. package/lib/constants.js +47 -0
  207. package/lib/engineering-manager.js +336 -0
  208. package/lib/error-budget.js +370 -0
  209. package/lib/hook-system.js +256 -0
  210. package/lib/ide-generator.js +434 -0
  211. package/lib/identity.js +240 -0
  212. package/lib/io.js +146 -0
  213. package/lib/learning-engine.js +163 -0
  214. package/lib/loading-engine.js +421 -0
  215. package/lib/logger.js +118 -0
  216. package/lib/marketplace.js +321 -0
  217. package/lib/plugin-system.js +604 -0
  218. package/lib/plugin-verifier.js +197 -0
  219. package/lib/rate-limiter.js +113 -0
  220. package/lib/security-scanner.js +312 -0
  221. package/lib/self-healing.js +468 -0
  222. package/lib/session-manager.js +264 -0
  223. package/lib/skill-sandbox.js +244 -0
  224. package/lib/task-governance.js +522 -0
  225. package/lib/task-model.js +332 -0
  226. package/lib/updater.js +240 -0
  227. package/lib/verify.js +279 -0
  228. package/lib/workflow-engine.js +373 -0
  229. package/lib/workflow-events.js +166 -0
  230. package/lib/workflow-persistence.js +160 -0
  231. package/package.json +57 -0
@@ -0,0 +1,468 @@
1
+ /**
2
+ * Devran AI Kit — Self-Healing Pipeline
3
+ *
4
+ * Detects CI failures, diagnoses root causes, generates
5
+ * JSON fix patches, and applies with confirmation.
6
+ *
7
+ * @module lib/self-healing
8
+ * @author Emre Dursun
9
+ * @since v3.0.0
10
+ */
11
+
12
+ 'use strict';
13
+
14
+ const fs = require('fs');
15
+ const path = require('path');
16
+ const crypto = require('crypto');
17
+ const { writeJsonAtomic } = require('./io');
18
+ const { createLogger } = require('./logger');
19
+ const log = createLogger('self-healing');
20
+
21
+ const { AGENT_DIR, ENGINE_DIR } = require('./constants');
22
+ const HEALING_LOG_FILE = 'healing-log.json';
23
+
24
+ /** Maximum healing log entries before pruning */
25
+ const MAX_LOG_ENTRIES = 100;
26
+
27
+ /**
28
+ * Sanitizes a module path into a valid JavaScript variable name.
29
+ * Handles scoped packages (@scope/name), paths with slashes,
30
+ * and ensures the result starts with a letter.
31
+ *
32
+ * @param {string} modulePath - Raw module name or path
33
+ * @returns {string} Valid JS variable name
34
+ */
35
+ function sanitizeVariableName(modulePath) {
36
+ // Extract the last segment (handle scoped packages and paths)
37
+ const segments = modulePath.replace('@', '').split(/[/\\]/);
38
+ const baseName = segments[segments.length - 1] || 'module';
39
+
40
+ // Convert to camelCase: strip non-alphanumeric, capitalize after separators
41
+ const sanitized = baseName
42
+ .replace(/[^a-zA-Z0-9]+(.)?/g, (_, chr) => (chr ? chr.toUpperCase() : ''))
43
+ .replace(/^[^a-zA-Z]/, '');
44
+
45
+ return sanitized || 'unknownModule';
46
+ }
47
+
48
+ /**
49
+ * @typedef {object} FailureDetection
50
+ * @property {string} type - Failure type: 'test' | 'build' | 'dependency' | 'lint'
51
+ * @property {string} message - Failure message
52
+ * @property {string | null} file - Affected file (if detectable)
53
+ * @property {number | null} line - Line number (if detectable)
54
+ * @property {string} severity - 'critical' | 'high' | 'medium' | 'low'
55
+ */
56
+
57
+ /**
58
+ * @typedef {object} Diagnosis
59
+ * @property {string} category - Root cause: 'syntax' | 'import' | 'type' | 'config' | 'assertion' | 'unknown'
60
+ * @property {string} explanation - Human-readable diagnosis
61
+ * @property {boolean} autoFixable - Whether auto-fix is possible
62
+ */
63
+
64
+ /**
65
+ * @typedef {object} FixPatch
66
+ * @property {string} patchId - Unique patch ID
67
+ * @property {string} file - Target file path
68
+ * @property {'insert' | 'replace' | 'delete'} type - Patch operation type
69
+ * @property {number | null} line - Target line number
70
+ * @property {string} original - Original content (for replace/delete)
71
+ * @property {string} replacement - New content (for insert/replace)
72
+ * @property {'high' | 'medium' | 'low'} confidence - Fix confidence level
73
+ */
74
+
75
+ /**
76
+ * Resolves the healing log file path.
77
+ *
78
+ * @param {string} projectRoot - Root directory
79
+ * @returns {string}
80
+ */
81
+ function resolveHealingLogPath(projectRoot) {
82
+ return path.join(projectRoot, AGENT_DIR, ENGINE_DIR, HEALING_LOG_FILE);
83
+ }
84
+
85
+ /**
86
+ * Loads the healing log from disk.
87
+ *
88
+ * @param {string} projectRoot - Root directory
89
+ * @returns {{ entries: object[] }}
90
+ */
91
+ function loadHealingLog(projectRoot) {
92
+ const filePath = resolveHealingLogPath(projectRoot);
93
+
94
+ if (!fs.existsSync(filePath)) {
95
+ return { entries: [] };
96
+ }
97
+
98
+ try {
99
+ return JSON.parse(fs.readFileSync(filePath, 'utf-8'));
100
+ } catch {
101
+ return { entries: [] };
102
+ }
103
+ }
104
+
105
+ /**
106
+ * Writes the healing log atomically with pruning.
107
+ *
108
+ * @param {string} projectRoot - Root directory
109
+ * @param {{ entries: object[] }} data
110
+ * @returns {void}
111
+ */
112
+ function writeHealingLog(projectRoot, data) {
113
+ const filePath = resolveHealingLogPath(projectRoot);
114
+
115
+ // Prune to last MAX_LOG_ENTRIES (immutable)
116
+ const prunedData = data.entries.length > MAX_LOG_ENTRIES
117
+ ? { ...data, entries: data.entries.slice(-MAX_LOG_ENTRIES) }
118
+ : data;
119
+
120
+ writeJsonAtomic(filePath, prunedData);
121
+ }
122
+
123
+ // ═══════════════════════════════════════════════════
124
+ // Failure Detection Patterns
125
+ // ═══════════════════════════════════════════════════
126
+
127
+ /** @type {{ pattern: RegExp, type: string, severity: string }[]} */
128
+ const FAILURE_PATTERNS = [
129
+ // Test failures
130
+ {
131
+ pattern: /FAIL\s+([\w./\\-]+)\s*>/,
132
+ type: 'test',
133
+ severity: 'high',
134
+ },
135
+ {
136
+ pattern: /AssertionError:\s*(.+)/,
137
+ type: 'test',
138
+ severity: 'high',
139
+ },
140
+ {
141
+ pattern: /Expected\s*(.+)\s*to\s*(equal|be|match)/i,
142
+ type: 'test',
143
+ severity: 'high',
144
+ },
145
+ // Build failures
146
+ {
147
+ pattern: /error TS(\d+):\s*(.+)/,
148
+ type: 'build',
149
+ severity: 'critical',
150
+ },
151
+ {
152
+ pattern: /SyntaxError:\s*(.+)/,
153
+ type: 'build',
154
+ severity: 'critical',
155
+ },
156
+ // Import/dependency issues
157
+ {
158
+ pattern: /Cannot find module '([^']+)'/,
159
+ type: 'dependency',
160
+ severity: 'high',
161
+ },
162
+ {
163
+ pattern: /Module not found:\s*(.+)/,
164
+ type: 'dependency',
165
+ severity: 'high',
166
+ },
167
+ {
168
+ pattern: /ERR_MODULE_NOT_FOUND/,
169
+ type: 'dependency',
170
+ severity: 'high',
171
+ },
172
+ // Lint errors
173
+ {
174
+ pattern: /(\d+):(\d+)\s+error\s+(.+?)\s+([\w/-]+)$/m,
175
+ type: 'lint',
176
+ severity: 'medium',
177
+ },
178
+ {
179
+ pattern: /eslint.*error/i,
180
+ type: 'lint',
181
+ severity: 'medium',
182
+ },
183
+ ];
184
+
185
+ /**
186
+ * Detects failures from raw CI output text.
187
+ *
188
+ * @param {string} ciOutput - Raw CI log text
189
+ * @returns {FailureDetection[]}
190
+ */
191
+ function detectFailure(ciOutput) {
192
+ if (!ciOutput || typeof ciOutput !== 'string') {
193
+ return [];
194
+ }
195
+
196
+ /** @type {FailureDetection[]} */
197
+ const failures = [];
198
+ const lines = ciOutput.split('\n');
199
+
200
+ for (const line of lines) {
201
+ for (const { pattern, type, severity } of FAILURE_PATTERNS) {
202
+ const match = line.match(pattern);
203
+ if (match) {
204
+ // Try to extract file and line from context
205
+ const fileMatch = line.match(/([\w./\\-]+\.(js|ts|jsx|tsx|json))/);
206
+ const lineMatch = line.match(/:(\d+):/);
207
+
208
+ failures.push({
209
+ type,
210
+ message: match[0].trim(),
211
+ file: fileMatch ? fileMatch[1] : null,
212
+ line: lineMatch ? parseInt(lineMatch[1], 10) : null,
213
+ severity,
214
+ });
215
+ break; // One match per line
216
+ }
217
+ }
218
+ }
219
+
220
+ return failures;
221
+ }
222
+
223
+ // ═══════════════════════════════════════════════════
224
+ // Failure Diagnosis
225
+ // ═══════════════════════════════════════════════════
226
+
227
+ /**
228
+ * Diagnoses the root cause of a detected failure.
229
+ *
230
+ * @param {FailureDetection} failure - Detected failure
231
+ * @returns {Diagnosis}
232
+ */
233
+ function diagnoseFailure(failure) {
234
+ const message = failure.message.toLowerCase();
235
+
236
+ // Import/module issues
237
+ if (failure.type === 'dependency' || message.includes('cannot find module') || message.includes('module not found')) {
238
+ return {
239
+ category: 'import',
240
+ explanation: `Missing module or incorrect import path: ${failure.message}`,
241
+ autoFixable: true,
242
+ };
243
+ }
244
+
245
+ // Syntax errors
246
+ if (failure.type === 'build' && message.includes('syntaxerror')) {
247
+ return {
248
+ category: 'syntax',
249
+ explanation: `Syntax error in source code: ${failure.message}`,
250
+ autoFixable: false,
251
+ };
252
+ }
253
+
254
+ // TypeScript type errors
255
+ if (failure.type === 'build' && message.includes('error ts')) {
256
+ return {
257
+ category: 'type',
258
+ explanation: `TypeScript type error: ${failure.message}`,
259
+ autoFixable: false,
260
+ };
261
+ }
262
+
263
+ // Test assertion failures
264
+ if (failure.type === 'test') {
265
+ return {
266
+ category: 'assertion',
267
+ explanation: `Test assertion failed — requires manual review: ${failure.message}`,
268
+ autoFixable: false,
269
+ };
270
+ }
271
+
272
+ // Lint errors
273
+ if (failure.type === 'lint') {
274
+ return {
275
+ category: 'config',
276
+ explanation: `Lint rule violation: ${failure.message}`,
277
+ autoFixable: true,
278
+ };
279
+ }
280
+
281
+ return {
282
+ category: 'unknown',
283
+ explanation: `Unclassified failure: ${failure.message}`,
284
+ autoFixable: false,
285
+ };
286
+ }
287
+
288
+ // ═══════════════════════════════════════════════════
289
+ // Fix Patch Generation
290
+ // ═══════════════════════════════════════════════════
291
+
292
+ /**
293
+ * Generates a fix patch for a diagnosed failure.
294
+ * Only generates patches for auto-fixable issues.
295
+ *
296
+ * @param {FailureDetection} failure - Detected failure
297
+ * @param {Diagnosis} diagnosis - Diagnosis result
298
+ * @returns {FixPatch | null} Generated patch, or null if not auto-fixable
299
+ */
300
+ function generateFixPatch(failure, diagnosis) {
301
+ if (!diagnosis.autoFixable) {
302
+ return null;
303
+ }
304
+
305
+ const patchId = `HEAL-${crypto.randomUUID().slice(0, 8).toUpperCase()}`;
306
+
307
+ // Missing import → suggest adding import
308
+ if (diagnosis.category === 'import') {
309
+ const moduleMatch = failure.message.match(/(?:Cannot find module|Module not found)[:\s]*'?([^'"\s]+)/i);
310
+ const rawModuleName = moduleMatch ? moduleMatch[1] : 'unknown-module';
311
+ // Sanitize module name to prevent injection via crafted CI output (M-11)
312
+ const safeModuleName = rawModuleName.replace(/[^a-zA-Z0-9@/_.-]/g, '');
313
+
314
+ return {
315
+ patchId,
316
+ file: failure.file || 'unknown',
317
+ type: 'insert',
318
+ line: 1,
319
+ original: '',
320
+ replacement: `const ${sanitizeVariableName(safeModuleName)} = require('${safeModuleName}');`,
321
+ confidence: 'medium',
322
+ };
323
+ }
324
+
325
+ // Lint fix → suggest formatting change
326
+ if (diagnosis.category === 'config') {
327
+ return {
328
+ patchId,
329
+ file: failure.file || 'unknown',
330
+ type: 'replace',
331
+ line: failure.line,
332
+ original: failure.message,
333
+ replacement: `// TODO: Fix lint rule — ${failure.message}`,
334
+ confidence: 'low',
335
+ };
336
+ }
337
+
338
+ return null;
339
+ }
340
+
341
+ /**
342
+ * Applies a fix patch with confirmation safeguard.
343
+ * Dry-run by default — requires explicit opt-in for file writes.
344
+ *
345
+ * @param {string} projectRoot - Root directory
346
+ * @param {FixPatch} patch - Patch to apply
347
+ * @param {object} [options] - Apply options
348
+ * @param {boolean} [options.dryRun] - If true (default), just preview
349
+ * @returns {{ applied: boolean, preview: string, patchId: string }}
350
+ */
351
+ function applyFixWithConfirmation(projectRoot, patch, options = {}) {
352
+ const dryRun = options.dryRun !== false; // Default: true
353
+
354
+ const logEntry = {
355
+ patchId: patch.patchId,
356
+ file: patch.file,
357
+ type: patch.type,
358
+ applied: false,
359
+ dryRun,
360
+ timestamp: new Date().toISOString(),
361
+ rollbackData: {
362
+ original: patch.original,
363
+ line: patch.line,
364
+ },
365
+ };
366
+
367
+ const preview = [
368
+ `Patch: ${patch.patchId}`,
369
+ `File: ${patch.file}`,
370
+ `Type: ${patch.type}`,
371
+ `Line: ${patch.line || 'N/A'}`,
372
+ `Confidence: ${patch.confidence}`,
373
+ `Original: ${patch.original || '(empty)'}`,
374
+ `Replacement: ${patch.replacement}`,
375
+ dryRun ? '[DRY RUN — no changes applied]' : '[APPLIED]',
376
+ ].join('\n');
377
+
378
+ if (!dryRun && patch.file !== 'unknown') {
379
+ const targetPath = path.join(projectRoot, patch.file);
380
+
381
+ // Validate target stays within project root (prevent path traversal via crafted CI logs)
382
+ const resolvedTarget = path.resolve(targetPath);
383
+ const resolvedRoot = path.resolve(projectRoot);
384
+ if (!resolvedTarget.startsWith(resolvedRoot + path.sep) && resolvedTarget !== resolvedRoot) {
385
+ logEntry.applied = false;
386
+ // Load healing log and record the blocked attempt (H-6: fixed variable shadowing)
387
+ const healingLog = loadHealingLog(projectRoot);
388
+ writeHealingLog(projectRoot, { ...healingLog, entries: [...healingLog.entries, logEntry] });
389
+ return { applied: false, preview, patchId: patch.patchId };
390
+ }
391
+
392
+ if (fs.existsSync(targetPath)) {
393
+ try {
394
+ const content = fs.readFileSync(targetPath, 'utf-8');
395
+
396
+ // Create backup before applying patch
397
+ const backupPath = `${targetPath}.bak`;
398
+ fs.writeFileSync(backupPath, content, 'utf-8');
399
+ logEntry.rollbackData.backupPath = backupPath;
400
+
401
+ const lines = content.split('\n');
402
+
403
+ if (patch.type === 'insert' && patch.line !== null) {
404
+ const insertIndex = Math.max(0, (patch.line || 1) - 1);
405
+ lines.splice(insertIndex, 0, patch.replacement);
406
+ } else if (patch.type === 'replace' && patch.line !== null) {
407
+ const replaceIndex = (patch.line || 1) - 1;
408
+ if (replaceIndex >= 0 && replaceIndex < lines.length) {
409
+ lines[replaceIndex] = patch.replacement;
410
+ }
411
+ } else if (patch.type === 'delete' && patch.line !== null) {
412
+ const deleteIndex = (patch.line || 1) - 1;
413
+ if (deleteIndex >= 0 && deleteIndex < lines.length) {
414
+ lines.splice(deleteIndex, 1);
415
+ }
416
+ }
417
+
418
+ const tempPath = `${targetPath}.tmp`;
419
+ fs.writeFileSync(tempPath, lines.join('\n'), 'utf-8');
420
+ fs.renameSync(tempPath, targetPath);
421
+
422
+ logEntry.applied = true;
423
+ } catch {
424
+ logEntry.applied = false;
425
+ }
426
+ }
427
+ }
428
+
429
+ // Log the action (using distinct variable name to avoid shadowing logger)
430
+ const healingLog = loadHealingLog(projectRoot);
431
+ writeHealingLog(projectRoot, { ...healingLog, entries: [...healingLog.entries, logEntry] });
432
+
433
+ return {
434
+ applied: logEntry.applied,
435
+ preview,
436
+ patchId: patch.patchId,
437
+ };
438
+ }
439
+
440
+ /**
441
+ * Returns the healing report: recent heal activities and stats.
442
+ *
443
+ * @param {string} projectRoot - Root directory
444
+ * @returns {{ totalHeals: number, successRate: number, recentEntries: object[], pendingPatches: number }}
445
+ */
446
+ function getHealingReport(projectRoot) {
447
+ const log = loadHealingLog(projectRoot);
448
+ const entries = log.entries || [];
449
+
450
+ const appliedEntries = entries.filter((e) => !e.dryRun);
451
+ const applied = appliedEntries.filter((e) => e.applied).length;
452
+ const dryRuns = entries.filter((e) => e.dryRun).length;
453
+
454
+ return {
455
+ totalHeals: entries.length,
456
+ successRate: appliedEntries.length > 0 ? Math.round((applied / appliedEntries.length) * 100) : 0,
457
+ recentEntries: entries.slice(-5),
458
+ pendingPatches: dryRuns,
459
+ };
460
+ }
461
+
462
+ module.exports = {
463
+ detectFailure,
464
+ diagnoseFailure,
465
+ generateFixPatch,
466
+ applyFixWithConfirmation,
467
+ getHealingReport,
468
+ };