Two things here. Build a GitHub Copilot skill from scratch that turns a Jira story into a PDF + XLSX test plan, and download the full 36-skill QA Skill Suite covering every phase of the STLC. Both free, both copy-paste ready.
You have probably seen the n8n version: a chat trigger feeds an AI Agent, a chat model does the thinking, and a Jira tool acts. We are going to rebuild that pattern as a portable agent skill that GitHub Copilot (or any skill-aware agent, per agentskills.io) can pick up on its own, then have it produce shareable PDF and XLSX test plans.
A skill is a folder with a SKILL.md file plus optional scripts. The SKILL.md has a short front-matter (name + description) and a body of instructions. When your request matches the description, the agent loads the instructions and follows them, running the bundled scripts as needed. It is the cleanest way to teach GitHub Copilot a repeatable, multi-step workflow like "turn this Jira story into a test plan."
Here is the whole pipeline. Compare it to the n8n graph: the "AI Agent" is Copilot, the "Chat Model" is Copilot's model, and instead of a Jira create-issue tool we use a Jira fetch step plus two export steps.
The skill is just three files in a folder. Copilot reads SKILL.md, and the two scripts do the Jira call and the file export.
~/.config/github-copilot/skills/ for a global skill): mkdir -p test-plan-from-jira/scripts.SKILL.md with the front-matter and steps (full text below). The description is what makes Copilot pick the skill automatically.scripts/. Install deps once: pip install requests reportlab openpyxl.JIRA_BASE_URL, JIRA_EMAIL, JIRA_API_TOKEN. Create the token at id.atlassian.com → Security → API tokens.SKILL.md or any tracked file. The script reads it from os.environ.SKILL.mdThis is the whole brain of the skill. Copy it verbatim into test-plan-from-jira/SKILL.md.
--- name: test-plan-from-jira description: Fetch a Jira story or epic by key and generate a complete QA test plan (scope, scenarios, positive / negative / edge cases with steps and expected results), then export it to a polished PDF and a filable XLSX. Use whenever the user says "create a test plan from <JIRA-KEY>", "write test cases for this story", "turn this ticket into a test plan", or asks to export a test plan to PDF / Excel. --- # Test Plan from Jira Turn one Jira issue into a review-ready QA test plan and shareable files. ## Steps 1. Get the Jira key(s) from the user (e.g. `QAJB-42`). If none given, ask for one. 2. Fetch the story: `python scripts/fetch_jira.py <KEY> > story.json` This returns summary, description, acceptance criteria, type, priority, labels. 3. Read `story.json` and design the test plan. Cover, at minimum: - **Scope + assumptions** (what is / is not tested) - **Test scenarios** grouped by feature area - For each scenario: `id`, `title`, `type` (positive / negative / edge / boundary), `priority` (P1..P3), `preconditions`, numbered `steps`, `expected_result` - Derive negative + boundary cases from every acceptance criterion, do not just restate the happy path. 4. Write the plan as JSON to `plan.json` (schema below). 5. Export both files: `python scripts/export.py plan.json` -> writes `test-plan-<KEY>.pdf` and `test-plan-<KEY>.xlsx`. 6. Tell the user the file paths and give a 3-line summary (scope + case count by type). ## plan.json schema ```json { "key": "QAJB-42", "title": "Coupon code at checkout", "scope": "Applying, validating, and removing coupon codes at checkout.", "out_of_scope": ["Payment gateway", "Inventory"], "cases": [ { "id": "TC-01", "title": "Valid coupon applies discount", "type": "positive", "priority": "P1", "preconditions": "Cart has 1 eligible item; coupon SAVE10 is active", "steps": ["Open checkout", "Enter SAVE10", "Click Apply"], "expected_result": "10% discount shown; total updates; success toast" } ] } ``` ## Rules - Never hard-code the Jira token. `fetch_jira.py` reads `JIRA_*` env vars. - Every acceptance criterion must map to at least one positive AND one negative case. - Keep steps atomic and imperative; expected results must be verifiable. - If the story is thin, list the assumptions you made at the top of the plan.
Pulls the issue over the Jira Cloud REST API (v3) and prints a clean JSON the model can reason over. Auth is Basic (email + API token) over HTTPS.
# Fetch one Jira issue and print normalized JSON. Usage: python fetch_jira.py QAJB-42 import os, sys, json, base64, urllib.request def adf_to_text(node): # Atlassian Document Format -> plain text (recursive, best-effort). if node is None: return "" if isinstance(node, str): return node out = [] if node.get("text"): out.append(node["text"]) for ch in node.get("content", []) or []: out.append(adf_to_text(ch)) sep = "\n" if node.get("type") in ("paragraph", "listItem", "heading") else "" return sep.join(p for p in out if p) def main(): key = sys.argv[1] base = os.environ["JIRA_BASE_URL"].rstrip("/") # https://your.atlassian.net email = os.environ["JIRA_EMAIL"] token = os.environ["JIRA_API_TOKEN"] auth = base64.b64encode(f"{email}:{token}".encode()).decode() url = f"{base}/rest/api/3/issue/{key}" \ "?fields=summary,description,issuetype,priority,labels,customfield_10000" req = urllib.request.Request(url, headers={ "Authorization": f"Basic {auth}", "Accept": "application/json"}) with urllib.request.urlopen(req) as r: data = json.load(r) f = data["fields"] story = { "key": data["key"], "summary": f.get("summary", ""), "type": (f.get("issuetype") or {}).get("name"), "priority": (f.get("priority") or {}).get("name"), "labels": f.get("labels", []), "description": adf_to_text(f.get("description")), } print(json.dumps(story, indent=2)) if __name__ == "__main__": main()
customfield_10000) or inside the description. Adjust the field id to your Jira; find it via /rest/api/3/field.Takes the model's plan.json and writes both a formatted PDF (reportlab) and a spreadsheet (openpyxl) with one row per test case, ready to paste into a test-management tool.
# Render plan.json to test-plan-<KEY>.pdf and .xlsx. Usage: python export.py plan.json import sys, json from reportlab.lib import colors from reportlab.lib.pagesizes import A4 from reportlab.lib.styles import getSampleStyleSheet from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle from openpyxl import Workbook from openpyxl.styles import Font, PatternFill, Alignment plan = json.load(open(sys.argv[1])) key = plan["key"]; cases = plan.get("cases", []) # ---------- XLSX ---------- wb = Workbook(); ws = wb.active; ws.title = "Test Cases" cols = ["ID","Title","Type","Priority","Preconditions","Steps","Expected Result"] ws.append(cols) for i, c in enumerate(ws[1], 1): c.font = Font(bold=True, color="FFFFFF"); c.fill = PatternFill("solid", fgColor="7C3AED") for t in cases: ws.append([t.get("id"), t.get("title"), t.get("type"), t.get("priority"), t.get("preconditions",""), "\n".join(t.get("steps",[])), t.get("expected_result","")]) for col, w in zip("ABCDEFG", [10,34,12,10,30,44,40]): ws.column_dimensions[col].width = w for row in ws.iter_rows(min_row=2): for cell in row: cell.alignment = Alignment(wrap_text=True, vertical="top") wb.save(f"test-plan-{key}.xlsx") # ---------- PDF ---------- doc = SimpleDocTemplate(f"test-plan-{key}.pdf", pagesize=A4, title=f"Test Plan {key}") st = getSampleStyleSheet(); flow = [] flow += [Paragraph(f"Test Plan — {key}", st["Title"]), Paragraph(plan.get("title",""), st["Heading2"]), Paragraph("<b>Scope:</b> " + plan.get("scope",""), st["Normal"]), Spacer(1,12)] head = ["ID","Title","Type","Pri","Steps","Expected"] rows = [head] + [[t.get("id"), Paragraph(t.get("title",""), st["BodyText"]), t.get("type"), t.get("priority"), Paragraph("<br/>".join(t.get("steps",[])), st["BodyText"]), Paragraph(t.get("expected_result",""), st["BodyText"])] for t in cases] tbl = Table(rows, colWidths=[40,110,48,32,150,140], repeatRows=1) tbl.setStyle(TableStyle([ ("BACKGROUND",(0,0),(-1,0),colors.HexColor("#7C3AED")), ("TEXTCOLOR",(0,0),(-1,0),colors.white), ("FONTSIZE",(0,0),(-1,-1),8), ("VALIGN",(0,0),(-1,-1),"TOP"), ("GRID",(0,0),(-1,-1),0.5,colors.HexColor("#d0d5dd")), ("ROWBACKGROUNDS",(0,1),(-1,-1),[colors.white, colors.HexColor("#f6f4fe")])])) flow.append(tbl) doc.build(flow) print(f"wrote test-plan-{key}.pdf and test-plan-{key}.xlsx ({len(cases)} cases)")
With the folder in place and env vars set, just ask in Copilot Chat:
create a test plan from QAJB-42
Copilot matches the skill by its description, runs fetch_jira.py QAJB-42, drafts the plan against every acceptance criterion, writes plan.json, runs export.py, and hands you test-plan-QAJB-42.pdf + test-plan-QAJB-42.xlsx. Follow-ups work too: "add security and performance cases", "make it a regression suite", "also do QAJB-43".
fetch_jira.py + export.py are the tools. n8n is great for always-on automation; a Copilot skill is great for the developer-in-the-loop moment. Build both and pick per job.Building one skill (the tab on the left) is the pattern. Here is the payoff: a complete, free QA Skill Suite of 36 agent skills covering every phase of the Software Testing Life Cycle, plus two automation framework packs. Drop any folder into ~/.claude/skills/ for Claude Code, or convert it to a .prompt.md for GitHub Copilot.
Three of these skills have a full build-it-yourself guide: the prompt that generates them, the real scripts, and a working CLI table plus a shareable HTML report. Start here if you want to watch one run before grabbing the whole set. New to the ideas themselves? The two concept explainers cover what a skill even is, and what turns an LLM into an agent.
Turn one Jira story into a full QA test plan (scope, scenarios, positive / negative / edge cases) and export it to a polished PDF and a filable XLSX.
Run your Playwright or Selenium suite several times, score every test with a real flakiness formula, and get a CLI table plus an HTML report that tells flaky apart from broken.
Triage a bug end to end: severity P0-P4 with a justification, root cause traced down the stack, duplicate leads, the tests to prove the fix, and an HTML triage report.
When should a prompt graduate into a skill file, and a skill into an agent? One comparison table, the ladder diagram, and the same Jira test-plan job done four ways.
An LLM is a brain in a jar. Add memory and tools and it becomes an agent. The next-token diagram, the six limitations, the formula, and why MCP matters.
Get n8n running on Windows and Mac (Node or Docker, full commands), then wire a Jira test-plan generator in four nodes: Chat Trigger, Jira, AI Agent, reply.
Install LangFlow via pip/uv or Docker, then rebuild the same Jira test-plan generator as five visual components with a paste-ready prompt template.
The folders are numbered in testing order, so the skills hand off the way real work flows: analyze the ticket, plan, design scenarios, write cases and data, execute (with a framework pack), manage defects, then close out and feed the next cycle.
One or a few focused skills per phase. Each card links the raw SKILL.md you can read or drop straight into your agent.
jira-requirement-analyzerRead a JIRA ticket and judge whether it is actually ready to test.
test-plan-generatorTurn a JIRA ticket into a review-ready test plan.
api-test-designerDesign API-level test coverage from an endpoint or contract.
test-scenario-designerDerive high-level test scenarios from a requirement or acceptance criteria before anyone writes detailed steps.
test-case-writerExpand approved test scenarios into detailed, executable test cases.
test-data-generatorProduce the data sets a scenario or test case needs to run.
automation-script-generatorTurn an approved test case into a runnable automation script skeleton.
regression-suite-selectorPick a targeted regression subset for a change instead of running the whole suite.
test-execution-trackerLog and summarize the execution of a test cycle.
bug-reporterTurn a failure into a clean, reproducible bug report.
bug-triage-assistantTriage a batch of defects instead of grinding through them one by one.
rca-analyzerRun a structured root-cause analysis on a defect.
test-closure-reporterProduce the closure report that wraps up a test cycle.
test-coverage-analyzerFind the gaps between what was required and what was actually tested.
Cross-cutting automation skills for the two stacks most QA teams run. These plug into phase 5 (execution) but are useful any time you are writing or fixing tests.
pw-accessibility-auditorIntegrates automated accessibility checks into Playwright tests using axe-core.
pw-api-testerDesigns and generates API tests using Playwright's request context.
pw-ci-configuratorGenerates CI configuration (GitHub Actions) for a Playwright suite.
pw-fixture-designerDesigns custom Playwright test fixtures (auth/session, seeded data, page objects) with correct setup/teardown and scope.
pw-flaky-debuggerDiagnoses a flaky Playwright test and proposes web-first fixes.
pw-locator-fixerScans a Playwright spec or Page Object for brittle locators and rewrites them to resilient ones.
pw-network-mockerDesigns Playwright route interception and mocking.
pw-page-object-builderBuilds a Playwright Page Object Model class from a page or URL.
pw-test-generatorGenerates a Playwright (TypeScript) test spec from a described user flow or scenario.
pw-trace-analyzerAnalyzes a Playwright trace.zip or test failure to pinpoint the root cause.
pw-visual-regressionSets up Playwright visual/screenshot regression testing.
se-cross-browser-runnerSets up cross-browser Selenium execution across Chrome, Firefox, and Edge via TestNG parameters or a factory, plus a browser matrix.
se-data-driven-designerDesigns data-driven Selenium tests with TestNG @DataProvider (or Excel/CSV/JSON sources), covering valid, invalid, and boundary data sets.
se-driver-managerSets up browser driver management with Selenium Manager (built-in) or WebDriverManager, browser options, and a thread-safe driver factory.
se-flaky-debuggerDiagnoses flaky Selenium tests - StaleElementReferenceException, timing and synchronization races, dynamic/late-rendering elements - and proposes robust fixes.
se-framework-scaffolderScaffolds a Maven Selenium framework - TestNG suite, base test, Page Object package, config loader, logging, and reporting hooks.
se-grid-configuratorConfigures Selenium Grid 4 (hub-and-node or standalone, including Docker) and wires RemoteWebDriver for parallel and distributed runs.
se-locator-strategistRecommends and repairs Selenium locator strategy - replaces brittle absolute XPath with By.id, By.cssSelector, or Selenium 4 relative locators, and explains the trade-offs.
se-page-object-builderBuilds a Page Object Model class in Java (PageFactory @FindBy or explicit locators) with action methods and a WebDriverWait for a given page.
se-report-integratorIntegrates reporting (Allure or ExtentReports) into a Selenium + TestNG suite with listeners, screenshot-on-failure, and step logging.
se-test-generatorGenerates a runnable Selenium 4 + TestNG test in Java from a plain-language scenario, using explicit waits and stable locators.
se-wait-fixerScans Selenium Java code for Thread.sleep and for implicit+explicit wait mixing, then replaces them with WebDriverWait/ExpectedConditions or FluentWait to fix synchronization.
Grab the zip above (or a single folder), then:
# one skill cp -r ./02-test-planning/test-plan-generator ~/.claude/skills/ # a whole phase cp -r ./05-test-execution/* ~/.claude/skills/ # a framework pack cp -r ./framework-packs/playwright-pack/* ~/.claude/skills/
Then just ask your agent in plain English, for example "analyze QAJB-42 for testability" or "write a Playwright test for the login flow". The agent matches the skill by its description and follows it.