@kb-labs/qa-entry 2.109.0 → 2.111.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/dist/cli/commands/baseline-diff.d.ts +1 -3
- package/dist/cli/commands/baseline-diff.js +3 -3
- package/dist/cli/commands/baseline-diff.js.map +1 -1
- package/dist/cli/commands/baseline-status.d.ts +1 -3
- package/dist/cli/commands/baseline-status.js +1 -1
- package/dist/cli/commands/baseline-status.js.map +1 -1
- package/dist/cli/commands/baseline-update.d.ts +1 -3
- package/dist/cli/commands/baseline-update.js +1 -1
- package/dist/cli/commands/baseline-update.js.map +1 -1
- package/dist/cli/commands/flags.d.ts +70 -1
- package/dist/cli/commands/flags.js +67 -1
- package/dist/cli/commands/flags.js.map +1 -1
- package/dist/cli/commands/qa-check.d.ts +1 -3
- package/dist/cli/commands/qa-check.js +1 -1
- package/dist/cli/commands/qa-check.js.map +1 -1
- package/dist/cli/commands/qa-ci-evidence-capture.d.ts +29 -0
- package/dist/cli/commands/qa-ci-evidence-capture.js +59 -0
- package/dist/cli/commands/qa-ci-evidence-capture.js.map +1 -0
- package/dist/cli/commands/qa-ci-evidence-sync.d.ts +31 -0
- package/dist/cli/commands/qa-ci-evidence-sync.js +51 -0
- package/dist/cli/commands/qa-ci-evidence-sync.js.map +1 -0
- package/dist/cli/commands/qa-ci-overview.d.ts +21 -0
- package/dist/cli/commands/qa-ci-overview.js +49 -0
- package/dist/cli/commands/qa-ci-overview.js.map +1 -0
- package/dist/cli/commands/qa-e2e-flaky.d.ts +1 -3
- package/dist/cli/commands/qa-e2e-flaky.js +6 -6
- package/dist/cli/commands/qa-e2e-flaky.js.map +1 -1
- package/dist/cli/commands/qa-gate.d.ts +1 -3
- package/dist/cli/commands/qa-gate.js +1 -1
- package/dist/cli/commands/qa-gate.js.map +1 -1
- package/dist/cli/commands/qa-history.d.ts +1 -3
- package/dist/cli/commands/qa-history.js +1 -1
- package/dist/cli/commands/qa-history.js.map +1 -1
- package/dist/cli/commands/qa-regressions.d.ts +1 -3
- package/dist/cli/commands/qa-regressions.js +1 -1
- package/dist/cli/commands/qa-regressions.js.map +1 -1
- package/dist/cli/commands/qa-run.d.ts +1 -3
- package/dist/cli/commands/qa-run.js +1 -1
- package/dist/cli/commands/qa-run.js.map +1 -1
- package/dist/cli/commands/qa-stats.d.ts +1 -3
- package/dist/cli/commands/qa-stats.js +1 -1
- package/dist/cli/commands/qa-stats.js.map +1 -1
- package/dist/cli/commands/qa-trends.d.ts +1 -3
- package/dist/cli/commands/qa-trends.js +1 -1
- package/dist/cli/commands/qa-trends.js.map +1 -1
- package/dist/index.js +99 -3
- package/dist/index.js.map +1 -1
- package/dist/manifest.d.ts +4 -0
- package/dist/manifest.js +99 -3
- package/dist/manifest.js.map +1 -1
- package/dist/manifest.json +245 -12
- package/dist/rest/handlers/ci-overview-handler.d.ts +7 -0
- package/dist/rest/handlers/ci-overview-handler.js +19 -0
- package/dist/rest/handlers/ci-overview-handler.js.map +1 -0
- package/dist/widgets/__federation_expose_QADashboard.js +1 -1
- package/dist/widgets/mf-manifest.json +12 -12
- package/dist/widgets/mf-stats.json +12 -12
- package/dist/widgets/remoteEntry.js +2 -2
- package/package.json +4 -4
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { mkdirSync, writeFileSync } from 'fs';
|
|
2
|
+
import { resolve, dirname } from 'path';
|
|
3
|
+
import { defineCommand } from '@kb-labs/sdk';
|
|
4
|
+
import { loadCiDossiers, analyzeCiReliability } from '@kb-labs/qa-core';
|
|
5
|
+
|
|
6
|
+
// src/cli/commands/qa-ci-overview.ts
|
|
7
|
+
var qa_ci_overview_default = defineCommand({
|
|
8
|
+
id: "qa:ci-overview",
|
|
9
|
+
description: "Show a compact CI reliability overview from captured dossiers",
|
|
10
|
+
handler: {
|
|
11
|
+
async execute(ctx, input) {
|
|
12
|
+
const cwd = ctx.cwd ?? process.cwd();
|
|
13
|
+
const inputPath = resolve(cwd, input.flags.input);
|
|
14
|
+
const dossiers = loadCiDossiers(inputPath);
|
|
15
|
+
if (dossiers.length === 0) {
|
|
16
|
+
ctx.ui?.error?.(`No CI dossiers found under ${inputPath}. Run \`kb qa ci evidence capture\` in CI first.`);
|
|
17
|
+
return { ok: false, error: "Command failed" };
|
|
18
|
+
}
|
|
19
|
+
const overview = analyzeCiReliability(dossiers);
|
|
20
|
+
if (input.flags.output) {
|
|
21
|
+
const output = resolve(cwd, input.flags.output);
|
|
22
|
+
mkdirSync(dirname(output), { recursive: true });
|
|
23
|
+
writeFileSync(output, `${JSON.stringify(overview, null, 2)}
|
|
24
|
+
`);
|
|
25
|
+
}
|
|
26
|
+
if (input.flags.json) {
|
|
27
|
+
ctx.ui?.json?.(overview);
|
|
28
|
+
} else {
|
|
29
|
+
ctx.ui?.success?.(`CI reliability (${overview.runsAnalyzed} run${overview.runsAnalyzed === 1 ? "" : "s"})`, {
|
|
30
|
+
sections: [
|
|
31
|
+
{ header: "Summary", items: [
|
|
32
|
+
`failed runs: ${overview.failedRuns}`,
|
|
33
|
+
`failed before tests: ${overview.failedBeforeTests}`,
|
|
34
|
+
`evidence: ${overview.collection.complete} complete, ${overview.collection.partial} partial`,
|
|
35
|
+
...input.flags.output ? [`saved JSON: ${resolve(cwd, input.flags.output)}`] : []
|
|
36
|
+
] },
|
|
37
|
+
{ header: "Top findings", items: overview.findings.length > 0 ? overview.findings.map((finding) => `${finding.fingerprint} \u2014 ${finding.occurrences} occurrence(s), ${(finding.confidence * 100).toFixed(0)}% confidence; ${finding.affectedJobs.join(", ")}`) : ["No failed CI steps in the captured evidence."] },
|
|
38
|
+
{ header: "Drill down", items: overview.drillDown.runIds.map((runId) => `kb qa ci run --run-id ${runId} --json`) }
|
|
39
|
+
]
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
return { ok: true };
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
export { qa_ci_overview_default as default };
|
|
48
|
+
//# sourceMappingURL=qa-ci-overview.js.map
|
|
49
|
+
//# sourceMappingURL=qa-ci-overview.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/cli/commands/qa-ci-overview.ts"],"names":[],"mappings":";;;;;;AAMA,IAAO,yBAAQ,aAAA,CAA6D;AAAA,EAC1E,EAAA,EAAI,gBAAA;AAAA,EACJ,WAAA,EAAa,+DAAA;AAAA,EACb,OAAA,EAAS;AAAA,IACP,MAAM,OAAA,CAAQ,GAAA,EAAsB,KAAA,EAA4D;AAC9F,MAAA,MAAM,GAAA,GAAM,GAAA,CAAI,GAAA,IAAO,OAAA,CAAQ,GAAA,EAAI;AACnC,MAAA,MAAM,SAAA,GAAY,OAAA,CAAQ,GAAA,EAAK,KAAA,CAAM,MAAM,KAAK,CAAA;AAChD,MAAA,MAAM,QAAA,GAAW,eAAe,SAAS,CAAA;AACzC,MAAA,IAAI,QAAA,CAAS,WAAW,CAAA,EAAG;AACzB,QAAA,GAAA,CAAI,EAAA,EAAI,KAAA,GAAQ,CAAA,2BAAA,EAA8B,SAAS,CAAA,gDAAA,CAAkD,CAAA;AACzG,QAAA,OAAO,EAAE,EAAA,EAAI,KAAA,EAAO,KAAA,EAAO,gBAAA,EAAiB;AAAA,MAC9C;AACA,MAAA,MAAM,QAAA,GAAW,qBAAqB,QAAQ,CAAA;AAC9C,MAAA,IAAI,KAAA,CAAM,MAAM,MAAA,EAAQ;AACtB,QAAA,MAAM,MAAA,GAAS,OAAA,CAAQ,GAAA,EAAK,KAAA,CAAM,MAAM,MAAM,CAAA;AAC9C,QAAA,SAAA,CAAU,QAAQ,MAAM,CAAA,EAAG,EAAE,SAAA,EAAW,MAAM,CAAA;AAC9C,QAAA,aAAA,CAAc,QAAQ,CAAA,EAAG,IAAA,CAAK,UAAU,QAAA,EAAU,IAAA,EAAM,CAAC,CAAC;AAAA,CAAI,CAAA;AAAA,MAChE;AACA,MAAA,IAAI,KAAA,CAAM,MAAM,IAAA,EAAM;AACpB,QAAA,GAAA,CAAI,EAAA,EAAI,OAAO,QAAQ,CAAA;AAAA,MACzB,CAAA,MAAO;AACL,QAAA,GAAA,CAAI,EAAA,EAAI,OAAA,GAAU,CAAA,gBAAA,EAAmB,QAAA,CAAS,YAAY,CAAA,IAAA,EAAO,QAAA,CAAS,YAAA,KAAiB,CAAA,GAAI,EAAA,GAAK,GAAG,CAAA,CAAA,CAAA,EAAK;AAAA,UAC1G,QAAA,EAAU;AAAA,YACR,EAAE,MAAA,EAAQ,SAAA,EAAW,KAAA,EAAO;AAAA,cAC1B,CAAA,aAAA,EAAgB,SAAS,UAAU,CAAA,CAAA;AAAA,cACnC,CAAA,qBAAA,EAAwB,SAAS,iBAAiB,CAAA,CAAA;AAAA,cAClD,aAAa,QAAA,CAAS,UAAA,CAAW,QAAQ,CAAA,WAAA,EAAc,QAAA,CAAS,WAAW,OAAO,CAAA,QAAA,CAAA;AAAA,cAClF,GAAI,KAAA,CAAM,KAAA,CAAM,MAAA,GAAS,CAAC,CAAA,YAAA,EAAe,OAAA,CAAQ,GAAA,EAAK,KAAA,CAAM,KAAA,CAAM,MAAM,CAAC,CAAA,CAAE,IAAI;AAAC,aAClF,EAAE;AAAA,YACF,EAAE,MAAA,EAAQ,cAAA,EAAgB,KAAA,EAAO,SAAS,QAAA,CAAS,MAAA,GAAS,CAAA,GACxD,QAAA,CAAS,SAAS,GAAA,CAAI,CAAA,OAAA,KAAW,CAAA,EAAG,OAAA,CAAQ,WAAW,CAAA,QAAA,EAAM,OAAA,CAAQ,WAAW,CAAA,gBAAA,EAAA,CAAoB,QAAQ,UAAA,GAAa,GAAA,EAAK,OAAA,CAAQ,CAAC,CAAC,CAAA,cAAA,EAAiB,OAAA,CAAQ,YAAA,CAAa,IAAA,CAAK,IAAI,CAAC,CAAA,CAAE,CAAA,GAC1L,CAAC,8CAA8C,CAAA,EAAE;AAAA,YACrD,EAAE,MAAA,EAAQ,YAAA,EAAc,KAAA,EAAO,QAAA,CAAS,SAAA,CAAU,MAAA,CAAO,GAAA,CAAI,CAAA,KAAA,KAAS,CAAA,sBAAA,EAAyB,KAAK,CAAA,OAAA,CAAS,CAAA;AAAE;AACjH,SACD,CAAA;AAAA,MACH;AACA,MAAA,OAAO,EAAE,IAAI,IAAA,EAAK;AAAA,IACpB;AAAA;AAEJ,CAAC","file":"qa-ci-overview.js","sourcesContent":["import { mkdirSync, writeFileSync } from 'node:fs';\nimport { dirname, resolve } from 'node:path';\nimport { defineCommand, type CLIInput, type PluginContextV3 , type CommandResult} from '@kb-labs/sdk';\nimport { analyzeCiReliability, loadCiDossiers } from '@kb-labs/qa-core';\nimport type { QaCiOverviewFlags } from './flags.js';\n\nexport default defineCommand<unknown, CLIInput<QaCiOverviewFlags>, unknown>({\n id: 'qa:ci-overview',\n description: 'Show a compact CI reliability overview from captured dossiers',\n handler: {\n async execute(ctx: PluginContextV3, input: CLIInput<QaCiOverviewFlags>): Promise<CommandResult> {\n const cwd = ctx.cwd ?? process.cwd();\n const inputPath = resolve(cwd, input.flags.input);\n const dossiers = loadCiDossiers(inputPath);\n if (dossiers.length === 0) {\n ctx.ui?.error?.(`No CI dossiers found under ${inputPath}. Run \\`kb qa ci evidence capture\\` in CI first.`);\n return { ok: false, error: 'Command failed' };\n }\n const overview = analyzeCiReliability(dossiers);\n if (input.flags.output) {\n const output = resolve(cwd, input.flags.output);\n mkdirSync(dirname(output), { recursive: true });\n writeFileSync(output, `${JSON.stringify(overview, null, 2)}\\n`);\n }\n if (input.flags.json) {\n ctx.ui?.json?.(overview);\n } else {\n ctx.ui?.success?.(`CI reliability (${overview.runsAnalyzed} run${overview.runsAnalyzed === 1 ? '' : 's'})`, {\n sections: [\n { header: 'Summary', items: [\n `failed runs: ${overview.failedRuns}`,\n `failed before tests: ${overview.failedBeforeTests}`,\n `evidence: ${overview.collection.complete} complete, ${overview.collection.partial} partial`,\n ...(input.flags.output ? [`saved JSON: ${resolve(cwd, input.flags.output)}`] : []),\n ] },\n { header: 'Top findings', items: overview.findings.length > 0\n ? overview.findings.map(finding => `${finding.fingerprint} — ${finding.occurrences} occurrence(s), ${(finding.confidence * 100).toFixed(0)}% confidence; ${finding.affectedJobs.join(', ')}`)\n : ['No failed CI steps in the captured evidence.'] },\n { header: 'Drill down', items: overview.drillDown.runIds.map(runId => `kb qa ci run --run-id ${runId} --json`) },\n ],\n });\n }\n return { ok: true };\n },\n },\n});\n"]}
|
|
@@ -21,13 +21,13 @@ async function runIngest(ctx, cwd, store, dir) {
|
|
|
21
21
|
}
|
|
22
22
|
if (cases.length === 0) {
|
|
23
23
|
ctx.ui?.error?.(`No flaky-report.json files found under ${dir} \u2014 nothing to ingest.`);
|
|
24
|
-
return {
|
|
24
|
+
return { ok: false, error: "Command failed" };
|
|
25
25
|
}
|
|
26
26
|
const git = await captureGit(ctx.api.shell, cwd);
|
|
27
27
|
const snap = store.saveE2eFlaky(cases, 0, git);
|
|
28
28
|
ctx.ui?.success?.(`Ingested ${cases.length} e2e case result(s) from ${dir} into flaky history`, {});
|
|
29
29
|
ctx.ui?.json?.(snap);
|
|
30
|
-
return {
|
|
30
|
+
return { ok: true };
|
|
31
31
|
}
|
|
32
32
|
async function runSync(ctx, cwd) {
|
|
33
33
|
const fetch = await ctx.api.shell.exec("git", ["fetch", "origin", "ci-data"], { cwd, throwOnError: false });
|
|
@@ -62,7 +62,7 @@ var qa_e2e_flaky_default = defineCommand({
|
|
|
62
62
|
if (flags.sync) {
|
|
63
63
|
const synced = await runSync(ctx, cwd);
|
|
64
64
|
if (!synced) {
|
|
65
|
-
return {
|
|
65
|
+
return { ok: false, error: "Command failed" };
|
|
66
66
|
}
|
|
67
67
|
}
|
|
68
68
|
if (flags.ingest) {
|
|
@@ -74,7 +74,7 @@ var qa_e2e_flaky_default = defineCommand({
|
|
|
74
74
|
const timeline = buildCaseTimeline(history.slice(-window), flags.case);
|
|
75
75
|
if (!timeline) {
|
|
76
76
|
ctx.ui?.error?.(`No history found for case "${flags.case}". Run with --agent to see the "top" list of known cases.`);
|
|
77
|
-
return {
|
|
77
|
+
return { ok: false, error: "Command failed" };
|
|
78
78
|
}
|
|
79
79
|
if (json) {
|
|
80
80
|
ctx.ui?.json?.(timeline);
|
|
@@ -92,7 +92,7 @@ var qa_e2e_flaky_default = defineCommand({
|
|
|
92
92
|
]
|
|
93
93
|
});
|
|
94
94
|
}
|
|
95
|
-
return {
|
|
95
|
+
return { ok: true };
|
|
96
96
|
}
|
|
97
97
|
const overview = analyzeFlakyCases(history, window);
|
|
98
98
|
if (json) {
|
|
@@ -110,7 +110,7 @@ var qa_e2e_flaky_default = defineCommand({
|
|
|
110
110
|
]
|
|
111
111
|
});
|
|
112
112
|
}
|
|
113
|
-
return {
|
|
113
|
+
return { ok: true };
|
|
114
114
|
}
|
|
115
115
|
}
|
|
116
116
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/cli/commands/qa-e2e-flaky.ts"],"names":[],"mappings":"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","file":"qa-e2e-flaky.js","sourcesContent":["import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport {\n defineCommand,\n useConfig,\n type CLIInput,\n type PluginContextV3,\n} from '@kb-labs/sdk';\nimport {\n SnapshotStore,\n captureGit,\n analyzeFlakyCases,\n buildCaseTimeline,\n} from '@kb-labs/qa-core';\nimport { PATHS, TRENDS_WINDOW, type QAPluginConfig, type E2eCaseResult } from '@kb-labs/qa-contracts';\nimport type { QaE2eFlakyFlags } from './flags.js';\n\nasync function runIngest(\n ctx: PluginContextV3,\n cwd: string,\n store: SnapshotStore,\n dir: string,\n): Promise<{ exitCode: number }> {\n const cases: E2eCaseResult[] = [];\n if (existsSync(dir)) {\n for (const entry of readdirSync(dir, { withFileTypes: true })) {\n const file = entry.isDirectory()\n ? join(dir, entry.name, 'flaky-report.json')\n : entry.name === 'flaky-report.json' ? join(dir, entry.name) : null;\n if (!file || !existsSync(file)) {continue;}\n try {\n cases.push(...(JSON.parse(readFileSync(file, 'utf-8')) as E2eCaseResult[]));\n } catch {\n // A crashed/partial shard shouldn't block ingesting the rest.\n }\n }\n }\n\n if (cases.length === 0) {\n // No flaky-report.json files found under any shard — most likely every\n // shard failed before Playwright's reporter ever ran. Don't push an\n // empty snapshot into history; it would silently dilute the flip-rate\n // window with a run that carried no real signal.\n ctx.ui?.error?.(`No flaky-report.json files found under ${dir} — nothing to ingest.`);\n return { exitCode: 1 };\n }\n\n const git = await captureGit(ctx.api.shell, cwd);\n const snap = store.saveE2eFlaky(cases, 0, git);\n ctx.ui?.success?.(`Ingested ${cases.length} e2e case result(s) from ${dir} into flaky history`, {});\n ctx.ui?.json?.(snap);\n return { exitCode: 0 };\n}\n\nasync function runSync(ctx: PluginContextV3, cwd: string): Promise<boolean> {\n const fetch = await ctx.api.shell.exec('git', ['fetch', 'origin', 'ci-data'], { cwd, throwOnError: false });\n if (!fetch.ok) {\n ctx.ui?.error?.('No flaky history yet — the \"ci-data\" branch has not been created by CI.');\n return false;\n }\n const show = await ctx.api.shell.exec(\n 'git',\n ['show', `origin/ci-data:${PATHS.SNAPSHOTS_E2E_FLAKY}`],\n { cwd, throwOnError: false },\n );\n if (!show.ok) {\n ctx.ui?.error?.('No flaky history yet — \"ci-data\" branch exists but has no snapshot file.');\n return false;\n }\n const target = join(cwd, PATHS.SNAPSHOTS_E2E_FLAKY);\n mkdirSync(dirname(target), { recursive: true });\n writeFileSync(target, show.stdout);\n return true;\n}\n\nexport default defineCommand<unknown, CLIInput<QaE2eFlakyFlags>, { exitCode: number }>({\n id: 'qa:e2e-flaky',\n description: 'Track flaky e2e cases across CI runs — compact overview by default, per-case drill-down on request',\n\n handler: {\n async execute(ctx: PluginContextV3, input: CLIInput<QaE2eFlakyFlags>): Promise<{ exitCode: number }> {\n const { flags } = input;\n const config = await useConfig<QAPluginConfig>();\n const cwd = ctx.cwd ?? process.cwd();\n const store = new SnapshotStore(cwd, config?.historyMaxEntries);\n const json = flags.agent || flags.json;\n\n // --sync runs first so --ingest (if also passed) builds on the synced\n // history instead of silently ignoring --sync — the two compose rather\n // than one implicitly winning.\n if (flags.sync) {\n const synced = await runSync(ctx, cwd);\n if (!synced) {return { exitCode: 1 };}\n }\n\n if (flags.ingest) {\n return runIngest(ctx, cwd, store, flags.ingest);\n }\n\n const history = store.loadE2eFlakyHistory();\n const window = flags.window ?? TRENDS_WINDOW;\n\n if (flags.case) {\n const timeline = buildCaseTimeline(history.slice(-window), flags.case);\n if (!timeline) {\n ctx.ui?.error?.(`No history found for case \"${flags.case}\". Run with --agent to see the \"top\" list of known cases.`);\n return { exitCode: 1 };\n }\n if (json) {\n ctx.ui?.json?.(timeline);\n } else {\n ctx.ui?.success?.(`Case timeline: ${timeline.caseKey}`, {\n sections: [\n { header: 'Summary', items: [\n `flakyScore: ${timeline.flakyScore}`,\n `streak: ${timeline.currentStreak.status} x${timeline.currentStreak.count}`,\n `firstFailure: ${timeline.firstFailure ?? 'none'}`,\n ] },\n { header: 'History', items: timeline.history.map(\n h => `${h.timestamp} [${h.gitCommit}] ${h.status}${h.errorCategory ? ` (${h.errorCategory}: ${h.errorMessage ?? ''})` : ''}`,\n ) },\n ],\n });\n }\n return { exitCode: 0 };\n }\n\n const overview = analyzeFlakyCases(history, window);\n if (json) {\n ctx.ui?.json?.(overview);\n } else {\n ctx.ui?.success?.(`E2E flaky overview (last ${overview.runsAnalyzed} runs)`, {\n sections: [\n { header: 'Summary', items: [\n `totalCases: ${overview.totalCases}, flakyCases: ${overview.flakyCases}`,\n `delta: +${overview.delta.new.length} new, -${overview.delta.fixed.length} fixed, ${overview.delta.unchanged.length} unchanged`,\n ] },\n { header: 'Top flaky cases', items: overview.top.map(\n t => `${t.case} — score ${t.flakyScore}, ${t.streak}, ${t.category}`,\n ) },\n ],\n });\n }\n return { exitCode: 0 };\n },\n },\n});\n"]}
|
|
1
|
+
{"version":3,"sources":["../../../src/cli/commands/qa-e2e-flaky.ts"],"names":[],"mappings":"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","file":"qa-e2e-flaky.js","sourcesContent":["import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport {\n defineCommand,\n useConfig,\n type CLIInput,\n type PluginContextV3,\n type CommandResult,\n} from '@kb-labs/sdk';\nimport {\n SnapshotStore,\n captureGit,\n analyzeFlakyCases,\n buildCaseTimeline,\n} from '@kb-labs/qa-core';\nimport { PATHS, TRENDS_WINDOW, type QAPluginConfig, type E2eCaseResult } from '@kb-labs/qa-contracts';\nimport type { QaE2eFlakyFlags } from './flags.js';\n\nasync function runIngest(\n ctx: PluginContextV3,\n cwd: string,\n store: SnapshotStore,\n dir: string,\n): Promise<CommandResult> {\n const cases: E2eCaseResult[] = [];\n if (existsSync(dir)) {\n for (const entry of readdirSync(dir, { withFileTypes: true })) {\n const file = entry.isDirectory()\n ? join(dir, entry.name, 'flaky-report.json')\n : entry.name === 'flaky-report.json' ? join(dir, entry.name) : null;\n if (!file || !existsSync(file)) {continue;}\n try {\n cases.push(...(JSON.parse(readFileSync(file, 'utf-8')) as E2eCaseResult[]));\n } catch {\n // A crashed/partial shard shouldn't block ingesting the rest.\n }\n }\n }\n\n if (cases.length === 0) {\n // No flaky-report.json files found under any shard — most likely every\n // shard failed before Playwright's reporter ever ran. Don't push an\n // empty snapshot into history; it would silently dilute the flip-rate\n // window with a run that carried no real signal.\n ctx.ui?.error?.(`No flaky-report.json files found under ${dir} — nothing to ingest.`);\n return { ok: false, error: 'Command failed' };\n }\n\n const git = await captureGit(ctx.api.shell, cwd);\n const snap = store.saveE2eFlaky(cases, 0, git);\n ctx.ui?.success?.(`Ingested ${cases.length} e2e case result(s) from ${dir} into flaky history`, {});\n ctx.ui?.json?.(snap);\n return { ok: true };\n}\n\nasync function runSync(ctx: PluginContextV3, cwd: string): Promise<boolean> {\n const fetch = await ctx.api.shell.exec('git', ['fetch', 'origin', 'ci-data'], { cwd, throwOnError: false });\n if (!fetch.ok) {\n ctx.ui?.error?.('No flaky history yet — the \"ci-data\" branch has not been created by CI.');\n return false;\n }\n const show = await ctx.api.shell.exec(\n 'git',\n ['show', `origin/ci-data:${PATHS.SNAPSHOTS_E2E_FLAKY}`],\n { cwd, throwOnError: false },\n );\n if (!show.ok) {\n ctx.ui?.error?.('No flaky history yet — \"ci-data\" branch exists but has no snapshot file.');\n return false;\n }\n const target = join(cwd, PATHS.SNAPSHOTS_E2E_FLAKY);\n mkdirSync(dirname(target), { recursive: true });\n writeFileSync(target, show.stdout);\n return true;\n}\n\nexport default defineCommand<unknown, CLIInput<QaE2eFlakyFlags>, unknown>({\n id: 'qa:e2e-flaky',\n description: 'Track flaky e2e cases across CI runs — compact overview by default, per-case drill-down on request',\n\n handler: {\n async execute(ctx: PluginContextV3, input: CLIInput<QaE2eFlakyFlags>): Promise<CommandResult> {\n const { flags } = input;\n const config = await useConfig<QAPluginConfig>();\n const cwd = ctx.cwd ?? process.cwd();\n const store = new SnapshotStore(cwd, config?.historyMaxEntries);\n const json = flags.agent || flags.json;\n\n // --sync runs first so --ingest (if also passed) builds on the synced\n // history instead of silently ignoring --sync — the two compose rather\n // than one implicitly winning.\n if (flags.sync) {\n const synced = await runSync(ctx, cwd);\n if (!synced) {return { ok: false, error: 'Command failed' };}\n }\n\n if (flags.ingest) {\n return runIngest(ctx, cwd, store, flags.ingest);\n }\n\n const history = store.loadE2eFlakyHistory();\n const window = flags.window ?? TRENDS_WINDOW;\n\n if (flags.case) {\n const timeline = buildCaseTimeline(history.slice(-window), flags.case);\n if (!timeline) {\n ctx.ui?.error?.(`No history found for case \"${flags.case}\". Run with --agent to see the \"top\" list of known cases.`);\n return { ok: false, error: 'Command failed' };\n }\n if (json) {\n ctx.ui?.json?.(timeline);\n } else {\n ctx.ui?.success?.(`Case timeline: ${timeline.caseKey}`, {\n sections: [\n { header: 'Summary', items: [\n `flakyScore: ${timeline.flakyScore}`,\n `streak: ${timeline.currentStreak.status} x${timeline.currentStreak.count}`,\n `firstFailure: ${timeline.firstFailure ?? 'none'}`,\n ] },\n { header: 'History', items: timeline.history.map(\n h => `${h.timestamp} [${h.gitCommit}] ${h.status}${h.errorCategory ? ` (${h.errorCategory}: ${h.errorMessage ?? ''})` : ''}`,\n ) },\n ],\n });\n }\n return { ok: true };\n }\n\n const overview = analyzeFlakyCases(history, window);\n if (json) {\n ctx.ui?.json?.(overview);\n } else {\n ctx.ui?.success?.(`E2E flaky overview (last ${overview.runsAnalyzed} runs)`, {\n sections: [\n { header: 'Summary', items: [\n `totalCases: ${overview.totalCases}, flakyCases: ${overview.flakyCases}`,\n `delta: +${overview.delta.new.length} new, -${overview.delta.fixed.length} fixed, ${overview.delta.unchanged.length} unchanged`,\n ] },\n { header: 'Top flaky cases', items: overview.top.map(\n t => `${t.case} — score ${t.flakyScore}, ${t.streak}, ${t.category}`,\n ) },\n ],\n });\n }\n return { ok: true };\n },\n },\n});\n"]}
|
|
@@ -26,7 +26,7 @@ var qa_gate_default = defineCommand({
|
|
|
26
26
|
const items = raw.violations.map((v) => `[${v.severity}] ${v.package} ${v.check}: ${v.message}`);
|
|
27
27
|
ctx.ui?.error?.("Gate failed", { sections: [{ header: "Violations", items }] });
|
|
28
28
|
}
|
|
29
|
-
return {
|
|
29
|
+
return raw.ok ? { ok: true, result: raw } : { ok: false, error: "QA gate failed", result: raw };
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/cli/commands/qa-gate.ts"],"names":[],"mappings":";;;;
|
|
1
|
+
{"version":3,"sources":["../../../src/cli/commands/qa-gate.ts"],"names":[],"mappings":";;;;AAgBA,IAAO,kBAAQ,aAAA,CAAuD;AAAA,EACpE,EAAA,EAAI,SAAA;AAAA,EACJ,WAAA,EAAa,yDAAA;AAAA,EAEb,OAAA,EAAS;AAAA,IACP,MAAM,OAAA,CAAQ,GAAA,EAAsB,KAAA,EAAsD;AACxF,MAAA,MAAM,EAAE,OAAM,GAAI,KAAA;AAClB,MAAA,MAAM,MAAA,GAAS,MAAM,SAAA,EAA0B;AAC/C,MAAA,MAAM,GAAA,GAAM,GAAA,CAAI,GAAA,IAAO,OAAA,CAAQ,GAAA,EAAI;AAEnC,MAAA,MAAM,UAAA,GAAa,gBAAA,CAAiB,GAAA,EAAK,MAAA,EAAQ,UAAU,CAAA;AAC3D,MAAA,MAAM,OAAA,GAAU,IAAI,aAAA,CAAc,EAAE,UAAA,EAAY,KAAK,KAAA,EAAO,GAAA,CAAI,GAAA,CAAI,KAAA,EAAO,CAAA;AAC3E,MAAA,MAAM,KAAA,GAAQ,IAAI,aAAA,CAAc,GAAA,EAAK,QAAQ,iBAAiB,CAAA;AAE9D,MAAA,MAAM,MAAM,MAAM,UAAA,CAAW,GAAA,CAAI,GAAA,CAAI,OAAO,GAAG,CAAA;AAE/C,MAAA,MAAM,KAAA,GAAQ,KAAK,GAAA,EAAI;AACvB,MAAA,MAAM,GAAA,GAAM,MAAM,OAAA,CAAQ,IAAA,EAAK;AAC/B,MAAA,MAAM,UAAA,GAAa,IAAA,CAAK,GAAA,EAAI,GAAI,KAAA;AAEhC,MAAA,IAAI,MAAM,IAAA,EAAM,KAAA,CAAM,QAAA,CAAS,GAAA,EAAK,YAAY,GAAG,CAAA;AAEnD,MAAA,IAAI,MAAM,IAAA,EAAM;AACd,QAAA,GAAA,CAAI,EAAA,EAAI,OAAO,GAAG,CAAA;AAAA,MACpB,CAAA,MAAA,IAAW,IAAI,EAAA,EAAI;AACjB,QAAA,GAAA,CAAI,EAAA,EAAI,OAAA,GAAU,CAAA,aAAA,EAAgB,GAAA,CAAI,YAAY,CAAA,+BAAA,CAAiC,CAAA;AAAA,MACrF,CAAA,MAAO;AACL,QAAA,MAAM,QAAQ,GAAA,CAAI,UAAA,CAAW,GAAA,CAAI,CAAA,CAAA,KAAK,IAAI,CAAA,CAAE,QAAQ,CAAA,EAAA,EAAK,CAAA,CAAE,OAAO,CAAA,EAAA,EAAK,CAAA,CAAE,KAAK,CAAA,EAAA,EAAK,CAAA,CAAE,OAAO,CAAA,CAAE,CAAA;AAC9F,QAAA,GAAA,CAAI,EAAA,EAAI,KAAA,GAAQ,aAAA,EAAe,EAAE,QAAA,EAAU,CAAC,EAAE,MAAA,EAAQ,YAAA,EAAc,KAAA,EAAO,CAAA,EAAG,CAAA;AAAA,MAChF;AAEA,MAAA,OAAO,GAAA,CAAI,EAAA,GAAK,EAAE,EAAA,EAAI,MAAM,MAAA,EAAQ,GAAA,EAAI,GAAI,EAAE,EAAA,EAAI,KAAA,EAAO,KAAA,EAAO,gBAAA,EAAkB,QAAQ,GAAA,EAAI;AAAA,IAChG;AAAA;AAEJ,CAAC","file":"qa-gate.js","sourcesContent":["import {\n defineCommand,\n useConfig,\n type CLIInput,\n type PluginContextV3,\n type CommandResult,\n} from '@kb-labs/sdk';\nimport {\n DevkitAdapter,\n SnapshotStore,\n resolveDevkitBin,\n captureGit,\n} from '@kb-labs/qa-core';\nimport { type QAPluginConfig } from '@kb-labs/qa-contracts';\nimport type { QaGateFlags } from './flags.js';\n\nexport default defineCommand<unknown, CLIInput<QaGateFlags>, unknown>({\n id: 'qa:gate',\n description: 'Run pre-commit gate check (exits 1 if violations found)',\n\n handler: {\n async execute(ctx: PluginContextV3, input: CLIInput<QaGateFlags>): Promise<CommandResult> {\n const { flags } = input;\n const config = await useConfig<QAPluginConfig>();\n const cwd = ctx.cwd ?? process.cwd();\n\n const binaryPath = resolveDevkitBin(cwd, config?.devkitPath);\n const adapter = new DevkitAdapter({ binaryPath, cwd, shell: ctx.api.shell });\n const store = new SnapshotStore(cwd, config?.historyMaxEntries);\n\n const git = await captureGit(ctx.api.shell, cwd);\n\n const start = Date.now();\n const raw = await adapter.gate();\n const durationMs = Date.now() - start;\n\n if (flags.save) store.saveGate(raw, durationMs, git);\n\n if (flags.json) {\n ctx.ui?.json?.(raw);\n } else if (raw.ok) {\n ctx.ui?.success?.(`Gate passed. ${raw.staged_files} staged file(s), no violations.`);\n } else {\n const items = raw.violations.map(v => `[${v.severity}] ${v.package} ${v.check}: ${v.message}`);\n ctx.ui?.error?.('Gate failed', { sections: [{ header: 'Violations', items }] });\n }\n\n return raw.ok ? { ok: true, result: raw } : { ok: false, error: 'QA gate failed', result: raw };\n },\n },\n});\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/cli/commands/qa-history.ts"],"names":[],"mappings":";;;;
|
|
1
|
+
{"version":3,"sources":["../../../src/cli/commands/qa-history.ts"],"names":[],"mappings":";;;;AAcA,IAAO,qBAAQ,aAAA,CAA0D;AAAA,EACvE,EAAA,EAAI,YAAA;AAAA,EACJ,WAAA,EAAa,qBAAA;AAAA,EAEb,OAAA,EAAS;AAAA,IACP,MAAM,OAAA,CAAQ,GAAA,EAAsB,KAAA,EAAyD;AAC3F,MAAA,MAAM,EAAE,OAAM,GAAI,KAAA;AAClB,MAAA,MAAM,MAAA,GAAS,MAAM,SAAA,EAA0B;AAC/C,MAAA,MAAM,GAAA,GAAM,GAAA,CAAI,GAAA,IAAO,OAAA,CAAQ,GAAA,EAAI;AAEnC,MAAA,MAAM,KAAA,GAAQ,IAAI,aAAA,CAAc,GAAA,EAAK,QAAQ,iBAAiB,CAAA;AAC9D,MAAA,MAAM,KAAA,GAAQ,MAAM,KAAA,IAAS,EAAA;AAC7B,MAAA,MAAM,OAAA,GAAU,MAAM,cAAA,EAAe;AAErC,MAAA,IAAI,MAAM,IAAA,EAAM;AACd,QAAA,GAAA,CAAI,EAAA,EAAI,IAAA,GAAO,CAAC,GAAG,OAAO,CAAA,CAAE,OAAA,EAAQ,CAAE,KAAA,CAAM,CAAA,EAAG,KAAK,CAAC,CAAA;AAAA,MACvD,CAAA,MAAO;AACL,QAAA,KAAA,MAAW,OAAA,IAAW,iBAAA,CAAkB,OAAA,EAAS,KAAK,CAAA,EAAG;AACvD,UAAA,GAAA,CAAI,EAAA,EAAI,OAAA,GAAU,CAAA,EAAG,OAAA,CAAQ,MAAM,CAAA,CAAA,EAAI,EAAE,QAAA,EAAU,CAAC,EAAE,KAAA,EAAO,OAAA,CAAQ,KAAA,EAAO,GAAG,CAAA;AAAA,QACjF;AAAA,MACF;AAEA,MAAA,OAAO,EAAE,IAAI,IAAA,EAAK;AAAA,IACpB;AAAA;AAEJ,CAAC","file":"qa-history.js","sourcesContent":["import {\n defineCommand,\n useConfig,\n type CLIInput,\n type PluginContextV3,\n type CommandResult,\n} from '@kb-labs/sdk';\nimport {\n SnapshotStore,\n buildHistoryTable,\n} from '@kb-labs/qa-core';\nimport { type QAPluginConfig } from '@kb-labs/qa-contracts';\nimport type { QaHistoryFlags } from './flags.js';\n\nexport default defineCommand<unknown, CLIInput<QaHistoryFlags>, unknown>({\n id: 'qa:history',\n description: 'Show QA run history',\n\n handler: {\n async execute(ctx: PluginContextV3, input: CLIInput<QaHistoryFlags>): Promise<CommandResult> {\n const { flags } = input;\n const config = await useConfig<QAPluginConfig>();\n const cwd = ctx.cwd ?? process.cwd();\n\n const store = new SnapshotStore(cwd, config?.historyMaxEntries);\n const limit = flags.limit ?? 20;\n const history = store.loadRunHistory();\n\n if (flags.json) {\n ctx.ui?.json?.([...history].reverse().slice(0, limit));\n } else {\n for (const section of buildHistoryTable(history, limit)) {\n ctx.ui?.success?.(`${section.header}`, { sections: [{ items: section.lines }] });\n }\n }\n\n return { ok: true };\n },\n },\n});\n"]}
|
|
@@ -20,7 +20,7 @@ var qa_regressions_default = defineCommand({
|
|
|
20
20
|
ctx.ui?.success?.(`${section.header}`, { sections: [{ items: section.lines }] });
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
|
-
return {
|
|
23
|
+
return detection.hasRegressions ? { ok: false, error: "Regressions found", result: detection } : { ok: true, result: detection };
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
26
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/cli/commands/qa-regressions.ts"],"names":[],"mappings":";;;;
|
|
1
|
+
{"version":3,"sources":["../../../src/cli/commands/qa-regressions.ts"],"names":[],"mappings":";;;;AAeA,IAAO,yBAAQ,aAAA,CAA8D;AAAA,EAC3E,EAAA,EAAI,gBAAA;AAAA,EACJ,WAAA,EAAa,mCAAA;AAAA,EAEb,OAAA,EAAS;AAAA,IACP,MAAM,OAAA,CAAQ,GAAA,EAAsB,KAAA,EAA6D;AAC/F,MAAA,MAAM,EAAE,OAAM,GAAI,KAAA;AAClB,MAAA,MAAM,MAAA,GAAS,MAAM,SAAA,EAA0B;AAC/C,MAAA,MAAM,GAAA,GAAM,GAAA,CAAI,GAAA,IAAO,OAAA,CAAQ,GAAA,EAAI;AAEnC,MAAA,MAAM,KAAA,GAAQ,IAAI,aAAA,CAAc,GAAA,EAAK,QAAQ,iBAAiB,CAAA;AAC9D,MAAA,MAAM,OAAA,GAAU,MAAM,cAAA,EAAe;AACrC,MAAA,MAAM,SAAA,GAAY,kBAAkB,OAAO,CAAA;AAE3C,MAAA,IAAI,MAAM,IAAA,EAAM;AACd,QAAA,GAAA,CAAI,EAAA,EAAI,OAAO,SAAS,CAAA;AAAA,MAC1B,CAAA,MAAO;AACL,QAAA,KAAA,MAAW,OAAA,IAAW,sBAAA,CAAuB,SAAS,CAAA,EAAG;AACvD,UAAA,GAAA,CAAI,EAAA,EAAI,OAAA,GAAU,CAAA,EAAG,OAAA,CAAQ,MAAM,CAAA,CAAA,EAAI,EAAE,QAAA,EAAU,CAAC,EAAE,KAAA,EAAO,OAAA,CAAQ,KAAA,EAAO,GAAG,CAAA;AAAA,QACjF;AAAA,MACF;AAEA,MAAA,OAAO,SAAA,CAAU,cAAA,GACb,EAAE,EAAA,EAAI,OAAO,KAAA,EAAO,mBAAA,EAAqB,MAAA,EAAQ,SAAA,EAAU,GAC3D,EAAE,EAAA,EAAI,IAAA,EAAM,QAAQ,SAAA,EAAU;AAAA,IACpC;AAAA;AAEJ,CAAC","file":"qa-regressions.js","sourcesContent":["import {\n defineCommand,\n useConfig,\n type CLIInput,\n type PluginContextV3,\n type CommandResult,\n} from '@kb-labs/sdk';\nimport {\n SnapshotStore,\n detectRegressions,\n buildRegressionsReport,\n} from '@kb-labs/qa-core';\nimport { type QAPluginConfig } from '@kb-labs/qa-contracts';\nimport type { QaRegressionsFlags } from './flags.js';\n\nexport default defineCommand<unknown, CLIInput<QaRegressionsFlags>, unknown>({\n id: 'qa:regressions',\n description: 'Detect regressions since last run',\n\n handler: {\n async execute(ctx: PluginContextV3, input: CLIInput<QaRegressionsFlags>): Promise<CommandResult> {\n const { flags } = input;\n const config = await useConfig<QAPluginConfig>();\n const cwd = ctx.cwd ?? process.cwd();\n\n const store = new SnapshotStore(cwd, config?.historyMaxEntries);\n const history = store.loadRunHistory();\n const detection = detectRegressions(history);\n\n if (flags.json) {\n ctx.ui?.json?.(detection);\n } else {\n for (const section of buildRegressionsReport(detection)) {\n ctx.ui?.success?.(`${section.header}`, { sections: [{ items: section.lines }] });\n }\n }\n\n return detection.hasRegressions\n ? { ok: false, error: 'Regressions found', result: detection }\n : { ok: true, result: detection };\n },\n },\n});\n"]}
|
|
@@ -36,7 +36,7 @@ var qa_run_default = defineCommand({
|
|
|
36
36
|
ctx.ui?.success?.(`${section.header}`, { sections: [{ items: section.lines }] });
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
|
-
return {
|
|
39
|
+
return raw.ok ? { ok: true, result: raw } : { ok: false, error: "QA run failed", result: raw };
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
42
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/cli/commands/qa-run.ts"],"names":[],"mappings":";;;;;
|
|
1
|
+
{"version":3,"sources":["../../../src/cli/commands/qa-run.ts"],"names":[],"mappings":";;;;;AAkBA,IAAO,iBAAQ,aAAA,CAAsD;AAAA,EACnE,EAAA,EAAI,QAAA;AAAA,EACJ,WAAA,EAAa,qCAAA;AAAA,EAEb,OAAA,EAAS;AAAA,IACP,MAAM,OAAA,CAAQ,GAAA,EAAsB,KAAA,EAAqD;AACvF,MAAA,MAAM,EAAE,OAAM,GAAI,KAAA;AAClB,MAAA,MAAM,MAAA,GAAS,MAAM,SAAA,EAA0B;AAC/C,MAAA,MAAM,GAAA,GAAM,GAAA,CAAI,GAAA,IAAO,OAAA,CAAQ,GAAA,EAAI;AAEnC,MAAA,MAAM,UAAA,GAAa,gBAAA,CAAiB,GAAA,EAAK,MAAA,EAAQ,UAAU,CAAA;AAC3D,MAAA,MAAM,OAAA,GAAU,IAAI,aAAA,CAAc,EAAE,UAAA,EAAY,KAAK,KAAA,EAAO,GAAA,CAAI,GAAA,CAAI,KAAA,EAAO,CAAA;AAC3E,MAAA,MAAM,KAAA,GAAQ,IAAI,aAAA,CAAc,GAAA,EAAK,QAAQ,iBAAiB,CAAA;AAE9D,MAAA,MAAM,KAAA,GAAkB,MAAM,KAAA,GAC1B,KAAA,CAAM,MAAM,KAAA,CAAM,GAAG,CAAA,CAAE,GAAA,CAAI,CAAA,CAAA,KAAK,CAAA,CAAE,MAAM,CAAA,CAAE,MAAA,CAAO,OAAO,CAAA,GACxD,MAAA,EAAQ,cAAc,KAAA,EAAM,IAAK,CAAC,GAAG,aAAa,CAAA;AACtD,MAAA,MAAM,IAAA,GAAO,MAAM,IAAA,KAAS,KAAA;AAE5B,MAAA,MAAM,MAAM,MAAM,UAAA,CAAW,GAAA,CAAI,GAAA,CAAI,OAAO,GAAG,CAAA;AAE/C,MAAA,MAAM,KAAA,GAAQ,KAAK,GAAA,EAAI;AACvB,MAAA,MAAM,GAAA,GAAM,MAAM,OAAA,CAAQ,GAAA,CAAI,KAAK,CAAA;AACnC,MAAA,MAAM,UAAA,GAAa,IAAA,CAAK,GAAA,EAAI,GAAI,KAAA;AAEhC,MAAA,MAAM,IAAA,GAAO,OAAO,KAAA,CAAM,OAAA,CAAQ,KAAK,KAAA,EAAO,UAAA,EAAY,GAAG,CAAA,GAAI,IAAA;AACjE,MAAA,MAAM,YAAyD,IAAA,IAAQ;AAAA,QACrE,IAAA,EAAM,KAAA;AAAA,QAAO,EAAA,EAAI,EAAA;AAAA,QAAI,SAAA,EAAA,iBAAW,IAAI,IAAA,EAAK,EAAE,WAAA,EAAY;AAAA,QAAG,UAAA;AAAA,QAAY,KAAA;AAAA,QAAO;AAAA,OAC/E;AAEA,MAAA,IAAI,MAAM,IAAA,EAAM;AACd,QAAA,GAAA,CAAI,EAAA,EAAI,IAAA,GAAO,kBAAA,CAAmB,SAAS,CAAC,CAAA;AAAA,MAC9C,CAAA,MAAO;AACL,QAAA,KAAA,MAAW,OAAA,IAAW,cAAA,CAAe,SAAS,CAAA,EAAG;AAC/C,UAAA,GAAA,CAAI,EAAA,EAAI,OAAA,GAAU,CAAA,EAAG,OAAA,CAAQ,MAAM,CAAA,CAAA,EAAI,EAAE,QAAA,EAAU,CAAC,EAAE,KAAA,EAAO,OAAA,CAAQ,KAAA,EAAO,GAAG,CAAA;AAAA,QACjF;AAAA,MACF;AAEA,MAAA,OAAO,GAAA,CAAI,EAAA,GAAK,EAAE,EAAA,EAAI,MAAM,MAAA,EAAQ,GAAA,EAAI,GAAI,EAAE,EAAA,EAAI,KAAA,EAAO,KAAA,EAAO,eAAA,EAAiB,QAAQ,GAAA,EAAI;AAAA,IAC/F;AAAA;AAEJ,CAAC","file":"qa-run.js","sourcesContent":["import {\n defineCommand,\n useConfig,\n type CLIInput,\n type PluginContextV3,\n type CommandResult,\n} from '@kb-labs/sdk';\nimport {\n DevkitAdapter,\n SnapshotStore,\n resolveDevkitBin,\n captureGit,\n buildRunReport,\n buildRunJsonReport,\n} from '@kb-labs/qa-core';\nimport { DEFAULT_TASKS, type QAPluginConfig } from '@kb-labs/qa-contracts';\nimport type { QaRunFlags } from './flags.js';\n\nexport default defineCommand<unknown, CLIInput<QaRunFlags>, unknown>({\n id: 'qa:run',\n description: 'Run devkit tasks and record results',\n\n handler: {\n async execute(ctx: PluginContextV3, input: CLIInput<QaRunFlags>): Promise<CommandResult> {\n const { flags } = input;\n const config = await useConfig<QAPluginConfig>();\n const cwd = ctx.cwd ?? process.cwd();\n\n const binaryPath = resolveDevkitBin(cwd, config?.devkitPath);\n const adapter = new DevkitAdapter({ binaryPath, cwd, shell: ctx.api.shell });\n const store = new SnapshotStore(cwd, config?.historyMaxEntries);\n\n const tasks: string[] = flags.tasks\n ? flags.tasks.split(',').map(s => s.trim()).filter(Boolean)\n : config?.defaultTasks?.slice() ?? [...DEFAULT_TASKS];\n const save = flags.save !== false;\n\n const git = await captureGit(ctx.api.shell, cwd);\n\n const start = Date.now();\n const raw = await adapter.run(tasks);\n const durationMs = Date.now() - start;\n\n const snap = save ? store.saveRun(raw, tasks, durationMs, git) : null;\n const effective: import('@kb-labs/qa-contracts').RunSnapshot = snap ?? {\n kind: 'run', id: '', timestamp: new Date().toISOString(), durationMs, tasks, raw,\n };\n\n if (flags.json) {\n ctx.ui?.json?.(buildRunJsonReport(effective));\n } else {\n for (const section of buildRunReport(effective)) {\n ctx.ui?.success?.(`${section.header}`, { sections: [{ items: section.lines }] });\n }\n }\n\n return raw.ok ? { ok: true, result: raw } : { ok: false, error: 'QA run failed', result: raw };\n },\n },\n});\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/cli/commands/qa-stats.ts"],"names":[],"mappings":";;;;
|
|
1
|
+
{"version":3,"sources":["../../../src/cli/commands/qa-stats.ts"],"names":[],"mappings":";;;;AAkBA,IAAO,mBAAQ,aAAA,CAAwD;AAAA,EACrE,EAAA,EAAI,UAAA;AAAA,EACJ,WAAA,EAAa,iDAAA;AAAA,EAEb,OAAA,EAAS;AAAA,IACP,MAAM,OAAA,CAAQ,GAAA,EAAsB,KAAA,EAAuD;AACzF,MAAA,MAAM,EAAE,OAAM,GAAI,KAAA;AAClB,MAAA,MAAM,MAAA,GAAS,MAAM,SAAA,EAA0B;AAC/C,MAAA,MAAM,GAAA,GAAM,GAAA,CAAI,GAAA,IAAO,OAAA,CAAQ,GAAA,EAAI;AAEnC,MAAA,MAAM,UAAA,GAAa,gBAAA,CAAiB,GAAA,EAAK,MAAA,EAAQ,UAAU,CAAA;AAC3D,MAAA,MAAM,OAAA,GAAU,IAAI,aAAA,CAAc,EAAE,UAAA,EAAY,KAAK,KAAA,EAAO,GAAA,CAAI,GAAA,CAAI,KAAA,EAAO,CAAA;AAC3E,MAAA,MAAM,KAAA,GAAQ,IAAI,aAAA,CAAc,GAAA,EAAK,QAAQ,iBAAiB,CAAA;AAE9D,MAAA,MAAM,IAAA,GAAO,MAAM,IAAA,KAAS,KAAA;AAC5B,MAAA,MAAM,MAAM,MAAM,UAAA,CAAW,GAAA,CAAI,GAAA,CAAI,OAAO,GAAG,CAAA;AAE/C,MAAA,MAAM,KAAA,GAAQ,KAAK,GAAA,EAAI;AACvB,MAAA,MAAM,GAAA,GAAM,MAAM,OAAA,CAAQ,KAAA,EAAM;AAChC,MAAA,MAAM,UAAA,GAAa,IAAA,CAAK,GAAA,EAAI,GAAI,KAAA;AAEhC,MAAA,MAAM,OAAO,IAAA,GAAO,KAAA,CAAM,UAAU,GAAA,EAAK,UAAA,EAAY,GAAG,CAAA,GAAI,IAAA;AAC5D,MAAA,MAAM,YAA2D,IAAA,IAAQ;AAAA,QACvE,IAAA,EAAM,OAAA;AAAA,QAAS,EAAA,EAAI,EAAA;AAAA,QAAI,SAAA,EAAA,iBAAW,IAAI,IAAA,EAAK,EAAE,WAAA,EAAY;AAAA,QAAG,UAAA;AAAA,QAAY;AAAA,OAC1E;AAEA,MAAA,IAAI,MAAM,IAAA,EAAM;AACd,QAAA,GAAA,CAAI,EAAA,EAAI,IAAA,GAAO,oBAAA,CAAqB,SAAS,CAAC,CAAA;AAAA,MAChD,CAAA,MAAO;AACL,QAAA,KAAA,MAAW,OAAA,IAAW,gBAAA,CAAiB,SAAS,CAAA,EAAG;AACjD,UAAA,GAAA,CAAI,EAAA,EAAI,OAAA,GAAU,CAAA,EAAG,OAAA,CAAQ,MAAM,CAAA,CAAA,EAAI,EAAE,QAAA,EAAU,CAAC,EAAE,KAAA,EAAO,OAAA,CAAQ,KAAA,EAAO,GAAG,CAAA;AAAA,QACjF;AAAA,MACF;AAEA,MAAA,OAAO,EAAE,IAAI,IAAA,EAAK;AAAA,IACpB;AAAA;AAEJ,CAAC","file":"qa-stats.js","sourcesContent":["import {\n defineCommand,\n useConfig,\n type CLIInput,\n type PluginContextV3,\n type CommandResult,\n} from '@kb-labs/sdk';\nimport {\n DevkitAdapter,\n SnapshotStore,\n resolveDevkitBin,\n captureGit,\n buildStatsReport,\n buildStatsJsonReport,\n} from '@kb-labs/qa-core';\nimport { type QAPluginConfig } from '@kb-labs/qa-contracts';\nimport type { QaStatsFlags } from './flags.js';\n\nexport default defineCommand<unknown, CLIInput<QaStatsFlags>, unknown>({\n id: 'qa:stats',\n description: 'Show devkit health score and category breakdown',\n\n handler: {\n async execute(ctx: PluginContextV3, input: CLIInput<QaStatsFlags>): Promise<CommandResult> {\n const { flags } = input;\n const config = await useConfig<QAPluginConfig>();\n const cwd = ctx.cwd ?? process.cwd();\n\n const binaryPath = resolveDevkitBin(cwd, config?.devkitPath);\n const adapter = new DevkitAdapter({ binaryPath, cwd, shell: ctx.api.shell });\n const store = new SnapshotStore(cwd, config?.historyMaxEntries);\n\n const save = flags.save !== false;\n const git = await captureGit(ctx.api.shell, cwd);\n\n const start = Date.now();\n const raw = await adapter.stats();\n const durationMs = Date.now() - start;\n\n const snap = save ? store.saveStats(raw, durationMs, git) : null;\n const effective: import('@kb-labs/qa-contracts').StatsSnapshot = snap ?? {\n kind: 'stats', id: '', timestamp: new Date().toISOString(), durationMs, raw,\n };\n\n if (flags.json) {\n ctx.ui?.json?.(buildStatsJsonReport(effective));\n } else {\n for (const section of buildStatsReport(effective)) {\n ctx.ui?.success?.(`${section.header}`, { sections: [{ items: section.lines }] });\n }\n }\n\n return { ok: true };\n },\n },\n});\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/cli/commands/qa-trends.ts"],"names":[],"mappings":";;;;;
|
|
1
|
+
{"version":3,"sources":["../../../src/cli/commands/qa-trends.ts"],"names":[],"mappings":";;;;;AAeA,IAAO,oBAAQ,aAAA,CAAyD;AAAA,EACtE,EAAA,EAAI,WAAA;AAAA,EACJ,WAAA,EAAa,kCAAA;AAAA,EAEb,OAAA,EAAS;AAAA,IACP,MAAM,OAAA,CAAQ,GAAA,EAAsB,KAAA,EAAwD;AAC1F,MAAA,MAAM,EAAE,OAAM,GAAI,KAAA;AAClB,MAAA,MAAM,MAAA,GAAS,MAAM,SAAA,EAA0B;AAC/C,MAAA,MAAM,GAAA,GAAM,GAAA,CAAI,GAAA,IAAO,OAAA,CAAQ,GAAA,EAAI;AAEnC,MAAA,MAAM,KAAA,GAAQ,IAAI,aAAA,CAAc,GAAA,EAAK,QAAQ,iBAAiB,CAAA;AAC9D,MAAA,MAAM,MAAA,GAAS,MAAM,MAAA,IAAU,aAAA;AAC/B,MAAA,MAAM,OAAA,GAAU,MAAM,cAAA,EAAe;AACrC,MAAA,MAAM,QAAA,GAAW,aAAA,CAAc,OAAA,EAAS,MAAM,CAAA;AAE9C,MAAA,IAAI,MAAM,IAAA,EAAM;AACd,QAAA,GAAA,CAAI,EAAA,EAAI,OAAO,QAAQ,CAAA;AAAA,MACzB,CAAA,MAAO;AACL,QAAA,KAAA,MAAW,OAAA,IAAW,iBAAA,CAAkB,QAAQ,CAAA,EAAG;AACjD,UAAA,GAAA,CAAI,EAAA,EAAI,OAAA,GAAU,CAAA,EAAG,OAAA,CAAQ,MAAM,CAAA,CAAA,EAAI,EAAE,QAAA,EAAU,CAAC,EAAE,KAAA,EAAO,OAAA,CAAQ,KAAA,EAAO,GAAG,CAAA;AAAA,QACjF;AAAA,MACF;AAEA,MAAA,OAAO,EAAE,IAAI,IAAA,EAAK;AAAA,IACpB;AAAA;AAEJ,CAAC","file":"qa-trends.js","sourcesContent":["import {\n defineCommand,\n useConfig,\n type CLIInput,\n type PluginContextV3,\n type CommandResult,\n} from '@kb-labs/sdk';\nimport {\n SnapshotStore,\n analyzeTrends,\n buildTrendsReport,\n} from '@kb-labs/qa-core';\nimport { TRENDS_WINDOW, type QAPluginConfig } from '@kb-labs/qa-contracts';\nimport type { QaTrendsFlags } from './flags.js';\n\nexport default defineCommand<unknown, CLIInput<QaTrendsFlags>, unknown>({\n id: 'qa:trends',\n description: 'Show QA quality trends over time',\n\n handler: {\n async execute(ctx: PluginContextV3, input: CLIInput<QaTrendsFlags>): Promise<CommandResult> {\n const { flags } = input;\n const config = await useConfig<QAPluginConfig>();\n const cwd = ctx.cwd ?? process.cwd();\n\n const store = new SnapshotStore(cwd, config?.historyMaxEntries);\n const window = flags.window ?? TRENDS_WINDOW;\n const history = store.loadRunHistory();\n const analysis = analyzeTrends(history, window);\n\n if (flags.json) {\n ctx.ui?.json?.(analysis);\n } else {\n for (const section of buildTrendsReport(analysis)) {\n ctx.ui?.success?.(`${section.header}`, { sections: [{ items: section.lines }] });\n }\n }\n\n return { ok: true };\n },\n },\n});\n"]}
|
package/dist/index.js
CHANGED
|
@@ -108,6 +108,72 @@ var qaE2eFlakyFlags = defineFlags({
|
|
|
108
108
|
description: "CI-only: directory of per-shard flaky-report.json files to merge and save as a new snapshot"
|
|
109
109
|
}
|
|
110
110
|
});
|
|
111
|
+
var qaCiEvidenceCaptureFlags = defineFlags({
|
|
112
|
+
runId: {
|
|
113
|
+
type: "string",
|
|
114
|
+
description: "GitHub Actions run ID (defaults to GITHUB_RUN_ID)"
|
|
115
|
+
},
|
|
116
|
+
repository: {
|
|
117
|
+
type: "string",
|
|
118
|
+
description: "GitHub repository in owner/name form (defaults to GITHUB_REPOSITORY)"
|
|
119
|
+
},
|
|
120
|
+
output: {
|
|
121
|
+
type: "string",
|
|
122
|
+
description: "Path for the immutable dossier JSON",
|
|
123
|
+
default: ".kb/qa/ci/dossier.json"
|
|
124
|
+
},
|
|
125
|
+
excludeJob: {
|
|
126
|
+
type: "string",
|
|
127
|
+
description: "Comma-separated job names to exclude from evidence (for example, the collector job itself)"
|
|
128
|
+
},
|
|
129
|
+
json: {
|
|
130
|
+
type: "boolean",
|
|
131
|
+
description: "Output the captured dossier as JSON",
|
|
132
|
+
default: false
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
var qaCiEvidenceSyncFlags = defineFlags({
|
|
136
|
+
repository: {
|
|
137
|
+
type: "string",
|
|
138
|
+
description: "GitHub repository in owner/name form (defaults to config or GITHUB_REPOSITORY)"
|
|
139
|
+
},
|
|
140
|
+
workflow: {
|
|
141
|
+
type: "string",
|
|
142
|
+
description: "GitHub Actions workflow file or name",
|
|
143
|
+
default: "e2e-platform.yml"
|
|
144
|
+
},
|
|
145
|
+
limit: {
|
|
146
|
+
type: "number",
|
|
147
|
+
description: "Recent workflow runs to inspect for evidence artifacts",
|
|
148
|
+
default: 50
|
|
149
|
+
},
|
|
150
|
+
output: {
|
|
151
|
+
type: "string",
|
|
152
|
+
description: "Local evidence cache directory",
|
|
153
|
+
default: ".kb/qa/ci/evidence"
|
|
154
|
+
},
|
|
155
|
+
json: {
|
|
156
|
+
type: "boolean",
|
|
157
|
+
description: "Output sync details as JSON",
|
|
158
|
+
default: false
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
var qaCiOverviewFlags = defineFlags({
|
|
162
|
+
input: {
|
|
163
|
+
type: "string",
|
|
164
|
+
description: "Dossier JSON file or directory containing dossier.json files",
|
|
165
|
+
default: ".kb/qa/ci"
|
|
166
|
+
},
|
|
167
|
+
output: {
|
|
168
|
+
type: "string",
|
|
169
|
+
description: "Optional file to receive the compact agent-ready JSON overview"
|
|
170
|
+
},
|
|
171
|
+
json: {
|
|
172
|
+
type: "boolean",
|
|
173
|
+
description: "Output the agent-ready reliability overview as JSON",
|
|
174
|
+
default: false
|
|
175
|
+
}
|
|
176
|
+
});
|
|
111
177
|
var qaRegressionsFlags = defineFlags({
|
|
112
178
|
json: {
|
|
113
179
|
type: "boolean",
|
|
@@ -140,7 +206,7 @@ var pluginPermissions = combinePermissions().with(kbPlatformPreset).withFs({
|
|
|
140
206
|
mode: "readWrite",
|
|
141
207
|
allow: ["**"]
|
|
142
208
|
}).withShell({
|
|
143
|
-
allow: ["kb-devkit", "git"]
|
|
209
|
+
allow: ["kb-devkit", "git", "gh"]
|
|
144
210
|
}).withPlatform({
|
|
145
211
|
analytics: true
|
|
146
212
|
}).withQuotas({
|
|
@@ -164,7 +230,9 @@ var manifest = {
|
|
|
164
230
|
cli: {
|
|
165
231
|
groupMeta: [
|
|
166
232
|
{ path: "qa", describe: "Quality assurance powered by kb-devkit" },
|
|
167
|
-
{ path: "qa baseline", describe: "Baseline management" }
|
|
233
|
+
{ path: "qa baseline", describe: "Baseline management" },
|
|
234
|
+
{ path: "qa ci", describe: "CI evidence and reliability analysis" },
|
|
235
|
+
{ path: "qa ci evidence", describe: "Immutable CI evidence capture" }
|
|
168
236
|
],
|
|
169
237
|
commands: [
|
|
170
238
|
{
|
|
@@ -230,6 +298,33 @@ var manifest = {
|
|
|
230
298
|
flags: defineCommandFlags(qaE2eFlakyFlags.schema),
|
|
231
299
|
permissions: pluginPermissions
|
|
232
300
|
},
|
|
301
|
+
{
|
|
302
|
+
path: "qa ci evidence capture",
|
|
303
|
+
category: "CI",
|
|
304
|
+
describe: "Capture a GitHub Actions run dossier (CI producer command)",
|
|
305
|
+
operationType: "execute",
|
|
306
|
+
handler: "./cli/commands/qa-ci-evidence-capture.js#default",
|
|
307
|
+
flags: defineCommandFlags(qaCiEvidenceCaptureFlags.schema),
|
|
308
|
+
permissions: pluginPermissions
|
|
309
|
+
},
|
|
310
|
+
{
|
|
311
|
+
path: "qa ci evidence sync",
|
|
312
|
+
category: "CI",
|
|
313
|
+
describe: "Download new CI evidence artifacts into a local cache",
|
|
314
|
+
operationType: "execute",
|
|
315
|
+
handler: "./cli/commands/qa-ci-evidence-sync.js#default",
|
|
316
|
+
flags: defineCommandFlags(qaCiEvidenceSyncFlags.schema),
|
|
317
|
+
permissions: pluginPermissions
|
|
318
|
+
},
|
|
319
|
+
{
|
|
320
|
+
path: "qa ci overview",
|
|
321
|
+
category: "CI",
|
|
322
|
+
describe: "Show a compact CI reliability overview",
|
|
323
|
+
operationType: "read",
|
|
324
|
+
handler: "./cli/commands/qa-ci-overview.js#default",
|
|
325
|
+
flags: defineCommandFlags(qaCiOverviewFlags.schema),
|
|
326
|
+
permissions: pluginPermissions
|
|
327
|
+
},
|
|
233
328
|
{
|
|
234
329
|
path: "qa regressions",
|
|
235
330
|
category: "History",
|
|
@@ -283,7 +378,8 @@ var manifest = {
|
|
|
283
378
|
{ method: "POST", path: QA_ROUTES.BASELINE_UPDATE, handler: "./rest/handlers/baseline-update-handler.js#default" },
|
|
284
379
|
{ method: "GET", path: QA_ROUTES.BASELINE_DIFF, handler: "./rest/handlers/baseline-diff-handler.js#default" },
|
|
285
380
|
{ method: "GET", path: QA_ROUTES.PACKAGE_TIMELINE, handler: "./rest/handlers/package-timeline-handler.js#default" },
|
|
286
|
-
{ method: "GET", path: QA_ROUTES.TASKS, handler: "./rest/handlers/tasks-handler.js#default" }
|
|
381
|
+
{ method: "GET", path: QA_ROUTES.TASKS, handler: "./rest/handlers/tasks-handler.js#default" },
|
|
382
|
+
{ method: "GET", path: QA_ROUTES.CI_OVERVIEW, handler: "./rest/handlers/ci-overview-handler.js#default" }
|
|
287
383
|
]
|
|
288
384
|
},
|
|
289
385
|
studio: {
|