RosaCli 0.8.1
dotnet tool install --global RosaCli --version 0.8.1
dotnet new tool-manifest
dotnet tool install --local RosaCli --version 0.8.1
#tool dotnet:?package=RosaCli&version=0.8.1
nuke :add-package RosaCli --version 0.8.1
rosa CLI
Scaffolds and maintains Godot + rosa game projects. Published publicly as RosaCli on nuget.org (the rosa libs themselves stay on the private GitHub feed).
dnx rosacli # run once, no install
dotnet tool install -g RosaCli # or install as `rosa`
Commands
dnx rosacli init [Name]— scaffold a project in the current directory (empty or Godot-created).Nameis theRosaRootNamespace; defaults to the PascalCased directory name (my_game→MyGame).dnx rosacli lib <name> [--fs]— seed a C# (or F#) lib subproject wired to the machinery: namespaceGameNs.LibName, its ownRosaModulesmenu, added to the sln; paste the printedProjectReferenceinto the game csproj.dnx rosacli sync [version]— rewrite the rosa-owned files to the current tool's templates and stampRosaVersion(default: latest stable on the feed; offline keeps the currently stamped version). Run after updating the tool.dnx rosacli local [on|off] [path]— build against a local rosa checkout (default../rosa) instead of the feed. Writes the gitignoredDirectory.Build.local.props(no file means off) and maintains.vscode/{name}.local.sln— pick that solution in the IDE while local is on and rosa loads as source (live cross-repo IntelliSense, go-to-def, find-refs). Both commands finish with a restore + project-file touch so language servers re-crack without a window reload.
The contract
Owned (rewritten by every init/sync — never edit): rosa.godot.props, rosa.godot.targets, Directory.Build.props, Directory.Build.targets, .editorconfig, nuget.config, .gitattributes, .gitignore, .vscode/*, .config/dotnet-tools.json, .config/mise/config.toml.
Seeded (written once, then yours): {name}.csproj, {name}.sln, Directory.Packages.props, project.godot, src/*, and everything rosa lib creates.
Versions are centrally managed (CPM): projects hold versionless PackageReferences; game package versions live in the seeded Directory.Packages.props, while the single rosa pin is the RosaVersion stamped into owned rosa.godot.props by sync.
Tools (mise)
mise is the per-repo tool manager: it pins the tools and exports the env. The owned .config/mise/config.toml holds the pins — the .NET SDK, bud (the content build, installed straight from rosa-rs's private release assets), this tool, the Godot version, GODOT for .vscode/launch.json, and the private feed's credential built from gh — plus a postinstall hook that runs dotnet tool restore for csharpier and fantomas. Sync rewrites it, so put your own tools and env in mise.toml at the repo root and per-machine values (a different Godot path) in the gitignored mise.local.toml; both load after the owned file and win. init runs mise trust && mise install itself; sync says when the pins moved so you can rerun it.
direnv and .envrc are gone — mise's [env] replaces them, and sync no longer writes the file (delete a leftover .envrc by hand).
Fresh machine, in order:
- Install Godot (mono/.NET build) into
/Applications— it stays a hand-installed app, andGODOT_VERSIONin the mise config says which one the repo expects. - Install mise and activate it in your shell.
gh auth login— one login covers the private NuGet feed and bud's release assets. Then, once per machine,mise settings set github.credential_command "gh auth token"— mise reads that login for bud's private release; a project config may not set it, and gh keeps the token in the keychain where mise's own lookup does not find it.rosa initin a new directory, or clone the repo androsa sync; thenmise trust && mise install.dotnet build.
Modules
Each project file holds only decisions:
<Project Sdk="Godot.NET.Sdk/4.7.0">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<RosaRootNamespace>Poplar</RosaRootNamespace>
<RosaModules>
Utils
...commented menu, uncomment to opt in...
</RosaModules>
</PropertyGroup>
</Project>
rosa.godot.targets expands RosaModules by convention — Godot.Grove → Rosa.Cs.Godot.Grove (feed) or $(RosaLocalPath)/cs/godot/grove/grove.csproj (local), Fs.Utils → Rosa.Fs.Utils / fs/utils/utils.fsproj — so new rosa modules need no CLI release, and a typo'd module fails the build instead of silently doing nothing. In local mode every sibling project of a module (<name>.gen, <name>.fix, …, <name>.tests excepted) is picked up as a Roslyn analyzer; the packages bundle the same assemblies themselves.
Current menu: Utils, Grove, Star, Random, Godot.Clock, Godot.R3, Godot.Grove, Godot.Star, Godot.Input, Godot.Validation, Fs.Utils, Fs.Random, Fs.Interop. C# and F# modules mix freely.
Conventions (enforced at build)
- Code lives under
src/; folders are snake_case; files are snake_case (C# may also be PascalCase). Folders never start with_or+. - Namespace =
RosaRootNamespace+._Pascalper path segment:src/game/enemy_ai/brain.cs→Game._Game._EnemyAi._Brain. - A
_-prefixed file is the folder's own module — its final bit is the literal_Mod(src/core/_def.fs→Game._Core._Mod);mod/_modnames are reserved. - C# is checked by the
Godot.Validationanalyzer, F# by an inline task inrosa.godot.targets— same RGV0008–0013 codes (escape hatch:<RosaSkipFsNamespaceCheck>true</RosaSkipFsNamespaceCheck>). - F# compile order comes from
+ordermanifests (rootsrc/+order): one name per line,@folderrecurses intofolder/+order,#comments;_def.fsis auto-included first and_api.fslast in every folder — never list them. Builds write the expansion to the gitignoredobj/order.g.propsand bump the fsproj mtime when it changed, so IDEs re-crack automatically: edit+order, build, done — no window reloads, no committed-file churn. The running build always compiles the fresh list, so the generated file is never load-bearing.
Libs
Libs are ordinary subprojects riding the same game-root machinery, with their own RosaRootNamespace (e.g. Poplar.Engine) and RosaModules. F# libs get a seeded src/+order instead of a hand-kept <Compile> list, and reference FSharp.Core explicitly — CPM suppresses the F# SDK's implicit reference, and without it C# consumers can't see F# types. GodotSharp is referenced versionless; the version is centrally pinned for non-Godot-Sdk projects.
Releasing
Tag vX.Y.Z + publish a GitHub release → CI packs and pushes to nuget.org via trusted publishing (OIDC — no stored API key). Requires a trusted publishing policy on nuget.org (username → Trusted Publishing: repo owner orcharddweller, repo rosa-cli, workflow file publish.yml) and the correct user: in the workflow's NuGet login step. Bump Feed.DefaultRosaVersion when rosa releases and Feed.DefaultBudVersion when bud does, so the offline fallbacks stay fresh — both versions are otherwise resolved live (the feed for rosa, rosa-rs's latest release for bud). Feed.DefaultCliVersion is the same fallback for this tool's own pin, used only by unpacked dev builds, whose assembly carries the SDK's placeholder version.
| 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 |
|---|---|---|
| 0.8.1 | 98 | 9/12/2026 |
| 0.8.0 | 85 | 9/12/2026 |
| 0.7.0 | 92 | 9/2/2026 |
| 0.6.1 | 104 | 8/25/2026 |
| 0.6.0 | 104 | 8/22/2026 |
| 0.5.1 | 133 | 7/22/2026 |
| 0.5.0 | 111 | 7/21/2026 |
| 0.4.1 | 112 | 7/21/2026 |
| 0.4.0 | 106 | 7/18/2026 |
| 0.3.0 | 114 | 7/16/2026 |
| 0.2.0 | 106 | 7/16/2026 |
| 0.1.5 | 115 | 7/14/2026 |
| 0.1.4 | 108 | 7/14/2026 |
| 0.1.3 | 119 | 7/14/2026 |
| 0.1.2 | 111 | 7/14/2026 |