@roopesh.yadava/qa-pack 1.5.0 → 1.5.1
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/claude/skills/SKILLS_CONTEXT.md +15 -4
- package/claude/skills/manual-testing/SKILL.md +97 -24
- package/claude/skills/manual-testing/TEST_DESIGN_GUIDE.md +200 -0
- package/claude/skills/mobile-automation/BDD_TEMPLATES.md +221 -0
- package/claude/skills/mobile-automation/LOCATOR_PATTERNS.md +205 -0
- package/claude/skills/mobile-automation/MOBILE_MCP_REFERENCE.md +234 -0
- package/claude/skills/mobile-automation/SKILL.md +593 -0
- package/claude/skills/qa-agent/toolkit/qa-toolkit.cjs +62 -9
- package/package.json +1 -1
|
@@ -365,11 +365,17 @@ function cmdRiskScore(args) {
|
|
|
365
365
|
|
|
366
366
|
// ── trust: trust ratchet on auto-approve ──────────────────────────────────
|
|
367
367
|
|
|
368
|
-
|
|
368
|
+
// `namespace` (optional) routes to trust.{namespace}.json instead of trust.json — full
|
|
369
|
+
// isolation for a second automation surface (e.g. mobile-automation) on the same product,
|
|
370
|
+
// so its gate streaks never enter the min() that decides an unrelated pipeline's eligibility.
|
|
371
|
+
// Omitted → identical behavior to before this flag existed (trust.json, no callers changed).
|
|
372
|
+
function trustFile(product, namespace) {
|
|
373
|
+
return path.join(productDir(product), namespace ? `trust.${namespace}.json` : 'trust.json');
|
|
374
|
+
}
|
|
369
375
|
|
|
370
376
|
function cmdTrustRecord(args) {
|
|
371
377
|
requireArgs(args, ['product', 'gate', 'result']);
|
|
372
|
-
const file = trustFile(args.product);
|
|
378
|
+
const file = trustFile(args.product, args.namespace);
|
|
373
379
|
const store = readJson(file, {});
|
|
374
380
|
if (!store[args.gate]) store[args.gate] = { clean: 0, edited: 0, streak: 0 };
|
|
375
381
|
if (args.result === 'clean') { store[args.gate].clean++; store[args.gate].streak++; }
|
|
@@ -380,7 +386,7 @@ function cmdTrustRecord(args) {
|
|
|
380
386
|
|
|
381
387
|
function cmdTrustStatus(args) {
|
|
382
388
|
requireArgs(args, ['product']);
|
|
383
|
-
const store = readJson(trustFile(args.product), {});
|
|
389
|
+
const store = readJson(trustFile(args.product, args.namespace), {});
|
|
384
390
|
const gates = Object.keys(store);
|
|
385
391
|
if (!gates.length) return console.log('NOT_ELIGIBLE — no gate history yet for this product.');
|
|
386
392
|
const minStreak = Math.min(...gates.map((g) => store[g].streak || 0));
|
|
@@ -500,9 +506,12 @@ function escapeHtml(s) {
|
|
|
500
506
|
return String(s).replace(/[&<>"']/g, (c) => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c]));
|
|
501
507
|
}
|
|
502
508
|
|
|
503
|
-
function cmdDashboard() {
|
|
509
|
+
function cmdDashboard(args) {
|
|
504
510
|
const products = listProductFolders();
|
|
505
|
-
if (!products.length)
|
|
511
|
+
if (!products.length) {
|
|
512
|
+
if (args && args.json) return console.log(JSON.stringify({ products: 0, totalRuns: 0, totalOpenBugs: 0, repos: [] }));
|
|
513
|
+
return console.log('NO_PRODUCTS — no product has run yet.');
|
|
514
|
+
}
|
|
506
515
|
const stats = products.map(collectProductStats).sort((a, b) => b.runCount - a.runCount);
|
|
507
516
|
const totalRuns = stats.reduce((a, s) => a + s.runCount, 0);
|
|
508
517
|
const totalOpenBugs = stats.reduce((a, s) => a + s.openBugs, 0);
|
|
@@ -553,6 +562,21 @@ function cmdDashboard() {
|
|
|
553
562
|
|
|
554
563
|
ensureDir(OUTPUTS_DIR);
|
|
555
564
|
fs.writeFileSync(path.join(OUTPUTS_DIR, 'dashboard.html'), html);
|
|
565
|
+
|
|
566
|
+
if (args && args.json) {
|
|
567
|
+
console.log(JSON.stringify({
|
|
568
|
+
products: products.length,
|
|
569
|
+
totalRuns,
|
|
570
|
+
totalOpenBugs,
|
|
571
|
+
htmlPath: 'outputs/dashboard.html',
|
|
572
|
+
repos: stats.map((s) => ({
|
|
573
|
+
product: s.product, name: s.name, env: s.env, runCount: s.runCount,
|
|
574
|
+
avgReuse: s.avgReuse, flowCount: s.flowCount, openBugs: s.openBugs, bugCount: s.bugCount,
|
|
575
|
+
lastRunDate: s.lastRun ? s.lastRun['Date'] : null, lastRunCard: s.lastRun ? s.lastRun['Card'] : null,
|
|
576
|
+
})),
|
|
577
|
+
}));
|
|
578
|
+
return;
|
|
579
|
+
}
|
|
556
580
|
console.log(`Dashboard saved: outputs/dashboard.html (${products.length} products, ${totalRuns} runs, ${totalOpenBugs} open bugs)`);
|
|
557
581
|
}
|
|
558
582
|
|
|
@@ -569,11 +593,15 @@ function withinDays(dateStr, days) {
|
|
|
569
593
|
function cmdDigest(args) {
|
|
570
594
|
const days = parseInt(args.days, 10) || 7;
|
|
571
595
|
const products = listProductFolders();
|
|
572
|
-
if (!products.length)
|
|
596
|
+
if (!products.length) {
|
|
597
|
+
if (args && args.json) return console.log(JSON.stringify({ days, totalRuns: 0, totalBugs: 0, activeProducts: 0, totalProducts: 0, products: [] }));
|
|
598
|
+
return console.log('NO_PRODUCTS');
|
|
599
|
+
}
|
|
573
600
|
|
|
574
601
|
let totalRuns = 0;
|
|
575
602
|
let totalBugs = 0;
|
|
576
603
|
const sections = [];
|
|
604
|
+
const productsOut = [];
|
|
577
605
|
for (const product of products) {
|
|
578
606
|
const info = getProductInfo(product);
|
|
579
607
|
const runs = getTable(product, 'Runs Log').rows.filter((r) => withinDays(r['Date'], days));
|
|
@@ -582,10 +610,12 @@ function cmdDigest(args) {
|
|
|
582
610
|
const bugsFiled = runs.reduce((sum, r) => sum + bugsFiledCount(r), 0);
|
|
583
611
|
totalBugs += bugsFiled;
|
|
584
612
|
const avgReuse = avg(pctList(runs, 'Reuse %'));
|
|
613
|
+
const cards = runs.map((r) => r['Card']);
|
|
585
614
|
sections.push(
|
|
586
|
-
`### ${info.name || product}\n- Runs: ${runs.length} (${
|
|
615
|
+
`### ${info.name || product}\n- Runs: ${runs.length} (${cards.join(', ')})\n` +
|
|
587
616
|
`- Bugs filed: ${bugsFiled}\n- Avg reuse: ${avgReuse !== null ? avgReuse + '%' : 'n/a'}\n`
|
|
588
617
|
);
|
|
618
|
+
productsOut.push({ product, name: info.name || product, runCount: runs.length, cards, bugsFiled, avgReuse });
|
|
589
619
|
}
|
|
590
620
|
|
|
591
621
|
const today = todayStr();
|
|
@@ -598,6 +628,16 @@ function cmdDigest(args) {
|
|
|
598
628
|
ensureDir(OUTPUTS_DIR);
|
|
599
629
|
const file = path.join(OUTPUTS_DIR, `qa-weekly-digest-${today}.md`);
|
|
600
630
|
fs.writeFileSync(file, md);
|
|
631
|
+
|
|
632
|
+
if (args && args.json) {
|
|
633
|
+
console.log(JSON.stringify({
|
|
634
|
+
days, totalRuns, totalBugs,
|
|
635
|
+
activeProducts: sections.length, totalProducts: products.length,
|
|
636
|
+
mdPath: `outputs/qa-weekly-digest-${today}.md`,
|
|
637
|
+
products: productsOut,
|
|
638
|
+
}));
|
|
639
|
+
return;
|
|
640
|
+
}
|
|
601
641
|
console.log(
|
|
602
642
|
`Digest saved: outputs/qa-weekly-digest-${today}.md — ${totalRuns} runs, ${totalBugs} bugs across ` +
|
|
603
643
|
`${sections.length} active product(s) in the last ${days} days.`
|
|
@@ -606,9 +646,12 @@ function cmdDigest(args) {
|
|
|
606
646
|
|
|
607
647
|
// ── roi: token-spend / ROI view ────────────────────────────────────────────
|
|
608
648
|
|
|
609
|
-
function cmdRoi() {
|
|
649
|
+
function cmdRoi(args) {
|
|
610
650
|
const products = listProductFolders();
|
|
611
|
-
if (!products.length)
|
|
651
|
+
if (!products.length) {
|
|
652
|
+
if (args && args.json) return console.log(JSON.stringify({ totalRuns: 0, totalBugs: 0, hasTracking: false, rows: [] }));
|
|
653
|
+
return console.log('NO_PRODUCTS');
|
|
654
|
+
}
|
|
612
655
|
|
|
613
656
|
const tracking = tryReadTrackingLog();
|
|
614
657
|
const hasTracking = Array.isArray(tracking) && tracking.length > 0;
|
|
@@ -660,6 +703,16 @@ function cmdRoi() {
|
|
|
660
703
|
|
|
661
704
|
ensureDir(OUTPUTS_DIR);
|
|
662
705
|
fs.writeFileSync(path.join(OUTPUTS_DIR, 'roi-report.md'), md);
|
|
706
|
+
|
|
707
|
+
if (args && args.json) {
|
|
708
|
+
console.log(JSON.stringify({
|
|
709
|
+
totalRuns, totalBugs, hasTracking,
|
|
710
|
+
avgTokensPerRun: hasTracking && tokenRuns ? Math.round(totalTokens / tokenRuns) : null,
|
|
711
|
+
mdPath: 'outputs/roi-report.md',
|
|
712
|
+
rows: rowsOut,
|
|
713
|
+
}));
|
|
714
|
+
return;
|
|
715
|
+
}
|
|
663
716
|
console.log(
|
|
664
717
|
`ROI report saved: outputs/roi-report.md — ${totalRuns} runs, ${totalBugs} bugs` +
|
|
665
718
|
`${hasTracking ? ', token data included' : ' (token data unavailable — set QA_TRACKING_DIR to enable)'}.`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@roopesh.yadava/qa-pack",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.1",
|
|
4
4
|
"description": "AI-powered QA agent skills for Claude Code — manual testing, BDD automation, accessibility, UI/Figma diff, bug reporting",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"postinstall": "node bin/postinstall.js"
|