Kepler 1.0.0
dotnet tool install --global Kepler --version 1.0.0
dotnet new tool-manifest
dotnet tool install --local Kepler --version 1.0.0
#tool dotnet:?package=Kepler&version=1.0.0
nuke :add-package Kepler --version 1.0.0
Kepler
Kepler is a .NET global tool for planning, packing, publishing, and confirming coordinated NuGet releases from a multi-project repository. It persists the internal dependency graph discovered during scan and reuses its dependency levels throughout the release, so packages are always processed from foundations to dependents.
Requirements and conventions
- .NET SDK 10 or later.
- Package projects under
src/**/*.csproj. - A package project must define
<Version>and must not set<IsPackable>false</IsPackable>. - Central package management through
Directory.Packages.propsat the repository root. - NuGet.org is currently the fixed remote source:
https://api.nuget.org/v3/index.json. - Run every command from the repository root. Paths in
.keplerare calculated from the current directory.
Kepler currently has no configuration command or configuration-file arguments. Project discovery, remote source, and workspace paths are conventions encoded by the tool.
Installation
From this repository:
dotnet pack src/keplercli/keplercli.csproj --configuration Release
dotnet tool install --global --add-source src/keplercli/bin/Release Kepler
To update an existing installation from a newly generated package:
dotnet tool update --global --add-source src/keplercli/bin/Release Kepler
Confirm the command is available:
kepler --help
Workspace and persisted state
kepler init creates a local .kepler workspace:
.kepler/
├── nuget.config
├── package-list.json
└── packages/
nuget.configputs the localpackagesdirectory before NuGet.org. This allows a dependent package to restore a package produced earlier in the same release.package-list.jsonis the release state machine. It stores package paths, local and remote versions, internal dependencies, dependency levels, publication decisions, and publication state.packages/contains the generated.nupkgfiles untilcheckconfirms them remotely.
The entire .kepler/ directory is an execution artifact and must remain excluded from source control. Directory.Packages.props, however, belongs to the repository and is modified by pack so downstream projects restore the newly packed versions.
Recommended release flow
kepler init
kepler scan
kepler plan --dry-run
kepler plan
kepler pack
kepler publish --dry-run
kepler publish --api-key <NUGET_API_KEY>
kepler check --wait --timeout 00:20:00 --interval 00:00:30
Run the commands in this order. With the exception of init, commands require an initialized .kepler workspace.
Commands
kepler init
Initializes the local Kepler workspace. It has no arguments or options.
kepler init
It performs the following operations:
- Creates
.kepler/and.kepler/packages/when they do not exist. - Writes
.kepler/nuget.configwith the localkeplersource first and NuGet.org second. - Creates or attaches
.kepler/package-list.json. - Prints every generated path.
Running init again is safe: it ensures the required directories and sources exist without deleting package state.
kepler scan
Discovers package projects and synchronizes dependency and remote-version state. It has no arguments or options.
kepler scan
For every src/**/*.csproj, scan:
- Skips the project when
<IsPackable>false</IsPackable>is set. - Treats a project with
<Version>as packable. A project explicitly marked packable but missing<Version>is rejected. - Resolves the package ID from
<PackageId>, then<AssemblyName>, and finally the project filename. - Reads
ProjectReference,PackageReference, andPackageVersionrelationships. - Builds the internal dependency graph and calculates each package's dependency level.
- Queries NuGet.org for the latest published version.
- Replaces the scanned package inventory in
.kepler/package-list.jsonand records synchronization timestamps.
Duplicate package IDs or invalid project XML stop the command. Because scan replaces the discovered inventory, do not run it in the middle of an unfinished Pending/Pushed publication flow.
kepler plan [--dry-run]
Calculates which packages must be released using the dependencies and dependency levels persisted by scan.
kepler plan
kepler plan --dry-run
Options:
| Option | Value | Default | Effect |
|---|---|---|---|
--dry-run |
Boolean switch | false |
Prints and validates the plan without modifying package-list.json. |
A package becomes a release root when it has never been published or its local version is greater than the remote version. Every transitive dependent of a release root is also selected.
Version rules:
- First publication: keeps the version declared by the project.
- Explicit local increment: keeps the newer local version.
- Dependency-only change: increments the patch component automatically.
- Local version lower than the remote version: validation fails and nothing is written.
Without --dry-run, selected packages are marked Pending and ShouldPublish=true. Their planned version and output path are persisted. Unselected packages are marked as already published for this plan.
kepler pack
Builds and packs every Pending package selected by plan. It has no arguments or options.
kepler pack
Before packing, Kepler refuses to continue if any package is Pushed and still awaits confirmation. It then starts from a clean local state:
- Empties
.kepler/packages/. - Restores every managed internal package in
Directory.Packages.propsto its known remote version; entries without a remote version are removed. - Orders selected packages by dependency level and then package ID.
- Runs
dotnet build <project> --configuration Releasewith forced restore against.kepler/nuget.config. - Runs
dotnet pack <project> --configuration Release --no-build --no-restore, forcing the planned package version and writing directly to.kepler/packages/. - Updates
Directory.Packages.propsimmediately after each successful package, allowing later dependency levels to restore that local version.
A blank line separates dependency levels in the console output.
If any build, pack, or expected-file check fails, Kepler empties the package directory and restores Directory.Packages.props to the remote-version baseline. A new pack invocation therefore starts from zero.
kepler publish --api-key <key> [--dry-run]
Validates and pushes packages created by pack to NuGet.org in publication order.
kepler publish --dry-run
kepler publish --api-key <NUGET_API_KEY>
Options:
| Option | Value | Required | Effect |
|---|---|---|---|
--api-key |
String | Required unless --dry-run is used or no package remains Pending |
Passed to dotnet nuget push. It is not written to Kepler files. |
--dry-run |
Boolean switch | No | Executes strict preflight and prints publication order without pushing or changing state. |
Strict preflight runs before any push and requires:
- Exactly one expected
.nupkgfor every package selected by the plan. - No unexpected
.nupkgfiles in.kepler/packages/. - Exactly one
.nuspecinside each package. - Package ID and semantic version matching
package-list.json.
After each successful NuGet push, Kepler immediately persists that package as Pushed. It does not delete the file and does not assume that the package is already queryable from NuGet.org.
publish is resumable but an actual push is not inherently idempotent. On a retry, Kepler skips packages already marked Pushed and continues with those still Pending. If a push succeeds remotely but the process terminates before state is saved, confirm the remote state before retrying to avoid submitting the same immutable version again.
The API key is not persisted by Kepler, but it is supplied as a child-process argument. In CI, pass it from a masked secret and ensure command/process diagnostics do not expose arguments.
kepler check [--wait] [--timeout <duration>] [--interval <duration>]
Confirms that Pushed package versions are visible through the NuGet remote API.
kepler check
kepler check --wait
kepler check --wait --timeout 00:20:00 --interval 00:00:30
Options:
| Option | Value | Default | Effect |
|---|---|---|---|
--wait |
Boolean switch | false |
Repeats checks until all packages are visible or the timeout expires. |
--timeout |
.NET TimeSpan |
00:15:00 |
Maximum total wait. Must be greater than zero. Primarily meaningful with --wait. |
--interval |
.NET TimeSpan |
00:00:30 |
Delay between remote checks. Must be greater than zero. |
Without --wait, check performs one pass and returns success even when some packages remain pending; the summary reports how many are still missing. This makes it suitable for polling across separate pipeline runs.
With --wait, it polls in the same process. For every version found remotely, Kepler:
- Marks the package
Published. - Copies its local version into
RemoteVersion. - Clears
ShouldPublish. - Saves
package-list.json. - Deletes the confirmed
.nupkgfrom.kepler/packages/.
On timeout, check fails and lists the versions still missing. Their state remains Pushed and their package files remain available, so the command can safely be run again.
Publication state model
| State | Meaning | Next command |
|---|---|---|
Pending |
Planned and not yet successfully pushed. | pack, then publish |
Pushed |
NuGet accepted the push, but Kepler has not confirmed remote visibility. | check |
Published |
The exact version is visible remotely and local release artifacts were finalized. | Start the next release with scan |
CI/CD example
Use a secret provided by the CI platform and let check --wait absorb NuGet indexing delay:
kepler init
kepler scan
kepler plan
kepler pack
kepler publish --dry-run
kepler publish --api-key "$NUGET_API_KEY"
kepler check --wait --timeout 00:20:00 --interval 00:00:30
Persist .kepler/package-list.json and .kepler/packages/ between retryable pipeline jobs if publish and check run in different jobs or runners. Do not commit .kepler/.
After check succeeds, review and commit the resulting Directory.Packages.props changes separately. Kepler intentionally does not stage or commit repository files.
Recovery guide
Kepler is not initialized: runkepler initfrom the repository root.- Plan reports a local version below remote: update the project version, then run
scanandplanagain. - Pack fails: fix the project and rerun
kepler pack; failed output and central versions have already been rolled back. - Publish preflight fails: rerun
packto recreate an exact, clean package set. - Publish stops after some packages: rerun
publishwith the API key; packages recorded asPushedare skipped. - Check times out: rerun
check --wait; no pushed package is resent. - A package was accepted remotely but remains
Pendinglocally: verify that exact version on NuGet.org before retrying. NuGet package versions are immutable.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
This package has no dependencies.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.0 | 93 | 7/12/2026 |