sela-core 1.0.6 → 1.0.7
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.
- package/README.md +1 -1
- package/dist/config/ConfigLoader.d.ts +1 -0
- package/dist/config/ConfigLoader.d.ts.map +1 -1
- package/dist/config/ConfigLoader.js +10 -0
- package/dist/config/DryRunGuard.d.ts +14 -0
- package/dist/config/DryRunGuard.d.ts.map +1 -0
- package/dist/config/DryRunGuard.js +79 -0
- package/dist/config/SelaConfig.d.ts +15 -1
- package/dist/config/SelaConfig.d.ts.map +1 -1
- package/dist/config/SelaConfig.js +27 -1
- package/dist/engine/SelaEngine.d.ts +12 -9
- package/dist/engine/SelaEngine.d.ts.map +1 -1
- package/dist/engine/SelaEngine.js +249 -72
- package/dist/errors/SelaError.d.ts +133 -0
- package/dist/errors/SelaError.d.ts.map +1 -0
- package/dist/errors/SelaError.js +155 -0
- package/dist/fixtures/expectProxy.d.ts.map +1 -1
- package/dist/fixtures/expectProxy.js +7 -0
- package/dist/fixtures/index.d.ts +7 -1
- package/dist/fixtures/index.d.ts.map +1 -1
- package/dist/fixtures/index.js +15 -0
- package/dist/fixtures/proxyTag.d.ts +12 -0
- package/dist/fixtures/proxyTag.d.ts.map +1 -0
- package/dist/fixtures/proxyTag.js +45 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -1
- package/dist/reporter/SelaReporter.d.ts +110 -0
- package/dist/reporter/SelaReporter.d.ts.map +1 -0
- package/dist/reporter/SelaReporter.js +328 -0
- package/dist/services/ASTSourceUpdater.d.ts +19 -0
- package/dist/services/ASTSourceUpdater.d.ts.map +1 -1
- package/dist/services/ASTSourceUpdater.js +173 -29
- package/dist/services/HealReportService.d.ts +42 -0
- package/dist/services/HealReportService.d.ts.map +1 -1
- package/dist/services/HealReportService.js +80 -1
- package/dist/services/HealingCacheService.d.ts +135 -0
- package/dist/services/HealingCacheService.d.ts.map +1 -0
- package/dist/services/HealingCacheService.js +460 -0
- package/dist/services/InitializerUpdater.d.ts.map +1 -1
- package/dist/services/InitializerUpdater.js +20 -1
- package/dist/services/IntentAuditor.d.ts +18 -1
- package/dist/services/IntentAuditor.d.ts.map +1 -1
- package/dist/services/IntentAuditor.js +19 -6
- package/dist/services/InteractiveReview.d.ts +24 -0
- package/dist/services/InteractiveReview.d.ts.map +1 -0
- package/dist/services/InteractiveReview.js +218 -0
- package/dist/services/LLMService.d.ts +17 -1
- package/dist/services/LLMService.d.ts.map +1 -1
- package/dist/services/LLMService.js +18 -8
- package/dist/services/PRAutomationService.d.ts +14 -1
- package/dist/services/PRAutomationService.d.ts.map +1 -1
- package/dist/services/PRAutomationService.js +260 -69
- package/dist/services/PendingPromptLedger.d.ts +44 -0
- package/dist/services/PendingPromptLedger.d.ts.map +1 -0
- package/dist/services/PendingPromptLedger.js +180 -0
- package/dist/services/ReportGenerator.d.ts +70 -0
- package/dist/services/ReportGenerator.d.ts.map +1 -0
- package/dist/services/ReportGenerator.js +191 -0
- package/dist/services/SafetyGuard.d.ts +27 -1
- package/dist/services/SafetyGuard.d.ts.map +1 -1
- package/dist/services/SafetyGuard.js +58 -12
- package/dist/services/SourceUpdater.d.ts.map +1 -1
- package/dist/services/SourceUpdater.js +44 -13
- package/dist/services/WorkspaceSnapshotService.d.ts +71 -0
- package/dist/services/WorkspaceSnapshotService.d.ts.map +1 -0
- package/dist/services/WorkspaceSnapshotService.js +202 -0
- package/dist/utils/IsolatedDiff.d.ts +45 -0
- package/dist/utils/IsolatedDiff.d.ts.map +1 -0
- package/dist/utils/IsolatedDiff.js +379 -0
- package/package.json +71 -67
|
@@ -95,14 +95,27 @@ RESPONSE FORMAT (strict JSON, no other text):
|
|
|
95
95
|
"reason": "<one sentence explaining the verdict>",
|
|
96
96
|
"inversionType": "polarity" | "quantitative" | "state" | "identity" | null
|
|
97
97
|
}`;
|
|
98
|
-
|
|
99
|
-
// IntentAuditor
|
|
100
|
-
// ═══════════════════════════════════════════════════════════════════
|
|
98
|
+
const DEFAULT_AUDITOR_MODEL = "claude-haiku-4-5-20251001";
|
|
101
99
|
class IntentAuditor {
|
|
102
100
|
anthropic;
|
|
103
101
|
disabled;
|
|
104
|
-
|
|
105
|
-
|
|
102
|
+
model;
|
|
103
|
+
constructor(opts = {}) {
|
|
104
|
+
this.model = opts.model ?? DEFAULT_AUDITOR_MODEL;
|
|
105
|
+
// Explicit `null` from the caller forces disabled mode — used by tests.
|
|
106
|
+
if (opts.anthropic === null) {
|
|
107
|
+
this.anthropic = null;
|
|
108
|
+
this.disabled = true;
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
// Caller supplied a ready-to-use client → use it as-is.
|
|
112
|
+
if (opts.anthropic !== undefined) {
|
|
113
|
+
this.anthropic = opts.anthropic;
|
|
114
|
+
this.disabled = false;
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
// Otherwise fall back to env-based construction (legacy default).
|
|
118
|
+
const apiKey = opts.apiKey ?? process.env.ANTHROPIC_API_KEY;
|
|
106
119
|
if (!apiKey) {
|
|
107
120
|
console.warn("[IntentAuditor] ANTHROPIC_API_KEY not set — auditor disabled, will return SUSPICIOUS for all audited heals");
|
|
108
121
|
this.anthropic = null;
|
|
@@ -128,7 +141,7 @@ class IntentAuditor {
|
|
|
128
141
|
.join("\n");
|
|
129
142
|
try {
|
|
130
143
|
const response = await this.anthropic.messages.create({
|
|
131
|
-
model:
|
|
144
|
+
model: this.model,
|
|
132
145
|
max_tokens: 256,
|
|
133
146
|
temperature: 0.1,
|
|
134
147
|
system: SYSTEM_PROMPT,
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { PendingPromptEntry } from "./PendingPromptLedger";
|
|
2
|
+
export interface InteractiveReviewDeps {
|
|
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. */
|
|
7
|
+
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;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
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.
|
|
22
|
+
*/
|
|
23
|
+
export declare function runInteractiveReview(deps: InteractiveReviewDeps): Promise<InteractiveReviewOutcome[]>;
|
|
24
|
+
//# sourceMappingURL=InteractiveReview.d.ts.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// src/services/InteractiveReview.ts
|
|
3
|
+
//
|
|
4
|
+
// Local DX — Interactive CLI review of post-run heals.
|
|
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.
|
|
10
|
+
//
|
|
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.
|
|
19
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
20
|
+
if (k2 === undefined) k2 = k;
|
|
21
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
22
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
23
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
24
|
+
}
|
|
25
|
+
Object.defineProperty(o, k2, desc);
|
|
26
|
+
}) : (function(o, m, k, k2) {
|
|
27
|
+
if (k2 === undefined) k2 = k;
|
|
28
|
+
o[k2] = m[k];
|
|
29
|
+
}));
|
|
30
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
31
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
32
|
+
}) : function(o, v) {
|
|
33
|
+
o["default"] = v;
|
|
34
|
+
});
|
|
35
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
36
|
+
var ownKeys = function(o) {
|
|
37
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
38
|
+
var ar = [];
|
|
39
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
40
|
+
return ar;
|
|
41
|
+
};
|
|
42
|
+
return ownKeys(o);
|
|
43
|
+
};
|
|
44
|
+
return function (mod) {
|
|
45
|
+
if (mod && mod.__esModule) return mod;
|
|
46
|
+
var result = {};
|
|
47
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
48
|
+
__setModuleDefault(result, mod);
|
|
49
|
+
return result;
|
|
50
|
+
};
|
|
51
|
+
})();
|
|
52
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
53
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
54
|
+
};
|
|
55
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
56
|
+
exports.runInteractiveReview = runInteractiveReview;
|
|
57
|
+
const fs = __importStar(require("fs"));
|
|
58
|
+
const path = __importStar(require("path"));
|
|
59
|
+
const readline = __importStar(require("readline"));
|
|
60
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
61
|
+
const IsolatedDiff_1 = require("../utils/IsolatedDiff");
|
|
62
|
+
const DEFAULT_PROMPT = "[Sela] Apply this fix? (Y/n): ";
|
|
63
|
+
/**
|
|
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.
|
|
70
|
+
*/
|
|
71
|
+
async function runInteractiveReview(deps) {
|
|
72
|
+
const log = deps.log ?? ((m) => process.stdout.write(m + "\n"));
|
|
73
|
+
const prompt = deps.prompt ?? defaultPrompt;
|
|
74
|
+
const rollback = deps.rollback ?? defaultRollback;
|
|
75
|
+
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).
|
|
82
|
+
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 });
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
log("");
|
|
104
|
+
return outcomes;
|
|
105
|
+
}
|
|
106
|
+
function renderEntry(entry, log) {
|
|
107
|
+
const fileName = path.basename(entry.absolutePath);
|
|
108
|
+
const diff = (0, IsolatedDiff_1.generateUnifiedDiff)(entry.contentBefore, entry.contentAfter, {
|
|
109
|
+
filePath: entry.relativePath,
|
|
110
|
+
});
|
|
111
|
+
log("");
|
|
112
|
+
log(chalk_1.default.bold(`[Sela] 🔧 Healed selector in ${fileName}`));
|
|
113
|
+
log("");
|
|
114
|
+
log(colorizeDiff(diff));
|
|
115
|
+
log("");
|
|
116
|
+
log(`👉 To review and accept this fix directly in VS Code, click here:`);
|
|
117
|
+
log(` ${chalk_1.default.cyan(buildVsCodeLink(entry.absolutePath, entry.lineNumber))}`);
|
|
118
|
+
log("");
|
|
119
|
+
}
|
|
120
|
+
function colorizeDiff(diff) {
|
|
121
|
+
if (!diff)
|
|
122
|
+
return chalk_1.default.dim("(no diff)");
|
|
123
|
+
return diff
|
|
124
|
+
.split("\n")
|
|
125
|
+
.map((row) => {
|
|
126
|
+
if (row.startsWith("+++") ||
|
|
127
|
+
row.startsWith("---") ||
|
|
128
|
+
row.startsWith("diff --git")) {
|
|
129
|
+
return chalk_1.default.bold(row);
|
|
130
|
+
}
|
|
131
|
+
if (row.startsWith("@@"))
|
|
132
|
+
return chalk_1.default.cyan(row);
|
|
133
|
+
if (row.startsWith("+"))
|
|
134
|
+
return chalk_1.default.green(row);
|
|
135
|
+
if (row.startsWith("-"))
|
|
136
|
+
return chalk_1.default.red(row);
|
|
137
|
+
return row;
|
|
138
|
+
})
|
|
139
|
+
.join("\n");
|
|
140
|
+
}
|
|
141
|
+
function buildVsCodeLink(absPath, line) {
|
|
142
|
+
const normalised = absPath.split(path.sep).join("/");
|
|
143
|
+
return line && line > 0
|
|
144
|
+
? `vscode://file/${normalised}:${line}`
|
|
145
|
+
: `vscode://file/${normalised}`;
|
|
146
|
+
}
|
|
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
|
+
});
|
|
218
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import Anthropic from "@anthropic-ai/sdk";
|
|
1
2
|
import { FixRequest } from "../types";
|
|
2
3
|
export interface SelectorSegment {
|
|
3
4
|
type: "frame" | "element";
|
|
@@ -86,9 +87,24 @@ export interface FixResponse {
|
|
|
86
87
|
new_selector?: string | null;
|
|
87
88
|
contentChange?: ContentChange;
|
|
88
89
|
}
|
|
90
|
+
/**
|
|
91
|
+
* Constructor options for LLMService.
|
|
92
|
+
*
|
|
93
|
+
* - `anthropic`: pre-built Anthropic client. Tests use this to inject a
|
|
94
|
+
* mock client without touching env vars or network.
|
|
95
|
+
* - `apiKey`: override the env-based ANTHROPIC_API_KEY when constructing
|
|
96
|
+
* the default client.
|
|
97
|
+
* - `model`: override the Sonnet model id (lets tests pin the SDK call).
|
|
98
|
+
*/
|
|
99
|
+
export interface LLMServiceOptions {
|
|
100
|
+
anthropic?: Anthropic;
|
|
101
|
+
apiKey?: string;
|
|
102
|
+
model?: string;
|
|
103
|
+
}
|
|
89
104
|
export declare class LLMService {
|
|
90
105
|
private anthropic;
|
|
91
|
-
|
|
106
|
+
private readonly model;
|
|
107
|
+
constructor(opts?: LLMServiceOptions);
|
|
92
108
|
private cleanJson;
|
|
93
109
|
/**
|
|
94
110
|
* Attempts to infer contentChange when the AI did not return the field.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LLMService.d.ts","sourceRoot":"","sources":["../../src/services/LLMService.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"LLMService.d.ts","sourceRoot":"","sources":["../../src/services/LLMService.ts"],"names":[],"mappings":"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"}
|
|
@@ -39,6 +39,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
39
39
|
exports.LLMService = void 0;
|
|
40
40
|
const sdk_1 = __importDefault(require("@anthropic-ai/sdk"));
|
|
41
41
|
const dotenv = __importStar(require("dotenv"));
|
|
42
|
+
const SelaError_1 = require("../errors/SelaError");
|
|
42
43
|
dotenv.config();
|
|
43
44
|
// ─────────────────────────────────────────────────────────────────
|
|
44
45
|
// SYSTEM PROMPT
|
|
@@ -199,15 +200,24 @@ function inferOriginalChainHint(selector) {
|
|
|
199
200
|
}
|
|
200
201
|
return segments;
|
|
201
202
|
}
|
|
202
|
-
|
|
203
|
-
// LLM SERVICE
|
|
204
|
-
// ─────────────────────────────────────────────────────────────────
|
|
203
|
+
const DEFAULT_LLM_MODEL = "claude-sonnet-4-20250514";
|
|
205
204
|
class LLMService {
|
|
206
205
|
anthropic;
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
206
|
+
model;
|
|
207
|
+
constructor(opts = {}) {
|
|
208
|
+
this.model = opts.model ?? DEFAULT_LLM_MODEL;
|
|
209
|
+
if (opts.anthropic) {
|
|
210
|
+
this.anthropic = opts.anthropic;
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
const apiKey = opts.apiKey ?? process.env.ANTHROPIC_API_KEY ?? "";
|
|
214
|
+
if (!apiKey) {
|
|
215
|
+
throw new SelaError_1.SelaError({
|
|
216
|
+
subsystem: "LLMService",
|
|
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",
|
|
219
|
+
});
|
|
220
|
+
}
|
|
211
221
|
this.anthropic = new sdk_1.default({ apiKey });
|
|
212
222
|
}
|
|
213
223
|
cleanJson(text) {
|
|
@@ -344,7 +354,7 @@ ${request.currentDom}
|
|
|
344
354
|
Return ONLY a raw JSON object — no markdown, no preamble.`;
|
|
345
355
|
try {
|
|
346
356
|
const msg = await this.anthropic.messages.create({
|
|
347
|
-
model:
|
|
357
|
+
model: this.model,
|
|
348
358
|
max_tokens: 1024,
|
|
349
359
|
system: SYSTEM_PROMPT_TEXT +
|
|
350
360
|
"\nIMPORTANT: Respond ONLY with a raw JSON object.",
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ResolvedPRAutomation, PRStrategy, BugAction } from "../config/SelaConfig";
|
|
2
2
|
import type { HealedEvent, ProtectedEvent } from "./HealReportService";
|
|
3
|
+
import { type WorkspaceSnapshotService } from "./WorkspaceSnapshotService";
|
|
3
4
|
export interface BranchInfo {
|
|
4
5
|
branch: string | null;
|
|
5
6
|
isPR: boolean;
|
|
@@ -32,6 +33,18 @@ export interface ExecutionResult {
|
|
|
32
33
|
branchPushed: string | null;
|
|
33
34
|
errors: string[];
|
|
34
35
|
}
|
|
35
|
-
|
|
36
|
+
/**
|
|
37
|
+
* Clean-Room execution lifecycle.
|
|
38
|
+
*
|
|
39
|
+
* The legacy flow committed the developer's whole dirty working tree onto
|
|
40
|
+
* the PR branch — polluting reviewers with `test.only`, commented debug
|
|
41
|
+
* blocks, etc. This refactor isolates Sela's specific AST changes onto an
|
|
42
|
+
* ephemeral branch, leaves the developer's local workspace byte-identical
|
|
43
|
+
* to its pre-Sela state, and is wrapped in a try/finally that restores
|
|
44
|
+
* the workspace even on mid-flight crashes.
|
|
45
|
+
*/
|
|
46
|
+
export declare function execute(cfg: ResolvedPRAutomation, decision: StrategyDecision, heals: HealedEvent[], branchInfo: BranchInfo, ctx: ExecuteContext,
|
|
47
|
+
/** Injectable for tests — defaults to the module singleton. */
|
|
48
|
+
workspace?: WorkspaceSnapshotService): Promise<ExecutionResult>;
|
|
36
49
|
export declare function handleBugDetected(action: BugAction, events: ProtectedEvent[], branchInfo: BranchInfo, ctx: ExecuteContext): Promise<void>;
|
|
37
50
|
//# sourceMappingURL=PRAutomationService.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PRAutomationService.d.ts","sourceRoot":"","sources":["../../src/services/PRAutomationService.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EACV,oBAAoB,EACpB,UAAU,EACV,SAAS,EACV,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EACV,WAAW,EACX,cAAc,EACf,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"PRAutomationService.d.ts","sourceRoot":"","sources":["../../src/services/PRAutomationService.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EACV,oBAAoB,EACpB,UAAU,EACV,SAAS,EACV,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EACV,WAAW,EACX,cAAc,EACf,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAGL,KAAK,wBAAwB,EAC9B,MAAM,4BAA4B,CAAC;AAUpC,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,IAAI,EAAE,OAAO,CAAC;IACd,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,MAAM,EAAE,KAAK,GAAG,KAAK,GAAG,MAAM,CAAC;CAChC;AAED,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,UAAU,CAAC;IACtB,UAAU,EAAE,UAAU,CAAC;IACvB,eAAe,EAAE,kBAAkB,GAAG,gBAAgB,GAAG,aAAa,GAAG,IAAI,CAAC;IAC9E;;;;OAIG;IACH,eAAe,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,6EAA6E;IAC7E,oBAAoB,EAAE,MAAM,GAAG,SAAS,CAAC;CAC1C;AAED,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B;AA2ED,wBAAgB,YAAY,CAAC,GAAG,GAAE,MAAsB,GAAG,UAAU,CA8EpE;AAMD,wBAAgB,uBAAuB,CACrC,GAAG,EAAE,oBAAoB,EACzB,MAAM,EAAE,MAAM,GAAG,IAAI,EACrB,KAAK,EAAE,WAAW,EAAE,GACnB,gBAAgB,CAgElB;AAMD,wBAAgB,MAAM,CAAC,CAAC,EAAE,OAAO,GAAG,MAAM,CAEzC;AAuFD,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,UAAU,CAAC;IACtB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED;;;;;;;;;GASG;AACH,wBAAsB,OAAO,CAC3B,GAAG,EAAE,oBAAoB,EACzB,QAAQ,EAAE,gBAAgB,EAC1B,KAAK,EAAE,WAAW,EAAE,EACpB,UAAU,EAAE,UAAU,EACtB,GAAG,EAAE,cAAc;AACnB,+DAA+D;AAC/D,SAAS,GAAE,wBAAkD,GAC5D,OAAO,CAAC,eAAe,CAAC,CAkG1B;AA4SD,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,cAAc,EAAE,EACxB,UAAU,EAAE,UAAU,EACtB,GAAG,EAAE,cAAc,GAClB,OAAO,CAAC,IAAI,CAAC,CA0Ff"}
|