@morya-ui/setup 0.3.5 → 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 (32) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +119 -107
  3. package/bin/morya-ui-setup.js +14 -14
  4. package/catalog/skills.json +46 -46
  5. package/package.json +4 -3
  6. package/src/__tests__/mcp.test.mjs +141 -0
  7. package/src/cli.mjs +330 -312
  8. package/src/copy-template.mjs +78 -78
  9. package/src/fs-utils.mjs +24 -24
  10. package/src/install.mjs +134 -134
  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 -188
  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/design-system.md +101 -101
  19. package/template/.agents/skills/morya-ui-pages/references/feedback.md +68 -68
  20. package/template/.agents/skills/morya-ui-pages/references/optional-companions.md +62 -62
  21. package/template/.agents/skills/morya-ui-pages/references/page-layouts.md +133 -133
  22. package/template/.agents/skills/morya-ui-pages/references/review-checklist.md +76 -63
  23. package/template/.agents/skills/morya-ui-pages/references/style-presets.md +68 -45
  24. package/template/.agents/skills/morya-ui-pages/references/surfaces.md +91 -91
  25. package/template/.agents/skills/morya-ui-pages/references/visual-craft.md +131 -129
  26. package/template/.cursor/rules/coding-style.mdc +41 -41
  27. package/template/.cursor/rules/component-usage.mdc +41 -41
  28. package/template/.cursor/rules/design-system.mdc +18 -17
  29. package/template/.cursor/rules/page-layout.mdc +97 -97
  30. package/template/AGENTS.md +33 -0
  31. package/template/DESIGN.md +81 -59
  32. package/template/scripts/check-raw-colors.mjs +74 -74
package/src/cli.mjs CHANGED
@@ -1,312 +1,330 @@
1
- import { dirname, join, resolve } from 'node:path'
2
- import { fileURLToPath } from 'node:url'
3
- import { copyTemplate } from './copy-template.mjs'
4
- import { installMoryaUi } from './install.mjs'
5
- import { mergeMcpConfig } from './mcp.mjs'
6
- import { ensureCheckColorsScript } from './package-json.mjs'
7
- import {
8
- buildAiInclude,
9
- installSkillsCli,
10
- loadSkillsCatalog,
11
- parseSkillsFlag,
12
- resolveSkillSelection,
13
- } from './skills.mjs'
14
- import { ensureStylesImport } from './styles.mjs'
15
-
16
- const __dirname = dirname(fileURLToPath(import.meta.url))
17
- const PKG_ROOT = resolve(__dirname, '..')
18
- const TEMPLATE_ROOT = join(PKG_ROOT, 'template')
19
-
20
- const MODES = new Set(['app', 'ai', 'full'])
21
-
22
- /** Mode → default skips (user --skip-* can only add more skips). */
23
- const MODE_DEFAULTS = {
24
- full: {},
25
- app: { skipTemplate: true, skipMcp: true, skipScripts: true },
26
- // ai still upgrades morya-ui / @morya-ui/* to latest unless --skip-install
27
- ai: { skipStyles: true },
28
- }
29
-
30
- const SKIP_FLAGS = {
31
- '--skip-install': 'skipInstall',
32
- '--skip-template': 'skipTemplate',
33
- '--skip-mcp': 'skipMcp',
34
- '--skip-styles': 'skipStyles',
35
- '--skip-scripts': 'skipScripts',
36
- }
37
-
38
- export function printHelp() {
39
- const { skills } = loadSkillsCatalog()
40
- const skillList = skills.map((s) => {
41
- const tag = s.required ? 'required' : s.default ? 'default' : 'optional'
42
- const via = s.install === 'skills-cli' ? ' [skills-cli latest]' : ''
43
- return ` ${s.id.padEnd(24)} (${tag})${via} ${s.description}`
44
- }).join('\n')
45
-
46
- console.log(`Usage: morya-ui-setup [command] [options]
47
-
48
- Commands:
49
- (default) / full Upgrade morya packages to latest, AI template, MCP, styles, check:colors
50
- app Upgrade morya packages to latest and inject styles.css
51
- ai Upgrade morya packages to latest, copy Agent skills / rules / DESIGN,
52
- merge Cursor MCP, add check:colors
53
-
54
- Default command:
55
- - install / upgrade morya-ui@latest and any existing @morya-ui/* to @latest
56
- - copy DESIGN.md, selected Agent skills, Cursor rules
57
- - merge .cursor/mcp.json for @morya-ui/mcp@latest
58
- - inject import 'morya-ui/styles.css' into the app entry when found
59
- - add check:colors script when missing
60
-
61
- Options:
62
- --cwd <dir> Target project root (default: process.cwd())
63
- --pm <name> Package manager: pnpm | yarn | npm (auto-detect by lockfile)
64
- --skills <list> Comma-separated skill ids, or "all" (skips interactive prompt)
65
- --yes Use default skills without prompting (CI / non-interactive)
66
- --force Overwrite existing template files and MCP server entry
67
- --dry-run Print actions without writing or installing
68
- --skip-install Skip dependency install / upgrade
69
- --skip-template Skip copying AI template files
70
- --skip-mcp Skip writing .cursor/mcp.json
71
- --skip-styles Skip injecting styles.css
72
- --skip-scripts Skip adding check:colors to package.json
73
- -h, --help Show this help
74
-
75
- Skills:
76
- ${skillList}
77
- `)
78
- }
79
-
80
- /**
81
- * @param {string[]} argv
82
- */
83
- export function parseArgs(argv) {
84
- const options = {
85
- mode: 'full',
86
- cwd: process.cwd(),
87
- pm: undefined,
88
- skills: undefined,
89
- yes: false,
90
- force: false,
91
- dryRun: false,
92
- skipInstall: false,
93
- skipTemplate: false,
94
- skipMcp: false,
95
- skipStyles: false,
96
- skipScripts: false,
97
- help: false,
98
- }
99
-
100
- for (let i = 0; i < argv.length; i++) {
101
- const arg = argv[i]
102
- if (arg === '-h' || arg === '--help') {
103
- options.help = true
104
- continue
105
- }
106
- if (MODES.has(arg)) {
107
- options.mode = arg
108
- continue
109
- }
110
- if (arg === '--force') {
111
- options.force = true
112
- continue
113
- }
114
- if (arg === '--dry-run') {
115
- options.dryRun = true
116
- continue
117
- }
118
- if (arg === '--yes' || arg === '-y') {
119
- options.yes = true
120
- continue
121
- }
122
- if (SKIP_FLAGS[arg]) {
123
- options[SKIP_FLAGS[arg]] = true
124
- continue
125
- }
126
- if (arg === '--cwd') {
127
- const value = argv[++i]
128
- if (!value) throw new Error('--cwd requires a path')
129
- options.cwd = resolve(value)
130
- continue
131
- }
132
- if (arg.startsWith('--cwd=')) {
133
- options.cwd = resolve(arg.slice('--cwd='.length))
134
- continue
135
- }
136
- if (arg === '--pm') {
137
- const value = argv[++i]
138
- if (!value) throw new Error('--pm requires pnpm, yarn, or npm')
139
- options.pm = value
140
- continue
141
- }
142
- if (arg.startsWith('--pm=')) {
143
- options.pm = arg.slice('--pm='.length)
144
- continue
145
- }
146
- if (arg === '--skills') {
147
- const value = argv[++i]
148
- if (!value) throw new Error('--skills requires a comma-separated list or "all"')
149
- options.skills = value
150
- continue
151
- }
152
- if (arg.startsWith('--skills=')) {
153
- options.skills = arg.slice('--skills='.length)
154
- continue
155
- }
156
- throw new Error(`Unknown argument: ${arg}`)
157
- }
158
-
159
- const defaults = MODE_DEFAULTS[options.mode] || {}
160
- for (const [key, value] of Object.entries(defaults)) {
161
- if (value) options[key] = true
162
- }
163
-
164
- return options
165
- }
166
-
167
- function rel(cwd, path) {
168
- if (!path) return ''
169
- return path.startsWith(cwd) ? path.slice(cwd.length).replace(/^[\\/]/, '') || path : path
170
- }
171
-
172
- /**
173
- * @param {ReturnType<typeof parseArgs>} options
174
- */
175
- export async function runSetup(options) {
176
- const {
177
- mode,
178
- cwd,
179
- force,
180
- dryRun,
181
- skipInstall,
182
- skipTemplate,
183
- skipMcp,
184
- skipStyles,
185
- skipScripts,
186
- pm,
187
- skills: skillsFlag,
188
- yes,
189
- } = options
190
-
191
- console.log(`@morya-ui/setup [${mode}] → ${cwd}${dryRun ? ' (dry-run)' : ''}`)
192
- console.log('')
193
-
194
- const catalog = loadSkillsCatalog()
195
- let selectedSkills = []
196
- let aiInclude
197
-
198
- const needsTemplate = !skipTemplate && (mode === 'ai' || mode === 'full')
199
- if (needsTemplate) {
200
- if (skillsFlag != null) {
201
- selectedSkills = parseSkillsFlag(skillsFlag, catalog.skills)
202
- } else {
203
- selectedSkills = await resolveSkillSelection(catalog.skills, {
204
- skipPrompt: yes || dryRun,
205
- })
206
- }
207
- aiInclude = buildAiInclude(selectedSkills, catalog.skills)
208
- console.log(`Skills: ${selectedSkills.join(', ')}`)
209
- console.log('')
210
- }
211
-
212
- // Always ensure morya-ui@latest; also bump any existing @morya-ui/* (nuxt, mcp, …).
213
- const install = installMoryaUi(cwd, { pm, dryRun, skipInstall, ensureCore: true })
214
-
215
- const template = skipTemplate
216
- ? { copied: [], skipped: [], forced: [], skippedStep: true }
217
- : copyTemplate(TEMPLATE_ROOT, cwd, {
218
- force,
219
- dryRun,
220
- include: aiInclude,
221
- })
222
-
223
- const remoteSkills = skipTemplate || !selectedSkills.length
224
- ? { installed: [], commands: [], skipped: true }
225
- : installSkillsCli(cwd, selectedSkills, catalog.skills, { dryRun })
226
-
227
- const mcp = skipMcp
228
- ? { path: join(cwd, '.cursor', 'mcp.json'), action: 'skipped-flag' }
229
- : mergeMcpConfig(cwd, { force, dryRun })
230
-
231
- const styles = skipStyles
232
- ? { action: 'skipped', reason: 'skip-styles' }
233
- : ensureStylesImport(cwd, { dryRun })
234
-
235
- const scripts = skipScripts
236
- ? { action: 'skipped-flag' }
237
- : ensureCheckColorsScript(cwd, { force, dryRun })
238
-
239
- console.log('--- Summary ---')
240
- const pkgList = (install.packages || []).join(', ') || 'morya-ui'
241
- if (install.skipped) {
242
- console.log(`Install: skipped (${install.reason}) — would run: ${install.command}`)
243
- } else {
244
- console.log(`Install: upgraded to latest (${install.pm}) — ${pkgList}`)
245
- console.log(` $ ${install.command}`)
246
- }
247
-
248
- if (template.skippedStep) {
249
- console.log('Template: skipped (--skip-template or app mode)')
250
- } else {
251
- console.log(
252
- `Template: ${template.copied.length} copied, ${template.forced.length} overwritten, ${template.skipped.length} skipped`,
253
- )
254
- if (selectedSkills.length) console.log(` skills: ${selectedSkills.join(', ')}`)
255
- if (template.skipped.length && template.skipped.length <= 8) {
256
- for (const file of template.skipped) console.log(` skip ${file}`)
257
- } else if (template.skipped.length > 8) {
258
- for (const file of template.skipped.slice(0, 5)) console.log(` skip ${file}`)
259
- console.log(` … and ${template.skipped.length - 5} more (use --force to overwrite)`)
260
- }
261
- }
262
-
263
- if (remoteSkills.skipped) {
264
- // no companions selected or template step skipped
265
- } else if (remoteSkills.dryRun) {
266
- console.log(`Skills CLI: dry-run — would install ${remoteSkills.installed.join(', ')}`)
267
- for (const command of remoteSkills.commands) console.log(` $ ${command}`)
268
- } else {
269
- console.log(`Skills CLI: installed ${remoteSkills.installed.join(', ')} (latest)`)
270
- }
271
-
272
- if (mcp.action === 'skipped-flag') {
273
- console.log('MCP: skipped (--skip-mcp or app mode)')
274
- } else {
275
- console.log(`MCP: ${mcp.action} (${rel(cwd, mcp.path) || '.cursor/mcp.json'})`)
276
- }
277
-
278
- if (styles.reason === 'skip-styles') {
279
- console.log('Styles: skipped (--skip-styles or ai mode)')
280
- } else if (styles.action === 'injected') {
281
- console.log(`Styles: injected into ${rel(cwd, styles.path)}`)
282
- } else if (styles.action === 'skipped') {
283
- console.log(`Styles: skipped (${styles.reason}) in ${rel(cwd, styles.path)}`)
284
- } else {
285
- console.log('Styles: no entry file found — add manually:')
286
- console.log(" import 'morya-ui/styles.css'")
287
- }
288
-
289
- if (scripts.action === 'skipped-flag') {
290
- console.log('Scripts: skipped (--skip-scripts or app mode)')
291
- } else {
292
- console.log(`Scripts: check:colors ${scripts.action}`)
293
- }
294
-
295
- console.log('')
296
- console.log('Next:')
297
- if (mode === 'app') {
298
- console.log(' 1. Ensure the styles.css import is in your app entry.')
299
- console.log(' 2. Optional AI pack: npx @morya-ui/setup ai')
300
- } else if (mode === 'ai') {
301
- console.log(' 1. Restart Cursor (or reload MCP) so morya-ui MCP tools appear.')
302
- console.log(' 2. Have the agent read DESIGN.md before generating pages.')
303
- console.log(' 3. Optional: pnpm check:colors')
304
- } else {
305
- console.log(' 1. Ensure the styles.css import is in your app entry.')
306
- console.log(' 2. Restart Cursor (or reload MCP) so morya-ui MCP tools appear.')
307
- console.log(' 3. Have the agent read DESIGN.md before generating pages.')
308
- console.log(' 4. Optional: pnpm check:colors')
309
- }
310
-
311
- return { mode, install, template, remoteSkills, mcp, styles, scripts, skills: selectedSkills }
312
- }
1
+ import { dirname, join, resolve } from 'node:path'
2
+ import { fileURLToPath } from 'node:url'
3
+ import { copyTemplate } from './copy-template.mjs'
4
+ import { installMoryaUi } from './install.mjs'
5
+ import { DEFAULT_EDITORS, mergeMcpConfig, parseEditorsFlag } from './mcp.mjs'
6
+ import { ensureCheckColorsScript } from './package-json.mjs'
7
+ import {
8
+ buildAiInclude,
9
+ installSkillsCli,
10
+ loadSkillsCatalog,
11
+ parseSkillsFlag,
12
+ resolveSkillSelection,
13
+ } from './skills.mjs'
14
+ import { ensureStylesImport } from './styles.mjs'
15
+
16
+ const __dirname = dirname(fileURLToPath(import.meta.url))
17
+ const PKG_ROOT = resolve(__dirname, '..')
18
+ const TEMPLATE_ROOT = join(PKG_ROOT, 'template')
19
+
20
+ const MODES = new Set(['app', 'ai', 'full'])
21
+
22
+ /** Mode → default skips (user --skip-* can only add more skips). */
23
+ const MODE_DEFAULTS = {
24
+ full: {},
25
+ app: { skipTemplate: true, skipMcp: true, skipScripts: true },
26
+ // ai still upgrades morya-ui / @morya-ui/* to latest unless --skip-install
27
+ ai: { skipStyles: true },
28
+ }
29
+
30
+ const SKIP_FLAGS = {
31
+ '--skip-install': 'skipInstall',
32
+ '--skip-template': 'skipTemplate',
33
+ '--skip-mcp': 'skipMcp',
34
+ '--skip-styles': 'skipStyles',
35
+ '--skip-scripts': 'skipScripts',
36
+ }
37
+
38
+ export function printHelp() {
39
+ const { skills } = loadSkillsCatalog()
40
+ const skillList = skills.map((s) => {
41
+ const tag = s.required ? 'required' : s.default ? 'default' : 'optional'
42
+ const via = s.install === 'skills-cli' ? ' [skills-cli latest]' : ''
43
+ return ` ${s.id.padEnd(24)} (${tag})${via} ${s.description}`
44
+ }).join('\n')
45
+
46
+ console.log(`Usage: morya-ui-setup [command] [options]
47
+
48
+ Commands:
49
+ (default) / full Upgrade morya packages to latest, AI template, MCP, styles, check:colors
50
+ app Upgrade morya packages to latest and inject styles.css
51
+ ai Upgrade morya packages to latest, copy Agent skills / rules / DESIGN,
52
+ merge MCP for Cursor / VS Code / Zed, add check:colors
53
+
54
+ Default command:
55
+ - install / upgrade morya-ui@latest and any existing @morya-ui/* to @latest
56
+ - copy DESIGN.md, AGENTS.md, selected Agent skills, Cursor rules
57
+ - merge MCP configs for @morya-ui/mcp@latest (Cursor, VS Code, Zed, + .mcp.json)
58
+ - inject import 'morya-ui/styles.css' into the app entry when found
59
+ - add check:colors script when missing
60
+
61
+ Options:
62
+ --cwd <dir> Target project root (default: process.cwd())
63
+ --pm <name> Package manager: pnpm | yarn | npm (auto-detect by lockfile)
64
+ --skills <list> Comma-separated skill ids, or "all" (skips interactive prompt)
65
+ --editors <list> MCP targets: cursor,vscode,zed (default: all); always also writes .mcp.json
66
+ --yes Use default skills without prompting (CI / non-interactive)
67
+ --force Overwrite existing template files and MCP server entry
68
+ --dry-run Print actions without writing or installing
69
+ --skip-install Skip dependency install / upgrade
70
+ --skip-template Skip copying AI template files
71
+ --skip-mcp Skip writing editor MCP configs and .mcp.json
72
+ --skip-styles Skip injecting styles.css
73
+ --skip-scripts Skip adding check:colors to package.json
74
+ -h, --help Show this help
75
+
76
+ Skills:
77
+ ${skillList}
78
+ `)
79
+ }
80
+
81
+ /**
82
+ * @param {string[]} argv
83
+ */
84
+ export function parseArgs(argv) {
85
+ const options = {
86
+ mode: 'full',
87
+ cwd: process.cwd(),
88
+ pm: undefined,
89
+ skills: undefined,
90
+ editors: undefined,
91
+ yes: false,
92
+ force: false,
93
+ dryRun: false,
94
+ skipInstall: false,
95
+ skipTemplate: false,
96
+ skipMcp: false,
97
+ skipStyles: false,
98
+ skipScripts: false,
99
+ help: false,
100
+ }
101
+
102
+ for (let i = 0; i < argv.length; i++) {
103
+ const arg = argv[i]
104
+ if (arg === '-h' || arg === '--help') {
105
+ options.help = true
106
+ continue
107
+ }
108
+ if (MODES.has(arg)) {
109
+ options.mode = arg
110
+ continue
111
+ }
112
+ if (arg === '--force') {
113
+ options.force = true
114
+ continue
115
+ }
116
+ if (arg === '--dry-run') {
117
+ options.dryRun = true
118
+ continue
119
+ }
120
+ if (arg === '--yes' || arg === '-y') {
121
+ options.yes = true
122
+ continue
123
+ }
124
+ if (SKIP_FLAGS[arg]) {
125
+ options[SKIP_FLAGS[arg]] = true
126
+ continue
127
+ }
128
+ if (arg === '--cwd') {
129
+ const value = argv[++i]
130
+ if (!value) throw new Error('--cwd requires a path')
131
+ options.cwd = resolve(value)
132
+ continue
133
+ }
134
+ if (arg.startsWith('--cwd=')) {
135
+ options.cwd = resolve(arg.slice('--cwd='.length))
136
+ continue
137
+ }
138
+ if (arg === '--pm') {
139
+ const value = argv[++i]
140
+ if (!value) throw new Error('--pm requires pnpm, yarn, or npm')
141
+ options.pm = value
142
+ continue
143
+ }
144
+ if (arg.startsWith('--pm=')) {
145
+ options.pm = arg.slice('--pm='.length)
146
+ continue
147
+ }
148
+ if (arg === '--skills') {
149
+ const value = argv[++i]
150
+ if (!value) throw new Error('--skills requires a comma-separated list or "all"')
151
+ options.skills = value
152
+ continue
153
+ }
154
+ if (arg.startsWith('--skills=')) {
155
+ options.skills = arg.slice('--skills='.length)
156
+ continue
157
+ }
158
+ if (arg === '--editors') {
159
+ const value = argv[++i]
160
+ if (!value) throw new Error('--editors requires a comma-separated list or "all"')
161
+ options.editors = value
162
+ continue
163
+ }
164
+ if (arg.startsWith('--editors=')) {
165
+ options.editors = arg.slice('--editors='.length)
166
+ continue
167
+ }
168
+ throw new Error(`Unknown argument: ${arg}`)
169
+ }
170
+
171
+ const defaults = MODE_DEFAULTS[options.mode] || {}
172
+ for (const [key, value] of Object.entries(defaults)) {
173
+ if (value) options[key] = true
174
+ }
175
+
176
+ return options
177
+ }
178
+
179
+ function rel(cwd, path) {
180
+ if (!path) return ''
181
+ return path.startsWith(cwd) ? path.slice(cwd.length).replace(/^[\\/]/, '') || path : path
182
+ }
183
+
184
+ /**
185
+ * @param {ReturnType<typeof parseArgs>} options
186
+ */
187
+ export async function runSetup(options) {
188
+ const {
189
+ mode,
190
+ cwd,
191
+ force,
192
+ dryRun,
193
+ skipInstall,
194
+ skipTemplate,
195
+ skipMcp,
196
+ skipStyles,
197
+ skipScripts,
198
+ pm,
199
+ skills: skillsFlag,
200
+ editors: editorsFlag,
201
+ yes,
202
+ } = options
203
+
204
+ const editors = editorsFlag != null ? parseEditorsFlag(editorsFlag) : [...DEFAULT_EDITORS]
205
+
206
+ console.log(`@morya-ui/setup [${mode}] → ${cwd}${dryRun ? ' (dry-run)' : ''}`)
207
+ console.log('')
208
+
209
+ const catalog = loadSkillsCatalog()
210
+ let selectedSkills = []
211
+ let aiInclude
212
+
213
+ const needsTemplate = !skipTemplate && (mode === 'ai' || mode === 'full')
214
+ if (needsTemplate) {
215
+ if (skillsFlag != null) {
216
+ selectedSkills = parseSkillsFlag(skillsFlag, catalog.skills)
217
+ } else {
218
+ selectedSkills = await resolveSkillSelection(catalog.skills, {
219
+ skipPrompt: yes || dryRun,
220
+ })
221
+ }
222
+ aiInclude = buildAiInclude(selectedSkills, catalog.skills)
223
+ console.log(`Skills: ${selectedSkills.join(', ')}`)
224
+ console.log('')
225
+ }
226
+
227
+ // Always ensure morya-ui@latest; also bump any existing @morya-ui/* (nuxt, mcp, …).
228
+ const install = installMoryaUi(cwd, { pm, dryRun, skipInstall, ensureCore: true })
229
+
230
+ const template = skipTemplate
231
+ ? { copied: [], skipped: [], forced: [], skippedStep: true }
232
+ : copyTemplate(TEMPLATE_ROOT, cwd, {
233
+ force,
234
+ dryRun,
235
+ include: aiInclude,
236
+ })
237
+
238
+ const remoteSkills = skipTemplate || !selectedSkills.length
239
+ ? { installed: [], commands: [], skipped: true }
240
+ : installSkillsCli(cwd, selectedSkills, catalog.skills, { dryRun })
241
+
242
+ const mcp = skipMcp
243
+ ? { skipped: true, results: [] }
244
+ : mergeMcpConfig(cwd, { force, dryRun, editors })
245
+
246
+ const styles = skipStyles
247
+ ? { action: 'skipped', reason: 'skip-styles' }
248
+ : ensureStylesImport(cwd, { dryRun })
249
+
250
+ const scripts = skipScripts
251
+ ? { action: 'skipped-flag' }
252
+ : ensureCheckColorsScript(cwd, { force, dryRun })
253
+
254
+ console.log('--- Summary ---')
255
+ const pkgList = (install.packages || []).join(', ') || 'morya-ui'
256
+ if (install.skipped) {
257
+ console.log(`Install: skipped (${install.reason}) — would run: ${install.command}`)
258
+ } else {
259
+ console.log(`Install: upgraded to latest (${install.pm}) — ${pkgList}`)
260
+ console.log(` $ ${install.command}`)
261
+ }
262
+
263
+ if (template.skippedStep) {
264
+ console.log('Template: skipped (--skip-template or app mode)')
265
+ } else {
266
+ console.log(
267
+ `Template: ${template.copied.length} copied, ${template.forced.length} overwritten, ${template.skipped.length} skipped`,
268
+ )
269
+ if (selectedSkills.length) console.log(` skills: ${selectedSkills.join(', ')}`)
270
+ if (template.skipped.length && template.skipped.length <= 8) {
271
+ for (const file of template.skipped) console.log(` skip ${file}`)
272
+ } else if (template.skipped.length > 8) {
273
+ for (const file of template.skipped.slice(0, 5)) console.log(` skip ${file}`)
274
+ console.log(` … and ${template.skipped.length - 5} more (use --force to overwrite)`)
275
+ }
276
+ }
277
+
278
+ if (remoteSkills.skipped) {
279
+ // no companions selected or template step skipped
280
+ } else if (remoteSkills.dryRun) {
281
+ console.log(`Skills CLI: dry-run — would install ${remoteSkills.installed.join(', ')}`)
282
+ for (const command of remoteSkills.commands) console.log(` $ ${command}`)
283
+ } else {
284
+ console.log(`Skills CLI: installed ${remoteSkills.installed.join(', ')} (latest)`)
285
+ }
286
+
287
+ if (mcp.skipped) {
288
+ console.log('MCP: skipped (--skip-mcp or app mode)')
289
+ } else {
290
+ console.log(`MCP: editors=${editors.join(',')}+portable`)
291
+ for (const entry of mcp.results) {
292
+ console.log(` ${entry.editor}: ${entry.action} (${rel(cwd, entry.path)})`)
293
+ }
294
+ }
295
+
296
+ if (styles.reason === 'skip-styles') {
297
+ console.log('Styles: skipped (--skip-styles or ai mode)')
298
+ } else if (styles.action === 'injected') {
299
+ console.log(`Styles: injected into ${rel(cwd, styles.path)}`)
300
+ } else if (styles.action === 'skipped') {
301
+ console.log(`Styles: skipped (${styles.reason}) in ${rel(cwd, styles.path)}`)
302
+ } else {
303
+ console.log('Styles: no entry file found — add manually:')
304
+ console.log(" import 'morya-ui/styles.css'")
305
+ }
306
+
307
+ if (scripts.action === 'skipped-flag') {
308
+ console.log('Scripts: skipped (--skip-scripts or app mode)')
309
+ } else {
310
+ console.log(`Scripts: check:colors ${scripts.action}`)
311
+ }
312
+
313
+ console.log('')
314
+ console.log('Next:')
315
+ if (mode === 'app') {
316
+ console.log(' 1. Ensure the styles.css import is in your app entry.')
317
+ console.log(' 2. Optional AI pack: npx @morya-ui/setup ai')
318
+ } else if (mode === 'ai') {
319
+ console.log(' 1. Restart / reload MCP in your editor so morya-ui tools appear.')
320
+ console.log(' 2. Have the agent read DESIGN.md (and AGENTS.md) before generating pages.')
321
+ console.log(' 3. Optional: pnpm check:colors')
322
+ } else {
323
+ console.log(' 1. Ensure the styles.css import is in your app entry.')
324
+ console.log(' 2. Restart / reload MCP in your editor so morya-ui tools appear.')
325
+ console.log(' 3. Have the agent read DESIGN.md (and AGENTS.md) before generating pages.')
326
+ console.log(' 4. Optional: pnpm check:colors')
327
+ }
328
+
329
+ return { mode, install, template, remoteSkills, mcp, styles, scripts, skills: selectedSkills, editors }
330
+ }