m2s2-cli 0.2.15

CLI for scaffolding M²S² design system projects
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
# m2s2-cli

[![GitHub Sponsors](https://img.shields.io/github/sponsors/mgmaster24?style=flat&logo=githubsponsors&label=Sponsor)](https://github.com/sponsors/mgmaster24)

The official CLI for scaffolding and working with [M²S²](https://github.com/M2S2-Engineering-Group) design system projects. Create new frontend (React, Angular, Vue), backend (Go, Node, Python), or fullstack projects pre-wired with M²S² components, generate components and pages, and keep your installation up to date — all from the terminal.

## Table of Contents

- [Installation]#installation
- [Commands]#commands
  - [new]#m2s2-new
  - [generate component]#m2s2-generate-component
  - [generate page]#m2s2-generate-page
  - [generate service]#m2s2-generate-service
  - [upgrade]#m2s2-upgrade
  - [completions]#m2s2-completions
- [Building from Source]#building-from-source
- [Testing]#testing
- [Project Structure]#project-structure
- [Workflows]#workflows
- [Contributing]#contributing

---

## Installation

### Shell installer (macOS / Linux)

```bash
curl --proto '=https' --tlsv1.2 -LsSf \
  https://github.com/M2S2-Engineering-Group/m2s2-cli/releases/latest/download/m2s2-cli-installer.sh | sh
```

### PowerShell (Windows)

```powershell
irm https://github.com/M2S2-Engineering-Group/m2s2-cli/releases/latest/download/m2s2-cli-installer.ps1 | iex
```

### npm / npx

```bash
# Install globally
npm install -g @m2s2/cli

# Or run without installing
npx @m2s2/cli new my-app
```

### Cargo

```bash
cargo install m2s2-cli
```

### Verify installation

```bash
m2s2 --version
```

---

## Commands

### `m2s2 new`

Scaffold a new project pre-configured with the M²S² design system.

```bash
m2s2 new <name> [OPTIONS]
```

**Arguments**

| Argument | Description |
|----------|-------------|
| `<name>` | Project directory name |

**Options**

| Flag | Values | Description |
|------|--------|-------------|
| `--project-type` | `frontend` `backend` `fullstack` | Project type. Prompted interactively if omitted. |
| `--framework` | `react` `angular` `vue` | Frontend framework. Required for frontend and fullstack projects. |
| `--runtime` | `go` `node` `python` | Backend runtime. Required for backend and fullstack projects. |
| `--api-framework` | `gin` `echo` `fiber` `express` `fastify` `fastapi` `flask` | API framework. Infers `--runtime` if omitted. |
| `--skip-install` | | Skip `npm install` / `go mod tidy` / `pip install` after scaffolding. |
| `--offline` | | Skip npm version resolution (uses placeholder versions). Intended for testing. |

**Examples**

```bash
# Interactive — prompts for all choices
m2s2 new my-app

# Frontend
m2s2 new my-app --project-type frontend --framework react
m2s2 new my-app --project-type frontend --framework angular
m2s2 new my-app --project-type frontend --framework vue

# Backend — Go
m2s2 new my-api --project-type backend --runtime go --api-framework gin
m2s2 new my-api --project-type backend --runtime go --api-framework echo
m2s2 new my-api --project-type backend --runtime go --api-framework fiber

# Backend — Node
m2s2 new my-api --project-type backend --runtime node --api-framework express
m2s2 new my-api --project-type backend --runtime node --api-framework fastify

# Backend — Python (creates a .venv and runs pip install)
m2s2 new my-api --project-type backend --runtime python --api-framework fastapi
m2s2 new my-api --project-type backend --runtime python --api-framework flask

# Fullstack
m2s2 new my-app --project-type fullstack --framework react --runtime go --api-framework gin
m2s2 new my-app --project-type fullstack --framework vue --runtime python --api-framework fastapi

# Skip install (useful in CI)
m2s2 new my-app --project-type frontend --framework react --skip-install
```

**What gets generated**

*React*
```
my-app/
├── src/
│   ├── main.tsx        # BrowserRouter + M2S2Provider
│   ├── App.tsx         # Navbar + Footer wired with M²S² configs
│   └── App.scss
├── index.html
├── package.json        # @m2s2/react-lib, @m2s2/tokens, Vite, TypeScript
├── vite.config.ts
├── tsconfig.json
└── .gitignore
```

*Angular*
```
my-app/
├── src/
│   ├── main.ts
│   ├── styles.scss
│   ├── index.html
│   └── app/
│       ├── app.component.ts    # Standalone, imports NavbarComponent + FooterComponent
│       ├── app.component.html
│       ├── app.component.scss
│       ├── app.routes.ts
│       └── app.config.ts
├── package.json                # @m2s2/ng-lib, @m2s2/tokens, Angular 21
├── angular.json
├── tsconfig.json
├── tsconfig.app.json
└── .gitignore
```

*Vue*
```
my-app/
├── src/
│   ├── main.ts         # createApp with @m2s2/vue-lib styles
│   ├── App.vue         # Navbar + Footer wired with M²S² configs
│   └── App.scss
├── index.html
├── package.json        # @m2s2/vue-lib, @m2s2/tokens, Vite, TypeScript
├── vite.config.ts
├── tsconfig.json
└── .gitignore
```

---

### `m2s2 generate component`

Scaffold a new component inside an existing M²S² project.

```bash
m2s2 generate component <name> [OPTIONS]
```

Run this command from your project root. The framework is detected automatically by reading `package.json` — no flag needed if your project was created with `m2s2 new`.

**Arguments**

| Argument | Description |
|----------|-------------|
| `<name>` | Component name. Accepts any casing — `MyCard`, `my-card`, and `myCard` all produce the same output. |

**Options**

| Flag | Description |
|------|-------------|
| `--framework <react\|angular\|vue>` | Override framework detection. |
| `--path <dir>` | Override the output directory. A subdirectory named after the component is always created inside `<dir>`. |

**Examples**

```bash
# Auto-detect framework from package.json
m2s2 generate component HeroSection

# Kebab-case input — same result
m2s2 generate component hero-section

# Override output directory
m2s2 generate component HeroSection --path src/features

# Override framework
m2s2 generate component HeroSection --framework vue
```

**What gets generated**

*React* — written to `src/components/<Name>/`
```
src/components/HeroSection/
├── HeroSection.tsx     # Typed props interface, BEM className application
├── HeroSection.scss    # Scoped class stub (.hero-section)
└── index.ts            # Barrel re-export
```

*Angular* — written to `src/app/components/<name>/`
```
src/app/components/hero-section/
├── hero-section.component.ts    # Standalone component, app-hero-section selector
├── hero-section.component.html  # BEM wrapper div
└── hero-section.component.scss  # Scoped class stub (.hero-section)
```

*Vue* — written to `src/components/<Name>/`
```
src/components/HeroSection/
├── HeroSection.vue     # <script setup> SFC with slot and scoped SCSS
├── HeroSection.scss    # Scoped class stub (.hero-section)
└── index.ts            # Barrel re-export
```

---

### `m2s2 generate page`

Scaffold a new route page inside an existing M²S² project.

```bash
m2s2 generate page <name> [OPTIONS]
```

Run this from your project root. Framework is auto-detected from `package.json` or `.m2s2.json`.

**Arguments**

| Argument | Description |
|----------|-------------|
| `<name>` | Page name. Accepts any casing — `UserProfile`, `user-profile`, and `userProfile` all produce the same output. |

**Options**

| Flag | Description |
|------|-------------|
| `--framework <react\|angular\|vue>` | Override framework detection. |
| `--path <dir>` | Override the output directory. |

**Examples**

```bash
m2s2 generate page UserProfile
m2s2 generate page user-profile --framework angular
m2s2 generate page Dashboard --path src/views
```

**What gets generated**

*React* — written to `src/pages/<Name>/`
```
src/pages/UserProfile/
├── UserProfilePage.tsx   # Typed props, BEM className
├── UserProfilePage.scss  # Scoped class stub
└── index.ts              # Barrel re-export
```

*Angular* — written to `src/app/pages/<name>/`
```
src/app/pages/user-profile/
├── user-profile.component.ts    # Standalone, OnPush, app-user-profile selector
├── user-profile.component.html
└── user-profile.component.scss
```

A lazy-load route snippet is printed to the terminal after generation:
```typescript
{ path: 'user-profile', loadComponent: () => import('./pages/user-profile/user-profile.component').then(m => m.UserProfilePageComponent) }
```

*Vue* — written to `src/pages/<Name>/`
```
src/pages/UserProfile/
├── UserProfilePage.vue   # <script setup> SFC with scoped SCSS
└── index.ts              # Barrel re-export
```

---

### `m2s2 generate service`

Scaffold an injectable Angular service inside an existing M²S² project.

```bash
m2s2 generate service <name> [OPTIONS]
```

> Only supported for Angular projects. React and Vue projects receive a helpful error message.

**Arguments**

| Argument | Description |
|----------|-------------|
| `<name>` | Service name (without the `Service` suffix). |

**Options**

| Flag | Description |
|------|-------------|
| `--path <dir>` | Override the output directory. Defaults to `src/app/services/`. |

**Examples**

```bash
m2s2 generate service Auth
m2s2 generate service user-data
m2s2 generate service Analytics --path src/app/core/services
```

**What gets generated** — written to `src/app/services/<name>/`
```
src/app/services/auth/
└── auth.service.ts   # @Injectable({ providedIn: 'root' }) AuthService
```

---

### `m2s2 completions`

Install shell completions for `m2s2`. Auto-detects your shell from `$SHELL` and writes a completion script to your home directory, then patches your shell's rc file to source it automatically.

```bash
m2s2 completions [shell]
```

**Arguments**

| Argument | Description |
|----------|-------------|
| `[shell]` | Shell to generate completions for. Auto-detected from `$SHELL` if omitted. One of: `bash`, `zsh`, `fish`, `elvish`, `powershell`. |

**Examples**

```bash
# Auto-detect shell and install
m2s2 completions

# Explicit shell
m2s2 completions zsh
```

After running, reload your shell or source your rc file:

```bash
source ~/.zshrc   # zsh
source ~/.bashrc  # bash
# fish sources completions automatically — no reload needed
```

---

### `m2s2 upgrade`

Check for and install a newer version of the CLI.

```bash
m2s2 upgrade [OPTIONS]
```

**Options**

| Flag | Description |
|------|-------------|
| `--check` | Print available version without installing. |

**Examples**

```bash
# Check if an update is available
m2s2 upgrade --check

# Download and install the latest version
m2s2 upgrade
```

The upgrade command hits the GitHub Releases API, compares the latest release tag against the running binary version, and — if a newer version exists — runs the official installer script for your platform. Restart your terminal after upgrading.

---

## Building from Source

**Prerequisites:** Rust 1.85+ (edition 2024), Node.js 18+ for running scaffolded projects.

```bash
git clone https://github.com/M2S2-Engineering-Group/m2s2-cli.git
cd m2s2-cli

# Debug build
cargo build

# Run directly
cargo run -- new my-app --framework react --skip-install

# Optimised release build (matches the distributed binary)
cargo build --profile dist
```

The binary is written to `target/debug/m2s2` (debug) or `target/dist/m2s2` (dist profile).

### Templates

All scaffold and generate templates live under `templates/` and are embedded into the binary at compile time via [`rust-embed`](https://github.com/pyros2097/rust-embed). Changes to template files require a rebuild to take effect.

```
templates/
├── react/               # m2s2 new --framework react
├── angular/             # m2s2 new --framework angular
├── vue/                 # m2s2 new --framework vue
└── generate/
    ├── react/           # m2s2 generate component/page (React)
    ├── angular/         # m2s2 generate component/page/service (Angular)
    └── vue/             # m2s2 generate component/page (Vue)
```

---

## Testing

The test suite has two tiers.

### Unit / integration tests (fast, no network)

```bash
cargo test
```

Covers all scaffold and generate paths using temp directories and an `--offline`
flag that skips npm version resolution. Runs in under a second. Also includes
`cargo clippy` and `cargo fmt` checks in CI.

```bash
cargo clippy -- -D warnings
cargo fmt --check
```

### End-to-end tests (network required)

E2e tests scaffold real projects, resolve live package versions, and run the
full install step (`npm install`, `go mod tidy`, `python3 -m venv` + `pip
install`). They are marked `#[ignore]` and never run during normal CI.

```bash
# Run the full e2e suite
cargo e2e

# Run a single scenario
cargo test --test e2e new_react_frontend_e2e -- --ignored --nocapture
```

Each test asserts on install artifacts to confirm the toolchain ran:

| Scenario | Artifact checked |
|----------|-----------------|
| React / Angular / Vue frontend | `node_modules/` |
| Go backend (gin / echo / fiber) | `go.sum` |
| Node backend (express / fastify) | `node_modules/` |
| Python backend (fastapi / flask) | `.venv/` + `.venv/bin/pip` |
| Fullstack | web `node_modules/` + api artifact |

Temp directories are cleaned up automatically after each test.

---

## Project Structure

```
src/
├── main.rs                        # CLI entry point, command routing
├── utils.rs                       # Shared case conversion (to_pascal_case, to_kebab_case)
├── config.rs                      # .m2s2.json read/write, framework detection/resolution
├── scaffold/
│   └── mod.rs                     # Template engine (rust-embed + Handlebars), write_files helper
└── commands/
    ├── mod.rs
    ├── new.rs                     # m2s2 new
    ├── upgrade.rs                 # m2s2 upgrade
    └── generate/
        ├── mod.rs                 # m2s2 generate (subcommand router)
        ├── component.rs          # m2s2 generate component
        ├── page.rs               # m2s2 generate page
        └── service.rs            # m2s2 generate service
```

---

## Workflows

### CI (`ci.yml`)

Runs on every push to `main` and every pull request.

| Job | Description |
|-----|-------------|
| `build` | Compiles the crate for macOS (arm64), Linux (x86_64), and Windows (x86_64) in a matrix. |
| `test` | Runs `cargo test` on Linux. |
| `clippy` | Runs `cargo clippy -- -D warnings`. |
| `fmt` | Runs `cargo fmt --check`. |

### Release (`release-plz.yml` + `auto-merge-release.yml` + `release-tag.yml` + `release.yml`)

Releases are fully automated from conventional commits — no manual tagging or PR review required.

1. **CI passes on `main`**`release-plz.yml` triggers only after the `CI` workflow succeeds.
2. **Release PR opened**`release-plz release-pr` reads conventional commit history, bumps `Cargo.toml`, updates `CHANGELOG.md`, and opens a PR (e.g. `chore: release v0.1.5`). Only `feat`, `fix`, and `perf` commits trigger a bump.
3. **PR auto-approved and merged**`auto-merge-release.yml` detects the `release-plz-*` branch, approves the PR using the GitHub App token, and immediately squash-merges it.
4. **Tag pushed**`release-tag.yml` fires on the merge commit, reads the version from `Cargo.toml`, and pushes the `vX.Y.Z` tag if it doesn't already exist.
5. **`release.yml` (cargo-dist) triggers** — builds platform binaries in parallel:
   - `aarch64-apple-darwin`
   - `x86_64-apple-darwin`
   - `aarch64-unknown-linux-gnu`
   - `x86_64-unknown-linux-gnu`
   - `x86_64-pc-windows-msvc`
6. **GitHub Release created** — all binaries, checksums, shell installer, and PowerShell installer are attached.
7. **`publish-npm.yml` triggers** — publishes `@m2s2/cli` to npm.
8. **`publish-crates.yml` triggers** — publishes `m2s2-cli` to crates.io.

### Template Sync (`template-sync.yml`)

Keeps scaffold template dependencies in sync with the latest published M²S² library versions.

Triggers:
- **Weekly** — every Monday at 08:00 UTC.
- **`repository_dispatch`** — fired automatically by the design system CI whenever any `@m2s2` library publishes a new release.

When a version change is detected, the workflow opens a pull request updating the pinned package versions in `templates/react/package.json.hbs`, `templates/angular/package.json.hbs`, and `templates/vue/package.json.hbs`. Merging that PR flows through the normal release pipeline.

### Required Repository Secrets

| Secret | Used By | Description |
|--------|---------|-------------|
| `APP_ID` | `release-plz.yml`, `auto-merge-release.yml`, `release-tag.yml`, `release.yml` | GitHub App ID used to open, approve, merge release PRs, and push tags. |
| `APP_PRIVATE_KEY` | `release-plz.yml`, `auto-merge-release.yml`, `release-tag.yml`, `release.yml` | GitHub App private key. |
| `NPM_TOKEN` | `publish-npm.yml` | npm access token with publish rights to the `@m2s2` scope. |
| `CARGO_REGISTRY_TOKEN` | `publish-crates.yml` | crates.io API token for publishing `m2s2-cli`. |

---

## Contributing

Commit messages follow the [Conventional Commits](https://www.conventionalcommits.org/) specification — this is what release-plz uses to determine version bumps and generate the CHANGELOG.

| Prefix | Version bump |
|--------|-------------|
| `fix:` | Patch |
| `feat:` | Minor |
| `feat!:` / `BREAKING CHANGE` | Major |
| `chore:`, `docs:`, `refactor:` | No release |

```bash
# Good examples
git commit -m "feat: add m2s2 init command"
git commit -m "fix: detect camelCase component names correctly"
git commit -m "chore: update Angular template to v22"
```