From 54fba59698e99ae0457144bec462ca01a068d7c8 Mon Sep 17 00:00:00 2001 From: Kamil Listopad Date: Fri, 17 Apr 2026 15:03:56 +0200 Subject: [PATCH 01/11] docs: flesh out mops init and fix user set properties MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit mops init had a two-line description that didn't cover the interactive prompts, what files get created, the --yes behavior, or the vessel.dhall migration, forcing users to read the source. Rewrite to document the prompt flow, the files written, and the project/package split. mops user set listed only name/github/twitter, but the CLI also accepts site and email. Add the missing properties. Also clean up a couple of style nits in user set (Example → Examples, drop non-idiomatic phrasing) for consistency with peer docs. Made-with: Cursor --- docs/docs/cli/00-mops-init.md | 63 +++++++++++++++++++++++- docs/docs/cli/3-user/03-mops-user-set.md | 8 ++- 2 files changed, 67 insertions(+), 4 deletions(-) diff --git a/docs/docs/cli/00-mops-init.md b/docs/docs/cli/00-mops-init.md index d7082541..4eafeb1c 100644 --- a/docs/docs/cli/00-mops-init.md +++ b/docs/docs/cli/00-mops-init.md @@ -5,13 +5,72 @@ sidebar_label: mops init # `mops init` -Initialize a new project or package in the current directory +Initialize a new Motoko project or package in the current directory. + ``` mops init ``` +Exits without changes if `mops.toml` already exists. + +## Interactive prompts + +### 1. Project type + +``` +Select type: +› Project (I just want to use mops packages in my project) + Package (I plan to publish this package on mops) +``` + +- **Project** — you want to consume mops packages. No `[package]` section is written. +- **Package** — you plan to publish to the mops registry. Prompts for metadata and creates starter files. + +### 2. Package metadata (package only) + +- **Name** — defaults to the kebab-cased directory name +- **Description** +- **Repository URL** +- **Keywords** — space-separated +- **License** — `MIT` or `Apache-2.0` +- **Copyright owner** — written into the license file +- **Add example test file?** — defaults to yes, creates `test/lib.test.mo` + +The version is initialized to `1.0.0`. + +### 3. GitHub workflow + +``` +Setup GitHub workflow? (run `mops test` on push) +``` + +When accepted, adds `.github/workflows/mops-test.yml` that runs `mops test` on push to `main`/`master` and on every pull request. + +## What it creates + +1. **`dfx.json`** — sets `defaults.build.packtool = "mops sources"` if `dfx.json` is present. Existing indentation is preserved. +2. **`mops.toml`** — with `[package]` metadata for packages; for projects, populated with the default package set for your detected `dfx` version (fetched from the registry). +3. **`src/lib.mo`** — starter module (package only, when `src/` doesn't exist). +4. **`test/lib.test.mo`** — starter test (package only, when you opted in and `test/` doesn't exist). +5. **`LICENSE`** (and `NOTICE` for Apache-2.0) — package only, filled with the current year and copyright owner. +6. **`README.md`** — package only, with placeholders replaced by the package name. +7. **`.github/workflows/mops-test.yml`** — when the workflow prompt was accepted. +8. **`.mops`** appended to `.gitignore` (created if missing). + +Existing `LICENSE`, `README.md`, and workflow files are not overwritten. + +For projects, `mops install` runs at the end to fetch the default packages. + +### Migrating from Vessel + +If `vessel.dhall` exists, `mops init` reads it and copies the listed dependencies into the new `mops.toml`. Skipped when `--yes` is used. + ## Options ### `--yes`, `-y` -Initialize with all defaults. +Skip prompts and initialize as a **project** with defaults: no `[package]` section, no starter files, GitHub workflow enabled, default packages installed. Useful for CI and scripted scaffolding. + +``` +mops init --yes +``` diff --git a/docs/docs/cli/3-user/03-mops-user-set.md b/docs/docs/cli/3-user/03-mops-user-set.md index 623dabdb..9781931d 100644 --- a/docs/docs/cli/3-user/03-mops-user-set.md +++ b/docs/docs/cli/3-user/03-mops-user-set.md @@ -19,17 +19,21 @@ mops user get ## Available properties - `name` - username on mops.one +- `site` - personal website URL +- `email` - email address - `github` - github username - `twitter` - twitter username -## Example +### Examples ```bash mops user set name zen +mops user set site https://example.com +mops user set email zen@example.com mops user set github ZenVoich mops user set twitter mops_one ``` -Here is how the user info looks like on mops.one: +How this appears on mops.one: ![user info on mops.one](user-info.png) \ No newline at end of file From 9b99e9971cb511e3e8df2e1db599ee256b297d36 Mon Sep 17 00:00:00 2001 From: Kamil Listopad Date: Fri, 17 Apr 2026 15:34:43 +0200 Subject: [PATCH 02/11] feat: mops init uses core + latest moc for standalone projects MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When there is no dfx.json, mops init was silently consulting the installed dfx's --version to pick a default package set, which could pin an old `base` on a fresh standalone Motoko project. Treat the absence of dfx.json as the signal for "standalone Motoko project" and skip the dfx sniff: ask the backend for defaults with an empty version (falls through to latest core) and pin the latest moc in [toolchain] so the project is ready to compile on its own. With dfx.json present the behavior is unchanged — the backend still returns base for old dfx versions and core for newer ones. Made-with: Cursor --- cli/CHANGELOG.md | 1 + cli/commands/init.ts | 41 ++++++++++++++++++++++++++--------- docs/docs/cli/00-mops-init.md | 6 +++-- 3 files changed, 36 insertions(+), 12 deletions(-) diff --git a/cli/CHANGELOG.md b/cli/CHANGELOG.md index ea0cfab2..2261d011 100644 --- a/cli/CHANGELOG.md +++ b/cli/CHANGELOG.md @@ -1,6 +1,7 @@ # Mops CLI Changelog ## Next +- `mops init` on projects without `dfx.json` now treats them as standalone Motoko projects: adds latest `core` to `[dependencies]` and pins the latest `moc` in `[toolchain]`, instead of falling back to the installed dfx's default packages ## 2.11.0 - Add `mops migrate new ` and `mops migrate freeze` commands for managing enhanced migration chains diff --git a/cli/commands/init.ts b/cli/commands/init.ts index 43cf7e5a..1a460e4a 100644 --- a/cli/commands/init.ts +++ b/cli/commands/init.ts @@ -11,6 +11,7 @@ import { installAll } from "./install/install-all.js"; import { VesselConfig, readVesselConfig } from "../vessel.js"; import { Config, Dependencies } from "../types.js"; import { template } from "./template.js"; +import { toolchain } from "./toolchain/index.js"; import { kebabCase } from "change-case"; export async function init({ yes = false } = {}) { @@ -222,24 +223,38 @@ async function applyInit({ } // get default packages + // + // Branching on dfx.json presence: + // - With dfx.json: query the backend by the detected dfx version. The backend + // returns `base` pinned to whatever ships with that dfx (for older dfx + // versions whose bundled moc can't use `core`), or latest `core` for newer + // dfx versions (>= 0.28) via its fallthrough case. + // - Without dfx.json: this is a standalone Motoko project. We pin the latest + // moc in [toolchain] ourselves and ask the backend with an empty version + // string, which falls through to the latest `core`. if (type === "project") { let compatible = await checkApiCompatibility(); if (!compatible) { return; } - let dfxVersion = dfxJsonData?.dfx || ""; - if (!dfxVersion) { - try { - let res = execSync("dfx --version").toString(); - let match = res.match(/\d+\.\d+\.\d+/); - if (match) { - dfxVersion = match[0]; - } - } catch {} + let dfxVersion = ""; + if (dfxJsonData) { + dfxVersion = dfxJsonData.dfx || ""; + if (!dfxVersion) { + try { + let res = execSync("dfx --version").toString(); + let match = res.match(/\d+\.\d+\.\d+/); + if (match) { + dfxVersion = match[0]; + } + } catch {} + } + console.log(`Fetching default packages for dfx ${dfxVersion}...`); + } else { + console.log("Fetching default packages..."); } - console.log(`Fetching default packages for dfx ${dfxVersion}...`); let actor = await mainActor(); let defaultPackages = await actor.getDefaultPackages(dfxVersion); @@ -257,6 +272,12 @@ async function applyInit({ writeConfig(config, configFile); console.log(chalk.green("Created"), "mops.toml"); + // standalone Motoko project: pin latest moc. toolchain.use reads mops.toml, + // so it must run after writeConfig above. + if (type === "project" && !dfxJsonData) { + await toolchain.use("moc", "latest"); + } + // add src/lib.mo if (type === "package" && !existsSync(path.join(process.cwd(), "src"))) { await template("lib.mo"); diff --git a/docs/docs/cli/00-mops-init.md b/docs/docs/cli/00-mops-init.md index 4eafeb1c..20302ac1 100644 --- a/docs/docs/cli/00-mops-init.md +++ b/docs/docs/cli/00-mops-init.md @@ -49,7 +49,9 @@ When accepted, adds `.github/workflows/mops-test.yml` that runs `mops test` on p ## What it creates 1. **`dfx.json`** — sets `defaults.build.packtool = "mops sources"` if `dfx.json` is present. Existing indentation is preserved. -2. **`mops.toml`** — with `[package]` metadata for packages; for projects, populated with the default package set for your detected `dfx` version (fetched from the registry). +2. **`mops.toml`** — with `[package]` metadata for packages. For projects: + - **With `dfx.json`** — populated with the default package set for your detected `dfx` version: `base` pinned to the version shipped with that dfx, or latest `core` for dfx versions whose bundled `moc` supports it. + - **Without `dfx.json`** — standalone Motoko project. Adds latest `core` to `[dependencies]` and pins the latest `moc` in `[toolchain]`. 3. **`src/lib.mo`** — starter module (package only, when `src/` doesn't exist). 4. **`test/lib.test.mo`** — starter test (package only, when you opted in and `test/` doesn't exist). 5. **`LICENSE`** (and `NOTICE` for Apache-2.0) — package only, filled with the current year and copyright owner. @@ -69,7 +71,7 @@ If `vessel.dhall` exists, `mops init` reads it and copies the listed dependencie ### `--yes`, `-y` -Skip prompts and initialize as a **project** with defaults: no `[package]` section, no starter files, GitHub workflow enabled, default packages installed. Useful for CI and scripted scaffolding. +Skip prompts and initialize as a **project** with defaults: no `[package]` section, no starter files, GitHub workflow enabled, default packages installed per the rules in [What it creates](#what-it-creates). Useful for CI and scripted scaffolding. ``` mops init --yes From 7ca13e826e16be9e3cd43880d4e50a79d5684ade Mon Sep 17 00:00:00 2001 From: Kamil Listopad Date: Fri, 17 Apr 2026 15:38:47 +0200 Subject: [PATCH 03/11] refactor: trim init comment and update mops-cli skill MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Narrating-code comment replaced with a two-line intent comment; the condition duplication is hoisted into a `standalone` local. The mops-cli skill's "New project" section now reflects the new init behavior — `mops toolchain use moc latest` and `mops add core` are redundant when there is no dfx.json. Made-with: Cursor --- .agents/skills/mops-cli/SKILL.md | 6 +++--- cli/commands/init.ts | 19 ++++++------------- 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/.agents/skills/mops-cli/SKILL.md b/.agents/skills/mops-cli/SKILL.md index d1d443ce..492ceb89 100644 --- a/.agents/skills/mops-cli/SKILL.md +++ b/.agents/skills/mops-cli/SKILL.md @@ -206,12 +206,12 @@ args = ["-A=M0198"] ### New project ```bash -mops init -y -mops toolchain use moc latest # pin latest moc (non-interactive) +mops init -y # pins latest moc + adds latest core (when no dfx.json) mops toolchain use lintoko latest # pin latest lintoko -mops add core ``` +If `dfx.json` is present, `mops init -y` adds `base` keyed to the dfx version (or `core` for dfx ≥ 0.28) and does not pin `moc`. Run `mops toolchain use moc latest` and `mops remove base && mops add core` if you want the standalone setup anyway. + Then configure `[moc].args`, `[canisters]`, and `[build]` in `mops.toml`. To update tools later: `mops toolchain update moc` or `mops toolchain update` (all tools). diff --git a/cli/commands/init.ts b/cli/commands/init.ts index 1a460e4a..0176df81 100644 --- a/cli/commands/init.ts +++ b/cli/commands/init.ts @@ -222,16 +222,10 @@ async function applyInit({ } } - // get default packages - // - // Branching on dfx.json presence: - // - With dfx.json: query the backend by the detected dfx version. The backend - // returns `base` pinned to whatever ships with that dfx (for older dfx - // versions whose bundled moc can't use `core`), or latest `core` for newer - // dfx versions (>= 0.28) via its fallthrough case. - // - Without dfx.json: this is a standalone Motoko project. We pin the latest - // moc in [toolchain] ourselves and ask the backend with an empty version - // string, which falls through to the latest `core`. + // Standalone Motoko project (no dfx.json): add latest core and pin latest moc. + // With dfx.json: ask the backend for defaults keyed on the dfx version. + let standalone = type === "project" && !dfxJsonData; + if (type === "project") { let compatible = await checkApiCompatibility(); if (!compatible) { @@ -272,9 +266,8 @@ async function applyInit({ writeConfig(config, configFile); console.log(chalk.green("Created"), "mops.toml"); - // standalone Motoko project: pin latest moc. toolchain.use reads mops.toml, - // so it must run after writeConfig above. - if (type === "project" && !dfxJsonData) { + // toolchain.use reads mops.toml, so it must run after writeConfig above. + if (standalone) { await toolchain.use("moc", "latest"); } From ae110caac3e06e1daceafa7d3ffb8bc03fc9c5d1 Mon Sep 17 00:00:00 2001 From: Kamil Listopad Date: Fri, 17 Apr 2026 15:39:21 +0200 Subject: [PATCH 04/11] docs: mention core before base in mops init docs Made-with: Cursor --- docs/docs/cli/00-mops-init.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/cli/00-mops-init.md b/docs/docs/cli/00-mops-init.md index 20302ac1..3381daeb 100644 --- a/docs/docs/cli/00-mops-init.md +++ b/docs/docs/cli/00-mops-init.md @@ -50,7 +50,7 @@ When accepted, adds `.github/workflows/mops-test.yml` that runs `mops test` on p 1. **`dfx.json`** — sets `defaults.build.packtool = "mops sources"` if `dfx.json` is present. Existing indentation is preserved. 2. **`mops.toml`** — with `[package]` metadata for packages. For projects: - - **With `dfx.json`** — populated with the default package set for your detected `dfx` version: `base` pinned to the version shipped with that dfx, or latest `core` for dfx versions whose bundled `moc` supports it. + - **With `dfx.json`** — populated with the default package set for your detected `dfx` version: latest `core` when the bundled `moc` supports it, otherwise `base` pinned to the version shipped with that dfx. - **Without `dfx.json`** — standalone Motoko project. Adds latest `core` to `[dependencies]` and pins the latest `moc` in `[toolchain]`. 3. **`src/lib.mo`** — starter module (package only, when `src/` doesn't exist). 4. **`test/lib.test.mo`** — starter test (package only, when you opted in and `test/` doesn't exist). From e3fc99d63017bad548b9a3d8c2d077db3d911484 Mon Sep 17 00:00:00 2001 From: Kamil Listopad Date: Fri, 17 Apr 2026 15:41:43 +0200 Subject: [PATCH 05/11] docs(skill): restore moc/core steps with no-op comments Made-with: Cursor --- .agents/skills/mops-cli/SKILL.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.agents/skills/mops-cli/SKILL.md b/.agents/skills/mops-cli/SKILL.md index 492ceb89..ab960e29 100644 --- a/.agents/skills/mops-cli/SKILL.md +++ b/.agents/skills/mops-cli/SKILL.md @@ -208,10 +208,10 @@ args = ["-A=M0198"] ```bash mops init -y # pins latest moc + adds latest core (when no dfx.json) mops toolchain use lintoko latest # pin latest lintoko +mops toolchain use moc latest # no-op after init -y without dfx.json; forces latest moc otherwise +mops add core # no-op if already a dep; adds it when init picked base instead ``` -If `dfx.json` is present, `mops init -y` adds `base` keyed to the dfx version (or `core` for dfx ≥ 0.28) and does not pin `moc`. Run `mops toolchain use moc latest` and `mops remove base && mops add core` if you want the standalone setup anyway. - Then configure `[moc].args`, `[canisters]`, and `[build]` in `mops.toml`. To update tools later: `mops toolchain update moc` or `mops toolchain update` (all tools). From 87dc3f12bbe998653b27f90fa82a12a417d54a98 Mon Sep 17 00:00:00 2001 From: Kamil Listopad Date: Fri, 17 Apr 2026 15:44:08 +0200 Subject: [PATCH 06/11] refactor: gate moc pin on dfx.json presence alone, not project type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit dfx.json presence is the legacy signal for 'something else will provide moc'. Whether the user is building a library or a project deployed via dfx / icp-cli / raw wasm is independent — pin moc whenever dfx is not in the picture, for both project and package. Made-with: Cursor --- .agents/skills/mops-cli/SKILL.md | 2 +- cli/CHANGELOG.md | 2 +- cli/commands/init.ts | 10 +++++----- docs/docs/cli/00-mops-init.md | 8 +++++--- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/.agents/skills/mops-cli/SKILL.md b/.agents/skills/mops-cli/SKILL.md index ab960e29..673ee9a9 100644 --- a/.agents/skills/mops-cli/SKILL.md +++ b/.agents/skills/mops-cli/SKILL.md @@ -206,7 +206,7 @@ args = ["-A=M0198"] ### New project ```bash -mops init -y # pins latest moc + adds latest core (when no dfx.json) +mops init -y # pins latest moc (when no dfx.json) + adds latest core mops toolchain use lintoko latest # pin latest lintoko mops toolchain use moc latest # no-op after init -y without dfx.json; forces latest moc otherwise mops add core # no-op if already a dep; adds it when init picked base instead diff --git a/cli/CHANGELOG.md b/cli/CHANGELOG.md index 2261d011..e2be3375 100644 --- a/cli/CHANGELOG.md +++ b/cli/CHANGELOG.md @@ -1,7 +1,7 @@ # Mops CLI Changelog ## Next -- `mops init` on projects without `dfx.json` now treats them as standalone Motoko projects: adds latest `core` to `[dependencies]` and pins the latest `moc` in `[toolchain]`, instead of falling back to the installed dfx's default packages +- `mops init` without `dfx.json` now pins the latest `moc` in `[toolchain]` (since no dfx toolchain provides it) and picks latest `core` for projects, instead of consulting `dfx --version` from `PATH` and pulling in the legacy `base` set ## 2.11.0 - Add `mops migrate new ` and `mops migrate freeze` commands for managing enhanced migration chains diff --git a/cli/commands/init.ts b/cli/commands/init.ts index 0176df81..43755d38 100644 --- a/cli/commands/init.ts +++ b/cli/commands/init.ts @@ -222,10 +222,9 @@ async function applyInit({ } } - // Standalone Motoko project (no dfx.json): add latest core and pin latest moc. - // With dfx.json: ask the backend for defaults keyed on the dfx version. - let standalone = type === "project" && !dfxJsonData; - + // For projects, populate default dependencies. + // With dfx.json: backend picks defaults keyed on the dfx version (legacy path). + // Without dfx.json: backend falls through to latest core. if (type === "project") { let compatible = await checkApiCompatibility(); if (!compatible) { @@ -266,8 +265,9 @@ async function applyInit({ writeConfig(config, configFile); console.log(chalk.green("Created"), "mops.toml"); + // Without dfx.json, nothing else pins moc — do it here. // toolchain.use reads mops.toml, so it must run after writeConfig above. - if (standalone) { + if (!dfxJsonData) { await toolchain.use("moc", "latest"); } diff --git a/docs/docs/cli/00-mops-init.md b/docs/docs/cli/00-mops-init.md index 3381daeb..049a300e 100644 --- a/docs/docs/cli/00-mops-init.md +++ b/docs/docs/cli/00-mops-init.md @@ -49,9 +49,11 @@ When accepted, adds `.github/workflows/mops-test.yml` that runs `mops test` on p ## What it creates 1. **`dfx.json`** — sets `defaults.build.packtool = "mops sources"` if `dfx.json` is present. Existing indentation is preserved. -2. **`mops.toml`** — with `[package]` metadata for packages. For projects: - - **With `dfx.json`** — populated with the default package set for your detected `dfx` version: latest `core` when the bundled `moc` supports it, otherwise `base` pinned to the version shipped with that dfx. - - **Without `dfx.json`** — standalone Motoko project. Adds latest `core` to `[dependencies]` and pins the latest `moc` in `[toolchain]`. +2. **`mops.toml`** — with `[package]` metadata for packages. For projects, `[dependencies]` is populated: + - **Without `dfx.json`** — latest `core`. + - **With `dfx.json`** — default package set keyed on your detected `dfx` version: latest `core` when the bundled `moc` supports it, otherwise `base` pinned to the version shipped with that dfx (legacy). + + When `dfx.json` is absent, `[toolchain].moc` is also pinned to the latest `moc` — since no `dfx` toolchain is going to provide it. 3. **`src/lib.mo`** — starter module (package only, when `src/` doesn't exist). 4. **`test/lib.test.mo`** — starter test (package only, when you opted in and `test/` doesn't exist). 5. **`LICENSE`** (and `NOTICE` for Apache-2.0) — package only, filled with the current year and copyright owner. From 5b04259f91dacd8f722a88b665a5a63470bb0fbd Mon Sep 17 00:00:00 2001 From: Kamil Listopad Date: Fri, 17 Apr 2026 15:45:16 +0200 Subject: [PATCH 07/11] refactor(init): tighten diff to the actual behavior change Made-with: Cursor --- cli/commands/init.ts | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/cli/commands/init.ts b/cli/commands/init.ts index 43755d38..11c618d4 100644 --- a/cli/commands/init.ts +++ b/cli/commands/init.ts @@ -222,32 +222,29 @@ async function applyInit({ } } - // For projects, populate default dependencies. - // With dfx.json: backend picks defaults keyed on the dfx version (legacy path). - // Without dfx.json: backend falls through to latest core. + // get default packages if (type === "project") { let compatible = await checkApiCompatibility(); if (!compatible) { return; } - let dfxVersion = ""; - if (dfxJsonData) { - dfxVersion = dfxJsonData.dfx || ""; - if (!dfxVersion) { - try { - let res = execSync("dfx --version").toString(); - let match = res.match(/\d+\.\d+\.\d+/); - if (match) { - dfxVersion = match[0]; - } - } catch {} - } - console.log(`Fetching default packages for dfx ${dfxVersion}...`); - } else { - console.log("Fetching default packages..."); + let dfxVersion = dfxJsonData?.dfx || ""; + // Skip `dfx --version` fallback without dfx.json — otherwise a stale global + // dfx would force legacy `base` defaults on a non-dfx project. + if (dfxJsonData && !dfxVersion) { + try { + let res = execSync("dfx --version").toString(); + let match = res.match(/\d+\.\d+\.\d+/); + if (match) { + dfxVersion = match[0]; + } + } catch {} } + console.log( + `Fetching default packages${dfxVersion ? ` for dfx ${dfxVersion}` : ""}...`, + ); let actor = await mainActor(); let defaultPackages = await actor.getDefaultPackages(dfxVersion); From 1f09f225795709de5fe2a80a88185132d921bdee Mon Sep 17 00:00:00 2001 From: Kamil Listopad Date: Fri, 17 Apr 2026 15:47:55 +0200 Subject: [PATCH 08/11] fix(init): pin moc after installAll, not before toolchain.use validates installed dep manifests via checkRequirements, so it crashes if run before installAll downloads the deps. Caught by smoke-testing `mops init -y` in an empty dir. Made-with: Cursor --- cli/commands/init.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/cli/commands/init.ts b/cli/commands/init.ts index 11c618d4..7a977983 100644 --- a/cli/commands/init.ts +++ b/cli/commands/init.ts @@ -262,12 +262,6 @@ async function applyInit({ writeConfig(config, configFile); console.log(chalk.green("Created"), "mops.toml"); - // Without dfx.json, nothing else pins moc — do it here. - // toolchain.use reads mops.toml, so it must run after writeConfig above. - if (!dfxJsonData) { - await toolchain.use("moc", "latest"); - } - // add src/lib.mo if (type === "package" && !existsSync(path.join(process.cwd(), "src"))) { await template("lib.mo"); @@ -312,5 +306,11 @@ async function applyInit({ await installAll({ verbose: true }); } + // Without dfx.json, nothing else pins moc — do it here. Must run after + // installAll because toolchain.use validates installed dep manifests. + if (!dfxJsonData) { + await toolchain.use("moc", "latest"); + } + console.log(chalk.green("Done!")); } From 8016f21d0ba2f87aedfef7c24e42a4ce48887825 Mon Sep 17 00:00:00 2001 From: Kamil Listopad Date: Fri, 17 Apr 2026 15:50:32 +0200 Subject: [PATCH 09/11] docs: fix changelog + init docs to match actual PR scope MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The dep-selection behavior didn't actually change — the backend already returns `core` for modern dfx, empty version, or unknown versions via its fallthrough case. The only real change in this PR is pinning latest `moc` when no `dfx.json` is present. Made-with: Cursor --- cli/CHANGELOG.md | 2 +- cli/commands/init.ts | 4 +--- docs/docs/cli/00-mops-init.md | 4 +--- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/cli/CHANGELOG.md b/cli/CHANGELOG.md index e2be3375..eac75682 100644 --- a/cli/CHANGELOG.md +++ b/cli/CHANGELOG.md @@ -1,7 +1,7 @@ # Mops CLI Changelog ## Next -- `mops init` without `dfx.json` now pins the latest `moc` in `[toolchain]` (since no dfx toolchain provides it) and picks latest `core` for projects, instead of consulting `dfx --version` from `PATH` and pulling in the legacy `base` set +- `mops init` without `dfx.json` now pins the latest `moc` in `[toolchain]`, since no dfx toolchain is going to provide it ## 2.11.0 - Add `mops migrate new ` and `mops migrate freeze` commands for managing enhanced migration chains diff --git a/cli/commands/init.ts b/cli/commands/init.ts index 7a977983..5f3e2f0d 100644 --- a/cli/commands/init.ts +++ b/cli/commands/init.ts @@ -230,9 +230,7 @@ async function applyInit({ } let dfxVersion = dfxJsonData?.dfx || ""; - // Skip `dfx --version` fallback without dfx.json — otherwise a stale global - // dfx would force legacy `base` defaults on a non-dfx project. - if (dfxJsonData && !dfxVersion) { + if (!dfxVersion) { try { let res = execSync("dfx --version").toString(); let match = res.match(/\d+\.\d+\.\d+/); diff --git a/docs/docs/cli/00-mops-init.md b/docs/docs/cli/00-mops-init.md index 049a300e..20e38cfb 100644 --- a/docs/docs/cli/00-mops-init.md +++ b/docs/docs/cli/00-mops-init.md @@ -49,9 +49,7 @@ When accepted, adds `.github/workflows/mops-test.yml` that runs `mops test` on p ## What it creates 1. **`dfx.json`** — sets `defaults.build.packtool = "mops sources"` if `dfx.json` is present. Existing indentation is preserved. -2. **`mops.toml`** — with `[package]` metadata for packages. For projects, `[dependencies]` is populated: - - **Without `dfx.json`** — latest `core`. - - **With `dfx.json`** — default package set keyed on your detected `dfx` version: latest `core` when the bundled `moc` supports it, otherwise `base` pinned to the version shipped with that dfx (legacy). +2. **`mops.toml`** — `[package]` metadata for packages. For projects, `[dependencies]` is populated with the default package set keyed on your `dfx` version (from `dfx.json` or `dfx --version` on `PATH`): latest `core` for modern `dfx` (≥ 0.28), empty, or missing; otherwise `base` pinned to whatever ships with that older `dfx`. When `dfx.json` is absent, `[toolchain].moc` is also pinned to the latest `moc` — since no `dfx` toolchain is going to provide it. 3. **`src/lib.mo`** — starter module (package only, when `src/` doesn't exist). From c49af9ea13b6f5be034ebb38a77da3901852161c Mon Sep 17 00:00:00 2001 From: Kamil Listopad Date: Fri, 17 Apr 2026 15:51:30 +0200 Subject: [PATCH 10/11] revert: drop init.ts behavior change, keep docs-only PR MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Existing init behavior already returns latest `core` for modern dfx, missing dfx, and unknown versions via the backend's fallthrough case. No CLI change is warranted — just documentation. Made-with: Cursor --- .agents/skills/mops-cli/SKILL.md | 6 +++--- cli/CHANGELOG.md | 1 - cli/commands/init.ts | 11 +---------- docs/docs/cli/00-mops-init.md | 2 -- 4 files changed, 4 insertions(+), 16 deletions(-) diff --git a/.agents/skills/mops-cli/SKILL.md b/.agents/skills/mops-cli/SKILL.md index 673ee9a9..d1d443ce 100644 --- a/.agents/skills/mops-cli/SKILL.md +++ b/.agents/skills/mops-cli/SKILL.md @@ -206,10 +206,10 @@ args = ["-A=M0198"] ### New project ```bash -mops init -y # pins latest moc (when no dfx.json) + adds latest core +mops init -y +mops toolchain use moc latest # pin latest moc (non-interactive) mops toolchain use lintoko latest # pin latest lintoko -mops toolchain use moc latest # no-op after init -y without dfx.json; forces latest moc otherwise -mops add core # no-op if already a dep; adds it when init picked base instead +mops add core ``` Then configure `[moc].args`, `[canisters]`, and `[build]` in `mops.toml`. diff --git a/cli/CHANGELOG.md b/cli/CHANGELOG.md index eac75682..ea0cfab2 100644 --- a/cli/CHANGELOG.md +++ b/cli/CHANGELOG.md @@ -1,7 +1,6 @@ # Mops CLI Changelog ## Next -- `mops init` without `dfx.json` now pins the latest `moc` in `[toolchain]`, since no dfx toolchain is going to provide it ## 2.11.0 - Add `mops migrate new ` and `mops migrate freeze` commands for managing enhanced migration chains diff --git a/cli/commands/init.ts b/cli/commands/init.ts index 5f3e2f0d..43cf7e5a 100644 --- a/cli/commands/init.ts +++ b/cli/commands/init.ts @@ -11,7 +11,6 @@ import { installAll } from "./install/install-all.js"; import { VesselConfig, readVesselConfig } from "../vessel.js"; import { Config, Dependencies } from "../types.js"; import { template } from "./template.js"; -import { toolchain } from "./toolchain/index.js"; import { kebabCase } from "change-case"; export async function init({ yes = false } = {}) { @@ -240,9 +239,7 @@ async function applyInit({ } catch {} } - console.log( - `Fetching default packages${dfxVersion ? ` for dfx ${dfxVersion}` : ""}...`, - ); + console.log(`Fetching default packages for dfx ${dfxVersion}...`); let actor = await mainActor(); let defaultPackages = await actor.getDefaultPackages(dfxVersion); @@ -304,11 +301,5 @@ async function applyInit({ await installAll({ verbose: true }); } - // Without dfx.json, nothing else pins moc — do it here. Must run after - // installAll because toolchain.use validates installed dep manifests. - if (!dfxJsonData) { - await toolchain.use("moc", "latest"); - } - console.log(chalk.green("Done!")); } diff --git a/docs/docs/cli/00-mops-init.md b/docs/docs/cli/00-mops-init.md index 20e38cfb..2f2c825c 100644 --- a/docs/docs/cli/00-mops-init.md +++ b/docs/docs/cli/00-mops-init.md @@ -50,8 +50,6 @@ When accepted, adds `.github/workflows/mops-test.yml` that runs `mops test` on p 1. **`dfx.json`** — sets `defaults.build.packtool = "mops sources"` if `dfx.json` is present. Existing indentation is preserved. 2. **`mops.toml`** — `[package]` metadata for packages. For projects, `[dependencies]` is populated with the default package set keyed on your `dfx` version (from `dfx.json` or `dfx --version` on `PATH`): latest `core` for modern `dfx` (≥ 0.28), empty, or missing; otherwise `base` pinned to whatever ships with that older `dfx`. - - When `dfx.json` is absent, `[toolchain].moc` is also pinned to the latest `moc` — since no `dfx` toolchain is going to provide it. 3. **`src/lib.mo`** — starter module (package only, when `src/` doesn't exist). 4. **`test/lib.test.mo`** — starter test (package only, when you opted in and `test/` doesn't exist). 5. **`LICENSE`** (and `NOTICE` for Apache-2.0) — package only, filled with the current year and copyright owner. From d5c79c204e7324000cce16eb0c44106b8a314eb9 Mon Sep 17 00:00:00 2001 From: Kamil Listopad Date: Fri, 17 Apr 2026 15:56:11 +0200 Subject: [PATCH 11/11] docs: apply review fixes - restore ## Examples as h2 on mops user set - split the dfx version matrix description into two sentences and name the 0.9.0-0.27.0 range explicitly instead of 'modern dfx' - call out that vessel dev-dependencies are not migrated - add trailing newline Made-with: Cursor --- docs/docs/cli/00-mops-init.md | 4 ++-- docs/docs/cli/3-user/03-mops-user-set.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/docs/cli/00-mops-init.md b/docs/docs/cli/00-mops-init.md index 2f2c825c..52c92c5d 100644 --- a/docs/docs/cli/00-mops-init.md +++ b/docs/docs/cli/00-mops-init.md @@ -49,7 +49,7 @@ When accepted, adds `.github/workflows/mops-test.yml` that runs `mops test` on p ## What it creates 1. **`dfx.json`** — sets `defaults.build.packtool = "mops sources"` if `dfx.json` is present. Existing indentation is preserved. -2. **`mops.toml`** — `[package]` metadata for packages. For projects, `[dependencies]` is populated with the default package set keyed on your `dfx` version (from `dfx.json` or `dfx --version` on `PATH`): latest `core` for modern `dfx` (≥ 0.28), empty, or missing; otherwise `base` pinned to whatever ships with that older `dfx`. +2. **`mops.toml`** — `[package]` metadata for packages. For projects, `[dependencies]` is populated with the default package set keyed on your `dfx` version (from `dfx.json` or `dfx --version` on `PATH`). Older `dfx` versions `0.9.0`–`0.27.0` resolve to the `base` release bundled with that `dfx`. For any other version, or when `dfx` cannot be detected, the latest `core` is used. 3. **`src/lib.mo`** — starter module (package only, when `src/` doesn't exist). 4. **`test/lib.test.mo`** — starter test (package only, when you opted in and `test/` doesn't exist). 5. **`LICENSE`** (and `NOTICE` for Apache-2.0) — package only, filled with the current year and copyright owner. @@ -63,7 +63,7 @@ For projects, `mops install` runs at the end to fetch the default packages. ### Migrating from Vessel -If `vessel.dhall` exists, `mops init` reads it and copies the listed dependencies into the new `mops.toml`. Skipped when `--yes` is used. +If `vessel.dhall` exists, `mops init` reads it and copies the listed dependencies into the new `mops.toml`. Dev-dependencies are not migrated. Skipped when `--yes` is used. ## Options diff --git a/docs/docs/cli/3-user/03-mops-user-set.md b/docs/docs/cli/3-user/03-mops-user-set.md index 9781931d..0860a5c1 100644 --- a/docs/docs/cli/3-user/03-mops-user-set.md +++ b/docs/docs/cli/3-user/03-mops-user-set.md @@ -24,7 +24,7 @@ mops user get - `github` - github username - `twitter` - twitter username -### Examples +## Examples ```bash mops user set name zen @@ -36,4 +36,4 @@ mops user set twitter mops_one How this appears on mops.one: -![user info on mops.one](user-info.png) \ No newline at end of file +![user info on mops.one](user-info.png)