create-alta-app 1.5.0 → 1.6.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 +157 -55
  2. package/package.json +1 -1
package/index.mjs CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  import { execSync } from 'node:child_process';
4
4
  import fs from 'node:fs';
5
+ import os from 'node:os';
5
6
  import path from 'node:path';
6
7
  import prompts from 'prompts';
7
8
  import ora from 'ora';
@@ -39,7 +40,11 @@ async function createProject(name, password) {
39
40
  throw new Error(err.error || `Server error: ${res.status}`);
40
41
  }
41
42
 
42
- return res.json();
43
+ const data = await res.json();
44
+ if (data.error) {
45
+ throw new Error(data.error);
46
+ }
47
+ return data;
43
48
  }
44
49
 
45
50
  function removeAuth(targetDir) {
@@ -259,6 +264,7 @@ async function main() {
259
264
  console.log(pc.magenta(' ┗' + '━'.repeat(W) + '┛'));
260
265
  console.log('');
261
266
 
267
+ const MONOREPO_BASE = path.join(os.homedir(), 'alta', 'apps', 'ai-engineer');
262
268
  const argName = process.argv[2];
263
269
 
264
270
  const response = await prompts(
@@ -289,7 +295,13 @@ async function main() {
289
295
  );
290
296
 
291
297
  const projectName = argName || response.projectName;
292
- const targetDir = path.resolve(process.cwd(), projectName);
298
+
299
+ // Always install under the Nx monorepo
300
+ if (!fs.existsSync(MONOREPO_BASE)) {
301
+ fs.mkdirSync(MONOREPO_BASE, { recursive: true });
302
+ }
303
+
304
+ const targetDir = path.resolve(MONOREPO_BASE, projectName);
293
305
 
294
306
  if (fs.existsSync(targetDir)) {
295
307
  console.log(`\n ${pc.red('✗')} Directory ${pc.bold(`"${projectName}"`)} already exists.\n`);
@@ -315,6 +327,80 @@ async function main() {
315
327
  rootPkg.name = projectName;
316
328
  fs.writeFileSync(rootPkgPath, JSON.stringify(rootPkg, null, 2) + '\n');
317
329
 
330
+ // Generate Nx project.json
331
+ const nxProject = {
332
+ name: projectName,
333
+ $schema: '../../node_modules/nx/schemas/project-schema.json',
334
+ sourceRoot: `apps/ai-engineer/${projectName}/src`,
335
+ projectType: 'application',
336
+ tags: [],
337
+ targets: {
338
+ build: {
339
+ executor: '@nx/vite:build',
340
+ outputs: ['{options.outputPath}'],
341
+ defaultConfiguration: 'production',
342
+ options: {
343
+ outputPath: `dist/apps/ai-engineer/${projectName}`,
344
+ emptyOutDir: true,
345
+ },
346
+ configurations: {
347
+ development: { mode: 'development' },
348
+ staging: { mode: 'staging' },
349
+ production: { mode: 'production' },
350
+ },
351
+ },
352
+ serve: {
353
+ executor: '@nx/vite:dev-server',
354
+ defaultConfiguration: 'development',
355
+ options: {
356
+ buildTarget: `${projectName}:build`,
357
+ },
358
+ configurations: {
359
+ development: {
360
+ buildTarget: `${projectName}:build:development`,
361
+ hmr: true,
362
+ },
363
+ production: {
364
+ buildTarget: `${projectName}:build:production`,
365
+ hmr: false,
366
+ },
367
+ },
368
+ },
369
+ preview: {
370
+ executor: '@nx/vite:preview-server',
371
+ defaultConfiguration: 'development',
372
+ options: {
373
+ buildTarget: `${projectName}:build`,
374
+ },
375
+ configurations: {
376
+ development: {
377
+ buildTarget: `${projectName}:build:development`,
378
+ },
379
+ production: {
380
+ buildTarget: `${projectName}:build:production`,
381
+ },
382
+ },
383
+ dependsOn: ['build'],
384
+ },
385
+ test: {
386
+ executor: '@nx/vitest:test',
387
+ outputs: ['{options.reportsDirectory}'],
388
+ options: {
389
+ passWithNoTests: true,
390
+ reportsDirectory: `../../coverage/apps/ai-engineer/${projectName}`,
391
+ },
392
+ },
393
+ lint: {
394
+ executor: '@nx/eslint:lint',
395
+ outputs: ['{options.outputFile}'],
396
+ },
397
+ },
398
+ };
399
+ fs.writeFileSync(
400
+ path.join(targetDir, 'project.json'),
401
+ JSON.stringify(nxProject, null, 2) + '\n'
402
+ );
403
+
318
404
  spinnerClone.succeed(pc.green('Template downloaded'));
319
405
  } catch (err) {
320
406
  spinnerClone.fail(pc.red('Failed to download template'));
@@ -346,24 +432,21 @@ async function main() {
346
432
  credentials = null;
347
433
  }
348
434
 
349
- // ── Step 4: Write env files ──
435
+ // ── Step 4: Write project config (committed to git — no .env needed) ──
350
436
  if (credentials) {
351
- const spinnerEnv = ora({ text: 'Writing environment variables...', indent: 2 }).start();
352
- const githubToken = credentials.githubRepoUrl
353
- ? credentials.githubRepoUrl.match(/https:\/\/([^@]+)@/)?.[1] || ''
354
- : '';
355
- const env = [
356
- `VITE_SUPABASE_URL=${credentials.supabaseUrl}`,
357
- `VITE_SUPABASE_ANON_KEY=${credentials.supabaseAnonKey}`,
358
- `SUPABASE_PROJECT_REF=${credentials.supabaseProjectRef}`,
359
- `SUPABASE_DB_PASSWORD=${credentials.dbPassword}`,
360
- `DATABASE_URL=${credentials.databaseUrl}`,
361
- `GITHUB_TOKEN=${githubToken}`,
362
- `GITHUB_REPO=${credentials.githubFullName || ''}`,
363
- '',
364
- ].join('\n');
365
- fs.writeFileSync(path.join(targetDir, '.env'), env);
366
- spinnerEnv.succeed(pc.green('Environment configured'));
437
+ const spinnerEnv = ora({ text: 'Writing project config...', indent: 2 }).start();
438
+ const altaConfig = {
439
+ supabaseProjectRef: credentials.supabaseProjectRef,
440
+ supabaseUrl: credentials.supabaseUrl,
441
+ supabaseAnonKey: credentials.supabaseAnonKey,
442
+ vercelProjectName: projectName,
443
+ vercelUrl: credentials.vercelUrl || '',
444
+ };
445
+ fs.writeFileSync(
446
+ path.join(targetDir, 'alta.config.json'),
447
+ JSON.stringify(altaConfig, null, 2) + '\n'
448
+ );
449
+ spinnerEnv.succeed(pc.green('Project configured'));
367
450
  }
368
451
 
369
452
  // ── Step 5: Install dependencies ──
@@ -381,7 +464,58 @@ async function main() {
381
464
  console.log(` ${pc.dim(`Then run: cd ${projectName} && pnpm install`)}`);
382
465
  }
383
466
 
384
- // ── Step 6: Install Claude Skills ──
467
+ // ── Step 6: Setup shell credentials (like AWS sandbox keys in ~/.zshrc) ──
468
+ const shellRc = path.join(os.homedir(), '.zshrc');
469
+ const tokensToSet = {
470
+ SUPABASE_ACCESS_TOKEN: 'sbp_85c565540fbc50b75873cf643226881cd4f58af5',
471
+ };
472
+
473
+ try {
474
+ const rcContent = fs.existsSync(shellRc) ? fs.readFileSync(shellRc, 'utf-8') : '';
475
+ const missing = Object.entries(tokensToSet).filter(([key]) => !rcContent.includes(`export ${key}=`));
476
+
477
+ if (missing.length > 0) {
478
+ const spinnerShell = ora({ text: 'Setting up shell credentials...', indent: 2 }).start();
479
+ const lines = ['\n# Alta'];
480
+ for (const [key, value] of missing) {
481
+ lines.push(`export ${key}=${value}`);
482
+ }
483
+ fs.appendFileSync(shellRc, lines.join('\n') + '\n');
484
+ spinnerShell.succeed(pc.green('Shell credentials configured'));
485
+ }
486
+ } catch {
487
+ console.log(` ${pc.dim('Could not update ~/.zshrc — add SUPABASE_ACCESS_TOKEN manually')}`);
488
+ }
489
+
490
+ // ── Step 7: Write project-specific MCP config ──
491
+ if (credentials) {
492
+ const spinnerMcp = ora({ text: 'Configuring Claude MCP...', indent: 2 }).start();
493
+ try {
494
+ const claudeDir = path.join(targetDir, '.claude');
495
+ if (!fs.existsSync(claudeDir)) fs.mkdirSync(claudeDir, { recursive: true });
496
+
497
+ const mcpConfig = {
498
+ mcpServers: {
499
+ supabase: {
500
+ url: `https://mcp.supabase.com/mcp`,
501
+ headers: {
502
+ 'x-supabase-access-token': '${SUPABASE_ACCESS_TOKEN}',
503
+ 'x-project-ref': credentials.supabaseProjectRef,
504
+ },
505
+ },
506
+ },
507
+ };
508
+ fs.writeFileSync(
509
+ path.join(claudeDir, 'mcp.json'),
510
+ JSON.stringify(mcpConfig, null, 2) + '\n'
511
+ );
512
+ spinnerMcp.succeed(pc.green('Claude MCP configured'));
513
+ } catch {
514
+ console.log(` ${pc.dim('Could not write .claude/mcp.json — configure MCP manually')}`);
515
+ }
516
+ }
517
+
518
+ // ── Step 8: Install Claude Skills ──
385
519
  const spinnerSkills = ora({ text: 'Installing Claude Skills...', indent: 2 }).start();
386
520
  try {
387
521
  run('npx --yes skills add https://github.com/supabase/agent-skills --skill supabase-postgres-best-practices', targetDir);
@@ -394,30 +528,6 @@ async function main() {
394
528
  console.log(` ${pc.dim('Install manually: npx skills add <package>')}`);
395
529
  }
396
530
 
397
- // ── Step 7: Init git + push ──
398
- const spinnerGit = ora({ text: 'Initializing git...', indent: 2 }).start();
399
- try {
400
- run('git init', targetDir);
401
- run('git add -A', targetDir);
402
- run('git commit -m "Initial commit from create-alta-app"', targetDir);
403
- spinnerGit.succeed(pc.green('Git initialized'));
404
- } catch {
405
- spinnerGit.warn(pc.yellow('Git init completed (commit may have failed)'));
406
- }
407
-
408
- if (credentials?.githubRepoUrl) {
409
- const spinnerPush = ora({ text: 'Pushing to GitHub...', indent: 2 }).start();
410
- try {
411
- run(`git remote add origin ${credentials.githubRepoUrl}`, targetDir);
412
- run('git branch -M main', targetDir);
413
- run('git push -u origin main', targetDir);
414
- spinnerPush.succeed(pc.green('Pushed to GitHub'));
415
- } catch {
416
- spinnerPush.warn(pc.yellow('Could not push to GitHub'));
417
- console.log(` ${pc.dim('Push manually: git push -u origin main')}`);
418
- }
419
- }
420
-
421
531
  // ── Done ──
422
532
  console.log('');
423
533
  console.log(pc.bold(pc.green(' Your project is ready!')));
@@ -429,21 +539,13 @@ async function main() {
429
539
  console.log(` ${pc.magenta('◆')} ${pc.dim('Supabase')} ${credentials.supabaseUrl}`);
430
540
  if (credentials.vercelUrl) {
431
541
  console.log(` ${pc.magenta('◆')} ${pc.dim('Vercel')} ${credentials.vercelUrl} ${pc.dim('(first deploy may take a few minutes)')}`);
432
-
433
- }
434
- if (credentials.githubRepoUrl) {
435
- const cleanGitUrl = credentials.githubRepoUrl.replace(/https:\/\/[^@]+@/, 'https://');
436
- console.log(` ${pc.magenta('◆')} ${pc.dim('GitHub')} ${cleanGitUrl}`);
437
542
  }
438
- console.log(` ${pc.magenta('◆')} ${pc.dim('DB pass')} Saved in .env`);
439
- console.log('');
440
- console.log(` ${pc.dim('───────────────────────────────────────────')}`);
441
- console.log(` ${pc.dim('Auto-deploy: every push to GitHub → Vercel')}`);
442
- console.log(` ${pc.dim('───────────────────────────────────────────')}`);
543
+ console.log(` ${pc.magenta('◆')} ${pc.dim('Config')} alta.config.json`);
544
+ console.log(` ${pc.magenta('')} ${pc.dim('Location')} ${targetDir}`);
443
545
  console.log('');
444
546
  }
445
547
 
446
- // ── Step 8: Start dev server ──
548
+ // ── Step 9: Start dev server ──
447
549
  console.log(` ${pc.bold('Starting dev server...')}`);
448
550
  console.log('');
449
551
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-alta-app",
3
- "version": "1.5.0",
3
+ "version": "1.6.0",
4
4
  "description": "Create a new Alta project",
5
5
  "bin": {
6
6
  "create-alta-app": "./index.mjs"