Agent skill

Pyats Dynamic Test

by automateyournetwork in automateyournetwork/netclaw

Generate and execute deterministic pyATS aetest validation scripts - interface state, OSPF neighbors, BGP paths, ping matrices, and custom compliance tests.

Apache-2.0Auto-check passedTesting & QA

Install Pyats Dynamic Test

skills CLI
$ npx skills add automateyournetwork/netclaw --skill pyats-dynamic-test -a claude-code

Project install by default; add -g for ~/.claude/skills/.

GitHub CLI
$ gh skill install automateyournetwork/netclaw pyats-dynamic-test --agent claude-code

Project scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).

Manual copy
$ git clone --depth 1 https://github.com/automateyournetwork/netclaw.git skills-src && mkdir -p .claude/skills && cp -r skills-src/workspace/skills/pyats-dynamic-test .claude/skills/pyats-dynamic-test && rm -rf skills-src

Use ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.

Claude Code skills documentation · loads skills from .claude/skills/

Facts

Skill name
pyats-dynamic-test
GitHub stars
676
Token cost
~4.7k tokens
SKILL.md length
610 words
Files
1
Skills in repo
120
Repo updated
First seen
Licence
Apache-2.0

At a glance

Generate and execute deterministic pyATS aetest validation scripts - interface state, OSPF neighbors, BGP paths, ping matrices, and custom compliance tests.

  • Works in 4 steps: Collect Data First → Build TEST_DATA from Collected Output → Write the Script Following the Template → …
  • Writing a network test
  • SKILL.md covers When to Use, How the Tool Works, Script Template Structure and Example 1: Interface State…, plus 6 more sections
  • Calls python3

What it does

Pyats Dynamic Test is an agent skill from automateyournetwork/netclaw. Generate and execute deterministic pyATS aetest validation scripts - interface state, OSPF neighbors, BGP paths, ping matrices, and custom compliance tests. Use when writing a network test, validating post-change state, running pass/fail checks, or building automated regression tests. For Arista EOS devices, prefer anta-validation's pre-built 208-test catalog over writing a custom script here. For simply running the resulting test (not authoring it), pyats-network can also invoke it.

Its SKILL.md is about 4.7k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.

It sits in Testing & QA. It works with Python. The repository describes itself as: An AI agent that claws through your network. The licence is Apache-2.0.

When your agent uses it

  • Writing a network test
  • Validating post-change state
  • Running pass/fail checks
  • Building automated regression tests

Example prompts

  • “/pyats-dynamic-test”

Requirements

  • Python 3

Workflow steps

4 steps, taken from the step headings in SKILL.md.

  1. Collect Data First
  2. Build TEST_DATA from Collected Output
  3. Write the Script Following the Template
  4. Serialize and Invoke

What it can do on your machine

Read from SKILL.md and the folder at commit 95bb17e. It shows what the files ask for, not the result of running them.

  • Tool permissions

    Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.

    From allowed-tools in the SKILL.md frontmatter.

  • Runs code

    Shell commands in SKILL.md call:

    • python3

    From the folder's file list and the shell code blocks in SKILL.md.

  • Network

    No URLs in SKILL.md.

    From URLs in SKILL.md, links to its own repository left out.

  • Credentials

    Names no API keys, tokens, secrets or passwords.

    From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.

Context cost

Pyats Dynamic Test loads about 4.7k tokens when it runs. Until then it costs about 128 tokens; SKILL.md has 610 words of instructions outside code blocks.

Always · name and description, kept in context so the agent knows when to use it
~128
When it runs · the whole SKILL.md, loaded when a task matches
~4.7k

Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.

Safety

Auto-check passed

The automated check found no risky patterns in SKILL.md.

Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.

SKILL.md

The full file from automateyournetwork/netclaw at commit 95bb17e, republished under its Apache-2.0 licence (© automateyournetwork). 610 words, ~4,733 tokens.

Download SKILL.mdSave it as .claude/skills/pyats-dynamic-test/SKILL.md (or your agent's skills folder).
name
pyats-dynamic-test
description
Generate and execute deterministic pyATS aetest validation scripts - interface state, OSPF neighbors, BGP paths, ping matrices, and custom compliance tests. Use when writing a network test, validating post-change state, running pass/fail checks, or building automated regression tests. For Arista EOS devices, prefer `anta-validation`'s pre-built 208-test catalog over writing a custom script here. For simply running the resulting test (not authoring it), `pyats-network` can also invoke it.
license
Apache-2.0
user-invocable
true

Dynamic pyATS Test Execution

When to Use

  • Automated pass/fail validation after configuration changes
  • Compliance checks that require multi-step assertions
  • Regression testing before and after maintenance windows
  • Data-driven validation across multiple interfaces, neighbors, or routes
  • Any scenario where you need a formal test report with PASSED/FAILED verdicts

How the Tool Works

The pyats_run_dynamic_test tool accepts a complete Python aetest script as a string. The script is executed in a sandboxed environment with a 300-second timeout.

bash
PYATS_TESTBED_PATH=$PYATS_TESTBED_PATH python3 $MCP_CALL "${PYATS_PYTHON:-python3} -u $PYATS_MCP_SCRIPT" pyats_run_dynamic_test '{"test_script":"<FULL PYTHON SOURCE CODE>"}'

The test_script parameter takes the entire Python source code as a single JSON string. Newlines are encoded as \n, quotes as \".

Script Template Structure

Every aetest script follows this structure:

1. Imports (logging, aetest)
2. TEST_DATA dict (all expected values as a Python literal)
3. CommonSetup class (connect to devices)
4. Testcase class(es) (test methods with assertions)
5. CommonCleanup class (disconnect)
6. if __name__ == "__main__": aetest.main()
Rules for TEST_DATA
  • Must be a Python dict literal defined at module level
  • Contains all expected values the test will validate against
  • Cannot be loaded from files, network, or environment variables
  • Keeps the test deterministic and self-documenting
Rules for the Script
  • Banned imports: os, sys, subprocess, shutil, socket, pathlib, pickle, yaml, requests, urllib, http, ssl
  • Banned functions: __import__(), eval(), exec(), compile(), open(), json.loads()
  • The script cannot connect to devices directly; embed all validation data inline in TEST_DATA
  • Use logger.info() for verbose output and self.failed() or assert for test verdicts

Example 1: Interface State Validation

Verify that all expected interfaces are in up/up state.

bash
PYATS_TESTBED_PATH=$PYATS_TESTBED_PATH python3 $MCP_CALL "${PYATS_PYTHON:-python3} -u $PYATS_MCP_SCRIPT" pyats_run_dynamic_test '{"test_script":"import logging\nfrom pyats import aetest\n\nlogger = logging.getLogger(__name__)\n\nTEST_DATA = {\n    \"device\": \"R1\",\n    \"expected_interfaces\": {\n        \"GigabitEthernet1\": {\"status\": \"up\", \"protocol\": \"up\"},\n        \"GigabitEthernet2\": {\"status\": \"up\", \"protocol\": \"up\"},\n        \"GigabitEthernet3\": {\"status\": \"up\", \"protocol\": \"up\"},\n        \"Loopback0\": {\"status\": \"up\", \"protocol\": \"up\"}\n    }\n}\n\nclass CommonSetup(aetest.CommonSetup):\n    @aetest.subsection\n    def connect_to_device(self, testbed):\n        device = testbed.devices[TEST_DATA[\"device\"]]\n        device.connect(learn_hostname=True, log_stdout=False)\n        self.parent.parameters[\"device\"] = device\n\nclass InterfaceStateValidation(aetest.Testcase):\n    @aetest.setup\n    def gather_interface_data(self):\n        device = self.parent.parameters[\"device\"]\n        self.parsed = device.parse(\"show ip interface brief\")\n        logger.info(\"Parsed interface data: %s\", self.parsed)\n\n    @aetest.test\n    def verify_interface_status(self):\n        interfaces = self.parsed.get(\"interface\", {})\n        failed_interfaces = []\n        for intf_name, expected in TEST_DATA[\"expected_interfaces\"].items():\n            if intf_name not in interfaces:\n                failed_interfaces.append(f\"{intf_name}: NOT FOUND in device output\")\n                continue\n            actual = interfaces[intf_name]\n            actual_status = actual.get(\"status\", \"unknown\")\n            actual_protocol = actual.get(\"protocol\", \"unknown\")\n            if actual_status != expected[\"status\"] or actual_protocol != expected[\"protocol\"]:\n                failed_interfaces.append(\n                    f\"{intf_name}: expected {expected[\"status\"]}/{expected[\"protocol\"]}, \"\n                    f\"got {actual_status}/{actual_protocol}\"\n                )\n            else:\n                logger.info(\"PASS: %s is %s/%s\", intf_name, actual_status, actual_protocol)\n        if failed_interfaces:\n            self.failed(\"Interface state failures:\\n\" + \"\\n\".join(failed_interfaces))\n        else:\n            self.passed(\"All interfaces in expected state\")\n\nclass CommonCleanup(aetest.CommonCleanup):\n    @aetest.subsection\n    def disconnect(self, testbed):\n        for device in testbed.devices.values():\n            if device.connected:\n                device.disconnect()\n\nif __name__ == \"__main__\":\n    aetest.main()"}'

What this tests:

  • Connects to R1 via the testbed
  • Parses show ip interface brief with Genie
  • Checks each expected interface exists and is up/up
  • Reports per-interface PASS/FAIL with details

Example 2: OSPF Neighbor Validation

Verify that expected OSPF neighbors are present and in FULL state.

bash
PYATS_TESTBED_PATH=$PYATS_TESTBED_PATH python3 $MCP_CALL "${PYATS_PYTHON:-python3} -u $PYATS_MCP_SCRIPT" pyats_run_dynamic_test '{"test_script":"import logging\nfrom pyats import aetest\n\nlogger = logging.getLogger(__name__)\n\nTEST_DATA = {\n    \"device\": \"R1\",\n    \"ospf_process\": \"1\",\n    \"expected_neighbors\": [\n        {\"neighbor_id\": \"2.2.2.2\", \"interface\": \"GigabitEthernet1\", \"state\": \"FULL\"},\n        {\"neighbor_id\": \"3.3.3.3\", \"interface\": \"GigabitEthernet2\", \"state\": \"FULL\"}\n    ]\n}\n\nclass CommonSetup(aetest.CommonSetup):\n    @aetest.subsection\n    def connect_to_device(self, testbed):\n        device = testbed.devices[TEST_DATA[\"device\"]]\n        device.connect(learn_hostname=True, log_stdout=False)\n        self.parent.parameters[\"device\"] = device\n\nclass OSPFNeighborValidation(aetest.Testcase):\n    @aetest.setup\n    def gather_ospf_data(self):\n        device = self.parent.parameters[\"device\"]\n        self.parsed = device.parse(\"show ip ospf neighbor\")\n        logger.info(\"OSPF neighbor data: %s\", self.parsed)\n\n    @aetest.test\n    def verify_neighbor_count(self):\n        interfaces = self.parsed.get(\"interfaces\", {})\n        total_neighbors = sum(\n            len(nbrs.get(\"neighbors\", {})) for nbrs in interfaces.values()\n        )\n        expected_count = len(TEST_DATA[\"expected_neighbors\"])\n        if total_neighbors < expected_count:\n            self.failed(\n                f\"Expected at least {expected_count} OSPF neighbors, found {total_neighbors}\"\n            )\n        else:\n            self.passed(f\"Found {total_neighbors} OSPF neighbors (expected {expected_count})\")\n\n    @aetest.test\n    def verify_each_neighbor(self):\n        interfaces = self.parsed.get(\"interfaces\", {})\n        failures = []\n        for expected in TEST_DATA[\"expected_neighbors\"]:\n            intf = expected[\"interface\"]\n            nbr_id = expected[\"neighbor_id\"]\n            if intf not in interfaces:\n                failures.append(f\"{intf}: interface not found in OSPF output\")\n                continue\n            neighbors = interfaces[intf].get(\"neighbors\", {})\n            if nbr_id not in neighbors:\n                failures.append(f\"{nbr_id} on {intf}: neighbor not found\")\n                continue\n            actual_state = neighbors[nbr_id].get(\"state\", \"unknown\")\n            if expected[\"state\"] not in actual_state.upper():\n                failures.append(\n                    f\"{nbr_id} on {intf}: expected {expected[\"state\"]}, got {actual_state}\"\n                )\n            else:\n                logger.info(\"PASS: %s on %s is %s\", nbr_id, intf, actual_state)\n        if failures:\n            self.failed(\"OSPF neighbor failures:\\n\" + \"\\n\".join(failures))\n        else:\n            self.passed(\"All expected OSPF neighbors verified\")\n\nclass CommonCleanup(aetest.CommonCleanup):\n    @aetest.subsection\n    def disconnect(self, testbed):\n        for device in testbed.devices.values():\n            if device.connected:\n                device.disconnect()\n\nif __name__ == \"__main__\":\n    aetest.main()"}'

What this tests:

  • Parses show ip ospf neighbor with Genie structured parser
  • Verifies total neighbor count meets minimum expected
  • Checks each expected neighbor by router ID, interface, and adjacency state
  • Distinguishes between missing neighbors and wrong-state neighbors

Example 3: BGP Path Validation

Verify that a specific route exists in the BGP table with the expected next-hop and AS path.

bash
PYATS_TESTBED_PATH=$PYATS_TESTBED_PATH python3 $MCP_CALL "${PYATS_PYTHON:-python3} -u $PYATS_MCP_SCRIPT" pyats_run_dynamic_test '{"test_script":"import logging\nfrom pyats import aetest\n\nlogger = logging.getLogger(__name__)\n\nTEST_DATA = {\n    \"device\": \"R1\",\n    \"expected_routes\": [\n        {\n            \"prefix\": \"10.0.0.0/8\",\n            \"next_hop\": \"10.1.1.2\",\n            \"as_path\": \"65002\",\n            \"origin\": \"IGP\"\n        },\n        {\n            \"prefix\": \"172.16.0.0/16\",\n            \"next_hop\": \"10.1.1.2\",\n            \"as_path\": \"65002 65003\",\n            \"origin\": \"IGP\"\n        }\n    ]\n}\n\nclass CommonSetup(aetest.CommonSetup):\n    @aetest.subsection\n    def connect_to_device(self, testbed):\n        device = testbed.devices[TEST_DATA[\"device\"]]\n        device.connect(learn_hostname=True, log_stdout=False)\n        self.parent.parameters[\"device\"] = device\n\nclass BGPPathValidation(aetest.Testcase):\n    @aetest.setup\n    def gather_bgp_data(self):\n        device = self.parent.parameters[\"device\"]\n        self.parsed = device.parse(\"show ip bgp\")\n        logger.info(\"BGP table parsed successfully\")\n\n    @aetest.test\n    def verify_bgp_routes(self):\n        vrf_default = self.parsed.get(\"vrf\", {}).get(\"default\", {})\n        address_family = vrf_default.get(\"address_family\", {}).get(\"ipv4 unicast\", {})\n        prefixes = address_family.get(\"prefixes\", {})\n        failures = []\n        for route in TEST_DATA[\"expected_routes\"]:\n            prefix = route[\"prefix\"]\n            if prefix not in prefixes:\n                failures.append(f\"{prefix}: NOT FOUND in BGP table\")\n                continue\n            paths = prefixes[prefix].get(\"index\", {})\n            found_match = False\n            for idx, path in paths.items():\n                nh = path.get(\"next_hop\", \"\")\n                if nh == route[\"next_hop\"]:\n                    found_match = True\n                    logger.info(\"PASS: %s via %s found in BGP table\", prefix, nh)\n                    break\n            if not found_match:\n                failures.append(\n                    f\"{prefix}: expected next-hop {route[\"next_hop\"]}, not found in any path\"\n                )\n        if failures:\n            self.failed(\"BGP path failures:\\n\" + \"\\n\".join(failures))\n        else:\n            self.passed(\"All expected BGP routes verified\")\n\nclass CommonCleanup(aetest.CommonCleanup):\n    @aetest.subsection\n    def disconnect(self, testbed):\n        for device in testbed.devices.values():\n            if device.connected:\n                device.disconnect()\n\nif __name__ == \"__main__\":\n    aetest.main()"}'

What this tests:

  • Parses the full BGP table with Genie
  • Navigates the VRF/address-family/prefix hierarchy
  • Checks each expected prefix exists with the correct next-hop
  • Reports missing prefixes and next-hop mismatches separately
Show full SKILL.md (269 more words)Show less

Example 4: Ping Reachability Matrix

Execute a ping matrix between device pairs and validate reachability.

bash
PYATS_TESTBED_PATH=$PYATS_TESTBED_PATH python3 $MCP_CALL "${PYATS_PYTHON:-python3} -u $PYATS_MCP_SCRIPT" pyats_run_dynamic_test '{"test_script":"import logging\nfrom pyats import aetest\n\nlogger = logging.getLogger(__name__)\n\nTEST_DATA = {\n    \"ping_matrix\": [\n        {\"device\": \"R1\", \"destination\": \"10.1.1.2\", \"source\": \"GigabitEthernet1\", \"min_success\": 80},\n        {\"device\": \"R1\", \"destination\": \"10.2.2.2\", \"source\": \"GigabitEthernet2\", \"min_success\": 80},\n        {\"device\": \"R1\", \"destination\": \"8.8.8.8\", \"source\": \"GigabitEthernet1\", \"min_success\": 100},\n        {\"device\": \"R1\", \"destination\": \"1.1.1.1\", \"source\": \"Loopback0\", \"min_success\": 100}\n    ]\n}\n\nclass CommonSetup(aetest.CommonSetup):\n    @aetest.subsection\n    def connect_all_devices(self, testbed):\n        devices = {}\n        for entry in TEST_DATA[\"ping_matrix\"]:\n            dev_name = entry[\"device\"]\n            if dev_name not in devices:\n                device = testbed.devices[dev_name]\n                device.connect(learn_hostname=True, log_stdout=False)\n                devices[dev_name] = device\n        self.parent.parameters[\"devices\"] = devices\n\nclass PingReachabilityMatrix(aetest.Testcase):\n    @aetest.test\n    def execute_ping_matrix(self):\n        devices = self.parent.parameters[\"devices\"]\n        results = []\n        failures = []\n        for entry in TEST_DATA[\"ping_matrix\"]:\n            device = devices[entry[\"device\"]]\n            dst = entry[\"destination\"]\n            src = entry.get(\"source\", \"\")\n            min_pct = entry[\"min_success\"]\n            try:\n                if src:\n                    ping_result = device.ping(dst, source=src, count=5, timeout=10)\n                else:\n                    ping_result = device.ping(dst, count=5, timeout=10)\n                logger.info(\n                    \"PASS: %s -> %s from %s: reachable\",\n                    entry[\"device\"], dst, src or \"default\"\n                )\n                results.append({\n                    \"device\": entry[\"device\"],\n                    \"destination\": dst,\n                    \"status\": \"PASS\"\n                })\n            except Exception as e:\n                msg = f\"{entry[\"device\"]} -> {dst} from {src or \"default\"}: FAILED ({str(e)[:80]})\"\n                logger.error(msg)\n                failures.append(msg)\n                results.append({\n                    \"device\": entry[\"device\"],\n                    \"destination\": dst,\n                    \"status\": \"FAIL\"\n                })\n        logger.info(\"\\n=== PING MATRIX RESULTS ===\")\n        for r in results:\n            logger.info(\"%s -> %s: %s\", r[\"device\"], r[\"destination\"], r[\"status\"])\n        if failures:\n            self.failed(\n                f\"{len(failures)}/{len(TEST_DATA[\"ping_matrix\"])} pings failed:\\n\"\n                + \"\\n\".join(failures)\n            )\n        else:\n            self.passed(f\"All {len(results)} pings successful\")\n\nclass CommonCleanup(aetest.CommonCleanup):\n    @aetest.subsection\n    def disconnect_all(self, testbed):\n        for device in testbed.devices.values():\n            if device.connected:\n                device.disconnect()\n\nif __name__ == \"__main__\":\n    aetest.main()"}'

What this tests:

  • Connects to all unique devices referenced in the ping matrix
  • Executes pings from each device to each destination with optional source interface
  • Collects pass/fail results into a summary matrix
  • Fails the test if any ping drops below the minimum success threshold

Writing Your Own Test Scripts

Step 1: Collect Data First

Before writing a dynamic test, collect the device data you need using the other pyATS tools:

bash
# Gather current state
PYATS_TESTBED_PATH=$PYATS_TESTBED_PATH python3 $MCP_CALL "${PYATS_PYTHON:-python3} -u $PYATS_MCP_SCRIPT" pyats_run_show_command '{"device_name":"R1","command":"show ip ospf neighbor"}'
Step 2: Build TEST_DATA from Collected Output

Use the collected output to populate the TEST_DATA dictionary with the expected values. This makes the test deterministic -- it validates that the network matches the known-good state.

Step 3: Write the Script Following the Template
python
import logging
from pyats import aetest

logger = logging.getLogger(__name__)

TEST_DATA = {
    # All expected values go here as Python literals
}

class CommonSetup(aetest.CommonSetup):
    @aetest.subsection
    def connect_to_device(self, testbed):
        device = testbed.devices["R1"]
        device.connect(learn_hostname=True, log_stdout=False)
        self.parent.parameters["device"] = device

class YourTestcase(aetest.Testcase):
    @aetest.setup
    def gather_data(self):
        device = self.parent.parameters["device"]
        self.parsed = device.parse("show COMMAND")

    @aetest.test
    def verify_something(self):
        # Compare self.parsed against TEST_DATA
        # Use self.passed(), self.failed(), or assert statements
        pass

class CommonCleanup(aetest.CommonCleanup):
    @aetest.subsection
    def disconnect(self, testbed):
        for device in testbed.devices.values():
            if device.connected:
                device.disconnect()

if __name__ == "__main__":
    aetest.main()
Step 4: Serialize and Invoke

Convert the script to a single-line JSON string (replace newlines with \n, escape quotes) and pass it as the test_script parameter.

Interpreting Results

The tool returns the aetest execution results:

  • Passed -- All assertions succeeded, the network state matches TEST_DATA
  • Failed -- One or more assertions did not match; the failure message explains what was wrong
  • Errored -- The script itself had a runtime error (syntax error, import violation, timeout)
  • Blocked -- CommonSetup failed (could not connect to device), so testcases were skipped

Integration with Other Skills

  • Use pyats-health-check to collect device state, then write dynamic tests to formalize pass/fail criteria for that state
  • Use pyats-config-mgmt Phase 4 (post-change verification) to trigger dynamic tests after configuration changes
  • Use pyats-routing data to build BGP/OSPF validation tests with real expected values
  • Use pyats-parallel-ops to run dynamic tests as part of fleet-wide validation sweeps

© automateyournetwork, Apache-2.0. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file

Files

Just SKILL.md in workspace/skills/pyats-dynamic-test of automateyournetwork/netclaw.

Open the folder on GitHubat commit 95bb17e

Compare with similar skills

Pyats Dynamic Test next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.

Pyats Dynamic Test compared with similar skills
SkillStarsUsed inTokensAuto-checkLicenceRepo updated
Pyats Dynamic Test this skillautomateyournetwork/netclaw676—~4.7kAutomated safety check: PassApache-2.0
Web Application Testinganthropics/skills180k51 repos~966Automated safety check: PassApache-2.0
OpenHarness End-to-End EvalsHKUDS/OpenHarness16k1 repos~2.1kAutomated safety check: NotesMIT
Adk Verify Snippetsgoogle/adk-python22k—~1.4kAutomated safety check: PassApache-2.0
Apple Container Test RunnerRustPython/RustPython22k—~467Automated safety check: PassMIT
JSON Repair Docs Demo Local Testmangiucugna/json_repair5.1k—~549Automated safety check: PassMIT

Similar skills

  • Web Application Testing

    anthropics/skills

    Official

    Tests local web applications with Python Playwright scripts, checking frontend behavior, capturing screenshots and reading browser console logs.

    180k GitHub starsUsed in 51 repos~966 tokens
    Testing & QAAuto-check passed
  • Validates OpenHarness features by running real multi-turn agent loops with live LLM calls against an unfamiliar codebase, checking actual tool execution.

    16k GitHub starsUsed in 1 repo~2.1k tokens
    Testing & QAAuto-check: notes
  • Adk Verify Snippets

    google/adk-python

    Official

    Checks that every Python code block in a Markdown file actually compiles and runs, by extracting each block to a temporary file, executing it in an isolated subprocess, and writing a pass/fail…

    22k GitHub stars~1.4k tokensUpdated yesterday
    Testing & QAAuto-check passed
  • Apple Container Test Runner

    RustPython/RustPython

    Runs RustPython tests inside a Linux container built with Apple's container CLI, so macOS users can compare Linux results with their local ones.

    22k GitHub stars~467 tokensUpdated yesterday
    Testing & QAAuto-check passed
  • JSON Repair Docs Demo Local Test

    mangiucugna/json_repair

    Runs the json_repair docs demo against a local Flask API and static server, so changes to docs/app.py or the docs UI are checked end to end before publishing.

    5.1k GitHub stars~549 tokensUpdated yesterday
    Testing & QAAuto-check passed
  • Official

    A skill your agent uses when a user asks to wobble ty constraint ordering, check constraint-set or TDD ordering determinism, test reversed constraint/typevar IDs, or investigate nondeterministic ty…

    50k GitHub stars~838 tokensUpdated today
    Testing & QAAuto-check passed

More from automateyournetwork/netclaw

All 120 skills in this repo
  • EVE-NG Lab Topology Design

    automateyournetwork/netclaw

    Entry point for designing EVE-NG network labs: classifies the request, gathers missing requirements, proposes options and validates the resulting topology.

    676 GitHub stars~612 tokensUpdated 4 days ago
    Auto-check passed
  • ACI Policy Change Deployment

    automateyournetwork/netclaw

    Deploys Cisco ACI policy changes only behind an approved ServiceNow Change Request, capturing pre and post-change fault baselines and rolling back automatically on a fault delta.

    676 GitHub stars~4.2k tokensUpdated 4 days ago
    Auto-check passed
  • Cisco ACI Fabric Health Audit

    automateyournetwork/netclaw

    Runs a phased health audit of a Cisco ACI fabric through MCP tools: node status, links, tenant and policy review, faults and endpoint learning.

    676 GitHub stars~2.9k tokensUpdated 4 days ago
    Auto-check passed
  • Anta Validation

    automateyournetwork/netclaw

    Validate Arista EOS network state against ANTA's pre-built 208-test catalogue, with structured pass/fail verdicts.

    676 GitHub stars~1.2k tokensUpdated 4 days ago
    Auto-check passed
  • Arista Cvp

    automateyournetwork/netclaw

    Arista CloudVision Portal (CVP) automation via REST API — device inventory, events, connectivity monitoring, tag management (4 tools).

    676 GitHub stars~2.2k tokensUpdated 4 days ago
    Auto-check: notes
  • AWS Cloud Monitoring

    automateyournetwork/netclaw

    AWS CloudWatch monitoring — metrics, alarms, log queries, VPC flow log analysis, network performance.

    676 GitHub stars~1k tokensUpdated 4 days ago
    Auto-check passed

Works with

Categories

Questions about Pyats Dynamic Test

What does Pyats Dynamic Test do?

Generate and execute deterministic pyATS aetest validation scripts - interface state, OSPF neighbors, BGP paths, ping matrices, and custom compliance tests. Pyats Dynamic Test is an agent skill from automateyournetwork/netclaw. Generate and execute deterministic pyATS aetest validation scripts - interface state, OSPF neighbors, BGP paths, ping matrices, and custom compliance tests.

When should I use Pyats Dynamic Test?

Pyats Dynamic Test fits situations like: writing a network test; validating post-change state; running pass/fail checks; building automated regression tests.

How do I install Pyats Dynamic Test in Claude Code?

Run `npx skills add automateyournetwork/netclaw --skill pyats-dynamic-test -a claude-code`. Or copy the skill folder (workspace/skills/pyats-dynamic-test in automateyournetwork/netclaw) into .claude/skills/pyats-dynamic-test in your project. Claude Code loads it when a task matches its description.

How do I install Pyats Dynamic Test in Codex?

Run `npx skills add automateyournetwork/netclaw --skill pyats-dynamic-test -a codex`. Or copy the skill folder (workspace/skills/pyats-dynamic-test in automateyournetwork/netclaw) into .agents/skills/pyats-dynamic-test in your project. Codex loads it when a task matches its description.

Can I use Pyats Dynamic Test in Cursor, Gemini CLI or GitHub Copilot?

Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add automateyournetwork/netclaw --skill pyats-dynamic-test -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/pyats-dynamic-test, .gemini/skills/pyats-dynamic-test, .github/skills/pyats-dynamic-test and .opencode/skills/pyats-dynamic-test in your project.

What does Pyats Dynamic Test need to run?

Going by SKILL.md and its folder, Pyats Dynamic Test needs the command-line tools its instructions call (python3). Our summary lists: Python 3.

Does Pyats Dynamic Test access the network?

SKILL.md contains no URLs. Any network use would come from the scripts or tools the agent runs. This is read from the text; nothing was executed.

Is Pyats Dynamic Test safe to install?

Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.

What licence does Pyats Dynamic Test use?

Pyats Dynamic Test is published under the Apache-2.0 licence (declared in SKILL.md). It allows redistribution, so the full SKILL.md is shown on this page.

How many tokens does Pyats Dynamic Test use?

About 4.7k tokens (SKILL.md is roughly 19k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.

What are the alternatives to Pyats Dynamic Test?

Skills that share tags, products or a category with Pyats Dynamic Test: Web Application Testing (anthropics/skills, 180k stars), OpenHarness End-to-End Evals (HKUDS/OpenHarness, 16k stars), Adk Verify Snippets (google/adk-python, 22k stars) and Apple Container Test Runner (RustPython/RustPython, 22k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.

Who maintains Pyats Dynamic Test?

automateyournetwork (a GitHub user) maintains it in automateyournetwork/netclaw, which has 676 GitHub stars. The repository holds 120 skills in this directory. The repository was last updated on October 5, 2026.

Source: automateyournetwork/netclaw on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.