create-alta-app 1.0.0 → 1.4.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 (2) hide show
  1. package/index.mjs +125 -73
  2. package/package.json +3 -1
package/index.mjs CHANGED
@@ -4,15 +4,15 @@ import { execSync } from 'node:child_process';
4
4
  import fs from 'node:fs';
5
5
  import path from 'node:path';
6
6
  import prompts from 'prompts';
7
+ import ora from 'ora';
8
+ import pc from 'picocolors';
7
9
 
8
10
  const TEMPLATE_REPO = 'adiel-hub/alta-boilerplate';
9
11
  const BRANCH = 'main';
10
-
11
- // ── Alta project provisioning service ──
12
12
  const ALTA_SERVICE_URL = 'https://ikbbbmmzxeemjwzrzsmt.supabase.co/functions/v1/create-project';
13
13
 
14
14
  function run(cmd, cwd) {
15
- execSync(cmd, { cwd, stdio: 'inherit' });
15
+ execSync(cmd, { cwd, stdio: 'ignore' });
16
16
  }
17
17
 
18
18
  function canRun(cmd) {
@@ -45,16 +45,10 @@ async function createProject(name, password) {
45
45
  function removeAuth(targetDir) {
46
46
  const appDir = path.join(targetDir, 'app');
47
47
 
48
- // Remove auth routes
49
48
  fs.rmSync(path.join(appDir, 'routes', 'auth'), { recursive: true, force: true });
50
-
51
- // Remove auth provider
52
49
  fs.unlinkSync(path.join(appDir, 'providers', 'auth-provider.tsx'));
53
-
54
- // Rewrite providers/index.ts — no auth exports
55
50
  fs.writeFileSync(path.join(appDir, 'providers', 'index.ts'), '');
56
51
 
57
- // Rewrite routes.ts — no auth routes
58
52
  fs.writeFileSync(
59
53
  path.join(appDir, 'routes.ts'),
60
54
  `import { type RouteConfig, route, layout } from '@react-router/dev/routes';
@@ -70,7 +64,6 @@ export default [
70
64
  `
71
65
  );
72
66
 
73
- // Rewrite _index.tsx — redirect to dashboard
74
67
  fs.writeFileSync(
75
68
  path.join(appDir, 'routes', '_index.tsx'),
76
69
  `import { useEffect } from 'react';
@@ -88,7 +81,6 @@ export default function IndexRoute() {
88
81
  `
89
82
  );
90
83
 
91
- // Rewrite app/_layout.tsx — no auth guard
92
84
  fs.writeFileSync(
93
85
  path.join(appDir, 'routes', 'app', '_layout.tsx'),
94
86
  `import { Outlet } from 'react-router';
@@ -111,7 +103,6 @@ export default function AppLayoutRoute() {
111
103
  `
112
104
  );
113
105
 
114
- // Rewrite root.tsx — no AuthProvider
115
106
  fs.writeFileSync(
116
107
  path.join(appDir, 'root.tsx'),
117
108
  `import { Links, Meta, Outlet, Scripts, ScrollRestoration } from 'react-router';
@@ -150,7 +141,6 @@ export default function Root() {
150
141
  `
151
142
  );
152
143
 
153
- // Rewrite header.tsx — no user/signOut
154
144
  fs.writeFileSync(
155
145
  path.join(appDir, 'components', 'layout', 'header.tsx'),
156
146
  `import { Separator } from '@altahq/design-system/components/ui/separator';
@@ -169,7 +159,6 @@ export function Header() {
169
159
  `
170
160
  );
171
161
 
172
- // Rewrite dashboard — no user reference
173
162
  fs.writeFileSync(
174
163
  path.join(appDir, 'routes', 'app', 'dashboard.tsx'),
175
164
  `import { Card, CardContent, CardHeader, CardTitle } from '@altahq/design-system/components/ui/card';
@@ -221,7 +210,6 @@ export default function DashboardRoute() {
221
210
  `
222
211
  );
223
212
 
224
- // Rewrite settings — no user reference
225
213
  fs.writeFileSync(
226
214
  path.join(appDir, 'routes', 'app', 'settings.tsx'),
227
215
  `import { Card, CardContent, CardHeader, CardTitle } from '@altahq/design-system/components/ui/card';
@@ -250,7 +238,26 @@ export default function SettingsRoute() {
250
238
  }
251
239
 
252
240
  async function main() {
253
- console.log('\n create-alta-app\n');
241
+ console.clear();
242
+ console.log('');
243
+ const W = 44;
244
+ const line = (s) => pc.magenta(' ┃') + pc.bold(pc.white(s.padEnd(W))) + pc.magenta('┃');
245
+ const dim = (s) => pc.magenta(' ┃') + pc.dim(s.padEnd(W)) + pc.magenta('┃');
246
+ const empty = () => pc.magenta(' ┃') + ' '.repeat(W) + pc.magenta('┃');
247
+ console.log(pc.magenta(' ┏' + '━'.repeat(W) + '┓'));
248
+ console.log(empty());
249
+ console.log(line(' █████╗ ██╗ ████████╗█████╗ '));
250
+ console.log(line(' ██╔══██╗██║ ╚══██╔══╝██╔══██╗'));
251
+ console.log(line(' ███████║██║ ██║ ███████║'));
252
+ console.log(line(' ██╔══██║██║ ██║ ██╔══██║'));
253
+ console.log(line(' ██║ ██║███████╗██║ ██║ ██║'));
254
+ console.log(line(' ╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝ ╚═╝'));
255
+ console.log(empty());
256
+ console.log(dim(' The Full-Stack Project Scaffolder'));
257
+ console.log(dim(' React · Supabase · Vercel · Claude Skills'));
258
+ console.log(empty());
259
+ console.log(pc.magenta(' ┗' + '━'.repeat(W) + '┛'));
260
+ console.log('');
254
261
 
255
262
  const argName = process.argv[2];
256
263
 
@@ -259,14 +266,14 @@ async function main() {
259
266
  {
260
267
  type: argName ? null : 'text',
261
268
  name: 'projectName',
262
- message: 'Project name:',
269
+ message: 'Project name',
263
270
  initial: 'my-alta-app',
264
271
  validate: (v) => (v.length > 0 ? true : 'Project name is required'),
265
272
  },
266
273
  {
267
274
  type: 'password',
268
275
  name: 'password',
269
- message: 'Alta password:',
276
+ message: 'Alta password',
270
277
  validate: (v) => (v.length > 0 ? true : 'Password is required'),
271
278
  },
272
279
  {
@@ -285,52 +292,63 @@ async function main() {
285
292
  const targetDir = path.resolve(process.cwd(), projectName);
286
293
 
287
294
  if (fs.existsSync(targetDir)) {
288
- console.error(`\n Error: Directory "${projectName}" already exists.\n`);
295
+ console.log(`\n ${pc.red('✗')} Directory ${pc.bold(`"${projectName}"`)} already exists.\n`);
289
296
  process.exit(1);
290
297
  }
291
298
 
292
- // ── Clone template ──
293
- console.log(' Downloading template...\n');
294
- run(`npx --yes degit ${TEMPLATE_REPO}#${BRANCH} "${projectName}"`, process.cwd());
299
+ console.log('');
295
300
 
296
- // Remove the create-alta-app package from the cloned template
297
- const cliDir = path.join(targetDir, 'packages', 'create-alta-app');
298
- if (fs.existsSync(cliDir)) {
299
- fs.rmSync(cliDir, { recursive: true, force: true });
300
- }
301
+ // ── Step 1: Clone template ──
302
+ const spinnerClone = ora({ text: 'Downloading template...', indent: 2 }).start();
303
+ try {
304
+ run(`npx --yes degit ${TEMPLATE_REPO}#${BRANCH} "${projectName}"`, process.cwd());
301
305
 
302
- // Remove the packages dir if empty
303
- const packagesDir = path.join(targetDir, 'packages');
304
- if (fs.existsSync(packagesDir) && fs.readdirSync(packagesDir).length === 0) {
305
- fs.rmSync(packagesDir, { recursive: true, force: true });
306
- }
306
+ const cliDir = path.join(targetDir, 'packages', 'create-alta-app');
307
+ if (fs.existsSync(cliDir)) fs.rmSync(cliDir, { recursive: true, force: true });
308
+ const packagesDir = path.join(targetDir, 'packages');
309
+ if (fs.existsSync(packagesDir) && fs.readdirSync(packagesDir).length === 0) {
310
+ fs.rmSync(packagesDir, { recursive: true, force: true });
311
+ }
307
312
 
308
- // Update package.json name
309
- const rootPkgPath = path.join(targetDir, 'package.json');
310
- const rootPkg = JSON.parse(fs.readFileSync(rootPkgPath, 'utf-8'));
311
- rootPkg.name = projectName;
312
- fs.writeFileSync(rootPkgPath, JSON.stringify(rootPkg, null, 2) + '\n');
313
+ const rootPkgPath = path.join(targetDir, 'package.json');
314
+ const rootPkg = JSON.parse(fs.readFileSync(rootPkgPath, 'utf-8'));
315
+ rootPkg.name = projectName;
316
+ fs.writeFileSync(rootPkgPath, JSON.stringify(rootPkg, null, 2) + '\n');
313
317
 
314
- // ── Remove auth if not needed ──
315
- if (!response.includeAuth) {
316
- console.log(' Removing auth...\n');
317
- removeAuth(targetDir);
318
+ spinnerClone.succeed(pc.green('Template downloaded'));
319
+ } catch (err) {
320
+ spinnerClone.fail(pc.red('Failed to download template'));
321
+ throw err;
318
322
  }
319
323
 
320
- // ── Create Supabase + Vercel projects ──
321
- console.log(' Creating Supabase & Vercel projects...\n');
324
+ // ── Step 2: Remove auth if not needed ──
325
+ if (!response.includeAuth) {
326
+ const spinnerAuth = ora({ text: 'Removing authentication...', indent: 2 }).start();
327
+ try {
328
+ removeAuth(targetDir);
329
+ spinnerAuth.succeed(pc.green('Authentication removed'));
330
+ } catch (err) {
331
+ spinnerAuth.fail(pc.red('Failed to remove auth'));
332
+ throw err;
333
+ }
334
+ }
322
335
 
336
+ // ── Step 3: Create cloud projects ──
337
+ const spinnerCloud = ora({ text: 'Creating Supabase & Vercel projects...', indent: 2 }).start();
323
338
  let credentials;
324
339
  try {
325
340
  credentials = await createProject(projectName, response.password);
341
+ spinnerCloud.succeed(pc.green('Cloud projects created'));
326
342
  } catch (err) {
327
- console.error(` Error creating projects: ${err.message}`);
328
- console.error(' You can set up manually later.\n');
343
+ spinnerCloud.fail(pc.yellow('Could not create cloud projects'));
344
+ console.log(` ${pc.dim(err.message)}`);
345
+ console.log(` ${pc.dim('You can set up manually later.')}`);
329
346
  credentials = null;
330
347
  }
331
348
 
349
+ // ── Step 4: Write env files ──
332
350
  if (credentials) {
333
- // Write .env at project root used by Vite dev server
351
+ const spinnerEnv = ora({ text: 'Writing environment variables...', indent: 2 }).start();
334
352
  const env = [
335
353
  `VITE_SUPABASE_URL=${credentials.supabaseUrl}`,
336
354
  `VITE_SUPABASE_ANON_KEY=${credentials.supabaseAnonKey}`,
@@ -340,63 +358,97 @@ async function main() {
340
358
  '',
341
359
  ].join('\n');
342
360
  fs.writeFileSync(path.join(targetDir, '.env'), env);
343
-
344
- console.log(' Supabase project created.');
345
- if (credentials.vercelUrl) {
346
- console.log(' Vercel project created.');
347
- }
348
- console.log('');
361
+ spinnerEnv.succeed(pc.green('Environment configured'));
349
362
  }
350
363
 
351
- // ── Install dependencies ──
352
- console.log(' Installing dependencies...\n');
364
+ // ── Step 5: Install dependencies ──
353
365
  if (canRun('pnpm --version')) {
354
- run('pnpm install', targetDir);
366
+ const spinnerInstall = ora({ text: 'Installing dependencies...', indent: 2 }).start();
367
+ try {
368
+ run('pnpm install', targetDir);
369
+ spinnerInstall.succeed(pc.green('Dependencies installed'));
370
+ } catch {
371
+ spinnerInstall.fail(pc.yellow('Failed to install dependencies'));
372
+ console.log(` ${pc.dim(`Run: cd ${projectName} && pnpm install`)}`);
373
+ }
355
374
  } else {
356
- console.log(' pnpm not found. Install it with: npm install -g pnpm');
357
- console.log(` Then run: cd ${projectName} && pnpm install\n`);
375
+ console.log(` ${pc.yellow('⚠')} pnpm not found. Install it: ${pc.bold('npm install -g pnpm')}`);
376
+ console.log(` ${pc.dim(`Then run: cd ${projectName} && pnpm install`)}`);
377
+ }
378
+
379
+ // ── Step 6: Install Claude Skills ──
380
+ const spinnerSkills = ora({ text: 'Installing Claude Skills...', indent: 2 }).start();
381
+ try {
382
+ run('npx --yes skills add https://github.com/supabase/agent-skills --skill supabase-postgres-best-practices', targetDir);
383
+ run('npx --yes skills add https://github.com/vercel-labs/agent-skills --skill vercel-react-best-practices', targetDir);
384
+ run('npx --yes skills add https://github.com/vercel-labs/agent-skills --skill web-design-guidelines', targetDir);
385
+ run('npx --yes skills add https://github.com/anthropics/skills --skill frontend-design', targetDir);
386
+ spinnerSkills.succeed(pc.green('Claude Skills installed'));
387
+ } catch {
388
+ spinnerSkills.warn(pc.yellow('Could not install Claude Skills'));
389
+ console.log(` ${pc.dim('Install manually: npx skills add <package>')}`);
358
390
  }
359
391
 
360
- // ── Init git + push to GitHub ──
361
- run('git init', targetDir);
362
- run('git add -A', targetDir);
392
+ // ── Step 7: Init git + push ──
393
+ const spinnerGit = ora({ text: 'Initializing git...', indent: 2 }).start();
363
394
  try {
395
+ run('git init', targetDir);
396
+ run('git add -A', targetDir);
364
397
  run('git commit -m "Initial commit from create-alta-app"', targetDir);
398
+ spinnerGit.succeed(pc.green('Git initialized'));
365
399
  } catch {
366
- // git commit can fail if no user is configured
400
+ spinnerGit.warn(pc.yellow('Git init completed (commit may have failed)'));
367
401
  }
368
402
 
369
403
  if (credentials?.githubRepoUrl) {
370
- console.log(' Pushing to GitHub...\n');
404
+ const spinnerPush = ora({ text: 'Pushing to GitHub...', indent: 2 }).start();
371
405
  try {
372
406
  run(`git remote add origin ${credentials.githubRepoUrl}`, targetDir);
373
407
  run('git branch -M main', targetDir);
374
408
  run('git push -u origin main', targetDir);
375
- console.log(' Pushed to GitHub.\n');
409
+ spinnerPush.succeed(pc.green('Pushed to GitHub'));
376
410
  } catch {
377
- console.log(' Could not push to GitHub. Push manually with: git push -u origin main\n');
411
+ spinnerPush.warn(pc.yellow('Could not push to GitHub'));
412
+ console.log(` ${pc.dim('Push manually: git push -u origin main')}`);
378
413
  }
379
414
  }
380
415
 
381
416
  // ── Done ──
382
- console.log(`\n Done! Your project is ready.\n`);
417
+ console.log('');
418
+ console.log(pc.bold(pc.green(' Your project is ready!')));
419
+ console.log('');
420
+
383
421
  if (credentials) {
384
- console.log(` Supabase: ${credentials.supabaseUrl}`);
422
+ console.log(` ${pc.bold('Project Details:')}`);
423
+ console.log('');
424
+ console.log(` ${pc.magenta('◆')} ${pc.dim('Supabase')} ${credentials.supabaseUrl}`);
385
425
  if (credentials.vercelUrl) {
386
- console.log(` Vercel: ${credentials.vercelUrl}`);
426
+ console.log(` ${pc.magenta('◆')} ${pc.dim('Vercel')} ${credentials.vercelUrl} ${pc.dim('(first deploy may take a few minutes)')}`);
427
+
387
428
  }
388
429
  if (credentials.githubRepoUrl) {
389
- console.log(` GitHub: ${credentials.githubRepoUrl}`);
430
+ console.log(` ${pc.magenta('◆')} ${pc.dim('GitHub')} ${credentials.githubRepoUrl}`);
390
431
  }
391
- console.log(` DB pass: ${credentials.dbPassword} (save this!)`);
432
+ console.log(` ${pc.magenta('◆')} ${pc.dim('DB pass')} Saved in .env`);
392
433
  console.log('');
393
- console.log(' Auto-deploy: every push deploys to Vercel');
434
+ console.log(` ${pc.dim('───────────────────────────────────────────')}`);
435
+ console.log(` ${pc.dim('Auto-deploy: every push to GitHub → Vercel')}`);
436
+ console.log(` ${pc.dim('───────────────────────────────────────────')}`);
394
437
  console.log('');
395
438
  }
396
- console.log(` Next steps:\n`);
397
- console.log(` cd ${projectName}`);
398
- console.log(` pnpm dev`);
439
+
440
+ // ── Step 8: Start dev server ──
441
+ console.log(` ${pc.bold('Starting dev server...')}`);
399
442
  console.log('');
443
+ try {
444
+ execSync('pnpm dev', { cwd: targetDir, stdio: 'inherit' });
445
+ } catch {
446
+ console.log('');
447
+ console.log(` ${pc.dim('To start manually:')}`);
448
+ console.log(` ${pc.cyan(`cd ${projectName}`)}`);
449
+ console.log(` ${pc.cyan('pnpm dev')}`);
450
+ console.log('');
451
+ }
400
452
  }
401
453
 
402
454
  main().catch((err) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-alta-app",
3
- "version": "1.0.0",
3
+ "version": "1.4.0",
4
4
  "description": "Create a new Alta project",
5
5
  "bin": {
6
6
  "create-alta-app": "./index.mjs"
@@ -10,6 +10,8 @@
10
10
  "keywords": ["alta", "boilerplate", "supabase", "react", "vite"],
11
11
  "license": "MIT",
12
12
  "dependencies": {
13
+ "ora": "^8.0.1",
14
+ "picocolors": "^1.1.0",
13
15
  "prompts": "^2.4.2"
14
16
  }
15
17
  }