sela-core 1.0.7 → 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.
- package/dist/cli/ErrorHandler.d.ts +1 -1
- package/dist/cli/ErrorHandler.js +14 -14
- package/dist/cli/commands/bulk.d.ts +1 -1
- package/dist/cli/commands/bulk.d.ts.map +1 -1
- package/dist/cli/commands/bulk.js +51 -33
- package/dist/cli/commands/init.d.ts +1 -1
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +26 -18
- package/dist/cli/commands/showReport.d.ts +1 -1
- package/dist/cli/commands/showReport.d.ts.map +1 -1
- package/dist/cli/commands/showReport.js +12 -10
- package/dist/cli/commands/status.d.ts +1 -1
- package/dist/cli/commands/status.d.ts.map +1 -1
- package/dist/cli/commands/status.js +8 -8
- package/dist/cli/commands/sync.d.ts +1 -1
- package/dist/cli/commands/sync.d.ts.map +1 -1
- package/dist/cli/commands/sync.js +38 -30
- package/dist/cli/index.js +9 -9
- package/dist/cli/ui/DnaTable.d.ts +1 -1
- package/dist/cli/ui/DnaTable.d.ts.map +1 -1
- package/dist/cli/ui/DnaTable.js +23 -16
- package/dist/cli/ui/RefactorWizard.d.ts +3 -3
- package/dist/cli/ui/RefactorWizard.d.ts.map +1 -1
- package/dist/cli/ui/RefactorWizard.js +108 -82
- package/dist/config/ConfigLoader.d.ts +2 -2
- package/dist/config/ConfigLoader.d.ts.map +1 -1
- package/dist/config/ConfigLoader.js +15 -15
- package/dist/config/DryRunGuard.d.ts +1 -1
- package/dist/config/DryRunGuard.js +7 -7
- package/dist/config/SelaConfig.d.ts +12 -16
- package/dist/config/SelaConfig.d.ts.map +1 -1
- package/dist/config/SelaConfig.js +32 -26
- package/dist/engine/HealingRegistry.d.ts +1 -1
- package/dist/engine/HealingRegistry.js +3 -3
- package/dist/engine/SelaEngine.d.ts +1 -1
- package/dist/engine/SelaEngine.d.ts.map +1 -1
- package/dist/engine/SelaEngine.js +63 -49
- package/dist/errors/SelaError.d.ts +0 -1
- package/dist/errors/SelaError.d.ts.map +1 -1
- package/dist/errors/SelaError.js +6 -7
- package/dist/fixtures/expectProxy.d.ts +1 -1
- package/dist/fixtures/expectProxy.js +11 -11
- package/dist/fixtures/index.js +14 -14
- package/dist/fixtures/proxyTag.d.ts +1 -1
- package/dist/fixtures/proxyTag.js +3 -3
- package/dist/index.d.ts +8 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -4
- package/dist/reporter/SelaReporter.d.ts +45 -12
- package/dist/reporter/SelaReporter.d.ts.map +1 -1
- package/dist/reporter/SelaReporter.js +139 -39
- package/dist/services/ASTSourceUpdater.d.ts +3 -3
- package/dist/services/ASTSourceUpdater.d.ts.map +1 -1
- package/dist/services/ASTSourceUpdater.js +92 -68
- package/dist/services/ArgumentTypeAnalyzer.js +7 -7
- package/dist/services/BlastRadiusAnalyzer.d.ts.map +1 -1
- package/dist/services/BlastRadiusAnalyzer.js +5 -2
- package/dist/services/ChainValidator.js +22 -22
- package/dist/services/CrossFileHealer.js +7 -7
- package/dist/services/DefinitionTracer.d.ts +1 -1
- package/dist/services/DefinitionTracer.d.ts.map +1 -1
- package/dist/services/DefinitionTracer.js +28 -13
- package/dist/services/DnaEditorService.d.ts +2 -2
- package/dist/services/DnaEditorService.d.ts.map +1 -1
- package/dist/services/DnaEditorService.js +81 -62
- package/dist/services/DnaIndexService.d.ts +1 -1
- package/dist/services/DnaIndexService.d.ts.map +1 -1
- package/dist/services/DnaIndexService.js +14 -14
- package/dist/services/HealReportService.d.ts +6 -6
- package/dist/services/HealReportService.d.ts.map +1 -1
- package/dist/services/HealReportService.js +33 -29
- package/dist/services/HealingAdvisory.js +1 -1
- package/dist/services/HealingCacheService.d.ts +8 -8
- package/dist/services/HealingCacheService.d.ts.map +1 -1
- package/dist/services/HealingCacheService.js +24 -24
- package/dist/services/HealthReportService.d.ts +1 -1
- package/dist/services/HealthReportService.d.ts.map +1 -1
- package/dist/services/HealthReportService.js +25 -25
- package/dist/services/InitializerUpdater.js +2 -2
- package/dist/services/IntentAuditor.d.ts.map +1 -1
- package/dist/services/IntentAuditor.js +32 -21
- package/dist/services/InteractiveReview.d.ts +8 -17
- package/dist/services/InteractiveReview.d.ts.map +1 -1
- package/dist/services/InteractiveReview.js +31 -124
- package/dist/services/LLMService.d.ts +1 -1
- package/dist/services/LLMService.js +16 -16
- package/dist/services/PRAutomationService.d.ts +2 -2
- package/dist/services/PRAutomationService.d.ts.map +1 -1
- package/dist/services/PRAutomationService.js +43 -39
- package/dist/services/PendingPromptLedger.d.ts +2 -2
- package/dist/services/PendingPromptLedger.js +6 -6
- package/dist/services/ReportGenerator.d.ts +2 -2
- package/dist/services/ReportGenerator.d.ts.map +1 -1
- package/dist/services/ReportGenerator.js +5 -5
- package/dist/services/SafetyGuard.d.ts +3 -6
- package/dist/services/SafetyGuard.d.ts.map +1 -1
- package/dist/services/SafetyGuard.js +29 -106
- package/dist/services/SnapshotService.js +5 -5
- package/dist/services/SourceLinkService.d.ts +2 -2
- package/dist/services/SourceLinkService.d.ts.map +1 -1
- package/dist/services/SourceLinkService.js +31 -22
- package/dist/services/SourceUpdater.d.ts +1 -1
- package/dist/services/SourceUpdater.d.ts.map +1 -1
- package/dist/services/SourceUpdater.js +27 -26
- package/dist/services/TemplateDiffService.d.ts.map +1 -1
- package/dist/services/TemplateDiffService.js +18 -15
- package/dist/services/WorkspaceSnapshotService.d.ts +6 -6
- package/dist/services/WorkspaceSnapshotService.d.ts.map +1 -1
- package/dist/services/WorkspaceSnapshotService.js +10 -12
- package/dist/types/index.d.ts +1 -1
- package/dist/utils/IsolatedDiff.d.ts +2 -2
- package/dist/utils/IsolatedDiff.d.ts.map +1 -1
- package/dist/utils/IsolatedDiff.js +13 -10
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HealthReportService.d.ts","sourceRoot":"","sources":["../../src/services/HealthReportService.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAa,YAAY,EAAE,MAAM,YAAY,CAAC;AAEpE,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,KAAK,EAAE,QAAQ,GAAG,YAAY,CAAC;IACxC,MAAM,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAAC;CACtC;
|
|
1
|
+
{"version":3,"file":"HealthReportService.d.ts","sourceRoot":"","sources":["../../src/services/HealthReportService.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAa,YAAY,EAAE,MAAM,YAAY,CAAC;AAEpE,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,KAAK,EAAE,QAAQ,GAAG,YAAY,CAAC;IACxC,MAAM,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAAC;CACtC;AAoBD,qBAAa,mBAAoB,YAAW,oBAAoB;IAC9D,QAAQ,CAAC,KAAK,EAAE,QAAQ,GAAG,YAAY;IAkBvC,MAAM,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM;CA0FrC"}
|
|
@@ -28,48 +28,48 @@ class HealthReportService {
|
|
|
28
28
|
snapshotDir,
|
|
29
29
|
total: records.length,
|
|
30
30
|
orphaned: records.filter((r) => r.isOrphaned),
|
|
31
|
-
unverifiedHeals: records.filter((r) => !r.isOrphaned && r.status ===
|
|
31
|
+
unverifiedHeals: records.filter((r) => !r.isOrphaned && r.status === "healed"),
|
|
32
32
|
recentlyHealed: records.filter((r) => r.lastHealed !== null && isWithin7Days(r.lastHealed)),
|
|
33
33
|
neverHealed: records.filter((r) => r.lastHealed === null),
|
|
34
34
|
topHealedFiles: topHealedFiles(records),
|
|
35
35
|
};
|
|
36
36
|
}
|
|
37
37
|
format(report) {
|
|
38
|
-
const lines = [
|
|
39
|
-
lines.push(chalk_1.default.bold.cyan(
|
|
38
|
+
const lines = [""];
|
|
39
|
+
lines.push(chalk_1.default.bold.cyan(" Sela Health Report") +
|
|
40
40
|
chalk_1.default.dim(` (${new Date(report.generatedAt).toISOString()})`));
|
|
41
|
-
lines.push(
|
|
41
|
+
lines.push("");
|
|
42
42
|
const row = (label, value) => ` ${chalk_1.default.dim(label.padEnd(20))} ${value}`;
|
|
43
|
-
lines.push(row(
|
|
43
|
+
lines.push(row("Total DNAs", chalk_1.default.bold(String(report.total))));
|
|
44
44
|
const orphanCount = report.orphaned.length;
|
|
45
|
-
lines.push(row(
|
|
46
|
-
? chalk_1.default.yellow(`${orphanCount} ⚠`)
|
|
47
|
-
: chalk_1.default.green('0')));
|
|
45
|
+
lines.push(row("Orphaned", orphanCount > 0 ? chalk_1.default.yellow(`${orphanCount} ⚠`) : chalk_1.default.green("0")));
|
|
48
46
|
const unverifiedCount = report.unverifiedHeals.length;
|
|
49
|
-
lines.push(row(
|
|
50
|
-
? chalk_1.default.yellow(`${unverifiedCount} ⚠ ${chalk_1.default.dim(
|
|
51
|
-
: chalk_1.default.green(
|
|
47
|
+
lines.push(row("Unverified heals", unverifiedCount > 0
|
|
48
|
+
? chalk_1.default.yellow(`${unverifiedCount} ⚠ ${chalk_1.default.dim("(AI-healed, needs review)")}`)
|
|
49
|
+
: chalk_1.default.green("0")));
|
|
52
50
|
const brokenTotal = orphanCount + unverifiedCount;
|
|
53
51
|
const brokenRatio = report.total > 0
|
|
54
52
|
? ((brokenTotal / report.total) * 100).toFixed(1)
|
|
55
|
-
:
|
|
56
|
-
lines.push(row(
|
|
57
|
-
lines.push(row(
|
|
58
|
-
lines.push(row(
|
|
59
|
-
lines.push(row(
|
|
53
|
+
: "0.0";
|
|
54
|
+
lines.push(row("Broken ratio", (brokenTotal > 0 ? chalk_1.default.yellow : chalk_1.default.green)(`${brokenRatio}% ${chalk_1.default.dim(`(${brokenTotal}/${report.total})`)}`)));
|
|
55
|
+
lines.push(row("Healed (7 days)", chalk_1.default.bold(String(report.recentlyHealed.length))));
|
|
56
|
+
lines.push(row("Healed (all time)", chalk_1.default.bold(String(report.total - report.neverHealed.length))));
|
|
57
|
+
lines.push(row("Never healed", chalk_1.default.bold(String(report.neverHealed.length))));
|
|
60
58
|
if (report.topHealedFiles.length > 0) {
|
|
61
|
-
lines.push(
|
|
62
|
-
lines.push(` ${chalk_1.default.bold(
|
|
59
|
+
lines.push("");
|
|
60
|
+
lines.push(` ${chalk_1.default.bold("Top healed files:")}`);
|
|
63
61
|
for (const { sourceFile, count } of report.topHealedFiles.slice(0, 5)) {
|
|
64
62
|
const rel = path_1.default.relative(process.cwd(), sourceFile);
|
|
65
|
-
const label = (rel.length < sourceFile.length ? rel : sourceFile)
|
|
66
|
-
|
|
67
|
-
|
|
63
|
+
const label = (rel.length < sourceFile.length ? rel : sourceFile)
|
|
64
|
+
.slice(-60)
|
|
65
|
+
.padEnd(62);
|
|
66
|
+
const noun = count === 1 ? "heal" : "heals";
|
|
67
|
+
lines.push(` ${chalk_1.default.cyan(label)} ${chalk_1.default.dim("-")} ${chalk_1.default.bold(String(count))} ${chalk_1.default.dim(noun)}`);
|
|
68
68
|
}
|
|
69
69
|
}
|
|
70
70
|
if (report.orphaned.length > 0) {
|
|
71
|
-
lines.push(
|
|
72
|
-
lines.push(` ${chalk_1.default.yellow.bold(
|
|
71
|
+
lines.push("");
|
|
72
|
+
lines.push(` ${chalk_1.default.yellow.bold("Orphaned DNA files:")}`);
|
|
73
73
|
for (const r of report.orphaned.slice(0, 10)) {
|
|
74
74
|
lines.push(` ${chalk_1.default.dim(r.key)} ${chalk_1.default.yellow(r.sourceFile)}`);
|
|
75
75
|
}
|
|
@@ -77,8 +77,8 @@ class HealthReportService {
|
|
|
77
77
|
lines.push(chalk_1.default.dim(` … and ${report.orphaned.length - 10} more`));
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
|
-
lines.push(
|
|
81
|
-
return lines.join(
|
|
80
|
+
lines.push("");
|
|
81
|
+
return lines.join("\n");
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
84
|
exports.HealthReportService = HealthReportService;
|
|
@@ -24,14 +24,14 @@ class InitializerUpdater {
|
|
|
24
24
|
else {
|
|
25
25
|
return null;
|
|
26
26
|
}
|
|
27
|
-
// Centralised write gate
|
|
27
|
+
// Centralised write gate - mirrors ASTSourceUpdater._persistChanges.
|
|
28
28
|
// InitializerUpdater is the canonical write-head for definition-site
|
|
29
29
|
// mutations (SemanticDispatch → tracer-resolved identifier / property
|
|
30
30
|
// access / cross-file paths all converge here), so the Clean-Room
|
|
31
31
|
// snapshot ledger and DryRunGuard MUST be enforced here too.
|
|
32
32
|
// Without this, an upstream-traced fix (e.g. global StringLiteral at
|
|
33
33
|
// top of file) writes to disk but `WorkspaceSnapshotService` never
|
|
34
|
-
// sees it
|
|
34
|
+
// sees it - `getMutatedSnapshots()` returns empty and PR automation
|
|
35
35
|
// skips the heal entirely.
|
|
36
36
|
const fp = sourceFile.getFilePath();
|
|
37
37
|
if (DryRunGuard_1.DryRunGuard.active()) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"IntentAuditor.d.ts","sourceRoot":"","sources":["../../src/services/IntentAuditor.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,mBAAmB,CAAC;AAE1C,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAOpD,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,WAAW,GAAG,QAAQ,CAAC;IAEjC,8EAA8E;IAC9E,WAAW,CAAC,EAAE,YAAY,EAAE,CAAC;IAC7B,+EAA+E;IAC/E,YAAY,CAAC,EAAE,YAAY,EAAE,CAAC;IAC9B,+CAA+C;IAC/C,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,qEAAqE;IACrE,WAAW,CAAC,EAAE,UAAU,CAAC;CAC1B;AAED,MAAM,MAAM,iBAAiB,GAAG,YAAY,GAAG,cAAc,GAAG,YAAY,CAAC;AAE7E,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,iBAAiB,CAAC;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,UAAU,GAAG,cAAc,GAAG,OAAO,GAAG,UAAU,GAAG,IAAI,CAAC;IACzE,yEAAyE;IACzE,cAAc,CAAC,EAAE;QAAE,aAAa,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;CAC5E;AAkED;;;;;;;;;GASG;AACH,MAAM,WAAW,oBAAoB;IACnC,SAAS,CAAC,EAAE,SAAS,GAAG,IAAI,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAID,qBAAa,aAAa;IACxB,OAAO,CAAC,SAAS,CAAmB;IACpC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAU;IACnC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;gBAEnB,IAAI,GAAE,oBAAyB;IA+BrC,WAAW,CAAC,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"IntentAuditor.d.ts","sourceRoot":"","sources":["../../src/services/IntentAuditor.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,mBAAmB,CAAC;AAE1C,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAOpD,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,WAAW,GAAG,QAAQ,CAAC;IAEjC,8EAA8E;IAC9E,WAAW,CAAC,EAAE,YAAY,EAAE,CAAC;IAC7B,+EAA+E;IAC/E,YAAY,CAAC,EAAE,YAAY,EAAE,CAAC;IAC9B,+CAA+C;IAC/C,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,qEAAqE;IACrE,WAAW,CAAC,EAAE,UAAU,CAAC;CAC1B;AAED,MAAM,MAAM,iBAAiB,GAAG,YAAY,GAAG,cAAc,GAAG,YAAY,CAAC;AAE7E,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,iBAAiB,CAAC;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,UAAU,GAAG,cAAc,GAAG,OAAO,GAAG,UAAU,GAAG,IAAI,CAAC;IACzE,yEAAyE;IACzE,cAAc,CAAC,EAAE;QAAE,aAAa,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;CAC5E;AAkED;;;;;;;;;GASG;AACH,MAAM,WAAW,oBAAoB;IACnC,SAAS,CAAC,EAAE,SAAS,GAAG,IAAI,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAID,qBAAa,aAAa;IACxB,OAAO,CAAC,SAAS,CAAmB;IACpC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAU;IACnC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;gBAEnB,IAAI,GAAE,oBAAyB;IA+BrC,WAAW,CAAC,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC;IAyH3D,sBAAsB,CACpB,OAAO,EAAE,YAAY,EAAE,EACvB,QAAQ,EAAE,YAAY,EAAE,GACvB,MAAM;IA4CT,kBAAkB,CAChB,UAAU,EAAE,UAAU,GAAG,IAAI,GAAG,SAAS,EACzC,WAAW,EAAE,UAAU,GAAG,IAAI,GAAG,SAAS,GACzC,MAAM;IAmCT,OAAO,CAAC,YAAY;CA8CrB"}
|
|
@@ -46,15 +46,15 @@ dotenv.config();
|
|
|
46
46
|
const FALLBACK_VERDICT = {
|
|
47
47
|
verdict: "SUSPICIOUS",
|
|
48
48
|
confidence: 0,
|
|
49
|
-
reason: "Auditor unavailable
|
|
49
|
+
reason: "Auditor unavailable - defaulting to cautious SUSPICIOUS verdict",
|
|
50
50
|
inversionType: null,
|
|
51
51
|
};
|
|
52
52
|
// Ancestry drift penalties (spec §2.1).
|
|
53
53
|
// Applied as negative adjustments to the LLM's confidence score.
|
|
54
54
|
// Maximum combined penalty: −30 pts.
|
|
55
55
|
const DRIFT_PENALTIES = [
|
|
56
|
-
{ full: -20, partial: -10 }, // depth 1
|
|
57
|
-
{ full: -10, partial: -5 }, // depth 2
|
|
56
|
+
{ full: -20, partial: -10 }, // depth 1 - direct parent
|
|
57
|
+
{ full: -10, partial: -5 }, // depth 2 - grandparent
|
|
58
58
|
];
|
|
59
59
|
// Anchor bonuses: positive-only (spec §3.3).
|
|
60
60
|
// closestLabel exact match: +10 pts
|
|
@@ -85,8 +85,8 @@ RULES:
|
|
|
85
85
|
3. Minor reformatting is irrelevant. "Save Changes" and "Save" are CONSISTENT.
|
|
86
86
|
4. Role/aria context matters. A button that was "Submit" becoming "Cancel" is INCONSISTENT (identity inversion).
|
|
87
87
|
5. When both texts are very short (3 chars or less) and differ, prefer SUSPICIOUS unless an obvious opposite pair.
|
|
88
|
-
6. In "assertion" mode, be stricter
|
|
89
|
-
7. In "action" mode, be more lenient
|
|
88
|
+
6. In "assertion" mode, be stricter - lean toward SUSPICIOUS or INCONSISTENT when the change is ambiguous.
|
|
89
|
+
7. In "action" mode, be more lenient - lean toward CONSISTENT when the change looks like a UI refactor.
|
|
90
90
|
|
|
91
91
|
RESPONSE FORMAT (strict JSON, no other text):
|
|
92
92
|
{
|
|
@@ -102,7 +102,7 @@ class IntentAuditor {
|
|
|
102
102
|
model;
|
|
103
103
|
constructor(opts = {}) {
|
|
104
104
|
this.model = opts.model ?? DEFAULT_AUDITOR_MODEL;
|
|
105
|
-
// Explicit `null` from the caller forces disabled mode
|
|
105
|
+
// Explicit `null` from the caller forces disabled mode - used by tests.
|
|
106
106
|
if (opts.anthropic === null) {
|
|
107
107
|
this.anthropic = null;
|
|
108
108
|
this.disabled = true;
|
|
@@ -117,7 +117,7 @@ class IntentAuditor {
|
|
|
117
117
|
// Otherwise fall back to env-based construction (legacy default).
|
|
118
118
|
const apiKey = opts.apiKey ?? process.env.ANTHROPIC_API_KEY;
|
|
119
119
|
if (!apiKey) {
|
|
120
|
-
console.warn("[IntentAuditor] ANTHROPIC_API_KEY not set
|
|
120
|
+
console.warn("[IntentAuditor] ANTHROPIC_API_KEY not set - auditor disabled, will return SUSPICIOUS for all audited heals");
|
|
121
121
|
this.anthropic = null;
|
|
122
122
|
this.disabled = true;
|
|
123
123
|
}
|
|
@@ -154,7 +154,7 @@ class IntentAuditor {
|
|
|
154
154
|
// ── Structural scoring ────────────────────────────────────────
|
|
155
155
|
// Apply ancestry drift penalty and anchor bonus to the LLM's raw
|
|
156
156
|
// confidence. The semantic verdict category (CONSISTENT / SUSPICIOUS /
|
|
157
|
-
// INCONSISTENT) is never changed by scoring
|
|
157
|
+
// INCONSISTENT) is never changed by scoring - only the confidence value.
|
|
158
158
|
// The low-confidence CONSISTENT→SUSPICIOUS downgrade is re-evaluated
|
|
159
159
|
// after scoring so the adjusted value is used for the threshold.
|
|
160
160
|
const penalty = this.computeAncestryPenalty(req.dnaAncestry ?? [], req.liveAncestry ?? []);
|
|
@@ -172,7 +172,11 @@ class IntentAuditor {
|
|
|
172
172
|
// Re-apply low-confidence CONSISTENT downgrade with the adjusted value.
|
|
173
173
|
// The raw check in parseVerdict used the pre-adjustment confidence; a
|
|
174
174
|
// penalty may push a borderline CONSISTENT below the 50% threshold.
|
|
175
|
-
const scoring = {
|
|
175
|
+
const scoring = {
|
|
176
|
+
rawConfidence: rawVerdict.confidence,
|
|
177
|
+
penalty,
|
|
178
|
+
bonus,
|
|
179
|
+
};
|
|
176
180
|
if (rawVerdict.verdict === "CONSISTENT" && adjConf < 50) {
|
|
177
181
|
finalVerdict = {
|
|
178
182
|
verdict: "SUSPICIOUS",
|
|
@@ -184,22 +188,29 @@ class IntentAuditor {
|
|
|
184
188
|
console.log(`[IntentAuditor] CONSISTENT→SUSPICIOUS after scoring (${rawVerdict.confidence}%→${adjConf}%)`);
|
|
185
189
|
}
|
|
186
190
|
else {
|
|
187
|
-
finalVerdict = {
|
|
191
|
+
finalVerdict = {
|
|
192
|
+
...rawVerdict,
|
|
193
|
+
confidence: adjConf,
|
|
194
|
+
scoreBreakdown: scoring,
|
|
195
|
+
};
|
|
188
196
|
}
|
|
189
197
|
}
|
|
190
198
|
else {
|
|
191
199
|
finalVerdict = rawVerdict;
|
|
192
200
|
}
|
|
193
|
-
console.log(`[IntentAuditor] verdict=${finalVerdict.verdict} (${finalVerdict.confidence}%)
|
|
201
|
+
console.log(`[IntentAuditor] verdict=${finalVerdict.verdict} (${finalVerdict.confidence}%) - ${finalVerdict.reason}`);
|
|
194
202
|
return finalVerdict;
|
|
195
203
|
}
|
|
196
204
|
catch (err) {
|
|
197
|
-
console.warn(`[IntentAuditor] Warning: Auditor call failed: ${err.message}
|
|
198
|
-
return {
|
|
205
|
+
console.warn(`[IntentAuditor] Warning: Auditor call failed: ${err.message} - returning SUSPICIOUS`);
|
|
206
|
+
return {
|
|
207
|
+
...FALLBACK_VERDICT,
|
|
208
|
+
reason: `Auditor call failed: ${err.message}`,
|
|
209
|
+
};
|
|
199
210
|
}
|
|
200
211
|
}
|
|
201
212
|
// ─────────────────────────────────────────────────────────────
|
|
202
|
-
// computeAncestryPenalty
|
|
213
|
+
// computeAncestryPenalty - DNA v2 Ancestry Drift (spec §2)
|
|
203
214
|
//
|
|
204
215
|
// Compares at most 2 ancestor depths between the DNA baseline and
|
|
205
216
|
// the live candidate. Returns a value in [−30, 0]; 0 = no drift.
|
|
@@ -216,7 +227,7 @@ class IntentAuditor {
|
|
|
216
227
|
// ─────────────────────────────────────────────────────────────
|
|
217
228
|
computeAncestryPenalty(dnaPath, livePath) {
|
|
218
229
|
if (dnaPath.length === 0)
|
|
219
|
-
return 0; // v1 migrated snapshot
|
|
230
|
+
return 0; // v1 migrated snapshot - skip
|
|
220
231
|
let totalPenalty = 0;
|
|
221
232
|
const maxDepth = Math.min(dnaPath.length, livePath.length, 2);
|
|
222
233
|
for (let i = 0; i < maxDepth; i++) {
|
|
@@ -229,21 +240,21 @@ class IntentAuditor {
|
|
|
229
240
|
const depthLabel = i === 0 ? "depth-1 (parent)" : "depth-2 (grandparent)";
|
|
230
241
|
if (dna.tag !== live.tag) {
|
|
231
242
|
totalPenalty += full;
|
|
232
|
-
console.log(`[IntentAuditor] 🏗️ Ancestry drift ${full} pts
|
|
243
|
+
console.log(`[IntentAuditor] 🏗️ Ancestry drift ${full} pts - ${depthLabel}: ` +
|
|
233
244
|
`tag mismatch (${dna.fingerprint} → ${live.fingerprint})`);
|
|
234
245
|
}
|
|
235
246
|
else {
|
|
236
247
|
totalPenalty += partial;
|
|
237
|
-
console.log(`[IntentAuditor] 🏗️ Ancestry drift ${partial} pts
|
|
248
|
+
console.log(`[IntentAuditor] 🏗️ Ancestry drift ${partial} pts - ${depthLabel}: ` +
|
|
238
249
|
`same tag, id/class changed (${dna.fingerprint} → ${live.fingerprint})`);
|
|
239
250
|
}
|
|
240
251
|
}
|
|
241
252
|
return totalPenalty;
|
|
242
253
|
}
|
|
243
254
|
// ─────────────────────────────────────────────────────────────
|
|
244
|
-
// computeAnchorBonus
|
|
255
|
+
// computeAnchorBonus - DNA v2 Anchor Confirmation (spec §3.3)
|
|
245
256
|
//
|
|
246
|
-
// Anchors only provide positive confirmation
|
|
257
|
+
// Anchors only provide positive confirmation - no penalty for
|
|
247
258
|
// mismatch. Returns a value in [0, +15].
|
|
248
259
|
//
|
|
249
260
|
// closestLabel exact match (case-insensitive): +10 pts
|
|
@@ -257,13 +268,13 @@ class IntentAuditor {
|
|
|
257
268
|
const liveLabel = liveAnchors.closestLabel?.toLowerCase().trim();
|
|
258
269
|
if (dnaLabel && liveLabel && dnaLabel === liveLabel) {
|
|
259
270
|
bonus += ANCHOR_BONUS_LABEL;
|
|
260
|
-
console.log(`[IntentAuditor] 🎯 Anchor +${ANCHOR_BONUS_LABEL} pts
|
|
271
|
+
console.log(`[IntentAuditor] 🎯 Anchor +${ANCHOR_BONUS_LABEL} pts - closestLabel match: "${dnaAnchors.closestLabel}"`);
|
|
261
272
|
}
|
|
262
273
|
const dnaRow = dnaAnchors.rowAnchor?.toLowerCase().trim();
|
|
263
274
|
const liveRow = liveAnchors.rowAnchor?.toLowerCase().trim();
|
|
264
275
|
if (dnaRow && liveRow && dnaRow === liveRow) {
|
|
265
276
|
bonus += ANCHOR_BONUS_ROW;
|
|
266
|
-
console.log(`[IntentAuditor] 🎯 Anchor +${ANCHOR_BONUS_ROW} pts
|
|
277
|
+
console.log(`[IntentAuditor] 🎯 Anchor +${ANCHOR_BONUS_ROW} pts - rowAnchor match: "${dnaAnchors.rowAnchor}"`);
|
|
267
278
|
}
|
|
268
279
|
return bonus;
|
|
269
280
|
}
|
|
@@ -1,24 +1,15 @@
|
|
|
1
1
|
import type { PendingPromptEntry } from "./PendingPromptLedger";
|
|
2
|
-
export interface
|
|
2
|
+
export interface PrintReviewDeps {
|
|
3
3
|
entries: PendingPromptEntry[];
|
|
4
|
-
/** Override the
|
|
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
|
-
/**
|
|
9
|
-
|
|
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
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
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
|
|
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":"
|
|
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,CA0B3D"}
|
|
@@ -1,21 +1,18 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
// src/services/InteractiveReview.ts
|
|
3
3
|
//
|
|
4
|
-
// Local DX
|
|
4
|
+
// Local DX - Post-run summary printer (MVP write-and-notify).
|
|
5
5
|
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
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
|
-
//
|
|
12
|
-
//
|
|
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,34 @@ 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.
|
|
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
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
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
|
-
|
|
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
|
|
77
|
-
if (entries.length === 0)
|
|
78
|
-
return
|
|
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
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
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
|
+
log(chalk_1.default.bold.bgCyan.black(` [Sela] Local DX Review - ${entries.length} fix${entries.length === 1 ? "" : "es"} applied to disk `));
|
|
71
|
+
for (const entry of entries) {
|
|
72
|
+
renderEntry(entry, log);
|
|
101
73
|
}
|
|
102
74
|
}
|
|
75
|
+
if (reportHtmlPath) {
|
|
76
|
+
log("");
|
|
77
|
+
log(chalk_1.default.bold(`[Sela] 📄 Insights report:`));
|
|
78
|
+
log(` ${chalk_1.default.cyan(buildFileLink(reportHtmlPath))}`);
|
|
79
|
+
}
|
|
103
80
|
log("");
|
|
104
|
-
return outcomes;
|
|
105
81
|
}
|
|
106
82
|
function renderEntry(entry, log) {
|
|
107
83
|
const fileName = path.basename(entry.absolutePath);
|
|
@@ -113,9 +89,8 @@ function renderEntry(entry, log) {
|
|
|
113
89
|
log("");
|
|
114
90
|
log(colorizeDiff(diff));
|
|
115
91
|
log("");
|
|
116
|
-
log(`👉
|
|
92
|
+
log(`👉 Open in VS Code:`);
|
|
117
93
|
log(` ${chalk_1.default.cyan(buildVsCodeLink(entry.absolutePath, entry.lineNumber))}`);
|
|
118
|
-
log("");
|
|
119
94
|
}
|
|
120
95
|
function colorizeDiff(diff) {
|
|
121
96
|
if (!diff)
|
|
@@ -144,75 +119,7 @@ function buildVsCodeLink(absPath, line) {
|
|
|
144
119
|
? `vscode://file/${normalised}:${line}`
|
|
145
120
|
: `vscode://file/${normalised}`;
|
|
146
121
|
}
|
|
147
|
-
function
|
|
148
|
-
const
|
|
149
|
-
|
|
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
|
-
});
|
|
122
|
+
function buildFileLink(absPath) {
|
|
123
|
+
const normalised = absPath.split(path.sep).join("/");
|
|
124
|
+
return `file://${normalised}`;
|
|
218
125
|
}
|
|
@@ -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
|
|
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
|
|
75
|
-
PRIORITY 2
|
|
76
|
-
PRIORITY 3
|
|
77
|
-
PRIORITY 4
|
|
78
|
-
NARROWING
|
|
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
|
|
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"
|
|
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
|
|
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
|
|
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
|
|
114
|
-
- 1-49: speculative match
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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,
|
|
@@ -37,14 +37,14 @@ export interface ExecutionResult {
|
|
|
37
37
|
* Clean-Room execution lifecycle.
|
|
38
38
|
*
|
|
39
39
|
* The legacy flow committed the developer's whole dirty working tree onto
|
|
40
|
-
* the PR branch
|
|
40
|
+
* the PR branch - polluting reviewers with `test.only`, commented debug
|
|
41
41
|
* blocks, etc. This refactor isolates Sela's specific AST changes onto an
|
|
42
42
|
* ephemeral branch, leaves the developer's local workspace byte-identical
|
|
43
43
|
* to its pre-Sela state, and is wrapped in a try/finally that restores
|
|
44
44
|
* the workspace even on mid-flight crashes.
|
|
45
45
|
*/
|
|
46
46
|
export declare function execute(cfg: ResolvedPRAutomation, decision: StrategyDecision, heals: HealedEvent[], branchInfo: BranchInfo, ctx: ExecuteContext,
|
|
47
|
-
/** Injectable for tests
|
|
47
|
+
/** Injectable for tests - defaults to the module singleton. */
|
|
48
48
|
workspace?: WorkspaceSnapshotService): Promise<ExecutionResult>;
|
|
49
49
|
export declare function handleBugDetected(action: BugAction, events: ProtectedEvent[], branchInfo: BranchInfo, ctx: ExecuteContext): Promise<void>;
|
|
50
50
|
//# sourceMappingURL=PRAutomationService.d.ts.map
|