{
  "id": "pandas-3-upgrade-guard",
  "type": "loadout",
  "title": "pandas 3 guard for copy-on-write and removed APIs",
  "summary": "Stop agents writing applymap, fillna(method=), freq=\"H\" and chained assignment into pandas 3 projects, where copy-on-write silently drops the write.",
  "description": "pandas 3.0 removed the APIs deprecated through 2.x and made two behaviour changes permanent. Copy-on-write is always on, so chained assignment such as df[\"v\"][0] = 1, or df[\"v\"].fillna(0, inplace=True), changes a temporary copy and leaves the frame untouched, with only a warning. Strings are now the str dtype, so dtype == object checks quietly stop matching. Agents learnt pandas from a decade of answers that rely on all of this.\n\nThis loadout pairs a short Cursor rule and AGENTS.md (a never/always table) with a skill that checks the work. A standard-library Python scanner reports removed APIs (applymap, append, fillna(method=), delim_whitespace, date_parser, old offset aliases such as H and M, errors=\"ignore\") and chained writes as errors, and object-dtype checks and agg(np.sum) as warnings. Then the tests run with ChainedAssignmentError raised as an error, so every lost write fails. The verifier runs the scanner against a pandas 3 script and a pandas 2 sample, and runs both with the pinned pandas 3.1.0.\n",
  "highlights": [
    "Never/always table for pandas 3 in a .py-scoped Cursor rule",
    "Scanner flags removed APIs and chained writes as errors, read-only",
    "Catches writes copy-on-write throws away, like df[\"v\"][0] = 1",
    "Tests run with ChainedAssignmentError as an error, so lost writes fail",
    "Warns on object-dtype string checks now that strings are dtype str"
  ],
  "version": "1.0.0",
  "license": "MIT",
  "author": {
    "name": "SetupBay",
    "url": "https://setupbay.com"
  },
  "credits": [
    {
      "title": "pandas docs: what's new in 3.0.0",
      "url": "https://pandas.pydata.org/docs/whatsnew/v3.0.0.html"
    },
    {
      "title": "pandas docs: copy-on-write",
      "url": "https://pandas.pydata.org/docs/user_guide/copy_on_write.html"
    },
    {
      "title": "pandas 3.1.0 on PyPI (released 22 Oct 2026)",
      "url": "https://pypi.org/project/pandas/3.1.0/"
    },
    {
      "title": "@jmdevilleros: a pandas 3 update broke a data tool an agent was meant to use",
      "url": "https://x.com/jmdevilleros/status/2112904481021075601"
    }
  ],
  "categories": [
    "data"
  ],
  "stacks": [
    "pandas",
    "python",
    "uv"
  ],
  "files": [
    {
      "path": "AGENTS.md",
      "role": "instructions",
      "description": "pandas 3 version facts, rules and the done checklist.",
      "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://setupbay.com/raw/pandas-3-upgrade-guard/1.0.0/AGENTS.md",
      "raw_latest_url": "https://setupbay.com/raw/pandas-3-upgrade-guard/latest/AGENTS.md",
      "bytes": 2231,
      "lang": "markdown",
      "content": "# AGENTS.md: pandas 3 project (pandas 1 and 2 guard)\n\nThis project uses **pandas 3** (`pandas>=3.1,<4`). Most pandas code you have seen predates it. pandas 3 removed a\nlong list of deprecated APIs and changed two things that make old code wrong without an error:\n**copy-on-write is always on**, and **strings are the `str` dtype, not `object`**.\n\n## Version facts\n- Check the pin first: `uv pip show pandas` or the `pandas` line in `pyproject.toml`/`uv.lock`.\n- Copy-on-write: every indexing result behaves as a copy. Chained assignment (`df[\"a\"][0] = 1`) and\n  `df[\"a\"].fillna(0, inplace=True)` change a temporary copy and leave `df` alone. pandas only warns.\n- `pd.Series([\"a\"]).dtype` is `str`. `dtype == object` checks are now false for string columns, and a\n  string column refuses non-string values.\n- Offset aliases: `h`, `min`, `s`, `ms`, `us`, `ns`; month, quarter and year ends are `ME`, `QE`, `YE`.\n- If you are unsure an API exists in pandas 3, check the API reference (https://pandas.pydata.org/docs/reference/)\n  or the 3.0 release notes (https://pandas.pydata.org/docs/whatsnew/v3.0.0.html). Don't copy pandas 1 answers.\n\n## Rules\n- Write pandas 3 only. The `pandas-3-guard` skill has the full table; the common traps are:\n  `DataFrame.map` (not `applymap`), `pd.concat` (not `append`), `.ffill()` (not `fillna(method=)`),\n  `sep=r\"\\s+\"` (not `delim_whitespace`), `date_format=` (not `date_parser`), `freq=\"h\"`/`\"ME\"` (not `\"H\"`/`\"M\"`),\n  `errors=\"coerce\"` (not `\"ignore\"`), `pd.read_json(io.StringIO(text))` (not literal JSON).\n- Assign with one `.loc` call: `df.loc[mask, \"col\"] = value`. Never chain `[...][...] =`.\n- Reassign instead of `inplace=True` on a column: `df[\"col\"] = df[\"col\"].fillna(0)`.\n- Test for strings with `pd.api.types.is_string_dtype(s)`, not `dtype == object`.\n- Don't upgrade or downgrade pandas without asking the human.\n\n## Before you say \"done\"\n1. Run the `pandas-3-guard` scanner on the files you touched; it must report `0 error(s)`. Read every `warn`.\n2. Run the tests with warnings as errors for pandas' own warnings:\n   `uv run pytest -W error::pandas.errors.ChainedAssignmentError`.\n3. Say which silent changes (copy-on-write writes, string dtype checks) you looked for and where.\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://setupbay.com/raw/pandas-3-upgrade-guard/1.0.0/CLAUDE.md",
      "raw_latest_url": "https://setupbay.com/raw/pandas-3-upgrade-guard/latest/CLAUDE.md",
      "bytes": 392,
      "lang": "markdown",
      "content": "# CLAUDE.md\n\n@AGENTS.md\n\n## Claude Code notes\n\n- Skills from this loadout live in `.claude/skills/`: `pandas-3-guard`. 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 SetupBay \"pandas 3 guard for copy-on-write and removed APIs\" v1.0.0 — https://setupbay.com/loadouts/pandas-3-upgrade-guard/ -->\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://setupbay.com/raw/pandas-3-upgrade-guard/1.0.0/GEMINI.md",
      "raw_latest_url": "https://setupbay.com/raw/pandas-3-upgrade-guard/latest/GEMINI.md",
      "bytes": 393,
      "lang": "markdown",
      "content": "# GEMINI.md\n\n@./AGENTS.md\n\n## Gemini CLI notes\n\n- Skills from this loadout live in `.agents/skills/`: `pandas-3-guard`. 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 SetupBay \"pandas 3 guard for copy-on-write and removed APIs\" v1.0.0 — https://setupbay.com/loadouts/pandas-3-upgrade-guard/ -->\n"
    },
    {
      "path": ".cursor/rules/pandas-3-only.mdc",
      "role": "rules",
      "description": "Never/always table for Python files.",
      "skill": null,
      "agent": null,
      "install_to": {
        "claude-code": null,
        "codex": null,
        "cursor": ".cursor/rules/pandas-3-only.mdc",
        "gemini-cli": null,
        "antigravity-cli": null,
        "copilot": null
      },
      "raw_url": "https://setupbay.com/raw/pandas-3-upgrade-guard/1.0.0/.cursor/rules/pandas-3-only.mdc",
      "raw_latest_url": "https://setupbay.com/raw/pandas-3-upgrade-guard/latest/.cursor/rules/pandas-3-only.mdc",
      "bytes": 1156,
      "lang": "markdown",
      "content": "---\ndescription: pandas 3 only; copy-on-write and the str dtype, no APIs removed in 3.0\nglobs: \"**/*.py,**/*.ipynb\"\nalwaysApply: false\n---\n\nThis project pins pandas 3. Copy-on-write is always on and strings are dtype `str`.\n\n| Never (pandas 1 and 2) | Always (pandas 3) |\n|---|---|\n| `df[\"a\"][0] = 1` | `df.loc[0, \"a\"] = 1` |\n| `df[\"a\"].fillna(0, inplace=True)` | `df[\"a\"] = df[\"a\"].fillna(0)` |\n| `df.applymap(f)` | `df.map(f)` |\n| `df.append(other)` | `pd.concat([df, other], ignore_index=True)` |\n| `fillna(method=\"ffill\")` | `.ffill()` |\n| `read_csv(delim_whitespace=True)` | `read_csv(sep=r\"\\s+\")` |\n| `read_csv(date_parser=...)` | `read_csv(date_format=\"%Y-%m-%d\")` |\n| `freq=\"H\"`, `\"T\"`, `\"M\"`, `\"Y\"` | `freq=\"h\"`, `\"min\"`, `\"ME\"`, `\"YE\"` |\n| `to_numeric(s, errors=\"ignore\")` | `to_numeric(s, errors=\"coerce\")` |\n| `pd.read_json('[{...}]')` | `pd.read_json(io.StringIO(text))` |\n| `s.dtype == object` for strings | `pd.api.types.is_string_dtype(s)` |\n| `agg(np.sum)` | `agg(\"sum\")` |\n\n- No defensive `.copy()` to silence SettingWithCopyWarning; that warning no longer exists.\n- After editing, run the `pandas-3-guard` skill's scanner and the tests.\n"
    },
    {
      "path": "skills/pandas-3-guard/SKILL.md",
      "role": "skill",
      "description": "Scan for removed pandas APIs and lost writes, then run the tests with ChainedAssignmentError as an error.",
      "skill": "pandas-3-guard",
      "agent": null,
      "install_to": {
        "claude-code": ".claude/skills/pandas-3-guard/SKILL.md",
        "codex": ".agents/skills/pandas-3-guard/SKILL.md",
        "cursor": ".agents/skills/pandas-3-guard/SKILL.md",
        "gemini-cli": ".agents/skills/pandas-3-guard/SKILL.md",
        "antigravity-cli": ".agents/skills/pandas-3-guard/SKILL.md",
        "copilot": ".agents/skills/pandas-3-guard/SKILL.md"
      },
      "raw_url": "https://setupbay.com/raw/pandas-3-upgrade-guard/1.0.0/skills/pandas-3-guard/SKILL.md",
      "raw_latest_url": "https://setupbay.com/raw/pandas-3-upgrade-guard/latest/skills/pandas-3-guard/SKILL.md",
      "bytes": 2617,
      "lang": "markdown",
      "content": "---\nname: pandas-3-guard\ndescription: Check Python for pandas APIs removed in 3.0, chained assignment that copy-on-write ignores, and object-dtype string checks, before saying a pandas change is done. Use after writing or editing pandas code, when pandas raises an unexpected keyword or attribute error, warns ChainedAssignmentError, or when porting older pandas snippets.\n---\n\n# pandas 3 guard\n\npandas 3.0 (21 January 2026; 3.1 on 22 October 2026) removed what 2.x deprecated and turned on two behaviour\nchanges for good. Copy-on-write means a chained write such as `df[\"v\"][0] = 1` changes a copy and leaves `df`\nalone, with only a warning. The `str` dtype means string columns are no longer `object`. Removed names fail loudly.\nThose two changes don't fail at all.\n\n## 1. Confirm the version\n- `uv run python -c \"import pandas; print(pandas.__version__)\"` must print `3.x`. If it prints `2.x`, stop and ask\n  whether the project is moving to pandas 3. The pandas docs suggest getting clean on 2.3 first.\n\n## 2. Scan\n```bash\npython3 .agents/skills/pandas-3-guard/scripts/pandas3_guard.py src tests notebooks\n```\n(Use the path the skill was installed to: `.claude/skills/...` for Claude Code, `.agents/skills/...` for the others.)\n\n- Every `error` line is either an API pandas 3 removed or a write that copy-on-write throws away. Fix it with the\n  replacement the line gives; `references/rename-table.md` has the table.\n- The chained-assignment rules only look at names like `df`, `*_df`, `frame*` and `data`, so a frame called\n  something else can slip through. The test run below catches those.\n- `warn` lines still run but may mean something different now: `dtype == object` checks, `agg(np.sum)`,\n  groupby on categoricals. Fix them, or add `# guard: ok` to a line where the new behaviour is what you want.\n  That silences warnings, never errors.\n\n## 3. Let pandas check it\n```bash\nuv run pytest -q -W error::pandas.errors.ChainedAssignmentError\n```\n- `ChainedAssignmentError` is a warning by default. Turning it into an error makes every lost write fail a test.\n- `TypeError: ... got an unexpected keyword argument` and `AttributeError: 'DataFrame' object has no attribute`\n  mean a removed API. The release notes name each replacement.\n- `TypeError: Invalid value ... for dtype 'str'` means code put a non-string into a string column. Convert on purpose\n  (`astype(object)`) only if mixed values are really wanted.\n\n## 4. Report\nSay how many removed calls and chained writes you replaced, which dtype checks you changed, and that the scanner and\nthe tests (with ChainedAssignmentError as an error) were clean.\n"
    },
    {
      "path": "skills/pandas-3-guard/scripts/pandas3_guard.py",
      "role": "reference",
      "description": "Read-only scanner for removed pandas APIs, chained writes and dtype checks.",
      "skill": "pandas-3-guard",
      "agent": null,
      "install_to": {
        "claude-code": ".claude/skills/pandas-3-guard/scripts/pandas3_guard.py",
        "codex": ".agents/skills/pandas-3-guard/scripts/pandas3_guard.py",
        "cursor": ".agents/skills/pandas-3-guard/scripts/pandas3_guard.py",
        "gemini-cli": ".agents/skills/pandas-3-guard/scripts/pandas3_guard.py",
        "antigravity-cli": ".agents/skills/pandas-3-guard/scripts/pandas3_guard.py",
        "copilot": ".agents/skills/pandas-3-guard/scripts/pandas3_guard.py"
      },
      "raw_url": "https://setupbay.com/raw/pandas-3-upgrade-guard/1.0.0/skills/pandas-3-guard/scripts/pandas3_guard.py",
      "raw_latest_url": "https://setupbay.com/raw/pandas-3-upgrade-guard/latest/skills/pandas-3-guard/scripts/pandas3_guard.py",
      "bytes": 8206,
      "lang": "markdown",
      "content": "#!/usr/bin/env python3\n\"\"\"Find pandas 1.x/2.x code that pandas 3 removed, and code that copy-on-write or the str dtype quietly changed.\n\nUsage: python3 pandas3_guard.py [PATH ...]        (default: the current folder)\n       python3 pandas3_guard.py --list            (print every rule)\n\nA \"guard: ok\" comment on a line silences its warnings (never its errors).\nPrints one line per finding:  path:line: level [rule] what is wrong -> what to use\nExit 0 when nothing at \"error\" level is found, 1 when something is, 2 on bad usage.\nStandard library only. Read-only: it never edits a file and makes no network calls.\n\"\"\"\nimport fnmatch\nimport os\nimport re\nimport sys\n\n# (rule id, level, regex, message, file patterns). Patterns run against each line with comments removed.\nRULES = [\n    ('applymap', 'error', '\\\\.applymap\\\\(',\n     'DataFrame.applymap() was removed -> DataFrame.map()', []),\n    ('append', 'error', '\\\\b(df\\\\w*|frame\\\\w*|data\\\\w*|result\\\\w*|out\\\\w*)\\\\.append\\\\(',\n     'DataFrame.append() was removed (2.0) -> pd.concat([a, b], ignore_index=True)', []),\n    ('iteritems', 'error', '\\\\.iteritems\\\\(',\n     'iteritems() was removed (2.0) -> items()', []),\n    ('fillna-method', 'error', '\\\\.fillna\\\\([^)]*\\\\bmethod\\\\s*=',\n     \"fillna(method='ffill') was removed -> .ffill() / .bfill()\", []),\n    ('replace-method', 'error', '\\\\.replace\\\\([^)]*\\\\b(method|limit)\\\\s*=',\n     'replace(method=..., limit=...) was removed -> .ffill() / .bfill() after replacing with NA', []),\n    ('interpolate-pad', 'error', '\\\\.interpolate\\\\([^)]*[\\\\\"\\'](pad|ffill|bfill|backfill)[\\\\\"\\']',\n     \"interpolate('ffill') was removed -> .ffill() / .bfill()\", []),\n    ('freq-alias', 'error', '\\\\b(freq|rule)\\\\s*=\\\\s*[\\\\\"\\'](\\\\d*)(H|BH|CBH|T|L|U|N|M|BM|SM|CBM|Q|BQ|Y|BY|A|BA|AS|BAS)(-[A-Z]{3})?[\\\\\"\\']',\n     \"old offset alias -> 'h', 'min', 'ms', 'us', 'ns', 'ME', 'QE', 'YE' (month/quarter/year END aliases)\", []),\n    ('resample-alias', 'error', '\\\\.(resample|asfreq)\\\\(\\\\s*[\\\\\"\\'](\\\\d*)(H|T|M|Q|Y|A)[\\\\\"\\']',\n     \"old offset alias in resample/asfreq -> 'h', 'min', 'ME', 'QE', 'YE'\", []),\n    ('delim-whitespace', 'error', '\\\\bdelim_whitespace\\\\s*=',\n     'read_csv(delim_whitespace=True) was removed -> sep=r\"\\\\s+\"', []),\n    ('date-parser', 'error', '\\\\b(date_parser|infer_datetime_format|keep_date_col)\\\\s*=',\n     'read_csv date_parser / infer_datetime_format / keep_date_col were removed -> date_format=... or pd.to_datetime after reading', []),\n    ('swapaxes', 'error', '\\\\.swapaxes\\\\(',\n     'swapaxes() was removed -> .T or .transpose()', []),\n    ('series-view', 'error', '\\\\]\\\\.view\\\\(|\\\\bseries\\\\w*\\\\.view\\\\(|\\\\bs\\\\.view\\\\(',\n     'Series.view() was removed -> .astype(...)', []),\n    ('groupby-axis', 'error', '\\\\.(groupby|rolling|resample)\\\\([^)]*\\\\baxis\\\\s*=',\n     'the axis= argument was removed from groupby/rolling/resample -> transpose first', []),\n    ('first-last', 'error', '\\\\.(first|last)\\\\(\\\\s*[\\\\\"\\']\\\\d',\n     \"DataFrame.first('3D') / last('3D') were removed -> df.loc[df.index < df.index[0] + pd.Timedelta('3D')]\", []),\n    ('errors-ignore', 'error', '\\\\bto_(numeric|datetime|timedelta)\\\\([^)]*errors\\\\s*=\\\\s*[\\\\\"\\']ignore[\\\\\"\\']',\n     \"errors='ignore' was removed -> errors='coerce' and handle NaN, or try/except\", []),\n    ('series-bool', 'error', '\\\\.bool\\\\(\\\\s*\\\\)',\n     'Series.bool() / DataFrame.bool() were removed -> bool(s.item())', []),\n    ('use-inf-as-na', 'error', 'use_inf_as_na',\n     'mode.use_inf_as_na was removed -> df.replace([np.inf, -np.inf], np.nan)', []),\n    ('copy-on-write-option', 'warn', 'mode\\\\.copy_on_write|copy_on_write\\\\s*=',\n     'copy-on-write is always on in pandas 3; the option does nothing and will be removed', []),\n    ('read-literal', 'error', 'pd\\\\.read_(json|html|xml|excel)\\\\(\\\\s*(f?[\\\\\"\\'][\\\\[{<]|response\\\\.text|r\\\\.text)',\n     'read_json/read_html/read_xml no longer take literal text -> wrap it: pd.read_json(io.StringIO(text))', []),\n    ('chained-assignment', 'error', '\\\\b(df\\\\w*|\\\\w+_df|frame\\\\w*|data)\\\\[[^\\\\]]+\\\\]\\\\[[^\\\\]]+\\\\]\\\\s*=[^=]',\n     \"chained assignment never writes back under copy-on-write -> df.loc[row, 'col'] = value\", []),\n    ('inplace-on-column', 'error', '\\\\b(df\\\\w*|\\\\w+_df|frame\\\\w*|data)\\\\[[^\\\\]]+\\\\]\\\\.\\\\w+\\\\([^)]*inplace\\\\s*=\\\\s*True',\n     \"df['col'].method(inplace=True) changes a copy, not df -> df['col'] = df['col'].method()\", []),\n    ('object-dtype-check', 'warn', 'dtype\\\\s*==\\\\s*(object|[\\\\\"\\']object[\\\\\"\\']|np\\\\.object_)|select_dtypes\\\\([^)]*[\\\\\"\\']object[\\\\\"\\']',\n     \"strings are now dtype 'str', not object -> pd.api.types.is_string_dtype(s) or select_dtypes(include=['str'])\", []),\n    ('setting-with-copy', 'warn', 'SettingWithCopyWarning|\\\\.copy\\\\(\\\\)\\\\s*#.*SettingWithCopy',\n     'SettingWithCopyWarning is gone in pandas 3; the defensive .copy() is not needed for it', []),\n    ('observed-default', 'warn', '\\\\.groupby\\\\((?![^)]*observed)[^)]*\\\\)(?=.*cat)',\n     'groupby on categoricals now defaults to observed=True; pass observed=False to keep empty categories', []),\n    ('future-stack', 'warn', '\\\\.stack\\\\([^)]*future_stack',\n     'future_stack is the default in pandas 3; drop the argument', []),\n    ('np-reduction-in-agg', 'warn', '\\\\.(agg|apply|transform)\\\\(\\\\s*(np\\\\.(sum|mean|max|min|std)|sum|max|min)\\\\s*[,)]',\n     \"agg(np.sum) is no longer swapped for pandas' own sum -> pass the string 'sum'\", []),\n]\nFILES = ['*.py', '*.ipynb']\nCOMMENT = '#'\nSKIP_DIRS = {\".git\", \".venv\", \"venv\", \"node_modules\", \"__pycache__\", \".agents\", \".claude\", \".cursor\", \"dist\", \"build\", \"target\", \".godot\"}\nCOMPILED = [(rid, lvl, re.compile(rx), msg, globs) for rid, lvl, rx, msg, globs in RULES]\n\n\ndef strip_comment(line):\n    \"\"\"Drop a trailing comment, leaving string literals alone.\"\"\"\n    q = None\n    i = 0\n    while i < len(line):\n        c = line[i]\n        if q:\n            if c == \"\\\\\":\n                i += 2\n                continue\n            if c == q:\n                q = None\n        elif c in \"\\\"'`\":\n            q = c\n        elif line.startswith(COMMENT, i):\n            return line[:i]\n        i += 1\n    return line\n\n\ndef wanted(name):\n    return any(fnmatch.fnmatch(name, g) for g in FILES)\n\n\ndef walk(paths):\n    for p in paths:\n        if os.path.isfile(p):\n            yield p\n            continue\n        for root, dirs, files in os.walk(p):\n            dirs[:] = sorted(d for d in dirs if d not in SKIP_DIRS)\n            for f in sorted(files):\n                if wanted(f):\n                    yield os.path.join(root, f)\n\n\ndef scan(path):\n    out = []\n    name = os.path.basename(path)\n    try:\n        with open(path, encoding=\"utf-8\", errors=\"replace\") as fh:\n            lines = fh.read().splitlines()\n    except OSError as e:\n        return [(path, 0, \"error\", \"unreadable\", str(e))]\n    for n, raw in enumerate(lines, 1):\n        code = strip_comment(raw)\n        if not code.strip():\n            continue\n        accepted = \"guard: ok\" in raw  # a person accepted the warnings on this line\n        for rid, lvl, rx, msg, globs in COMPILED:\n            if accepted and lvl == \"warn\":\n                continue\n            if globs and not any(fnmatch.fnmatch(name, g) for g in globs):\n                continue\n            if rx.search(code):\n                out.append((path, n, lvl, rid, msg))\n    return out\n\n\ndef main(argv):\n    if \"--list\" in argv:\n        for rid, lvl, _, msg, globs in RULES:\n            print(f\"{lvl:5} [{rid}] {msg}\" + (f\"  (only {', '.join(globs)})\" if globs else \"\"))\n        return 0\n    if any(a.startswith(\"-\") for a in argv):\n        print(__doc__)\n        return 2\n    paths = argv or [\".\"]\n    for p in paths:\n        if not os.path.exists(p):\n            print(f\"no such path: {p}\", file=sys.stderr)\n            return 2\n    files = list(walk(paths))\n    findings = []\n    for f in files:\n        findings.extend(scan(f))\n    for path, n, lvl, rid, msg in findings:\n        print(f\"{os.path.relpath(path)}:{n}: {lvl} [{rid}] {msg}\")\n    errors = sum(1 for x in findings if x[2] == \"error\")\n    warns = sum(1 for x in findings if x[2] == \"warn\")\n    print(f\"pandas3_guard: {len(files)} file(s), {errors} error(s), {warns} warning(s)\")\n    return 1 if errors else 0\n\n\nif __name__ == \"__main__\":\n    sys.exit(main(sys.argv[1:]))\n"
    },
    {
      "path": "skills/pandas-3-guard/references/rename-table.md",
      "role": "reference",
      "description": "pandas 1 and 2 to pandas 3 table, including copy-on-write fixes.",
      "skill": "pandas-3-guard",
      "agent": null,
      "install_to": {
        "claude-code": ".claude/skills/pandas-3-guard/references/rename-table.md",
        "codex": ".agents/skills/pandas-3-guard/references/rename-table.md",
        "cursor": ".agents/skills/pandas-3-guard/references/rename-table.md",
        "gemini-cli": ".agents/skills/pandas-3-guard/references/rename-table.md",
        "antigravity-cli": ".agents/skills/pandas-3-guard/references/rename-table.md",
        "copilot": ".agents/skills/pandas-3-guard/references/rename-table.md"
      },
      "raw_url": "https://setupbay.com/raw/pandas-3-upgrade-guard/1.0.0/skills/pandas-3-guard/references/rename-table.md",
      "raw_latest_url": "https://setupbay.com/raw/pandas-3-upgrade-guard/latest/skills/pandas-3-guard/references/rename-table.md",
      "bytes": 2677,
      "lang": "markdown",
      "content": "# pandas 1/2 → pandas 3 table\n\nFrom the pandas 3.0.0 release notes (https://pandas.pydata.org/docs/whatsnew/v3.0.0.html) and the copy-on-write\nguide (https://pandas.pydata.org/docs/user_guide/copy_on_write.html). Checked against pandas 3.1.0.\n\n## Removed: raises in pandas 3\n\n| Old | pandas 3 |\n|---|---|\n| `df.applymap(f)` | `df.map(f)` |\n| `df.append(other)` (removed in 2.0) | `pd.concat([df, other], ignore_index=True)` |\n| `df.iteritems()` (removed in 2.0) | `df.items()` |\n| `df.fillna(method=\"ffill\")` | `df.ffill()` |\n| `df.replace(x, method=\"pad\")` | `df.replace(x, np.nan).ffill()` |\n| `s.interpolate(\"pad\")` | `s.ffill()` |\n| `freq=\"H\"`, `\"BH\"`, `\"T\"`, `\"L\"`, `\"U\"`, `\"N\"` | `\"h\"`, `\"bh\"`, `\"min\"`, `\"ms\"`, `\"us\"`, `\"ns\"` |\n| `freq=\"M\"`, `\"Q\"`, `\"Y\"`, `\"A\"` (ends) | `\"ME\"`, `\"QE\"`, `\"YE\"` |\n| `read_csv(delim_whitespace=True)` | `read_csv(sep=r\"\\s+\")` |\n| `read_csv(date_parser=f)` | `read_csv(date_format=\"...\")` or `pd.to_datetime` afterwards |\n| `read_csv(infer_datetime_format=True)` | drop it; strict inference is the default |\n| `df.swapaxes(0, 1)` | `df.T` |\n| `s.view(\"int64\")` | `s.astype(\"int64\")` |\n| `df.groupby(\"k\", axis=1)` | `df.T.groupby(\"k\")` |\n| `df.first(\"3D\")` / `df.last(\"3D\")` | `df.loc[df.index < df.index[0] + pd.Timedelta(\"3D\")]` |\n| `pd.to_numeric(s, errors=\"ignore\")` | `pd.to_numeric(s, errors=\"coerce\")` |\n| `s.bool()` | `bool(s.item())` |\n| `pd.set_option(\"mode.use_inf_as_na\", True)` | `df.replace([np.inf, -np.inf], np.nan)` |\n| `pd.read_json('[{\"a\": 1}]')` | `pd.read_json(io.StringIO(text))` (same for read_html, read_xml) |\n\n## Copy-on-write: writes that are silently lost\n\n| Old | pandas 3 |\n|---|---|\n| `df[\"v\"][0] = 1` | `df.loc[0, \"v\"] = 1` |\n| `df[df.a > 0][\"b\"] = 0` | `df.loc[df.a > 0, \"b\"] = 0` |\n| `df[\"v\"].fillna(0, inplace=True)` | `df[\"v\"] = df[\"v\"].fillna(0)` |\n| `sub = df[cols]; sub[\"x\"] = 1` expecting `df` to change | change `df` itself |\n| defensive `.copy()` to silence SettingWithCopyWarning | not needed; the warning is gone |\n\n## Changed meaning: same code, different answer\n\n| Change | What to do |\n|---|---|\n| String columns are dtype `str`, not `object` | `pd.api.types.is_string_dtype(s)`; `select_dtypes(include=[\"str\"])` |\n| A `str` column refuses non-strings | convert on purpose if mixed values are wanted |\n| Missing strings are `NaN` | test with `isna()`, not `is None` |\n| `groupby(...)` on categoricals defaults to `observed=True` | pass `observed=False` to keep empty categories |\n| `agg(np.sum)` is no longer swapped for pandas' sum | `agg(\"sum\")` |\n| `Series[int]` always means a label, never a position | `s.iloc[i]` for positions |\n| `mode.copy_on_write` option does nothing | remove it |\n"
    },
    {
      "path": "skills/pandas-3-guard/references/pandas2-sample.py.txt",
      "role": "reference",
      "description": "A pandas 2 cleaning script for checking the guard.",
      "skill": "pandas-3-guard",
      "agent": null,
      "install_to": {
        "claude-code": ".claude/skills/pandas-3-guard/references/pandas2-sample.py.txt",
        "codex": ".agents/skills/pandas-3-guard/references/pandas2-sample.py.txt",
        "cursor": ".agents/skills/pandas-3-guard/references/pandas2-sample.py.txt",
        "gemini-cli": ".agents/skills/pandas-3-guard/references/pandas2-sample.py.txt",
        "antigravity-cli": ".agents/skills/pandas-3-guard/references/pandas2-sample.py.txt",
        "copilot": ".agents/skills/pandas-3-guard/references/pandas2-sample.py.txt"
      },
      "raw_url": "https://setupbay.com/raw/pandas-3-upgrade-guard/1.0.0/skills/pandas-3-guard/references/pandas2-sample.py.txt",
      "raw_latest_url": "https://setupbay.com/raw/pandas-3-upgrade-guard/latest/skills/pandas-3-guard/references/pandas2-sample.py.txt",
      "bytes": 1292,
      "lang": "markdown",
      "content": "# A pandas 2 cleaning script, used to check the guard. Every marked line fails or silently changes in pandas 3.\nimport numpy as np\nimport pandas as pd\n\ndf = pd.read_csv(\"readings.txt\", delim_whitespace=True)                       # delim-whitespace\ndf = pd.read_csv(\"readings.csv\", parse_dates=[\"day\"], date_parser=pd.to_datetime)  # date-parser\ndf = df.applymap(lambda v: v.strip() if isinstance(v, str) else v)            # applymap\ndf = df.fillna(method=\"ffill\")                                                # fillna-method\ndf[\"reading\"][0] = 0.0                                                        # chained-assignment\ndf[\"site\"].fillna(\"unknown\", inplace=True)                                    # inplace-on-column\nhourly = df.set_index(\"day\").resample(\"H\").mean()                             # resample-alias\nmonths = pd.date_range(\"2026-01-01\", periods=12, freq=\"M\")                    # freq-alias\ncodes = pd.to_numeric(df[\"code\"], errors=\"ignore\")                            # errors-ignore\nmore = df.append(df.tail(1))                                                  # append\ntext_cols = [c for c in df.columns if df[c].dtype == object]                  # object-dtype-check\ntotals = df.groupby(\"site\").agg(np.sum)                                       # np-reduction-in-agg\n"
    }
  ],
  "requires": {
    "tools": [
      "python3",
      "uv"
    ],
    "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/2",
    "verdict": "clean",
    "network": true,
    "scripts": [
      "pandas3_guard.py"
    ],
    "notes": "The scanner only reads .py and .ipynb files and prints findings; it never edits files and makes no network calls. Running the project with uv may download the pinned packages."
  },
  "featured": false,
  "deprecated": false,
  "last_checked": "2026-10-29",
  "published": "2026-10-29",
  "updated": "2026-10-29",
  "compatibility": {
    "claude-code": "native",
    "codex": "native",
    "cursor": "native",
    "gemini-cli": "native",
    "antigravity-cli": "native",
    "copilot": "native"
  },
  "counts": {
    "rules": 2,
    "skills": 1,
    "mcp": 0
  },
  "url": "https://setupbay.com/loadouts/pandas-3-upgrade-guard/",
  "api_url": "https://setupbay.com/api/loadouts/pandas-3-upgrade-guard.json",
  "verification": {
    "level": "verified-install",
    "status": "verified",
    "label": "Verified install",
    "verified": true,
    "checked_at": "2026-10-29T12:17:03Z",
    "commit": "92149c1d12ea85ca9bcf65b25e1a6877c9112547",
    "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: 6 files, 1 skill in .claude/skills/",
          "Codex CLI: 5 files, 1 skill in .agents/skills/",
          "Cursor: 6 files, 1 skill in .agents/skills/, 1 rule",
          "Gemini CLI: 6 files, 1 skill in .agents/skills/",
          "Antigravity CLI: 5 files, 1 skill in .agents/skills/",
          "GitHub Copilot: 5 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, hidden text or covert instructions",
        "details": [
          "Scanner intake-precheck/2: verdict clean; network no (listing says yes)"
        ]
      },
      {
        "id": "references",
        "name": "References",
        "status": "pass",
        "summary": "3 references between files resolve for every agent",
        "details": []
      },
      {
        "id": "size",
        "name": "Size limits",
        "status": "pass",
        "summary": "8 files, 18.5 KB total; largest skills/pandas-3-guard/scripts/pandas3_guard.py 8.0 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 SetupBay; text in the repository LICENSE",
        "details": []
      },
      {
        "id": "starter-build",
        "name": "Starter build",
        "status": "pass",
        "summary": "pandas3_guard.py passes a pandas 3 cleaning script, catches the deliberate chained write in its test, and flags every pandas 2 line in the skill's sample: 10 removed calls and lost writes as errors, 2 meaning changes as warnings. The pinned pandas 3.1.0 runs the script and its tests with ChainedAssi",
        "details": [
          "Applied 12 loadout files at every agent's paths",
          "✓ scanner: the pandas 3 script is clean: bash -c python3 .agents/skills/pandas-3-guard/scripts/pandas3_guard.py src; echo \"exit=$?\"",
          "✓ scanner: the test's deliberate chained write is caught: bash -c python3 .agents/skills/pandas-3-guard/scripts/pandas3_guard.py tests; echo \"exit=$?\"",
          "✓ scanner: the pandas 2 sample is flagged line by line: bash -c mkdir -p /tmp/pandas2-sample && cp .claude/skills/pandas-3-guard/references/pandas2-sample.py.txt /tmp/pandas2-sample/clean2.py && python3 .claude/skills/pandas-3-guard/scripts/pandas3_guard.py /tmp/pandas2-sample; echo \"exit=$?\"",
          "✓ pandas 3.1.0 runs the script: uv run --python 3.12 python src/clean.py",
          "✓ pytest with ChainedAssignmentError as an error: uv run --python 3.12 pytest -q -p no:cacheprovider -W error::pandas.errors.ChainedAssignmentError",
          "✓ pandas 3.1.0 refuses the sample's removed calls and drops the chained write: uv run --python 3.12 python legacy_probe.py",
          "Covers: The scanner's removed-API rules (applymap, append, fillna(method=), delim_whitespace, date_parser, offset aliases, errors='ignore') against real pandas 2 code",
          "Covers: Copy-on-write: the scanner's chained-write rules, pandas 3.1.0 leaving the frame unchanged, and ChainedAssignmentError raised as an error in the tests",
          "Covers: The str dtype: an object-dtype check is false for strings, and the scanner warns on it",
          "Covers: Not covered: chained writes on frames with names the scanner doesn't look for (the test run catches those)"
        ]
      }
    ]
  },
  "install": {
    "claude-code": {
      "files": [
        {
          "path": "AGENTS.md",
          "target": "AGENTS.md",
          "raw_url": "https://setupbay.com/raw/pandas-3-upgrade-guard/1.0.0/AGENTS.md",
          "role": "instructions"
        },
        {
          "path": "CLAUDE.md",
          "target": "CLAUDE.md",
          "raw_url": "https://setupbay.com/raw/pandas-3-upgrade-guard/1.0.0/CLAUDE.md",
          "role": "adapter"
        },
        {
          "path": "skills/pandas-3-guard/SKILL.md",
          "target": ".claude/skills/pandas-3-guard/SKILL.md",
          "raw_url": "https://setupbay.com/raw/pandas-3-upgrade-guard/1.0.0/skills/pandas-3-guard/SKILL.md",
          "role": "skill"
        },
        {
          "path": "skills/pandas-3-guard/scripts/pandas3_guard.py",
          "target": ".claude/skills/pandas-3-guard/scripts/pandas3_guard.py",
          "raw_url": "https://setupbay.com/raw/pandas-3-upgrade-guard/1.0.0/skills/pandas-3-guard/scripts/pandas3_guard.py",
          "role": "reference"
        },
        {
          "path": "skills/pandas-3-guard/references/rename-table.md",
          "target": ".claude/skills/pandas-3-guard/references/rename-table.md",
          "raw_url": "https://setupbay.com/raw/pandas-3-upgrade-guard/1.0.0/skills/pandas-3-guard/references/rename-table.md",
          "role": "reference"
        },
        {
          "path": "skills/pandas-3-guard/references/pandas2-sample.py.txt",
          "target": ".claude/skills/pandas-3-guard/references/pandas2-sample.py.txt",
          "raw_url": "https://setupbay.com/raw/pandas-3-upgrade-guard/1.0.0/skills/pandas-3-guard/references/pandas2-sample.py.txt",
          "role": "reference"
        }
      ],
      "manual_steps": [],
      "zip_url": "https://setupbay.com/download/pandas-3-upgrade-guard/claude-code.zip"
    },
    "codex": {
      "files": [
        {
          "path": "AGENTS.md",
          "target": "AGENTS.md",
          "raw_url": "https://setupbay.com/raw/pandas-3-upgrade-guard/1.0.0/AGENTS.md",
          "role": "instructions"
        },
        {
          "path": "skills/pandas-3-guard/SKILL.md",
          "target": ".agents/skills/pandas-3-guard/SKILL.md",
          "raw_url": "https://setupbay.com/raw/pandas-3-upgrade-guard/1.0.0/skills/pandas-3-guard/SKILL.md",
          "role": "skill"
        },
        {
          "path": "skills/pandas-3-guard/scripts/pandas3_guard.py",
          "target": ".agents/skills/pandas-3-guard/scripts/pandas3_guard.py",
          "raw_url": "https://setupbay.com/raw/pandas-3-upgrade-guard/1.0.0/skills/pandas-3-guard/scripts/pandas3_guard.py",
          "role": "reference"
        },
        {
          "path": "skills/pandas-3-guard/references/rename-table.md",
          "target": ".agents/skills/pandas-3-guard/references/rename-table.md",
          "raw_url": "https://setupbay.com/raw/pandas-3-upgrade-guard/1.0.0/skills/pandas-3-guard/references/rename-table.md",
          "role": "reference"
        },
        {
          "path": "skills/pandas-3-guard/references/pandas2-sample.py.txt",
          "target": ".agents/skills/pandas-3-guard/references/pandas2-sample.py.txt",
          "raw_url": "https://setupbay.com/raw/pandas-3-upgrade-guard/1.0.0/skills/pandas-3-guard/references/pandas2-sample.py.txt",
          "role": "reference"
        }
      ],
      "manual_steps": [],
      "zip_url": "https://setupbay.com/download/pandas-3-upgrade-guard/codex.zip"
    },
    "cursor": {
      "files": [
        {
          "path": "AGENTS.md",
          "target": "AGENTS.md",
          "raw_url": "https://setupbay.com/raw/pandas-3-upgrade-guard/1.0.0/AGENTS.md",
          "role": "instructions"
        },
        {
          "path": ".cursor/rules/pandas-3-only.mdc",
          "target": ".cursor/rules/pandas-3-only.mdc",
          "raw_url": "https://setupbay.com/raw/pandas-3-upgrade-guard/1.0.0/.cursor/rules/pandas-3-only.mdc",
          "role": "rules"
        },
        {
          "path": "skills/pandas-3-guard/SKILL.md",
          "target": ".agents/skills/pandas-3-guard/SKILL.md",
          "raw_url": "https://setupbay.com/raw/pandas-3-upgrade-guard/1.0.0/skills/pandas-3-guard/SKILL.md",
          "role": "skill"
        },
        {
          "path": "skills/pandas-3-guard/scripts/pandas3_guard.py",
          "target": ".agents/skills/pandas-3-guard/scripts/pandas3_guard.py",
          "raw_url": "https://setupbay.com/raw/pandas-3-upgrade-guard/1.0.0/skills/pandas-3-guard/scripts/pandas3_guard.py",
          "role": "reference"
        },
        {
          "path": "skills/pandas-3-guard/references/rename-table.md",
          "target": ".agents/skills/pandas-3-guard/references/rename-table.md",
          "raw_url": "https://setupbay.com/raw/pandas-3-upgrade-guard/1.0.0/skills/pandas-3-guard/references/rename-table.md",
          "role": "reference"
        },
        {
          "path": "skills/pandas-3-guard/references/pandas2-sample.py.txt",
          "target": ".agents/skills/pandas-3-guard/references/pandas2-sample.py.txt",
          "raw_url": "https://setupbay.com/raw/pandas-3-upgrade-guard/1.0.0/skills/pandas-3-guard/references/pandas2-sample.py.txt",
          "role": "reference"
        }
      ],
      "manual_steps": [],
      "zip_url": "https://setupbay.com/download/pandas-3-upgrade-guard/cursor.zip"
    },
    "gemini-cli": {
      "files": [
        {
          "path": "AGENTS.md",
          "target": "AGENTS.md",
          "raw_url": "https://setupbay.com/raw/pandas-3-upgrade-guard/1.0.0/AGENTS.md",
          "role": "instructions"
        },
        {
          "path": "GEMINI.md",
          "target": "GEMINI.md",
          "raw_url": "https://setupbay.com/raw/pandas-3-upgrade-guard/1.0.0/GEMINI.md",
          "role": "adapter"
        },
        {
          "path": "skills/pandas-3-guard/SKILL.md",
          "target": ".agents/skills/pandas-3-guard/SKILL.md",
          "raw_url": "https://setupbay.com/raw/pandas-3-upgrade-guard/1.0.0/skills/pandas-3-guard/SKILL.md",
          "role": "skill"
        },
        {
          "path": "skills/pandas-3-guard/scripts/pandas3_guard.py",
          "target": ".agents/skills/pandas-3-guard/scripts/pandas3_guard.py",
          "raw_url": "https://setupbay.com/raw/pandas-3-upgrade-guard/1.0.0/skills/pandas-3-guard/scripts/pandas3_guard.py",
          "role": "reference"
        },
        {
          "path": "skills/pandas-3-guard/references/rename-table.md",
          "target": ".agents/skills/pandas-3-guard/references/rename-table.md",
          "raw_url": "https://setupbay.com/raw/pandas-3-upgrade-guard/1.0.0/skills/pandas-3-guard/references/rename-table.md",
          "role": "reference"
        },
        {
          "path": "skills/pandas-3-guard/references/pandas2-sample.py.txt",
          "target": ".agents/skills/pandas-3-guard/references/pandas2-sample.py.txt",
          "raw_url": "https://setupbay.com/raw/pandas-3-upgrade-guard/1.0.0/skills/pandas-3-guard/references/pandas2-sample.py.txt",
          "role": "reference"
        }
      ],
      "manual_steps": [],
      "zip_url": "https://setupbay.com/download/pandas-3-upgrade-guard/gemini-cli.zip"
    },
    "antigravity-cli": {
      "files": [
        {
          "path": "AGENTS.md",
          "target": "AGENTS.md",
          "raw_url": "https://setupbay.com/raw/pandas-3-upgrade-guard/1.0.0/AGENTS.md",
          "role": "instructions"
        },
        {
          "path": "skills/pandas-3-guard/SKILL.md",
          "target": ".agents/skills/pandas-3-guard/SKILL.md",
          "raw_url": "https://setupbay.com/raw/pandas-3-upgrade-guard/1.0.0/skills/pandas-3-guard/SKILL.md",
          "role": "skill"
        },
        {
          "path": "skills/pandas-3-guard/scripts/pandas3_guard.py",
          "target": ".agents/skills/pandas-3-guard/scripts/pandas3_guard.py",
          "raw_url": "https://setupbay.com/raw/pandas-3-upgrade-guard/1.0.0/skills/pandas-3-guard/scripts/pandas3_guard.py",
          "role": "reference"
        },
        {
          "path": "skills/pandas-3-guard/references/rename-table.md",
          "target": ".agents/skills/pandas-3-guard/references/rename-table.md",
          "raw_url": "https://setupbay.com/raw/pandas-3-upgrade-guard/1.0.0/skills/pandas-3-guard/references/rename-table.md",
          "role": "reference"
        },
        {
          "path": "skills/pandas-3-guard/references/pandas2-sample.py.txt",
          "target": ".agents/skills/pandas-3-guard/references/pandas2-sample.py.txt",
          "raw_url": "https://setupbay.com/raw/pandas-3-upgrade-guard/1.0.0/skills/pandas-3-guard/references/pandas2-sample.py.txt",
          "role": "reference"
        }
      ],
      "manual_steps": [],
      "zip_url": "https://setupbay.com/download/pandas-3-upgrade-guard/antigravity-cli.zip"
    },
    "copilot": {
      "files": [
        {
          "path": "AGENTS.md",
          "target": "AGENTS.md",
          "raw_url": "https://setupbay.com/raw/pandas-3-upgrade-guard/1.0.0/AGENTS.md",
          "role": "instructions"
        },
        {
          "path": "skills/pandas-3-guard/SKILL.md",
          "target": ".agents/skills/pandas-3-guard/SKILL.md",
          "raw_url": "https://setupbay.com/raw/pandas-3-upgrade-guard/1.0.0/skills/pandas-3-guard/SKILL.md",
          "role": "skill"
        },
        {
          "path": "skills/pandas-3-guard/scripts/pandas3_guard.py",
          "target": ".agents/skills/pandas-3-guard/scripts/pandas3_guard.py",
          "raw_url": "https://setupbay.com/raw/pandas-3-upgrade-guard/1.0.0/skills/pandas-3-guard/scripts/pandas3_guard.py",
          "role": "reference"
        },
        {
          "path": "skills/pandas-3-guard/references/rename-table.md",
          "target": ".agents/skills/pandas-3-guard/references/rename-table.md",
          "raw_url": "https://setupbay.com/raw/pandas-3-upgrade-guard/1.0.0/skills/pandas-3-guard/references/rename-table.md",
          "role": "reference"
        },
        {
          "path": "skills/pandas-3-guard/references/pandas2-sample.py.txt",
          "target": ".agents/skills/pandas-3-guard/references/pandas2-sample.py.txt",
          "raw_url": "https://setupbay.com/raw/pandas-3-upgrade-guard/1.0.0/skills/pandas-3-guard/references/pandas2-sample.py.txt",
          "role": "reference"
        }
      ],
      "manual_steps": [],
      "zip_url": "https://setupbay.com/download/pandas-3-upgrade-guard/copilot.zip"
    }
  },
  "source_zip_url": "https://setupbay.com/download/pandas-3-upgrade-guard/source.zip",
  "claude_code_plugin": {
    "name": "pandas-3-upgrade-guard",
    "marketplace_url": "https://setupbay.com/marketplace.json",
    "install": [
      "/plugin marketplace add https://setupbay.com/marketplace.json",
      "/plugin install pandas-3-upgrade-guard@setupbay"
    ],
    "zip_url": "https://setupbay.com/plugins/pandas-3-upgrade-guard.zip",
    "sha256": "f3ad66c8643583d605881e6c74eb4eb061dffeab89db2afb03d0b8efb50bd962",
    "carries": [
      "skills"
    ],
    "note": "Plugins do not carry project rules (AGENTS.md, CLAUDE.md); install those from install.claude-code."
  }
}