sela-core 0.1.0-alpha.34 → 0.1.0-alpha.36
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/commands/init.d.ts +12 -0
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +21 -1
- package/dist/cli/commands/pr.d.ts.map +1 -1
- package/dist/cli/commands/pr.js +6 -15
- package/dist/cli/commands/shadowRegression.d.ts +21 -0
- package/dist/cli/commands/shadowRegression.d.ts.map +1 -0
- package/dist/cli/commands/shadowRegression.js +193 -0
- package/dist/cli/conventionsInit.d.ts +51 -0
- package/dist/cli/conventionsInit.d.ts.map +1 -0
- package/dist/cli/conventionsInit.js +326 -0
- package/dist/cli/index.js +3 -0
- package/dist/cli/ui/ConventionsTable.d.ts +7 -0
- package/dist/cli/ui/ConventionsTable.d.ts.map +1 -0
- package/dist/cli/ui/ConventionsTable.js +32 -0
- package/dist/config/ConfigLoader.d.ts +8 -1
- package/dist/config/ConfigLoader.d.ts.map +1 -1
- package/dist/config/ConfigLoader.js +28 -0
- package/dist/config/Conventions.d.ts +131 -0
- package/dist/config/Conventions.d.ts.map +1 -0
- package/dist/config/Conventions.js +268 -0
- package/dist/config/SelaConfig.d.ts +127 -1
- package/dist/config/SelaConfig.d.ts.map +1 -1
- package/dist/config/SelaConfig.js +68 -17
- package/dist/engine/SelaEngine.d.ts +3 -0
- package/dist/engine/SelaEngine.d.ts.map +1 -1
- package/dist/engine/SelaEngine.js +316 -56
- package/dist/errors/SelaError.d.ts +3 -0
- package/dist/errors/SelaError.d.ts.map +1 -1
- package/dist/errors/SelaError.js +3 -0
- package/dist/errors/SelaHealingRefusalError.d.ts +17 -0
- package/dist/errors/SelaHealingRefusalError.d.ts.map +1 -0
- package/dist/errors/SelaHealingRefusalError.js +42 -0
- package/dist/fixtures/index.d.ts.map +1 -1
- package/dist/fixtures/index.js +101 -14
- package/dist/reporter/SelaReporter.d.ts +16 -1
- package/dist/reporter/SelaReporter.d.ts.map +1 -1
- package/dist/reporter/SelaReporter.js +40 -4
- package/dist/services/ASTSourceUpdater.d.ts +14 -2
- package/dist/services/ASTSourceUpdater.d.ts.map +1 -1
- package/dist/services/ASTSourceUpdater.js +520 -58
- package/dist/services/ArgumentTypeAnalyzer.d.ts +1 -1
- package/dist/services/ArgumentTypeAnalyzer.d.ts.map +1 -1
- package/dist/services/ArgumentTypeAnalyzer.js +1 -1
- package/dist/services/ConventionsScanner.d.ts +28 -0
- package/dist/services/ConventionsScanner.d.ts.map +1 -0
- package/dist/services/ConventionsScanner.js +441 -0
- package/dist/services/CrossFileHealer.d.ts +14 -0
- package/dist/services/CrossFileHealer.d.ts.map +1 -1
- package/dist/services/CrossFileHealer.js +135 -2
- package/dist/services/DecisionEngine.d.ts +17 -1
- package/dist/services/DecisionEngine.d.ts.map +1 -1
- package/dist/services/DecisionEngine.js +152 -1
- package/dist/services/HealFieldParser.d.ts +31 -0
- package/dist/services/HealFieldParser.d.ts.map +1 -0
- package/dist/services/HealFieldParser.js +153 -0
- package/dist/services/HealReportService.d.ts +60 -3
- package/dist/services/HealReportService.d.ts.map +1 -1
- package/dist/services/HealReportService.js +307 -17
- package/dist/services/HealingCacheService.d.ts +7 -0
- package/dist/services/HealingCacheService.d.ts.map +1 -1
- package/dist/services/HealingCacheService.js +1 -0
- package/dist/services/InboundArgumentGate.d.ts +18 -0
- package/dist/services/InboundArgumentGate.d.ts.map +1 -0
- package/dist/services/InboundArgumentGate.js +67 -0
- package/dist/services/IntentAuditor.d.ts.map +1 -1
- package/dist/services/IntentAuditor.js +41 -82
- package/dist/services/InteractiveReview.d.ts +20 -0
- package/dist/services/InteractiveReview.d.ts.map +1 -1
- package/dist/services/InteractiveReview.js +23 -2
- package/dist/services/LLMService.d.ts.map +1 -1
- package/dist/services/LLMService.js +145 -13
- package/dist/services/LocatorArgModel.d.ts +23 -0
- package/dist/services/LocatorArgModel.d.ts.map +1 -0
- package/dist/services/LocatorArgModel.js +137 -0
- package/dist/services/MutationApplier.d.ts +15 -0
- package/dist/services/MutationApplier.d.ts.map +1 -1
- package/dist/services/MutationApplier.js +64 -11
- package/dist/services/OrdinalTracker.d.ts +24 -0
- package/dist/services/OrdinalTracker.d.ts.map +1 -0
- package/dist/services/OrdinalTracker.js +42 -0
- package/dist/services/PendingPromptLedger.d.ts +61 -1
- package/dist/services/PendingPromptLedger.d.ts.map +1 -1
- package/dist/services/PendingPromptLedger.js +141 -0
- package/dist/services/RegressionManifestService.d.ts +59 -0
- package/dist/services/RegressionManifestService.d.ts.map +1 -0
- package/dist/services/RegressionManifestService.js +181 -0
- package/dist/services/ReportGenerator.d.ts +20 -0
- package/dist/services/ReportGenerator.d.ts.map +1 -1
- package/dist/services/ReportGenerator.js +20 -3
- package/dist/services/ReportMergeService.js +0 -0
- package/dist/services/SafetyGuard.d.ts +12 -0
- package/dist/services/SafetyGuard.d.ts.map +1 -1
- package/dist/services/SafetyGuard.js +22 -1
- package/dist/services/SemanticAnchor.d.ts +67 -0
- package/dist/services/SemanticAnchor.d.ts.map +1 -0
- package/dist/services/SemanticAnchor.js +160 -0
- package/dist/services/SharedLocatorGuard.d.ts +36 -0
- package/dist/services/SharedLocatorGuard.d.ts.map +1 -0
- package/dist/services/SharedLocatorGuard.js +360 -0
- package/dist/services/SnapshotService.d.ts +1 -0
- package/dist/services/SnapshotService.d.ts.map +1 -1
- package/dist/services/SnapshotService.js +15 -1
- package/dist/services/SourceUpdater.d.ts.map +1 -1
- package/dist/services/SourceUpdater.js +10 -5
- package/dist/services/TraceBackEngine.d.ts +47 -0
- package/dist/services/TraceBackEngine.d.ts.map +1 -1
- package/dist/services/TraceBackEngine.js +187 -4
- package/dist/services/WorkspaceSnapshotService.d.ts +27 -0
- package/dist/services/WorkspaceSnapshotService.d.ts.map +1 -1
- package/dist/services/WorkspaceSnapshotService.js +23 -0
- package/dist/services/confidence/BusinessContextParser.d.ts +31 -0
- package/dist/services/confidence/BusinessContextParser.d.ts.map +1 -0
- package/dist/services/confidence/BusinessContextParser.js +124 -0
- package/dist/services/confidence/ConfidenceVectorEngine.d.ts +5 -0
- package/dist/services/confidence/ConfidenceVectorEngine.d.ts.map +1 -0
- package/dist/services/confidence/ConfidenceVectorEngine.js +187 -0
- package/dist/services/confidence/StructuralScoringUtils.d.ts +27 -0
- package/dist/services/confidence/StructuralScoringUtils.d.ts.map +1 -0
- package/dist/services/confidence/StructuralScoringUtils.js +127 -0
- package/dist/services/confidence/index.d.ts +6 -0
- package/dist/services/confidence/index.d.ts.map +1 -0
- package/dist/services/confidence/index.js +12 -0
- package/dist/services/confidence/types.d.ts +64 -0
- package/dist/services/confidence/types.d.ts.map +1 -0
- package/dist/services/confidence/types.js +5 -0
- package/dist/services/interceptors/FileWriteInterceptor.d.ts +29 -0
- package/dist/services/interceptors/FileWriteInterceptor.d.ts.map +1 -0
- package/dist/services/interceptors/FileWriteInterceptor.js +83 -0
- package/dist/services/safety/PipelineContext.d.ts +16 -0
- package/dist/services/safety/PipelineContext.d.ts.map +1 -1
- package/dist/services/safety/PipelineContext.js +6 -0
- package/dist/services/safety/rules/ConfidenceTierRule.d.ts.map +1 -1
- package/dist/services/safety/rules/ConfidenceTierRule.js +12 -1
- package/dist/services/safety/rules/ConfidenceVectorRule.d.ts +15 -0
- package/dist/services/safety/rules/ConfidenceVectorRule.d.ts.map +1 -0
- package/dist/services/safety/rules/ConfidenceVectorRule.js +109 -0
- package/dist/services/safety/rules/IntentAuditorRule.d.ts.map +1 -1
- package/dist/services/safety/rules/IntentAuditorRule.js +5 -1
- package/dist/services/safety/rules/StructuralOnlyRule.d.ts.map +1 -1
- package/dist/services/safety/rules/StructuralOnlyRule.js +9 -4
- package/dist/services/safety/rules/index.d.ts.map +1 -1
- package/dist/services/safety/rules/index.js +9 -7
- package/dist/utils/DOMUtils.d.ts.map +1 -1
- package/dist/utils/DOMUtils.js +62 -2
- package/dist/utils/IsolatedDiff.d.ts +8 -0
- package/dist/utils/IsolatedDiff.d.ts.map +1 -1
- package/dist/utils/IsolatedDiff.js +8 -0
- package/dist/utils/logger.d.ts +2 -2
- package/dist/utils/logger.js +4 -4
- package/package.json +79 -72
|
@@ -1,3 +1,15 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
|
+
export interface InitOptions {
|
|
3
|
+
updateImports?: boolean;
|
|
4
|
+
/** Skip the conventions auto-discovery wizard. */
|
|
5
|
+
conventions?: boolean;
|
|
6
|
+
/** Enable the optional LLM polish step (Commander: --no-llm sets false). */
|
|
7
|
+
llm?: boolean;
|
|
8
|
+
yes?: boolean;
|
|
9
|
+
dryRun?: boolean;
|
|
10
|
+
glob?: string;
|
|
11
|
+
maxFiles?: string;
|
|
12
|
+
fileTimeout?: string;
|
|
13
|
+
}
|
|
2
14
|
export declare function registerInit(program: Command): void;
|
|
3
15
|
//# sourceMappingURL=init.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA8BpC,MAAM,WAAW,WAAW;IAC1B,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,kDAAkD;IAClD,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,4EAA4E;IAC5E,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAoBnD"}
|
|
@@ -37,6 +37,7 @@ exports.registerInit = registerInit;
|
|
|
37
37
|
const fs = __importStar(require("fs"));
|
|
38
38
|
const path = __importStar(require("path"));
|
|
39
39
|
const logger_js_1 = require("../../utils/logger.js");
|
|
40
|
+
const conventionsInit_js_1 = require("../conventionsInit.js");
|
|
40
41
|
const PACKAGE_NAME = "sela-core";
|
|
41
42
|
const CONFIG_TEMPLATE_TS = `import { defineConfig } from '${PACKAGE_NAME}';
|
|
42
43
|
|
|
@@ -60,8 +61,15 @@ const REQUIRED_ENV_VARS = ["ANTHROPIC_API_KEY"];
|
|
|
60
61
|
function registerInit(program) {
|
|
61
62
|
program
|
|
62
63
|
.command("init")
|
|
63
|
-
.description("Initialize Sela
|
|
64
|
+
.description("Initialize Sela and auto-discover your testing conventions")
|
|
64
65
|
.option("--no-update-imports", "Skip swapping @playwright/test imports to sela-core")
|
|
66
|
+
.option("--no-conventions", "Skip the conventions auto-discovery wizard")
|
|
67
|
+
.option("--no-llm", "Skip the optional Claude refinement step")
|
|
68
|
+
.option("-y, --yes", "Accept defaults (smart discovery + write)")
|
|
69
|
+
.option("--dry-run", "Print conventions to stdout without writing")
|
|
70
|
+
.option("--glob <pattern>", "Scan files matching this glob (non-interactive)")
|
|
71
|
+
.option("--max-files <n>", "Hard cap on files scanned")
|
|
72
|
+
.option("--file-timeout <ms>", "Per-file analysis budget (ms)")
|
|
65
73
|
.action(async (opts) => {
|
|
66
74
|
await runInit(opts);
|
|
67
75
|
});
|
|
@@ -106,6 +114,18 @@ async function runInit(opts) {
|
|
|
106
114
|
}
|
|
107
115
|
logger_js_1.logger.info(`\nSpec files should import from:`);
|
|
108
116
|
logger_js_1.logger.info(` import { test, expect } from '${PACKAGE_NAME}'\n`);
|
|
117
|
+
// ── Conventions auto-discovery (default ON; opt out --no-conventions) ──
|
|
118
|
+
if (opts.conventions !== false) {
|
|
119
|
+
logger_js_1.logger.info(`Discovering testing conventions…`);
|
|
120
|
+
await (0, conventionsInit_js_1.runConventionsWizard)(cwd, {
|
|
121
|
+
llm: opts.llm,
|
|
122
|
+
yes: opts.yes,
|
|
123
|
+
dryRun: opts.dryRun,
|
|
124
|
+
glob: opts.glob,
|
|
125
|
+
maxFiles: opts.maxFiles ? Number(opts.maxFiles) : undefined,
|
|
126
|
+
fileTimeoutMs: opts.fileTimeout ? Number(opts.fileTimeout) : undefined,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
109
129
|
}
|
|
110
130
|
function loadDotEnv(cwd) {
|
|
111
131
|
const envPath = path.join(cwd, ".env");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pr.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/pr.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"pr.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/pr.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAczC;;;;;;;;;;;;GAYG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,CAsEhD"}
|
package/dist/cli/commands/pr.js
CHANGED
|
@@ -34,7 +34,6 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.registerPr = registerPr;
|
|
37
|
-
const fs = __importStar(require("fs"));
|
|
38
37
|
const path = __importStar(require("path"));
|
|
39
38
|
const PendingPromptLedger_js_1 = require("../../services/PendingPromptLedger.js");
|
|
40
39
|
const SelaReporter_js_1 = require("../../reporter/SelaReporter.js");
|
|
@@ -76,20 +75,12 @@ function registerPr(parent) {
|
|
|
76
75
|
// The aggregator machine checked out the original (broken) source files
|
|
77
76
|
// and never ran tests itself, so we must physically write the patched
|
|
78
77
|
// code here — otherwise git sees 0 mutations and skips the PR entirely.
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
try {
|
|
86
|
-
fs.writeFileSync(targetPath, entry.contentAfter, "utf8");
|
|
87
|
-
patchedCount++;
|
|
88
|
-
}
|
|
89
|
-
catch (err) {
|
|
90
|
-
console.error(`[sela pr aggregate] Failed to patch ${targetPath}: ${err instanceof Error ? err.message : String(err)}`);
|
|
91
|
-
}
|
|
92
|
-
}
|
|
78
|
+
//
|
|
79
|
+
// mergeLedgerEntriesByFile applies each shard's granular patches
|
|
80
|
+
// sequentially against the shared baseline so ALL shards' fixes are
|
|
81
|
+
// preserved (no last-write-wins clobber). Falls back to contentAfter
|
|
82
|
+
// blob for legacy ledgers that predate the patches field.
|
|
83
|
+
const patchedCount = await (0, PendingPromptLedger_js_1.mergeLedgerEntriesByFile)(resolvedLedgerDir, opts.cwd);
|
|
93
84
|
if (patchedCount === 0) {
|
|
94
85
|
console.error("[sela pr aggregate] No source patches could be applied to disk — skipping PR.");
|
|
95
86
|
return;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { Command } from "commander";
|
|
2
|
+
import type { RegressionResult } from "../../services/ReportGenerator.js";
|
|
3
|
+
/**
|
|
4
|
+
* `sela shadow-regression --manifest <path>`
|
|
5
|
+
*
|
|
6
|
+
* Job 2 of the stateless shadow-regression flow. Reads the manifest emitted by
|
|
7
|
+
* Job 1, re-runs the affected specs with the proposed shared-locator heal hot-
|
|
8
|
+
* swapped into memory (SELA_REGRESSION_ACTIVE), parses Playwright's JSON
|
|
9
|
+
* report, and merges a PASSED/FAILED verdict into the existing sela-report.
|
|
10
|
+
* Zero source-code mutation — the heal lives only in execution memory.
|
|
11
|
+
*
|
|
12
|
+
* Exit code: 1 when the regression FAILS (downstream side-effects), else 0.
|
|
13
|
+
*/
|
|
14
|
+
export declare function registerShadowRegression(program: Command): void;
|
|
15
|
+
/**
|
|
16
|
+
* Flatten a Playwright JSON report into RegressionResult[], walking nested
|
|
17
|
+
* suites. A spec is FAILED when `ok` is false; the first failing result's error
|
|
18
|
+
* (message + stack, ANSI-stripped) is attached for the report drilldown.
|
|
19
|
+
*/
|
|
20
|
+
export declare function collectResults(report: any): RegressionResult[];
|
|
21
|
+
//# sourceMappingURL=shadowRegression.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shadowRegression.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/shadowRegression.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAOzC,OAAO,KAAK,EACV,gBAAgB,EAEjB,MAAM,mCAAmC,CAAC;AAe3C;;;;;;;;;;GAUG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAgI/D;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,GAAG,GAAG,gBAAgB,EAAE,CAqC9D"}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.registerShadowRegression = registerShadowRegression;
|
|
37
|
+
exports.collectResults = collectResults;
|
|
38
|
+
const fs = __importStar(require("fs"));
|
|
39
|
+
const os = __importStar(require("os"));
|
|
40
|
+
const path = __importStar(require("path"));
|
|
41
|
+
const child_process_1 = require("child_process");
|
|
42
|
+
const RegressionManifestService_js_1 = require("../../services/RegressionManifestService.js");
|
|
43
|
+
const HealReportService_js_1 = require("../../services/HealReportService.js");
|
|
44
|
+
// eslint-disable-next-line no-control-regex
|
|
45
|
+
const ANSI = /\[[0-9;]*m/g;
|
|
46
|
+
function stripAnsi(s) {
|
|
47
|
+
return s.replace(ANSI, "");
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* `sela shadow-regression --manifest <path>`
|
|
51
|
+
*
|
|
52
|
+
* Job 2 of the stateless shadow-regression flow. Reads the manifest emitted by
|
|
53
|
+
* Job 1, re-runs the affected specs with the proposed shared-locator heal hot-
|
|
54
|
+
* swapped into memory (SELA_REGRESSION_ACTIVE), parses Playwright's JSON
|
|
55
|
+
* report, and merges a PASSED/FAILED verdict into the existing sela-report.
|
|
56
|
+
* Zero source-code mutation — the heal lives only in execution memory.
|
|
57
|
+
*
|
|
58
|
+
* Exit code: 1 when the regression FAILS (downstream side-effects), else 0.
|
|
59
|
+
*/
|
|
60
|
+
function registerShadowRegression(program) {
|
|
61
|
+
program
|
|
62
|
+
.command("shadow-regression")
|
|
63
|
+
.description("Verify a bypassed shared-locator heal against its downstream blast radius " +
|
|
64
|
+
"(stateless, in-memory, zero disk mutation)")
|
|
65
|
+
.requiredOption("--manifest <path>", "Path to sela-regression-manifest.json (emitted by the primary run)")
|
|
66
|
+
.option("--dir <path>", "Project dir holding sela-report.json (merge target)", process.cwd())
|
|
67
|
+
.option("--config <path>", "Playwright config to pass through to the run")
|
|
68
|
+
.option("--report <path>", "Override path to the base sela-report.json to merge into")
|
|
69
|
+
.action(async (opts) => {
|
|
70
|
+
const targetDir = path.resolve(opts.dir ?? process.cwd());
|
|
71
|
+
const manifestAbs = path.isAbsolute(opts.manifest)
|
|
72
|
+
? opts.manifest
|
|
73
|
+
: path.resolve(process.cwd(), opts.manifest);
|
|
74
|
+
// 1. Load + validate the manifest (throws loudly on bad input).
|
|
75
|
+
let manifest;
|
|
76
|
+
try {
|
|
77
|
+
manifest = RegressionManifestService_js_1.RegressionManifestService.readManifest(manifestAbs);
|
|
78
|
+
}
|
|
79
|
+
catch (err) {
|
|
80
|
+
process.stderr.write(`[sela] ${err.message}\n`);
|
|
81
|
+
process.exit(1);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
if (manifest.records.length === 0) {
|
|
85
|
+
process.stdout.write("[sela] Manifest has no bypassed locators — nothing to verify.\n");
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
// 2. Union of affected spec files across all records (dedup).
|
|
89
|
+
const affectedFiles = Array.from(new Set(manifest.records.flatMap((r) => r.affectedFiles)));
|
|
90
|
+
if (affectedFiles.length === 0) {
|
|
91
|
+
process.stdout.write("[sela] No affected files in manifest — nothing to re-run.\n");
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
// 3. Run Playwright over the affected files with the JSON reporter and
|
|
95
|
+
// the injection env active. JSON goes to a temp file via
|
|
96
|
+
// PLAYWRIGHT_JSON_OUTPUT_NAME so stdout stays clean.
|
|
97
|
+
const tmpJson = path.join(fs.mkdtempSync(path.join(os.tmpdir(), "sela-shadow-")), "pw-report.json");
|
|
98
|
+
const pwArgs = ["playwright", "test", ...affectedFiles, "--reporter=json"];
|
|
99
|
+
if (opts.config)
|
|
100
|
+
pwArgs.push(`--config=${opts.config}`);
|
|
101
|
+
process.stdout.write(`[sela] Shadow regression: re-running ${affectedFiles.length} affected ` +
|
|
102
|
+
`spec file(s) with ${manifest.records.length} injected heal(s)…\n`);
|
|
103
|
+
const run = (0, child_process_1.spawnSync)("npx", pwArgs, {
|
|
104
|
+
cwd: targetDir,
|
|
105
|
+
shell: true,
|
|
106
|
+
env: {
|
|
107
|
+
...process.env,
|
|
108
|
+
SELA_REGRESSION_ACTIVE: "true",
|
|
109
|
+
SELA_REGRESSION_MANIFEST: manifestAbs,
|
|
110
|
+
PLAYWRIGHT_JSON_OUTPUT_NAME: tmpJson,
|
|
111
|
+
},
|
|
112
|
+
encoding: "utf8",
|
|
113
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
114
|
+
});
|
|
115
|
+
// 4. Parse the JSON report → RegressionResult[].
|
|
116
|
+
let pwReport;
|
|
117
|
+
try {
|
|
118
|
+
pwReport = JSON.parse(fs.readFileSync(tmpJson, "utf8"));
|
|
119
|
+
}
|
|
120
|
+
catch (err) {
|
|
121
|
+
process.stderr.write(`[sela] Could not read Playwright JSON report (${tmpJson}): ${err.message}\n`);
|
|
122
|
+
if (run.stderr)
|
|
123
|
+
process.stderr.write(run.stderr.toString());
|
|
124
|
+
process.exit(1);
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
const regressionTests = collectResults(pwReport);
|
|
128
|
+
const regressionState = regressionTests.some((t) => t.status === "FAILED")
|
|
129
|
+
? "FAILURE"
|
|
130
|
+
: "SUCCESS";
|
|
131
|
+
// 5. Merge the verdict into the report + regenerate the dashboard.
|
|
132
|
+
let htmlPath;
|
|
133
|
+
try {
|
|
134
|
+
htmlPath = (0, HealReportService_js_1.mergeRegressionIntoReport)(targetDir, { regressionState, regressionTests }, { reportJsonPath: opts.report });
|
|
135
|
+
}
|
|
136
|
+
catch (err) {
|
|
137
|
+
process.stderr.write(`[sela] Report merge failed: ${err.message}\n`);
|
|
138
|
+
process.exit(1);
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
const passN = regressionTests.filter((t) => t.status === "PASSED").length;
|
|
142
|
+
const failN = regressionTests.filter((t) => t.status === "FAILED").length;
|
|
143
|
+
process.stdout.write(`[sela] Regression ${regressionState}: ${passN} passed · ${failN} failed.\n` +
|
|
144
|
+
` Report updated: ${path.relative(process.cwd(), htmlPath)}\n`);
|
|
145
|
+
// FAILURE = the proposed fix breaks downstream tests → fail the CI job.
|
|
146
|
+
process.exit(regressionState === "FAILURE" ? 1 : 0);
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Flatten a Playwright JSON report into RegressionResult[], walking nested
|
|
151
|
+
* suites. A spec is FAILED when `ok` is false; the first failing result's error
|
|
152
|
+
* (message + stack, ANSI-stripped) is attached for the report drilldown.
|
|
153
|
+
*/
|
|
154
|
+
function collectResults(report) {
|
|
155
|
+
const out = [];
|
|
156
|
+
const walk = (suite, fileFallback) => {
|
|
157
|
+
const file = suite?.file ?? fileFallback;
|
|
158
|
+
for (const spec of suite?.specs ?? []) {
|
|
159
|
+
const specFile = spec.file ?? file ?? "";
|
|
160
|
+
const ok = spec.ok !== false;
|
|
161
|
+
let error;
|
|
162
|
+
if (!ok) {
|
|
163
|
+
for (const t of spec.tests ?? []) {
|
|
164
|
+
for (const r of t.results ?? []) {
|
|
165
|
+
if (r.status === "passed" || r.status === "skipped")
|
|
166
|
+
continue;
|
|
167
|
+
const e = (r.errors && r.errors[0]) || r.error;
|
|
168
|
+
if (e) {
|
|
169
|
+
error = {
|
|
170
|
+
message: stripAnsi(String(e.message ?? "")),
|
|
171
|
+
stack: e.stack ? stripAnsi(String(e.stack)) : undefined,
|
|
172
|
+
};
|
|
173
|
+
break;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
if (error)
|
|
177
|
+
break;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
out.push({
|
|
181
|
+
status: ok ? "PASSED" : "FAILED",
|
|
182
|
+
testName: spec.title ?? "",
|
|
183
|
+
filePath: specFile,
|
|
184
|
+
...(error ? { error } : {}),
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
for (const child of suite?.suites ?? [])
|
|
188
|
+
walk(child, file);
|
|
189
|
+
};
|
|
190
|
+
for (const s of report?.suites ?? [])
|
|
191
|
+
walk(s);
|
|
192
|
+
return out;
|
|
193
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type Anthropic from "@anthropic-ai/sdk";
|
|
2
|
+
import { type ScanOptions } from "../services/ConventionsScanner.js";
|
|
3
|
+
import { type Conventions, type ScanStats } from "../config/Conventions.js";
|
|
4
|
+
export type DiscoveryMode = "smart" | "folders" | "glob";
|
|
5
|
+
export interface ConventionsInitOptions {
|
|
6
|
+
/** smart | folders | glob. */
|
|
7
|
+
mode?: DiscoveryMode;
|
|
8
|
+
/** Folders (cwd-relative) when mode === 'folders'. */
|
|
9
|
+
folders?: string[];
|
|
10
|
+
/** Glob pattern when mode === 'glob'. */
|
|
11
|
+
glob?: string;
|
|
12
|
+
fileTimeoutMs?: number;
|
|
13
|
+
maxFiles?: number;
|
|
14
|
+
/** Run the optional LLM polish step. Default: false. */
|
|
15
|
+
llm?: boolean;
|
|
16
|
+
/** Injected Anthropic client (tests). Falls back to a sanitizing client. */
|
|
17
|
+
client?: Anthropic;
|
|
18
|
+
model?: string;
|
|
19
|
+
/** Print JSON to stdout, write nothing. */
|
|
20
|
+
dryRun?: boolean;
|
|
21
|
+
}
|
|
22
|
+
/** Convert a glob (supports **, *, ?) to an anchored RegExp over posix paths. */
|
|
23
|
+
export declare function globToRegex(glob: string): RegExp;
|
|
24
|
+
/** Resolve the absolute, sorted spec file list for a discovery mode. */
|
|
25
|
+
export declare function resolveSpecFiles(cwd: string, opts: Pick<ConventionsInitOptions, "mode" | "folders" | "glob">): string[];
|
|
26
|
+
export interface GenerationResult {
|
|
27
|
+
conventions: Conventions;
|
|
28
|
+
stats: ScanStats;
|
|
29
|
+
}
|
|
30
|
+
export declare function generateConventions(files: string[], opts?: ConventionsInitOptions & {
|
|
31
|
+
onProgress?: ScanOptions["onProgress"];
|
|
32
|
+
}): Promise<GenerationResult>;
|
|
33
|
+
/**
|
|
34
|
+
* Optional LLM refinement. Sends ONLY the integer stats blob + the draft (no
|
|
35
|
+
* raw source) and asks Claude to correct obvious misjudgements while keeping
|
|
36
|
+
* the exact schema. Fully defensive: any failure returns null → caller keeps
|
|
37
|
+
* the deterministic draft.
|
|
38
|
+
*/
|
|
39
|
+
export declare function polishConventions(draft: Conventions, stats: ScanStats, client: Anthropic, model: string): Promise<Conventions | null>;
|
|
40
|
+
export interface WriteResult {
|
|
41
|
+
path: string;
|
|
42
|
+
json: string;
|
|
43
|
+
written: boolean;
|
|
44
|
+
}
|
|
45
|
+
export declare function writeConventionsArtifact(cwd: string, conventions: Conventions, opts?: {
|
|
46
|
+
dryRun?: boolean;
|
|
47
|
+
}): WriteResult;
|
|
48
|
+
export declare function runConventionsWizard(cwd: string, opts?: ConventionsInitOptions & {
|
|
49
|
+
yes?: boolean;
|
|
50
|
+
}): Promise<void>;
|
|
51
|
+
//# sourceMappingURL=conventionsInit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conventionsInit.d.ts","sourceRoot":"","sources":["../../src/cli/conventionsInit.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,SAAS,MAAM,mBAAmB,CAAC;AAO/C,OAAO,EAGL,KAAK,WAAW,EACjB,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,SAAS,EAEf,MAAM,0BAA0B,CAAC;AAelC,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;AAEzD,MAAM,WAAW,sBAAsB;IACrC,8BAA8B;IAC9B,IAAI,CAAC,EAAE,aAAa,CAAC;IACrB,sDAAsD;IACtD,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,yCAAyC;IACzC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,wDAAwD;IACxD,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,4EAA4E;IAC5E,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2CAA2C;IAC3C,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAkCD,iFAAiF;AACjF,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CA0BhD;AAED,wEAAwE;AACxE,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,IAAI,CAAC,sBAAsB,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAAC,GAC9D,MAAM,EAAE,CAcV;AAMD,MAAM,WAAW,gBAAgB;IAC/B,WAAW,EAAE,WAAW,CAAC;IACzB,KAAK,EAAE,SAAS,CAAC;CAClB;AAED,wBAAsB,mBAAmB,CACvC,KAAK,EAAE,MAAM,EAAE,EACf,IAAI,GAAE,sBAAsB,GAAG;IAAE,UAAU,CAAC,EAAE,WAAW,CAAC,YAAY,CAAC,CAAA;CAAO,GAC7E,OAAO,CAAC,gBAAgB,CAAC,CAsB3B;AAQD;;;;;GAKG;AACH,wBAAsB,iBAAiB,CACrC,KAAK,EAAE,WAAW,EAClB,KAAK,EAAE,SAAS,EAChB,MAAM,EAAE,SAAS,EACjB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CA4C7B;AAMD,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,wBAAgB,wBAAwB,CACtC,GAAG,EAAE,MAAM,EACX,WAAW,EAAE,WAAW,EACxB,IAAI,GAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAA;CAAO,GAC9B,WAAW,CAQb;AAMD,wBAAsB,oBAAoB,CACxC,GAAG,EAAE,MAAM,EACX,IAAI,GAAE,sBAAsB,GAAG;IAAE,GAAG,CAAC,EAAE,OAAO,CAAA;CAAO,GACpD,OAAO,CAAC,IAAI,CAAC,CAgHf"}
|