create-time2ship 1.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.
package/LICENSE ADDED
@@ -0,0 +1,15 @@
1
+ ISC License
2
+
3
+ Copyright (c) 2026, Time2Build
4
+
5
+ Permission to use, copy, modify, and/or distribute this software for any
6
+ purpose with or without fee is hereby granted, provided that the above
7
+ copyright notice and this permission notice appear in all copies.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,91 @@
1
+ # create-time2ship
2
+
3
+ Create a new Time2Ship full-stack application in seconds.
4
+
5
+ ## Quick Start
6
+
7
+ ```bash
8
+ npx create-time2ship my-app
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ### With npx (recommended)
14
+
15
+ ```bash
16
+ npx create-time2ship my-app
17
+ ```
18
+
19
+ ### With npm
20
+
21
+ ```bash
22
+ npm create time2ship my-app
23
+ ```
24
+
25
+ ### With yarn
26
+
27
+ ```bash
28
+ yarn create time2ship my-app
29
+ ```
30
+
31
+ ### With pnpm
32
+
33
+ ```bash
34
+ pnpm create time2ship my-app
35
+ ```
36
+
37
+ ## What You Get
38
+
39
+ Time2Ship is a production-ready full-stack monorepo featuring:
40
+
41
+ - โšก **Next.js 16** with React 19
42
+ - ๐Ÿ›ก๏ธ **Express API** with TypeScript
43
+ - ๐Ÿ—„๏ธ **PostgreSQL** with Drizzle ORM
44
+ - ๐Ÿณ **Docker** ready
45
+ - ๐Ÿ” **JWT Authentication** out of the box
46
+ - ๐Ÿงช **Comprehensive testing** setup
47
+ - ๐Ÿ“ง **Email service** with templates
48
+ - ๐ŸŽจ **Tailwind CSS 4**
49
+
50
+ ## Interactive Setup
51
+
52
+ The CLI will guide you through:
53
+
54
+ 1. **Project Name** - Choose your project name
55
+ 2. **Description** - Optional project description
56
+ 3. **Package Manager** - npm, yarn, or pnpm
57
+ 4. **Dependencies** - Install now or later
58
+ 5. **Git** - Initialize git repository
59
+ 6. **Environment** - Auto-generate .env files
60
+
61
+ ## After Creation
62
+
63
+ ```bash
64
+ cd my-app
65
+
66
+ # Start with Docker (recommended)
67
+ docker-compose up
68
+
69
+ # Or run individually
70
+ docker-compose up db # Database
71
+ cd apps/api && npm run dev # API
72
+ cd apps/client && npm run dev # Client
73
+ ```
74
+
75
+ Your app will be available at:
76
+ - Frontend: http://localhost:3000
77
+ - API: http://localhost:3001
78
+
79
+ ## Documentation
80
+
81
+ Full documentation available in your project's README.md
82
+
83
+ ## Support
84
+
85
+ - ๐Ÿ“– [Documentation](https://github.com/time2build-ai/time2ship)
86
+ - ๐Ÿ› [Report Issues](https://github.com/time2build-ai/time2ship/issues)
87
+ - ๐Ÿ’ฌ [Discussions](https://github.com/time2build-ai/time2ship/discussions)
88
+
89
+ ## License
90
+
91
+ ISC
package/bin/cli.js ADDED
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * create-time2ship CLI
5
+ * Entry point for the package
6
+ */
7
+
8
+ const currentNodeVersion = process.versions.node;
9
+ const semver = currentNodeVersion.split('.');
10
+ const major = parseInt(semver[0], 10);
11
+
12
+ // Check Node.js version
13
+ if (major < 18) {
14
+ console.error(
15
+ 'You are running Node ' +
16
+ currentNodeVersion +
17
+ '.\n' +
18
+ 'create-time2ship requires Node 18 or higher.\n' +
19
+ 'Please update your version of Node.'
20
+ );
21
+ process.exit(1);
22
+ }
23
+
24
+ // Run the main CLI
25
+ require('../src/index.js');
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "create-time2ship",
3
+ "version": "1.0.0",
4
+ "description": "Create a new Time2Ship full-stack application in seconds",
5
+ "main": "src/index.js",
6
+ "bin": {
7
+ "create-time2ship": "./bin/cli.js"
8
+ },
9
+ "scripts": {
10
+ "test": "node test.js",
11
+ "lint": "eslint .",
12
+ "format": "prettier --write \"**/*.js\""
13
+ },
14
+ "keywords": [
15
+ "create-time2ship",
16
+ "time2ship",
17
+ "boilerplate",
18
+ "nextjs",
19
+ "express",
20
+ "fullstack",
21
+ "typescript",
22
+ "postgresql",
23
+ "monorepo",
24
+ "template",
25
+ "cli"
26
+ ],
27
+ "author": "Thiago Lopez <thiago@time2build.ai>",
28
+ "license": "ISC",
29
+ "repository": {
30
+ "type": "git",
31
+ "url": "https://github.com/time2build-ai/create-time2ship.git"
32
+ },
33
+ "bugs": {
34
+ "url": "https://github.com/time2build-ai/create-time2ship/issues"
35
+ },
36
+ "homepage": "https://github.com/time2build-ai/create-time2ship#readme",
37
+ "engines": {
38
+ "node": ">=18.0.0"
39
+ },
40
+ "dependencies": {
41
+ "chalk": "^4.1.2",
42
+ "execa": "^5.1.1",
43
+ "fs-extra": "^11.1.1",
44
+ "ora": "^5.4.1",
45
+ "prompts": "^2.4.2",
46
+ "validate-npm-package-name": "^5.0.0"
47
+ },
48
+ "devDependencies": {
49
+ "eslint": "^8.50.0",
50
+ "prettier": "^3.0.3"
51
+ }
52
+ }
package/src/index.js ADDED
@@ -0,0 +1,41 @@
1
+ const path = require('path');
2
+ const chalk = require('chalk');
3
+ const { runPrompts } = require('./prompts');
4
+ const { setupProject } = require('./setup');
5
+ const { printWelcome, printSuccess, printNextSteps } = require('./utils');
6
+
7
+ async function run() {
8
+ try {
9
+ // Print welcome message
10
+ printWelcome();
11
+
12
+ // Get user input
13
+ const config = await runPrompts();
14
+
15
+ if (!config) {
16
+ console.log(chalk.yellow('\nSetup cancelled.'));
17
+ process.exit(0);
18
+ }
19
+
20
+ // Setup the project
21
+ const projectPath = path.join(process.cwd(), config.projectName);
22
+ await setupProject(config, projectPath);
23
+
24
+ // Print success and next steps
25
+ printSuccess(config.projectName);
26
+ printNextSteps(config);
27
+
28
+ } catch (error) {
29
+ console.error(chalk.red('\nโœ— Error:'), error.message);
30
+ console.error(error.stack);
31
+ process.exit(1);
32
+ }
33
+ }
34
+
35
+ // Handle unhandled rejections
36
+ process.on('unhandledRejection', (err) => {
37
+ console.error(chalk.red('\nUnhandled error:'), err);
38
+ process.exit(1);
39
+ });
40
+
41
+ run();
package/src/prompts.js ADDED
@@ -0,0 +1,92 @@
1
+ const prompts = require('prompts');
2
+ const chalk = require('chalk');
3
+ const fs = require('fs-extra');
4
+ const path = require('path');
5
+ const validatePackageName = require('validate-npm-package-name');
6
+
7
+ /**
8
+ * Validate project name
9
+ */
10
+ function validateProjectName(name) {
11
+ const validation = validatePackageName(name);
12
+
13
+ if (!validation.validForNewPackages) {
14
+ const errors = [
15
+ ...(validation.errors || []),
16
+ ...(validation.warnings || [])
17
+ ];
18
+ return errors[0] || 'Invalid project name';
19
+ }
20
+
21
+ // Check if directory already exists
22
+ const targetDir = path.join(process.cwd(), name);
23
+ if (fs.existsSync(targetDir)) {
24
+ return `Directory "${name}" already exists. Please choose a different name.`;
25
+ }
26
+
27
+ return true;
28
+ }
29
+
30
+ /**
31
+ * Run interactive prompts
32
+ */
33
+ async function runPrompts() {
34
+ const questions = [
35
+ {
36
+ type: 'text',
37
+ name: 'projectName',
38
+ message: 'What is your project name?',
39
+ initial: 'my-time2ship-app',
40
+ validate: validateProjectName
41
+ },
42
+ {
43
+ type: 'text',
44
+ name: 'description',
45
+ message: 'Project description (optional):',
46
+ initial: 'A Time2Ship application'
47
+ },
48
+ {
49
+ type: 'select',
50
+ name: 'packageManager',
51
+ message: 'Which package manager do you want to use?',
52
+ choices: [
53
+ { title: 'npm', value: 'npm' },
54
+ { title: 'yarn', value: 'yarn' },
55
+ { title: 'pnpm', value: 'pnpm' }
56
+ ],
57
+ initial: 0
58
+ },
59
+ {
60
+ type: 'confirm',
61
+ name: 'installDeps',
62
+ message: 'Install dependencies now?',
63
+ initial: true
64
+ },
65
+ {
66
+ type: 'confirm',
67
+ name: 'initGit',
68
+ message: 'Initialize a git repository?',
69
+ initial: true
70
+ },
71
+ {
72
+ type: 'confirm',
73
+ name: 'setupEnv',
74
+ message: 'Create environment files (.env)?',
75
+ initial: true
76
+ }
77
+ ];
78
+
79
+ const response = await prompts(questions, {
80
+ onCancel: () => {
81
+ console.log(chalk.red('\nโœ— Setup cancelled'));
82
+ process.exit(0);
83
+ }
84
+ });
85
+
86
+ return response;
87
+ }
88
+
89
+ module.exports = {
90
+ runPrompts,
91
+ validateProjectName
92
+ };
package/src/setup.js ADDED
@@ -0,0 +1,177 @@
1
+ const fs = require('fs-extra');
2
+ const path = require('path');
3
+ const ora = require('ora');
4
+ const chalk = require('chalk');
5
+ const { execSync } = require('child_process');
6
+ const { execa } = require('execa');
7
+ const { generateEnvFiles } = require('./templates');
8
+
9
+ /**
10
+ * Clone the Time2Ship repository
11
+ */
12
+ async function cloneRepository(targetDir) {
13
+ const spinner = ora('Cloning Time2Ship boilerplate...').start();
14
+
15
+ try {
16
+ // Clone from GitHub
17
+ execSync(
18
+ `git clone --depth 1 https://github.com/time2build-ai/time2ship.git "${targetDir}"`,
19
+ { stdio: 'pipe' }
20
+ );
21
+
22
+ // Remove .git directory
23
+ await fs.remove(path.join(targetDir, '.git'));
24
+
25
+ spinner.succeed('Boilerplate cloned successfully');
26
+ } catch (error) {
27
+ spinner.fail('Failed to clone repository');
28
+ throw new Error(`Git clone failed: ${error.message}`);
29
+ }
30
+ }
31
+
32
+ /**
33
+ * Install dependencies
34
+ */
35
+ async function installDependencies(projectPath, packageManager) {
36
+ const spinner = ora(`Installing dependencies with ${packageManager}...`).start();
37
+
38
+ try {
39
+ await execa(packageManager, ['install'], {
40
+ cwd: projectPath,
41
+ stdio: 'pipe'
42
+ });
43
+
44
+ spinner.succeed('Dependencies installed');
45
+ } catch (error) {
46
+ spinner.fail('Failed to install dependencies');
47
+ console.log(chalk.yellow('\nYou can install dependencies manually later by running:'));
48
+ console.log(chalk.cyan(` cd ${path.basename(projectPath)}`));
49
+ console.log(chalk.cyan(` ${packageManager} install\n`));
50
+ }
51
+ }
52
+
53
+ /**
54
+ * Initialize git repository
55
+ */
56
+ async function initializeGit(projectPath) {
57
+ const spinner = ora('Initializing git repository...').start();
58
+
59
+ try {
60
+ execSync('git init', { cwd: projectPath, stdio: 'pipe' });
61
+ execSync('git add .', { cwd: projectPath, stdio: 'pipe' });
62
+ execSync('git commit -m "Initial commit from create-time2ship"', {
63
+ cwd: projectPath,
64
+ stdio: 'pipe'
65
+ });
66
+
67
+ spinner.succeed('Git repository initialized');
68
+ } catch (error) {
69
+ spinner.fail('Failed to initialize git');
70
+ console.log(chalk.yellow('You can initialize git manually later.'));
71
+ }
72
+ }
73
+
74
+ /**
75
+ * Setup environment files
76
+ */
77
+ async function setupEnvironmentFiles(projectPath, config) {
78
+ const spinner = ora('Creating environment files...').start();
79
+
80
+ try {
81
+ const { apiEnv, clientEnv } = generateEnvFiles(config);
82
+
83
+ // Write API .env
84
+ const apiEnvPath = path.join(projectPath, 'apps', 'api', '.env');
85
+ await fs.writeFile(apiEnvPath, apiEnv);
86
+
87
+ // Write Client .env
88
+ const clientEnvPath = path.join(projectPath, 'apps', 'client', '.env.local');
89
+ await fs.writeFile(clientEnvPath, clientEnv);
90
+
91
+ spinner.succeed('Environment files created');
92
+ } catch (error) {
93
+ spinner.fail('Failed to create environment files');
94
+ console.log(chalk.yellow('\nYou can create environment files manually:'));
95
+ console.log(chalk.cyan(' cp apps/api/.env.example apps/api/.env'));
96
+ console.log(chalk.cyan(' cp apps/client/.env.example apps/client/.env.local\n'));
97
+ }
98
+ }
99
+
100
+ /**
101
+ * Update package.json with project details
102
+ */
103
+ async function updateProjectPackageJson(projectPath, config) {
104
+ const spinner = ora('Updating package.json...').start();
105
+
106
+ try {
107
+ // Update root package.json
108
+ const rootPkgPath = path.join(projectPath, 'package.json');
109
+ const rootPkg = await fs.readJson(rootPkgPath);
110
+
111
+ rootPkg.name = config.projectName;
112
+ rootPkg.description = config.description;
113
+ rootPkg.version = '0.1.0';
114
+
115
+ await fs.writeJson(rootPkgPath, rootPkg, { spaces: 2 });
116
+
117
+ // Update client package.json
118
+ const clientPkgPath = path.join(projectPath, 'apps', 'client', 'package.json');
119
+ const clientPkg = await fs.readJson(clientPkgPath);
120
+
121
+ clientPkg.name = `${config.projectName}-client`;
122
+ clientPkg.version = '0.1.0';
123
+
124
+ await fs.writeJson(clientPkgPath, clientPkg, { spaces: 2 });
125
+
126
+ // Update API package.json
127
+ const apiPkgPath = path.join(projectPath, 'apps', 'api', 'package.json');
128
+ const apiPkg = await fs.readJson(apiPkgPath);
129
+
130
+ apiPkg.name = `${config.projectName}-api`;
131
+ apiPkg.version = '0.1.0';
132
+
133
+ await fs.writeJson(apiPkgPath, apiPkg, { spaces: 2 });
134
+
135
+ spinner.succeed('package.json files updated');
136
+ } catch (error) {
137
+ spinner.warn('Could not update package.json files');
138
+ }
139
+ }
140
+
141
+ /**
142
+ * Main setup function
143
+ */
144
+ async function setupProject(config, projectPath) {
145
+ console.log(); // Empty line for spacing
146
+
147
+ // 1. Clone repository
148
+ await cloneRepository(projectPath);
149
+
150
+ // 2. Update package.json files
151
+ await updateProjectPackageJson(projectPath, config);
152
+
153
+ // 3. Setup environment files
154
+ if (config.setupEnv) {
155
+ await setupEnvironmentFiles(projectPath, config);
156
+ }
157
+
158
+ // 4. Install dependencies
159
+ if (config.installDeps) {
160
+ await installDependencies(projectPath, config.packageManager);
161
+ }
162
+
163
+ // 5. Initialize git
164
+ if (config.initGit) {
165
+ await initializeGit(projectPath);
166
+ }
167
+
168
+ console.log(); // Empty line for spacing
169
+ }
170
+
171
+ module.exports = {
172
+ setupProject,
173
+ cloneRepository,
174
+ installDependencies,
175
+ initializeGit,
176
+ setupEnvironmentFiles
177
+ };
@@ -0,0 +1,87 @@
1
+ const crypto = require('crypto');
2
+
3
+ /**
4
+ * Generate a random secret
5
+ */
6
+ function generateSecret(length = 32) {
7
+ return crypto.randomBytes(length).toString('base64');
8
+ }
9
+
10
+ /**
11
+ * Generate API .env file content
12
+ */
13
+ function generateApiEnv(config) {
14
+ return `# ๐Ÿ–ฅ๏ธ Server Configuration
15
+ NODE_ENV=development
16
+ PORT=3001
17
+
18
+ # ๐Ÿ—„๏ธ Database Configuration
19
+ DB_HOST=localhost
20
+ DB_PORT=5432
21
+ DB_NAME=${config.projectName.replace(/-/g, '_')}_db
22
+ DB_USER=postgres
23
+ DB_PASSWORD=postgres
24
+
25
+ # ๐Ÿ” JWT Secrets (Auto-generated - Change in production!)
26
+ JWT_ACCESS_SECRET=${generateSecret()}
27
+ JWT_REFRESH_SECRET=${generateSecret()}
28
+
29
+ # ๐Ÿ“ง Email Configuration (Update with your SMTP details)
30
+ EMAIL_HOST=smtp.gmail.com
31
+ EMAIL_PORT=587
32
+ EMAIL_USER=your-email@gmail.com
33
+ EMAIL_PASSWORD=your-app-password
34
+ EMAIL_FROM=noreply@${config.projectName}.com
35
+
36
+ # ๐ŸŒ URLs
37
+ CLIENT_URL=http://localhost:3000
38
+ API_URL=http://localhost:3001
39
+
40
+ # ๐Ÿ”‘ OTP Configuration
41
+ OTP_EXPIRY_MINUTES=10
42
+ `;
43
+ }
44
+
45
+ /**
46
+ * Generate Client .env file content
47
+ */
48
+ function generateClientEnv(config) {
49
+ return `# ๐ŸŒ API Configuration
50
+ NEXT_PUBLIC_API_URL=http://localhost:3001
51
+ API_URL=http://api:3001
52
+
53
+ # ๐Ÿ“ฑ App Configuration
54
+ NEXT_PUBLIC_APP_NAME=${config.projectName}
55
+ NEXT_PUBLIC_APP_URL=http://localhost:3000
56
+ `;
57
+ }
58
+
59
+ /**
60
+ * Generate both environment files
61
+ */
62
+ function generateEnvFiles(config) {
63
+ return {
64
+ apiEnv: generateApiEnv(config),
65
+ clientEnv: generateClientEnv(config)
66
+ };
67
+ }
68
+
69
+ /**
70
+ * Update package.json content
71
+ */
72
+ function updatePackageJson(packageJson, config) {
73
+ return {
74
+ ...packageJson,
75
+ name: config.projectName,
76
+ description: config.description,
77
+ version: '0.1.0'
78
+ };
79
+ }
80
+
81
+ module.exports = {
82
+ generateEnvFiles,
83
+ generateApiEnv,
84
+ generateClientEnv,
85
+ updatePackageJson,
86
+ generateSecret
87
+ };
package/src/utils.js ADDED
@@ -0,0 +1,106 @@
1
+ const chalk = require('chalk');
2
+
3
+ /**
4
+ * Print welcome banner
5
+ */
6
+ function printWelcome() {
7
+ console.log();
8
+ console.log(chalk.blue.bold(' โ•”โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•—'));
9
+ console.log(chalk.blue.bold(' โ•‘ โ•‘'));
10
+ console.log(chalk.blue.bold(' โ•‘ ๐Ÿš€ Welcome to Time2Ship ๐Ÿš€ โ•‘'));
11
+ console.log(chalk.blue.bold(' โ•‘ โ•‘'));
12
+ console.log(chalk.blue.bold(' โ•‘ Ship your ideas faster! โ•‘'));
13
+ console.log(chalk.blue.bold(' โ•‘ โ•‘'));
14
+ console.log(chalk.blue.bold(' โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'));
15
+ console.log();
16
+ }
17
+
18
+ /**
19
+ * Print success message
20
+ */
21
+ function printSuccess(projectName) {
22
+ console.log();
23
+ console.log(chalk.green.bold(' โœ“ Success!'), `Your project "${projectName}" is ready!`);
24
+ console.log();
25
+ }
26
+
27
+ /**
28
+ * Print next steps
29
+ */
30
+ function printNextSteps(config) {
31
+ const { projectName, installDeps, setupEnv, packageManager } = config;
32
+
33
+ console.log(chalk.cyan.bold('๐Ÿ“‹ Next Steps:\n'));
34
+
35
+ // Step 1: Navigate to directory
36
+ console.log(chalk.white(' 1. Navigate to your project:'));
37
+ console.log(chalk.gray(` cd ${projectName}\n`));
38
+
39
+ // Step 2: Install dependencies (if not done)
40
+ if (!installDeps) {
41
+ console.log(chalk.white(' 2. Install dependencies:'));
42
+ console.log(chalk.gray(` ${packageManager} install\n`));
43
+ }
44
+
45
+ // Step 3: Configure environment (if not done)
46
+ if (!setupEnv) {
47
+ console.log(chalk.white(` ${installDeps ? '2' : '3'}. Set up environment variables:`));
48
+ console.log(chalk.gray(' cp apps/api/.env.example apps/api/.env'));
49
+ console.log(chalk.gray(' cp apps/client/.env.example apps/client/.env.local'));
50
+ console.log(chalk.yellow(' โš ๏ธ Update the .env files with your configuration\n'));
51
+ } else {
52
+ console.log(chalk.white(` ${installDeps ? '2' : '3'}. Review and update environment variables:`));
53
+ console.log(chalk.gray(' apps/api/.env'));
54
+ console.log(chalk.gray(' apps/client/.env.local'));
55
+ console.log(chalk.yellow(' โš ๏ธ JWT secrets have been auto-generated\n'));
56
+ }
57
+
58
+ // Step 4: Start development
59
+ const stepNum = installDeps && setupEnv ? '3' : installDeps || setupEnv ? '4' : '5';
60
+ console.log(chalk.white(` ${stepNum}. Start development with Docker:`));
61
+ console.log(chalk.gray(' docker-compose up\n'));
62
+
63
+ console.log(chalk.white(' Or run services individually:'));
64
+ console.log(chalk.gray(' # Terminal 1 - Database'));
65
+ console.log(chalk.gray(' docker-compose up db\n'));
66
+ console.log(chalk.gray(' # Terminal 2 - API'));
67
+ console.log(chalk.gray(' cd apps/api && npm run dev\n'));
68
+ console.log(chalk.gray(' # Terminal 3 - Client'));
69
+ console.log(chalk.gray(' cd apps/client && npm run dev\n'));
70
+
71
+ // URLs
72
+ console.log(chalk.cyan.bold('๐ŸŒ Your app will be available at:\n'));
73
+ console.log(chalk.white(' Frontend:'), chalk.blue.underline('http://localhost:3000'));
74
+ console.log(chalk.white(' API: '), chalk.blue.underline('http://localhost:3001'));
75
+ console.log(chalk.white(' Database:'), chalk.gray('localhost:5432\n'));
76
+
77
+ // Documentation
78
+ console.log(chalk.cyan.bold('๐Ÿ“š Documentation:\n'));
79
+ console.log(chalk.gray(` Check out the README.md in your project for detailed docs\n`));
80
+
81
+ // Happy coding
82
+ console.log(chalk.green.bold(' Happy shipping! ๐Ÿš€\n'));
83
+
84
+ // Support
85
+ console.log(chalk.gray(' Need help? Visit: ') + chalk.blue.underline('https://github.com/time2build-ai/time2ship'));
86
+ console.log();
87
+ }
88
+
89
+ /**
90
+ * Check if a command exists
91
+ */
92
+ function commandExists(command) {
93
+ try {
94
+ require('child_process').execSync(`which ${command}`, { stdio: 'ignore' });
95
+ return true;
96
+ } catch {
97
+ return false;
98
+ }
99
+ }
100
+
101
+ module.exports = {
102
+ printWelcome,
103
+ printSuccess,
104
+ printNextSteps,
105
+ commandExists
106
+ };