sela-core 1.0.7 → 1.0.9

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 (146) hide show
  1. package/README.md +27 -17
  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/merge.d.ts +13 -0
  11. package/dist/cli/commands/merge.d.ts.map +1 -0
  12. package/dist/cli/commands/merge.js +119 -0
  13. package/dist/cli/commands/showReport.d.ts +1 -1
  14. package/dist/cli/commands/showReport.d.ts.map +1 -1
  15. package/dist/cli/commands/showReport.js +12 -10
  16. package/dist/cli/commands/status.d.ts +1 -1
  17. package/dist/cli/commands/status.d.ts.map +1 -1
  18. package/dist/cli/commands/status.js +8 -8
  19. package/dist/cli/commands/sync.d.ts +1 -1
  20. package/dist/cli/commands/sync.d.ts.map +1 -1
  21. package/dist/cli/commands/sync.js +38 -30
  22. package/dist/cli/index.js +12 -9
  23. package/dist/cli/ui/DnaTable.d.ts +1 -1
  24. package/dist/cli/ui/DnaTable.d.ts.map +1 -1
  25. package/dist/cli/ui/DnaTable.js +23 -16
  26. package/dist/cli/ui/RefactorWizard.d.ts +3 -3
  27. package/dist/cli/ui/RefactorWizard.d.ts.map +1 -1
  28. package/dist/cli/ui/RefactorWizard.js +108 -82
  29. package/dist/config/ConfigLoader.d.ts +2 -2
  30. package/dist/config/ConfigLoader.d.ts.map +1 -1
  31. package/dist/config/ConfigLoader.js +15 -15
  32. package/dist/config/DryRunGuard.d.ts +1 -1
  33. package/dist/config/DryRunGuard.js +7 -7
  34. package/dist/config/SelaConfig.d.ts +13 -17
  35. package/dist/config/SelaConfig.d.ts.map +1 -1
  36. package/dist/config/SelaConfig.js +32 -26
  37. package/dist/engine/HealingRegistry.d.ts +1 -1
  38. package/dist/engine/HealingRegistry.js +3 -3
  39. package/dist/engine/SelaEngine.d.ts +1 -1
  40. package/dist/engine/SelaEngine.d.ts.map +1 -1
  41. package/dist/engine/SelaEngine.js +117 -51
  42. package/dist/errors/SelaError.d.ts +8 -1
  43. package/dist/errors/SelaError.d.ts.map +1 -1
  44. package/dist/errors/SelaError.js +14 -7
  45. package/dist/fixtures/expectProxy.d.ts +2 -2
  46. package/dist/fixtures/expectProxy.js +16 -16
  47. package/dist/fixtures/index.d.ts +18 -2
  48. package/dist/fixtures/index.d.ts.map +1 -1
  49. package/dist/fixtures/index.js +47 -27
  50. package/dist/fixtures/moduleExpect.js +7 -7
  51. package/dist/fixtures/proxyTag.d.ts +1 -1
  52. package/dist/fixtures/proxyTag.js +4 -4
  53. package/dist/index.d.ts +8 -8
  54. package/dist/index.d.ts.map +1 -1
  55. package/dist/index.js +4 -4
  56. package/dist/reporter/SelaReporter.d.ts +45 -12
  57. package/dist/reporter/SelaReporter.d.ts.map +1 -1
  58. package/dist/reporter/SelaReporter.js +139 -39
  59. package/dist/services/ASTSourceUpdater.d.ts +48 -3
  60. package/dist/services/ASTSourceUpdater.d.ts.map +1 -1
  61. package/dist/services/ASTSourceUpdater.js +283 -105
  62. package/dist/services/AnchorResolver.d.ts +157 -0
  63. package/dist/services/AnchorResolver.d.ts.map +1 -0
  64. package/dist/services/AnchorResolver.js +289 -0
  65. package/dist/services/ArgumentTypeAnalyzer.js +7 -7
  66. package/dist/services/BlastRadiusAnalyzer.d.ts.map +1 -1
  67. package/dist/services/BlastRadiusAnalyzer.js +5 -2
  68. package/dist/services/ChainValidator.js +22 -22
  69. package/dist/services/CrossFileHealer.js +7 -7
  70. package/dist/services/DecisionEngine.d.ts +51 -0
  71. package/dist/services/DecisionEngine.d.ts.map +1 -0
  72. package/dist/services/DecisionEngine.js +260 -0
  73. package/dist/services/DefinitionTracer.d.ts +1 -1
  74. package/dist/services/DefinitionTracer.d.ts.map +1 -1
  75. package/dist/services/DefinitionTracer.js +28 -13
  76. package/dist/services/DnaEditorService.d.ts +2 -2
  77. package/dist/services/DnaEditorService.d.ts.map +1 -1
  78. package/dist/services/DnaEditorService.js +81 -62
  79. package/dist/services/DnaIndexService.d.ts +1 -1
  80. package/dist/services/DnaIndexService.d.ts.map +1 -1
  81. package/dist/services/DnaIndexService.js +14 -14
  82. package/dist/services/HealReportService.d.ts +31 -55
  83. package/dist/services/HealReportService.d.ts.map +1 -1
  84. package/dist/services/HealReportService.js +864 -1240
  85. package/dist/services/HealingAdvisory.d.ts +9 -1
  86. package/dist/services/HealingAdvisory.d.ts.map +1 -1
  87. package/dist/services/HealingAdvisory.js +9 -1
  88. package/dist/services/HealingCacheService.d.ts +8 -8
  89. package/dist/services/HealingCacheService.d.ts.map +1 -1
  90. package/dist/services/HealingCacheService.js +24 -24
  91. package/dist/services/HealthReportService.d.ts +1 -1
  92. package/dist/services/HealthReportService.d.ts.map +1 -1
  93. package/dist/services/HealthReportService.js +25 -25
  94. package/dist/services/InitializerUpdater.d.ts.map +1 -1
  95. package/dist/services/InitializerUpdater.js +12 -2
  96. package/dist/services/IntentAuditor.d.ts.map +1 -1
  97. package/dist/services/IntentAuditor.js +32 -21
  98. package/dist/services/InteractiveReview.d.ts +8 -17
  99. package/dist/services/InteractiveReview.d.ts.map +1 -1
  100. package/dist/services/InteractiveReview.js +34 -124
  101. package/dist/services/LLMService.d.ts +1 -1
  102. package/dist/services/LLMService.js +16 -16
  103. package/dist/services/MutationApplier.d.ts +55 -0
  104. package/dist/services/MutationApplier.d.ts.map +1 -0
  105. package/dist/services/MutationApplier.js +322 -0
  106. package/dist/services/PRAutomationService.d.ts +2 -2
  107. package/dist/services/PRAutomationService.d.ts.map +1 -1
  108. package/dist/services/PRAutomationService.js +43 -39
  109. package/dist/services/PendingPromptLedger.d.ts +9 -2
  110. package/dist/services/PendingPromptLedger.d.ts.map +1 -1
  111. package/dist/services/PendingPromptLedger.js +31 -6
  112. package/dist/services/ReportGenerator.d.ts +116 -30
  113. package/dist/services/ReportGenerator.d.ts.map +1 -1
  114. package/dist/services/ReportGenerator.js +149 -62
  115. package/dist/services/ReportMergeService.d.ts +95 -0
  116. package/dist/services/ReportMergeService.d.ts.map +1 -0
  117. package/dist/services/ReportMergeService.js +0 -0
  118. package/dist/services/SafetyGuard.d.ts +4 -7
  119. package/dist/services/SafetyGuard.d.ts.map +1 -1
  120. package/dist/services/SafetyGuard.js +29 -106
  121. package/dist/services/SelectorSanitizer.d.ts +52 -0
  122. package/dist/services/SelectorSanitizer.d.ts.map +1 -0
  123. package/dist/services/SelectorSanitizer.js +318 -0
  124. package/dist/services/SnapshotService.js +6 -6
  125. package/dist/services/SourceLinkService.d.ts +2 -2
  126. package/dist/services/SourceLinkService.d.ts.map +1 -1
  127. package/dist/services/SourceLinkService.js +31 -22
  128. package/dist/services/SourceUpdater.d.ts +1 -1
  129. package/dist/services/SourceUpdater.d.ts.map +1 -1
  130. package/dist/services/SourceUpdater.js +44 -26
  131. package/dist/services/TemplateDiffService.d.ts.map +1 -1
  132. package/dist/services/TemplateDiffService.js +18 -15
  133. package/dist/services/TraceBackEngine.d.ts +67 -0
  134. package/dist/services/TraceBackEngine.d.ts.map +1 -0
  135. package/dist/services/TraceBackEngine.js +672 -0
  136. package/dist/services/WorkspaceSnapshotService.d.ts +6 -6
  137. package/dist/services/WorkspaceSnapshotService.d.ts.map +1 -1
  138. package/dist/services/WorkspaceSnapshotService.js +10 -12
  139. package/dist/types/index.d.ts +1 -1
  140. package/dist/utils/DOMUtils.d.ts +18 -2
  141. package/dist/utils/DOMUtils.d.ts.map +1 -1
  142. package/dist/utils/DOMUtils.js +335 -49
  143. package/dist/utils/IsolatedDiff.d.ts +2 -2
  144. package/dist/utils/IsolatedDiff.d.ts.map +1 -1
  145. package/dist/utils/IsolatedDiff.js +13 -10
  146. package/package.json +2 -2
@@ -1,24 +1,15 @@
1
1
  import type { PendingPromptEntry } from "./PendingPromptLedger";
2
- export interface InteractiveReviewDeps {
2
+ export interface PrintReviewDeps {
3
3
  entries: PendingPromptEntry[];
4
- /** Override the prompt tests stub Y/N answers without spinning up readline. */
5
- prompt?: (question: string) => Promise<string>;
6
- /** Override the console sink — tests capture output for assertions. */
4
+ /** Override the console sink - tests capture output for assertions. */
7
5
  log?: (msg: string) => void;
8
- /** Override the filesystem rollback (rewrites contentBefore). Tests stub. */
9
- rollback?: (entry: PendingPromptEntry) => void;
10
- }
11
- export interface InteractiveReviewOutcome {
12
- file: string;
13
- applied: boolean;
6
+ /** Absolute path of the rendered Sela Insights HTML report, when present. */
7
+ reportHtmlPath?: string | null;
14
8
  }
15
9
  /**
16
- * Run the post-run interactive review loop sequentially across every
17
- * pending entry. Returns one outcome per entry so callers can log / test
18
- * the resulting Apply / Reject decisions.
19
- *
20
- * Sequential by design — every prompt `await`s before the next entry is
21
- * rendered. Concurrent prompts on a single TTY would interleave output.
10
+ * Print the post-run review summary. Returns immediately - never blocks
11
+ * on stdin. Safe to call from the reporter's onEnd() in any environment
12
+ * (TTY, piped CI, IDE-integrated runner).
22
13
  */
23
- export declare function runInteractiveReview(deps: InteractiveReviewDeps): Promise<InteractiveReviewOutcome[]>;
14
+ export declare function printHealReview(deps: PrintReviewDeps): void;
24
15
  //# sourceMappingURL=InteractiveReview.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"InteractiveReview.d.ts","sourceRoot":"","sources":["../../src/services/InteractiveReview.ts"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAEhE,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,kBAAkB,EAAE,CAAC;IAC9B,iFAAiF;IACjF,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC/C,uEAAuE;IACvE,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5B,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,kBAAkB,KAAK,IAAI,CAAC;CAChD;AAED,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;CAClB;AAID;;;;;;;GAOG;AACH,wBAAsB,oBAAoB,CACxC,IAAI,EAAE,qBAAqB,GAC1B,OAAO,CAAC,wBAAwB,EAAE,CAAC,CA+CrC"}
1
+ {"version":3,"file":"InteractiveReview.d.ts","sourceRoot":"","sources":["../../src/services/InteractiveReview.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAEhE,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,kBAAkB,EAAE,CAAC;IAC9B,uEAAuE;IACvE,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5B,6EAA6E;IAC7E,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,eAAe,GAAG,IAAI,CA6B3D"}
@@ -1,21 +1,18 @@
1
1
  "use strict";
2
2
  // src/services/InteractiveReview.ts
3
3
  //
4
- // Local DX Interactive CLI review of post-run heals.
4
+ // Local DX - Post-run summary printer (MVP write-and-notify).
5
5
  //
6
- // Runs in the REPORTER (main process) only — workers must never block on
7
- // stdin. The reporter reads cross-process state from
8
- // `PendingPromptLedger` and calls `runInteractiveReview()` with the
9
- // merged entry list.
6
+ // The MVP flow is write-on-disk-then-notify: workers (and the engine)
7
+ // already mutated the source files in-place during execution. The
8
+ // reporter calls printHealReview() at the end purely to display:
9
+ // a banner with the number of mutated files,
10
+ // • per-file isolated unified diff (clean room - never the working tree),
11
+ // • a vscode://file deeplink to the mutated line,
12
+ // • an optional deeplink to the generated HTML insights report.
10
13
  //
11
- // For every entry: prints `[Sela] 🔧 Healed selector in <FileName>`, the
12
- // isolated unified diff, a clickable `vscode://file/<abs>:<line>`
13
- // deeplink, then prompts the developer. 'Y' / 'y' / Enter keeps the
14
- // on-disk fix (the worker already wrote it); 'N' / 'n' rewrites the file
15
- // back to `contentBefore` (the pre-heal state captured by
16
- // `WorkspaceSnapshotService.preWrite()` in the worker).
17
- //
18
- // All I/O is injectable so this can be unit-tested without a real TTY.
14
+ // No prompts. No rollback. No blocking on stdin. SIGINT handling lives
15
+ // in SelaReporter - this module is pure stdout/stderr.
19
16
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
20
17
  if (k2 === undefined) k2 = k;
21
18
  var desc = Object.getOwnPropertyDescriptor(m, k);
@@ -53,55 +50,37 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
53
50
  return (mod && mod.__esModule) ? mod : { "default": mod };
54
51
  };
55
52
  Object.defineProperty(exports, "__esModule", { value: true });
56
- exports.runInteractiveReview = runInteractiveReview;
57
- const fs = __importStar(require("fs"));
53
+ exports.printHealReview = printHealReview;
58
54
  const path = __importStar(require("path"));
59
- const readline = __importStar(require("readline"));
60
55
  const chalk_1 = __importDefault(require("chalk"));
61
56
  const IsolatedDiff_1 = require("../utils/IsolatedDiff");
62
- const DEFAULT_PROMPT = "[Sela] Apply this fix? (Y/n): ";
63
57
  /**
64
- * Run the post-run interactive review loop sequentially across every
65
- * pending entry. Returns one outcome per entry so callers can log / test
66
- * the resulting Apply / Reject decisions.
67
- *
68
- * Sequential by design — every prompt `await`s before the next entry is
69
- * rendered. Concurrent prompts on a single TTY would interleave output.
58
+ * Print the post-run review summary. Returns immediately - never blocks
59
+ * on stdin. Safe to call from the reporter's onEnd() in any environment
60
+ * (TTY, piped CI, IDE-integrated runner).
70
61
  */
71
- async function runInteractiveReview(deps) {
62
+ function printHealReview(deps) {
72
63
  const log = deps.log ?? ((m) => process.stdout.write(m + "\n"));
73
- const prompt = deps.prompt ?? defaultPrompt;
74
- const rollback = deps.rollback ?? defaultRollback;
75
64
  const entries = deps.entries ?? [];
76
- const outcomes = [];
77
- if (entries.length === 0)
78
- return outcomes;
79
- // Headline banner — surfaces even when downstream chalk/readline
80
- // output is suppressed by the host terminal multiplexer (e.g.
81
- // Playwright's reporter-level stdio interception).
65
+ const reportHtmlPath = deps.reportHtmlPath ?? null;
66
+ if (entries.length === 0 && !reportHtmlPath)
67
+ return;
82
68
  log("");
83
- log(chalk_1.default.bold.bgCyan.black(` [Sela] Local DX Review — ${entries.length} pending fix(es) `));
84
- for (const entry of entries) {
85
- renderEntry(entry, log);
86
- const answer = await prompt(chalk_1.default.cyan(DEFAULT_PROMPT));
87
- const decision = parseDecision(answer);
88
- if (decision === "apply") {
89
- log(chalk_1.default.green(`[Sela] ✅ Fix kept on disk: ${entry.relativePath}`));
90
- outcomes.push({ file: entry.absolutePath, applied: true });
91
- }
92
- else {
93
- try {
94
- rollback(entry);
95
- log(chalk_1.default.yellow(`[Sela] ↩️ Rolled back: ${entry.relativePath} (restored to pre-heal state)`));
96
- }
97
- catch (err) {
98
- log(chalk_1.default.red(`[Sela] ⚠️ Rollback failed for ${entry.relativePath}: ${err.message}`));
99
- }
100
- outcomes.push({ file: entry.absolutePath, applied: false });
69
+ if (entries.length > 0) {
70
+ // Count actual healed selectors (a file can hold several), not files.
71
+ const fileCount = entries.length;
72
+ const fixCount = entries.reduce((n, e) => n + (e.healCount ?? 1), 0);
73
+ log(chalk_1.default.bold.bgCyan.black(` [Sela] Local DX Review - ${fixCount} fix${fixCount === 1 ? "" : "es"} applied across ${fileCount} file${fileCount === 1 ? "" : "s"} `));
74
+ for (const entry of entries) {
75
+ renderEntry(entry, log);
101
76
  }
102
77
  }
78
+ if (reportHtmlPath) {
79
+ log("");
80
+ log(chalk_1.default.bold(`[Sela] 📄 Insights report:`));
81
+ log(` ${chalk_1.default.cyan(buildFileLink(reportHtmlPath))}`);
82
+ }
103
83
  log("");
104
- return outcomes;
105
84
  }
106
85
  function renderEntry(entry, log) {
107
86
  const fileName = path.basename(entry.absolutePath);
@@ -113,9 +92,8 @@ function renderEntry(entry, log) {
113
92
  log("");
114
93
  log(colorizeDiff(diff));
115
94
  log("");
116
- log(`👉 To review and accept this fix directly in VS Code, click here:`);
95
+ log(`👉 Open in VS Code:`);
117
96
  log(` ${chalk_1.default.cyan(buildVsCodeLink(entry.absolutePath, entry.lineNumber))}`);
118
- log("");
119
97
  }
120
98
  function colorizeDiff(diff) {
121
99
  if (!diff)
@@ -144,75 +122,7 @@ function buildVsCodeLink(absPath, line) {
144
122
  ? `vscode://file/${normalised}:${line}`
145
123
  : `vscode://file/${normalised}`;
146
124
  }
147
- function parseDecision(raw) {
148
- const trimmed = (raw ?? "").trim().toLowerCase();
149
- // Empty (Enter) ⇒ apply. 'y' / 'yes' ⇒ apply. 'n' / 'no' ⇒ reject.
150
- // Anything else ⇒ apply (default-safe — never silently destroy work).
151
- if (trimmed === "" || trimmed === "y" || trimmed === "yes")
152
- return "apply";
153
- if (trimmed === "n" || trimmed === "no")
154
- return "reject";
155
- return "apply";
156
- }
157
- /**
158
- * Default rollback — rewrites `contentBefore` to disk. Honours the
159
- * `createdBySela` flag so files Sela CREATED from nothing get unlinked
160
- * instead of being replaced with an empty buffer.
161
- */
162
- function defaultRollback(entry) {
163
- if (entry.createdBySela) {
164
- if (fs.existsSync(entry.absolutePath)) {
165
- fs.unlinkSync(entry.absolutePath);
166
- }
167
- return;
168
- }
169
- fs.writeFileSync(entry.absolutePath, entry.contentBefore, "utf8");
170
- }
171
- /**
172
- * Bind `readline` to the live stdin/stdout. Reporter-only — workers must
173
- * never reach this code path (they have no TTY and would deadlock).
174
- *
175
- * Hardening:
176
- * • If stdin is not a TTY (CI, non-interactive shell, Playwright reporter
177
- * context that detached stdio), default to Apply ('y') without blocking.
178
- * • Explicitly `resume()` stdin — Node pauses it by default once any other
179
- * consumer (readline-from-an-earlier-iteration, raw-mode toggles) lets go.
180
- * • Write the prompt to `stderr` so Playwright's stdout summary buffer does
181
- * not overwrite or strip the question line.
182
- */
183
- function defaultPrompt(question) {
184
- const stdinIsTty = process.stdin.isTTY === true;
185
- if (!stdinIsTty) {
186
- process.stderr.write(question + "y (no TTY — auto-apply)\n");
187
- return Promise.resolve("y");
188
- }
189
- return new Promise((resolve) => {
190
- try {
191
- process.stdin.resume();
192
- }
193
- catch {
194
- // Already resumed / not resumable — readline will still try.
195
- }
196
- // `terminal: false` reads stdin line-buffered without raw-mode tricks.
197
- // This is reliable for both real TTYs AND piped harnesses (CI smoke
198
- // tests, child-process drivers). `terminal: true` only adds history /
199
- // arrow-key support, which we don't need for a single Y/N prompt and
200
- // which mis-parses piped input on Windows.
201
- process.stderr.write(question);
202
- const rl = readline.createInterface({
203
- input: process.stdin,
204
- output: process.stderr,
205
- terminal: false,
206
- });
207
- let settled = false;
208
- const finalise = (answer) => {
209
- if (settled)
210
- return;
211
- settled = true;
212
- rl.close();
213
- resolve(answer);
214
- };
215
- rl.once("line", (line) => finalise(line));
216
- rl.once("close", () => finalise("y"));
217
- });
125
+ function buildFileLink(absPath) {
126
+ const normalised = absPath.split(path.sep).join("/");
127
+ return `file://${normalised}`;
218
128
  }
@@ -79,7 +79,7 @@ export interface FixResponse {
79
79
  originalChainHint?: SmartChainSegment[];
80
80
  /**
81
81
  * Confidence score 0-100. Undefined when the model omitted it or returned a
82
- * non-finite value downstream code must treat undefined as "unknown" and
82
+ * non-finite value - downstream code must treat undefined as "unknown" and
83
83
  * render "n/a" rather than synthesising a 0.
84
84
  */
85
85
  confidence?: number;
@@ -71,11 +71,11 @@ Narrowing: filter, first, last, nth
71
71
  You MUST return a "chainSegments" array for the permanent code fix AND a matching "segments" array for the runtime proxy.
72
72
 
73
73
  Build the chain in this priority order:
74
- PRIORITY 1 getByTestId
75
- PRIORITY 2 getByRole (ALWAYS include "name" if it exists in the DOM to ensure uniqueness)
76
- PRIORITY 3 getByLabel
77
- PRIORITY 4 getByText (use exact: true if possible)
78
- NARROWING Use filter({ hasText: "..." }) to isolate an element within a repeated container.
74
+ PRIORITY 1 - getByTestId
75
+ PRIORITY 2 - getByRole (ALWAYS include "name" if it exists in the DOM to ensure uniqueness)
76
+ PRIORITY 3 - getByLabel
77
+ PRIORITY 4 - getByText (use exact: true if possible)
78
+ NARROWING - Use filter({ hasText: "..." }) to isolate an element within a repeated container.
79
79
 
80
80
  ### PROXY SELECTOR CONSTRUCTION (segments field)
81
81
  The "segments[0].selector" string is used for an immediate "live" retry.
@@ -90,10 +90,10 @@ Preserve the original code's structural pattern from "originalChainHint":
90
90
 
91
91
  ### TEMPLATE LITERAL / FUNCTION SELECTOR PRESERVATION
92
92
  When the failing selector originates from a Template Literal or a function returning a template,
93
- preserve the attribute structure update the value inside the attribute rather than removing it.
93
+ preserve the attribute structure - update the value inside the attribute rather than removing it.
94
94
  For chain selectors: if the original was "#section-B >> input[data-role='user_legacy']"
95
95
  and the DNA shows data-role changed to "user_active", return "#section-B >> input[data-role='user_active']".
96
- NEVER simplify to "#section-B >> input" that loses the attribute and breaks automated source healing.
96
+ NEVER simplify to "#section-B >> input" - that loses the attribute and breaks automated source healing.
97
97
  The healing system depends on the attribute value being present in your response to patch the template variable.
98
98
 
99
99
  ### TYPE SAFETY RULES
@@ -105,21 +105,21 @@ If the element's text changed, you MUST include:
105
105
  "contentChange": { "oldText": "<previous>", "newText": "<current>" }
106
106
 
107
107
  ### CONFIDENCE SCORE (MANDATORY)
108
- You MUST include a numeric "confidence" field an INTEGER in the range 0-100
108
+ You MUST include a numeric "confidence" field - an INTEGER in the range 0-100 -
109
109
  representing how certain you are that the returned chain will resolve the
110
110
  intended element on the next test run. Use this rubric:
111
- - 90-100: stable id, data-testid, or unique role+name zero ambiguity in the DOM.
111
+ - 90-100: stable id, data-testid, or unique role+name - zero ambiguity in the DOM.
112
112
  - 70-89: strong semantic match, but some structural drift or competing candidates.
113
- - 50-69: best-guess match multiple plausible candidates required disambiguation.
114
- - 1-49: speculative match return this only when no better candidate exists.
113
+ - 50-69: best-guess match - multiple plausible candidates required disambiguation.
114
+ - 1-49: speculative match - return this only when no better candidate exists.
115
115
  NEVER omit this field. NEVER return it as a string, boolean, or null.
116
- Reports rendered downstream will show "n/a" when this field is missing which
116
+ Reports rendered downstream will show "n/a" when this field is missing - which
117
117
  is a clear failure signal to the developer reviewing the heal.
118
118
 
119
119
  ### MANDATORY OUTPUT FORMAT
120
120
  Return ONLY valid JSON.
121
121
 
122
- Example output Identifying a specific button in a list row:
122
+ Example output - Identifying a specific button in a list row:
123
123
  {
124
124
  "status": "FIXED",
125
125
  "confidence": 92,
@@ -215,7 +215,7 @@ class LLMService {
215
215
  throw new SelaError_1.SelaError({
216
216
  subsystem: "LLMService",
217
217
  code: SelaError_1.LLM_SERVICE_CODES.NO_API_KEY,
218
- reason: "ANTHROPIC_API_KEY not found pass an `anthropic` client or `apiKey` to LLMService(), or set the env var",
218
+ reason: "ANTHROPIC_API_KEY not found - pass an `anthropic` client or `apiKey` to LLMService(), or set the env var",
219
219
  });
220
220
  }
221
221
  this.anthropic = new sdk_1.default({ apiKey });
@@ -339,7 +339,7 @@ ${request.targetIntent}
339
339
  ### PREVIOUS DNA (Element Fingerprint):
340
340
  ${dnaSummary && Object.keys(dnaSummary).length > 0
341
341
  ? JSON.stringify(dnaSummary, null, 2)
342
- : "NO DNA (First run infer from selector and DOM)"}
342
+ : "NO DNA (First run - infer from selector and DOM)"}
343
343
 
344
344
  ### CURRENT DOM SNIPPET:
345
345
  ${request.currentDom}
@@ -351,7 +351,7 @@ ${request.currentDom}
351
351
  4. Populate "contentChange" if the element's visible text changed.
352
352
  5. Also populate "segments" (legacy format) for backward compatibility.
353
353
 
354
- Return ONLY a raw JSON object no markdown, no preamble.`;
354
+ Return ONLY a raw JSON object - no markdown, no preamble.`;
355
355
  try {
356
356
  const msg = await this.anthropic.messages.create({
357
357
  model: this.model,
@@ -0,0 +1,55 @@
1
+ import { AnchorKey, SourceSpan } from "./AnchorResolver";
2
+ /**
3
+ * A single edit. `replace` edits carry an `anchor` and are RE-RESOLVED at
4
+ * apply-time (span recomputed from the fresh parse). `insert` edits (forks)
5
+ * carry a literal `span` computed against the same content being applied.
6
+ */
7
+ export interface AnchoredEdit {
8
+ kind: "replace" | "insert";
9
+ replacement: string;
10
+ /** Replace target — re-resolved via resolveAnchor at apply time. */
11
+ anchor?: AnchorKey;
12
+ /** Literal span for inserts (and as a fallback for anchorless replaces). */
13
+ span?: SourceSpan;
14
+ /**
15
+ * Require the replacement to be a Locator-returning expression. Defaults to
16
+ * true for `replace`. Set false for inserts / non-locator edits.
17
+ */
18
+ requireLocator?: boolean;
19
+ }
20
+ export interface FileBatch {
21
+ filePath: string;
22
+ edits: AnchoredEdit[];
23
+ }
24
+ export type ApplyStatus = "applied" | "rolled-back" | "no-op" | "skipped";
25
+ export interface ApplyOutcome {
26
+ filePath: string;
27
+ status: ApplyStatus;
28
+ reason: string;
29
+ written: boolean;
30
+ /** Content after the run — the new content when applied, else the original. */
31
+ finalContent: string;
32
+ /** Edits that could not be applied (stale anchor, type-incompat, …). */
33
+ skipped: Array<{
34
+ reason: string;
35
+ }>;
36
+ }
37
+ export interface ApplyOptions {
38
+ /** When false, compute + gate but never touch disk. Default true. */
39
+ write?: boolean;
40
+ /** Honour SELA_DRY_RUN by leaving disk untouched. Default true. */
41
+ respectDryRun?: boolean;
42
+ }
43
+ export declare class MutationApplier {
44
+ /** Apply many batches; each file is processed independently. */
45
+ static applyBatches(batches: FileBatch[], opts?: ApplyOptions): ApplyOutcome[];
46
+ static applyFileBatch(batch: FileBatch, opts?: ApplyOptions): ApplyOutcome;
47
+ }
48
+ /**
49
+ * Cheap return-type pre-check. The replacement must be an expression whose
50
+ * OUTERMOST call is a Locator-returning Playwright method (locator / getBy* /
51
+ * frameLocator / nth / first / last / filter / and / or). Rejects actions
52
+ * (.click()), string queries (.title(), .textContent()), bare literals, etc.
53
+ */
54
+ export declare function isLocatorReturningExpr(text: string): boolean;
55
+ //# sourceMappingURL=MutationApplier.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MutationApplier.d.ts","sourceRoot":"","sources":["../../src/services/MutationApplier.ts"],"names":[],"mappings":"AA2BA,OAAO,EACL,SAAS,EAET,UAAU,EACX,MAAM,kBAAkB,CAAC;AAS1B;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,SAAS,GAAG,QAAQ,CAAC;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,oEAAoE;IACpE,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,4EAA4E;IAC5E,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB;;;OAGG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,YAAY,EAAE,CAAC;CACvB;AAED,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,aAAa,GAAG,OAAO,GAAG,SAAS,CAAC;AAE1E,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,WAAW,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,+EAA+E;IAC/E,YAAY,EAAE,MAAM,CAAC;IACrB,wEAAwE;IACxE,OAAO,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACpC;AAED,MAAM,WAAW,YAAY;IAC3B,qEAAqE;IACrE,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,mEAAmE;IACnE,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAeD,qBAAa,eAAe;IAC1B,gEAAgE;IAChE,MAAM,CAAC,YAAY,CACjB,OAAO,EAAE,SAAS,EAAE,EACpB,IAAI,GAAE,YAAiB,GACtB,YAAY,EAAE;IAIjB,MAAM,CAAC,cAAc,CACnB,KAAK,EAAE,SAAS,EAChB,IAAI,GAAE,YAAiB,GACtB,YAAY;CA6JhB;AA2CD;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAkB5D"}