{"slug": "nuvyn-cli-1-2-0-spec-driven-net-maui-from-a-new-app-or-one-you-already-have", "title": "Nuvyn CLI 1.2.0: Spec-Driven .NET MAUI, From a New App or One You Already Have", "summary": "Nuvyntra Labs released Nuvyn CLI 1.2.0, a standalone .NET 10 global tool that scaffolds spec-driven .NET MAUI applications through `nuvyn init` for new projects and `nuvyn adopt` for existing ones. The tool writes a constitution, specification, plan, tasks, and analysis into the repository so coding agents build against a checked-in spec rather than a chat thread, and it locks the MAUI and Lumina stack while leaving individual plugins and NuvyntraLabs.UIKit independently installable. It is MIT licensed and distributed on nuget.org as NuvyntraLabs.Nuvyn.Cli.", "body_md": "A coding agent will happily build a .NET MAUI app from a chat thread. The next session often builds a different one.\n\n**Nuvyn** is the CLI that keeps that work on one stack and one written spec. Version **1.2.0** is on nuget.org as the global tool `NuvyntraLabs.Nuvyn.Cli`. The command is `nuvyn`.\n\nThis post is the walkthrough: what the tool is, how to install it, how to start a new app, how to attach it to an app you already have, and the slash commands the agent runs after that.\n\nNuvyn is a standalone `PackAsTool` CLI for **.NET 10**. It depends on `System.CommandLine` and `Spectre.Console`. It is MIT licensed.\n\nIt does two jobs:\n\n`nuvyn init`` nuvyn adopt`\nThe product domain is yours: clinic, field, bank, civic, retail, or anything else. The skills stay domain-agnostic. On a new host they lock only the MAUI and Lumina stack.\n\nIndividual plugins and `NuvyntraLabs.UIKit` stay installable on their own. You do not need Nuvyn to use one package.\n\n| Package | [`NuvyntraLabs.Nuvyn.Cli`](https://www.nuget.org/packages/NuvyntraLabs.Nuvyn.Cli) 1.2.0 | \n| Command | `nuvyn` | \n| Source | [github.com/nuvyntralabs/Nuvyn](https://github.com/nuvyntralabs/Nuvyn) | \n| Docs | [nuvyntralabs.github.io/toolkits/nuvyn/](https://nuvyntralabs.github.io/toolkits/nuvyn/) | \n| Guide | [Install, init, adopt, slash workflow](https://nuvyntralabs.github.io/toolkits/nuvyn/guide/) | \n\nInstall it as a **global dotnet tool**. Do not add `NuvyntraLabs.Nuvyn.Cli` as a `PackageReference` in an app.\n\nSpec-driven development treats a checked-in specification as the source of truth. Code follows that spec. A chat thread is a prompt log: it disappears, it contradicts itself, and a teammate cannot review it.\n\nNuvyn writes the artifacts into the repo:\n\n| Artifact | Role | \n|---|---|\n| Constitution | Standing principles: stack, platforms, privacy | \n| Specification | This increment: users, journeys, edge cases | \n| Plan | Smallest package set and one screen recipe per screen | \n| Tasks | Dependency-ordered work ( `T001 [P] [US1]` ) | \n| Analysis | Consistency check before production code | \n\nThat matters more once an agent writes the code. The same prompt can pick a different MVVM library on a different day. Long chats drop acceptance rules that were never written down. Agents add a login page, a second UI kit, or persistence nobody asked for, because a finished-looking app is an easy completion.\n\nNuvyn narrows that search space. Constitution locks the stack. Specify writes the product. Plan names packages and screens. Analysis fails the loop before implement invents a second architecture.\n\nGitHub Spec Kit (`specify init`) remains the usual choice when the stack is open. Nuvyn is the door when the host should be Nuvyntra, or when an existing MAUI app should grow through the same slash chain.\n\n| Requirement | Why | \n|---|---|\n| .NET 10 SDK | The CLI is `net10.0` . Hosts target`net10.0-android` ,`net10.0-ios` ,`net10.0-maccatalyst` , and`net10.0-windows10.0.19041.0` | \n| MAUI workload | So the host can build and run | \n| A coding agent | Cursor, GitHub Copilot, Claude Code, Gemini CLI, Codex, Windsurf, or any other Spec Kit agent | \n| nuget.org | `init` adds the default Nuvyntra packages at the latest stable versions | \n\nNuvyn targets those four platforms. It is the wrong tool for Tizen, Flutter, React Native, WPF, WinUI, Avalonia, or Uno.\n\n```\ndotnet tool install -g NuvyntraLabs.Nuvyn.Cli --source https://api.nuget.org/v3/index.json\nnuvyn version\n```\n\n`nuvyn version` prints `NuvyntraLabs.Nuvyn.Cli 1.2.0`.\n\nAlready installed? Update the tool only. Existing apps keep the packages they already reference:\n\n```\ndotnet tool update -g NuvyntraLabs.Nuvyn.Cli --source https://api.nuget.org/v3/index.json\nnuvyn version\n```\n\nOn an interactive terminal, `nuvyn` asks every four hours whether to update from nuget.org (`[y/N]`, default no). Skip that prompt with `--no-update-check` or `NUVYNTRA_NO_UPDATE_CHECK=1`. The cache file is `~/.nuvyntra/cli-updates.json`, shared with `maui-dev` and `maui-perf`. The CLIs do not phone home.\n\n`nuvyn init <folder_name>` always creates a **new folder**. If that name already exists, init prints an error and exits `1`. The existing tree is left untouched.\n\n```\nnuvyn init HarborDesk --agent cursor\n```\n\nOmit `--agent` and the CLI shows a searchable picker (`cursor (Cursor)`, `agy (Antigravity)`, and the rest of the Spec Kit set). Non-interactive runs default to Cursor.\n\nWhat init does:\n\n`.nuvyn/` (constitution, templates, reference, `init-options.json`).\nThe tree looks like this:\n\n```\nHarborDesk/\n├── HarborDesk.sln\n├── HarborDesk/                      # MAUI app\n│   ├── MauiProgram.cs\n│   ├── Pages/MainPage.xaml\n│   └── Resources/Images/nuvyntra.png\n├── HarborDesk.Core/                 # ViewModels\n├── HarborDesk.Tests/\n├── .nuvyn/\n│   ├── constitution.md\n│   ├── init-options.json\n│   ├── templates/\n│   └── reference/\n├── specs/                           # empty until /nuvyn.specify\n├── .cursor/skills/nuvyn-*/          # when --agent cursor\n└── README.md\n```\n\n`MainPage` is a Lumina screen: the Nuvyntra mark, an `NVHeading` counter, and Increase / Decrease `NVButton` controls. There is no stock `Entry` / `Button` / `Label` starter, and no seeded Login / Items / Edit pages. Screens come from the spec.\n\nBuild it the usual way:\n\n```\ncd HarborDesk\ndotnet restore\ndotnet build\ndotnet build HarborDesk/HarborDesk.csproj -f net10.0-android\n```\n\n`init` adds these, and nothing else from the catalog, until the spec asks:\n\n| Package | Role | \n|---|---|\n| [Plugin.Maui.MVVMExpress](https://nuvyntralabs.github.io/packages/plugin-maui-mvvmexpress/) | App shell, ViewModels, navigation | \n| [NuvyntraLabs.UIKit](https://nuvyntralabs.github.io/uikit/) | Lumina `NV*` controls and page recipes | \n| [Plugin.Maui.HttpForge](https://nuvyntralabs.github.io/packages/plugin-maui-httpforge/) | Typed REST client | \n| [Plugin.Maui.FormValidation](https://nuvyntralabs.github.io/packages/plugin-maui-form-validation/) | Form rules on the host | \n| [Plugin.Maui.KeyboardManager](https://nuvyntralabs.github.io/packages/plugin-maui-keyboard-manager/) | Soft keyboard hide, show, and resize | \n\nThe MAUI app gets MVVMExpress plus Dialogs and Navigation, UIKit, HttpForge, FormValidation, and KeyboardManager. Core gets `Plugin.Maui.MVVMExpress.Core` and the source generators. Tests get `Plugin.Maui.MVVMExpress.Testing`. Versions are never pinned: `dotnet add package` runs without `--version`.\n\nThe host wires up like this:\n\n``` js\nbuilder\n    .UseMauiApp<App>()\n    .UseMvvmExpress(o => o\n        .UseNavigationPage((nav, _) => nav\n            .Map<MainPageViewModel, MainPage>(\"main\"))\n        .UseDialogs())\n    .UseNuvyntraUIKit()\n    .UseHttpForge()\n    .UseMauiFormValidation()\n    .UseKeyboardManager();\n\nbuilder.Services.AddTransient<MainPageViewModel>();\nbuilder.Services.AddTransient<MainPage>();\n```\n\nRegister both the page and the view-model in `MauiProgram`. `[RegisterViewModel]` is a map for the generator, and it is not the DI registration. Chrome is `NavigationPage` unless the spec names Shell.\n\nLocalStore, NuvexaDB, AppLock, and the rest of the catalog wait until you ask. `/nuvyn.plan` then picks the smallest fit. An outside library needs a **Catalog gap** row in `plan.md`.\n\n**1.2.0** adds `nuvyn adopt`. This is the door for a tree that already exists.\n\n```\ncd FieldApp\nnuvyn adopt --agent cursor\n```\n\nOr point at another folder:\n\n```\nnuvyn adopt --path ../FieldApp --agent copilot\n```\n\nAdopt:\n\n`UseMaui` in a csproj), or when `specs/` folder, and `.nuvyn/adopt-report.md`.`\"mode\": \"adopt\"` in `maui-dev doctor` when MauiDev is on `PATH`.\nIt does **not** add MVVMExpress, Lumina UIKit, or HttpForge. It does **not** edit `MauiProgram`, pages, or `HttpClient` call sites. New work keeps the stack the app already uses. Lumina `NV*` is allowed on new screens only when UIKit is already referenced. `/nuvyn.plan` and `/nuvyn.implement` read `adopt-report.md` first.\n\nOpen the **project folder** in the agent you selected. Run the commands in order. Extra text after a command is the prompt. An empty `/nuvyn.specify` asks you to describe the product.\n\n```\n/nuvyn.constitution → /nuvyn.specify → /nuvyn.clarify → /nuvyn.plan\n    → /nuvyn.checklist → /nuvyn.task → /nuvyn.analysis\n    → /nuvyn.implement → /nuvyn.converge\n```\n\nIn Cursor the skill folders are named `nuvyn-constitution` (a folder name cannot contain `.`). Type `/nuvyn-constitution` in Cursor. Type `/nuvyn.constitution` in Copilot, Claude, Gemini, and the other command-file agents.\n\n| Step | You do | The agent writes | \n|---|---|---|\n| `/nuvyn.constitution` | Optional product rules (PII, lock, offline) | Updates `.nuvyn/constitution.md` . The stack stays locked on a new host. | \n| `/nuvyn.specify` | **Required:** what the app is for | `specs/NNN-short-name/spec.md` ,`.nuvyn/feature.json` ,`checklists/requirements.md` | \n| `/nuvyn.clarify` | Answer at most five questions | Updates `spec.md` | \n| `/nuvyn.plan` | Extra constraints, if any | `plan.md` +`research.md` — packages and one Lumina recipe per screen | \n| `/nuvyn.checklist` | Optional quality review | `checklists/<domain>.md` | \n| `/nuvyn.task` | — | `tasks.md` (`T001 [P] [US1] …` ) | \n| `/nuvyn.analysis` | — | A report only: spec, plan, and tasks agree | \n| `/nuvyn.implement` | Optional scope, such as Foundation only | Host code | \n| `/nuvyn.converge` | — | Appends remaining work to `tasks.md` | \n\nA specify prompt can carry the product in one line:\n\n```\n/nuvyn.specify Resident 311 desk: sign in, report a bin miss, see live bus times.\n/nuvyn.implement Implement only Foundation\n```\n\nStanding law lives in `.nuvyn/reference/constraints.md`. Each slash command stays short and points at that file, so the agent spends the window on the domain instead of restating the catalog every turn.\n\nUI on a new host is Lumina first. One recipe per screen. Bound fields go inside the recipe so they replace the demo seed. Prefer `NVInputField`, `NVPasswordField`, and `NVButton` when an `NV*` control exists. Control reference: [UIKit(MAUI) docs](https://nuvyntralabs.github.io/uikit/docs/).\n\n`nuvyn init` and `nuvyn adopt` write into the folder that agent already reads. The set matches Spec Kit, including more than thirty additional agents.\n\n| Agent | `--agent` | On disk | \n|---|---|---|\n| Cursor | `cursor` | `.cursor/skills/nuvyn-*/` | \n| GitHub Copilot | `copilot` | `.github/skills/` | \n| Claude Code | `claude` | `.claude/commands/` | \n| Gemini CLI | `gemini` | `.gemini/commands/` | \n| Codex CLI | `codex` | `.agents/skills/` | \n| Goose | `goose` | `.goose/recipes/` | \n| Windsurf | `windsurf` | `.windsurf/workflows/` | \n| Generic | `generic` | `.agents/commands/` | \n\nPass `--agent cursor-agent` when that Spec Kit key is the one you already use.\n\nDo not re-run `nuvyn init` on a tree that already exists. After the global tool updates, refresh slash files from inside the app:\n\n```\ncd HarborDesk\nnuvyn update\nnuvyn update --agent cursor\n```\n\n`nuvyn update` overwrites `.nuvyn/templates/`, `.nuvyn/reference/`, and the agent command files. It leaves host code, `specs/`, `.nuvyn/constitution.md`, and `.nuvyn/adopt-report.md` alone. It does not change `PackageReference` versions.\n\n`nuvyn check` confirms `dotnet` and the payload. Inside a greenfield app it also proves the host still uses MVVMExpress, UIKit, HttpForge, FormValidation, and KeyboardManager. Inside an adopted app it prints the inventory and skips that proof.\n\nWhen [MauiDev](https://nuvyntralabs.github.io/toolkits/maui-dev/) is on `PATH`, `init`, `adopt`, and `check` run `maui-dev doctor --path`. A missing doctor is a warning. Nuvyn still succeeds. Install it when you want the environment report:\n\n```\ndotnet tool install -g Plugin.Maui.MauiDev.Cli --source https://api.nuget.org/v3/index.json\nmaui-dev doctor\n```\n\n| What you see | What to do | \n|---|---|\n| `HarborDesk already exists` | `init` is for a new folder. Pick another name, or delete a leftover failed scaffold and retry. For an existing MAUI app,`cd` in and run`nuvyn adopt` . | \n| `is already a Nuvyn project` | Adopt or init already ran. Use `nuvyn update` . | \n| `is not a MAUI app` | `adopt` needs a`UseMaui` csproj in that folder. | \n| `Not a Nuvyn project` | `update` and`check` need a`.nuvyn/` folder from`init` or`adopt` . | \n| Unable to resolve `MainPageViewModel` | Add `builder.Services.AddTransient<MainPageViewModel>()` . | \n| `AddGeneratedViewModels` fails to build | Remove that call. Register the view-model with `AddTransient` . | \n| The agent added LocalStore, Syncfusion, or Refit | You did not ask. Revert. Catalog first, UIKit first. | \n| MAUI workload or TFM errors | Read the printed `maui-dev doctor` report. Do not re-run`nuvyn init` . | \n| Update prompt every few hours | Expected. Answer `n` , or pass`--no-update-check` . | \n\n| Need | Tool | \n|---|---|\n| New Nuvyntra MAUI host and the spec chain | **Nuvyn** —`nuvyn init` , then`nuvyn update` | \n| Existing MAUI app, same spec chain, keep its stack | **Nuvyn** —`nuvyn adopt` | \n| Diagnose an existing MAUI tree | [MauiDev](https://nuvyntralabs.github.io/toolkits/maui-dev/) —`maui-dev doctor` | \n| Sibling `.resx` localization | [NuvLoc](https://nuvyntralabs.github.io/toolkits/nuvloc/) | \n| Any stack, spec only | [GitHub Spec Kit](https://github.com/github/spec-kit) | \n| One runtime plugin | The matching [`Plugin.Maui.*`](https://nuvyntralabs.github.io/packages/) package | \n\n```\ndotnet tool install -g NuvyntraLabs.Nuvyn.Cli --source https://api.nuget.org/v3/index.json\nnuvyn init HarborDesk --agent cursor\ncd HarborDesk\n```\n\nOpen that folder in your agent and run `/nuvyn.constitution`, then `/nuvyn.specify` with the product in one sentence.\n\nIf the app already exists:\n\n```\ncd YourMauiApp\nnuvyn adopt --agent cursor\n```\n\nThen read `.nuvyn/adopt-report.md` before `/nuvyn.plan`.", "url": "https://wpnews.pro/news/nuvyn-cli-1-2-0-spec-driven-net-maui-from-a-new-app-or-one-you-already-have", "canonical_source": "https://dev.to/niladri_prasadpadhy_ccee/nuvyn-cli-120-spec-driven-net-maui-from-a-new-app-or-one-you-already-have-50nd", "published_at": "2026-09-23 08:37:49+00:00", "updated_at": "2026-09-23 08:58:31.292659+00:00", "lang": "en", "topics": ["developer-tools", "ai-agents", "ai-tools"], "entities": ["Nuvyntra Labs", "Nuvyn", "Nuvyn CLI", ".NET MAUI", "NuvyntraLabs.UIKit", "GitHub Spec Kit", "nuget.org", "System.CommandLine"], "alternates": {"html": "https://wpnews.pro/news/nuvyn-cli-1-2-0-spec-driven-net-maui-from-a-new-app-or-one-you-already-have", "markdown": "https://wpnews.pro/news/nuvyn-cli-1-2-0-spec-driven-net-maui-from-a-new-app-or-one-you-already-have.md", "text": "https://wpnews.pro/news/nuvyn-cli-1-2-0-spec-driven-net-maui-from-a-new-app-or-one-you-already-have.txt", "jsonld": "https://wpnews.pro/news/nuvyn-cli-1-2-0-spec-driven-net-maui-from-a-new-app-or-one-you-already-have.jsonld"}}