@qa-gentic/stlc-agents 1.0.23 → 1.0.26
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 +1 -1
- package/skills/generate-test-cases/SKILL.md +5 -0
- package/src/cli/cmd-cost.js +61 -30
- package/src/cli/cmd-init.js +88 -8
- package/src/stlc_agents/__pycache__/__init__.cpython-314.pyc +0 -0
- package/src/stlc_agents/agent_gherkin_generator/__pycache__/__init__.cpython-314.pyc +0 -0
- package/src/stlc_agents/agent_gherkin_generator/__pycache__/server.cpython-314.pyc +0 -0
- package/src/stlc_agents/agent_gherkin_generator/server.py +8 -7
- package/src/stlc_agents/agent_gherkin_generator/tools/__pycache__/__init__.cpython-314.pyc +0 -0
- package/src/stlc_agents/agent_gherkin_generator/tools/__pycache__/ado_gherkin.cpython-314.pyc +0 -0
- package/src/stlc_agents/agent_helix_writer/__pycache__/__init__.cpython-314.pyc +0 -0
- package/src/stlc_agents/agent_helix_writer/__pycache__/server.cpython-314.pyc +0 -0
- package/src/stlc_agents/agent_helix_writer/server.py +48 -12
- package/src/stlc_agents/agent_helix_writer/tools/__pycache__/__init__.cpython-314.pyc +0 -0
- package/src/stlc_agents/agent_helix_writer/tools/__pycache__/boilerplate.cpython-314.pyc +0 -0
- package/src/stlc_agents/agent_helix_writer/tools/__pycache__/helix_write.cpython-314.pyc +0 -0
- package/src/stlc_agents/agent_jira_manager/server.py +9 -8
- package/src/stlc_agents/agent_playwright_generator/__pycache__/__init__.cpython-314.pyc +0 -0
- package/src/stlc_agents/agent_playwright_generator/__pycache__/server.cpython-314.pyc +0 -0
- package/src/stlc_agents/agent_playwright_generator/server.py +419 -213
- package/src/stlc_agents/agent_playwright_generator/tools/__pycache__/__init__.cpython-314.pyc +0 -0
- package/src/stlc_agents/agent_playwright_generator/tools/__pycache__/ado_attach.cpython-314.pyc +0 -0
- package/src/stlc_agents/agent_test_case_manager/__pycache__/__init__.cpython-314.pyc +0 -0
- package/src/stlc_agents/agent_test_case_manager/__pycache__/server.cpython-314.pyc +0 -0
- package/src/stlc_agents/agent_test_case_manager/server.py +21 -8
- package/src/stlc_agents/agent_test_case_manager/tools/__pycache__/__init__.cpython-314.pyc +0 -0
- package/src/stlc_agents/agent_test_case_manager/tools/__pycache__/ado_workitem.cpython-314.pyc +0 -0
- package/src/stlc_agents/agent_test_case_manager/tools/ado_workitem.py +65 -1
- package/src/stlc_agents/shared/__pycache__/__init__.cpython-314.pyc +0 -0
- package/src/stlc_agents/shared/__pycache__/auth.cpython-314.pyc +0 -0
- package/src/stlc_agents/shared/__pycache__/cost_tracker.cpython-314.pyc +0 -0
- package/src/stlc_agents/shared/__pycache__/pricing.cpython-314.pyc +0 -0
- package/src/stlc_agents/shared/cost_tracker.py +378 -70
- package/src/stlc_agents/shared/pricing.py +115 -24
- package/src/stlc_agents/webhook_orchestrator/__init__.py +0 -0
- package/src/stlc_agents/webhook_orchestrator/agent_runner.py +599 -0
- package/src/stlc_agents/webhook_orchestrator/main.py +43 -0
- package/src/stlc_agents/webhook_orchestrator/models.py +63 -0
- package/src/stlc_agents/webhook_orchestrator/orchestrator.py +103 -0
- package/src/stlc_agents/webhook_orchestrator/pipelines/__init__.py +0 -0
- package/src/stlc_agents/webhook_orchestrator/pipelines/_base.py +57 -0
- package/src/stlc_agents/webhook_orchestrator/pipelines/ado_test_cases.py +55 -0
- package/src/stlc_agents/webhook_orchestrator/pipelines/full_pipeline.py +202 -0
- package/src/stlc_agents/webhook_orchestrator/pipelines/gherkin_playwright.py +156 -0
- package/src/stlc_agents/webhook_orchestrator/pipelines/jira_test_cases.py +48 -0
- package/src/stlc_agents/webhook_orchestrator/webhook_bridge.py +368 -0
|
Binary file
|
package/src/stlc_agents/agent_playwright_generator/tools/__pycache__/ado_attach.cpython-314.pyc
ADDED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -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
|
|
@@ -29,7 +30,9 @@ from stlc_agents.agent_test_case_manager.tools.ado_workitem import (
|
|
|
29
30
|
create_test_case as _create_test_case,
|
|
30
31
|
link_test_cases_to_work_item as _link_test_cases,
|
|
31
32
|
get_linked_test_cases as _get_linked_test_cases,
|
|
33
|
+
add_tag_to_work_item as _add_tag,
|
|
32
34
|
)
|
|
35
|
+
from stlc_agents.shared.cost_tracker import track
|
|
33
36
|
|
|
34
37
|
load_dotenv()
|
|
35
38
|
|
|
@@ -351,6 +354,7 @@ async def list_tools() -> list[types.Tool]:
|
|
|
351
354
|
|
|
352
355
|
@app.call_tool()
|
|
353
356
|
async def call_tool(name: str, arguments: dict) -> list[types.TextContent]:
|
|
357
|
+
t0 = time.monotonic()
|
|
354
358
|
try:
|
|
355
359
|
if name == "fetch_work_item":
|
|
356
360
|
result = await asyncio.to_thread(
|
|
@@ -380,7 +384,7 @@ async def call_tool(name: str, arguments: dict) -> list[types.TextContent]:
|
|
|
380
384
|
"below and retry. No test cases were created in ADO."
|
|
381
385
|
),
|
|
382
386
|
}
|
|
383
|
-
return
|
|
387
|
+
return track(result, tool_name=name, server="qa-test-case-manager", t0=t0)
|
|
384
388
|
|
|
385
389
|
# ── Epic guard — always check work item type before creating TCs ─
|
|
386
390
|
try:
|
|
@@ -399,7 +403,7 @@ async def call_tool(name: str, arguments: dict) -> list[types.TextContent]:
|
|
|
399
403
|
"Feature or PBI/Bug ID instead."
|
|
400
404
|
),
|
|
401
405
|
}
|
|
402
|
-
return
|
|
406
|
+
return track(result, tool_name=name, server="qa-test-case-manager", t0=t0)
|
|
403
407
|
except Exception:
|
|
404
408
|
pass # if the peek fails, continue and let the real call surface the error
|
|
405
409
|
|
|
@@ -448,7 +452,7 @@ async def call_tool(name: str, arguments: dict) -> list[types.TextContent]:
|
|
|
448
452
|
"Reply 'yes' or 'confirm' to proceed, or 'no' / 'cancel' to abort."
|
|
449
453
|
),
|
|
450
454
|
}
|
|
451
|
-
return
|
|
455
|
+
return track(result, tool_name=name, server="qa-test-case-manager", t0=t0)
|
|
452
456
|
except Exception:
|
|
453
457
|
pass # wi_data may not be set if the earlier peek failed; continue normally
|
|
454
458
|
|
|
@@ -480,6 +484,16 @@ async def call_tool(name: str, arguments: dict) -> list[types.TextContent]:
|
|
|
480
484
|
except Exception as e:
|
|
481
485
|
link_result = {"error": str(e)}
|
|
482
486
|
|
|
487
|
+
# Add tag to the parent work item after linking
|
|
488
|
+
tag_result = {}
|
|
489
|
+
if created:
|
|
490
|
+
try:
|
|
491
|
+
tag_result = await asyncio.to_thread(
|
|
492
|
+
_add_tag, org, project, wi_id, "STLCAgentTestCases"
|
|
493
|
+
)
|
|
494
|
+
except Exception as e:
|
|
495
|
+
tag_result = {"error": str(e)}
|
|
496
|
+
|
|
483
497
|
result = {
|
|
484
498
|
"summary": {
|
|
485
499
|
"requested": len(test_cases),
|
|
@@ -490,6 +504,7 @@ async def call_tool(name: str, arguments: dict) -> list[types.TextContent]:
|
|
|
490
504
|
"created_test_cases": created,
|
|
491
505
|
"failed": failed,
|
|
492
506
|
"link_result": link_result,
|
|
507
|
+
"tag_result": tag_result,
|
|
493
508
|
"_validation": {
|
|
494
509
|
"valid": len(failed) == 0 and bool(link_result.get("success", True)),
|
|
495
510
|
"input_validation": input_validation,
|
|
@@ -565,13 +580,11 @@ async def call_tool(name: str, arguments: dict) -> list[types.TextContent]:
|
|
|
565
580
|
else:
|
|
566
581
|
result = {"error": f"Unknown tool: {name}"}
|
|
567
582
|
|
|
568
|
-
return
|
|
583
|
+
return track(result, tool_name=name, server="qa-test-case-manager", t0=t0)
|
|
569
584
|
|
|
570
585
|
except Exception as exc:
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
text=json.dumps({"error": str(exc), "tool": name}, indent=2),
|
|
574
|
-
)]
|
|
586
|
+
err_result = {"error": str(exc), "tool": name}
|
|
587
|
+
return track(err_result, tool_name=name, server="qa-test-case-manager", t0=t0)
|
|
575
588
|
|
|
576
589
|
|
|
577
590
|
# ---------------------------------------------------------------------------
|
|
Binary file
|
package/src/stlc_agents/agent_test_case_manager/tools/__pycache__/ado_workitem.cpython-314.pyc
ADDED
|
Binary file
|
|
@@ -8,6 +8,8 @@ Public API:
|
|
|
8
8
|
create_test_case(org_url, project, title, steps, ...) -> dict
|
|
9
9
|
link_test_cases_to_work_item(org_url, project, wi_id, tc_ids) -> dict
|
|
10
10
|
get_linked_test_cases(org_url, project, work_item_id) -> dict
|
|
11
|
+
add_comment_to_work_item(org_url, project, work_item_id, text) -> dict
|
|
12
|
+
add_tag_to_work_item(org_url, project, work_item_id, tag) -> dict
|
|
11
13
|
"""
|
|
12
14
|
from __future__ import annotations
|
|
13
15
|
|
|
@@ -175,8 +177,13 @@ def link_test_cases_to_work_item(
|
|
|
175
177
|
project: str,
|
|
176
178
|
work_item_id: int,
|
|
177
179
|
test_case_ids: List[int],
|
|
180
|
+
link_comment: str = "STLC-Agent generated test case",
|
|
178
181
|
) -> dict:
|
|
179
|
-
"""Create TestedBy-Forward links from a work item to test cases.
|
|
182
|
+
"""Create TestedBy-Forward links from a work item to test cases.
|
|
183
|
+
|
|
184
|
+
link_comment is stored as attributes.comment on each relation and appears
|
|
185
|
+
in the Links tab Comments column in Azure DevOps.
|
|
186
|
+
"""
|
|
180
187
|
org_url = org_url.rstrip("/")
|
|
181
188
|
headers = get_auth_headers("application/json-patch+json")
|
|
182
189
|
|
|
@@ -187,6 +194,7 @@ def link_test_cases_to_work_item(
|
|
|
187
194
|
"value": {
|
|
188
195
|
"rel": "Microsoft.VSTS.Common.TestedBy-Forward",
|
|
189
196
|
"url": f"{org_url}/{project}/_apis/wit/workItems/{tc_id}",
|
|
197
|
+
"attributes": {"comment": link_comment},
|
|
190
198
|
},
|
|
191
199
|
}
|
|
192
200
|
for tc_id in test_case_ids
|
|
@@ -249,6 +257,62 @@ def get_linked_test_cases(org_url: str, project: str, work_item_id: int) -> dict
|
|
|
249
257
|
return {"work_item_id": work_item_id, "linked_test_cases": linked, "count": len(linked)}
|
|
250
258
|
|
|
251
259
|
|
|
260
|
+
# ---------------------------------------------------------------------------
|
|
261
|
+
# add_comment_to_work_item
|
|
262
|
+
# ---------------------------------------------------------------------------
|
|
263
|
+
|
|
264
|
+
def add_comment_to_work_item(org_url: str, project: str, work_item_id: int, text: str) -> dict:
|
|
265
|
+
"""Add a comment to a work item via the ADO comments API."""
|
|
266
|
+
org_url = org_url.rstrip("/")
|
|
267
|
+
headers = get_auth_headers()
|
|
268
|
+
|
|
269
|
+
resp = requests.post(
|
|
270
|
+
f"{org_url}/{project}/_apis/wit/workitems/{work_item_id}/comments",
|
|
271
|
+
headers=headers,
|
|
272
|
+
params={"api-version": "7.1-preview.3"},
|
|
273
|
+
json={"text": text},
|
|
274
|
+
timeout=30,
|
|
275
|
+
)
|
|
276
|
+
resp.raise_for_status()
|
|
277
|
+
return {"success": True, "comment_id": resp.json().get("id")}
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
# ---------------------------------------------------------------------------
|
|
281
|
+
# add_tag_to_work_item
|
|
282
|
+
# ---------------------------------------------------------------------------
|
|
283
|
+
|
|
284
|
+
def add_tag_to_work_item(org_url: str, project: str, work_item_id: int, tag: str) -> dict:
|
|
285
|
+
"""Append a tag to a work item's System.Tags field (no-op if already present)."""
|
|
286
|
+
org_url = org_url.rstrip("/")
|
|
287
|
+
|
|
288
|
+
fetch_resp = requests.get(
|
|
289
|
+
f"{org_url}/{project}/_apis/wit/workitems/{work_item_id}",
|
|
290
|
+
headers=get_auth_headers(),
|
|
291
|
+
params={"api-version": _API},
|
|
292
|
+
timeout=30,
|
|
293
|
+
)
|
|
294
|
+
fetch_resp.raise_for_status()
|
|
295
|
+
existing_str = fetch_resp.json().get("fields", {}).get("System.Tags", "") or ""
|
|
296
|
+
existing = [t.strip() for t in existing_str.split(";") if t.strip()]
|
|
297
|
+
|
|
298
|
+
if tag in existing:
|
|
299
|
+
return {"success": True, "tag": tag, "already_present": True}
|
|
300
|
+
|
|
301
|
+
existing.append(tag)
|
|
302
|
+
new_tags_str = "; ".join(existing)
|
|
303
|
+
|
|
304
|
+
patch = [{"op": "add", "path": "/fields/System.Tags", "value": new_tags_str}]
|
|
305
|
+
patch_resp = requests.patch(
|
|
306
|
+
f"{org_url}/{project}/_apis/wit/workitems/{work_item_id}",
|
|
307
|
+
headers=get_auth_headers("application/json-patch+json"),
|
|
308
|
+
params={"api-version": _API},
|
|
309
|
+
json=patch,
|
|
310
|
+
timeout=30,
|
|
311
|
+
)
|
|
312
|
+
patch_resp.raise_for_status()
|
|
313
|
+
return {"success": True, "tag": tag, "tags": new_tags_str}
|
|
314
|
+
|
|
315
|
+
|
|
252
316
|
# ---------------------------------------------------------------------------
|
|
253
317
|
# Helpers
|
|
254
318
|
# ---------------------------------------------------------------------------
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|