executable-stories-formatters 1.20.0 → 1.21.0
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 +0 -37
- package/dist/cli.js +438 -965
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +201 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +78 -6
- package/dist/index.d.ts +78 -6
- package/dist/index.js +196 -2
- package/dist/index.js.map +1 -1
- package/package.json +2 -3
- package/templates/astro-thin/astro.config.mjs +0 -46
- package/templates/astro-thin/executable-stories.config.mjs +0 -76
- package/templates/astro-thin/gitignore +0 -3
- package/templates/astro-thin/package.json +0 -20
- package/templates/astro-thin/reports/sample-run.json +0 -214
- package/templates/astro-thin/src/content/docs/404.md +0 -20
- package/templates/astro-thin/src/content/docs/guides/writing-docs.mdx +0 -58
- package/templates/astro-thin/src/content/docs/index.mdx +0 -51
- package/templates/astro-thin/src/content.config.ts +0 -33
- package/templates/astro-thin/src/styles/stories.css +0 -32
- package/templates/astro-thin/tsconfig.json +0 -5
package/dist/index.cjs
CHANGED
|
@@ -87,6 +87,7 @@ __export(src_exports, {
|
|
|
87
87
|
computeTestMetrics: () => computeTestMetrics,
|
|
88
88
|
copyMarkdownAssets: () => copyMarkdownAssets,
|
|
89
89
|
createAnchor: () => createAnchor,
|
|
90
|
+
createJevClient: () => createJevClient,
|
|
90
91
|
createPrCommentSummary: () => createPrCommentSummary,
|
|
91
92
|
createReportGenerator: () => createReportGenerator,
|
|
92
93
|
createTestRailProvider: () => createTestRailProvider,
|
|
@@ -99,6 +100,9 @@ __export(src_exports, {
|
|
|
99
100
|
diffRuns: () => diffRuns,
|
|
100
101
|
diffStoryReports: () => diffStoryReports,
|
|
101
102
|
emptyLockfile: () => emptyLockfile,
|
|
103
|
+
enrichGoal: () => enrichGoal,
|
|
104
|
+
enrichReview: () => enrichReview,
|
|
105
|
+
enrichTriage: () => enrichTriage,
|
|
102
106
|
findGitDir: () => findGitDir,
|
|
103
107
|
formatDuration: () => import_duration2.formatDuration,
|
|
104
108
|
generateRunComparison: () => generateRunComparison,
|
|
@@ -113,6 +117,7 @@ __export(src_exports, {
|
|
|
113
117
|
isProviderName: () => isProviderName,
|
|
114
118
|
isReviewableSource: () => isReviewableSource,
|
|
115
119
|
isTestFile: () => isTestFile,
|
|
120
|
+
jevFromEnv: () => jevFromEnv,
|
|
116
121
|
joinNameAndExt: () => joinNameAndExt,
|
|
117
122
|
listScenarios: () => listScenarios,
|
|
118
123
|
loadHistory: () => loadHistory,
|
|
@@ -5146,6 +5151,50 @@ function sourceBaseKey(sourceFile) {
|
|
|
5146
5151
|
return dot > slash ? sourceFile.slice(0, dot) : sourceFile;
|
|
5147
5152
|
}
|
|
5148
5153
|
|
|
5154
|
+
// src/jev.ts
|
|
5155
|
+
var JEV_ENDPOINT = "https://api.typesafe.ai/v1/systemone";
|
|
5156
|
+
var JEV_MODEL = "jev-latest";
|
|
5157
|
+
function createJevClient(options) {
|
|
5158
|
+
const {
|
|
5159
|
+
apiKey,
|
|
5160
|
+
model = JEV_MODEL,
|
|
5161
|
+
endpoint = JEV_ENDPOINT,
|
|
5162
|
+
fetch = globalThis.fetch,
|
|
5163
|
+
timeoutMs = 1e4
|
|
5164
|
+
} = options;
|
|
5165
|
+
return {
|
|
5166
|
+
model,
|
|
5167
|
+
// One attempt per question set; add backoff on 429/529 if a loop hits rate limits.
|
|
5168
|
+
async ask(state, questions) {
|
|
5169
|
+
const response = await fetch(endpoint, {
|
|
5170
|
+
method: "POST",
|
|
5171
|
+
headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" },
|
|
5172
|
+
body: JSON.stringify({ model, state, questions }),
|
|
5173
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
5174
|
+
});
|
|
5175
|
+
if (!response.ok) throw new Error(`Jev ${response.status}: ${(await response.text()).slice(0, 200)}`);
|
|
5176
|
+
const body = await response.json();
|
|
5177
|
+
if (!body.answers) throw new Error("Jev response has no answers");
|
|
5178
|
+
return body.answers;
|
|
5179
|
+
}
|
|
5180
|
+
};
|
|
5181
|
+
}
|
|
5182
|
+
function jevFromEnv(env = process.env) {
|
|
5183
|
+
const apiKey = env.JEV_API_KEY;
|
|
5184
|
+
if (!apiKey) return void 0;
|
|
5185
|
+
return createJevClient({
|
|
5186
|
+
apiKey,
|
|
5187
|
+
...env.JEV_MODEL ? { model: env.JEV_MODEL } : {},
|
|
5188
|
+
...env.JEV_ENDPOINT ? { endpoint: env.JEV_ENDPOINT } : {}
|
|
5189
|
+
});
|
|
5190
|
+
}
|
|
5191
|
+
function asChoice(answer) {
|
|
5192
|
+
return answer?.type === "choice" ? answer : void 0;
|
|
5193
|
+
}
|
|
5194
|
+
function asNoul(answer) {
|
|
5195
|
+
return answer?.type === "noul" ? answer.noul : void 0;
|
|
5196
|
+
}
|
|
5197
|
+
|
|
5149
5198
|
// src/review/build-review.ts
|
|
5150
5199
|
var STRENGTH_RANK = {
|
|
5151
5200
|
none: 0,
|
|
@@ -5346,6 +5395,61 @@ function buildReview(run, context = { changedFiles: [] }) {
|
|
|
5346
5395
|
codeDiffs: (context.codeDiffs ?? []).map((d) => buildCodeDiff(d, run, context))
|
|
5347
5396
|
};
|
|
5348
5397
|
}
|
|
5398
|
+
var REVIEW_CHANGE_TYPE_MIN_CONFIDENCE = 0.6;
|
|
5399
|
+
var PATCH_EXCERPT_CHARS = 4e3;
|
|
5400
|
+
var CHANGE_TYPE_CRITERIA = {
|
|
5401
|
+
feature: "new user-visible behaviour or capability",
|
|
5402
|
+
bugfix: "corrects behaviour that was wrong",
|
|
5403
|
+
refactor: "restructures code without changing behaviour",
|
|
5404
|
+
perf: "same behaviour, faster or cheaper",
|
|
5405
|
+
deps: "dependency, toolchain, or lockfile change"
|
|
5406
|
+
};
|
|
5407
|
+
async function enrichReview(review, jev) {
|
|
5408
|
+
const untagged = review.claims.filter((c) => c.changeType === "unknown" && c.coversFiles.length > 0);
|
|
5409
|
+
if (untagged.length === 0) return review;
|
|
5410
|
+
const hunksByPath = /* @__PURE__ */ new Map();
|
|
5411
|
+
for (const group of review.context.codeDiffs ?? []) {
|
|
5412
|
+
for (const file of parseUnifiedDiff(group.patch)) {
|
|
5413
|
+
const path15 = file.newPath ?? file.oldPath;
|
|
5414
|
+
if (!path15) continue;
|
|
5415
|
+
const text2 = file.hunks.flatMap((h) => h.lines.map((l) => (l.kind === "add" ? "+" : l.kind === "del" ? "-" : " ") + l.text));
|
|
5416
|
+
hunksByPath.set(path15, [...hunksByPath.get(path15) ?? [], ...text2]);
|
|
5417
|
+
}
|
|
5418
|
+
}
|
|
5419
|
+
const inferred = /* @__PURE__ */ new Map();
|
|
5420
|
+
await Promise.all(
|
|
5421
|
+
untagged.map(async (claim) => {
|
|
5422
|
+
const patch = claim.coversFiles.flatMap((f) => hunksByPath.get(f) ?? []).join("\n").slice(0, PATCH_EXCERPT_CHARS);
|
|
5423
|
+
const answers = await jev.ask(
|
|
5424
|
+
{
|
|
5425
|
+
scenario: claim.scenario,
|
|
5426
|
+
steps: claim.testCase.story.steps.map((s) => `${s.keyword} ${s.text}`),
|
|
5427
|
+
...claim.intent ? { intent: claim.intent } : {},
|
|
5428
|
+
changedFiles: claim.coversFiles,
|
|
5429
|
+
...patch ? { patch } : {}
|
|
5430
|
+
},
|
|
5431
|
+
{
|
|
5432
|
+
changeType: {
|
|
5433
|
+
type: "choice",
|
|
5434
|
+
instructions: "What kind of change does this scenario prove?",
|
|
5435
|
+
criteria: CHANGE_TYPE_CRITERIA
|
|
5436
|
+
}
|
|
5437
|
+
}
|
|
5438
|
+
);
|
|
5439
|
+
const answer = asChoice(answers.changeType);
|
|
5440
|
+
if (!answer || answer.confidence < REVIEW_CHANGE_TYPE_MIN_CONFIDENCE) return;
|
|
5441
|
+
const changeType = answer.choice;
|
|
5442
|
+
if (VALID_CHANGE_TYPES.has(changeType)) inferred.set(claim.id, { changeType, confidence: answer.confidence });
|
|
5443
|
+
})
|
|
5444
|
+
);
|
|
5445
|
+
return {
|
|
5446
|
+
...review,
|
|
5447
|
+
claims: review.claims.map((claim) => {
|
|
5448
|
+
const hit = inferred.get(claim.id);
|
|
5449
|
+
return hit ? { ...claim, changeType: hit.changeType, changeTypeConfidence: hit.confidence } : claim;
|
|
5450
|
+
})
|
|
5451
|
+
};
|
|
5452
|
+
}
|
|
5349
5453
|
function codeDiffDiagnostics(review) {
|
|
5350
5454
|
const issues = [];
|
|
5351
5455
|
for (const evidence of review.codeDiffs) {
|
|
@@ -9679,9 +9783,41 @@ function buildGoal(args, _deps = {}) {
|
|
|
9679
9783
|
requirements,
|
|
9680
9784
|
regressions,
|
|
9681
9785
|
regressionsEnforced: Boolean(baseline && args.enforceNoRegressions),
|
|
9682
|
-
ratchet: { enforced: Boolean(baseline && args.enforceRatchet), violations }
|
|
9786
|
+
ratchet: { enforced: Boolean(baseline && args.enforceRatchet), violations, advisories: [] }
|
|
9683
9787
|
};
|
|
9684
9788
|
}
|
|
9789
|
+
var GOAL_WEAKENED_MIN_PROBABILITY = 0.75;
|
|
9790
|
+
var stepText = (tc) => tc.story.steps.map((s) => `${s.keyword} ${s.text}`);
|
|
9791
|
+
async function enrichGoal(report, args, jev) {
|
|
9792
|
+
const { baseline } = args;
|
|
9793
|
+
if (!baseline || !report.ratchet.enforced) return report;
|
|
9794
|
+
const current = new Map(args.run.testCases.map((tc) => [tc.id, tc]));
|
|
9795
|
+
const flagged = new Set(report.ratchet.violations.map((v) => v.id));
|
|
9796
|
+
const rewritten = baseline.testCases.flatMap((base) => {
|
|
9797
|
+
const now = current.get(base.id);
|
|
9798
|
+
if (!now || flagged.has(base.id)) return [];
|
|
9799
|
+
const before = stepText(base);
|
|
9800
|
+
const after = stepText(now);
|
|
9801
|
+
return before.join("\n") === after.join("\n") ? [] : [{ base, before, after }];
|
|
9802
|
+
});
|
|
9803
|
+
const advisories = (await Promise.all(
|
|
9804
|
+
rewritten.map(async ({ base, before, after }) => {
|
|
9805
|
+
const answers = await jev.ask(
|
|
9806
|
+
{ baseline: before, now: after },
|
|
9807
|
+
{
|
|
9808
|
+
weakened: {
|
|
9809
|
+
type: "noul",
|
|
9810
|
+
instructions: "Does the NOW scenario check less than the BASELINE scenario: fewer or weaker assertions, looser expectations, or a removed check?"
|
|
9811
|
+
}
|
|
9812
|
+
}
|
|
9813
|
+
);
|
|
9814
|
+
const p = asNoul(answers.weakened);
|
|
9815
|
+
if (p === void 0 || p < GOAL_WEAKENED_MIN_PROBABILITY) return [];
|
|
9816
|
+
return [{ id: base.id, title: base.story.scenario, kind: "weakened", detail: `jev ${p.toFixed(2)}: steps rewritten, checks less` }];
|
|
9817
|
+
})
|
|
9818
|
+
)).flat();
|
|
9819
|
+
return { ...report, ratchet: { ...report.ratchet, advisories } };
|
|
9820
|
+
}
|
|
9685
9821
|
function evaluate(selector, matched) {
|
|
9686
9822
|
const passed = matched.filter((tc) => tc.status === "passed").length;
|
|
9687
9823
|
const failing = matched.filter((tc) => tc.status !== "passed").map((tc) => tc.story.scenario);
|
|
@@ -9723,6 +9859,9 @@ function renderGoal(report, format) {
|
|
|
9723
9859
|
lines.push(` ${v.kind}: ${v.title} (${v.detail})`);
|
|
9724
9860
|
}
|
|
9725
9861
|
}
|
|
9862
|
+
for (const v of report.ratchet.advisories) {
|
|
9863
|
+
lines.push(` advisory ${v.kind}: ${v.title} (${v.detail})`);
|
|
9864
|
+
}
|
|
9726
9865
|
}
|
|
9727
9866
|
return lines.join("\n");
|
|
9728
9867
|
}
|
|
@@ -9792,6 +9931,55 @@ function buildTriage(args, _deps = {}) {
|
|
|
9792
9931
|
items
|
|
9793
9932
|
};
|
|
9794
9933
|
}
|
|
9934
|
+
var TRIAGE_SUGGEST_MIN_PROBABILITY = 0.5;
|
|
9935
|
+
var FAILURE_KIND_CRITERIA = {
|
|
9936
|
+
product: "the application code under test behaves wrongly",
|
|
9937
|
+
test: "the scenario, assertion, fixture, or test data is wrong or stale",
|
|
9938
|
+
infra: "environment, network, timeout, resource, or flaky-timing failure"
|
|
9939
|
+
};
|
|
9940
|
+
async function enrichTriage(report, testCases, jev, codeowners) {
|
|
9941
|
+
const candidates = new Set(testCases.flatMap((tc) => tc.story.covers ?? []));
|
|
9942
|
+
for (const rule of codeowners ?? []) candidates.add(rule.pattern);
|
|
9943
|
+
const criteria = Object.fromEntries([...candidates].map((path15) => [path15, null]));
|
|
9944
|
+
const byId = new Map(testCases.map((tc) => [tc.id, tc]));
|
|
9945
|
+
const items = await Promise.all(
|
|
9946
|
+
report.items.map(async (item) => {
|
|
9947
|
+
if (item.covers.length > 0) return item;
|
|
9948
|
+
const tc = byId.get(item.id);
|
|
9949
|
+
const answers = await jev.ask(
|
|
9950
|
+
{
|
|
9951
|
+
scenario: item.scenario,
|
|
9952
|
+
steps: tc?.story.steps.map((s) => `${s.keyword} ${s.text}`) ?? [],
|
|
9953
|
+
testFile: item.location,
|
|
9954
|
+
error: item.errorMessage ?? null
|
|
9955
|
+
},
|
|
9956
|
+
{
|
|
9957
|
+
kind: {
|
|
9958
|
+
type: "choice",
|
|
9959
|
+
instructions: "What is most likely broken, given this failing scenario and its error?",
|
|
9960
|
+
criteria: FAILURE_KIND_CRITERIA
|
|
9961
|
+
},
|
|
9962
|
+
...candidates.size > 0 ? {
|
|
9963
|
+
covers: {
|
|
9964
|
+
type: "choice",
|
|
9965
|
+
instructions: "Which of these product paths does the fix for this failure most likely land in?",
|
|
9966
|
+
criteria
|
|
9967
|
+
}
|
|
9968
|
+
} : {}
|
|
9969
|
+
}
|
|
9970
|
+
);
|
|
9971
|
+
const kind = asChoice(answers.kind);
|
|
9972
|
+
const covers = asChoice(answers.covers);
|
|
9973
|
+
const probability = covers ? covers.probabilities[covers.choice] ?? 0 : 0;
|
|
9974
|
+
return {
|
|
9975
|
+
...item,
|
|
9976
|
+
...kind && kind.choice in FAILURE_KIND_CRITERIA ? { failureKind: { kind: kind.choice, confidence: kind.confidence } } : {},
|
|
9977
|
+
...covers && probability >= TRIAGE_SUGGEST_MIN_PROBABILITY ? { suggestedCovers: { path: covers.choice, probability } } : {}
|
|
9978
|
+
};
|
|
9979
|
+
})
|
|
9980
|
+
);
|
|
9981
|
+
return { ...report, items };
|
|
9982
|
+
}
|
|
9795
9983
|
function renderTriage(report, format, options = {}) {
|
|
9796
9984
|
if (format === "json") return JSON.stringify(report, null, 2);
|
|
9797
9985
|
if (report.items.length === 0) {
|
|
@@ -9809,9 +9997,14 @@ function renderTriage(report, format, options = {}) {
|
|
|
9809
9997
|
}
|
|
9810
9998
|
if (item.covers.length > 0) {
|
|
9811
9999
|
lines.push(` fix: ${item.covers.join(", ")}`);
|
|
10000
|
+
} else if (item.suggestedCovers) {
|
|
10001
|
+
lines.push(` fix: ${item.suggestedCovers.path}? (jev ${item.suggestedCovers.probability.toFixed(2)}, no covers declared)`);
|
|
9812
10002
|
} else {
|
|
9813
10003
|
lines.push(" fix: (no covers declared \u2014 add `covers` to route this to code)");
|
|
9814
10004
|
}
|
|
10005
|
+
if (item.failureKind) {
|
|
10006
|
+
lines.push(` kind: ${item.failureKind.kind} (jev ${item.failureKind.confidence.toFixed(2)})`);
|
|
10007
|
+
}
|
|
9815
10008
|
if (item.tickets.length > 0) {
|
|
9816
10009
|
lines.push(` ticket: ${item.tickets.join(", ")}`);
|
|
9817
10010
|
}
|
|
@@ -10062,7 +10255,8 @@ function renderClaim(lines, claim) {
|
|
|
10062
10255
|
lines.push("");
|
|
10063
10256
|
lines.push(`- File: \`${claim.sourceFile}:${claim.sourceLine}\``);
|
|
10064
10257
|
if (claim.changeType !== "unknown") {
|
|
10065
|
-
|
|
10258
|
+
const inferred = claim.changeTypeConfidence === void 0 ? "" : ` _(inferred, jev ${claim.changeTypeConfidence.toFixed(2)})_`;
|
|
10259
|
+
lines.push(`- Change: \`${claim.changeType}\`${inferred}`);
|
|
10066
10260
|
}
|
|
10067
10261
|
const tickets = claim.testCase.story.tickets ?? [];
|
|
10068
10262
|
if (tickets.length > 0) {
|
|
@@ -10784,6 +10978,7 @@ function normalizePlaywrightResults(testResults, adapterOptions, canonicalizeOpt
|
|
|
10784
10978
|
computeTestMetrics,
|
|
10785
10979
|
copyMarkdownAssets,
|
|
10786
10980
|
createAnchor,
|
|
10981
|
+
createJevClient,
|
|
10787
10982
|
createPrCommentSummary,
|
|
10788
10983
|
createReportGenerator,
|
|
10789
10984
|
createTestRailProvider,
|
|
@@ -10796,6 +10991,9 @@ function normalizePlaywrightResults(testResults, adapterOptions, canonicalizeOpt
|
|
|
10796
10991
|
diffRuns,
|
|
10797
10992
|
diffStoryReports,
|
|
10798
10993
|
emptyLockfile,
|
|
10994
|
+
enrichGoal,
|
|
10995
|
+
enrichReview,
|
|
10996
|
+
enrichTriage,
|
|
10799
10997
|
findGitDir,
|
|
10800
10998
|
formatDuration,
|
|
10801
10999
|
generateRunComparison,
|
|
@@ -10810,6 +11008,7 @@ function normalizePlaywrightResults(testResults, adapterOptions, canonicalizeOpt
|
|
|
10810
11008
|
isProviderName,
|
|
10811
11009
|
isReviewableSource,
|
|
10812
11010
|
isTestFile,
|
|
11011
|
+
jevFromEnv,
|
|
10813
11012
|
joinNameAndExt,
|
|
10814
11013
|
listScenarios,
|
|
10815
11014
|
loadHistory,
|