$npx -y skills add jsturtevant/copilot-codeact-plugin --skill monty-codeactCodeAct via Monty. Use when looping over many files (8+), cross-referencing results from multiple sources, aggregating data across directories, or chaining 5+ dependent tool calls. Collapses N round-trips into one sandboxed Python run via scripts/codeact.py. NOT beneficial for <5
| 1 | # Monty CodeAct |
| 2 | |
| 3 | Collapse multi-step tool chains into a single sandboxed Python execution using |
| 4 | [Pydantic Monty](https://github.com/pydantic/monty) — a minimal, secure Python |
| 5 | interpreter written in Rust with sub-microsecond startup. |
| 6 | |
| 7 | ## Syntax |
| 8 | |
| 9 | Tools are called as regular Python functions with keyword arguments: |
| 10 | |
| 11 | ```python |
| 12 | content = view(path="src/main.py") |
| 13 | files = glob(pattern="**/*.py") |
| 14 | hits = grep(pattern="TODO", paths="src") |
| 15 | result = bash(command="git log --oneline -5") |
| 16 | edit(path="config.json", old_str='"debug": false', new_str='"debug": true') |
| 17 | ``` |
| 18 | |
| 19 | Chain by sequencing: |
| 20 | |
| 21 | ```python |
| 22 | for f in glob(pattern="**/*.py", paths="src"): |
| 23 | content = view(path=f) |
| 24 | if "TODO" in content: |
| 25 | print(f + ": " + str(content.count("TODO")) + " TODOs") |
| 26 | ``` |
| 27 | |
| 28 | ## Discover tools |
| 29 | |
| 30 | ```bash |
| 31 | python3 scripts/codeact.py --discover # JSON manifest |
| 32 | python3 scripts/codeact.py --instructions # LLM-ready reference |
| 33 | ``` |
| 34 | |
| 35 | Tools are auto-detected based on what's installed on the host. |
| 36 | |
| 37 | ## Execute |
| 38 | |
| 39 | ```bash |
| 40 | uv run --with pydantic-monty python3 scripts/codeact.py --auto --workspace . --code '...' |
| 41 | ``` |
| 42 | |
| 43 | Output: `{"stdout": "...", "stderr": "...", "return_value": null, "success": true}` |
| 44 | |
| 45 | ## Monty limitations |
| 46 | |
| 47 | Monty runs a subset of Python. **Will error on:** |
| 48 | - **Classes** — no `class` keyword at all |
| 49 | - **Match statements** — no `match`/`case` |
| 50 | - **f-string format specs** — `f"{x:<10}"`, `f"{x:>5}"`, `f"{x:.2f}"` all fail |
| 51 | - **`str.format()`** — `"{:<10}".format(x)` fails |
| 52 | - **`str.startswith()` with tuple** — use `or` instead |
| 53 | - **Set comprehensions** — build with list + `in` checks |
| 54 | - **Third-party imports** — only stdlib subset |
| 55 | - **Most stdlib** — only: json, re, datetime, sys, os.environ (no os.path, no os.walk) |
| 56 | - **Brace expansion in glob** — `glob(pattern="src/{db,services}/**/*.py")` is supported. |
| 57 | |
| 58 | **Sandbox tool return types** (getting these wrong causes retries): |
| 59 | - `glob(pattern=...)` → **list of strings** like `["src/app.py", "src/utils.py"]` |
| 60 | - `view(path=...)` → **string** (file content) |
| 61 | - `mcp_call(server=..., tool=..., ...)` → **string** |
| 62 | - `bash(command=...)` → **dict** with keys `stdout`, `stderr`, `returncode` |
| 63 | |
| 64 | **Key usage patterns:** |
| 65 | - **Do NOT scout first.** `glob()` and `view()` are in the sandbox — discover |
| 66 | files inside your codeact program, not with separate tool calls before it. |
| 67 | - **One program, one bash call.** Do not run multiple codeact invocations. |
| 68 | If the first one fails, fix the bug in the program, don't add a scouting step. |
| 69 | - **Wrap file reads in try/except** so one bad file doesn't abort the run. |
| 70 | - Use `for f in glob(pattern="**/*.py"):` to iterate files. No os.walk or os.path. |
| 71 | |
| 72 | **Output formatting workaround** (use instead of format specs): |
| 73 | ```python |
| 74 | def pad(s, w): |
| 75 | s = str(s) |
| 76 | return s + " " * max(0, w - len(s)) |
| 77 | ``` |
| 78 | |
| 79 | **Tips:** Use `chr(10)` for newlines. Use `import json` explicitly. |
| 80 | Use string concatenation (`+`) or simple f-strings (`f"count: {n}"`). |
| 81 | |
| 82 | ## Trust model |
| 83 | |
| 84 | Sandboxed code can only reach the outside world through registered tool |
| 85 | functions. Use `--workspace` to restrict file tools to a directory tree. |
| 86 | |
| 87 | ## Prerequisites |
| 88 | |
| 89 | - Python 3.10+ |
| 90 | - `uv` (recommended) or `pip install pydantic-monty` |
| 91 | |
| 92 | ## References |
| 93 | |
| 94 | - [references/tool-patterns.md](references/tool-patterns.md) |