{
  "id": "django-rest-api",
  "type": "loadout",
  "title": "Django REST Framework API rules",
  "summary": "DRF rules for agents: explicit serializer fields, querysets scoped to the user, owner set on the server, pagination, and six tests for every endpoint.",
  "description": "The DRF code agents write usually works for the person testing it and leaks for everyone else: fields set to __all__, a queryset of every row with a permission check bolted on, an owner field accepted from the request body, an unpaginated list that runs a query per row. This loadout gives your agent the DRF conventions that prevent that, from the official docs and the OWASP API Security Top 10's broken object level authorisation. A skill adds endpoints with the six tests each one needs. The verifier runs the tests on Django 6.1 and DRF 3.18, and checks that the query-count test fails without select_related.\n",
  "highlights": [
    "IsAuthenticated by default; AllowAny only when asked",
    "get_queryset scoped to request.user: other users' rows are a 404",
    "Owner set in perform_create, never from the request body",
    "Explicit serializer fields; no __all__ or exclude",
    "Six tests per endpoint, including a flat query count"
  ],
  "version": "1.0.0",
  "license": "MIT",
  "author": {
    "name": "Agent Loadout",
    "url": "https://loadout.jnw-it.com"
  },
  "credits": [
    {
      "title": "Django REST Framework docs: permissions",
      "url": "https://www.django-rest-framework.org/api-guide/permissions/"
    },
    {
      "title": "Django REST Framework docs: serializers",
      "url": "https://www.django-rest-framework.org/api-guide/serializers/"
    },
    {
      "title": "Django REST Framework docs: testing",
      "url": "https://www.django-rest-framework.org/api-guide/testing/"
    },
    {
      "title": "OWASP API Security Top 10 2023: broken object level authorisation",
      "url": "https://owasp.org/API-Security/editions/2023/en/0xa1-broken-object-level-authorization/"
    }
  ],
  "categories": [
    "web-app",
    "security"
  ],
  "stacks": [
    "python",
    "django",
    "uv"
  ],
  "files": [
    {
      "path": "AGENTS.md",
      "role": "instructions",
      "description": "Rules for a Django REST Framework API: security defaults, serializers, queries and tests.",
      "skill": null,
      "agent": null,
      "install_to": {
        "claude-code": "AGENTS.md",
        "codex": "AGENTS.md",
        "cursor": "AGENTS.md",
        "gemini-cli": "AGENTS.md",
        "antigravity-cli": "AGENTS.md",
        "copilot": "AGENTS.md"
      },
      "raw_url": "https://loadout.jnw-it.com/raw/django-rest-api/1.0.0/AGENTS.md",
      "raw_latest_url": "https://loadout.jnw-it.com/raw/django-rest-api/latest/AGENTS.md",
      "bytes": 2166,
      "lang": "markdown",
      "content": "# AGENTS.md: Django REST Framework API\n\nDjango project serving a JSON API with Django REST Framework (DRF). Check `pyproject.toml` or `uv.lock` for the Django\nand DRF versions before using newer APIs.\n\n## Commands\n- Tests: `uv run python manage.py test` (or `uv run pytest` if pytest-django is set up).\n- Checks: `uv run python manage.py check` and `uv run python manage.py makemigrations --check --dry-run`.\n- Done means: tests green, no missing migrations, every new endpoint covered by the tests listed below.\n\n## Security defaults\n- `DEFAULT_PERMISSION_CLASSES` is `IsAuthenticated`. Never add `AllowAny` to an endpoint without the user asking.\n- Scope data in `get_queryset()` to the requesting user or tenant: `Note.objects.filter(owner=self.request.user)`.\n  Another user's object is then a 404, not a 403 that confirms it exists.\n- Set ownership on the server: `perform_create(self, serializer): serializer.save(owner=self.request.user)`. Never\n  trust an owner or tenant field from the request body.\n- Throttle anonymous and costly endpoints (`DEFAULT_THROTTLE_CLASSES` or per-view `throttle_classes`).\n\n## Serializers\n- List `fields` explicitly. Never `fields = \"__all__\"` or `exclude`: a new column must not leak into the API.\n- Mark server-set fields `read_only_fields`. Validate in `validate_<field>` or `validate()`, not in views.\n- Nested writes only when asked; prefer separate endpoints.\n\n## Views and queries\n- `ModelViewSet` or generic views with a router; keep views thin.\n- Paginate every list endpoint (`DEFAULT_PAGINATION_CLASS`).\n- `select_related` / `prefetch_related` in `get_queryset()` for every relation the serializer reads. Prove it with\n  `assertNumQueries` in a test.\n\n## Tests for every endpoint\n- Anonymous request rejected; owner can do the action; lists show only the user's rows; another user gets 404;\n  invalid input returns 400 naming the field; list query count stays flat as rows grow. Use the `drf-endpoint` skill.\n- `APITestCase` with `force_authenticate`. Test passwords and data are obviously fake.\n\n## Secrets and settings\n- `SECRET_KEY`, database URLs and API keys come from the environment. `DEBUG` defaults to off.\n"
    },
    {
      "path": "CLAUDE.md",
      "role": "adapter",
      "description": "Imports AGENTS.md for Claude Code.",
      "skill": null,
      "agent": "claude-code",
      "install_to": {
        "claude-code": "CLAUDE.md",
        "codex": null,
        "cursor": null,
        "gemini-cli": null,
        "antigravity-cli": null,
        "copilot": null
      },
      "raw_url": "https://loadout.jnw-it.com/raw/django-rest-api/1.0.0/CLAUDE.md",
      "raw_latest_url": "https://loadout.jnw-it.com/raw/django-rest-api/latest/CLAUDE.md",
      "bytes": 379,
      "lang": "markdown",
      "content": "# CLAUDE.md\n\n@AGENTS.md\n\n## Claude Code notes\n- Skills from this loadout live in `.claude/skills/`: `drf-endpoint`. Use them when their description matches the task.\n- Follow AGENTS.md above; where this file and AGENTS.md disagree, AGENTS.md wins.\n\n<!-- From the Agent Loadout \"Django REST Framework API rules\" v1.0.0 — https://loadout.jnw-it.com/loadouts/django-rest-api/ -->\n"
    },
    {
      "path": "GEMINI.md",
      "role": "adapter",
      "description": "Imports AGENTS.md for Gemini CLI.",
      "skill": null,
      "agent": "gemini-cli",
      "install_to": {
        "claude-code": null,
        "codex": null,
        "cursor": null,
        "gemini-cli": "GEMINI.md",
        "antigravity-cli": null,
        "copilot": null
      },
      "raw_url": "https://loadout.jnw-it.com/raw/django-rest-api/1.0.0/GEMINI.md",
      "raw_latest_url": "https://loadout.jnw-it.com/raw/django-rest-api/latest/GEMINI.md",
      "bytes": 380,
      "lang": "markdown",
      "content": "# GEMINI.md\n\n@./AGENTS.md\n\n## Gemini CLI notes\n- Skills from this loadout live in `.agents/skills/`: `drf-endpoint`. Use them when their description matches the task.\n- Follow AGENTS.md above; where this file and AGENTS.md disagree, AGENTS.md wins.\n\n<!-- From the Agent Loadout \"Django REST Framework API rules\" v1.0.0 — https://loadout.jnw-it.com/loadouts/django-rest-api/ -->\n"
    },
    {
      "path": "skills/drf-endpoint/SKILL.md",
      "role": "skill",
      "description": "Add an endpoint with the six tests it needs.",
      "skill": "drf-endpoint",
      "agent": null,
      "install_to": {
        "claude-code": ".claude/skills/drf-endpoint/SKILL.md",
        "codex": ".agents/skills/drf-endpoint/SKILL.md",
        "cursor": ".agents/skills/drf-endpoint/SKILL.md",
        "gemini-cli": ".agents/skills/drf-endpoint/SKILL.md",
        "antigravity-cli": ".agents/skills/drf-endpoint/SKILL.md",
        "copilot": ".agents/skills/drf-endpoint/SKILL.md"
      },
      "raw_url": "https://loadout.jnw-it.com/raw/django-rest-api/1.0.0/skills/drf-endpoint/SKILL.md",
      "raw_latest_url": "https://loadout.jnw-it.com/raw/django-rest-api/latest/skills/drf-endpoint/SKILL.md",
      "bytes": 1349,
      "lang": "markdown",
      "content": "---\nname: drf-endpoint\ndescription: Add a Django REST Framework endpoint with an explicit serializer, a user-scoped queryset, server-set ownership, pagination and the six tests every endpoint needs.\n---\n\n# Add a DRF endpoint\n\nExamples: `references/serializers.py`, `references/views.py`, `references/urls.py`, `references/tests.py`.\n\n1. Model first, if needed: add it, then `uv run python manage.py makemigrations` and check the migration.\n2. Serializer: explicit `fields`, server-set fields in `read_only_fields`, validation in `validate_<field>`.\n3. View: `ModelViewSet` (or a narrower generic view) with:\n   - `get_queryset()` filtered to `self.request.user`, with `select_related` for relations the serializer reads\n   - `perform_create()` that sets the owner\n   - permissions no weaker than the project default\n4. Route it with a `DefaultRouter` and `basename`.\n5. Tests (`APITestCase`):\n   - anonymous is rejected (401 or 403, depending on the authentication class)\n   - create sets the owner from the request, ignoring an owner in the body\n   - list shows only the user's own rows\n   - another user's object is 404 for read, update and delete\n   - invalid input is a 400 that names the field\n   - `assertNumQueries` on the list endpoint with several rows\n6. Run `uv run python manage.py test`, `check` and `makemigrations --check --dry-run`.\n"
    },
    {
      "path": "skills/drf-endpoint/references/serializers.py",
      "role": "reference",
      "description": "Explicit serializer with validation.",
      "skill": "drf-endpoint",
      "agent": null,
      "install_to": {
        "claude-code": ".claude/skills/drf-endpoint/references/serializers.py",
        "codex": ".agents/skills/drf-endpoint/references/serializers.py",
        "cursor": ".agents/skills/drf-endpoint/references/serializers.py",
        "gemini-cli": ".agents/skills/drf-endpoint/references/serializers.py",
        "antigravity-cli": ".agents/skills/drf-endpoint/references/serializers.py",
        "copilot": ".agents/skills/drf-endpoint/references/serializers.py"
      },
      "raw_url": "https://loadout.jnw-it.com/raw/django-rest-api/1.0.0/skills/drf-endpoint/references/serializers.py",
      "raw_latest_url": "https://loadout.jnw-it.com/raw/django-rest-api/latest/skills/drf-endpoint/references/serializers.py",
      "bytes": 651,
      "lang": "markdown",
      "content": "from rest_framework import serializers\n\nfrom notes.models import Note\n\n\nclass NoteSerializer(serializers.ModelSerializer):\n    # Explicit fields: a new model column never leaks into the API by accident.\n    owner = serializers.CharField(source=\"owner.username\", read_only=True)\n\n    class Meta:\n        model = Note\n        fields = [\"id\", \"title\", \"body\", \"owner\", \"created_at\"]\n        read_only_fields = [\"id\", \"owner\", \"created_at\"]\n\n    def validate_title(self, value: str) -> str:\n        value = value.strip()\n        if len(value) < 3:\n            raise serializers.ValidationError(\"Title must be at least 3 characters.\")\n        return value\n"
    },
    {
      "path": "skills/drf-endpoint/references/views.py",
      "role": "reference",
      "description": "User-scoped viewset.",
      "skill": "drf-endpoint",
      "agent": null,
      "install_to": {
        "claude-code": ".claude/skills/drf-endpoint/references/views.py",
        "codex": ".agents/skills/drf-endpoint/references/views.py",
        "cursor": ".agents/skills/drf-endpoint/references/views.py",
        "gemini-cli": ".agents/skills/drf-endpoint/references/views.py",
        "antigravity-cli": ".agents/skills/drf-endpoint/references/views.py",
        "copilot": ".agents/skills/drf-endpoint/references/views.py"
      },
      "raw_url": "https://loadout.jnw-it.com/raw/django-rest-api/1.0.0/skills/drf-endpoint/references/views.py",
      "raw_latest_url": "https://loadout.jnw-it.com/raw/django-rest-api/latest/skills/drf-endpoint/references/views.py",
      "bytes": 607,
      "lang": "markdown",
      "content": "from rest_framework import permissions, viewsets\n\nfrom notes.models import Note\nfrom notes.serializers import NoteSerializer\n\n\nclass NoteViewSet(viewsets.ModelViewSet):\n    \"\"\"Notes belong to one user. Every query is scoped to request.user, so other users' notes are a 404.\"\"\"\n\n    serializer_class = NoteSerializer\n    permission_classes = [permissions.IsAuthenticated]\n\n    def get_queryset(self):\n        return Note.objects.filter(owner=self.request.user).select_related(\"owner\").order_by(\"-created_at\", \"-id\")\n\n    def perform_create(self, serializer):\n        serializer.save(owner=self.request.user)\n"
    },
    {
      "path": "skills/drf-endpoint/references/urls.py",
      "role": "reference",
      "description": "Router registration.",
      "skill": "drf-endpoint",
      "agent": null,
      "install_to": {
        "claude-code": ".claude/skills/drf-endpoint/references/urls.py",
        "codex": ".agents/skills/drf-endpoint/references/urls.py",
        "cursor": ".agents/skills/drf-endpoint/references/urls.py",
        "gemini-cli": ".agents/skills/drf-endpoint/references/urls.py",
        "antigravity-cli": ".agents/skills/drf-endpoint/references/urls.py",
        "copilot": ".agents/skills/drf-endpoint/references/urls.py"
      },
      "raw_url": "https://loadout.jnw-it.com/raw/django-rest-api/1.0.0/skills/drf-endpoint/references/urls.py",
      "raw_latest_url": "https://loadout.jnw-it.com/raw/django-rest-api/latest/skills/drf-endpoint/references/urls.py",
      "bytes": 194,
      "lang": "markdown",
      "content": "from rest_framework.routers import DefaultRouter\n\nfrom notes.views import NoteViewSet\n\nrouter = DefaultRouter()\nrouter.register(\"notes\", NoteViewSet, basename=\"note\")\n\nurlpatterns = router.urls\n"
    },
    {
      "path": "skills/drf-endpoint/references/tests.py",
      "role": "reference",
      "description": "API tests for auth, ownership, validation and queries.",
      "skill": "drf-endpoint",
      "agent": null,
      "install_to": {
        "claude-code": ".claude/skills/drf-endpoint/references/tests.py",
        "codex": ".agents/skills/drf-endpoint/references/tests.py",
        "cursor": ".agents/skills/drf-endpoint/references/tests.py",
        "gemini-cli": ".agents/skills/drf-endpoint/references/tests.py",
        "antigravity-cli": ".agents/skills/drf-endpoint/references/tests.py",
        "copilot": ".agents/skills/drf-endpoint/references/tests.py"
      },
      "raw_url": "https://loadout.jnw-it.com/raw/django-rest-api/1.0.0/skills/drf-endpoint/references/tests.py",
      "raw_latest_url": "https://loadout.jnw-it.com/raw/django-rest-api/latest/skills/drf-endpoint/references/tests.py",
      "bytes": 2563,
      "lang": "markdown",
      "content": "from django.contrib.auth import get_user_model\nfrom rest_framework import status\nfrom rest_framework.test import APITestCase\n\nfrom notes.models import Note\n\nUser = get_user_model()\n\n\nclass NoteApiTests(APITestCase):\n    def setUp(self):\n        self.ada = User.objects.create_user(\"ada\", password=\"test-only-pass\")\n        self.bob = User.objects.create_user(\"bob\", password=\"test-only-pass\")\n        self.bobs_note = Note.objects.create(owner=self.bob, title=\"Bob's plan\", body=\"private\")\n\n    def test_anonymous_requests_are_rejected(self):\n        response = self.client.get(\"/api/notes/\")\n        self.assertIn(response.status_code, {status.HTTP_401_UNAUTHORIZED, status.HTTP_403_FORBIDDEN})\n\n    def test_create_sets_owner_from_the_request_not_the_body(self):\n        self.client.force_authenticate(self.ada)\n        response = self.client.post(\"/api/notes/\", {\"title\": \"  Shopping \", \"body\": \"milk\", \"owner\": \"bob\"})\n        self.assertEqual(response.status_code, status.HTTP_201_CREATED)\n        self.assertEqual(response.data[\"owner\"], \"ada\")\n        self.assertEqual(response.data[\"title\"], \"Shopping\")\n\n    def test_list_only_shows_own_notes(self):\n        Note.objects.create(owner=self.ada, title=\"Ada's note\", body=\"\")\n        self.client.force_authenticate(self.ada)\n        response = self.client.get(\"/api/notes/\")\n        self.assertEqual(response.status_code, status.HTTP_200_OK)\n        self.assertEqual([n[\"title\"] for n in response.data[\"results\"]], [\"Ada's note\"])\n\n    def test_other_users_note_is_not_found(self):\n        self.client.force_authenticate(self.ada)\n        for method in (\"get\", \"patch\", \"delete\"):\n            response = getattr(self.client, method)(f\"/api/notes/{self.bobs_note.pk}/\")\n            self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND, method)\n        self.assertTrue(Note.objects.filter(pk=self.bobs_note.pk).exists())\n\n    def test_validation_errors_name_the_field(self):\n        self.client.force_authenticate(self.ada)\n        response = self.client.post(\"/api/notes/\", {\"title\": \"x\", \"body\": \"\"})\n        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)\n        self.assertIn(\"title\", response.data)\n\n    def test_list_query_count_does_not_grow_with_rows(self):\n        for i in range(5):\n            Note.objects.create(owner=self.ada, title=f\"Note {i}\", body=\"\")\n        self.client.force_authenticate(self.ada)\n        # One count query for pagination, one select with the owner joined.\n        with self.assertNumQueries(2):\n            self.client.get(\"/api/notes/\")\n"
    }
  ],
  "requires": {
    "tools": [
      "uv",
      "python3"
    ],
    "env": [],
    "mcp": []
  },
  "tested": {
    "status": "untested",
    "last_run": null,
    "fixture": null,
    "per_agent": {
      "claude-code": "untested",
      "codex": "untested",
      "cursor": "untested",
      "gemini-cli": "untested",
      "antigravity-cli": "untested",
      "copilot": "untested"
    }
  },
  "security": {
    "scanned": true,
    "scanner": "intake-precheck/1",
    "verdict": "clean",
    "network": true,
    "scripts": [],
    "notes": "uv downloads Django and Django REST Framework from PyPI. Settings come from the environment and DEBUG defaults to off."
  },
  "featured": false,
  "deprecated": false,
  "last_checked": "2026-10-17",
  "published": "2026-10-17",
  "updated": "2026-10-17",
  "compatibility": {
    "claude-code": "native",
    "codex": "native",
    "cursor": "native",
    "gemini-cli": "native",
    "antigravity-cli": "native",
    "copilot": "native"
  },
  "counts": {
    "rules": 1,
    "skills": 1,
    "mcp": 0
  },
  "url": "https://loadout.jnw-it.com/loadouts/django-rest-api/",
  "api_url": "https://loadout.jnw-it.com/api/loadouts/django-rest-api.json",
  "verification": {
    "level": "verified-install",
    "status": "verified",
    "label": "Verified install",
    "verified": true,
    "checked_at": "2026-10-17T09:35:26Z",
    "commit": "021470d33f1f82ab91e12f0c64c43ad15bac6118",
    "content_current": true,
    "agent_tests": "not-run",
    "counts": {
      "pass": 9,
      "fail": 0,
      "skip": 0,
      "not_applicable": 1
    },
    "meaning": "Level 1: automated checks on the files (parse, install paths, MCP config, safety scan, references, size, licence, starter build). No AI agent was run.",
    "checks": [
      {
        "id": "listing",
        "name": "Listing",
        "status": "pass",
        "summary": "Schema valid; 8 files declared, none missing or extra",
        "details": [
          "tested.status: untested (agent tests are separate from Level 1)"
        ]
      },
      {
        "id": "parse",
        "name": "Files parse",
        "status": "pass",
        "summary": "All 8 files parse; front matter valid",
        "details": []
      },
      {
        "id": "adapters",
        "name": "Adapters",
        "status": "pass",
        "summary": "CLAUDE.md and GEMINI.md import AGENTS.md and are up to date",
        "details": [
          "CLAUDE.md: matches the generator",
          "GEMINI.md: matches the generator"
        ]
      },
      {
        "id": "install-paths",
        "name": "Install paths",
        "status": "pass",
        "summary": "Correct paths for all 6 agents; layouts re-discovered",
        "details": [
          "Claude Code: 7 files, 1 skill in .claude/skills/",
          "Codex CLI: 6 files, 1 skill in .agents/skills/",
          "Cursor: 6 files, 1 skill in .agents/skills/",
          "Gemini CLI: 7 files, 1 skill in .agents/skills/",
          "Antigravity CLI: 6 files, 1 skill in .agents/skills/",
          "GitHub Copilot: 6 files, 1 skill in .agents/skills/"
        ]
      },
      {
        "id": "mcp",
        "name": "MCP config",
        "status": "n/a",
        "summary": "No MCP servers in this loadout",
        "details": []
      },
      {
        "id": "safety",
        "name": "Safety scan",
        "status": "pass",
        "summary": "Clean: no credentials, risky shell or hidden text",
        "details": [
          "Scanner intake-precheck/1: verdict clean; network no (listing says yes)"
        ]
      },
      {
        "id": "references",
        "name": "References",
        "status": "pass",
        "summary": "1 reference between files resolves for every agent",
        "details": []
      },
      {
        "id": "size",
        "name": "Size limits",
        "status": "pass",
        "summary": "8 files, 8.1 KB total; largest skills/drf-endpoint/references/tests.py 2.5 KB",
        "details": [
          "Limits: 30 files, 32 KB per file, 128 KB total, 500 lines per skill or rule"
        ]
      },
      {
        "id": "licence",
        "name": "Licence",
        "status": "pass",
        "summary": "MIT, by Agent Loadout; text in the repository LICENSE",
        "details": []
      },
      {
        "id": "starter-build",
        "name": "Starter build",
        "status": "pass",
        "summary": "The drf-endpoint skill's serializer, viewset, router and tests run on Django 6.1 with DRF 3.18: six API tests pass (auth, ownership, 404 for other users, validation, flat query count), no missing migrations, and dropping select_related fails the query-count test",
        "details": [
          "Applied 13 loadout files at every agent's paths",
          "✓ place the examples: bash -c R=.agents/skills/drf-endpoint/references && cp $R/serializers.py $R/views.py $R/urls.py $R/tests.py notes/ && echo placed",
          "✓ makemigrations (initial): uv run --no-project --python 3.12 --with django==6.1.2 --with djangorestframework==3.18.3 python manage.py makemigrations notes",
          "✓ manage.py check: uv run --no-project --python 3.12 --with django==6.1.2 --with djangorestframework==3.18.3 python manage.py check",
          "✓ makemigrations --check --dry-run: uv run --no-project --python 3.12 --with django==6.1.2 --with djangorestframework==3.18.3 python manage.py makemigrations --check --dry-run",
          "✓ API tests: uv run --no-project --python 3.12 --with django==6.1.2 --with djangorestframework==3.18.3 python manage.py test notes -v 1",
          "✓ no __all__ serializers or AllowAny: bash -c ! grep -nE '__all__|AllowAny|exclude\\s*=' notes/*.py && echo clean",
          "✓ query-count test catches a missing select_related: bash -c sed -i 's/.select_related(\"owner\")//' notes/views.py && uv run --no-project --python 3.12 --with django==6.1.2 --with djangorestframework==3.18.3 python manage.py test notes; test $? -ne 0 && echo caught",
          "Covers: IsAuthenticated default; queryset scoped to request.user so other users get 404",
          "Covers: Owner set in perform_create, ignoring the request body",
          "Covers: Explicit serializer fields with field-level validation",
          "Covers: Paginated list with a flat query count proven by assertNumQueries"
        ]
      }
    ]
  },
  "install": {
    "claude-code": {
      "files": [
        {
          "path": "AGENTS.md",
          "target": "AGENTS.md",
          "raw_url": "https://loadout.jnw-it.com/raw/django-rest-api/1.0.0/AGENTS.md",
          "role": "instructions"
        },
        {
          "path": "CLAUDE.md",
          "target": "CLAUDE.md",
          "raw_url": "https://loadout.jnw-it.com/raw/django-rest-api/1.0.0/CLAUDE.md",
          "role": "adapter"
        },
        {
          "path": "skills/drf-endpoint/SKILL.md",
          "target": ".claude/skills/drf-endpoint/SKILL.md",
          "raw_url": "https://loadout.jnw-it.com/raw/django-rest-api/1.0.0/skills/drf-endpoint/SKILL.md",
          "role": "skill"
        },
        {
          "path": "skills/drf-endpoint/references/serializers.py",
          "target": ".claude/skills/drf-endpoint/references/serializers.py",
          "raw_url": "https://loadout.jnw-it.com/raw/django-rest-api/1.0.0/skills/drf-endpoint/references/serializers.py",
          "role": "reference"
        },
        {
          "path": "skills/drf-endpoint/references/views.py",
          "target": ".claude/skills/drf-endpoint/references/views.py",
          "raw_url": "https://loadout.jnw-it.com/raw/django-rest-api/1.0.0/skills/drf-endpoint/references/views.py",
          "role": "reference"
        },
        {
          "path": "skills/drf-endpoint/references/urls.py",
          "target": ".claude/skills/drf-endpoint/references/urls.py",
          "raw_url": "https://loadout.jnw-it.com/raw/django-rest-api/1.0.0/skills/drf-endpoint/references/urls.py",
          "role": "reference"
        },
        {
          "path": "skills/drf-endpoint/references/tests.py",
          "target": ".claude/skills/drf-endpoint/references/tests.py",
          "raw_url": "https://loadout.jnw-it.com/raw/django-rest-api/1.0.0/skills/drf-endpoint/references/tests.py",
          "role": "reference"
        }
      ],
      "manual_steps": [],
      "zip_url": "https://loadout.jnw-it.com/download/django-rest-api/claude-code.zip"
    },
    "codex": {
      "files": [
        {
          "path": "AGENTS.md",
          "target": "AGENTS.md",
          "raw_url": "https://loadout.jnw-it.com/raw/django-rest-api/1.0.0/AGENTS.md",
          "role": "instructions"
        },
        {
          "path": "skills/drf-endpoint/SKILL.md",
          "target": ".agents/skills/drf-endpoint/SKILL.md",
          "raw_url": "https://loadout.jnw-it.com/raw/django-rest-api/1.0.0/skills/drf-endpoint/SKILL.md",
          "role": "skill"
        },
        {
          "path": "skills/drf-endpoint/references/serializers.py",
          "target": ".agents/skills/drf-endpoint/references/serializers.py",
          "raw_url": "https://loadout.jnw-it.com/raw/django-rest-api/1.0.0/skills/drf-endpoint/references/serializers.py",
          "role": "reference"
        },
        {
          "path": "skills/drf-endpoint/references/views.py",
          "target": ".agents/skills/drf-endpoint/references/views.py",
          "raw_url": "https://loadout.jnw-it.com/raw/django-rest-api/1.0.0/skills/drf-endpoint/references/views.py",
          "role": "reference"
        },
        {
          "path": "skills/drf-endpoint/references/urls.py",
          "target": ".agents/skills/drf-endpoint/references/urls.py",
          "raw_url": "https://loadout.jnw-it.com/raw/django-rest-api/1.0.0/skills/drf-endpoint/references/urls.py",
          "role": "reference"
        },
        {
          "path": "skills/drf-endpoint/references/tests.py",
          "target": ".agents/skills/drf-endpoint/references/tests.py",
          "raw_url": "https://loadout.jnw-it.com/raw/django-rest-api/1.0.0/skills/drf-endpoint/references/tests.py",
          "role": "reference"
        }
      ],
      "manual_steps": [],
      "zip_url": "https://loadout.jnw-it.com/download/django-rest-api/codex.zip"
    },
    "cursor": {
      "files": [
        {
          "path": "AGENTS.md",
          "target": "AGENTS.md",
          "raw_url": "https://loadout.jnw-it.com/raw/django-rest-api/1.0.0/AGENTS.md",
          "role": "instructions"
        },
        {
          "path": "skills/drf-endpoint/SKILL.md",
          "target": ".agents/skills/drf-endpoint/SKILL.md",
          "raw_url": "https://loadout.jnw-it.com/raw/django-rest-api/1.0.0/skills/drf-endpoint/SKILL.md",
          "role": "skill"
        },
        {
          "path": "skills/drf-endpoint/references/serializers.py",
          "target": ".agents/skills/drf-endpoint/references/serializers.py",
          "raw_url": "https://loadout.jnw-it.com/raw/django-rest-api/1.0.0/skills/drf-endpoint/references/serializers.py",
          "role": "reference"
        },
        {
          "path": "skills/drf-endpoint/references/views.py",
          "target": ".agents/skills/drf-endpoint/references/views.py",
          "raw_url": "https://loadout.jnw-it.com/raw/django-rest-api/1.0.0/skills/drf-endpoint/references/views.py",
          "role": "reference"
        },
        {
          "path": "skills/drf-endpoint/references/urls.py",
          "target": ".agents/skills/drf-endpoint/references/urls.py",
          "raw_url": "https://loadout.jnw-it.com/raw/django-rest-api/1.0.0/skills/drf-endpoint/references/urls.py",
          "role": "reference"
        },
        {
          "path": "skills/drf-endpoint/references/tests.py",
          "target": ".agents/skills/drf-endpoint/references/tests.py",
          "raw_url": "https://loadout.jnw-it.com/raw/django-rest-api/1.0.0/skills/drf-endpoint/references/tests.py",
          "role": "reference"
        }
      ],
      "manual_steps": [],
      "zip_url": "https://loadout.jnw-it.com/download/django-rest-api/cursor.zip"
    },
    "gemini-cli": {
      "files": [
        {
          "path": "AGENTS.md",
          "target": "AGENTS.md",
          "raw_url": "https://loadout.jnw-it.com/raw/django-rest-api/1.0.0/AGENTS.md",
          "role": "instructions"
        },
        {
          "path": "GEMINI.md",
          "target": "GEMINI.md",
          "raw_url": "https://loadout.jnw-it.com/raw/django-rest-api/1.0.0/GEMINI.md",
          "role": "adapter"
        },
        {
          "path": "skills/drf-endpoint/SKILL.md",
          "target": ".agents/skills/drf-endpoint/SKILL.md",
          "raw_url": "https://loadout.jnw-it.com/raw/django-rest-api/1.0.0/skills/drf-endpoint/SKILL.md",
          "role": "skill"
        },
        {
          "path": "skills/drf-endpoint/references/serializers.py",
          "target": ".agents/skills/drf-endpoint/references/serializers.py",
          "raw_url": "https://loadout.jnw-it.com/raw/django-rest-api/1.0.0/skills/drf-endpoint/references/serializers.py",
          "role": "reference"
        },
        {
          "path": "skills/drf-endpoint/references/views.py",
          "target": ".agents/skills/drf-endpoint/references/views.py",
          "raw_url": "https://loadout.jnw-it.com/raw/django-rest-api/1.0.0/skills/drf-endpoint/references/views.py",
          "role": "reference"
        },
        {
          "path": "skills/drf-endpoint/references/urls.py",
          "target": ".agents/skills/drf-endpoint/references/urls.py",
          "raw_url": "https://loadout.jnw-it.com/raw/django-rest-api/1.0.0/skills/drf-endpoint/references/urls.py",
          "role": "reference"
        },
        {
          "path": "skills/drf-endpoint/references/tests.py",
          "target": ".agents/skills/drf-endpoint/references/tests.py",
          "raw_url": "https://loadout.jnw-it.com/raw/django-rest-api/1.0.0/skills/drf-endpoint/references/tests.py",
          "role": "reference"
        }
      ],
      "manual_steps": [],
      "zip_url": "https://loadout.jnw-it.com/download/django-rest-api/gemini-cli.zip"
    },
    "antigravity-cli": {
      "files": [
        {
          "path": "AGENTS.md",
          "target": "AGENTS.md",
          "raw_url": "https://loadout.jnw-it.com/raw/django-rest-api/1.0.0/AGENTS.md",
          "role": "instructions"
        },
        {
          "path": "skills/drf-endpoint/SKILL.md",
          "target": ".agents/skills/drf-endpoint/SKILL.md",
          "raw_url": "https://loadout.jnw-it.com/raw/django-rest-api/1.0.0/skills/drf-endpoint/SKILL.md",
          "role": "skill"
        },
        {
          "path": "skills/drf-endpoint/references/serializers.py",
          "target": ".agents/skills/drf-endpoint/references/serializers.py",
          "raw_url": "https://loadout.jnw-it.com/raw/django-rest-api/1.0.0/skills/drf-endpoint/references/serializers.py",
          "role": "reference"
        },
        {
          "path": "skills/drf-endpoint/references/views.py",
          "target": ".agents/skills/drf-endpoint/references/views.py",
          "raw_url": "https://loadout.jnw-it.com/raw/django-rest-api/1.0.0/skills/drf-endpoint/references/views.py",
          "role": "reference"
        },
        {
          "path": "skills/drf-endpoint/references/urls.py",
          "target": ".agents/skills/drf-endpoint/references/urls.py",
          "raw_url": "https://loadout.jnw-it.com/raw/django-rest-api/1.0.0/skills/drf-endpoint/references/urls.py",
          "role": "reference"
        },
        {
          "path": "skills/drf-endpoint/references/tests.py",
          "target": ".agents/skills/drf-endpoint/references/tests.py",
          "raw_url": "https://loadout.jnw-it.com/raw/django-rest-api/1.0.0/skills/drf-endpoint/references/tests.py",
          "role": "reference"
        }
      ],
      "manual_steps": [],
      "zip_url": "https://loadout.jnw-it.com/download/django-rest-api/antigravity-cli.zip"
    },
    "copilot": {
      "files": [
        {
          "path": "AGENTS.md",
          "target": "AGENTS.md",
          "raw_url": "https://loadout.jnw-it.com/raw/django-rest-api/1.0.0/AGENTS.md",
          "role": "instructions"
        },
        {
          "path": "skills/drf-endpoint/SKILL.md",
          "target": ".agents/skills/drf-endpoint/SKILL.md",
          "raw_url": "https://loadout.jnw-it.com/raw/django-rest-api/1.0.0/skills/drf-endpoint/SKILL.md",
          "role": "skill"
        },
        {
          "path": "skills/drf-endpoint/references/serializers.py",
          "target": ".agents/skills/drf-endpoint/references/serializers.py",
          "raw_url": "https://loadout.jnw-it.com/raw/django-rest-api/1.0.0/skills/drf-endpoint/references/serializers.py",
          "role": "reference"
        },
        {
          "path": "skills/drf-endpoint/references/views.py",
          "target": ".agents/skills/drf-endpoint/references/views.py",
          "raw_url": "https://loadout.jnw-it.com/raw/django-rest-api/1.0.0/skills/drf-endpoint/references/views.py",
          "role": "reference"
        },
        {
          "path": "skills/drf-endpoint/references/urls.py",
          "target": ".agents/skills/drf-endpoint/references/urls.py",
          "raw_url": "https://loadout.jnw-it.com/raw/django-rest-api/1.0.0/skills/drf-endpoint/references/urls.py",
          "role": "reference"
        },
        {
          "path": "skills/drf-endpoint/references/tests.py",
          "target": ".agents/skills/drf-endpoint/references/tests.py",
          "raw_url": "https://loadout.jnw-it.com/raw/django-rest-api/1.0.0/skills/drf-endpoint/references/tests.py",
          "role": "reference"
        }
      ],
      "manual_steps": [],
      "zip_url": "https://loadout.jnw-it.com/download/django-rest-api/copilot.zip"
    }
  },
  "source_zip_url": "https://loadout.jnw-it.com/download/django-rest-api/source.zip"
}