Claude Skill

bug-reporting

Use this skill when you need to write clear, reproducible bug reports with steps, environment details, and evidence; triggers include bug reporting and defect reporting.

LLM Mart · 0 points · 0 views 0 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download naodeng-awesome-qa-skills-skills_en_testing-types_bug-reporting-c44b892.zip · 20 KB
Part of naodeng/awesome-qa-skills — 97 skills

Install

skills CLI npx skills add https://github.com/naodeng/awesome-qa-skills/tree/main/skills/en/testing-types/bug-reporting
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install naodeng-awesome-qa-skills@llmmart
Git git clone https://github.com/naodeng/awesome-qa-skills.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole naodeng/awesome-qa-skills collection as a plugin from our marketplace. Git is the plain clone.

README

Bug Reporting

Skill Overview

Need help with bug reporting in a real project context; Need an output that can be used directly for execution, review, or follow-up.

How to Use

  1. Open SKILL.md in this folder and confirm this skill fits your task.
  2. In your AI tool, call @skill bug-reporting, then add your real project context and goal.
  3. If you need a specific output format (table, checklist, report), include it directly in your request.

One-Click Install Script

Run from the repository root:

macOS / Linux

bash ./scripts/install-skills-mac.sh --tool codex --lang en --skill bug-reporting

Windows PowerShell

powershell -ExecutionPolicy Bypass -File .\scripts\install-skills-windows.ps1 -Tool codex -Lang en -Skill bug-reporting

Skill manifest

Bug Reporting

Chinese version: See the corresponding Chinese skill.

When to Use

  • Need help with bug reporting in a real project context.
  • Need an output that can be used directly for execution, review, or follow-up.

Workflow

  1. Read and follow the main prompt listed under Progressive disclosure (coverage, structure, quality bar).
  2. Add only project context that changes the result: scope, environment, constraints, risks, dependencies, expected deliverable.
  3. If input is incomplete, return a usable first draft and explicitly mark assumptions and gaps.
  4. Default to Markdown; switch formats only when the user asks.

Core Constraints

  • Prioritize by risk / business impact — do not treat everything equally.
  • Separate confirmed facts from current assumptions.
  • Do not invent endpoints, fields, environments, or root causes the user did not provide.
  • Keep output executable: concrete scenarios, clear priority, clear next steps.

Progressive Disclosure

  • Before producing output, read and follow prompts/bug-reporting.md (minimum coverage, output structure, quality bar).
  • When Excel/CSV/JSON/Word is requested: read output-formats.md and honor the format.
  • When a ready-made template fits: use matching files under output-templates/.
  • For deep framework/troubleshoot/schema notes: read only the relevant file(s) under references/, do not load the whole directory.
  • For format conversion or helper checks: prefer existing scripts/ over reinventing.
  • For evaluating/regressing this skill: use evals/ with skill-up.

Pre-delivery Checklist

  • Followed the main prompt's output structure
  • Minimum coverage focus: title, environment, preconditions, repro steps, actual result, expected result, repro frequency, impact scope, ... (details in main prompt)
  • Covered the minimum checklist, or explained omissions
  • High-risk items have explicit priority
  • Did not invent details the user did not provide
  • Assumptions and gaps are marked

Common Pitfalls

  • Do not pretend completeness when scope/context is missing.
  • Do not treat every item as equally important.
  • Do not skip assumptions and information gaps.
  • Do not dump generic theory unrelated to the current toolchain.
Files (awesome-qa-skills)
  • agents
    • openai.yaml 442 B
      version: 1
      metadata:
        key: "bug-reporting"
      interface:
        display_name: "Bug Reporting"
        short_description: "Use this skill when you need to write clear, reproducible bug reports with steps, environment details, and evidence; triggers include bug reporting and defect…"
        default_prompt: "Use the bug-reporting skill to complete this testing task with structured outputs and practical next steps."
      policy:
        allow_implicit_invocation: true
      
  • evals
    • cases
      • basic-success.yaml 650 B
        id: basic-success
        title: "Bug reporting: reproducible defect report"
        description: |
          With symptoms and env, write steps, actual/expected, severity rationale.
        
        input:
          prompt: |
            Use bug-reporting.
            Symptom: on SIT, Chrome buyer pays successfully but order stays Pending Payment.
            Account: buyer_sit_01. Happens about 1 of 3 times.
            Write a reproducible bug report; separate facts from guesses; justify severity.
        
        expect:
          must_contain:
            - "Steps"
            - "Expected"
          must_not_contain:
            - "TODO"
            - "I cannot"
        
        judge:
          type: rule_based
          success:
            - output_contains:
                all:
                  - "Actual"
                  - "Severity"
        
      • edge-incomplete-input.yaml 598 B
        id: edge-incomplete-input
        title: "Bug reporting: mark uncertainty when evidence is thin"
        description: |
          Incomplete info still gets a structured draft with open questions.
        
        input:
          prompt: |
            Use bug-reporting. User says: points redemption sometimes fails. No screenshot, logs, or environment.
            Write a draft bug report and explicitly list what must be confirmed.
        
        expect:
          must_contain:
            - "confirm"
            - "Steps"
          must_not_contain:
            - "TODO"
            - "I cannot"
        
        judge:
          type: rule_based
          success:
            - output_contains:
                all:
                  - "confirm"
                  - "assumption"
        
      • edge-risk-priority.yaml 893 B
        id: "edge-risk-priority"
        title: "Bug reporting: severity needs business impact"
        description: |
          Severity must be justified; use agent_judge for semantic severity differentiation.
        
        input:
          prompt: |
            Use bug-reporting.
            Issue A: admin report export is ~20s slower but still succeeds.
            Issue B: payment succeeds but double-charges (intermittent).
            Give two short bug summaries and explain why severity differs.
        
        expect:
          must_contain:
            - "severity"
            - "payment"
          must_not_contain:
            - "TODO"
            - "I cannot"
        
        judge:
          type: agent_judge
          model: openai/gpt-5
          criteria:
            - "Covers both the slow-export issue and the double-charge issue as separate items"
            - "Double-charge is ranked clearly higher severity than slow export, with business-impact rationale"
            - "Separates facts from guesses; does not invent logs or accounts not provided"
          pass_threshold: 0.67
        
    • eval.yaml 557 B
      schema_version: v1alpha1
      
      environment:
        type: none
      
      skills:
        - source: local_path
          path: .
      
      engine:
        name: claude_code
        # model is optional; omit to use engine default
        # model:
        #   provider: anthropic
        #   name: claude-sonnet-4-6
      
      cases:
        files:
          - evals/cases/basic-success.yaml
          - evals/cases/edge-incomplete-input.yaml
          - evals/cases/edge-risk-priority.yaml
        defaults:
          timeout_seconds: 180
          max_turns: 8
          expect:
            exit_code: 0
            must_not_contain:
              - "TODO"
              - "I cannot"
      
      report:
        formats: [json]
      
  • output-templates
    • template-csv.csv 206 B · in bundle
    • template-excel.tsv 358 B · in bundle
    • template-json.json 393 B
      {
        "meta": {
          "skill": "",
          "scope": "",
          "environment": "",
          "priority": ""
        },
        "inputs": {
          "requirement": "",
          "constraints": [],
          "risks": []
        },
        "execution": [
          { "step": 1, "action": "", "expected": "" }
        ],
        "results": {
          "status": "",
          "evidence": [],
          "defects": []
        },
        "next_actions": [
          { "owner": "", "eta": "", "action": "" }
        ]
      }
      
    • template-markdown.md 250 B
      # QA Output Template
      
      ## Summary
      - Skill:
      - Scope:
      - Environment:
      - Priority:
      
      ## Inputs
      - Requirement:
      - Constraints:
      - Risks:
      
      ## Execution
      1. Step 1
      2. Step 2
      3. Step 3
      
      ## Results
      - Status:
      - Evidence:
      - Defects:
      
      ## Next Actions
      - Owner:
      - ETA:
      
    • template-word.md 302 B
      QA Report
      =========
      
      1. Basic Information
      - Skill:
      - Scope:
      - Environment:
      - Priority:
      
      2. Requirement and Constraints
      - Requirement:
      - Constraints:
      - Risks:
      
      3. Test/Review Process
      - Step 1:
      - Step 2:
      - Step 3:
      
      4. Outcome
      - Status:
      - Evidence:
      - Defects:
      
      5. Follow-up Plan
      - Owner:
      - ETA:
      - Action:
      
    • template-xmind.md 317 B
      # QA Output Mindmap
      
      - QA Output
        - Meta
          - Skill
          - Scope
          - Environment
          - Priority
        - Inputs
          - Requirement
          - Constraints
          - Risks
        - Execution
          - Step 1
          - Step 2
          - Step 3
        - Results
          - Status
          - Evidence
          - Defects
        - Next Actions
          - Owner
          - ETA
          - Action
      
  • prompts
    • bug-reporting.md 1.5 KB
      # Bug Reporting Prompt
      
      Produce a clear, actionable bug report that helps others understand, reproduce, and prioritize the issue quickly.
      
      ## Role
      
      - Act as a senior QA expert who writes bug reports that are precise, reproducible, and easy to act on.
      
      
      ## Input
      
      - bug description, screenshots, videos, logs, or repro notes
      - environment details, app version, device or browser, and account context
      - observed behavior, expected behavior, impact, and frequency
      
      ## What to do
      
      1. Clarify what actually happened, where it happened, and how reliably it reproduces.
      2. Focus on evidence, impact, and next action instead of storytelling.
      3. Produce a report that developers, PMs, and testers can use immediately.
      
      ## Execution Rules
      
      - Separate observed facts from guesses about root cause.
      - If reproduction is incomplete, state the uncertainty clearly.
      - Assess severity and priority using business and user impact, not emotion.
      
      ## Minimum Coverage Checklist
      
      Unless the user explicitly narrows the scope, make sure the result addresses these items:
      - title
      - environment
      - preconditions
      - repro steps
      - actual result
      - expected result
      - repro frequency
      - impact scope
      - severity and priority
      - evidence
      - open questions or missing information
      
      ## Output
      
      Return the result in this order:
      
      ### 1. Bug Summary
      ### 2. Environment and Preconditions
      ### 3. Reproduction Steps
      ### 4. Actual vs Expected
      ### 5. Impact Assessment
      ### 6. Evidence and Notes
      
      ## Quality Bar
      
      - Keep steps reproducible and concise.
      - Do not invent root cause.
      - Do not leave severity unsupported.
      
  • references
    • local
      • FAQ.md 529 B
        # Archived Local Reference
        
        This file is a lightweight legacy note. The previous long snapshot was removed to avoid duplicate and outdated guidance.
        
        ## Use Instead
        
        - Main prompt: `prompts/bug-reporting.md`
        - Main entry: `SKILL.md`
        - Output format guide: `output-formats.md`
        
        ## Notes
        
        - Keep using the current prompt and `SKILL.md` as the source of truth.
        - Load `references/`, `examples/`, or `scripts/` only when the task really needs extra detail.
        - Do not rely on this file for the latest prompt wording or workflow rules.
        
      • Users-nao.deng-awsomeCode-awesome-qa-skills-skills-testing-types-bug-reporting-examples-bug-report-templates.md 591 B
        # Local Example Collection Reference
        
        This file points to an example collection that used to be stored as a larger snapshot.
        
        ## What It Is
        
        - A local note for example-related materials.
        - Useful only when you need extra example context beyond the main prompt.
        
        ## Prefer These Files
        
        - `SKILL.md`
        - `prompts/bug-reporting.md`
        - `references/`
        
        ## Notes
        
        - This file is now a short local reference instead of a full snapshot copy.
        - Use it only when you need extra background beyond the main prompt and `SKILL.md`.
        - If the current prompt conflicts with this file, follow the current prompt.
        
      • Users-nao.deng-awsomeCode-awesome-qa-skills-skills-testing-types-bug-reporting-examples.md 591 B
        # Local Example Collection Reference
        
        This file points to an example collection that used to be stored as a larger snapshot.
        
        ## What It Is
        
        - A local note for example-related materials.
        - Useful only when you need extra example context beyond the main prompt.
        
        ## Prefer These Files
        
        - `SKILL.md`
        - `prompts/bug-reporting.md`
        - `references/`
        
        ## Notes
        
        - This file is now a short local reference instead of a full snapshot copy.
        - Use it only when you need extra background beyond the main prompt and `SKILL.md`.
        - If the current prompt conflicts with this file, follow the current prompt.
        
  • scripts
    • batch_convert_templates.py 2.7 KB
      #!/usr/bin/env python3
      import argparse
      import subprocess
      import sys
      from pathlib import Path
      
      
      def detect_from(file: Path) -> str:
          ext = file.suffix.lower()
          if file.name.endswith('.word.md'):
              return 'markdown'
          return {
              '.md': 'markdown',
              '.markdown': 'markdown',
              '.json': 'json',
              '.csv': 'csv',
              '.tsv': 'excel',
              '.docx': 'word',
              '.xlsx': 'excel',
              '.xmind': 'xmind',
          }.get(ext, 'markdown')
      
      
      def run_convert(convert_script: Path, src: Path, to_fmt: str, out: Path) -> int:
          cmd = [sys.executable, str(convert_script), str(src), '--from', detect_from(src), '--to', to_fmt, '--output', str(out)]
          return subprocess.call(cmd)
      
      
      def main() -> None:
          parser = argparse.ArgumentParser(description='Batch convert all template files into target formats.')
          parser.add_argument('--templates-dir', type=Path, default=Path('output-templates'))
          parser.add_argument('--artifacts-dir', type=Path, default=Path('artifacts'))
          parser.add_argument('--targets', default='word,excel,xmind,json,csv,markdown', help='comma-separated target formats')
          parser.add_argument('--skip-same', action='store_true', help='skip conversion when source format equals target format')
          args = parser.parse_args()
      
          cwd = Path.cwd()
          templates_dir = (cwd / args.templates_dir).resolve()
          artifacts_dir = (cwd / args.artifacts_dir).resolve()
          artifacts_dir.mkdir(parents=True, exist_ok=True)
      
          local_convert = (Path(__file__).resolve().parent / 'convert_formats.py').resolve()
          targets = [t.strip() for t in args.targets.split(',') if t.strip()]
      
          if not templates_dir.exists():
              raise SystemExit(f'templates directory not found: {templates_dir}')
      
          files = [p for p in sorted(templates_dir.iterdir()) if p.is_file()]
          total = 0
          failed = 0
      
          for src in files:
              src_fmt = detect_from(src)
              for to_fmt in targets:
                  if args.skip_same and src_fmt == to_fmt:
                      continue
                  out_ext = {
                      'json': '.json',
                      'csv': '.csv',
                      'excel': '.tsv',
                      'markdown': '.md',
                      'word': '.word.md',
                      'xmind': '.xmind.md',
                  }[to_fmt]
                  out = artifacts_dir / f"{src.stem}.to-{to_fmt}{out_ext}"
                  total += 1
                  rc = run_convert(local_convert, src, to_fmt, out)
                  if rc != 0:
                      failed += 1
                      print(f'[FAILED] {src.name} -> {to_fmt}')
                  else:
                      print(f'[OK] {src.name} -> {out.name}')
      
          print(f'\nDone. total={total}, failed={failed}, artifacts={artifacts_dir}')
          if failed:
              raise SystemExit(1)
      
      
      if __name__ == '__main__':
          main()
      
    • convert_formats.py 10.4 KB
      #!/usr/bin/env python3
      import argparse
      import csv
      import json
      import re
      import zipfile
      from pathlib import Path
      from typing import Any
      from xml.etree import ElementTree as ET
      
      
      # ---- parsing ----
      def parse_markdown(path: Path) -> dict[str, Any]:
          text = path.read_text(encoding="utf-8", errors="ignore")
          lines = text.splitlines()
          headings: list[dict[str, Any]] = []
          for line in lines:
              m = re.match(r"^(#{1,6})\s+(.*)$", line.strip())
              if m:
                  headings.append({"level": len(m.group(1)), "title": m.group(2).strip()})
          return {"title": headings[0]["title"] if headings else path.stem, "headings": headings, "text": text}
      
      
      def parse_json(path: Path) -> dict[str, Any]:
          data = json.loads(path.read_text(encoding="utf-8", errors="ignore"))
          return {"title": path.stem, "data": data}
      
      
      def parse_csv_file(path: Path) -> dict[str, Any]:
          with path.open("r", encoding="utf-8", errors="ignore", newline="") as f:
              reader = csv.DictReader(f)
              rows = list(reader)
          return {"title": path.stem, "columns": reader.fieldnames or [], "rows": rows}
      
      
      def parse_docx(path: Path) -> dict[str, Any]:
          paragraphs: list[str] = []
          with zipfile.ZipFile(path) as zf:
              with zf.open("word/document.xml") as f:
                  root = ET.fromstring(f.read())
          ns = {"w": "http://schemas.openxmlformats.org/wordprocessingml/2006/main"}
          for p in root.findall(".//w:p", ns):
              texts = [t.text for t in p.findall(".//w:t", ns) if t.text]
              s = "".join(texts).strip()
              if s:
                  paragraphs.append(s)
          return {"title": path.stem, "paragraphs": paragraphs}
      
      
      def _shared_strings(zf: zipfile.ZipFile) -> list[str]:
          out: list[str] = []
          try:
              with zf.open("xl/sharedStrings.xml") as f:
                  root = ET.fromstring(f.read())
              ns = {"a": "http://schemas.openxmlformats.org/spreadsheetml/2006/main"}
              for si in root.findall(".//a:si", ns):
                  out.append("".join((t.text or "") for t in si.findall(".//a:t", ns)))
          except KeyError:
              pass
          return out
      
      
      def parse_xlsx(path: Path) -> dict[str, Any]:
          rows: list[list[str]] = []
          with zipfile.ZipFile(path) as zf:
              shared = _shared_strings(zf)
              with zf.open("xl/worksheets/sheet1.xml") as f:
                  root = ET.fromstring(f.read())
          ns = {"a": "http://schemas.openxmlformats.org/spreadsheetml/2006/main"}
          for row in root.findall(".//a:sheetData/a:row", ns):
              vals = []
              for c in row.findall("a:c", ns):
                  t = c.attrib.get("t")
                  v = c.find("a:v", ns)
                  if v is None or v.text is None:
                      vals.append("")
                  elif t == "s":
                      idx = int(v.text)
                      vals.append(shared[idx] if 0 <= idx < len(shared) else "")
                  else:
                      vals.append(v.text)
              rows.append(vals)
          return {"title": path.stem, "rows": rows}
      
      
      def parse_xmind(path: Path) -> dict[str, Any]:
          with zipfile.ZipFile(path) as zf:
              names = set(zf.namelist())
              topics: list[str] = []
              if "content.json" in names:
                  data = json.loads(zf.read("content.json").decode("utf-8", errors="ignore"))
      
                  def walk(node: Any):
                      if isinstance(node, dict):
                          t = node.get("title")
                          if isinstance(t, str) and t.strip():
                              topics.append(t.strip())
                          for k in ("children", "topics", "rootTopic", "attached"):
                              walk(node.get(k))
                      elif isinstance(node, list):
                          for i in node:
                              walk(i)
      
                  walk(data)
              elif "content.xml" in names:
                  root = ET.fromstring(zf.read("content.xml"))
                  topics = [e.text.strip() for e in root.findall(".//title") if e.text and e.text.strip()]
              else:
                  raise ValueError("Unsupported XMind package structure")
          return {"title": topics[0] if topics else path.stem, "topics": topics}
      
      
      def detect_in_format(path: Path, forced: str | None) -> str:
          if forced and forced != "auto":
              return forced
          return {
              ".md": "markdown",
              ".markdown": "markdown",
              ".json": "json",
              ".csv": "csv",
              ".docx": "word",
              ".xlsx": "excel",
              ".xmind": "xmind",
              ".tsv": "excel",
          }.get(path.suffix.lower(), "markdown")
      
      
      def normalize(parsed: dict[str, Any]) -> dict[str, Any]:
          title = parsed.get("title") or "QA Output"
          sections: list[dict[str, Any]] = []
      
          if "text" in parsed:
              sections.append({"name": "content", "items": [{"key": "text", "value": parsed["text"]}]})
          if "headings" in parsed:
              sections.append({"name": "headings", "items": [{"key": "heading", "value": h.get("title", "")} for h in parsed["headings"]]})
          if "data" in parsed:
              data = parsed["data"]
              if isinstance(data, dict):
                  items = [{"key": k, "value": v} for k, v in list(data.items())[:100]]
                  sections.append({"name": "json_object", "items": items})
              elif isinstance(data, list):
                  sections.append({"name": "json_array", "items": [{"key": "row", "value": v} for v in data[:200]]})
              else:
                  sections.append({"name": "json_value", "items": [{"key": "value", "value": data}]})
          if "rows" in parsed:
              rows = parsed["rows"]
              sections.append({"name": "rows", "items": [{"key": f"row_{i+1}", "value": r} for i, r in enumerate(rows[:200])]})
          if "columns" in parsed:
              sections.append({"name": "columns", "items": [{"key": "column", "value": c} for c in parsed["columns"]]})
          if "paragraphs" in parsed:
              sections.append({"name": "paragraphs", "items": [{"key": f"p{i+1}", "value": p} for i, p in enumerate(parsed["paragraphs"][:200])]})
          if "topics" in parsed:
              sections.append({"name": "topics", "items": [{"key": "topic", "value": t} for t in parsed["topics"][:300]]})
      
          return {"title": title, "sections": sections}
      
      
      # ---- writers ----
      def write_json(model: dict[str, Any], output: Path) -> None:
          output.write_text(json.dumps(model, ensure_ascii=False, indent=2), encoding="utf-8")
      
      
      def _scalar(v: Any) -> str:
          if isinstance(v, (dict, list)):
              return json.dumps(v, ensure_ascii=False)
          return str(v)
      
      
      def write_csv(model: dict[str, Any], output: Path) -> None:
          with output.open("w", encoding="utf-8", newline="") as f:
              writer = csv.writer(f)
              writer.writerow(["section", "key", "value"])
              for s in model.get("sections", []):
                  for item in s.get("items", []):
                      writer.writerow([s.get("name", ""), item.get("key", ""), _scalar(item.get("value", ""))])
      
      
      def write_excel_tsv(model: dict[str, Any], output: Path) -> None:
          lines = ["Section\tKey\tValue"]
          for s in model.get("sections", []):
              for item in s.get("items", []):
                  lines.append(f"{s.get('name','')}\t{item.get('key','')}\t{_scalar(item.get('value','')).replace(chr(9), ' ')}")
          output.write_text("\n".join(lines) + "\n", encoding="utf-8")
      
      
      def write_markdown(model: dict[str, Any], output: Path) -> None:
          lines = [f"# {model.get('title', 'QA Output')}", ""]
          for s in model.get("sections", []):
              lines.append(f"## {s.get('name', 'section')}")
              for item in s.get("items", []):
                  lines.append(f"- **{item.get('key','key')}**: {_scalar(item.get('value',''))}")
              lines.append("")
          output.write_text("\n".join(lines).rstrip() + "\n", encoding="utf-8")
      
      
      def write_word_md(model: dict[str, Any], output: Path) -> None:
          lines = [model.get("title", "QA Output"), "=" * len(model.get("title", "QA Output")), ""]
          idx = 1
          for s in model.get("sections", []):
              lines.append(f"{idx}. {s.get('name', 'section').replace('_', ' ').title()}")
              for item in s.get("items", []):
                  lines.append(f"- {item.get('key','key')}: {_scalar(item.get('value',''))}")
              lines.append("")
              idx += 1
          output.write_text("\n".join(lines).rstrip() + "\n", encoding="utf-8")
      
      
      def write_xmind_md(model: dict[str, Any], output: Path) -> None:
          lines = [f"# {model.get('title', 'QA Output')}", "", f"- {model.get('title', 'QA Output')}"]
          for s in model.get("sections", []):
              lines.append(f"  - {s.get('name', 'section')}")
              for item in s.get("items", []):
                  lines.append(f"    - {item.get('key','key')}: {_scalar(item.get('value',''))}")
          output.write_text("\n".join(lines).rstrip() + "\n", encoding="utf-8")
      
      
      def default_output(input_path: Path, to_fmt: str) -> Path:
          ext = {
              "json": ".json",
              "csv": ".csv",
              "excel": ".tsv",
              "markdown": ".md",
              "word": ".word.md",
              "xmind": ".xmind.md",
          }[to_fmt]
          return input_path.with_name(input_path.stem + ".converted" + ext)
      
      
      def main() -> None:
          parser = argparse.ArgumentParser(description="Convert QA output files between common formats")
          parser.add_argument("input", type=Path, help="Input file path")
          parser.add_argument("--from", dest="from_fmt", default="auto", choices=["auto", "word", "excel", "xmind", "json", "csv", "markdown"])
          parser.add_argument("--to", required=True, choices=["word", "excel", "xmind", "json", "csv", "markdown"])
          parser.add_argument("--output", type=Path, help="Output file path")
          args = parser.parse_args()
      
          in_fmt = detect_in_format(args.input, args.from_fmt)
          if in_fmt == "word":
              parsed = parse_docx(args.input)
          elif in_fmt == "excel":
              if args.input.suffix.lower() == ".tsv":
                  rows = [line.rstrip("\n").split("\t") for line in args.input.read_text(encoding="utf-8", errors="ignore").splitlines() if line]
                  parsed = {"title": args.input.stem, "rows": rows}
              else:
                  parsed = parse_xlsx(args.input)
          elif in_fmt == "xmind":
              parsed = parse_xmind(args.input)
          elif in_fmt == "json":
              parsed = parse_json(args.input)
          elif in_fmt == "csv":
              parsed = parse_csv_file(args.input)
          else:
              parsed = parse_markdown(args.input)
      
          model = normalize(parsed)
          output = args.output or default_output(args.input, args.to)
      
          if args.to == "json":
              write_json(model, output)
          elif args.to == "csv":
              write_csv(model, output)
          elif args.to == "excel":
              write_excel_tsv(model, output)
          elif args.to == "markdown":
              write_markdown(model, output)
          elif args.to == "word":
              write_word_md(model, output)
          else:
              write_xmind_md(model, output)
      
          print(str(output))
      
      
      if __name__ == "__main__":
          main()
      
    • convert_output_formats.py 255 B
      #!/usr/bin/env python3
      import os
      import subprocess
      import sys
      LOCAL = os.path.normpath(os.path.join(os.path.dirname(__file__), 'convert_formats.py'))
      if __name__ == '__main__':
          raise SystemExit(subprocess.call([sys.executable, LOCAL] + sys.argv[1:]))
      
    • convert_to_csv.py 270 B
      #!/usr/bin/env python3
      import os
      import subprocess
      import sys
      LOCAL = os.path.normpath(os.path.join(os.path.dirname(__file__), 'convert_formats.py'))
      if __name__ == '__main__':
          raise SystemExit(subprocess.call([sys.executable, LOCAL, '--to', 'csv'] + sys.argv[1:]))
      
    • convert_to_excel.py 272 B
      #!/usr/bin/env python3
      import os
      import subprocess
      import sys
      LOCAL = os.path.normpath(os.path.join(os.path.dirname(__file__), 'convert_formats.py'))
      if __name__ == '__main__':
          raise SystemExit(subprocess.call([sys.executable, LOCAL, '--to', 'excel'] + sys.argv[1:]))
      
    • convert_to_json.py 271 B
      #!/usr/bin/env python3
      import os
      import subprocess
      import sys
      LOCAL = os.path.normpath(os.path.join(os.path.dirname(__file__), 'convert_formats.py'))
      if __name__ == '__main__':
          raise SystemExit(subprocess.call([sys.executable, LOCAL, '--to', 'json'] + sys.argv[1:]))
      
    • convert_to_markdown.py 275 B
      #!/usr/bin/env python3
      import os
      import subprocess
      import sys
      LOCAL = os.path.normpath(os.path.join(os.path.dirname(__file__), 'convert_formats.py'))
      if __name__ == '__main__':
          raise SystemExit(subprocess.call([sys.executable, LOCAL, '--to', 'markdown'] + sys.argv[1:]))
      
    • convert_to_word.py 271 B
      #!/usr/bin/env python3
      import os
      import subprocess
      import sys
      LOCAL = os.path.normpath(os.path.join(os.path.dirname(__file__), 'convert_formats.py'))
      if __name__ == '__main__':
          raise SystemExit(subprocess.call([sys.executable, LOCAL, '--to', 'word'] + sys.argv[1:]))
      
    • convert_to_xmind.py 272 B
      #!/usr/bin/env python3
      import os
      import subprocess
      import sys
      LOCAL = os.path.normpath(os.path.join(os.path.dirname(__file__), 'convert_formats.py'))
      if __name__ == '__main__':
          raise SystemExit(subprocess.call([sys.executable, LOCAL, '--to', 'xmind'] + sys.argv[1:]))
      
    • parse_csv.py 272 B
      #!/usr/bin/env python3
      import os
      import subprocess
      import sys
      LOCAL = os.path.normpath(os.path.join(os.path.dirname(__file__), 'parse_formats.py'))
      if __name__ == '__main__':
          raise SystemExit(subprocess.call([sys.executable, LOCAL, '--format', 'csv'] + sys.argv[1:]))
      
    • parse_excel.py 274 B
      #!/usr/bin/env python3
      import os
      import subprocess
      import sys
      LOCAL = os.path.normpath(os.path.join(os.path.dirname(__file__), 'parse_formats.py'))
      if __name__ == '__main__':
          raise SystemExit(subprocess.call([sys.executable, LOCAL, '--format', 'excel'] + sys.argv[1:]))
      
    • parse_formats.py 5.9 KB
      #!/usr/bin/env python3
      import argparse
      import csv
      import json
      import re
      import zipfile
      from pathlib import Path
      from typing import Any
      from xml.etree import ElementTree as ET
      
      
      def parse_markdown(path: Path) -> dict[str, Any]:
          text = path.read_text(encoding="utf-8", errors="ignore")
          headings = []
          for line in text.splitlines():
              m = re.match(r"^(#{1,6})\s+(.*)$", line.strip())
              if m:
                  headings.append({"level": len(m.group(1)), "title": m.group(2).strip()})
          return {"format": "markdown", "headings": headings, "preview": text[:500]}
      
      
      def parse_json(path: Path) -> dict[str, Any]:
          data = json.loads(path.read_text(encoding="utf-8", errors="ignore"))
          if isinstance(data, dict):
              shape = {"type": "object", "keys": list(data.keys())[:50]}
          elif isinstance(data, list):
              shape = {"type": "array", "size": len(data)}
          else:
              shape = {"type": type(data).__name__}
          return {"format": "json", "shape": shape, "data": data}
      
      
      def parse_csv_file(path: Path) -> dict[str, Any]:
          with path.open("r", encoding="utf-8", errors="ignore", newline="") as f:
              reader = csv.DictReader(f)
              rows = list(reader)
          return {
              "format": "csv",
              "columns": reader.fieldnames or [],
              "row_count": len(rows),
              "sample_rows": rows[:10],
          }
      
      
      def parse_docx(path: Path) -> dict[str, Any]:
          paragraphs = []
          with zipfile.ZipFile(path) as zf:
              with zf.open("word/document.xml") as f:
                  root = ET.fromstring(f.read())
          ns = {"w": "http://schemas.openxmlformats.org/wordprocessingml/2006/main"}
          for p in root.findall(".//w:p", ns):
              texts = [t.text for t in p.findall(".//w:t", ns) if t.text]
              joined = "".join(texts).strip()
              if joined:
                  paragraphs.append(joined)
          return {"format": "word", "paragraph_count": len(paragraphs), "paragraphs": paragraphs[:100]}
      
      
      def _read_shared_strings(zf: zipfile.ZipFile) -> list[str]:
          strings = []
          try:
              with zf.open("xl/sharedStrings.xml") as f:
                  root = ET.fromstring(f.read())
              ns = {"a": "http://schemas.openxmlformats.org/spreadsheetml/2006/main"}
              for si in root.findall(".//a:si", ns):
                  parts = [t.text or "" for t in si.findall(".//a:t", ns)]
                  strings.append("".join(parts))
          except KeyError:
              pass
          return strings
      
      
      def parse_xlsx(path: Path) -> dict[str, Any]:
          rows_out: list[list[str]] = []
          with zipfile.ZipFile(path) as zf:
              shared = _read_shared_strings(zf)
              with zf.open("xl/worksheets/sheet1.xml") as f:
                  root = ET.fromstring(f.read())
          ns = {"a": "http://schemas.openxmlformats.org/spreadsheetml/2006/main"}
          for row in root.findall(".//a:sheetData/a:row", ns):
              vals = []
              for c in row.findall("a:c", ns):
                  cell_type = c.attrib.get("t")
                  v = c.find("a:v", ns)
                  if v is None or v.text is None:
                      vals.append("")
                      continue
                  if cell_type == "s":
                      idx = int(v.text)
                      vals.append(shared[idx] if 0 <= idx < len(shared) else "")
                  else:
                      vals.append(v.text)
              rows_out.append(vals)
          return {"format": "excel", "row_count": len(rows_out), "sample_rows": rows_out[:20]}
      
      
      def parse_xmind(path: Path) -> dict[str, Any]:
          with zipfile.ZipFile(path) as zf:
              names = set(zf.namelist())
              if "content.json" in names:
                  data = json.loads(zf.read("content.json").decode("utf-8", errors="ignore"))
                  titles: list[str] = []
      
                  def walk(node: Any):
                      if isinstance(node, dict):
                          title = node.get("title")
                          if isinstance(title, str) and title.strip():
                              titles.append(title.strip())
                          for k in ("children", "topics", "rootTopic", "attached"):
                              walk(node.get(k))
                      elif isinstance(node, list):
                          for i in node:
                              walk(i)
      
                  walk(data)
                  return {"format": "xmind", "topic_count": len(titles), "topics": titles[:200]}
              if "content.xml" in names:
                  root = ET.fromstring(zf.read("content.xml"))
                  titles = [el.text.strip() for el in root.findall(".//title") if el.text and el.text.strip()]
                  return {"format": "xmind", "topic_count": len(titles), "topics": titles[:200]}
          raise ValueError("Unsupported XMind package structure")
      
      
      def detect_format(path: Path, forced: str | None) -> str:
          if forced and forced != "auto":
              return forced
          ext = path.suffix.lower()
          return {
              ".md": "markdown",
              ".markdown": "markdown",
              ".json": "json",
              ".csv": "csv",
              ".docx": "word",
              ".xlsx": "excel",
              ".xmind": "xmind",
          }.get(ext, "markdown")
      
      
      def main() -> None:
          parser = argparse.ArgumentParser(description="Parse common QA output formats into normalized JSON")
          parser.add_argument("input", type=Path, help="Input file path")
          parser.add_argument("--format", default="auto", choices=["auto", "word", "excel", "xmind", "json", "csv", "markdown"])
          parser.add_argument("--output", type=Path, help="Output JSON path (default: stdout)")
          args = parser.parse_args()
      
          fmt = detect_format(args.input, args.format)
          if fmt == "word":
              result = parse_docx(args.input)
          elif fmt == "excel":
              result = parse_xlsx(args.input)
          elif fmt == "xmind":
              result = parse_xmind(args.input)
          elif fmt == "json":
              result = parse_json(args.input)
          elif fmt == "csv":
              result = parse_csv_file(args.input)
          else:
              result = parse_markdown(args.input)
      
          result["source"] = str(args.input)
          out = json.dumps(result, ensure_ascii=False, indent=2)
          if args.output:
              args.output.write_text(out, encoding="utf-8")
          else:
              print(out)
      
      
      if __name__ == "__main__":
          main()
      
    • parse_json.py 273 B
      #!/usr/bin/env python3
      import os
      import subprocess
      import sys
      LOCAL = os.path.normpath(os.path.join(os.path.dirname(__file__), 'parse_formats.py'))
      if __name__ == '__main__':
          raise SystemExit(subprocess.call([sys.executable, LOCAL, '--format', 'json'] + sys.argv[1:]))
      
    • parse_markdown.py 277 B
      #!/usr/bin/env python3
      import os
      import subprocess
      import sys
      LOCAL = os.path.normpath(os.path.join(os.path.dirname(__file__), 'parse_formats.py'))
      if __name__ == '__main__':
          raise SystemExit(subprocess.call([sys.executable, LOCAL, '--format', 'markdown'] + sys.argv[1:]))
      
    • parse_output_formats.py 253 B
      #!/usr/bin/env python3
      import os
      import subprocess
      import sys
      LOCAL = os.path.normpath(os.path.join(os.path.dirname(__file__), 'parse_formats.py'))
      if __name__ == '__main__':
          raise SystemExit(subprocess.call([sys.executable, LOCAL] + sys.argv[1:]))
      
    • parse_word.py 273 B
      #!/usr/bin/env python3
      import os
      import subprocess
      import sys
      LOCAL = os.path.normpath(os.path.join(os.path.dirname(__file__), 'parse_formats.py'))
      if __name__ == '__main__':
          raise SystemExit(subprocess.call([sys.executable, LOCAL, '--format', 'word'] + sys.argv[1:]))
      
    • parse_xmind.py 274 B
      #!/usr/bin/env python3
      import os
      import subprocess
      import sys
      LOCAL = os.path.normpath(os.path.join(os.path.dirname(__file__), 'parse_formats.py'))
      if __name__ == '__main__':
          raise SystemExit(subprocess.call([sys.executable, LOCAL, '--format', 'xmind'] + sys.argv[1:]))
      
  • output-formats.md 401 B
    # Output Format Options
    
    This skill **defaults to Markdown**. To get **Excel**, **CSV**, or **JSON**, add at the **end** of your request:
    
    - **Excel:** "Please output as tab-separated table for pasting into Excel."
    - **CSV:** "Please output as CSV (comma-separated, header row first)."
    - **JSON:** "Please output as JSON."
    
    See repo `skills/testing-types/_output-formats-template-en.md` for examples.
    
  • quick-start.md 2.1 KB
    # Bug Reporting: Quick Start
    
    Use the `bug-reporting` Skill to create reproducible, evidence-based defect reports that support triage and fixing.
    
    ## 1. Prepare the Minimum Context
    
    Provide what is available from this list:
    
    - observed behavior
    - expected behavior
    - environment
    - build
    - reproduction data
    - evidence
    - scope, version, environment, time budget, and prohibited actions
    - known failures, existing tests, and evidence links
    
    If some information is unavailable, say so explicitly. The Skill should still return a bounded first pass with assumptions and open questions.
    
    ## 2. Invoke the Skill
    
    ```text
    @skill bug-reporting
    
    Scenario: Report duplicate order creation during network retry with trace evidence.
    Prioritize the highest risks, distinguish facts from assumptions, and provide an executable result with evidence requirements and open questions.
    ```
    
    ## 3. Follow the Execution Flow
    
    1. Confirm the objective, subject, scope, and success criteria.
    2. Audit the completeness and credibility of the supplied material.
    3. Identify failure modes and rank them by impact, likelihood, and detectability.
    4. Produce concrete scenarios, checks, or decisions with expected results.
    5. Record evidence gaps, residual risk, and the smallest useful next action.
    
    ## 4. Minimum Coverage
    
    Unless the request narrows scope, cover:
    
    - clear title
    - reproducible steps
    - impact
    - severity
    - evidence
    - scope
    - workaround
    - privacy
    - confirmed facts, working assumptions, and open questions
    - P0/P1 priorities and the rationale for lower-priority deferral
    - blockers, stop conditions, and residual risk
    
    ## 5. Review the Result
    
    - Every high-risk item has a concrete failure mode and expected behavior.
    - Priorities are justified rather than evenly distributed.
    - No field, API, metric, environment, or execution result is invented.
    - The artifact can be executed or reviewed without guessing the next step.
    - Production, security, and privacy work uses least privilege and sanitized data.
    
    ## 6. Continue
    
    Add missing evidence and ask the Skill to refine the same artifact. Keep confirmed facts separate from newly introduced assumptions so changes remain reviewable.
    
  • README.md 795 B
    # Bug Reporting
    
    ## Skill Overview
    
    Need help with bug reporting in a real project context; Need an output that can be used directly for execution, review, or follow-up.
    
    ## How to Use
    
    1. Open `SKILL.md` in this folder and confirm this skill fits your task.
    2. In your AI tool, call `@skill bug-reporting`, then add your real project context and goal.
    3. If you need a specific output format (table, checklist, report), include it directly in your request.
    
    ## One-Click Install Script
    
    Run from the repository root:
    
    ### macOS / Linux
    
    ```bash
    bash ./scripts/install-skills-mac.sh --tool codex --lang en --skill bug-reporting
    ```
    
    ### Windows PowerShell
    
    ```powershell
    powershell -ExecutionPolicy Bypass -File .\scripts\install-skills-windows.ps1 -Tool codex -Lang en -Skill bug-reporting
    ```
    
  • SKILL.md 2.4 KB
    ---
    name: bug-reporting
    description: Use this skill when you need to write clear, reproducible bug reports with steps, environment details, and evidence; triggers include bug reporting and defect reporting.
    ---
    
    # Bug Reporting
    
    **Chinese version:** See the corresponding Chinese skill.
    
    ## When to Use
    
    - Need help with bug reporting in a real project context.
    - Need an output that can be used directly for execution, review, or follow-up.
    
    ## Workflow
    
    1. Read and follow the main prompt listed under Progressive disclosure (coverage, structure, quality bar).
    2. Add only project context that changes the result: scope, environment, constraints, risks, dependencies, expected deliverable.
    3. If input is incomplete, return a usable first draft and explicitly mark assumptions and gaps.
    4. Default to Markdown; switch formats only when the user asks.
    
    ## Core Constraints
    
    - Prioritize by risk / business impact — do not treat everything equally.
    - Separate confirmed facts from current assumptions.
    - Do not invent endpoints, fields, environments, or root causes the user did not provide.
    - Keep output executable: concrete scenarios, clear priority, clear next steps.
    
    ## Progressive Disclosure
    
    - Before producing output, read and follow `prompts/bug-reporting.md` (minimum coverage, output structure, quality bar).
    - When Excel/CSV/JSON/Word is requested: read `output-formats.md` and honor the format.
    - When a ready-made template fits: use matching files under `output-templates/`.
    - For deep framework/troubleshoot/schema notes: read only the relevant file(s) under `references/`, do not load the whole directory.
    - For format conversion or helper checks: prefer existing `scripts/` over reinventing.
    - For evaluating/regressing this skill: use `evals/` with skill-up.
    
    ## Pre-delivery Checklist
    
    - [ ] Followed the main prompt's output structure
    - [ ] Minimum coverage focus: title, environment, preconditions, repro steps, actual result, expected result, repro frequency, impact scope, ... (details in main prompt)
    - [ ] Covered the minimum checklist, or explained omissions
    - [ ] High-risk items have explicit priority
    - [ ] Did not invent details the user did not provide
    - [ ] Assumptions and gaps are marked
    
    ## Common Pitfalls
    
    - Do not pretend completeness when scope/context is missing.
    - Do not treat every item as equally important.
    - Do not skip assumptions and information gaps.
    - Do not dump generic theory unrelated to the current toolchain.
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related