@morya-ui/setup 0.3.4 → 0.3.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +119 -103
  3. package/bin/morya-ui-setup.js +14 -14
  4. package/catalog/skills.json +46 -46
  5. package/package.json +4 -5
  6. package/src/__tests__/mcp.test.mjs +141 -0
  7. package/src/cli.mjs +330 -335
  8. package/src/copy-template.mjs +78 -78
  9. package/src/fs-utils.mjs +24 -24
  10. package/src/install.mjs +134 -63
  11. package/src/mcp.mjs +220 -50
  12. package/src/package-json.mjs +30 -30
  13. package/src/skills.mjs +224 -223
  14. package/src/styles.mjs +119 -119
  15. package/template/.agents/skills/morya-ui-pages/SKILL.md +191 -184
  16. package/template/.agents/skills/morya-ui-pages/evals/evals.json +89 -89
  17. package/template/.agents/skills/morya-ui-pages/references/component-index.md +99 -99
  18. package/template/.agents/skills/morya-ui-pages/references/decision-recipes.md +72 -5
  19. package/template/.agents/skills/morya-ui-pages/references/design-system.md +101 -101
  20. package/template/.agents/skills/morya-ui-pages/references/feedback.md +68 -68
  21. package/template/.agents/skills/morya-ui-pages/references/optional-companions.md +62 -107
  22. package/template/.agents/skills/morya-ui-pages/references/page-layouts.md +133 -141
  23. package/template/.agents/skills/morya-ui-pages/references/review-checklist.md +76 -64
  24. package/template/.agents/skills/morya-ui-pages/references/style-presets.md +68 -73
  25. package/template/.agents/skills/morya-ui-pages/references/surfaces.md +91 -91
  26. package/template/.agents/skills/morya-ui-pages/references/visual-craft.md +131 -221
  27. package/template/.cursor/rules/coding-style.mdc +41 -41
  28. package/template/.cursor/rules/component-usage.mdc +41 -41
  29. package/template/.cursor/rules/design-system.mdc +18 -17
  30. package/template/.cursor/rules/page-layout.mdc +97 -97
  31. package/template/AGENTS.md +33 -0
  32. package/template/DESIGN.md +81 -59
  33. package/template/scripts/check-raw-colors.mjs +74 -74
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Morya UI contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,103 +1,119 @@
1
- # @morya-ui/setup
2
-
3
- One-shot setup for consumer Vue projects using [`morya-ui`](https://www.npmjs.com/package/morya-ui).
4
-
5
- Docs (zh/en):
6
-
7
- - [One-shot setup](https://morya-space.github.io/morya-ui/docs/setup) — CLI commands and options
8
- - [AI setup](https://morya-space.github.io/morya-ui/docs/ai-setup) — Agent workflow
9
- - [Agent Skill](https://morya-space.github.io/morya-ui/docs/agent-skill) — `morya-ui-pages` behavior
10
- - [Quick start](https://morya-space.github.io/morya-ui/docs/quick-start) — install and first component
11
-
12
- Installs the UI library, copies first-party Agent skills / Cursor rules / design docs from `design-kit`, installs optional companion skills at **latest** via the [skills CLI](https://skills.sh/), merges Cursor MCP for [`@morya-ui/mcp`](https://www.npmjs.com/package/@morya-ui/mcp), and injects `import 'morya-ui/styles.css'`.
13
-
14
- ## Usage
15
-
16
- In your app project root:
17
-
18
- ```bash
19
- npx @morya-ui/setup
20
- ```
21
-
22
- On a TTY, `full` / `ai` will prompt for optional Agent skills (required `morya-ui-pages` is always included).
23
-
24
- Other common commands:
25
-
26
- ```bash
27
- # AI config + MCP only (library already installed)
28
- npx @morya-ui/setup ai
29
-
30
- # Library + styles only
31
- npx @morya-ui/setup app
32
-
33
- # Non-interactive skill selection
34
- npx @morya-ui/setup ai --yes
35
- npx @morya-ui/setup ai --skills=morya-ui-pages,frontend-design,impeccable
36
- npx @morya-ui/setup ai --skills=all
37
- ```
38
-
39
- ### Options
40
-
41
- | Flag | Meaning |
42
- | --- | --- |
43
- | `--cwd <dir>` | Target project root (default: current directory) |
44
- | `--pm pnpm\|yarn\|npm` | Package manager (default: detect from lockfile, else `pnpm`) |
45
- | `--skills <list>` | Comma-separated skill ids, or `all` (skips the prompt) |
46
- | `--yes` / `-y` | Use default skills without prompting |
47
- | `--force` | Overwrite existing template files and the `morya-ui` MCP entry |
48
- | `--dry-run` | Print actions without writing or installing |
49
- | `--skip-install` | Do not install `morya-ui` |
50
- | `--skip-template` | Do not copy AI skill / rules / docs (also skips companion skill install) |
51
- | `--skip-mcp` | Do not write `.cursor/mcp.json` |
52
- | `--skip-styles` | Do not inject `styles.css` |
53
- | `--skip-scripts` | Do not add `check:colors` to `package.json` |
54
- | `-h`, `--help` | Show help |
55
-
56
- ### Skills
57
-
58
- | Id | Default | Install | Role |
59
- | --- | --- | --- | --- |
60
- | `morya-ui-pages` | required | template (from this package) | Page generation with `M*` + golden layouts |
61
- | `frontend-design` | optional | skills CLI → `anthropics/skills` (latest) | Express / brand visual taste |
62
- | `fixing-accessibility` | optional | skills CLI → `ibelick/ui-skills` (latest) | A11y audit and targeted fixes |
63
- | `impeccable` | optional | skills CLI → `pbakaus/impeccable` (latest) | Named polish / audit / redesign passes |
64
-
65
- Catalog: [`catalog/skills.json`](./catalog/skills.json).
66
-
67
- ### Conflict policy
68
-
69
- - Template files and `.cursor/rules/*`: **skip** if the destination exists (unless `--force`).
70
- - Companion skills (`skills-cli`): always reinstall/update to **latest** when selected.
71
- - `.cursor/mcp.json`: merge other servers; skip existing `morya-ui` entry unless `--force`.
72
- - `package.json` `check:colors`: add only if missing (unless `--force`).
73
- - Styles: inject only when an entry file is found and the import is not already present.
74
-
75
- ### What gets copied / installed
76
-
77
- From the package `template/` (synced from repo `design-kit/`):
78
-
79
- - `DESIGN.md` — core design contract (principles, tokens, bans)
80
- - `.agents/skills/morya-ui-pages/` — first-party page skill
81
- - `.cursor/rules/`
82
- - `scripts/check-raw-colors.mjs`
83
-
84
- Via `npx skills add …` when optional companions are selected:
85
-
86
- - `.agents/skills/frontend-design/`, `fixing-accessibility/`, and/or `impeccable/` (latest upstream)
87
-
88
- ### MCP (Cursor)
89
-
90
- Writes / merges:
91
-
92
- ```json
93
- {
94
- "mcpServers": {
95
- "morya-ui": {
96
- "command": "npx",
97
- "args": ["-y", "@morya-ui/mcp"]
98
- }
99
- }
100
- }
101
- ```
102
-
103
- Restart Cursor (or reload MCP) after install.
1
+ # @morya-ui/setup
2
+
3
+ One-shot setup for consumer Vue projects using [`morya-ui`](https://www.npmjs.com/package/morya-ui).
4
+
5
+ Docs (zh/en):
6
+
7
+ - [One-shot setup](https://morya-space.github.io/morya-ui/docs/setup) — CLI commands and options
8
+ - [AI setup](https://morya-space.github.io/morya-ui/docs/ai-setup) — Agent workflow
9
+ - [Agent Skill](https://morya-space.github.io/morya-ui/docs/agent-skill) — `morya-ui-pages` behavior
10
+ - [Quick start](https://morya-space.github.io/morya-ui/docs/quick-start) — install and first component
11
+
12
+ Installs / upgrades the UI library and any existing `@morya-ui/*` packages to **latest**, copies first-party Agent skills / Cursor rules / design docs from `design-kit`, installs optional companion skills at **latest** via the [skills CLI](https://skills.sh/), merges MCP for [`@morya-ui/mcp`](https://www.npmjs.com/package/@morya-ui/mcp) across Cursor / VS Code / Zed (plus portable `.mcp.json`), and injects `import 'morya-ui/styles.css'`.
13
+
14
+ ## Usage
15
+
16
+ In your app project root:
17
+
18
+ ```bash
19
+ npx @morya-ui/setup
20
+ ```
21
+
22
+ On a TTY, `full` / `ai` will prompt for optional Agent skills (required `morya-ui-pages` is always included).
23
+
24
+ Other common commands:
25
+
26
+ ```bash
27
+ # Upgrade deps + AI config + MCP (skips styles injection)
28
+ npx @morya-ui/setup ai
29
+
30
+ # Upgrade deps + styles only
31
+ npx @morya-ui/setup app
32
+
33
+ # Non-interactive skill selection
34
+ npx @morya-ui/setup ai --yes
35
+ npx @morya-ui/setup ai --skills=morya-ui-pages,frontend-design,impeccable
36
+ npx @morya-ui/setup ai --skills=all
37
+
38
+ # Only Cursor + VS Code MCP targets (still writes .mcp.json)
39
+ npx @morya-ui/setup ai --editors=cursor,vscode
40
+
41
+ # Refresh AI template / MCP without touching dependencies
42
+ npx @morya-ui/setup ai --skip-install
43
+ ```
44
+
45
+ ### Options
46
+
47
+ | Flag | Meaning |
48
+ | --- | --- |
49
+ | `--cwd <dir>` | Target project root (default: current directory) |
50
+ | `--pm pnpm\|yarn\|npm` | Package manager (default: detect from lockfile, else `pnpm`) |
51
+ | `--skills <list>` | Comma-separated skill ids, or `all` (skips the prompt) |
52
+ | `--editors <list>` | MCP targets: `cursor`, `vscode`, `zed`, or `all` (default: all three). Always also writes root `.mcp.json` |
53
+ | `--yes` / `-y` | Use default skills without prompting |
54
+ | `--force` | Overwrite existing template files and the `morya-ui` MCP entry |
55
+ | `--dry-run` | Print actions without writing or installing |
56
+ | `--skip-install` | Do not install / upgrade `morya-ui` or `@morya-ui/*` |
57
+ | `--skip-template` | Do not copy AI skill / rules / docs (also skips companion skill install) |
58
+ | `--skip-mcp` | Do not write editor MCP configs or `.mcp.json` |
59
+ | `--skip-styles` | Do not inject `styles.css` |
60
+ | `--skip-scripts` | Do not add `check:colors` to `package.json` |
61
+ | `-h`, `--help` | Show help |
62
+
63
+ ### Skills
64
+
65
+ | Id | Default | Install | Role |
66
+ | --- | --- | --- | --- |
67
+ | `morya-ui-pages` | required | template (from this package) | Page generation with `M*` + snippets / decisions (composition-first) |
68
+ | `frontend-design` | optional | skills CLI → `anthropics/skills` (latest) | Express / brand visual taste |
69
+ | `fixing-accessibility` | optional | skills CLI → `ibelick/ui-skills` (latest) | A11y audit and targeted fixes |
70
+ | `impeccable` | optional | skills CLI → `pbakaus/impeccable` (latest) | Named polish / audit / redesign passes |
71
+
72
+ Catalog: [`catalog/skills.json`](./catalog/skills.json).
73
+
74
+ ### Conflict policy
75
+
76
+ - Dependencies: always install / upgrade `morya-ui@latest` and any existing `@morya-ui/*` (e.g. `@morya-ui/nuxt`) to `@latest` unless `--skip-install`.
77
+ - Template files and `.cursor/rules/*`: **skip** if the destination exists (unless `--force`).
78
+ - Companion skills (`skills-cli`): always reinstall/update to **latest** when selected.
79
+ - MCP configs: merge other servers / settings; skip existing `morya-ui` entry unless `--force`. New or forced entries use `npx -y @morya-ui/mcp@latest`.
80
+ - `package.json` `check:colors`: add only if missing (unless `--force`).
81
+ - Styles: inject only when an entry file is found and the import is not already present.
82
+
83
+ ### What gets copied / installed
84
+
85
+ From the package `template/` (synced from repo `design-kit/`):
86
+
87
+ - `DESIGN.md` — core design contract (principles, tokens, bans)
88
+ - `AGENTS.md` — portable always-on agent checklist (editor-neutral)
89
+ - `.agents/skills/morya-ui-pages/` — first-party page skill
90
+ - `.cursor/rules/` — Cursor-specific always-apply rules
91
+ - `scripts/check-raw-colors.mjs`
92
+
93
+ Via `npx skills add …` when optional companions are selected:
94
+
95
+ - `.agents/skills/frontend-design/`, `fixing-accessibility/`, and/or `impeccable/` (latest upstream)
96
+
97
+ ### MCP (multi-editor)
98
+
99
+ | Editor | Path | JSON key |
100
+ | --- | --- | --- |
101
+ | Cursor | `.cursor/mcp.json` | `mcpServers.morya-ui` |
102
+ | VS Code | `.vscode/mcp.json` | `servers.morya-ui` |
103
+ | Zed | `.zed/settings.json` | `context_servers.morya-ui` |
104
+ | Portable | `.mcp.json` | `mcpServers.morya-ui` |
105
+
106
+ Shared server payload:
107
+
108
+ ```json
109
+ {
110
+ "command": "npx",
111
+ "args": ["-y", "@morya-ui/mcp@latest"]
112
+ }
113
+ ```
114
+
115
+ VS Code entries also set `"type": "stdio"`. Root `.mcp.json` is always written when MCP is not skipped (useful for Agent Host / cross-tool discovery).
116
+
117
+ Claude Code / Windsurf / other clients: configure manually with the same `command` / `args` (see [Agent MCP](https://morya-space.github.io/morya-ui/docs/mcp)).
118
+
119
+ Reload MCP in your editor after install.
@@ -1,14 +1,14 @@
1
- #!/usr/bin/env node
2
- import { parseArgs, printHelp, runSetup } from '../src/cli.mjs'
3
-
4
- try {
5
- const options = parseArgs(process.argv.slice(2))
6
- if (options.help) {
7
- printHelp()
8
- process.exit(0)
9
- }
10
- await runSetup(options)
11
- } catch (error) {
12
- console.error(error instanceof Error ? error.message : error)
13
- process.exit(1)
14
- }
1
+ #!/usr/bin/env node
2
+ import { parseArgs, printHelp, runSetup } from '../src/cli.mjs'
3
+
4
+ try {
5
+ const options = parseArgs(process.argv.slice(2))
6
+ if (options.help) {
7
+ printHelp()
8
+ process.exit(0)
9
+ }
10
+ await runSetup(options)
11
+ } catch (error) {
12
+ console.error(error instanceof Error ? error.message : error)
13
+ process.exit(1)
14
+ }
@@ -1,46 +1,46 @@
1
- {
2
- "skills": [
3
- {
4
- "id": "morya-ui-pages",
5
- "name": "Morya UI Pages",
6
- "description": "Build admin and product pages with M* components, golden layouts, and tokens.",
7
- "required": true,
8
- "default": true,
9
- "install": "template",
10
- "path": ".agents/skills/morya-ui-pages"
11
- },
12
- {
13
- "id": "frontend-design",
14
- "name": "Frontend Design",
15
- "description": "Distinctive visual direction for Express / brand surfaces; use after morya-ui-pages contract (taste only; still remediate with M*).",
16
- "required": false,
17
- "default": false,
18
- "install": "skills-cli",
19
- "source": "anthropics/skills",
20
- "skill": "frontend-design",
21
- "path": ".agents/skills/frontend-design"
22
- },
23
- {
24
- "id": "fixing-accessibility",
25
- "name": "Fixing Accessibility",
26
- "description": "Audit and fix names, keyboard, focus, forms, and WCAG basics.",
27
- "required": false,
28
- "default": false,
29
- "install": "skills-cli",
30
- "source": "ibelick/ui-skills",
31
- "skill": "fixing-accessibility",
32
- "path": ".agents/skills/fixing-accessibility"
33
- },
34
- {
35
- "id": "impeccable",
36
- "name": "Impeccable",
37
- "description": "Named polish / audit / bolder / quieter passes; pair with morya-ui-pages Operate/Persuade modes (latest from pbakaus/impeccable).",
38
- "required": false,
39
- "default": false,
40
- "install": "skills-cli",
41
- "source": "pbakaus/impeccable",
42
- "skill": "impeccable",
43
- "path": ".agents/skills/impeccable"
44
- }
45
- ]
46
- }
1
+ {
2
+ "skills": [
3
+ {
4
+ "id": "morya-ui-pages",
5
+ "name": "Morya UI Pages",
6
+ "description": "Build admin and product pages with M* components, golden layouts, and tokens.",
7
+ "required": true,
8
+ "default": true,
9
+ "install": "template",
10
+ "path": ".agents/skills/morya-ui-pages"
11
+ },
12
+ {
13
+ "id": "frontend-design",
14
+ "name": "Frontend Design",
15
+ "description": "Distinctive visual direction for Express / brand surfaces; use after morya-ui-pages contract (taste only; still remediate with M*).",
16
+ "required": false,
17
+ "default": false,
18
+ "install": "skills-cli",
19
+ "source": "anthropics/skills",
20
+ "skill": "frontend-design",
21
+ "path": ".agents/skills/frontend-design"
22
+ },
23
+ {
24
+ "id": "fixing-accessibility",
25
+ "name": "Fixing Accessibility",
26
+ "description": "Audit and fix names, keyboard, focus, forms, and WCAG basics.",
27
+ "required": false,
28
+ "default": false,
29
+ "install": "skills-cli",
30
+ "source": "ibelick/ui-skills",
31
+ "skill": "fixing-accessibility",
32
+ "path": ".agents/skills/fixing-accessibility"
33
+ },
34
+ {
35
+ "id": "impeccable",
36
+ "name": "Impeccable",
37
+ "description": "Named polish / audit / bolder / quieter passes; pair with morya-ui-pages Operate/Persuade modes (latest from pbakaus/impeccable).",
38
+ "required": false,
39
+ "default": false,
40
+ "install": "skills-cli",
41
+ "source": "pbakaus/impeccable",
42
+ "skill": "impeccable",
43
+ "path": ".agents/skills/impeccable"
44
+ }
45
+ ]
46
+ }
package/package.json CHANGED
@@ -1,9 +1,8 @@
1
1
  {
2
2
  "name": "@morya-ui/setup",
3
3
  "type": "module",
4
- "version": "0.3.4",
5
- "packageManager": "pnpm@10.12.4",
6
- "description": "One-shot setup for morya-ui: install the library, Cursor MCP, Agent skills, and design config.",
4
+ "version": "0.3.6",
5
+ "description": "One-shot setup for morya-ui: install the library, multi-editor MCP, Agent skills, and design config.",
7
6
  "license": "MIT",
8
7
  "homepage": "https://github.com/morya-space/morya-ui/tree/main/packages/setup#readme",
9
8
  "repository": {
@@ -33,6 +32,6 @@
33
32
  "scripts": {
34
33
  "sync-template": "node ./scripts/sync-template.mjs",
35
34
  "build": "pnpm run sync-template",
36
- "prepublishOnly": "pnpm run build"
35
+ "test": "node --test src/__tests__/**/*.test.mjs"
37
36
  }
38
- }
37
+ }
@@ -0,0 +1,141 @@
1
+ import assert from 'node:assert/strict'
2
+ import { mkdtempSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
3
+ import { tmpdir } from 'node:os'
4
+ import { join } from 'node:path'
5
+ import { afterEach, describe, it } from 'node:test'
6
+ import {
7
+ DEFAULT_EDITORS,
8
+ SERVER_CONFIG,
9
+ VSCODE_SERVER_CONFIG,
10
+ mergeMcpConfig,
11
+ parseEditorsFlag,
12
+ } from '../mcp.mjs'
13
+
14
+ /** @type {string[]} */
15
+ const temps = []
16
+
17
+ function makeCwd() {
18
+ const cwd = mkdtempSync(join(tmpdir(), 'morya-setup-mcp-'))
19
+ temps.push(cwd)
20
+ return cwd
21
+ }
22
+
23
+ afterEach(() => {
24
+ while (temps.length) {
25
+ rmSync(temps.pop(), { recursive: true, force: true })
26
+ }
27
+ })
28
+
29
+ function readJson(path) {
30
+ return JSON.parse(readFileSync(path, 'utf8'))
31
+ }
32
+
33
+ describe('parseEditorsFlag', () => {
34
+ it('defaults to all known editors', () => {
35
+ assert.deepEqual(parseEditorsFlag(), [...DEFAULT_EDITORS])
36
+ assert.deepEqual(parseEditorsFlag('all'), [...DEFAULT_EDITORS])
37
+ })
38
+
39
+ it('parses and dedupes a subset', () => {
40
+ assert.deepEqual(parseEditorsFlag('zed,cursor,zed'), ['zed', 'cursor'])
41
+ })
42
+
43
+ it('rejects unknown editors', () => {
44
+ assert.throws(() => parseEditorsFlag('cursor,windsurf'), /Unknown editor/)
45
+ })
46
+ })
47
+
48
+ describe('mergeMcpConfig', () => {
49
+ it('creates cursor, vscode, zed, and portable configs', () => {
50
+ const cwd = makeCwd()
51
+ const { results } = mergeMcpConfig(cwd)
52
+
53
+ assert.equal(results.length, 4)
54
+ assert.deepEqual(
55
+ results.map((r) => r.editor),
56
+ ['cursor', 'vscode', 'zed', 'portable'],
57
+ )
58
+ assert.ok(results.every((r) => r.action === 'created'))
59
+
60
+ assert.deepEqual(readJson(join(cwd, '.cursor', 'mcp.json')), {
61
+ mcpServers: { 'morya-ui': SERVER_CONFIG },
62
+ })
63
+ assert.deepEqual(readJson(join(cwd, '.vscode', 'mcp.json')), {
64
+ servers: { 'morya-ui': VSCODE_SERVER_CONFIG },
65
+ })
66
+ assert.deepEqual(readJson(join(cwd, '.zed', 'settings.json')), {
67
+ context_servers: { 'morya-ui': SERVER_CONFIG },
68
+ })
69
+ assert.deepEqual(readJson(join(cwd, '.mcp.json')), {
70
+ mcpServers: { 'morya-ui': SERVER_CONFIG },
71
+ })
72
+ })
73
+
74
+ it('skips existing morya-ui entries unless force', () => {
75
+ const cwd = makeCwd()
76
+ mkdirSync(join(cwd, '.cursor'), { recursive: true })
77
+ writeFileSync(
78
+ join(cwd, '.cursor', 'mcp.json'),
79
+ JSON.stringify({
80
+ mcpServers: {
81
+ other: { command: 'echo' },
82
+ 'morya-ui': { command: 'old' },
83
+ },
84
+ }),
85
+ 'utf8',
86
+ )
87
+
88
+ const skipped = mergeMcpConfig(cwd, { editors: ['cursor'] })
89
+ const cursorSkip = skipped.results.find((r) => r.editor === 'cursor')
90
+ assert.equal(cursorSkip.action, 'skipped')
91
+ assert.equal(readJson(join(cwd, '.cursor', 'mcp.json')).mcpServers['morya-ui'].command, 'old')
92
+ assert.ok(readJson(join(cwd, '.cursor', 'mcp.json')).mcpServers.other)
93
+
94
+ const forced = mergeMcpConfig(cwd, { editors: ['cursor'], force: true })
95
+ const cursorForce = forced.results.find((r) => r.editor === 'cursor')
96
+ assert.equal(cursorForce.action, 'forced')
97
+ const data = readJson(join(cwd, '.cursor', 'mcp.json'))
98
+ assert.deepEqual(data.mcpServers['morya-ui'], SERVER_CONFIG)
99
+ assert.ok(data.mcpServers.other)
100
+ })
101
+
102
+ it('merges Zed settings without wiping unrelated keys', () => {
103
+ const cwd = makeCwd()
104
+ mkdirSync(join(cwd, '.zed'), { recursive: true })
105
+ writeFileSync(
106
+ join(cwd, '.zed', 'settings.json'),
107
+ JSON.stringify({
108
+ theme: 'One Dark',
109
+ context_servers: {
110
+ other: { command: 'keep-me' },
111
+ },
112
+ }),
113
+ 'utf8',
114
+ )
115
+
116
+ const { results } = mergeMcpConfig(cwd, { editors: ['zed'] })
117
+ assert.equal(results.find((r) => r.editor === 'zed').action, 'merged')
118
+
119
+ const zed = readJson(join(cwd, '.zed', 'settings.json'))
120
+ assert.equal(zed.theme, 'One Dark')
121
+ assert.deepEqual(zed.context_servers.other, { command: 'keep-me' })
122
+ assert.deepEqual(zed.context_servers['morya-ui'], SERVER_CONFIG)
123
+ })
124
+
125
+ it('respects editors subset and still writes portable', () => {
126
+ const cwd = makeCwd()
127
+ const { results } = mergeMcpConfig(cwd, { editors: ['vscode'] })
128
+ assert.deepEqual(
129
+ results.map((r) => r.editor),
130
+ ['vscode', 'portable'],
131
+ )
132
+ })
133
+
134
+ it('dry-run does not write files', () => {
135
+ const cwd = makeCwd()
136
+ const { results } = mergeMcpConfig(cwd, { dryRun: true })
137
+ assert.ok(results.every((r) => r.action === 'created' && r.dryRun))
138
+ assert.throws(() => readFileSync(join(cwd, '.cursor', 'mcp.json')), /ENOENT/)
139
+ assert.throws(() => readFileSync(join(cwd, '.mcp.json')), /ENOENT/)
140
+ })
141
+ })