@qa-gentic/stlc-agents 1.0.23 → 1.0.25

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qa-gentic/stlc-agents",
3
- "version": "1.0.23",
3
+ "version": "1.0.25",
4
4
  "description": "QA STLC Agents — five MCP servers + skills for AI-powered test case, Gherkin, Playwright generation, and Helix-QA file writing against Azure DevOps and Jira Cloud. Full pipeline for both: fetch → test cases → Gherkin → Playwright → Helix-QA. Works with Claude Code, GitHub Copilot, Cursor, Windsurf.",
5
5
  "keywords": [
6
6
  "playwright",
@@ -17,6 +17,7 @@ Skills: see skills/generate-gherkin.md
17
17
  import asyncio
18
18
  import json
19
19
  import sys
20
+ import time
20
21
 
21
22
  from dotenv import load_dotenv
22
23
  from mcp.server import Server
@@ -31,6 +32,7 @@ from stlc_agents.agent_gherkin_generator.tools.ado_gherkin import (
31
32
  attach_work_item_file as _attach_wi_file,
32
33
  validate_gherkin_content as _validate_gherkin,
33
34
  )
35
+ from stlc_agents.shared.cost_tracker import track
34
36
 
35
37
  load_dotenv()
36
38
 
@@ -377,6 +379,7 @@ async def list_tools() -> list[types.Tool]:
377
379
 
378
380
  @app.call_tool()
379
381
  async def call_tool(name: str, arguments: dict) -> list[types.TextContent]:
382
+ t0 = time.monotonic()
380
383
  try:
381
384
  if name == "fetch_feature_hierarchy":
382
385
  result = await asyncio.to_thread(
@@ -415,7 +418,7 @@ async def call_tool(name: str, arguments: dict) -> list[types.TextContent]:
415
418
  "Fix the errors below before attaching to ADO."
416
419
  ),
417
420
  }
418
- return [types.TextContent(type="text", text=json.dumps(result, indent=2, ensure_ascii=False))]
421
+ return track(result, tool_name=name, server="qa-gherkin-generator", t0=t0)
419
422
 
420
423
  result = await asyncio.to_thread(
421
424
  _attach_feature,
@@ -445,7 +448,7 @@ async def call_tool(name: str, arguments: dict) -> list[types.TextContent]:
445
448
  "Fix the errors below before attaching to ADO."
446
449
  ),
447
450
  }
448
- return [types.TextContent(type="text", text=json.dumps(result, indent=2, ensure_ascii=False))]
451
+ return track(result, tool_name=name, server="qa-gherkin-generator", t0=t0)
449
452
 
450
453
  result = await asyncio.to_thread(
451
454
  _attach_wi_file,
@@ -506,13 +509,11 @@ async def call_tool(name: str, arguments: dict) -> list[types.TextContent]:
506
509
  else:
507
510
  result = {"error": f"Unknown tool: {name}"}
508
511
 
509
- return [types.TextContent(type="text", text=json.dumps(result, indent=2, ensure_ascii=False))]
512
+ return track(result, tool_name=name, server="qa-gherkin-generator", t0=t0)
510
513
 
511
514
  except Exception as exc:
512
- return [types.TextContent(
513
- type="text",
514
- text=json.dumps({"error": str(exc), "tool": name}, indent=2),
515
- )]
515
+ err_result = {"error": str(exc), "tool": name}
516
+ return track(err_result, tool_name=name, server="qa-gherkin-generator", t0=t0)
516
517
 
517
518
 
518
519
  async def _run():
@@ -23,6 +23,7 @@ import asyncio
23
23
  import json
24
24
  import re
25
25
  import sys
26
+ import time
26
27
 
27
28
  from dotenv import load_dotenv
28
29
  from mcp.server import Server
@@ -36,6 +37,7 @@ from stlc_agents.agent_helix_writer.tools.helix_write import (
36
37
  list_helix_tree as _list_tree,
37
38
  update_helix_file as _update_file,
38
39
  )
40
+ from stlc_agents.shared.cost_tracker import track
39
41
 
40
42
  load_dotenv()
41
43
 
@@ -448,6 +450,7 @@ async def list_tools() -> list[types.Tool]:
448
450
 
449
451
  @app.call_tool()
450
452
  async def call_tool(name: str, arguments: dict) -> list[types.TextContent]:
453
+ t0 = time.monotonic()
451
454
  try:
452
455
  if name == "inspect_helix_project":
453
456
  result = await asyncio.to_thread(_inspect, arguments["helix_root"])
@@ -467,7 +470,7 @@ async def call_tool(name: str, arguments: dict) -> list[types.TextContent]:
467
470
  "below and retry. No files were written to disk."
468
471
  ),
469
472
  }
470
- return [types.TextContent(type="text", text=json.dumps(result, indent=2, ensure_ascii=False))]
473
+ return track(result, tool_name=name, server="qa-helix-writer", t0=t0)
471
474
 
472
475
  result = await asyncio.to_thread(
473
476
  _write_files,
@@ -507,13 +510,11 @@ async def call_tool(name: str, arguments: dict) -> list[types.TextContent]:
507
510
  else:
508
511
  result = {"error": f"Unknown tool: {name}"}
509
512
 
510
- return [types.TextContent(type="text", text=json.dumps(result, indent=2, ensure_ascii=False))]
513
+ return track(result, tool_name=name, server="qa-helix-writer", t0=t0)
511
514
 
512
515
  except Exception as exc:
513
- return [types.TextContent(
514
- type="text",
515
- text=json.dumps({"error": str(exc), "tool": name}, indent=2),
516
- )]
516
+ err_result = {"error": str(exc), "tool": name}
517
+ return track(err_result, tool_name=name, server="qa-helix-writer", t0=t0)
517
518
 
518
519
 
519
520
  async def _run():
@@ -22,6 +22,7 @@ Skills: see skills/qa-jira-manager.md
22
22
  import asyncio
23
23
  import json
24
24
  import sys
25
+ import time
25
26
 
26
27
  from dotenv import load_dotenv
27
28
  from mcp.server import Server
@@ -36,6 +37,7 @@ from stlc_agents.agent_jira_manager.tools.jira_workitem import (
36
37
  get_linked_test_cases as _get_linked_test_cases,
37
38
  attach_gherkin_to_issue as _attach_gherkin,
38
39
  )
40
+ from stlc_agents.shared.cost_tracker import track
39
41
 
40
42
  load_dotenv()
41
43
 
@@ -411,6 +413,7 @@ async def list_tools() -> list[types.Tool]:
411
413
 
412
414
  @app.call_tool()
413
415
  async def call_tool(name: str, arguments: dict) -> list[types.TextContent]:
416
+ t0 = time.monotonic()
414
417
  try:
415
418
  cloud_id = (arguments.get("cloud_id") or "").strip() or get_cloud_id()
416
419
 
@@ -442,7 +445,7 @@ async def call_tool(name: str, arguments: dict) -> list[types.TextContent]:
442
445
  "below and retry. No test cases were created in Jira."
443
446
  ),
444
447
  }
445
- return [types.TextContent(type="text", text=json.dumps(result, indent=2, ensure_ascii=False))]
448
+ return track(result, tool_name=name, server="qa-jira-manager", t0=t0)
446
449
 
447
450
  # ── Peek at the issue to get type and project_key ─────────────
448
451
  issue_data: dict = {}
@@ -484,7 +487,7 @@ async def call_tool(name: str, arguments: dict) -> list[types.TextContent]:
484
487
  "Reply 'yes' or 'confirm' to proceed, or 'no' / 'cancel' to abort."
485
488
  ),
486
489
  }
487
- return [types.TextContent(type="text", text=json.dumps(result, indent=2, ensure_ascii=False))]
490
+ return track(result, tool_name=name, server="qa-jira-manager", t0=t0)
488
491
 
489
492
  if not project_key:
490
493
  result = {
@@ -494,7 +497,7 @@ async def call_tool(name: str, arguments: dict) -> list[types.TextContent]:
494
497
  "Pass project_key explicitly, e.g. 'PROJ'."
495
498
  ),
496
499
  }
497
- return [types.TextContent(type="text", text=json.dumps(result, indent=2, ensure_ascii=False))]
500
+ return track(result, tool_name=name, server="qa-jira-manager", t0=t0)
498
501
 
499
502
  # ── Create test cases ─────────────────────────────────────────
500
503
  created = []
@@ -638,13 +641,11 @@ async def call_tool(name: str, arguments: dict) -> list[types.TextContent]:
638
641
  else:
639
642
  result = {"error": f"Unknown tool: {name}"}
640
643
 
641
- return [types.TextContent(type="text", text=json.dumps(result, indent=2, ensure_ascii=False))]
644
+ return track(result, tool_name=name, server="qa-jira-manager", t0=t0)
642
645
 
643
646
  except Exception as exc:
644
- return [types.TextContent(
645
- type="text",
646
- text=json.dumps({"error": str(exc), "tool": name}, indent=2),
647
- )]
647
+ err_result = {"error": str(exc), "tool": name}
648
+ return track(err_result, tool_name=name, server="qa-jira-manager", t0=t0)
648
649
 
649
650
 
650
651
  # ---------------------------------------------------------------------------
@@ -17,6 +17,7 @@ import asyncio
17
17
  import json
18
18
  import os
19
19
  import sys
20
+ import time
20
21
 
21
22
  from dotenv import load_dotenv
22
23
  from mcp.server import Server
@@ -30,6 +31,7 @@ from stlc_agents.agent_test_case_manager.tools.ado_workitem import (
30
31
  link_test_cases_to_work_item as _link_test_cases,
31
32
  get_linked_test_cases as _get_linked_test_cases,
32
33
  )
34
+ from stlc_agents.shared.cost_tracker import track
33
35
 
34
36
  load_dotenv()
35
37
 
@@ -351,6 +353,7 @@ async def list_tools() -> list[types.Tool]:
351
353
 
352
354
  @app.call_tool()
353
355
  async def call_tool(name: str, arguments: dict) -> list[types.TextContent]:
356
+ t0 = time.monotonic()
354
357
  try:
355
358
  if name == "fetch_work_item":
356
359
  result = await asyncio.to_thread(
@@ -380,7 +383,7 @@ async def call_tool(name: str, arguments: dict) -> list[types.TextContent]:
380
383
  "below and retry. No test cases were created in ADO."
381
384
  ),
382
385
  }
383
- return [types.TextContent(type="text", text=json.dumps(result, indent=2, ensure_ascii=False))]
386
+ return track(result, tool_name=name, server="qa-test-case-manager", t0=t0)
384
387
 
385
388
  # ── Epic guard — always check work item type before creating TCs ─
386
389
  try:
@@ -399,7 +402,7 @@ async def call_tool(name: str, arguments: dict) -> list[types.TextContent]:
399
402
  "Feature or PBI/Bug ID instead."
400
403
  ),
401
404
  }
402
- return [types.TextContent(type="text", text=json.dumps(result, indent=2, ensure_ascii=False))]
405
+ return track(result, tool_name=name, server="qa-test-case-manager", t0=t0)
403
406
  except Exception:
404
407
  pass # if the peek fails, continue and let the real call surface the error
405
408
 
@@ -448,7 +451,7 @@ async def call_tool(name: str, arguments: dict) -> list[types.TextContent]:
448
451
  "Reply 'yes' or 'confirm' to proceed, or 'no' / 'cancel' to abort."
449
452
  ),
450
453
  }
451
- return [types.TextContent(type="text", text=json.dumps(result, indent=2, ensure_ascii=False))]
454
+ return track(result, tool_name=name, server="qa-test-case-manager", t0=t0)
452
455
  except Exception:
453
456
  pass # wi_data may not be set if the earlier peek failed; continue normally
454
457
 
@@ -565,13 +568,11 @@ async def call_tool(name: str, arguments: dict) -> list[types.TextContent]:
565
568
  else:
566
569
  result = {"error": f"Unknown tool: {name}"}
567
570
 
568
- return [types.TextContent(type="text", text=json.dumps(result, indent=2, ensure_ascii=False))]
571
+ return track(result, tool_name=name, server="qa-test-case-manager", t0=t0)
569
572
 
570
573
  except Exception as exc:
571
- return [types.TextContent(
572
- type="text",
573
- text=json.dumps({"error": str(exc), "tool": name}, indent=2),
574
- )]
574
+ err_result = {"error": str(exc), "tool": name}
575
+ return track(err_result, tool_name=name, server="qa-test-case-manager", t0=t0)
575
576
 
576
577
 
577
578
  # ---------------------------------------------------------------------------