zephyr-enterprise-tools 1.2.6 → 1.2.7

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/mcp-server.js CHANGED
@@ -20,12 +20,13 @@ import QualityGates from './zephyr-enterprise-tools.js';
20
20
  const TOOLS = [
21
21
  {
22
22
  name: 'release_readiness',
23
- description: 'Run all 4 quality gates to assess release readiness. Returns GO, CONDITIONAL GO, or NO GO status.',
23
+ description: 'Run all 4 quality gates with calculation details and action items. Optionally scope Test Plan Analysis to a Zephyr ZQL expression. Returns GO, CONDITIONAL GO, or NO GO status.',
24
24
  inputSchema: {
25
25
  type: 'object',
26
26
  properties: {
27
27
  projectId: { type: 'number', description: 'Zephyr project ID' },
28
28
  releaseId: { type: 'number', description: 'Zephyr release ID' },
29
+ query: { type: 'string', description: 'Optional Zephyr ZQL expression for Test Plan Analysis, e.g. priority = "P1"' },
29
30
  },
30
31
  required: ['projectId', 'releaseId'],
31
32
  },
@@ -265,7 +266,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
265
266
 
266
267
  switch (name) {
267
268
  case 'release_readiness':
268
- result = await tools.runAllGates(projectId, releaseId);
269
+ result = await tools.runAllGates(projectId, releaseId, { query: args.query });
269
270
  break;
270
271
 
271
272
  case 'requirement_coverage':
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zephyr-enterprise-tools",
3
- "version": "1.2.6",
3
+ "version": "1.2.7",
4
4
  "description": "Comprehensive Zephyr Enterprise Tools - Release Readiness, Project Health, Test Analytics & More",
5
5
  "main": "zephyr-enterprise-tools.js",
6
6
  "types": "zephyr-enterprise-tools.d.ts",
@@ -70,8 +70,15 @@ export interface ReleaseReadinessResult {
70
70
  projectId: number;
71
71
  releaseId: number;
72
72
  timestamp: string;
73
+ report: {
74
+ projectName: string;
75
+ releaseName: string;
76
+ generatedAt: string;
77
+ fileName: string;
78
+ };
73
79
  overallStatus: GateStatus;
74
80
  summary: { passed: number; failed: number; conditional: number };
81
+ details: Record<string, unknown>;
75
82
  recommendation: string;
76
83
  gates: {
77
84
  requirementCoverage: RequirementCoverageResult;
@@ -185,7 +192,7 @@ export declare class QualityGates {
185
192
  testPlanAnalysisGate(projectId: number, releaseId: number, options?: TestPlanOptions): Promise<TestPlanResult>;
186
193
  testExecutionGate(projectId: number, releaseId: number): Promise<TestExecutionResult>;
187
194
  defectQualityGate(projectId: number, releaseId: number): Promise<DefectQualityResult>;
188
- runAllGates(projectId: number, releaseId: number): Promise<ReleaseReadinessResult>;
195
+ runAllGates(projectId: number, releaseId: number, options?: TestPlanOptions): Promise<ReleaseReadinessResult>;
189
196
 
190
197
  // Analytics & Insights
191
198
  getProjectHealth(projectId: number, releaseId: number): Promise<ProjectHealthResult>;
@@ -540,13 +540,20 @@ export class QualityGates {
540
540
 
541
541
  // ─── Run All Gates ──────────────────────────────────────────────────────────
542
542
 
543
- async runAllGates(projectId, releaseId) {
544
- const results = await Promise.all([
545
- this.requirementCoverageGate(projectId, releaseId),
546
- this.testPlanAnalysisGate(projectId, releaseId),
547
- this.testExecutionGate(projectId, releaseId),
548
- this.defectQualityGate(projectId, releaseId),
543
+ async runAllGates(projectId, releaseId, options = {}) {
544
+ const { query } = options;
545
+ const [results, project, releases] = await Promise.all([
546
+ Promise.all([
547
+ this.requirementCoverageGate(projectId, releaseId),
548
+ this.testPlanAnalysisGate(projectId, releaseId, { query }),
549
+ this.testExecutionGate(projectId, releaseId),
550
+ this.defectQualityGate(projectId, releaseId),
551
+ ]),
552
+ this.GET(`/project/${projectId}`).catch(() => ({})),
553
+ this.GET('/release', { projectid: projectId, isaliasallowed: false }).catch(() => []),
549
554
  ]);
555
+ const release = Array.isArray(releases) ? releases.find(item => Number(item.id) === Number(releaseId)) : undefined;
556
+ const generatedAt = new Date().toISOString();
550
557
 
551
558
  const gates = {
552
559
  requirementCoverage: results[0],
@@ -576,7 +583,13 @@ export class QualityGates {
576
583
  return {
577
584
  projectId,
578
585
  releaseId,
579
- timestamp: new Date().toISOString(),
586
+ timestamp: generatedAt,
587
+ report: {
588
+ projectName: project.name || `Project ${projectId}`,
589
+ releaseName: release?.name || `Release ${releaseId}`,
590
+ generatedAt,
591
+ fileName: this.getReportFileName(project.name || `Project ${projectId}`, release?.name || `Release ${releaseId}`, generatedAt),
592
+ },
580
593
  overallStatus,
581
594
  summary: {
582
595
  passed: passedGates,
@@ -585,10 +598,72 @@ export class QualityGates {
585
598
  total: 4,
586
599
  },
587
600
  gates,
601
+ details: this.getReadinessDetails(gates, query),
588
602
  recommendation: this.getRecommendation(overallStatus, gates),
589
603
  };
590
604
  }
591
605
 
606
+ getReportFileName(projectName, releaseName, generatedAt) {
607
+ const safeSegment = value => String(value)
608
+ .trim()
609
+ .replace(/[^a-zA-Z0-9]+/g, '_')
610
+ .replace(/^_+|_+$/g, '');
611
+ const timestamp = new Date(generatedAt).toISOString();
612
+ const date = timestamp.slice(0, 10);
613
+ const time = timestamp.slice(11, 19).replace(/:/g, '-');
614
+
615
+ return `Release_Rediness_${safeSegment(projectName)}_${safeSegment(releaseName)}_${date}_${time}_UTC.pdf`;
616
+ }
617
+
618
+ getReadinessDetails(gates, query) {
619
+ const plan = gates.testPlanAnalysis;
620
+ const coverage = gates.requirementCoverage;
621
+ const execution = gates.testExecution;
622
+ const defects = gates.defectQuality;
623
+ const planAnalysis = plan.analysis;
624
+ const remainingToPlan = Math.max(0, planAnalysis.testcasePlanning.totalTestcases - planAnalysis.testcasePlanning.plannedTestcases);
625
+ const remainingToExecute = execution.incompleteTests;
626
+ const additionalTestsForGo = Math.max(0, Math.ceil((0.97 * execution.totalPlannedTests) - execution.completedTests));
627
+
628
+ return {
629
+ testPlanAnalysis: {
630
+ scope: query ? "ZQL-filtered test cases" : "Release-wide",
631
+ query: query || undefined,
632
+ calculation: {
633
+ testcasePlanning: `${planAnalysis.testcasePlanning.plannedTestcases} / ${planAnalysis.testcasePlanning.totalTestcases} = ${planAnalysis.testcasePlanning.percentage}%`,
634
+ executionAssignment: `${planAnalysis.executionAssignment.assignedExecutions} / ${planAnalysis.executionAssignment.totalExecutions} = ${planAnalysis.executionAssignment.percentage}%`,
635
+ overallPlanning: `(${planAnalysis.testcasePlanning.percentage}% + ${planAnalysis.executionAssignment.percentage}%) / 2 = ${plan.overallPlanningPercentage}%`,
636
+ },
637
+ actionItems: remainingToPlan > 0
638
+ ? [`Plan the remaining ${remainingToPlan} test cases.`, "Maintain execution assignment as tests are planned."]
639
+ : ["Maintain complete test planning and execution assignment."],
640
+ },
641
+ requirementCoverage: {
642
+ scope: "Release-wide",
643
+ calculation: `${coverage.coveredRequirements} / ${coverage.totalRequirements} = ${coverage.coveragePercentage}%`,
644
+ uncoveredRequirementIds: [],
645
+ detailAvailability: "Zephyr release summaries provide the uncovered count but not requirement IDs.",
646
+ actionItems: coverage.notCoveredRequirements > 0
647
+ ? [`Create and map tests for ${coverage.notCoveredRequirements} uncovered requirements.`, "Confirm requirement mappings before release sign-off."]
648
+ : ["Maintain requirement-to-test traceability through release sign-off."],
649
+ },
650
+ testExecution: {
651
+ scope: "Release-wide",
652
+ calculation: `${execution.completedTests} / ${execution.totalPlannedTests} = ${execution.executionPercentage}%`,
653
+ actionItems: remainingToExecute > 0
654
+ ? [`Execute or resolve ${remainingToExecute} incomplete tests.`, `Complete at least ${additionalTestsForGo} additional tests to reach the 97% GO threshold.`, ...(execution.breakdown.failed > 0 ? [`Investigate and rerun ${execution.breakdown.failed} failed test(s).`] : [])]
655
+ : ["Maintain execution completion through release sign-off."],
656
+ },
657
+ defectQuality: {
658
+ scope: "Release-wide",
659
+ defects: defects.breakdown,
660
+ actionItems: defects.unresolvedDefects > 0
661
+ ? ["Resolve all blocker defects.", "Reduce unresolved high-risk defects to 10 or fewer before approval."]
662
+ : ["Verify resolved defect fixes with targeted regression coverage.", "Confirm product-owner approval for any Won't Fix resolutions."],
663
+ },
664
+ };
665
+ }
666
+
592
667
  getRecommendation(status, gates) {
593
668
  const issues = [];
594
669