uv-kernel-manager 0.1.1a1__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.
@@ -0,0 +1,32 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main, develop]
6
+ pull_request:
7
+ branches: [main, develop]
8
+ workflow_dispatch:
9
+
10
+ permissions:
11
+ contents: read
12
+
13
+ jobs:
14
+ test-and-build:
15
+ name: Test and Build Validation
16
+ runs-on: ubuntu-latest
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+ - uses: astral-sh/setup-uv@v6
20
+ - name: Set up the project environment
21
+ run: uv sync --locked --group dev
22
+ - name: Run tests
23
+ run: uv run --group dev pytest
24
+ - name: Build distributions
25
+ run: uv build --no-sources --clear
26
+ - name: Validate distribution metadata
27
+ run: uv run --group dev twine check dist/*
28
+ - name: Upload distributions
29
+ uses: actions/upload-artifact@v4
30
+ with:
31
+ name: dist
32
+ path: dist/
@@ -0,0 +1,69 @@
1
+ name: Release to PyPI
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ # Trusted Publishing exchanges this short-lived GitHub OIDC identity for a
8
+ # PyPI upload credential. No repository secret or write-capable token is used.
9
+ permissions:
10
+ contents: read
11
+ id-token: write
12
+
13
+ concurrency:
14
+ group: pypi-publish
15
+ cancel-in-progress: false
16
+
17
+ jobs:
18
+ publish:
19
+ name: Validate, build, and publish
20
+ runs-on: ubuntu-latest
21
+ environment:
22
+ name: pypi
23
+ url: https://pypi.org/p/uv-kernel-manager
24
+ steps:
25
+ - name: Check out the released tag
26
+ uses: actions/checkout@v4
27
+ with:
28
+ ref: ${{ github.event.release.tag_name }}
29
+ persist-credentials: false
30
+
31
+ - name: Install uv
32
+ uses: astral-sh/setup-uv@v6
33
+
34
+ - name: Validate the release tag and package version
35
+ env:
36
+ RELEASE_TAG: ${{ github.event.release.tag_name }}
37
+ run: |
38
+ set -euo pipefail
39
+ if [[ ! "$RELEASE_TAG" =~ ^v(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)((a|b|rc)(0|[1-9][0-9]*))?$ ]]; then
40
+ echo "Release tag must be vMAJOR.MINOR.PATCH or a PyPI pre-release such as v0.1.0rc1; got: $RELEASE_TAG" >&2
41
+ exit 1
42
+ fi
43
+ package_version="$(python - <<'PY'
44
+ import tomllib
45
+ from pathlib import Path
46
+
47
+ manifest = tomllib.loads(Path("pyproject.toml").read_text(encoding="utf-8"))
48
+ print(manifest["project"]["version"])
49
+ PY
50
+ )"
51
+ if [[ "v$package_version" != "$RELEASE_TAG" ]]; then
52
+ echo "Release tag $RELEASE_TAG does not match pyproject.toml version $package_version" >&2
53
+ exit 1
54
+ fi
55
+
56
+ - name: Install locked development dependencies
57
+ run: uv sync --locked --group dev
58
+
59
+ - name: Run tests
60
+ run: uv run --group dev pytest
61
+
62
+ - name: Build distributions
63
+ run: uv build --no-sources --clear
64
+
65
+ - name: Validate distribution metadata
66
+ run: uv run --group dev twine check dist/*
67
+
68
+ - name: Publish to PyPI with Trusted Publishing
69
+ run: uv publish --trusted-publishing always dist/*
@@ -0,0 +1,11 @@
1
+ .venv/
2
+ .DS_Store
3
+ __pycache__/
4
+ *.py[cod]
5
+ .pytest_cache/
6
+ .coverage
7
+ dist/
8
+ build/
9
+ *.egg-info/
10
+ outputs/
11
+ .idea/
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 nOtFoRgEeKs
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,171 @@
1
+ Metadata-Version: 2.5
2
+ Name: uv-kernel-manager
3
+ Version: 0.1.1a1
4
+ Summary: A lightweight CLI to seamlessly register and manage uv-managed Python environments as Jupyter kernels.
5
+ Project-URL: Homepage, https://github.com/nOtFoRgEeKs/uv-kernel-manager
6
+ Author: nOtFoRgEeKs
7
+ License-Expression: MIT
8
+ License-File: LICENSE
9
+ Classifier: Environment :: Console
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Topic :: Software Development :: Build Tools
13
+ Requires-Python: >=3.10
14
+ Requires-Dist: jupyter-client>=8.0
15
+ Description-Content-Type: text/markdown
16
+
17
+ # uv-kernel-manager
18
+
19
+ `uv-kernel-manager` registers the Python environments created by
20
+ [`uv`](https://docs.astral.sh/uv/) projects as Jupyter kernels. It is designed
21
+ for a single JupyterLab installation shared across many projects, each with
22
+ its own project-local `.venv`.
23
+
24
+ The PyPI distribution is `uv-kernel-manager`; the command is `uv-kernel`; the
25
+ Python package is `uv_kernel`.
26
+
27
+ ## How it works
28
+
29
+ ```
30
+ JupyterLab installation
31
+
32
+ ├── Python 3.12 (analytics) ──> analytics/.venv/bin/python
33
+ └── Python 3.13 (forecasting) ─> forecasting/.venv/bin/python
34
+ ```
35
+
36
+ Run `uv-kernel register` from a uv project to create a standard, user-level
37
+ Jupyter kernelspec pointing at that project's interpreter. The command suggests
38
+ a label in the form `Python <major.minor> (<project-name>)`, such as
39
+ `Python 3.13 (forecasting)`, and lets you accept it or provide a label that is
40
+ more meaningful to you.
41
+
42
+ The tool manages kernelspecs only. It does not install, start, configure, or
43
+ upgrade JupyterLab; it also does not create or otherwise manage your uv
44
+ projects or their virtual environments.
45
+
46
+ ## Prerequisites
47
+
48
+ - [uv](https://docs.astral.sh/uv/) on your `PATH`
49
+ - A uv project with a `pyproject.toml` and a synchronized `.venv` (`uv sync`)
50
+ - JupyterLab or another Jupyter frontend installed wherever you normally run it
51
+
52
+ `register` adds `ipykernel` to the current project's development dependencies
53
+ by default. Use `--no-add-ipykernel` only when it is already installed or you
54
+ do not want the command to change project dependencies.
55
+
56
+ ## Installation
57
+
58
+ Install the tool permanently with uv:
59
+
60
+ ```bash
61
+ uv tool install uv-kernel-manager
62
+ ```
63
+
64
+ Then run the `uv-kernel` command from a project directory:
65
+
66
+ ```bash
67
+ cd path/to/forecasting
68
+ uv sync
69
+ uv-kernel register
70
+ jupyter lab
71
+ ```
72
+
73
+ During registration, the tool shows the suggested Jupyter label:
74
+
75
+ ```text
76
+ Register kernel as: Python 3.13 (forecasting)
77
+ Press Enter to continue, or enter a custom name:
78
+ ```
79
+
80
+ Press Enter to use it, or type a custom label. For scripts and other
81
+ non-interactive usage, accept the suggestion with `--yes` or set it explicitly
82
+ with `--name`:
83
+
84
+ ```bash
85
+ uv-kernel register --yes
86
+ uv-kernel register --name "Forecasting — production"
87
+ ```
88
+
89
+ To run without installing it, use `uvx`:
90
+
91
+ ```bash
92
+ uvx uv-kernel-manager register
93
+ uvx uv-kernel-manager status
94
+ ```
95
+
96
+ ## Commands
97
+
98
+ | Command | Purpose |
99
+ | --- | --- |
100
+ | `uv-kernel register` | Prompt for a label and add the current project's `.venv` as a Jupyter kernel. |
101
+ | `uv-kernel register --yes` | Register with the suggested label without prompting. |
102
+ | `uv-kernel register --name "NAME"` | Register with an explicit Jupyter label. |
103
+ | `uv-kernel status` | Show whether the current project is registered. |
104
+ | `uv-kernel refresh` | Recreate the current project's kernel with its current interpreter and settings. |
105
+ | `uv-kernel unregister` | Remove the current project's managed kernel. |
106
+ | `uv-kernel list` | List every kernel managed by this tool. |
107
+ | `uv-kernel clean` | Report managed kernels whose project or interpreter no longer exists. |
108
+ | `uv-kernel clean --yes` | Remove the stale managed kernels reported by `clean`. |
109
+ | `uv-kernel doctor` | Show diagnostic information about uv, the project, and Jupyter kernel locations. |
110
+
111
+ Use `--json` with reporting commands when scripting, for example:
112
+
113
+ ```bash
114
+ uv-kernel --json list
115
+ uv-kernel --json status
116
+ ```
117
+
118
+ For a non-interactive JSON registration, include either `--yes` or `--name`:
119
+
120
+ ```bash
121
+ uv-kernel --json register --yes
122
+ uv-kernel --json register --name "Forecasting — production"
123
+ ```
124
+
125
+ ## Safety and ownership
126
+
127
+ Every kernel created by this tool includes a structured `metadata.uv-kernel`
128
+ ownership record. `unregister`, `refresh`, and `clean` act only on kernels
129
+ with that record; manually installed kernels and kernels managed by other
130
+ tools are left untouched. Kernel directory names also begin with `uv-kernel-`,
131
+ but the metadata marker—not the directory name—is the ownership check.
132
+
133
+ `clean` is intentionally non-destructive unless you explicitly pass `--yes`.
134
+
135
+ ## Build from source
136
+
137
+ To build the package locally, clone the repository and install its development
138
+ dependencies:
139
+
140
+ ```bash
141
+ uv sync --dev
142
+ uv build
143
+ ```
144
+
145
+ The wheel and source distribution are written to `dist/`. To install the local
146
+ checkout as a tool, run:
147
+
148
+ ```bash
149
+ uv tool install .
150
+ ```
151
+
152
+ ## Development
153
+
154
+ Run the test suite and validate built package metadata before contributing:
155
+
156
+ ```bash
157
+ uv run pytest
158
+ uv run twine check dist/*
159
+ ```
160
+
161
+ You can also run the command directly from the checkout:
162
+
163
+ ```bash
164
+ uv run uv-kernel --help
165
+ ```
166
+
167
+ ## Contributing
168
+
169
+ Contributions are welcome. Please open an issue or pull request with a focused
170
+ change, include tests when behavior changes, and run the checks above before
171
+ submitting.
@@ -0,0 +1,155 @@
1
+ # uv-kernel-manager
2
+
3
+ `uv-kernel-manager` registers the Python environments created by
4
+ [`uv`](https://docs.astral.sh/uv/) projects as Jupyter kernels. It is designed
5
+ for a single JupyterLab installation shared across many projects, each with
6
+ its own project-local `.venv`.
7
+
8
+ The PyPI distribution is `uv-kernel-manager`; the command is `uv-kernel`; the
9
+ Python package is `uv_kernel`.
10
+
11
+ ## How it works
12
+
13
+ ```
14
+ JupyterLab installation
15
+
16
+ ├── Python 3.12 (analytics) ──> analytics/.venv/bin/python
17
+ └── Python 3.13 (forecasting) ─> forecasting/.venv/bin/python
18
+ ```
19
+
20
+ Run `uv-kernel register` from a uv project to create a standard, user-level
21
+ Jupyter kernelspec pointing at that project's interpreter. The command suggests
22
+ a label in the form `Python <major.minor> (<project-name>)`, such as
23
+ `Python 3.13 (forecasting)`, and lets you accept it or provide a label that is
24
+ more meaningful to you.
25
+
26
+ The tool manages kernelspecs only. It does not install, start, configure, or
27
+ upgrade JupyterLab; it also does not create or otherwise manage your uv
28
+ projects or their virtual environments.
29
+
30
+ ## Prerequisites
31
+
32
+ - [uv](https://docs.astral.sh/uv/) on your `PATH`
33
+ - A uv project with a `pyproject.toml` and a synchronized `.venv` (`uv sync`)
34
+ - JupyterLab or another Jupyter frontend installed wherever you normally run it
35
+
36
+ `register` adds `ipykernel` to the current project's development dependencies
37
+ by default. Use `--no-add-ipykernel` only when it is already installed or you
38
+ do not want the command to change project dependencies.
39
+
40
+ ## Installation
41
+
42
+ Install the tool permanently with uv:
43
+
44
+ ```bash
45
+ uv tool install uv-kernel-manager
46
+ ```
47
+
48
+ Then run the `uv-kernel` command from a project directory:
49
+
50
+ ```bash
51
+ cd path/to/forecasting
52
+ uv sync
53
+ uv-kernel register
54
+ jupyter lab
55
+ ```
56
+
57
+ During registration, the tool shows the suggested Jupyter label:
58
+
59
+ ```text
60
+ Register kernel as: Python 3.13 (forecasting)
61
+ Press Enter to continue, or enter a custom name:
62
+ ```
63
+
64
+ Press Enter to use it, or type a custom label. For scripts and other
65
+ non-interactive usage, accept the suggestion with `--yes` or set it explicitly
66
+ with `--name`:
67
+
68
+ ```bash
69
+ uv-kernel register --yes
70
+ uv-kernel register --name "Forecasting — production"
71
+ ```
72
+
73
+ To run without installing it, use `uvx`:
74
+
75
+ ```bash
76
+ uvx uv-kernel-manager register
77
+ uvx uv-kernel-manager status
78
+ ```
79
+
80
+ ## Commands
81
+
82
+ | Command | Purpose |
83
+ | --- | --- |
84
+ | `uv-kernel register` | Prompt for a label and add the current project's `.venv` as a Jupyter kernel. |
85
+ | `uv-kernel register --yes` | Register with the suggested label without prompting. |
86
+ | `uv-kernel register --name "NAME"` | Register with an explicit Jupyter label. |
87
+ | `uv-kernel status` | Show whether the current project is registered. |
88
+ | `uv-kernel refresh` | Recreate the current project's kernel with its current interpreter and settings. |
89
+ | `uv-kernel unregister` | Remove the current project's managed kernel. |
90
+ | `uv-kernel list` | List every kernel managed by this tool. |
91
+ | `uv-kernel clean` | Report managed kernels whose project or interpreter no longer exists. |
92
+ | `uv-kernel clean --yes` | Remove the stale managed kernels reported by `clean`. |
93
+ | `uv-kernel doctor` | Show diagnostic information about uv, the project, and Jupyter kernel locations. |
94
+
95
+ Use `--json` with reporting commands when scripting, for example:
96
+
97
+ ```bash
98
+ uv-kernel --json list
99
+ uv-kernel --json status
100
+ ```
101
+
102
+ For a non-interactive JSON registration, include either `--yes` or `--name`:
103
+
104
+ ```bash
105
+ uv-kernel --json register --yes
106
+ uv-kernel --json register --name "Forecasting — production"
107
+ ```
108
+
109
+ ## Safety and ownership
110
+
111
+ Every kernel created by this tool includes a structured `metadata.uv-kernel`
112
+ ownership record. `unregister`, `refresh`, and `clean` act only on kernels
113
+ with that record; manually installed kernels and kernels managed by other
114
+ tools are left untouched. Kernel directory names also begin with `uv-kernel-`,
115
+ but the metadata marker—not the directory name—is the ownership check.
116
+
117
+ `clean` is intentionally non-destructive unless you explicitly pass `--yes`.
118
+
119
+ ## Build from source
120
+
121
+ To build the package locally, clone the repository and install its development
122
+ dependencies:
123
+
124
+ ```bash
125
+ uv sync --dev
126
+ uv build
127
+ ```
128
+
129
+ The wheel and source distribution are written to `dist/`. To install the local
130
+ checkout as a tool, run:
131
+
132
+ ```bash
133
+ uv tool install .
134
+ ```
135
+
136
+ ## Development
137
+
138
+ Run the test suite and validate built package metadata before contributing:
139
+
140
+ ```bash
141
+ uv run pytest
142
+ uv run twine check dist/*
143
+ ```
144
+
145
+ You can also run the command directly from the checkout:
146
+
147
+ ```bash
148
+ uv run uv-kernel --help
149
+ ```
150
+
151
+ ## Contributing
152
+
153
+ Contributions are welcome. Please open an issue or pull request with a focused
154
+ change, include tests when behavior changes, and run the checks above before
155
+ submitting.
@@ -0,0 +1,37 @@
1
+ [build-system]
2
+ requires = ["hatchling>=1.25"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "uv-kernel-manager"
7
+ version = "0.1.1a1"
8
+
9
+ description = "A lightweight CLI to seamlessly register and manage uv-managed Python environments as Jupyter kernels."
10
+ readme = "README.md"
11
+ requires-python = ">=3.10"
12
+ license = "MIT"
13
+ license-files = ["LICENSE"]
14
+ authors = [{ name = "nOtFoRgEeKs" }]
15
+ classifiers = [
16
+ "Environment :: Console",
17
+ "License :: OSI Approved :: MIT License",
18
+ "Programming Language :: Python :: 3",
19
+ "Topic :: Software Development :: Build Tools",
20
+ ]
21
+ dependencies = ["jupyter_client>=8.0"]
22
+
23
+ [project.scripts]
24
+ uv-kernel = "uv_kernel.cli:main"
25
+
26
+ [project.urls]
27
+ Homepage = "https://github.com/nOtFoRgEeKs/uv-kernel-manager"
28
+
29
+ [tool.hatch.build.targets.wheel]
30
+ packages = ["src/uv_kernel"]
31
+
32
+ [tool.pytest.ini_options]
33
+ testpaths = ["tests"]
34
+ addopts = "-q"
35
+
36
+ [dependency-groups]
37
+ dev = ["pytest>=8", "twine>=6"]
@@ -0,0 +1,7 @@
1
+ """Manage Jupyter kernels for uv-managed projects."""
2
+
3
+ from importlib.metadata import version
4
+
5
+ # The installed distribution metadata is generated from [project].version in
6
+ # pyproject.toml, keeping that file as the only version authority.
7
+ __version__ = version("uv-kernel-manager")
@@ -0,0 +1,146 @@
1
+ """Command line interface for uv-kernel."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import json
7
+ import sys
8
+ from pathlib import Path
9
+ from typing import Any
10
+
11
+ from . import __version__
12
+ from .core import (UVKernelError, display_name, doctor, ensure_ipykernel,
13
+ find_project, install, matching, owned_kernels, remove, stale)
14
+
15
+ DESCRIPTION = """
16
+ Manage Jupyter kernels for uv-managed Python environments.
17
+
18
+ Register a project's uv `.venv` with the Jupyter installation you already use.
19
+ Only kernels carrying uv-kernel's ownership metadata are ever changed or removed.
20
+ """
21
+
22
+
23
+ def parser() -> argparse.ArgumentParser:
24
+ p = argparse.ArgumentParser(prog="uv-kernel", description=DESCRIPTION,
25
+ formatter_class=argparse.RawDescriptionHelpFormatter,
26
+ epilog="Examples:\n uv-kernel register\n uv-kernel --json list\n uv-kernel clean --yes")
27
+ p.add_argument("--version", action="version", version=f"%(prog)s {__version__}")
28
+ p.add_argument("--json", action="store_true", dest="json_output", help="Output machine-readable JSON.")
29
+ sub = p.add_subparsers(dest="command", required=True, metavar="COMMAND")
30
+ register = sub.add_parser("register", help="Register the current uv project as a Jupyter kernel.",
31
+ description="Adds ipykernel as a development dependency by default and writes a standard user kernelspec.")
32
+ register.add_argument("--no-add-ipykernel", action="store_true", help="Do not run `uv add --dev ipykernel` first.")
33
+ naming = register.add_mutually_exclusive_group()
34
+ naming.add_argument("--name", metavar="NAME", help="Use NAME as the kernel label instead of the suggested label.")
35
+ naming.add_argument("--yes", action="store_true", help="Accept the suggested kernel label without prompting.")
36
+ sub.add_parser("unregister", help="Remove this project's uv-kernel-owned kernel.",
37
+ description="Removes only kernels marked as owned by uv-kernel for the current project.")
38
+ sub.add_parser("list", help="List every kernel owned by uv-kernel.")
39
+ sub.add_parser("status", help="Show this project's uv-kernel registration status.")
40
+ sub.add_parser("refresh", help="Recreate this project's owned kernel with current settings.",
41
+ description="Updates the spec for the current .venv; it never touches other kernels.")
42
+ clean = sub.add_parser("clean", help="Find stale uv-kernel-owned kernels safely.",
43
+ description="Reports kernels whose project or .venv interpreter is gone. Nothing is deleted without --yes.")
44
+ clean.add_argument("--yes", action="store_true", help="Remove the stale kernels reported by clean.")
45
+ sub.add_parser("doctor", help="Diagnose uv, the current project, and Jupyter kernel paths.")
46
+ return p
47
+
48
+
49
+ def emit(value: Any, as_json: bool) -> None:
50
+ if as_json:
51
+ print(json.dumps(value, indent=2, sort_keys=True))
52
+ return
53
+ if isinstance(value, list):
54
+ if not value:
55
+ print("No uv-kernel-owned kernels found.")
56
+ for item in value:
57
+ print(f"{item['name']}: {item.get('display_name', '')} — {item.get('project_path', '')}")
58
+ elif isinstance(value, dict):
59
+ for key, item in value.items(): print(f"{key}: {item}")
60
+ else: print(value)
61
+
62
+
63
+ def choose_kernel_name(default: str) -> str:
64
+ """Prompt for a kernel label, accepting the suggested label on Enter."""
65
+ try:
66
+ response = input(
67
+ f"Register kernel as: {default}\n"
68
+ "Press Enter to continue, or enter a custom name: "
69
+ ).strip()
70
+ except EOFError as exc:
71
+ raise UVKernelError("No kernel name was provided. Use --yes or --name for non-interactive use.") from exc
72
+ return response or default
73
+
74
+
75
+ def main(argv: list[str] | None = None) -> int:
76
+ args = parser().parse_args(argv)
77
+ try:
78
+ if args.command == "list":
79
+ emit(owned_kernels(), args.json_output)
80
+ return 0
81
+
82
+ if args.command == "clean":
83
+ items = stale(owned_kernels())
84
+ if args.yes:
85
+ removed = remove(items)
86
+ emit({"removed": removed, "count": len(removed)}, args.json_output)
87
+ else:
88
+ emit(items, args.json_output)
89
+ if not args.json_output and items:
90
+ print("Run `uv-kernel clean --yes` to remove these kernels.")
91
+ return 0
92
+
93
+ if args.command == "doctor":
94
+ try:
95
+ project = find_project()
96
+ except UVKernelError:
97
+ project = None
98
+ emit(doctor(project), args.json_output)
99
+ return 0
100
+
101
+ project = find_project()
102
+ if args.command == "register":
103
+ suggested_name = display_name(project)
104
+ if args.name:
105
+ kernel_display_name = args.name
106
+ elif args.yes:
107
+ kernel_display_name = suggested_name
108
+ elif args.json_output:
109
+ raise UVKernelError("Use --yes or --name with --json register.")
110
+ else:
111
+ kernel_display_name = choose_kernel_name(suggested_name)
112
+ if not args.no_add_ipykernel:
113
+ ensure_ipykernel(project)
114
+ result = install(project, kernel_display_name=kernel_display_name)
115
+ emit({"registered": result}, args.json_output)
116
+ return 0
117
+
118
+ if args.command == "unregister":
119
+ removed = remove(matching(project))
120
+ emit({"removed": removed, "count": len(removed)}, args.json_output)
121
+ return 0
122
+
123
+ if args.command == "status":
124
+ items = matching(project)
125
+ result = {"project": str(project.root), "registered": bool(items), "kernels": items}
126
+ emit(result, args.json_output)
127
+ return 0 if items else 1
128
+
129
+ if args.command == "refresh":
130
+ remove(matching(project))
131
+ result = install(project)
132
+ emit({"refreshed": result}, args.json_output)
133
+ return 0
134
+
135
+ except UVKernelError as exc:
136
+ if args.json_output:
137
+ print(json.dumps({"error": str(exc)}))
138
+ else:
139
+ print(f"uv-kernel: error: {exc}", file=sys.stderr)
140
+ return 2
141
+
142
+ return 0
143
+
144
+
145
+ if __name__ == "__main__":
146
+ raise SystemExit(main())