genbox 1.0.43 → 1.0.45

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.
@@ -114,6 +114,27 @@ function getPrivateSshKey() {
114
114
  }
115
115
  return undefined;
116
116
  }
117
+ /**
118
+ * Get local git config for commits on genbox
119
+ */
120
+ function getGitConfig() {
121
+ const { execSync } = require('child_process');
122
+ let userName;
123
+ let userEmail;
124
+ try {
125
+ userName = execSync('git config --global user.name', { encoding: 'utf-8' }).trim();
126
+ }
127
+ catch {
128
+ // Git config not set
129
+ }
130
+ try {
131
+ userEmail = execSync('git config --global user.email', { encoding: 'utf-8' }).trim();
132
+ }
133
+ catch {
134
+ // Git config not set
135
+ }
136
+ return { userName, userEmail };
137
+ }
117
138
  /**
118
139
  * Prompt user for environment name
119
140
  */
@@ -200,9 +221,9 @@ exports.createCommand = new commander_1.Command('create')
200
221
  .option('--db <mode>', 'Database mode: none, local, copy, remote')
201
222
  .option('--db-source <source>', 'Database source: staging, production')
202
223
  .option('-s, --size <size>', 'Server size: small, medium, large, xl')
203
- .option('-b, --branch <branch>', 'Git branch to checkout')
204
- .option('-n, --new-branch <name>', 'Create a new branch with this name')
205
- .option('-f, --from-branch <branch>', 'Source branch to create new branch from (defaults to current/default branch)')
224
+ .option('-b, --branch <branch>', 'Use existing git branch (skips new branch creation)')
225
+ .option('-n, --new-branch <name>', 'Create a new branch with this name (defaults to env name)')
226
+ .option('-f, --from-branch <branch>', 'Source branch to create new branch from (defaults to main)')
206
227
  .option('-y, --yes', 'Skip interactive prompts')
207
228
  .option('--dry-run', 'Show what would be created without actually creating')
208
229
  .action(async (nameArg, options) => {
@@ -265,19 +286,19 @@ exports.createCommand = new commander_1.Command('create')
265
286
  }
266
287
  // Silently continue for other errors - the create API will validate
267
288
  }
268
- // Validate branch options
289
+ // Determine branch configuration
290
+ // Default: create new branch from 'main' with name = environment name
291
+ let newBranchName = options.newBranch;
292
+ let sourceBranch = options.fromBranch;
293
+ // If -n (--new-branch) is provided without -f (--from-branch), default source to 'main'
269
294
  if (options.newBranch && !options.fromBranch) {
270
- console.error(chalk_1.default.red('Error: --new-branch (-n) requires --from-branch (-f) to specify the source branch'));
271
- console.log(chalk_1.default.dim(' Example: genbox create my-env -n feature/new -f main'));
272
- return;
295
+ sourceBranch = 'main';
296
+ console.log(chalk_1.default.dim(` Source branch not specified, using 'main'`));
273
297
  }
274
- // Auto-generate branch name if only -f is provided
275
- let newBranchName = options.newBranch;
298
+ // If -f (--from-branch) is provided without -n (--new-branch), use env name as branch name
276
299
  if (options.fromBranch && !options.newBranch) {
277
- // Generate unique branch name: {genbox-name}-{short-timestamp}
278
- const timestamp = Date.now().toString(36); // Short alphanumeric timestamp
279
- newBranchName = `${name}-${timestamp}`;
280
- console.log(chalk_1.default.dim(` Auto-generated branch name: ${newBranchName}`));
300
+ newBranchName = name;
301
+ console.log(chalk_1.default.dim(` New branch name: ${newBranchName}`));
281
302
  }
282
303
  // Build create options
283
304
  const createOptions = {
@@ -291,7 +312,7 @@ exports.createCommand = new commander_1.Command('create')
291
312
  size: options.size,
292
313
  branch: options.branch,
293
314
  newBranch: newBranchName,
294
- sourceBranch: options.fromBranch,
315
+ sourceBranch: sourceBranch,
295
316
  yes: options.yes,
296
317
  dryRun: options.dryRun,
297
318
  };
@@ -302,7 +323,20 @@ exports.createCommand = new commander_1.Command('create')
302
323
  // Interactive branch selection if no branch options were specified
303
324
  // Skip if: -b (existing branch), -f (new branch from source), -n (explicit new branch name), or -y (skip prompts)
304
325
  if (!options.branch && !options.fromBranch && !options.newBranch && !options.yes && resolved.repos.length > 0) {
305
- resolved = await promptForBranchOptions(resolved, config);
326
+ resolved = await promptForBranchOptions(resolved, config, name);
327
+ }
328
+ // Default behavior when -y (non-interactive) and no branch options: create new branch from main
329
+ if (!options.branch && !options.fromBranch && !options.newBranch && options.yes && resolved.repos.length > 0) {
330
+ resolved = {
331
+ ...resolved,
332
+ repos: resolved.repos.map(repo => ({
333
+ ...repo,
334
+ branch: name, // Branch name same as environment name
335
+ newBranch: name,
336
+ sourceBranch: 'main',
337
+ })),
338
+ };
339
+ console.log(chalk_1.default.dim(` Creating new branch '${name}' from 'main'`));
306
340
  }
307
341
  // Display resolved configuration
308
342
  displayResolvedConfig(resolved);
@@ -494,59 +528,51 @@ function displayResolvedConfig(resolved) {
494
528
  }
495
529
  /**
496
530
  * Prompt for branch options interactively
531
+ * Default behavior: create new branch from 'main' with branch name = environment name
497
532
  */
498
- async function promptForBranchOptions(resolved, config) {
533
+ async function promptForBranchOptions(resolved, config, envName) {
499
534
  // Get the default branch from config or first repo
500
535
  const defaultBranch = config.defaults?.branch || resolved.repos[0]?.branch || 'main';
501
536
  console.log(chalk_1.default.blue('=== Branch Configuration ==='));
502
- console.log(chalk_1.default.dim(`Default branch: ${defaultBranch}`));
503
537
  console.log('');
504
538
  const branchChoice = await prompts.select({
505
539
  message: 'Branch option:',
506
540
  choices: [
507
541
  {
508
- name: `Use default branch (${defaultBranch})`,
509
- value: 'default',
542
+ name: `${chalk_1.default.green('Create new branch')} '${chalk_1.default.cyan(envName)}' from 'main' ${chalk_1.default.dim('(recommended)')}`,
543
+ value: 'new-from-main',
544
+ },
545
+ {
546
+ name: `Create new branch from a different source`,
547
+ value: 'new-custom',
510
548
  },
511
549
  {
512
- name: 'Use a different existing branch',
550
+ name: 'Use an existing branch',
513
551
  value: 'existing',
514
552
  },
515
553
  {
516
- name: 'Create a new branch',
517
- value: 'new',
554
+ name: `Use default branch (${defaultBranch}) without creating new branch`,
555
+ value: 'default',
518
556
  },
519
557
  ],
520
- default: 'default',
558
+ default: 'new-from-main',
521
559
  });
522
- if (branchChoice === 'default') {
523
- // Keep resolved repos as-is
524
- return resolved;
525
- }
526
- if (branchChoice === 'existing') {
527
- const branchName = await prompts.input({
528
- message: 'Enter branch name:',
529
- default: defaultBranch,
530
- validate: (value) => {
531
- if (!value.trim())
532
- return 'Branch name is required';
533
- return true;
534
- },
535
- });
536
- // Update all repos with the selected branch
560
+ if (branchChoice === 'new-from-main') {
561
+ // Create new branch from main with name = environment name
537
562
  return {
538
563
  ...resolved,
539
564
  repos: resolved.repos.map(repo => ({
540
565
  ...repo,
541
- branch: branchName.trim(),
542
- newBranch: undefined,
543
- sourceBranch: undefined,
566
+ branch: envName,
567
+ newBranch: envName,
568
+ sourceBranch: 'main',
544
569
  })),
545
570
  };
546
571
  }
547
- if (branchChoice === 'new') {
572
+ if (branchChoice === 'new-custom') {
548
573
  const newBranchName = await prompts.input({
549
574
  message: 'New branch name:',
575
+ default: envName,
550
576
  validate: (value) => {
551
577
  if (!value.trim())
552
578
  return 'Branch name is required';
@@ -557,7 +583,7 @@ async function promptForBranchOptions(resolved, config) {
557
583
  });
558
584
  const sourceBranch = await prompts.input({
559
585
  message: 'Create from branch:',
560
- default: defaultBranch,
586
+ default: 'main',
561
587
  validate: (value) => {
562
588
  if (!value.trim())
563
589
  return 'Source branch is required';
@@ -575,6 +601,31 @@ async function promptForBranchOptions(resolved, config) {
575
601
  })),
576
602
  };
577
603
  }
604
+ if (branchChoice === 'existing') {
605
+ const branchName = await prompts.input({
606
+ message: 'Enter branch name:',
607
+ default: defaultBranch,
608
+ validate: (value) => {
609
+ if (!value.trim())
610
+ return 'Branch name is required';
611
+ return true;
612
+ },
613
+ });
614
+ // Update all repos with the selected branch
615
+ return {
616
+ ...resolved,
617
+ repos: resolved.repos.map(repo => ({
618
+ ...repo,
619
+ branch: branchName.trim(),
620
+ newBranch: undefined,
621
+ sourceBranch: undefined,
622
+ })),
623
+ };
624
+ }
625
+ if (branchChoice === 'default') {
626
+ // Keep resolved repos as-is (no new branch)
627
+ return resolved;
628
+ }
578
629
  return resolved;
579
630
  }
580
631
  /**
@@ -794,6 +845,8 @@ function buildPayload(resolved, config, publicKey, privateKey, configLoader) {
794
845
  sourceBranch: repo.sourceBranch,
795
846
  };
796
847
  }
848
+ // Get local git config for commits
849
+ const gitConfig = getGitConfig();
797
850
  return {
798
851
  name: resolved.name,
799
852
  size: resolved.size,
@@ -805,6 +858,9 @@ function buildPayload(resolved, config, publicKey, privateKey, configLoader) {
805
858
  repos,
806
859
  privateKey,
807
860
  gitToken: envVars.GIT_TOKEN,
861
+ gitUserName: gitConfig.userName,
862
+ gitUserEmail: gitConfig.userEmail,
863
+ installClaudeCode: config.defaults?.install_claude_code,
808
864
  envVars: resolved.env,
809
865
  apps: resolved.apps.map(a => a.name),
810
866
  appConfigs: resolved.apps.map(a => ({
@@ -665,6 +665,16 @@ exports.initCommand = new commander_1.Command('init')
665
665
  v4Config.defaults = {};
666
666
  }
667
667
  v4Config.defaults.size = serverSize;
668
+ // Ask about Claude Code installation
669
+ if (!nonInteractive && !options.fromScan) {
670
+ const installClaudeCode = await prompts.confirm({
671
+ message: 'Install Claude Code CLI on genbox servers?',
672
+ default: true,
673
+ });
674
+ if (installClaudeCode) {
675
+ v4Config.defaults.install_claude_code = true;
676
+ }
677
+ }
668
678
  // Get default branch (use detected branch or allow override)
669
679
  const detectedBranch = scan.git?.branch || 'main';
670
680
  let defaultBranch = detectedBranch;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "genbox",
3
- "version": "1.0.43",
3
+ "version": "1.0.45",
4
4
  "description": "Genbox CLI - AI-Powered Development Environments",
5
5
  "main": "dist/index.js",
6
6
  "bin": {