@dimensional-innovations/tool-config 1.4.1 → 2.0.0

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 (30) hide show
  1. package/README.md +72 -18
  2. package/bin/lib/ci-setup.js +142 -0
  3. package/bin/lib/handlers/eslint.js +61 -0
  4. package/bin/lib/handlers/prettier.js +83 -0
  5. package/bin/lib/handlers/semantic-release.js +60 -0
  6. package/bin/lib/handlers/stylelint.js +85 -0
  7. package/bin/lib/handlers/typescript.js +156 -0
  8. package/bin/lib/package-manager.js +114 -0
  9. package/bin/lib/ui.js +111 -0
  10. package/bin/lib/validators.js +28 -0
  11. package/bin/setup-tool-config.js +112 -596
  12. package/package.json +16 -4
  13. package/src/detectors.js +27 -2
  14. package/src/index.js +8 -3
  15. package/src/tools/typescript/README.md +665 -0
  16. package/src/tools/typescript/checker-detection.js +113 -0
  17. package/src/tools/typescript/index.js +202 -0
  18. package/src/tools/typescript/presets/base.js +58 -0
  19. package/src/tools/typescript/presets/environments/browser.js +10 -0
  20. package/src/tools/typescript/presets/environments/node.js +11 -0
  21. package/src/tools/typescript/presets/environments/universal.js +11 -0
  22. package/src/tools/typescript/presets/frameworks/angular.js +11 -0
  23. package/src/tools/typescript/presets/frameworks/astro.js +11 -0
  24. package/src/tools/typescript/presets/frameworks/electron.js +100 -0
  25. package/src/tools/typescript/presets/frameworks/node.js +12 -0
  26. package/src/tools/typescript/presets/frameworks/react.js +10 -0
  27. package/src/tools/typescript/presets/frameworks/solid.js +11 -0
  28. package/src/tools/typescript/presets/frameworks/svelte.js +10 -0
  29. package/src/tools/typescript/presets/frameworks/vanilla.js +9 -0
  30. package/src/tools/typescript/presets/frameworks/vue.js +17 -0
@@ -3,485 +3,139 @@
3
3
  /**
4
4
  * @dimensional-innovations/tool-config CLI Setup Tool
5
5
  *
6
- * Interactive configuration generator for ESLint, Prettier, Stylelint, and semantic-release
6
+ * Interactive configuration generator for ESLint, Prettier, Stylelint, TypeScript, and semantic-release
7
7
  */
8
8
 
9
- import { writeFileSync, existsSync, readFileSync, mkdirSync, copyFileSync } from 'fs'
10
- import { join, dirname } from 'path'
11
- import { fileURLToPath } from 'url'
12
-
13
9
  import prompts from 'prompts'
14
10
 
15
11
  import { autoDetect } from '../src/detectors.js'
16
- import { prettierIgnoreContent } from '../src/tools/prettier/index.js'
17
-
18
- const __filename = fileURLToPath(import.meta.url)
19
- const __dirname = dirname(__filename)
20
-
21
- const VERSION = '0.0.0-development'
22
-
23
- // Parse CLI arguments
24
- const args = process.argv.slice(2)
25
- const flags = {
26
- help: args.includes('--help') || args.includes('-h'),
27
- version: args.includes('--version') || args.includes('-v'),
28
- dryRun: args.includes('--dry-run') || args.includes('-d'),
29
- all: args.includes('--all') || args.includes('-a'),
30
- setupCI: args.includes('--setup-ci')
31
- }
32
-
33
- // Check for --ci flag with optional provider
34
- const ciIndex = args.indexOf('--ci')
35
- const ciProvider =
36
- ciIndex !== -1 && args[ciIndex + 1] && !args[ciIndex + 1].startsWith('-')
37
- ? args[ciIndex + 1]
38
- : null
39
- const hasCI = args.includes('--ci')
40
-
41
- // Single tool mode: setup-tool-config eslint (but not --ci provider value)
42
- const singleTool = args.find(arg => !arg.startsWith('-') && arg !== ciProvider)
43
-
44
- /**
45
- * Show help message
46
- */
47
- function showHelp() {
48
- console.log(`
49
- đŸ“Ļ @dimensional-innovations/tool-config Setup Tool
50
-
51
- Usage:
52
- setup-tool-config [tool] [options]
53
-
54
- Tools:
55
- eslint Setup ESLint only
56
- prettier Setup Prettier only
57
- stylelint Setup Stylelint only
58
- semantic-release Setup semantic-release only
59
-
60
- Options:
61
- --all, -a Setup all tools (non-interactive)
62
- --ci [provider] Setup CI/CD pipeline (gitlab, github, bitbucket)
63
- --setup-ci Interactive CI/CD setup
64
- --dry-run, -d Preview changes without writing files
65
- --help, -h Show this help message
66
- --version, -v Show version number
67
-
68
- Examples:
69
- setup-tool-config # Interactive mode
70
- setup-tool-config eslint # Setup ESLint only
71
- setup-tool-config --all # Setup all tools
72
- setup-tool-config --dry-run # Preview without changes
73
- setup-tool-config --ci gitlab # Setup GitLab CI/CD
74
- setup-tool-config --ci github # Setup GitHub Actions
75
- setup-tool-config --setup-ci # Interactive CI setup
76
- setup-tool-config semantic-release --ci # Setup release + CI
77
-
78
- For more information, visit:
79
- https://gitlab.com/dimensional-innovations/tool-config
80
- `)
81
- }
82
-
83
- /**
84
- * Show version
85
- */
86
- function showVersion() {
87
- console.log(`@dimensional-innovations/tool-config v${VERSION}`)
88
- }
89
-
90
- /**
91
- * Generate config file content for a tool
92
- */
93
- function generateConfigContent(tool) {
94
- const configs = {
95
- eslint: `import { createConfig } from '@dimensional-innovations/tool-config'
96
-
97
- export default await createConfig('eslint')
98
- `,
99
- prettier: `import { createConfig } from '@dimensional-innovations/tool-config'
100
-
101
- export default createConfig('prettier')
102
- `,
103
- stylelint: `import { createConfig } from '@dimensional-innovations/tool-config'
104
-
105
- export default createConfig('stylelint')
106
- `,
107
- 'semantic-release': `import { createConfig } from '@dimensional-innovations/tool-config'
108
-
109
- export default createConfig('semantic-release')
110
- `
111
- }
112
12
 
113
- return configs[tool] || ''
13
+ import { copyCITemplate } from './lib/ci-setup.js'
14
+ import * as eslintHandler from './lib/handlers/eslint.js'
15
+ import * as prettierHandler from './lib/handlers/prettier.js'
16
+ import * as semanticReleaseHandler from './lib/handlers/semantic-release.js'
17
+ import * as stylelintHandler from './lib/handlers/stylelint.js'
18
+ import * as typescriptHandler from './lib/handlers/typescript.js'
19
+ import { updatePackageJsonScripts } from './lib/package-manager.js'
20
+ import {
21
+ showBanner,
22
+ showCICompletion,
23
+ showCompletion,
24
+ showHelp,
25
+ showSection,
26
+ showVersion
27
+ } from './lib/ui.js'
28
+ import { VALID_TOOLS, validateProvider, validateTool } from './lib/validators.js'
29
+
30
+ // Tool handler registry
31
+ const TOOL_HANDLERS = {
32
+ eslint: eslintHandler,
33
+ prettier: prettierHandler,
34
+ stylelint: stylelintHandler,
35
+ typescript: typescriptHandler,
36
+ 'semantic-release': semanticReleaseHandler
114
37
  }
115
38
 
116
39
  /**
117
- * Get config filename for a tool
40
+ * Get tool scripts (wrapper for handler getScripts)
118
41
  */
119
- function getConfigFilename(tool) {
120
- const filenames = {
121
- eslint: 'eslint.config.js',
122
- prettier: 'prettier.config.js',
123
- stylelint: 'stylelint.config.js',
124
- 'semantic-release': 'release.config.js'
125
- }
126
-
127
- return filenames[tool] || `${tool}.config.js`
42
+ function getToolScripts(tool, detected) {
43
+ const handler = TOOL_HANDLERS[tool]
44
+ return handler.getScripts ? handler.getScripts(detected) : {}
128
45
  }
129
46
 
130
47
  /**
131
- * Get file extensions for Stylelint based on framework and CSS type
132
- * @param {string} framework - Detected framework
133
- * @param {Object} cssType - CSS type detection results
134
- * @returns {string[]} Array of file extensions
48
+ * Setup tools
135
49
  */
136
- function getStylelintExtensions(framework, cssType) {
137
- const extensions = ['css']
138
-
139
- // Add preprocessor extensions
140
- if (cssType.preprocessor === 'scss') {
141
- extensions.push('scss')
142
- } else if (cssType.preprocessor === 'less') {
143
- extensions.push('less')
144
- }
145
-
146
- // Add framework-specific extensions
147
- if (framework === 'vue') extensions.push('vue')
148
- if (framework === 'svelte') extensions.push('svelte')
50
+ async function setupTools(tools, detected, cwd, dryRun = false) {
51
+ showSection('📝 Generating configuration files...')
149
52
 
150
- return extensions
151
- }
53
+ // Track which tools successfully created configs
54
+ const successfulTools = []
152
55
 
153
- /**
154
- * Generate framework-aware scripts based on detection
155
- * @param {string} tool - Tool name
156
- * @param {Object} detected - Detected configuration
157
- * @returns {Object} Tool-specific scripts
158
- */
159
- function getSmartScripts(tool, detected) {
160
- switch (tool) {
161
- case 'stylelint': {
162
- const extensions = getStylelintExtensions(detected.framework, detected.cssType)
163
- const pattern =
164
- extensions.length === 1 ? `**/*.${extensions[0]}` : `**/*.{${extensions.join(',')}}`
165
-
166
- return {
167
- style: `stylelint "${pattern}"`,
168
- 'style:fix': `stylelint "${pattern}" --fix`
169
- }
56
+ for (const tool of tools) {
57
+ console.log(`${tool}:`)
58
+ const handler = TOOL_HANDLERS[tool]
59
+ const success = await handler.writeConfig(cwd, detected, dryRun)
60
+ if (success) {
61
+ successfulTools.push(tool)
170
62
  }
171
-
172
- case 'eslint':
173
- return {
174
- lint: 'eslint .',
175
- 'lint:fix': 'eslint --fix .'
176
- }
177
-
178
- case 'prettier':
179
- return {
180
- 'prettier:fix': 'prettier --write .',
181
- prettier: 'prettier --check .'
182
- }
183
-
184
- case 'semantic-release':
185
- return {
186
- release: 'semantic-release'
187
- }
188
-
189
- default:
190
- return {}
191
63
  }
192
- }
193
64
 
194
- /**
195
- * Get npm scripts for a tool
196
- * @param {string} tool - Tool name
197
- * @param {Object} [detected] - Optional detected configuration for smart scripts
198
- * @returns {Object} Tool-specific scripts
199
- */
200
- function getToolScripts(tool, detected = null) {
201
- // If no detection provided, use basic scripts
202
- if (!detected) {
203
- return (
204
- {
205
- eslint: {
206
- lint: 'eslint .',
207
- 'lint:fix': 'eslint --fix .'
208
- },
209
- prettier: {
210
- 'prettier:fix': 'prettier --write .',
211
- prettier: 'prettier --check .'
212
- },
213
- stylelint: {
214
- style: 'stylelint "**/*.css"',
215
- 'style:fix': 'stylelint "**/*.css" --fix'
216
- },
217
- 'semantic-release': {
218
- release: 'semantic-release'
219
- }
220
- }[tool] || {}
221
- )
65
+ // Only add scripts for tools that created configs
66
+ if (successfulTools.length > 0) {
67
+ showSection('đŸ“Ļ Updating package.json scripts...')
68
+ updatePackageJsonScripts(successfulTools, getToolScripts, { detected, cwd, dryRun })
222
69
  }
223
-
224
- // Generate framework-aware scripts
225
- return getSmartScripts(tool, detected)
226
70
  }
227
71
 
228
72
  /**
229
- * Write config file
73
+ * Prompt for CI provider if not detected
230
74
  */
231
- function writeConfigFile(tool, cwd, dryRun = false) {
232
- const filename = getConfigFilename(tool)
233
- const filepath = join(cwd, filename)
234
- const content = generateConfigContent(tool)
235
-
236
- if (existsSync(filepath)) {
237
- console.log(` âš ī¸ ${filename} already exists - skipping`)
238
- return false
239
- }
240
-
241
- if (dryRun) {
242
- console.log(` 📄 Would create: ${filename}`)
243
-
244
- // Prettier also creates .prettierignore
245
- if (tool === 'prettier') {
246
- const ignoreFile = '.prettierignore'
247
- const ignoreExists = existsSync(join(cwd, ignoreFile))
248
- if (!ignoreExists) {
249
- console.log(` 📄 Would create: ${ignoreFile}`)
250
- }
251
- }
252
-
253
- return true
254
- }
255
-
256
- try {
257
- writeFileSync(filepath, content, 'utf8')
258
- console.log(` ✅ Created: ${filename}`)
259
-
260
- // Prettier also creates .prettierignore
261
- if (tool === 'prettier') {
262
- const ignoreFile = '.prettierignore'
263
- const ignorePath = join(cwd, ignoreFile)
264
-
265
- if (!existsSync(ignorePath)) {
266
- writeFileSync(ignorePath, prettierIgnoreContent, 'utf8')
267
- console.log(` ✅ Created: ${ignoreFile}`)
268
- } else {
269
- console.log(` âš ī¸ ${ignoreFile} already exists - skipping`)
270
- }
271
- }
272
-
273
- return true
274
- } catch (error) {
275
- console.error(` ❌ Failed to create ${filename}:`, error.message)
276
- return false
277
- }
75
+ async function promptForProvider() {
76
+ const providerResponse = await prompts({
77
+ type: 'select',
78
+ name: 'provider',
79
+ message: 'Which CI/CD platform would you like to use?',
80
+ choices: [
81
+ { title: 'GitLab CI', value: 'gitlab' },
82
+ { title: 'GitHub Actions', value: 'github' },
83
+ { title: 'Bitbucket Pipelines', value: 'bitbucket' }
84
+ ]
85
+ })
86
+
87
+ return providerResponse.provider
278
88
  }
279
89
 
280
90
  /**
281
- * Update package.json with scripts
91
+ * Setup CI/CD pipeline
282
92
  */
283
- function updatePackageJsonScripts(tools, detected, cwd, dryRun = false) {
284
- const packageJsonPath = join(cwd, 'package.json')
93
+ async function setupCI(detected, cwd, dryRun, ciProvider = null) {
94
+ showSection('🚀 CI/CD Pipeline Setup')
285
95
 
286
- if (!existsSync(packageJsonPath)) {
287
- console.log(' âš ī¸ No package.json found - skipping script injection')
288
- return
289
- }
290
-
291
- try {
292
- const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8'))
96
+ let provider = ciProvider || detected.gitProvider
293
97
 
294
- if (!packageJson.scripts) {
295
- packageJson.scripts = {}
296
- }
297
-
298
- const scriptsToAdd = {}
299
- for (const tool of tools) {
300
- const toolScripts = getToolScripts(tool, detected)
301
- for (const [name, command] of Object.entries(toolScripts)) {
302
- if (!packageJson.scripts[name]) {
303
- scriptsToAdd[name] = command
304
- }
305
- }
306
- }
307
-
308
- if (Object.keys(scriptsToAdd).length === 0) {
309
- console.log(' â„šī¸ All scripts already exist in package.json')
310
- return
311
- }
312
-
313
- if (dryRun) {
314
- console.log(' 📝 Would add scripts to package.json:')
315
- for (const [name, command] of Object.entries(scriptsToAdd)) {
316
- console.log(` "${name}": "${command}"`)
317
- }
98
+ // If provider not detected or unknown, prompt for it
99
+ if (!provider || provider === 'unknown') {
100
+ provider = await promptForProvider()
101
+ if (!provider) {
102
+ console.log('âš ī¸ Skipping CI/CD setup.')
103
+ console.log('')
318
104
  return
319
105
  }
320
-
321
- Object.assign(packageJson.scripts, scriptsToAdd)
322
-
323
- writeFileSync(packageJsonPath, `${JSON.stringify(packageJson, null, 2)}\n`, 'utf8')
324
-
325
- console.log(' ✅ Updated package.json with scripts:')
326
- for (const name of Object.keys(scriptsToAdd)) {
327
- console.log(` - ${name}`)
328
- }
329
- } catch (error) {
330
- console.error(' ❌ Failed to update package.json:', error.message)
331
106
  }
332
- }
333
107
 
334
- /**
335
- * Get CI filename and path for a git provider
336
- */
337
- function getCIFileInfo(provider) {
338
- const ciFiles = {
339
- gitlab: {
340
- filename: '.gitlab-ci.yml',
341
- destination: '.gitlab-ci.yml',
342
- template: '.gitlab-ci.yml'
343
- },
344
- github: {
345
- filename: 'ci.yml',
346
- destination: '.github/workflows/ci.yml',
347
- template: 'github-workflow.yml',
348
- needsDir: true
349
- },
350
- bitbucket: {
351
- filename: 'bitbucket-pipelines.yml',
352
- destination: 'bitbucket-pipelines.yml',
353
- template: 'bitbucket-pipelines.yml'
354
- }
355
- }
356
-
357
- return ciFiles[provider] || null
358
- }
359
-
360
- /**
361
- * Get environment variable instructions for a git provider
362
- */
363
- function getCIInstructions(provider) {
364
- const instructions = {
365
- gitlab: `
366
- âš ī¸ Configuration Required:
367
- Add these CI/CD variables in GitLab Settings > CI/CD > Variables:
368
- - GL_TOKEN: GitLab Personal Access Token (with api scope)
369
- - NPM_TOKEN: npm authentication token
370
-
371
- Then push to main branch to trigger automated releases.`,
372
- github: `
373
- âš ī¸ Configuration Required:
374
- Add these secrets in GitHub Settings > Secrets and variables > Actions:
375
- - GITHUB_TOKEN: Automatically provided by GitHub Actions
376
- - NPM_TOKEN: npm authentication token
377
-
378
- Then push to main branch to trigger automated releases.`,
379
- bitbucket: `
380
- âš ī¸ Configuration Required:
381
- Add this repository variable in Bitbucket Settings > Pipelines > Repository variables:
382
- - NPM_TOKEN: npm authentication token
383
-
384
- Then push to main branch to trigger automated releases.`
385
- }
386
-
387
- return instructions[provider] || ''
108
+ console.log('📝 Setting up CI/CD...')
109
+ console.log('')
110
+ copyCITemplate(provider, cwd, dryRun)
111
+ showCICompletion(dryRun)
388
112
  }
389
113
 
390
114
  /**
391
- * Copy CI/CD template
115
+ * Main CLI function
392
116
  */
393
- function copyCITemplate(provider, cwd, dryRun = false) {
394
- if (!provider || provider === 'unknown') {
395
- console.log(' âš ī¸ Could not detect git provider - skipping CI setup')
396
- console.log(' 💡 You can manually copy templates from:')
397
- console.log(
398
- ' node_modules/@dimensional-innovations/tool-config/src/tools/semantic-release/templates/'
399
- )
400
- return false
401
- }
402
-
403
- const ciInfo = getCIFileInfo(provider)
404
-
405
- if (!ciInfo) {
406
- console.log(` âš ī¸ Unsupported git provider: ${provider}`)
407
- return false
408
- }
409
-
410
- const destPath = join(cwd, ciInfo.destination)
411
- const destDir = dirname(destPath)
412
-
413
- // Check if CI file already exists
414
- if (existsSync(destPath)) {
415
- console.log(` âš ī¸ ${ciInfo.destination} already exists - skipping`)
416
- return false
117
+ async function main() {
118
+ // Parse CLI arguments
119
+ const args = process.argv.slice(2)
120
+ const flags = {
121
+ help: args.includes('--help') || args.includes('-h'),
122
+ version: args.includes('--version') || args.includes('-v'),
123
+ dryRun: args.includes('--dry-run') || args.includes('-d'),
124
+ all: args.includes('--all') || args.includes('-a'),
125
+ setupCI: args.includes('--setup-ci')
417
126
  }
418
127
 
419
- if (dryRun) {
420
- console.log(` 📄 Would create: ${ciInfo.destination}`)
421
- if (ciInfo.needsDir && !existsSync(destDir)) {
422
- console.log(` 📁 Would create directory: ${dirname(ciInfo.destination)}`)
423
- }
424
- return true
425
- }
128
+ // Check for --ci flag with optional provider
129
+ const ciIndex = args.indexOf('--ci')
130
+ const ciProvider =
131
+ ciIndex !== -1 && args[ciIndex + 1] && !args[ciIndex + 1].startsWith('-')
132
+ ? args[ciIndex + 1]
133
+ : null
134
+ const hasCI = args.includes('--ci')
426
135
 
427
- try {
428
- // Create directory if needed
429
- if (ciInfo.needsDir && !existsSync(destDir)) {
430
- mkdirSync(destDir, { recursive: true })
431
- console.log(` ✅ Created directory: ${dirname(ciInfo.destination)}`)
432
- }
136
+ // Single tool mode: setup-tool-config eslint (but not --ci provider value)
137
+ const singleTool = args.find(arg => !arg.startsWith('-') && arg !== ciProvider)
433
138
 
434
- // Copy template
435
- const templatePath = join(
436
- __dirname,
437
- '..',
438
- 'src',
439
- 'tools',
440
- 'semantic-release',
441
- 'templates',
442
- ciInfo.template
443
- )
444
- copyFileSync(templatePath, destPath)
445
- console.log(` ✅ Created: ${ciInfo.destination}`)
446
-
447
- // Show configuration instructions
448
- console.log(getCIInstructions(provider))
449
-
450
- console.log('\n 📖 For more details, see:')
451
- console.log(
452
- ' node_modules/@dimensional-innovations/tool-config/src/tools/semantic-release/CI_SETUP.md'
453
- )
454
-
455
- return true
456
- } catch (error) {
457
- console.error(` ❌ Failed to copy CI template:`, error.message)
458
- return false
459
- }
460
- }
461
-
462
- /**
463
- * Setup tools
464
- */
465
- function setupTools(tools, detected, cwd, dryRun = false) {
466
- console.log('')
467
- console.log('📝 Generating configuration files...')
468
- console.log('')
469
-
470
- for (const tool of tools) {
471
- console.log(`${tool}:`)
472
- writeConfigFile(tool, cwd, dryRun)
473
- }
474
-
475
- console.log('')
476
- console.log('đŸ“Ļ Updating package.json scripts...')
477
- console.log('')
478
- updatePackageJsonScripts(tools, detected, cwd, dryRun)
479
- }
480
-
481
- /**
482
- * Main CLI function
483
- */
484
- async function main() {
485
139
  // Handle flags
486
140
  if (flags.help) {
487
141
  showHelp()
@@ -495,159 +149,41 @@ async function main() {
495
149
 
496
150
  const cwd = process.cwd()
497
151
 
498
- // Show banner
499
- console.log('')
500
- console.log('đŸ“Ļ @dimensional-innovations/tool-config Setup')
501
- console.log('━'.repeat(50))
502
- console.log('')
503
-
504
- // Auto-detect project
152
+ // Show banner and auto-detect
505
153
  const detected = autoDetect(cwd)
506
- console.log('🔍 Detected project configuration:')
507
- console.log(` Framework: ${detected.framework}`)
508
- console.log(` Environment: ${detected.environment}`)
509
- console.log(` TypeScript: ${detected.typescript ? 'Yes' : 'No'}`)
510
- console.log(` Git Provider: ${detected.gitProvider || 'Unknown'}`)
511
- console.log('')
154
+ showBanner(detected)
512
155
 
513
156
  // Handle standalone --setup-ci flag
514
157
  if (flags.setupCI) {
515
- console.log('🚀 CI/CD Pipeline Setup')
516
- console.log('')
517
-
518
- let provider = detected.gitProvider
519
-
520
- // If provider not detected or unknown, prompt for it
521
- if (!provider || provider === 'unknown') {
522
- const providerResponse = await prompts({
523
- type: 'select',
524
- name: 'provider',
525
- message: 'Which CI/CD platform would you like to use?',
526
- choices: [
527
- { title: 'GitLab CI', value: 'gitlab' },
528
- { title: 'GitHub Actions', value: 'github' },
529
- { title: 'Bitbucket Pipelines', value: 'bitbucket' }
530
- ]
531
- })
532
-
533
- if (!providerResponse.provider) {
534
- console.log('❌ No provider selected. Exiting.')
535
- process.exit(0)
536
- }
537
-
538
- provider = providerResponse.provider
539
- }
540
-
541
- console.log('📝 Setting up CI/CD...')
542
- console.log('')
543
- copyCITemplate(provider, cwd, flags.dryRun)
544
-
545
- console.log('')
546
- if (flags.dryRun) {
547
- console.log('â„šī¸ Dry run mode - no files were modified')
548
- } else {
549
- console.log('✅ CI/CD setup complete!')
550
- }
551
- console.log('')
158
+ await setupCI(detected, cwd, flags.dryRun)
552
159
  process.exit(0)
553
160
  }
554
161
 
555
162
  // Handle --ci flag with provider
556
163
  if (hasCI && ciProvider) {
557
- const validProviders = ['gitlab', 'github', 'bitbucket']
558
-
559
- if (!validProviders.includes(ciProvider)) {
560
- console.error(`❌ Unknown provider: ${ciProvider}`)
561
- console.error(`Valid providers: ${validProviders.join(', ')}`)
562
- process.exit(1)
563
- }
564
-
565
- console.log('🚀 CI/CD Pipeline Setup')
566
- console.log('')
567
- console.log('📝 Setting up CI/CD...')
568
- console.log('')
569
- copyCITemplate(ciProvider, cwd, flags.dryRun)
570
-
571
- console.log('')
572
- if (flags.dryRun) {
573
- console.log('â„šī¸ Dry run mode - no files were modified')
574
- } else {
575
- console.log('✅ CI/CD setup complete!')
576
- }
577
- console.log('')
164
+ validateProvider(ciProvider)
165
+ await setupCI(detected, cwd, flags.dryRun, ciProvider)
578
166
  process.exit(0)
579
167
  }
580
168
 
581
169
  // Handle --all flag
582
170
  if (flags.all) {
583
- const tools = ['eslint', 'prettier', 'stylelint', 'semantic-release']
584
- setupTools(tools, detected, cwd, flags.dryRun)
585
- console.log('')
586
- if (flags.dryRun) {
587
- console.log('â„šī¸ Dry run mode - no files were modified')
588
- console.log('Run without --dry-run to apply changes')
589
- } else {
590
- console.log('✅ Setup complete!')
591
- }
592
- console.log('')
171
+ await setupTools(VALID_TOOLS, detected, cwd, flags.dryRun)
172
+ showCompletion(flags.dryRun)
593
173
  process.exit(0)
594
174
  }
595
175
 
596
176
  // Handle single tool mode
597
177
  if (singleTool) {
598
- const validTools = ['eslint', 'prettier', 'stylelint', 'semantic-release']
599
-
600
- if (!validTools.includes(singleTool)) {
601
- console.error(`❌ Unknown tool: ${singleTool}`)
602
- console.error(`Valid tools: ${validTools.join(', ')}`)
603
- process.exit(1)
604
- }
605
-
606
- setupTools([singleTool], detected, cwd, flags.dryRun)
178
+ validateTool(singleTool)
179
+ await setupTools([singleTool], detected, cwd, flags.dryRun)
607
180
 
608
181
  // If semantic-release + --ci flag, setup CI
609
182
  if (singleTool === 'semantic-release' && hasCI) {
610
- console.log('')
611
- console.log('🚀 CI/CD Pipeline Setup')
612
- console.log('')
613
-
614
- let provider = ciProvider || detected.gitProvider
615
-
616
- // If no provider specified and can't detect, prompt
617
- if (!provider || provider === 'unknown') {
618
- const providerResponse = await prompts({
619
- type: 'select',
620
- name: 'provider',
621
- message: 'Which CI/CD platform would you like to use?',
622
- choices: [
623
- { title: 'GitLab CI', value: 'gitlab' },
624
- { title: 'GitHub Actions', value: 'github' },
625
- { title: 'Bitbucket Pipelines', value: 'bitbucket' }
626
- ]
627
- })
628
-
629
- if (!providerResponse.provider) {
630
- console.log('âš ī¸ Skipping CI/CD setup.')
631
- console.log('')
632
- process.exit(0)
633
- }
634
-
635
- provider = providerResponse.provider
636
- }
637
-
638
- console.log('📝 Setting up CI/CD...')
639
- console.log('')
640
- copyCITemplate(provider, cwd, flags.dryRun)
183
+ await setupCI(detected, cwd, flags.dryRun, ciProvider)
641
184
  }
642
185
 
643
- console.log('')
644
- if (flags.dryRun) {
645
- console.log('â„šī¸ Dry run mode - no files were modified')
646
- console.log('Run without --dry-run to apply changes')
647
- } else {
648
- console.log('✅ Setup complete!')
649
- }
650
- console.log('')
186
+ showCompletion(flags.dryRun)
651
187
  process.exit(0)
652
188
  }
653
189
 
@@ -661,6 +197,11 @@ async function main() {
661
197
  { title: 'ESLint (JavaScript/TypeScript linting)', value: 'eslint', selected: true },
662
198
  { title: 'Prettier (Code formatting)', value: 'prettier', selected: true },
663
199
  { title: 'Stylelint (CSS/style linting)', value: 'stylelint', selected: false },
200
+ {
201
+ title: 'TypeScript (Type checking)',
202
+ value: 'typescript',
203
+ selected: detected.typescript
204
+ },
664
205
  {
665
206
  title: 'semantic-release (Automated versioning)',
666
207
  value: 'semantic-release',
@@ -677,13 +218,11 @@ async function main() {
677
218
  process.exit(0)
678
219
  }
679
220
 
680
- setupTools(response.tools, detected, cwd, flags.dryRun)
221
+ await setupTools(response.tools, detected, cwd, flags.dryRun)
681
222
 
682
223
  // If semantic-release selected, prompt for CI setup
683
224
  if (response.tools.includes('semantic-release')) {
684
- console.log('')
685
- console.log('🚀 CI/CD Pipeline Setup')
686
- console.log('')
225
+ showSection('🚀 CI/CD Pipeline Setup')
687
226
 
688
227
  const ciResponse = await prompts({
689
228
  type: 'confirm',
@@ -697,21 +236,10 @@ async function main() {
697
236
 
698
237
  // If provider not detected or unknown, prompt for it
699
238
  if (!provider || provider === 'unknown') {
700
- const providerResponse = await prompts({
701
- type: 'select',
702
- name: 'provider',
703
- message: 'Which CI/CD platform would you like to use?',
704
- choices: [
705
- { title: 'GitLab CI', value: 'gitlab' },
706
- { title: 'GitHub Actions', value: 'github' },
707
- { title: 'Bitbucket Pipelines', value: 'bitbucket' }
708
- ]
709
- })
710
-
711
- if (!providerResponse.provider) {
239
+ provider = await promptForProvider()
240
+ if (!provider) {
712
241
  console.log('âš ī¸ Skipping CI/CD setup.')
713
242
  } else {
714
- provider = providerResponse.provider
715
243
  console.log('📝 Setting up CI/CD...')
716
244
  console.log('')
717
245
  copyCITemplate(provider, cwd, flags.dryRun)
@@ -724,19 +252,7 @@ async function main() {
724
252
  }
725
253
  }
726
254
 
727
- console.log('')
728
- if (flags.dryRun) {
729
- console.log('â„šī¸ Dry run mode - no files were modified')
730
- console.log('Run without --dry-run to apply changes')
731
- } else {
732
- console.log('✅ Setup complete!')
733
- console.log('')
734
- console.log('Next steps:')
735
- console.log(' 1. Review the generated config files')
736
- console.log(' 2. Install peer dependencies if needed')
737
- console.log(' 3. Run: npm run lint (or other added scripts)')
738
- }
739
- console.log('')
255
+ showCompletion(flags.dryRun)
740
256
  }
741
257
 
742
258
  // Run CLI