sela-core 1.0.6 → 1.0.8

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 (124) hide show
  1. package/README.md +1 -1
  2. package/dist/cli/ErrorHandler.d.ts +1 -1
  3. package/dist/cli/ErrorHandler.js +14 -14
  4. package/dist/cli/commands/bulk.d.ts +1 -1
  5. package/dist/cli/commands/bulk.d.ts.map +1 -1
  6. package/dist/cli/commands/bulk.js +51 -33
  7. package/dist/cli/commands/init.d.ts +1 -1
  8. package/dist/cli/commands/init.d.ts.map +1 -1
  9. package/dist/cli/commands/init.js +26 -18
  10. package/dist/cli/commands/showReport.d.ts +1 -1
  11. package/dist/cli/commands/showReport.d.ts.map +1 -1
  12. package/dist/cli/commands/showReport.js +12 -10
  13. package/dist/cli/commands/status.d.ts +1 -1
  14. package/dist/cli/commands/status.d.ts.map +1 -1
  15. package/dist/cli/commands/status.js +8 -8
  16. package/dist/cli/commands/sync.d.ts +1 -1
  17. package/dist/cli/commands/sync.d.ts.map +1 -1
  18. package/dist/cli/commands/sync.js +38 -30
  19. package/dist/cli/index.js +9 -9
  20. package/dist/cli/ui/DnaTable.d.ts +1 -1
  21. package/dist/cli/ui/DnaTable.d.ts.map +1 -1
  22. package/dist/cli/ui/DnaTable.js +23 -16
  23. package/dist/cli/ui/RefactorWizard.d.ts +3 -3
  24. package/dist/cli/ui/RefactorWizard.d.ts.map +1 -1
  25. package/dist/cli/ui/RefactorWizard.js +108 -82
  26. package/dist/config/ConfigLoader.d.ts +3 -2
  27. package/dist/config/ConfigLoader.d.ts.map +1 -1
  28. package/dist/config/ConfigLoader.js +23 -13
  29. package/dist/config/DryRunGuard.d.ts +14 -0
  30. package/dist/config/DryRunGuard.d.ts.map +1 -0
  31. package/dist/config/DryRunGuard.js +79 -0
  32. package/dist/config/SelaConfig.d.ts +26 -16
  33. package/dist/config/SelaConfig.d.ts.map +1 -1
  34. package/dist/config/SelaConfig.js +54 -22
  35. package/dist/engine/HealingRegistry.d.ts +1 -1
  36. package/dist/engine/HealingRegistry.js +3 -3
  37. package/dist/engine/SelaEngine.d.ts +13 -10
  38. package/dist/engine/SelaEngine.d.ts.map +1 -1
  39. package/dist/engine/SelaEngine.js +292 -101
  40. package/dist/errors/SelaError.d.ts +132 -0
  41. package/dist/errors/SelaError.d.ts.map +1 -0
  42. package/dist/errors/SelaError.js +154 -0
  43. package/dist/fixtures/expectProxy.d.ts +1 -1
  44. package/dist/fixtures/expectProxy.d.ts.map +1 -1
  45. package/dist/fixtures/expectProxy.js +17 -10
  46. package/dist/fixtures/index.d.ts +7 -1
  47. package/dist/fixtures/index.d.ts.map +1 -1
  48. package/dist/fixtures/index.js +26 -11
  49. package/dist/fixtures/proxyTag.d.ts +12 -0
  50. package/dist/fixtures/proxyTag.d.ts.map +1 -0
  51. package/dist/fixtures/proxyTag.js +45 -0
  52. package/dist/index.d.ts +8 -6
  53. package/dist/index.d.ts.map +1 -1
  54. package/dist/index.js +9 -4
  55. package/dist/reporter/SelaReporter.d.ts +143 -0
  56. package/dist/reporter/SelaReporter.d.ts.map +1 -0
  57. package/dist/reporter/SelaReporter.js +428 -0
  58. package/dist/services/ASTSourceUpdater.d.ts +20 -1
  59. package/dist/services/ASTSourceUpdater.d.ts.map +1 -1
  60. package/dist/services/ASTSourceUpdater.js +257 -89
  61. package/dist/services/ArgumentTypeAnalyzer.js +7 -7
  62. package/dist/services/BlastRadiusAnalyzer.d.ts.map +1 -1
  63. package/dist/services/BlastRadiusAnalyzer.js +5 -2
  64. package/dist/services/ChainValidator.js +22 -22
  65. package/dist/services/CrossFileHealer.js +7 -7
  66. package/dist/services/DefinitionTracer.d.ts +1 -1
  67. package/dist/services/DefinitionTracer.d.ts.map +1 -1
  68. package/dist/services/DefinitionTracer.js +28 -13
  69. package/dist/services/DnaEditorService.d.ts +2 -2
  70. package/dist/services/DnaEditorService.d.ts.map +1 -1
  71. package/dist/services/DnaEditorService.js +81 -62
  72. package/dist/services/DnaIndexService.d.ts +1 -1
  73. package/dist/services/DnaIndexService.d.ts.map +1 -1
  74. package/dist/services/DnaIndexService.js +14 -14
  75. package/dist/services/HealReportService.d.ts +43 -1
  76. package/dist/services/HealReportService.d.ts.map +1 -1
  77. package/dist/services/HealReportService.js +108 -25
  78. package/dist/services/HealingAdvisory.js +1 -1
  79. package/dist/services/HealingCacheService.d.ts +135 -0
  80. package/dist/services/HealingCacheService.d.ts.map +1 -0
  81. package/dist/services/HealingCacheService.js +460 -0
  82. package/dist/services/HealthReportService.d.ts +1 -1
  83. package/dist/services/HealthReportService.d.ts.map +1 -1
  84. package/dist/services/HealthReportService.js +25 -25
  85. package/dist/services/InitializerUpdater.d.ts.map +1 -1
  86. package/dist/services/InitializerUpdater.js +20 -1
  87. package/dist/services/IntentAuditor.d.ts +18 -1
  88. package/dist/services/IntentAuditor.d.ts.map +1 -1
  89. package/dist/services/IntentAuditor.js +50 -26
  90. package/dist/services/InteractiveReview.d.ts +15 -0
  91. package/dist/services/InteractiveReview.d.ts.map +1 -0
  92. package/dist/services/InteractiveReview.js +125 -0
  93. package/dist/services/LLMService.d.ts +18 -2
  94. package/dist/services/LLMService.d.ts.map +1 -1
  95. package/dist/services/LLMService.js +33 -23
  96. package/dist/services/PRAutomationService.d.ts +14 -1
  97. package/dist/services/PRAutomationService.d.ts.map +1 -1
  98. package/dist/services/PRAutomationService.js +287 -92
  99. package/dist/services/PendingPromptLedger.d.ts +44 -0
  100. package/dist/services/PendingPromptLedger.d.ts.map +1 -0
  101. package/dist/services/PendingPromptLedger.js +180 -0
  102. package/dist/services/ReportGenerator.d.ts +70 -0
  103. package/dist/services/ReportGenerator.d.ts.map +1 -0
  104. package/dist/services/ReportGenerator.js +191 -0
  105. package/dist/services/SafetyGuard.d.ts +30 -7
  106. package/dist/services/SafetyGuard.d.ts.map +1 -1
  107. package/dist/services/SafetyGuard.js +85 -116
  108. package/dist/services/SnapshotService.js +5 -5
  109. package/dist/services/SourceLinkService.d.ts +2 -2
  110. package/dist/services/SourceLinkService.d.ts.map +1 -1
  111. package/dist/services/SourceLinkService.js +31 -22
  112. package/dist/services/SourceUpdater.d.ts +1 -1
  113. package/dist/services/SourceUpdater.d.ts.map +1 -1
  114. package/dist/services/SourceUpdater.js +66 -34
  115. package/dist/services/TemplateDiffService.d.ts.map +1 -1
  116. package/dist/services/TemplateDiffService.js +18 -15
  117. package/dist/services/WorkspaceSnapshotService.d.ts +71 -0
  118. package/dist/services/WorkspaceSnapshotService.d.ts.map +1 -0
  119. package/dist/services/WorkspaceSnapshotService.js +200 -0
  120. package/dist/types/index.d.ts +1 -1
  121. package/dist/utils/IsolatedDiff.d.ts +45 -0
  122. package/dist/utils/IsolatedDiff.d.ts.map +1 -0
  123. package/dist/utils/IsolatedDiff.js +382 -0
  124. package/package.json +71 -67
@@ -17,14 +17,14 @@ function bakPath(dir, key) {
17
17
  }
18
18
  // Derive a best-effort selector from engine snapshot fields when none is stored.
19
19
  function deriveSelector(snap) {
20
- const id = snap['id'];
21
- const classes = snap['classes'];
22
- const tagName = snap['tagName'];
23
- if (typeof id === 'string' && id)
20
+ const id = snap["id"];
21
+ const classes = snap["classes"];
22
+ const tagName = snap["tagName"];
23
+ if (typeof id === "string" && id)
24
24
  return `#${id}`;
25
25
  if (Array.isArray(classes) && classes.length > 0)
26
- return `.${classes.join('.')}`;
27
- return typeof tagName === 'string' ? tagName : 'unknown';
26
+ return `.${classes.join(".")}`;
27
+ return typeof tagName === "string" ? tagName : "unknown";
28
28
  }
29
29
  function dnaError(message, code) {
30
30
  const err = new Error(message);
@@ -40,15 +40,15 @@ class DnaEditorService {
40
40
  const target = jsonPath(this.snapshotDir, key);
41
41
  let raw;
42
42
  try {
43
- raw = await promises_1.default.readFile(target, 'utf-8');
43
+ raw = await promises_1.default.readFile(target, "utf-8");
44
44
  }
45
45
  catch {
46
- throw dnaError(`DNA not found: ${key}`, 'DNA_NOT_FOUND');
46
+ throw dnaError(`DNA not found: ${key}`, "DNA_NOT_FOUND");
47
47
  }
48
48
  const parsed = JSON.parse(raw);
49
49
  // Promote to extended format if selector not stored
50
- if (!parsed['selector']) {
51
- parsed['selector'] = deriveSelector(parsed);
50
+ if (!parsed["selector"]) {
51
+ parsed["selector"] = deriveSelector(parsed);
52
52
  }
53
53
  return parsed;
54
54
  }
@@ -59,22 +59,25 @@ class DnaEditorService {
59
59
  // 1. Read existing snapshot
60
60
  const original = await this.read(key);
61
61
  // 2. Shallow-merge patch (arrays and objects replaced, not deep-merged)
62
- const merged = { ...original, ...patch };
62
+ const merged = {
63
+ ...original,
64
+ ...patch,
65
+ };
63
66
  // Any CLI write = human has reviewed the DNA → clear auto-heal status flag.
64
- delete merged['status'];
65
- // 3. Derive selector only when absent never override an explicit (possibly invalid) value
66
- if (merged['selector'] == null) {
67
- merged['selector'] = deriveSelector(merged);
67
+ delete merged["status"];
68
+ // 3. Derive selector only when absent - never override an explicit (possibly invalid) value
69
+ if (merged["selector"] == null) {
70
+ merged["selector"] = deriveSelector(merged);
68
71
  }
69
72
  // 4. Validate before touching disk
70
73
  const validation = this.validate(merged);
71
74
  if (!validation.valid) {
72
- throw dnaError(`Validation failed for ${key}: ${validation.errors.join('; ')}`, 'VALIDATION_FAILED');
75
+ throw dnaError(`Validation failed for ${key}: ${validation.errors.join("; ")}`, "VALIDATION_FAILED");
73
76
  }
74
77
  // 5. Backup current file (overwrite previous bak)
75
- await promises_1.default.writeFile(bak, JSON.stringify(original, null, 2), 'utf-8');
78
+ await promises_1.default.writeFile(bak, JSON.stringify(original, null, 2), "utf-8");
76
79
  // 6. Write to tmp
77
- await promises_1.default.writeFile(tmp, JSON.stringify(merged, null, 2), 'utf-8');
80
+ await promises_1.default.writeFile(tmp, JSON.stringify(merged, null, 2), "utf-8");
78
81
  // 7. Atomic rename tmp → json
79
82
  await promises_1.default.rename(tmp, target);
80
83
  return bak;
@@ -86,111 +89,127 @@ class DnaEditorService {
86
89
  await promises_1.default.access(bak);
87
90
  }
88
91
  catch {
89
- throw dnaError(`No backup found for: ${key}`, 'DNA_NOT_FOUND');
92
+ throw dnaError(`No backup found for: ${key}`, "DNA_NOT_FOUND");
90
93
  }
91
94
  await promises_1.default.rename(bak, target);
92
95
  }
93
96
  validate(candidate) {
94
97
  const errors = [];
95
- if (typeof candidate !== 'object' || candidate === null || Array.isArray(candidate)) {
96
- return { valid: false, errors: ['candidate must be a plain object'] };
98
+ if (typeof candidate !== "object" ||
99
+ candidate === null ||
100
+ Array.isArray(candidate)) {
101
+ return { valid: false, errors: ["candidate must be a plain object"] };
97
102
  }
98
103
  const snap = candidate;
99
104
  // ── Required top-level fields ─────────────────────────────────────────────
100
- for (const field of ['tagName', 'selector', 'schemaVersion', 'timestamp']) {
105
+ for (const field of [
106
+ "tagName",
107
+ "selector",
108
+ "schemaVersion",
109
+ "timestamp",
110
+ ]) {
101
111
  if (!(field in snap) || snap[field] === undefined) {
102
112
  errors.push(`Missing required field: ${field}`);
103
113
  }
104
114
  }
105
- if (snap['schemaVersion'] !== 2) {
106
- errors.push(`schemaVersion must be 2, got ${String(snap['schemaVersion'])}`);
115
+ if (snap["schemaVersion"] !== 2) {
116
+ errors.push(`schemaVersion must be 2, got ${String(snap["schemaVersion"])}`);
107
117
  }
108
118
  // ── selector ─────────────────────────────────────────────────────────────
109
- const sel = snap['selector'];
110
- if (typeof sel === 'string') {
111
- if (sel.trim() === '')
112
- errors.push('selector must not be empty');
113
- if (sel === 'undefined')
119
+ const sel = snap["selector"];
120
+ if (typeof sel === "string") {
121
+ if (sel.trim() === "")
122
+ errors.push("selector must not be empty");
123
+ if (sel === "undefined")
114
124
  errors.push('selector must not be the string "undefined"');
115
125
  if (/[\r\n]/.test(sel))
116
- errors.push('selector must not contain raw newlines');
126
+ errors.push("selector must not contain raw newlines");
117
127
  }
118
- else if ('selector' in snap) {
119
- errors.push('selector must be a string');
128
+ else if ("selector" in snap) {
129
+ errors.push("selector must be a string");
120
130
  }
121
131
  // ── tagName ───────────────────────────────────────────────────────────────
122
- const tag = snap['tagName'];
123
- if (typeof tag === 'string') {
132
+ const tag = snap["tagName"];
133
+ if (typeof tag === "string") {
124
134
  if (!/^[a-z][a-z0-9-]*$/.test(tag)) {
125
- errors.push('tagName must match /^[a-z][a-z0-9-]*$/');
135
+ errors.push("tagName must match /^[a-z][a-z0-9-]*$/");
126
136
  }
127
137
  }
128
- else if ('tagName' in snap) {
129
- errors.push('tagName must be a string');
138
+ else if ("tagName" in snap) {
139
+ errors.push("tagName must be a string");
130
140
  }
131
141
  // ── text ─────────────────────────────────────────────────────────────────
132
- const text = snap['text'];
142
+ const text = snap["text"];
133
143
  if (text !== null && text !== undefined) {
134
- if (typeof text !== 'string') {
135
- errors.push('text must be a string or null');
144
+ if (typeof text !== "string") {
145
+ errors.push("text must be a string or null");
136
146
  }
137
147
  else if (text.length >= 2000) {
138
- errors.push('text must be fewer than 2000 characters');
148
+ errors.push("text must be fewer than 2000 characters");
139
149
  }
140
150
  }
141
151
  // ── attributes ───────────────────────────────────────────────────────────
142
- const attrs = snap['attributes'];
152
+ const attrs = snap["attributes"];
143
153
  if (attrs !== undefined) {
144
- if (typeof attrs !== 'object' || attrs === null || Array.isArray(attrs)) {
145
- errors.push('attributes must be a plain object');
154
+ if (typeof attrs !== "object" || attrs === null || Array.isArray(attrs)) {
155
+ errors.push("attributes must be a plain object");
146
156
  }
147
157
  else {
148
158
  for (const [k, v] of Object.entries(attrs)) {
149
159
  if (!k)
150
- errors.push('attributes keys must not be empty');
151
- if (typeof v !== 'string')
160
+ errors.push("attributes keys must not be empty");
161
+ if (typeof v !== "string")
152
162
  errors.push(`attributes["${k}"] must be a string`);
153
163
  }
154
164
  }
155
165
  }
156
166
  // ── visibility (all 4 keys required) ─────────────────────────────────────
157
- const vis = snap['visibility'];
158
- if (typeof vis === 'object' && vis !== null && !Array.isArray(vis)) {
167
+ const vis = snap["visibility"];
168
+ if (typeof vis === "object" && vis !== null && !Array.isArray(vis)) {
159
169
  const visObj = vis;
160
- for (const key of ['isGhost', 'ghostReason', 'isOccluded', 'opacity']) {
170
+ for (const key of [
171
+ "isGhost",
172
+ "ghostReason",
173
+ "isOccluded",
174
+ "opacity",
175
+ ]) {
161
176
  if (!(key in visObj))
162
177
  errors.push(`visibility.${key} is required`);
163
178
  }
164
179
  }
165
180
  else {
166
- errors.push('visibility block is required and must be an object');
181
+ errors.push("visibility block is required and must be an object");
167
182
  }
168
183
  // ── anchors (all 3 keys required) ────────────────────────────────────────
169
- const anch = snap['anchors'];
170
- if (typeof anch === 'object' && anch !== null && !Array.isArray(anch)) {
184
+ const anch = snap["anchors"];
185
+ if (typeof anch === "object" && anch !== null && !Array.isArray(anch)) {
171
186
  const anchObj = anch;
172
- for (const key of ['closestLabel', 'rowAnchor', 'neighborTexts']) {
187
+ for (const key of [
188
+ "closestLabel",
189
+ "rowAnchor",
190
+ "neighborTexts",
191
+ ]) {
173
192
  if (!(key in anchObj))
174
193
  errors.push(`anchors.${key} is required`);
175
194
  }
176
- const cl = anchObj['closestLabel'];
177
- if (cl !== null && typeof cl !== 'string') {
178
- errors.push('anchors.closestLabel must be a string or null');
195
+ const cl = anchObj["closestLabel"];
196
+ if (cl !== null && typeof cl !== "string") {
197
+ errors.push("anchors.closestLabel must be a string or null");
179
198
  }
180
- const nt = anchObj['neighborTexts'];
199
+ const nt = anchObj["neighborTexts"];
181
200
  if (!Array.isArray(nt)) {
182
- errors.push('anchors.neighborTexts must be an array');
201
+ errors.push("anchors.neighborTexts must be an array");
183
202
  }
184
203
  else {
185
204
  if (nt.length > 10)
186
- errors.push('anchors.neighborTexts must have at most 10 elements');
187
- if (!nt.every((x) => typeof x === 'string')) {
188
- errors.push('anchors.neighborTexts elements must all be strings');
205
+ errors.push("anchors.neighborTexts must have at most 10 elements");
206
+ if (!nt.every((x) => typeof x === "string")) {
207
+ errors.push("anchors.neighborTexts elements must all be strings");
189
208
  }
190
209
  }
191
210
  }
192
211
  else {
193
- errors.push('anchors block is required and must be an object');
212
+ errors.push("anchors block is required and must be an object");
194
213
  }
195
214
  return { valid: errors.length === 0, errors };
196
215
  }
@@ -1,4 +1,4 @@
1
- import type { DnaRecord, DnaIndex } from './types.js';
1
+ import type { DnaRecord, DnaIndex } from "./types.js";
2
2
  export interface IDnaIndexService {
3
3
  buildIndex(snapshotDir: string): Promise<DnaIndex>;
4
4
  findBySelector(pattern: string): DnaRecord[];
@@ -1 +1 @@
1
- {"version":3,"file":"DnaIndexService.d.ts","sourceRoot":"","sources":["../../src/services/DnaIndexService.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAatD,MAAM,WAAW,gBAAgB;IAC/B,UAAU,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;IACnD,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,EAAE,CAAC;IAC7C,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,EAAE,CAAC;IACzC,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,EAAE,CAAC;IAChD,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,EAAE,CAAC;IAC9C,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAAC;IACxC,QAAQ,IAAI,QAAQ,CAAC;IACrB,OAAO,IAAI,IAAI,CAAC;CACjB;AAkED,qBAAa,eAAgB,YAAW,gBAAgB;IACtD,OAAO,CAAC,WAAW,CAAyB;IAEtC,UAAU,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;IAkCxD,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,EAAE;IAK5C,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,EAAE;IAKxC,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,EAAE;IAO/C,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,EAAE;IAK7C,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI;IAIvC,QAAQ,IAAI,QAAQ;IAIpB,OAAO,IAAI,IAAI;IAIf,OAAO,CAAC,YAAY;CAQrB"}
1
+ {"version":3,"file":"DnaIndexService.d.ts","sourceRoot":"","sources":["../../src/services/DnaIndexService.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAatD,MAAM,WAAW,gBAAgB;IAC/B,UAAU,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;IACnD,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,EAAE,CAAC;IAC7C,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,EAAE,CAAC;IACzC,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,EAAE,CAAC;IAChD,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,EAAE,CAAC;IAC9C,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAAC;IACxC,QAAQ,IAAI,QAAQ,CAAC;IACrB,OAAO,IAAI,IAAI,CAAC;CACjB;AAkED,qBAAa,eAAgB,YAAW,gBAAgB;IACtD,OAAO,CAAC,WAAW,CAAyB;IAEtC,UAAU,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;IAoCxD,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,EAAE;IAK5C,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,EAAE;IAOxC,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,EAAE;IAO/C,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,EAAE;IAK7C,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI;IAIvC,QAAQ,IAAI,QAAQ;IAIpB,OAAO,IAAI,IAAI;IAIf,OAAO,CAAC,YAAY;CAQrB"}
@@ -12,7 +12,7 @@ function toRegExp(pattern) {
12
12
  const m = pattern.match(/^\/(.+)\/([gimsuy]*)$/);
13
13
  if (m)
14
14
  return new RegExp(m[1], m[2]);
15
- return new RegExp(pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'i');
15
+ return new RegExp(pattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "i");
16
16
  }
17
17
  function deriveSelector(snap) {
18
18
  if (snap.selector)
@@ -20,19 +20,19 @@ function deriveSelector(snap) {
20
20
  if (snap.id)
21
21
  return `#${snap.id}`;
22
22
  if (snap.classes.length > 0)
23
- return `.${snap.classes.join('.')}`;
23
+ return `.${snap.classes.join(".")}`;
24
24
  return snap.tagName;
25
25
  }
26
26
  function isWithin7Days(iso) {
27
27
  return new Date(iso).getTime() >= Date.now() - 7 * 24 * 60 * 60 * 1000;
28
28
  }
29
29
  function toRecord(filePath, raw) {
30
- const key = path_1.default.basename(filePath, '.json');
31
- const sourceFile = raw.sourceFile ?? '';
30
+ const key = path_1.default.basename(filePath, ".json");
31
+ const sourceFile = raw.sourceFile ?? "";
32
32
  return {
33
33
  key,
34
34
  filePath,
35
- testTitle: raw.testTitle ?? '',
35
+ testTitle: raw.testTitle ?? "",
36
36
  sourceFile,
37
37
  sourceLine: raw.sourceLine ?? 0,
38
38
  selector: deriveSelector(raw),
@@ -40,8 +40,8 @@ function toRecord(filePath, raw) {
40
40
  text: raw.text ?? null,
41
41
  lastHealed: raw.lastHealed ?? null,
42
42
  schemaVersion: raw.schemaVersion ?? 1,
43
- // Only flag orphaned when sourceFile is known engine snapshots have no sourceFile
44
- isOrphaned: sourceFile !== '' && !(0, fs_1.existsSync)(sourceFile),
43
+ // Only flag orphaned when sourceFile is known - engine snapshots have no sourceFile
44
+ isOrphaned: sourceFile !== "" && !(0, fs_1.existsSync)(sourceFile),
45
45
  status: raw.status,
46
46
  };
47
47
  }
@@ -60,9 +60,9 @@ async function collectJsonFiles(dir) {
60
60
  results.push(...(await collectJsonFiles(full)));
61
61
  }
62
62
  else if (entry.isFile() &&
63
- entry.name.endsWith('.json') &&
64
- !entry.name.endsWith('.bak') &&
65
- !entry.name.endsWith('.tmp')) {
63
+ entry.name.endsWith(".json") &&
64
+ !entry.name.endsWith(".bak") &&
65
+ !entry.name.endsWith(".tmp")) {
66
66
  results.push(full);
67
67
  }
68
68
  }));
@@ -73,11 +73,11 @@ class DnaIndexService {
73
73
  async buildIndex(snapshotDir) {
74
74
  const files = await collectJsonFiles(snapshotDir);
75
75
  const settled = await Promise.allSettled(files.map(async (filePath) => {
76
- const content = await promises_1.default.readFile(filePath, 'utf-8');
76
+ const content = await promises_1.default.readFile(filePath, "utf-8");
77
77
  return toRecord(filePath, JSON.parse(content));
78
78
  }));
79
79
  const records = settled
80
- .filter((r) => r.status === 'fulfilled')
80
+ .filter((r) => r.status === "fulfilled")
81
81
  .map((r) => r.value);
82
82
  const healedAllTime = records.filter((r) => r.lastHealed !== null).length;
83
83
  const healedLast7Days = records.filter((r) => r.lastHealed !== null && isWithin7Days(r.lastHealed)).length;
@@ -121,8 +121,8 @@ class DnaIndexService {
121
121
  }
122
122
  requireIndex() {
123
123
  if (!this.cachedIndex) {
124
- const err = new Error('Index not built call buildIndex() first');
125
- err.code = 'SNAPSHOT_DIR_MISSING';
124
+ const err = new Error("Index not built - call buildIndex() first");
125
+ err.code = "SNAPSHOT_DIR_MISSING";
126
126
  throw err;
127
127
  }
128
128
  return this.cachedIndex;
@@ -19,7 +19,7 @@ export interface AuditorBlock {
19
19
  verdict: AuditVerdictValue;
20
20
  /**
21
21
  * Adjusted confidence 0-100. `undefined` when the auditor produced no score
22
- * (e.g. SafetyGuard bypassed it) reports MUST render this as "n/a".
22
+ * (e.g. SafetyGuard bypassed it) - reports MUST render this as "n/a".
23
23
  */
24
24
  confidence?: number;
25
25
  reason: string;
@@ -41,6 +41,13 @@ interface HealEventBase {
41
41
  }
42
42
  export interface HealedEvent extends HealEventBase {
43
43
  kind: "HEALED";
44
+ /**
45
+ * Marks events produced during a `SELA_DRY_RUN=1` session - the in-memory
46
+ * heal completed (LLM call, SafetyGuard verdict, candidate diff captured)
47
+ * but no source file was modified and no commit was produced. Reports
48
+ * render these with an explicit "Dry Run · No Write" chip.
49
+ */
50
+ dryRun?: boolean;
44
51
  oldSelector: string;
45
52
  newSelector: string;
46
53
  /** First removed line from the native git diff (backward-compat fallback). */
@@ -83,6 +90,16 @@ export interface FailedEvent extends HealEventBase {
83
90
  aiConfidence?: number;
84
91
  aiExplanation?: string;
85
92
  dnaBefore: DnaSummary | null;
93
+ /**
94
+ * Base64-encoded PNG screenshot captured by `SelaReporter` from the
95
+ * Playwright `TestResult.attachments` slot at the moment the test
96
+ * failed. NEVER prefixed with `data:image/png;base64,` - the HTML
97
+ * renderer prepends the data URL header at render time so the JSON
98
+ * payload stays compact and the bytes survive Playwright's temp
99
+ * folder cleanup. `undefined` when no screenshot was attached
100
+ * (worker crash, screenshot disabled, off-attachment failure).
101
+ */
102
+ failureScreenshotPng?: string;
86
103
  }
87
104
  export interface ProtectedEvent extends HealEventBase {
88
105
  kind: "PROTECTED";
@@ -114,6 +131,31 @@ export declare class HealReportService {
114
131
  getHealedEvents(): HealedEvent[];
115
132
  /** All PROTECTED events (SafetyGuard blocks) accumulated in this session. */
116
133
  getProtectedEvents(): ProtectedEvent[];
134
+ /** All FAILED events accumulated in this session. */
135
+ getFailedEvents(): FailedEvent[];
136
+ /**
137
+ * Inject a Playwright failure screenshot (Base64 PNG, no data: prefix)
138
+ * onto the FAILED event(s) matching the supplied identity hints. Used
139
+ * by `SelaReporter` to enrich engine-emitted FAILED events with the
140
+ * Playwright-captured screenshot from `TestResult.attachments`.
141
+ *
142
+ * Match policy (all supplied hints must agree):
143
+ * • `sourceFile` - suffix-matched both directions to absorb
144
+ * project-relative vs absolute path mismatches between the engine
145
+ * (relative) and Playwright TestInfo.location.file (absolute).
146
+ * • `testTitle` - exact match.
147
+ * • `stableId` - exact match.
148
+ *
149
+ * Returns the number of events updated. Zero means the caller should
150
+ * keep the screenshot pending until the matching FailedEvent is
151
+ * recorded (worker-crash edge case where the heal pipeline did not
152
+ * run before the Reporter received the failure).
153
+ */
154
+ attachFailureScreenshot(match: {
155
+ sourceFile?: string;
156
+ testTitle?: string;
157
+ stableId?: string;
158
+ }, base64Png: string): number;
117
159
  /**
118
160
  * Write `sela-report.html` (+ `sela-report.json` debug dump) to cwd.
119
161
  * Returns the absolute path of the HTML report, or null if nothing to write.
@@ -1 +1 @@
1
- {"version":3,"file":"HealReportService.d.ts","sourceRoot":"","sources":["../../src/services/HealReportService.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACvE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAMlD,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,QAAQ,GAAG,WAAW,CAAC;AAE9D,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,UAAU,EAAE,OAAO,CAAC;IACpB,aAAa,EAAE,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,iBAAiB,CAAC;IAC3B;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,YAAY,CAAC,eAAe,CAAC,CAAC;IAC7C,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,UAAU,aAAa;IACrB,IAAI,EAAE,aAAa,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,QAAQ,EAAE,OAAO,CAAC;IAClB,WAAW,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,WAAY,SAAQ,aAAa;IAChD,IAAI,EAAE,QAAQ,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,8EAA8E;IAC9E,WAAW,EAAE,MAAM,CAAC;IACpB,4EAA4E;IAC5E,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB;;;;;;;OAOG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,2EAA2E;IAC3E,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,OAAO,EAAE,YAAY,GAAG,IAAI,CAAC;IAC7B,SAAS,EAAE,UAAU,CAAC;IACtB,QAAQ,EAAE,UAAU,GAAG,IAAI,CAAC;IAC5B,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IAChD,aAAa,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CACtD;AAED,MAAM,WAAW,WAAY,SAAQ,aAAa;IAChD,IAAI,EAAE,QAAQ,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,UAAU,GAAG,IAAI,CAAC;CAC9B;AAED,MAAM,WAAW,cAAe,SAAQ,aAAa;IACnD,IAAI,EAAE,WAAW,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,YAAY,GAAG,IAAI,CAAC;IAC7B,2EAA2E;IAC3E,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,UAAU,GAAG,IAAI,CAAC;IAC7B,QAAQ,EAAE,UAAU,GAAG,IAAI,CAAC;IAC5B,aAAa,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CACtD;AAED,MAAM,MAAM,SAAS,GAAG,WAAW,GAAG,WAAW,GAAG,cAAc,CAAC;AAWnE,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,iBAAiB,GAAG,IAAI,GAAG,SAAS,GAAG,UAAU,GAAG,IAAI,CAgB5F;AAMD,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,MAAM,CAAmB;IACjC,OAAO,CAAC,SAAS,CAAoC;IAErD,MAAM,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI;IAI9B,IAAI,IAAI,MAAM;IAId,KAAK,IAAI,IAAI;IAKb,UAAU,IAAI,OAAO;IAIrB,qDAAqD;IACrD,eAAe,IAAI,WAAW,EAAE;IAIhC,6EAA6E;IAC7E,kBAAkB,IAAI,cAAc,EAAE;IAItC;;;OAGG;IACH,WAAW,CAAC,SAAS,GAAE,MAAsB,GAAG,MAAM,GAAG,IAAI;IAyB7D,OAAO,CAAC,UAAU;IAWlB,OAAO,CAAC,YAAY;CAwBrB;AAMD,eAAO,MAAM,gBAAgB,mBAA0B,CAAC"}
1
+ {"version":3,"file":"HealReportService.d.ts","sourceRoot":"","sources":["../../src/services/HealReportService.ts"],"names":[],"mappings":"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"}