modal-uv 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. modal_uv-0.2.0/.github/workflows/release.yml +167 -0
  2. modal_uv-0.2.0/.gitignore +12 -0
  3. modal_uv-0.2.0/.python-version +1 -0
  4. modal_uv-0.2.0/AGENTS.md +23 -0
  5. modal_uv-0.2.0/PKG-INFO +232 -0
  6. modal_uv-0.2.0/README.md +218 -0
  7. modal_uv-0.2.0/docs/superpowers/plans/2026-07-06-modal-uv-global-cli.md +550 -0
  8. modal_uv-0.2.0/docs/superpowers/plans/2026-07-09-modal-uv-exec.md +358 -0
  9. modal_uv-0.2.0/docs/superpowers/specs/2026-06-06-modal-uv-connection-daemon-design.md +168 -0
  10. modal_uv-0.2.0/docs/superpowers/specs/2026-06-06-modal-uv-direct-sync-design.md +196 -0
  11. modal_uv-0.2.0/docs/superpowers/specs/2026-06-06-modal-uv-preload-imports-design.md +74 -0
  12. modal_uv-0.2.0/docs/superpowers/specs/2026-06-06-modal-uv-python-in-process-design.md +120 -0
  13. modal_uv-0.2.0/docs/superpowers/specs/2026-07-06-modal-uv-global-cli-design.md +188 -0
  14. modal_uv-0.2.0/docs/superpowers/specs/2026-07-09-modal-uv-exec-design.md +138 -0
  15. modal_uv-0.2.0/example-repo/.gitignore +6 -0
  16. modal_uv-0.2.0/example-repo/modal-uv.yaml +20 -0
  17. modal_uv-0.2.0/example-repo/pyproject.toml +21 -0
  18. modal_uv-0.2.0/example-repo/src/example_pkg/__init__.py +1 -0
  19. modal_uv-0.2.0/example-repo/src/example_pkg/main.py +9 -0
  20. modal_uv-0.2.0/example-repo/tests/test_main.py +9 -0
  21. modal_uv-0.2.0/example-repo/uv.lock +79 -0
  22. modal_uv-0.2.0/pyproject.toml +59 -0
  23. modal_uv-0.2.0/skills/use-modal-uv/SKILL.md +183 -0
  24. modal_uv-0.2.0/src/modal_uv/__init__.py +5 -0
  25. modal_uv-0.2.0/src/modal_uv/__main__.py +7 -0
  26. modal_uv-0.2.0/src/modal_uv/app.py +147 -0
  27. modal_uv-0.2.0/src/modal_uv/cli.py +793 -0
  28. modal_uv-0.2.0/src/modal_uv/client.py +113 -0
  29. modal_uv-0.2.0/src/modal_uv/config.py +271 -0
  30. modal_uv-0.2.0/src/modal_uv/daemon.py +170 -0
  31. modal_uv-0.2.0/src/modal_uv/deployment.py +216 -0
  32. modal_uv-0.2.0/src/modal_uv/paths.py +47 -0
  33. modal_uv-0.2.0/src/modal_uv/skill.py +83 -0
  34. modal_uv-0.2.0/src/modal_uv/sync.py +216 -0
  35. modal_uv-0.2.0/tests/test_modal_uv_app.py +294 -0
  36. modal_uv-0.2.0/tests/test_modal_uv_cli.py +1161 -0
  37. modal_uv-0.2.0/tests/test_modal_uv_config.py +368 -0
  38. modal_uv-0.2.0/tests/test_modal_uv_daemon.py +242 -0
  39. modal_uv-0.2.0/tests/test_modal_uv_deployment.py +374 -0
  40. modal_uv-0.2.0/tests/test_modal_uv_skill.py +113 -0
  41. modal_uv-0.2.0/tests/test_modal_uv_sync.py +150 -0
  42. modal_uv-0.2.0/uv.lock +1350 -0
@@ -0,0 +1,167 @@
1
+ name: Release
2
+
3
+ on:
4
+ pull_request:
5
+ push:
6
+ branches:
7
+ - main
8
+
9
+ permissions:
10
+ contents: read
11
+
12
+ jobs:
13
+ checks:
14
+ name: Checks
15
+ runs-on: ubuntu-latest
16
+
17
+ steps:
18
+ - name: Checkout
19
+ uses: actions/checkout@v6
20
+
21
+ - name: Install uv
22
+ id: setup-uv
23
+ uses: astral-sh/setup-uv@v8.1.0
24
+ with:
25
+ python-version: "3.12"
26
+ enable-cache: true
27
+ # Keep one stable uv cache per OS/Python so first PR runs can restore
28
+ # the cache populated by main. uv's cache is content-addressed; stale
29
+ # entries are safe, and missing artifacts are fetched during sync.
30
+ cache-dependency-glob: ""
31
+ cache-suffix: modal-uv-ci
32
+ restore-cache: true
33
+ save-cache: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
34
+
35
+ - name: Show uv cache status
36
+ run: |
37
+ echo "uv cache key: ${{ steps.setup-uv.outputs.cache-key }}"
38
+ echo "uv cache hit: ${{ steps.setup-uv.outputs.cache-hit }}"
39
+
40
+ - name: Install dependencies
41
+ run: uv sync --frozen --dev
42
+
43
+ - name: Check formatting
44
+ run: uv run ruff format --check .
45
+
46
+ - name: Type check
47
+ run: uv run ty check
48
+
49
+ - name: Lint
50
+ run: uv run ruff check .
51
+
52
+ - name: Build package
53
+ run: uv build
54
+
55
+ - name: Upload distributions
56
+ if: github.event_name == 'push' && github.ref == 'refs/heads/main'
57
+ uses: actions/upload-artifact@v4
58
+ with:
59
+ name: python-distributions
60
+ path: dist/
61
+ if-no-files-found: error
62
+
63
+ release-gate:
64
+ name: Release gate
65
+ runs-on: ubuntu-latest
66
+ needs: checks
67
+ if: github.event_name == 'push' && github.ref == 'refs/heads/main'
68
+ permissions:
69
+ contents: read
70
+
71
+ outputs:
72
+ should_publish: ${{ steps.release.outputs.should_publish }}
73
+ version: ${{ steps.release.outputs.version }}
74
+ tag: ${{ steps.release.outputs.tag }}
75
+
76
+ steps:
77
+ - name: Checkout
78
+ uses: actions/checkout@v6
79
+ with:
80
+ fetch-depth: 0
81
+
82
+ - name: Detect version bump
83
+ id: release
84
+ shell: bash
85
+ run: |
86
+ set -euo pipefail
87
+
88
+ version_at() {
89
+ local rev="$1"
90
+ python - "$rev" <<'PY'
91
+ import subprocess
92
+ import sys
93
+ import tomllib
94
+
95
+ rev = sys.argv[1]
96
+ data = subprocess.check_output(["git", "show", f"{rev}:pyproject.toml"])
97
+ print(tomllib.loads(data.decode())["project"]["version"])
98
+ PY
99
+ }
100
+
101
+ current_version="$(version_at HEAD)"
102
+ if ! git rev-parse --verify --quiet HEAD^ >/dev/null; then
103
+ echo "Initial commit has no parent; skipping publish."
104
+ echo "version=${current_version}" >> "$GITHUB_OUTPUT"
105
+ echo "tag=v${current_version}" >> "$GITHUB_OUTPUT"
106
+ echo "should_publish=false" >> "$GITHUB_OUTPUT"
107
+ exit 0
108
+ fi
109
+
110
+ previous_version="$(version_at HEAD^)"
111
+ tag="v${current_version}"
112
+
113
+ echo "version=${current_version}" >> "$GITHUB_OUTPUT"
114
+ echo "tag=${tag}" >> "$GITHUB_OUTPUT"
115
+
116
+ if [[ "${current_version}" == "${previous_version}" ]]; then
117
+ echo "Version unchanged (${current_version}); skipping publish."
118
+ echo "should_publish=false" >> "$GITHUB_OUTPUT"
119
+ exit 0
120
+ fi
121
+
122
+ git fetch --tags --quiet
123
+ if git rev-parse --verify --quiet "refs/tags/${tag}" >/dev/null; then
124
+ echo "Tag ${tag} already exists; skipping publish."
125
+ echo "should_publish=false" >> "$GITHUB_OUTPUT"
126
+ exit 0
127
+ fi
128
+
129
+ echo "Version changed ${previous_version} -> ${current_version}; publishing ${tag}."
130
+ echo "should_publish=true" >> "$GITHUB_OUTPUT"
131
+
132
+ publish:
133
+ name: Publish to PyPI
134
+ runs-on: ubuntu-latest
135
+ needs: release-gate
136
+ if: needs.release-gate.outputs.should_publish == 'true'
137
+ environment:
138
+ name: pypi
139
+ url: https://pypi.org/p/modal-uv
140
+ permissions:
141
+ contents: write
142
+ id-token: write
143
+
144
+ steps:
145
+ - name: Checkout
146
+ uses: actions/checkout@v6
147
+ with:
148
+ fetch-depth: 0
149
+
150
+ - name: Download distributions
151
+ uses: actions/download-artifact@v5
152
+ with:
153
+ name: python-distributions
154
+ path: dist/
155
+
156
+ - name: Publish package distributions to PyPI
157
+ uses: pypa/gh-action-pypi-publish@release/v1
158
+
159
+ - name: Create and push tag
160
+ env:
161
+ TAG: ${{ needs.release-gate.outputs.tag }}
162
+ run: |
163
+ set -euo pipefail
164
+ git config user.name "github-actions[bot]"
165
+ git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
166
+ git tag -a "${TAG}" -m "Release ${TAG}"
167
+ git push origin "${TAG}"
@@ -0,0 +1,12 @@
1
+ .venv/
2
+ .env
3
+ .pytest_cache/
4
+ .ruff_cache/
5
+ .mypy_cache/
6
+ .mypy/
7
+ __pycache__/
8
+ *.py[cod]
9
+ dist/
10
+ build/
11
+ *.egg-info/
12
+ .modal-uv/
@@ -0,0 +1 @@
1
+ 3.12
@@ -0,0 +1,23 @@
1
+ # AGENTS.md
2
+
3
+ ## Purpose
4
+
5
+ `modal-uv` exists to let coding agents and developers run ordinary `uv` workflows on Modal without turning every project into a Modal application.
6
+
7
+ The tool is for moments when local development is convenient, but local hardware is not enough: GPU tests, model experiments, long-running jobs, or artifact-producing scripts should be runnable from the same repo, with the same `uv` command shape, and without manual deployment ceremony.
8
+
9
+ ## Philosophy
10
+
11
+ - Preserve the local development loop. A project should stay a normal Python project first.
12
+ - Make remote execution feel boring. Running on Modal should be a transport choice, not a rewrite.
13
+ - Prefer explicit project configuration over hidden global state.
14
+ - Keep generated/runtime state out of the source tree's meaningful files.
15
+ - Optimize for coding agents as first-class users: commands should be discoverable, repeatable, and safe to diagnose.
16
+ - Separate fast iteration from deployment. Source changes should sync directly; deployment should happen only when the runtime shape changes.
17
+ - Treat Modal authentication as user-level infrastructure, not repo-local project state.
18
+
19
+ ## Problem It Solves
20
+
21
+ Without `modal-uv`, using cloud GPUs from an existing repo usually means adding Modal-specific entrypoints, managing deploy commands manually, copying files around, or teaching each coding agent a custom workflow.
22
+
23
+ `modal-uv` aims to provide one small bridge: keep the repo local, run the command remotely, sync only what changed, persist useful outputs in a Modal Volume, and return enough execution information for agents and humans to inspect logs or stop work.
@@ -0,0 +1,232 @@
1
+ Metadata-Version: 2.4
2
+ Name: modal-uv
3
+ Version: 0.2.0
4
+ Summary: Run uv commands on Modal.com with GPU and direct file sync
5
+ Requires-Python: >=3.12
6
+ Requires-Dist: fastapi>=0.115.0
7
+ Requires-Dist: httpx>=0.28.0
8
+ Requires-Dist: modal>=1.4.3
9
+ Requires-Dist: pathspec>=0.12.1
10
+ Requires-Dist: pydantic-settings[yaml]>=2.7.0
11
+ Requires-Dist: typer>=0.15.0
12
+ Requires-Dist: uvicorn>=0.34.0
13
+ Description-Content-Type: text/markdown
14
+
15
+ # modal-uv
16
+
17
+ Run `uv` commands on Modal.com with GPU support, direct file sync, async execution IDs, logs, abort, and persistent Modal volumes.
18
+
19
+ ## Installation
20
+
21
+ ### Option 1: Coding Agent (Recommended)
22
+
23
+ Paste this prompt to your coding agent (opencode, Claude Code, Gemini CLI, etc.):
24
+
25
+ ```
26
+ Install modal-uv globally and set it up:
27
+ 1. Run: pip install modal-uv
28
+ 2. Run: modal-uv onboard
29
+ - This opens a browser for Modal OAuth authentication
30
+ - Complete the auth flow in the browser
31
+ - It also installs the use-modal-uv skill to detected coding agents
32
+ 3. In the project repo, run: modal-uv init
33
+ - This creates modal-uv.yaml with defaults if missing
34
+ - It creates .modal-uv/ for generated state and adds it to .gitignore
35
+ 4. Edit modal-uv.yaml to set app_name, gpu, and volume.name for this project
36
+ 5. Run: modal-uv doctor
37
+ - This checks modal-uv health: auth state, volume existence, app deployment, daemon status
38
+ - Does not wake the container
39
+ ```
40
+
41
+ ### Option 2: Manual Getting Started
42
+
43
+ ```bash
44
+ pip install modal-uv
45
+ ```
46
+
47
+ Authenticate with Modal (opens browser for OAuth):
48
+
49
+ ```bash
50
+ modal-uv onboard
51
+ ```
52
+
53
+ This also installs the `use-modal-uv` skill to detected coding agents (`~/.config/opencode/`, `~/.claude/`, `~/.agents/`).
54
+
55
+ In your project repo, initialize modal-uv files:
56
+
57
+ ```bash
58
+ modal-uv init
59
+ ```
60
+
61
+ This creates `modal-uv.yaml` with defaults (using the directory name as `app_name`) if missing, and creates `.modal-uv/` for generated state with a `.gitignore` entry.
62
+
63
+ Edit `modal-uv.yaml` to configure your app:
64
+
65
+ ```yaml
66
+ app_name: "my-project"
67
+ gpu: "T4"
68
+ work_dir: "/tmp/work"
69
+
70
+ volumes:
71
+ - name: "modal-uv-cache"
72
+ mount_path: "/mnt/volume"
73
+ commit_interval_seconds: 30
74
+
75
+ env: {}
76
+
77
+ runtime:
78
+ scaledown_window_seconds: 300
79
+
80
+ image:
81
+ python_version: "3.12"
82
+ base_image: "python:3.12-slim"
83
+
84
+ sync:
85
+ ignore:
86
+ - "data/**"
87
+ - "*.ckpt"
88
+ ```
89
+
90
+ Then run commands on Modal:
91
+
92
+ ```bash
93
+ modal-uv run -- pytest
94
+ ```
95
+
96
+ ## Repository Configuration
97
+
98
+ `modal-uv.yaml` at the repository root is discovered by walking up from the current directory, similar to `git` or `uv`.
99
+
100
+ Fields:
101
+
102
+ - `app_name`: Modal app name (required)
103
+ - `gpu`: GPU type, such as `T4`, `A10G`, `A100`, `H100`, or `L4`
104
+ - `work_dir`: Working directory inside the Modal container (default: `/root/work`)
105
+ - `volumes`: Modal volumes to mount in the container; may be empty or omitted
106
+ - `volumes[].name`: Modal volume name
107
+ - `volumes[].mount_path`: Mount path in the container (default: `/root/.cache`)
108
+ - `volumes[].commit_interval_seconds`: Periodic Modal Volume commit interval while a command runs (default: `30`)
109
+ - `env`: Extra container environment variables merged over modal-uv defaults
110
+ - `runtime.scaledown_window_seconds`: Modal worker scaledown window (default: `300`)
111
+ - `runtime.exec`: Optional shell executable for `modal-uv exec`; if omitted, the remote Worker uses `$SHELL`, then `/bin/sh`
112
+ - `image.python_version`: Python version (default: `3.12`)
113
+ - `image.base_image`: Base Docker image (default: `python:3.12-slim`)
114
+ - `sync.ignore`: gitignore-style patterns excluded from direct sync
115
+
116
+ ## Repo-Local State
117
+
118
+ `modal-uv` creates `.modal-uv/` at the repo root for generated/runtime files and ensures the root `.gitignore` ignores it.
119
+
120
+ Examples of generated files:
121
+
122
+ - `.modal-uv/deployment.py`
123
+ - `.modal-uv/daemon.pid`
124
+ - `.modal-uv/daemon.sock`
125
+ - `.modal-uv/daemon.log`
126
+
127
+ `.modal-uv/` is not normally synced to the Modal work directory.
128
+
129
+ ## Sync And Deployment
130
+
131
+ `modal-uv run` and `modal-uv exec` scan local files, apply built-in ignores plus `sync.ignore`, ask the warm Modal container which files are missing or stale, upload only those files, spawn the execution, print the Modal function call ID, and return immediately.
132
+
133
+ The detached daemon lazily ensures the Modal app is deployed before running work. It generates `.modal-uv/deployment.py` and redeploys when the deployment fingerprint changes. The fingerprint includes the deployment template, Modal-relevant config values, and the repo `pyproject.toml` SHA256 when present.
134
+
135
+ During a running command, `modal-uv` periodically commits the Modal Volume every `volume.commit_interval_seconds` seconds, plus one final commit after the command exits. This persists outputs and checkpoints written under the mounted volume during long runs.
136
+
137
+ Ordinary source changes do not redeploy the app; they are handled by direct sync.
138
+
139
+ Modal authentication remains Modal's normal user-global authentication. `modal-uv` does not create repo-local auth files.
140
+
141
+ ## Commands
142
+
143
+ Run `uv` commands on Modal:
144
+
145
+ ```bash
146
+ modal-uv run -- pytest
147
+ modal-uv run -- python -m lab
148
+ modal-uv run -- python train.py --epochs 10
149
+ ```
150
+
151
+ Tail or abort a spawned execution:
152
+
153
+ ```bash
154
+ modal-uv logs fc-...
155
+ modal-uv abort fc-...
156
+ ```
157
+
158
+ Run shell-style commands in the synced Modal work directory:
159
+
160
+ ```bash
161
+ modal-uv exec -- nvidia-smi
162
+ modal-uv exec -- 'ls -la && pwd'
163
+ ```
164
+
165
+ Open Modal's native interactive shell through the passthrough command:
166
+
167
+ ```bash
168
+ modal-uv modal -- shell
169
+ ```
170
+
171
+ Show Modal app status:
172
+
173
+ ```bash
174
+ modal-uv status
175
+ ```
176
+
177
+ Check the configured Modal volume directly:
178
+
179
+ ```bash
180
+ modal-uv modal -- volume ls modal-uv-cache
181
+ ```
182
+
183
+ Initialize or align modal-uv files in the current directory:
184
+
185
+ ```bash
186
+ modal-uv init
187
+ ```
188
+
189
+ Run any Modal CLI command through the modal-uv environment:
190
+
191
+ ```bash
192
+ modal-uv modal -- app list
193
+ modal-uv modal -- volume ls
194
+ ```
195
+
196
+ Daemon helpers:
197
+
198
+ ```bash
199
+ modal-uv daemon-status
200
+ modal-uv daemon-stop
201
+ ```
202
+
203
+ ## Updates
204
+
205
+ Upgrade modal-uv and refresh the skill on all detected agents:
206
+
207
+ ```bash
208
+ modal-uv update
209
+ ```
210
+
211
+ Install the skill to a specific agent (`opencode`, `claude`, `agents`) or an explicit directory path:
212
+
213
+ ```bash
214
+ modal-uv install-skill opencode
215
+ modal-uv install-skill /path/to/skills/dir
216
+ ```
217
+
218
+ Detected agents are based on which config directories exist (`~/.config/opencode/`, `~/.claude/`, `~/.agents/`).
219
+
220
+ Use `--config` or `-c` to specify a custom config file:
221
+
222
+ ```bash
223
+ modal-uv run --config path/to/modal-uv.yaml -- pytest
224
+ ```
225
+
226
+ ## Checks
227
+
228
+ ```bash
229
+ uv run ruff check .
230
+ uv run ruff format --check .
231
+ uv run pytest
232
+ ```
@@ -0,0 +1,218 @@
1
+ # modal-uv
2
+
3
+ Run `uv` commands on Modal.com with GPU support, direct file sync, async execution IDs, logs, abort, and persistent Modal volumes.
4
+
5
+ ## Installation
6
+
7
+ ### Option 1: Coding Agent (Recommended)
8
+
9
+ Paste this prompt to your coding agent (opencode, Claude Code, Gemini CLI, etc.):
10
+
11
+ ```
12
+ Install modal-uv globally and set it up:
13
+ 1. Run: pip install modal-uv
14
+ 2. Run: modal-uv onboard
15
+ - This opens a browser for Modal OAuth authentication
16
+ - Complete the auth flow in the browser
17
+ - It also installs the use-modal-uv skill to detected coding agents
18
+ 3. In the project repo, run: modal-uv init
19
+ - This creates modal-uv.yaml with defaults if missing
20
+ - It creates .modal-uv/ for generated state and adds it to .gitignore
21
+ 4. Edit modal-uv.yaml to set app_name, gpu, and volume.name for this project
22
+ 5. Run: modal-uv doctor
23
+ - This checks modal-uv health: auth state, volume existence, app deployment, daemon status
24
+ - Does not wake the container
25
+ ```
26
+
27
+ ### Option 2: Manual Getting Started
28
+
29
+ ```bash
30
+ pip install modal-uv
31
+ ```
32
+
33
+ Authenticate with Modal (opens browser for OAuth):
34
+
35
+ ```bash
36
+ modal-uv onboard
37
+ ```
38
+
39
+ This also installs the `use-modal-uv` skill to detected coding agents (`~/.config/opencode/`, `~/.claude/`, `~/.agents/`).
40
+
41
+ In your project repo, initialize modal-uv files:
42
+
43
+ ```bash
44
+ modal-uv init
45
+ ```
46
+
47
+ This creates `modal-uv.yaml` with defaults (using the directory name as `app_name`) if missing, and creates `.modal-uv/` for generated state with a `.gitignore` entry.
48
+
49
+ Edit `modal-uv.yaml` to configure your app:
50
+
51
+ ```yaml
52
+ app_name: "my-project"
53
+ gpu: "T4"
54
+ work_dir: "/tmp/work"
55
+
56
+ volumes:
57
+ - name: "modal-uv-cache"
58
+ mount_path: "/mnt/volume"
59
+ commit_interval_seconds: 30
60
+
61
+ env: {}
62
+
63
+ runtime:
64
+ scaledown_window_seconds: 300
65
+
66
+ image:
67
+ python_version: "3.12"
68
+ base_image: "python:3.12-slim"
69
+
70
+ sync:
71
+ ignore:
72
+ - "data/**"
73
+ - "*.ckpt"
74
+ ```
75
+
76
+ Then run commands on Modal:
77
+
78
+ ```bash
79
+ modal-uv run -- pytest
80
+ ```
81
+
82
+ ## Repository Configuration
83
+
84
+ `modal-uv.yaml` at the repository root is discovered by walking up from the current directory, similar to `git` or `uv`.
85
+
86
+ Fields:
87
+
88
+ - `app_name`: Modal app name (required)
89
+ - `gpu`: GPU type, such as `T4`, `A10G`, `A100`, `H100`, or `L4`
90
+ - `work_dir`: Working directory inside the Modal container (default: `/root/work`)
91
+ - `volumes`: Modal volumes to mount in the container; may be empty or omitted
92
+ - `volumes[].name`: Modal volume name
93
+ - `volumes[].mount_path`: Mount path in the container (default: `/root/.cache`)
94
+ - `volumes[].commit_interval_seconds`: Periodic Modal Volume commit interval while a command runs (default: `30`)
95
+ - `env`: Extra container environment variables merged over modal-uv defaults
96
+ - `runtime.scaledown_window_seconds`: Modal worker scaledown window (default: `300`)
97
+ - `runtime.exec`: Optional shell executable for `modal-uv exec`; if omitted, the remote Worker uses `$SHELL`, then `/bin/sh`
98
+ - `image.python_version`: Python version (default: `3.12`)
99
+ - `image.base_image`: Base Docker image (default: `python:3.12-slim`)
100
+ - `sync.ignore`: gitignore-style patterns excluded from direct sync
101
+
102
+ ## Repo-Local State
103
+
104
+ `modal-uv` creates `.modal-uv/` at the repo root for generated/runtime files and ensures the root `.gitignore` ignores it.
105
+
106
+ Examples of generated files:
107
+
108
+ - `.modal-uv/deployment.py`
109
+ - `.modal-uv/daemon.pid`
110
+ - `.modal-uv/daemon.sock`
111
+ - `.modal-uv/daemon.log`
112
+
113
+ `.modal-uv/` is not normally synced to the Modal work directory.
114
+
115
+ ## Sync And Deployment
116
+
117
+ `modal-uv run` and `modal-uv exec` scan local files, apply built-in ignores plus `sync.ignore`, ask the warm Modal container which files are missing or stale, upload only those files, spawn the execution, print the Modal function call ID, and return immediately.
118
+
119
+ The detached daemon lazily ensures the Modal app is deployed before running work. It generates `.modal-uv/deployment.py` and redeploys when the deployment fingerprint changes. The fingerprint includes the deployment template, Modal-relevant config values, and the repo `pyproject.toml` SHA256 when present.
120
+
121
+ During a running command, `modal-uv` periodically commits the Modal Volume every `volume.commit_interval_seconds` seconds, plus one final commit after the command exits. This persists outputs and checkpoints written under the mounted volume during long runs.
122
+
123
+ Ordinary source changes do not redeploy the app; they are handled by direct sync.
124
+
125
+ Modal authentication remains Modal's normal user-global authentication. `modal-uv` does not create repo-local auth files.
126
+
127
+ ## Commands
128
+
129
+ Run `uv` commands on Modal:
130
+
131
+ ```bash
132
+ modal-uv run -- pytest
133
+ modal-uv run -- python -m lab
134
+ modal-uv run -- python train.py --epochs 10
135
+ ```
136
+
137
+ Tail or abort a spawned execution:
138
+
139
+ ```bash
140
+ modal-uv logs fc-...
141
+ modal-uv abort fc-...
142
+ ```
143
+
144
+ Run shell-style commands in the synced Modal work directory:
145
+
146
+ ```bash
147
+ modal-uv exec -- nvidia-smi
148
+ modal-uv exec -- 'ls -la && pwd'
149
+ ```
150
+
151
+ Open Modal's native interactive shell through the passthrough command:
152
+
153
+ ```bash
154
+ modal-uv modal -- shell
155
+ ```
156
+
157
+ Show Modal app status:
158
+
159
+ ```bash
160
+ modal-uv status
161
+ ```
162
+
163
+ Check the configured Modal volume directly:
164
+
165
+ ```bash
166
+ modal-uv modal -- volume ls modal-uv-cache
167
+ ```
168
+
169
+ Initialize or align modal-uv files in the current directory:
170
+
171
+ ```bash
172
+ modal-uv init
173
+ ```
174
+
175
+ Run any Modal CLI command through the modal-uv environment:
176
+
177
+ ```bash
178
+ modal-uv modal -- app list
179
+ modal-uv modal -- volume ls
180
+ ```
181
+
182
+ Daemon helpers:
183
+
184
+ ```bash
185
+ modal-uv daemon-status
186
+ modal-uv daemon-stop
187
+ ```
188
+
189
+ ## Updates
190
+
191
+ Upgrade modal-uv and refresh the skill on all detected agents:
192
+
193
+ ```bash
194
+ modal-uv update
195
+ ```
196
+
197
+ Install the skill to a specific agent (`opencode`, `claude`, `agents`) or an explicit directory path:
198
+
199
+ ```bash
200
+ modal-uv install-skill opencode
201
+ modal-uv install-skill /path/to/skills/dir
202
+ ```
203
+
204
+ Detected agents are based on which config directories exist (`~/.config/opencode/`, `~/.claude/`, `~/.agents/`).
205
+
206
+ Use `--config` or `-c` to specify a custom config file:
207
+
208
+ ```bash
209
+ modal-uv run --config path/to/modal-uv.yaml -- pytest
210
+ ```
211
+
212
+ ## Checks
213
+
214
+ ```bash
215
+ uv run ruff check .
216
+ uv run ruff format --check .
217
+ uv run pytest
218
+ ```