@nimbuslab/cli 0.1.3 → 0.2.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/dist/index.js CHANGED
@@ -764,27 +764,16 @@ var Y2 = ({ indicator: t = "dots" } = {}) => {
764
764
  l2 = R2(m2 ?? l2);
765
765
  } };
766
766
  };
767
- var Ce = async (t, n) => {
768
- const r2 = {}, i = Object.keys(t);
769
- for (const s of i) {
770
- const c = t[s], a = await c({ results: r2 })?.catch((l2) => {
771
- throw l2;
772
- });
773
- if (typeof n?.onCancel == "function" && pD(a)) {
774
- r2[s] = "canceled", n.onCancel({ results: r2 });
775
- continue;
776
- }
777
- r2[s] = a;
778
- }
779
- return r2;
780
- };
781
767
 
782
768
  // src/commands/create.ts
783
769
  var import_picocolors3 = __toESM(require_picocolors(), 1);
784
770
  var {$: $2 } = globalThis.Bun;
785
771
  import { rm } from "fs/promises";
786
772
  import { join } from "path";
787
- var TEMPLATE_REPO = "nimbuslab-templates/fast-by-nimbuslab";
773
+ var TEMPLATES = {
774
+ fast: "nimbuslab-templates/fast-template",
775
+ "fast+": "nimbuslab-templates/fastplus-template"
776
+ };
788
777
  async function create(args) {
789
778
  const checkCmd = process.platform === "win32" ? "where" : "which";
790
779
  const hasBun = await $2`${checkCmd} bun`.quiet().then(() => true).catch(() => false);
@@ -834,11 +823,22 @@ async function create(args) {
834
823
  name: projectName,
835
824
  type: "fast",
836
825
  git: true,
837
- install: true
826
+ install: true,
827
+ github: false,
828
+ githubOrg: null,
829
+ githubDescription: "",
830
+ resendApiKey: "",
831
+ resendFromEmail: "",
832
+ contactEmail: "",
833
+ railwayToken: "",
834
+ stagingUrl: "",
835
+ productionUrl: ""
838
836
  };
839
837
  console.log(import_picocolors3.default.dim(` Projeto: ${projectName}`));
840
838
  console.log(import_picocolors3.default.dim(` Tipo: fast`));
841
839
  console.log(import_picocolors3.default.dim(` Git: sim`));
840
+ console.log(import_picocolors3.default.dim(` GitHub: nao`));
841
+ console.log(import_picocolors3.default.dim(` Infra: configurar depois com 'bun setup'`));
842
842
  console.log(import_picocolors3.default.dim(` Instalar: sim`));
843
843
  console.log();
844
844
  } else {
@@ -885,35 +885,152 @@ async function promptConfig(initialName) {
885
885
  });
886
886
  if (pD(type))
887
887
  return type;
888
- const extras = await Ce({
889
- git: () => ye({
890
- message: "Inicializar repositorio Git?",
891
- initialValue: true
892
- }),
893
- install: () => ye({
894
- message: "Instalar dependencias?",
895
- initialValue: true
896
- })
888
+ const git = await ye({
889
+ message: "Inicializar repositorio Git?",
890
+ initialValue: true
891
+ });
892
+ if (pD(git))
893
+ return git;
894
+ let github = false;
895
+ let githubOrg = null;
896
+ let githubDescription = "";
897
+ if (git) {
898
+ const createGithub = await ye({
899
+ message: "Criar repositorio no GitHub?",
900
+ initialValue: false
901
+ });
902
+ if (pD(createGithub))
903
+ return createGithub;
904
+ github = createGithub;
905
+ if (github) {
906
+ const org = await ve({
907
+ message: "Organizacao GitHub:",
908
+ options: [
909
+ { value: "nimbuslab", label: "nimbuslab", hint: "Org principal" },
910
+ { value: "fast-by-nimbuslab", label: "fast-by-nimbuslab", hint: "Projetos de clientes" },
911
+ { value: "nimbuslab-templates", label: "nimbuslab-templates", hint: "Templates" },
912
+ { value: null, label: "Pessoal", hint: "Sem organizacao" }
913
+ ]
914
+ });
915
+ if (pD(org))
916
+ return org;
917
+ githubOrg = org;
918
+ const description = await he({
919
+ message: "Descricao do repositorio:",
920
+ placeholder: "Landing page para cliente X",
921
+ initialValue: type === "fast" ? "Landing page fast by nimbuslab" : "SaaS fast+ by nimbuslab"
922
+ });
923
+ if (pD(description))
924
+ return description;
925
+ githubDescription = description;
926
+ }
927
+ }
928
+ const configureInfra = await ye({
929
+ message: "Configurar infra agora? (Resend, URLs)",
930
+ initialValue: true
931
+ });
932
+ if (pD(configureInfra))
933
+ return configureInfra;
934
+ let resendApiKey = "";
935
+ let resendFromEmail = "";
936
+ let contactEmail = "";
937
+ let railwayToken = "";
938
+ let stagingUrl = "";
939
+ let productionUrl = "";
940
+ if (configureInfra) {
941
+ const suggestedDomain = `${name}.com.br`;
942
+ console.log();
943
+ console.log(import_picocolors3.default.dim(" Resend (Email)"));
944
+ const resendKey = await he({
945
+ message: "RESEND_API_KEY:",
946
+ placeholder: "re_xxxxxxxxxxxx"
947
+ });
948
+ if (pD(resendKey))
949
+ return resendKey;
950
+ resendApiKey = resendKey;
951
+ const fromEmail = await he({
952
+ message: "Email de envio (from):",
953
+ placeholder: `contato@${suggestedDomain}`,
954
+ initialValue: `contato@${suggestedDomain}`
955
+ });
956
+ if (pD(fromEmail))
957
+ return fromEmail;
958
+ resendFromEmail = fromEmail;
959
+ const contact = await he({
960
+ message: "Email de contato (recebe formularios):",
961
+ placeholder: `contato@${suggestedDomain}`,
962
+ initialValue: `contato@${suggestedDomain}`
963
+ });
964
+ if (pD(contact))
965
+ return contact;
966
+ contactEmail = contact;
967
+ console.log();
968
+ console.log(import_picocolors3.default.dim(" URLs do projeto"));
969
+ const staging = await he({
970
+ message: "URL de staging:",
971
+ placeholder: `https://${name}-staging.up.railway.app`,
972
+ initialValue: `https://${name}-staging.up.railway.app`
973
+ });
974
+ if (pD(staging))
975
+ return staging;
976
+ stagingUrl = staging;
977
+ const production = await he({
978
+ message: "URL de producao:",
979
+ placeholder: `https://${suggestedDomain}`,
980
+ initialValue: `https://${suggestedDomain}`
981
+ });
982
+ if (pD(production))
983
+ return production;
984
+ productionUrl = production;
985
+ const configRailway = await ye({
986
+ message: "Configurar Railway token?",
987
+ initialValue: false
988
+ });
989
+ if (pD(configRailway))
990
+ return configRailway;
991
+ if (configRailway) {
992
+ const railwayTk = await he({
993
+ message: "RAILWAY_TOKEN:",
994
+ placeholder: "railway_xxxxxxxxxxxx"
995
+ });
996
+ if (pD(railwayTk))
997
+ return railwayTk;
998
+ railwayToken = railwayTk;
999
+ }
1000
+ }
1001
+ const install = await ye({
1002
+ message: "Instalar dependencias?",
1003
+ initialValue: true
897
1004
  });
898
- if (pD(extras))
899
- return extras;
1005
+ if (pD(install))
1006
+ return install;
900
1007
  return {
901
1008
  name,
902
1009
  type,
903
- git: extras.git,
904
- install: extras.install
1010
+ git,
1011
+ install,
1012
+ github,
1013
+ githubOrg,
1014
+ githubDescription,
1015
+ resendApiKey,
1016
+ resendFromEmail,
1017
+ contactEmail,
1018
+ railwayToken,
1019
+ stagingUrl,
1020
+ productionUrl
905
1021
  };
906
1022
  }
907
1023
  async function createProject(config) {
908
1024
  const s = Y2();
909
- s.start("Clonando template...");
1025
+ const templateRepo = TEMPLATES[config.type];
1026
+ s.start(`Clonando template ${config.type}...`);
910
1027
  try {
911
- await $2`gh repo clone ${TEMPLATE_REPO} ${config.name} -- --depth 1`.quiet();
1028
+ await $2`gh repo clone ${templateRepo} ${config.name} -- --depth 1`.quiet();
912
1029
  await rm(join(config.name, ".git"), { recursive: true, force: true });
913
- s.stop("Template clonado");
1030
+ s.stop(`Template ${config.type} clonado`);
914
1031
  } catch (error) {
915
1032
  s.stop("Erro ao clonar template");
916
- throw new Error("Falha ao clonar template. Verifique se tem acesso ao repositorio.");
1033
+ throw new Error(`Falha ao clonar template ${templateRepo}. Verifique se tem acesso ao repositorio.`);
917
1034
  }
918
1035
  s.start("Configurando projeto...");
919
1036
  try {
@@ -933,13 +1050,40 @@ async function createProject(config) {
933
1050
  s.start("Inicializando Git...");
934
1051
  try {
935
1052
  const cwd = config.name;
936
- await $2`git init`.cwd(cwd).quiet();
1053
+ await $2`git init -b main`.cwd(cwd).quiet();
937
1054
  await $2`git add -A`.cwd(cwd).quiet();
938
1055
  await $2`git commit -m "chore: setup inicial via nimbus create"`.cwd(cwd).quiet();
939
- s.stop("Git inicializado");
1056
+ await $2`git checkout -b staging`.cwd(cwd).quiet();
1057
+ await $2`git checkout -b develop`.cwd(cwd).quiet();
1058
+ s.stop("Git inicializado (main -> staging -> develop)");
940
1059
  } catch (error) {
941
1060
  s.stop("Erro ao inicializar Git");
942
1061
  }
1062
+ if (config.github) {
1063
+ s.start("Criando repositorio no GitHub...");
1064
+ try {
1065
+ const cwd = config.name;
1066
+ const repoName = config.githubOrg ? `${config.githubOrg}/${config.name}` : config.name;
1067
+ const visibility = config.githubOrg === "fast-by-nimbuslab" ? "--private" : "--public";
1068
+ await $2`gh repo create ${repoName} ${visibility} --description ${config.githubDescription} --source . --remote origin --push`.cwd(cwd).quiet();
1069
+ await $2`git push -u origin staging`.cwd(cwd).quiet();
1070
+ await $2`git push -u origin develop`.cwd(cwd).quiet();
1071
+ s.stop(`GitHub: ${repoName} criado`);
1072
+ } catch (error) {
1073
+ s.stop("Erro ao criar repositorio GitHub");
1074
+ console.log(import_picocolors3.default.dim(" Voce pode criar manualmente com: gh repo create"));
1075
+ }
1076
+ }
1077
+ }
1078
+ if (config.resendApiKey || config.stagingUrl) {
1079
+ s.start("Gerando arquivo .env...");
1080
+ try {
1081
+ const envContent = generateEnvFile(config);
1082
+ await Bun.write(`${config.name}/.env`, envContent);
1083
+ s.stop("Arquivo .env criado");
1084
+ } catch (error) {
1085
+ s.stop("Erro ao criar .env");
1086
+ }
943
1087
  }
944
1088
  if (config.install) {
945
1089
  s.start("Instalando dependencias (pode demorar)...");
@@ -951,6 +1095,42 @@ async function createProject(config) {
951
1095
  }
952
1096
  }
953
1097
  }
1098
+ function generateEnvFile(config) {
1099
+ const lines = [
1100
+ "# Gerado automaticamente pelo nimbus-cli",
1101
+ "# Nao commitar este arquivo!",
1102
+ "",
1103
+ "# App",
1104
+ `NODE_ENV=development`,
1105
+ "",
1106
+ "# URLs",
1107
+ `NEXT_PUBLIC_APP_URL=${config.productionUrl || "http://localhost:3000"}`,
1108
+ `STAGING_URL=${config.stagingUrl || ""}`,
1109
+ `PRODUCTION_URL=${config.productionUrl || ""}`,
1110
+ "",
1111
+ "# Resend (Email)",
1112
+ `RESEND_API_KEY=${config.resendApiKey || ""}`,
1113
+ `RESEND_FROM_EMAIL=${config.resendFromEmail || ""}`,
1114
+ `CONTACT_EMAIL=${config.contactEmail || ""}`
1115
+ ];
1116
+ if (config.railwayToken) {
1117
+ lines.push("");
1118
+ lines.push("# Railway");
1119
+ lines.push(`RAILWAY_TOKEN=${config.railwayToken}`);
1120
+ }
1121
+ if (config.type === "fast+") {
1122
+ lines.push("");
1123
+ lines.push("# Database (fast+)");
1124
+ lines.push("DATABASE_URL=postgresql://postgres:postgres@localhost:5432/app");
1125
+ lines.push("");
1126
+ lines.push("# Auth (fast+)");
1127
+ lines.push("BETTER_AUTH_SECRET=");
1128
+ lines.push("BETTER_AUTH_URL=http://localhost:3000");
1129
+ }
1130
+ return lines.join(`
1131
+ `) + `
1132
+ `;
1133
+ }
954
1134
  function showNextSteps(config) {
955
1135
  console.log();
956
1136
  console.log(import_picocolors3.default.bold("Proximos passos:"));
@@ -962,11 +1142,30 @@ function showNextSteps(config) {
962
1142
  console.log(` ${import_picocolors3.default.cyan("bun")} setup`);
963
1143
  console.log(` ${import_picocolors3.default.cyan("bun")} dev`);
964
1144
  console.log();
1145
+ if (config.git) {
1146
+ console.log(import_picocolors3.default.dim(" Git flow: main -> staging -> develop (branch atual)"));
1147
+ console.log();
1148
+ if (config.github) {
1149
+ const repoUrl = config.githubOrg ? `https://github.com/${config.githubOrg}/${config.name}` : `https://github.com/${config.name}`;
1150
+ console.log(import_picocolors3.default.green(` GitHub: ${repoUrl}`));
1151
+ console.log();
1152
+ } else {
1153
+ console.log(import_picocolors3.default.yellow(" Dica: Para criar repo GitHub, use 'gh repo create' ou 'bun setup'."));
1154
+ console.log();
1155
+ }
1156
+ }
965
1157
  if (config.type === "fast+") {
966
- console.log(import_picocolors3.default.dim(" Dica: Execute 'bun setup' e escolha fast+ para configurar backend"));
1158
+ console.log(import_picocolors3.default.dim(" Dica: Para fast+, configure DATABASE_URL e BETTER_AUTH_SECRET no .env"));
1159
+ console.log();
1160
+ }
1161
+ if (config.resendApiKey || config.stagingUrl) {
1162
+ console.log(import_picocolors3.default.green(" .env configurado com sucesso!"));
1163
+ console.log();
1164
+ } else {
1165
+ console.log(import_picocolors3.default.yellow(" Dica: Configure .env manualmente ou use 'bun setup'."));
967
1166
  console.log();
968
1167
  }
969
- console.log(import_picocolors3.default.dim(" Documentacao: https://github.com/nimbuslab/fast-by-nimbuslab"));
1168
+ console.log(import_picocolors3.default.dim(" Documentacao: https://github.com/nimbuslab-templates"));
970
1169
  console.log();
971
1170
  }
972
1171
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nimbuslab/cli",
3
- "version": "0.1.3",
3
+ "version": "0.2.0",
4
4
  "description": "CLI para criar projetos nimbuslab",
5
5
  "type": "module",
6
6
  "bin": {
@@ -4,13 +4,27 @@ import { $} from "bun"
4
4
  import { rm } from "node:fs/promises"
5
5
  import { join } from "node:path"
6
6
 
7
- const TEMPLATE_REPO = "nimbuslab-templates/fast-by-nimbuslab"
7
+ // Templates separados por tipo
8
+ const TEMPLATES = {
9
+ "fast": "nimbuslab-templates/fast-template",
10
+ "fast+": "nimbuslab-templates/fastplus-template",
11
+ }
8
12
 
9
13
  interface ProjectConfig {
10
14
  name: string
11
15
  type: "fast" | "fast+"
12
16
  git: boolean
13
17
  install: boolean
18
+ github: boolean
19
+ githubOrg: string | null
20
+ githubDescription: string
21
+ // M21-M23: Configs de infra
22
+ resendApiKey: string
23
+ resendFromEmail: string
24
+ contactEmail: string
25
+ railwayToken: string
26
+ stagingUrl: string
27
+ productionUrl: string
14
28
  }
15
29
 
16
30
  export async function create(args: string[]) {
@@ -68,16 +82,27 @@ export async function create(args: string[]) {
68
82
  let config: ProjectConfig | symbol
69
83
 
70
84
  if (hasYes && projectName) {
71
- // Modo automatico com defaults
85
+ // Modo automatico com defaults (sem configs de infra)
72
86
  config = {
73
87
  name: projectName,
74
88
  type: "fast",
75
89
  git: true,
76
90
  install: true,
91
+ github: false,
92
+ githubOrg: null,
93
+ githubDescription: "",
94
+ resendApiKey: "",
95
+ resendFromEmail: "",
96
+ contactEmail: "",
97
+ railwayToken: "",
98
+ stagingUrl: "",
99
+ productionUrl: "",
77
100
  }
78
101
  console.log(pc.dim(` Projeto: ${projectName}`))
79
102
  console.log(pc.dim(` Tipo: fast`))
80
103
  console.log(pc.dim(` Git: sim`))
104
+ console.log(pc.dim(` GitHub: nao`))
105
+ console.log(pc.dim(` Infra: configurar depois com 'bun setup'`))
81
106
  console.log(pc.dim(` Instalar: sim`))
82
107
  console.log()
83
108
  } else {
@@ -130,41 +155,175 @@ async function promptConfig(initialName?: string): Promise<ProjectConfig | symbo
130
155
 
131
156
  if (p.isCancel(type)) return type
132
157
 
133
- const extras = await p.group({
134
- git: () =>
135
- p.confirm({
136
- message: "Inicializar repositorio Git?",
137
- initialValue: true,
138
- }),
139
- install: () =>
140
- p.confirm({
141
- message: "Instalar dependencias?",
142
- initialValue: true,
143
- }),
158
+ const git = await p.confirm({
159
+ message: "Inicializar repositorio Git?",
160
+ initialValue: true,
161
+ })
162
+
163
+ if (p.isCancel(git)) return git
164
+
165
+ // GitHub options (only if git is enabled)
166
+ let github = false
167
+ let githubOrg: string | null = null
168
+ let githubDescription = ""
169
+
170
+ if (git) {
171
+ const createGithub = await p.confirm({
172
+ message: "Criar repositorio no GitHub?",
173
+ initialValue: false,
174
+ })
175
+
176
+ if (p.isCancel(createGithub)) return createGithub
177
+
178
+ github = createGithub as boolean
179
+
180
+ if (github) {
181
+ // M13: Selecao de organizacao GitHub
182
+ const org = await p.select({
183
+ message: "Organizacao GitHub:",
184
+ options: [
185
+ { value: "nimbuslab", label: "nimbuslab", hint: "Org principal" },
186
+ { value: "fast-by-nimbuslab", label: "fast-by-nimbuslab", hint: "Projetos de clientes" },
187
+ { value: "nimbuslab-templates", label: "nimbuslab-templates", hint: "Templates" },
188
+ { value: null, label: "Pessoal", hint: "Sem organizacao" },
189
+ ],
190
+ })
191
+
192
+ if (p.isCancel(org)) return org
193
+ githubOrg = org as string | null
194
+
195
+ // M14: Descricao do repo
196
+ const description = await p.text({
197
+ message: "Descricao do repositorio:",
198
+ placeholder: "Landing page para cliente X",
199
+ initialValue: type === "fast" ? "Landing page fast by nimbuslab" : "SaaS fast+ by nimbuslab",
200
+ })
201
+
202
+ if (p.isCancel(description)) return description
203
+ githubDescription = description as string
204
+ }
205
+ }
206
+
207
+ // M21-M23: Configuracoes de infra (opcional)
208
+ const configureInfra = await p.confirm({
209
+ message: "Configurar infra agora? (Resend, URLs)",
210
+ initialValue: true,
144
211
  })
145
212
 
146
- if (p.isCancel(extras)) return extras
213
+ if (p.isCancel(configureInfra)) return configureInfra
214
+
215
+ let resendApiKey = ""
216
+ let resendFromEmail = ""
217
+ let contactEmail = ""
218
+ let railwayToken = ""
219
+ let stagingUrl = ""
220
+ let productionUrl = ""
221
+
222
+ if (configureInfra) {
223
+ // Extrair dominio do nome para sugestoes
224
+ const suggestedDomain = `${name}.com.br`
225
+
226
+ // M22: Resend config
227
+ console.log()
228
+ console.log(pc.dim(" Resend (Email)"))
229
+
230
+ const resendKey = await p.text({
231
+ message: "RESEND_API_KEY:",
232
+ placeholder: "re_xxxxxxxxxxxx",
233
+ })
234
+ if (p.isCancel(resendKey)) return resendKey
235
+ resendApiKey = resendKey as string
236
+
237
+ const fromEmail = await p.text({
238
+ message: "Email de envio (from):",
239
+ placeholder: `contato@${suggestedDomain}`,
240
+ initialValue: `contato@${suggestedDomain}`,
241
+ })
242
+ if (p.isCancel(fromEmail)) return fromEmail
243
+ resendFromEmail = fromEmail as string
244
+
245
+ const contact = await p.text({
246
+ message: "Email de contato (recebe formularios):",
247
+ placeholder: `contato@${suggestedDomain}`,
248
+ initialValue: `contato@${suggestedDomain}`,
249
+ })
250
+ if (p.isCancel(contact)) return contact
251
+ contactEmail = contact as string
252
+
253
+ // URLs
254
+ console.log()
255
+ console.log(pc.dim(" URLs do projeto"))
256
+
257
+ const staging = await p.text({
258
+ message: "URL de staging:",
259
+ placeholder: `https://${name}-staging.up.railway.app`,
260
+ initialValue: `https://${name}-staging.up.railway.app`,
261
+ })
262
+ if (p.isCancel(staging)) return staging
263
+ stagingUrl = staging as string
264
+
265
+ const production = await p.text({
266
+ message: "URL de producao:",
267
+ placeholder: `https://${suggestedDomain}`,
268
+ initialValue: `https://${suggestedDomain}`,
269
+ })
270
+ if (p.isCancel(production)) return production
271
+ productionUrl = production as string
272
+
273
+ // Railway token (opcional)
274
+ const configRailway = await p.confirm({
275
+ message: "Configurar Railway token?",
276
+ initialValue: false,
277
+ })
278
+ if (p.isCancel(configRailway)) return configRailway
279
+
280
+ if (configRailway) {
281
+ const railwayTk = await p.text({
282
+ message: "RAILWAY_TOKEN:",
283
+ placeholder: "railway_xxxxxxxxxxxx",
284
+ })
285
+ if (p.isCancel(railwayTk)) return railwayTk
286
+ railwayToken = railwayTk as string
287
+ }
288
+ }
289
+
290
+ const install = await p.confirm({
291
+ message: "Instalar dependencias?",
292
+ initialValue: true,
293
+ })
294
+
295
+ if (p.isCancel(install)) return install
147
296
 
148
297
  return {
149
298
  name: name as string,
150
299
  type: type as "fast" | "fast+",
151
- git: extras.git as boolean,
152
- install: extras.install as boolean,
300
+ git: git as boolean,
301
+ install: install as boolean,
302
+ github,
303
+ githubOrg,
304
+ githubDescription,
305
+ resendApiKey,
306
+ resendFromEmail,
307
+ contactEmail,
308
+ railwayToken,
309
+ stagingUrl,
310
+ productionUrl,
153
311
  }
154
312
  }
155
313
 
156
314
  async function createProject(config: ProjectConfig) {
157
315
  const s = p.spinner()
158
316
 
159
- // Clone template usando gh (funciona com token OAuth)
160
- s.start("Clonando template...")
317
+ // Clone template baseado no tipo (fast ou fast+)
318
+ const templateRepo = TEMPLATES[config.type]
319
+ s.start(`Clonando template ${config.type}...`)
161
320
  try {
162
- await $`gh repo clone ${TEMPLATE_REPO} ${config.name} -- --depth 1`.quiet()
321
+ await $`gh repo clone ${templateRepo} ${config.name} -- --depth 1`.quiet()
163
322
  await rm(join(config.name, ".git"), { recursive: true, force: true })
164
- s.stop("Template clonado")
323
+ s.stop(`Template ${config.type} clonado`)
165
324
  } catch (error) {
166
325
  s.stop("Erro ao clonar template")
167
- throw new Error("Falha ao clonar template. Verifique se tem acesso ao repositorio.")
326
+ throw new Error(`Falha ao clonar template ${templateRepo}. Verifique se tem acesso ao repositorio.`)
168
327
  }
169
328
 
170
329
  // Update package.json
@@ -187,18 +346,60 @@ async function createProject(config: ProjectConfig) {
187
346
  s.stop("Configuracao fast+ preparada")
188
347
  }
189
348
 
190
- // Git init
349
+ // Git init with full branch flow: main → staging → develop
191
350
  if (config.git) {
192
351
  s.start("Inicializando Git...")
193
352
  try {
194
353
  const cwd = config.name
195
- await $`git init`.cwd(cwd).quiet()
354
+ // Init with main branch (not master)
355
+ await $`git init -b main`.cwd(cwd).quiet()
196
356
  await $`git add -A`.cwd(cwd).quiet()
197
357
  await $`git commit -m "chore: setup inicial via nimbus create"`.cwd(cwd).quiet()
198
- s.stop("Git inicializado")
358
+
359
+ // Create branch flow: main → staging → develop
360
+ await $`git checkout -b staging`.cwd(cwd).quiet()
361
+ await $`git checkout -b develop`.cwd(cwd).quiet()
362
+
363
+ s.stop("Git inicializado (main -> staging -> develop)")
199
364
  } catch (error) {
200
365
  s.stop("Erro ao inicializar Git")
201
366
  }
367
+
368
+ // Create GitHub repo if requested (M13 + M14)
369
+ if (config.github) {
370
+ s.start("Criando repositorio no GitHub...")
371
+ try {
372
+ const cwd = config.name
373
+ const repoName = config.githubOrg
374
+ ? `${config.githubOrg}/${config.name}`
375
+ : config.name
376
+
377
+ // Create repo with description (private by default for client projects)
378
+ const visibility = config.githubOrg === "fast-by-nimbuslab" ? "--private" : "--public"
379
+ await $`gh repo create ${repoName} ${visibility} --description ${config.githubDescription} --source . --remote origin --push`.cwd(cwd).quiet()
380
+
381
+ // Push all branches
382
+ await $`git push -u origin staging`.cwd(cwd).quiet()
383
+ await $`git push -u origin develop`.cwd(cwd).quiet()
384
+
385
+ s.stop(`GitHub: ${repoName} criado`)
386
+ } catch (error) {
387
+ s.stop("Erro ao criar repositorio GitHub")
388
+ console.log(pc.dim(" Voce pode criar manualmente com: gh repo create"))
389
+ }
390
+ }
391
+ }
392
+
393
+ // M23: Gerar .env se configs foram fornecidas
394
+ if (config.resendApiKey || config.stagingUrl) {
395
+ s.start("Gerando arquivo .env...")
396
+ try {
397
+ const envContent = generateEnvFile(config)
398
+ await Bun.write(`${config.name}/.env`, envContent)
399
+ s.stop("Arquivo .env criado")
400
+ } catch (error) {
401
+ s.stop("Erro ao criar .env")
402
+ }
202
403
  }
203
404
 
204
405
  // Install deps
@@ -213,6 +414,47 @@ async function createProject(config: ProjectConfig) {
213
414
  }
214
415
  }
215
416
 
417
+ // M23: Gerar conteudo do .env
418
+ function generateEnvFile(config: ProjectConfig): string {
419
+ const lines = [
420
+ "# Gerado automaticamente pelo nimbus-cli",
421
+ "# Nao commitar este arquivo!",
422
+ "",
423
+ "# App",
424
+ `NODE_ENV=development`,
425
+ "",
426
+ "# URLs",
427
+ `NEXT_PUBLIC_APP_URL=${config.productionUrl || "http://localhost:3000"}`,
428
+ `STAGING_URL=${config.stagingUrl || ""}`,
429
+ `PRODUCTION_URL=${config.productionUrl || ""}`,
430
+ "",
431
+ "# Resend (Email)",
432
+ `RESEND_API_KEY=${config.resendApiKey || ""}`,
433
+ `RESEND_FROM_EMAIL=${config.resendFromEmail || ""}`,
434
+ `CONTACT_EMAIL=${config.contactEmail || ""}`,
435
+ ]
436
+
437
+ // Railway token (se fornecido)
438
+ if (config.railwayToken) {
439
+ lines.push("")
440
+ lines.push("# Railway")
441
+ lines.push(`RAILWAY_TOKEN=${config.railwayToken}`)
442
+ }
443
+
444
+ // Fast+ specific vars
445
+ if (config.type === "fast+") {
446
+ lines.push("")
447
+ lines.push("# Database (fast+)")
448
+ lines.push("DATABASE_URL=postgresql://postgres:postgres@localhost:5432/app")
449
+ lines.push("")
450
+ lines.push("# Auth (fast+)")
451
+ lines.push("BETTER_AUTH_SECRET=")
452
+ lines.push("BETTER_AUTH_URL=http://localhost:3000")
453
+ }
454
+
455
+ return lines.join("\n") + "\n"
456
+ }
457
+
216
458
  function showNextSteps(config: ProjectConfig) {
217
459
  console.log()
218
460
  console.log(pc.bold("Proximos passos:"))
@@ -227,11 +469,38 @@ function showNextSteps(config: ProjectConfig) {
227
469
  console.log(` ${pc.cyan("bun")} dev`)
228
470
  console.log()
229
471
 
472
+ // Git flow info
473
+ if (config.git) {
474
+ console.log(pc.dim(" Git flow: main -> staging -> develop (branch atual)"))
475
+ console.log()
476
+
477
+ // GitHub info
478
+ if (config.github) {
479
+ const repoUrl = config.githubOrg
480
+ ? `https://github.com/${config.githubOrg}/${config.name}`
481
+ : `https://github.com/${config.name}`
482
+ console.log(pc.green(` GitHub: ${repoUrl}`))
483
+ console.log()
484
+ } else {
485
+ console.log(pc.yellow(" Dica: Para criar repo GitHub, use 'gh repo create' ou 'bun setup'."))
486
+ console.log()
487
+ }
488
+ }
489
+
230
490
  if (config.type === "fast+") {
231
- console.log(pc.dim(" Dica: Execute 'bun setup' e escolha fast+ para configurar backend"))
491
+ console.log(pc.dim(" Dica: Para fast+, configure DATABASE_URL e BETTER_AUTH_SECRET no .env"))
492
+ console.log()
493
+ }
494
+
495
+ // Info sobre .env
496
+ if (config.resendApiKey || config.stagingUrl) {
497
+ console.log(pc.green(" .env configurado com sucesso!"))
498
+ console.log()
499
+ } else {
500
+ console.log(pc.yellow(" Dica: Configure .env manualmente ou use 'bun setup'."))
232
501
  console.log()
233
502
  }
234
503
 
235
- console.log(pc.dim(" Documentacao: https://github.com/nimbuslab/fast-by-nimbuslab"))
504
+ console.log(pc.dim(" Documentacao: https://github.com/nimbuslab-templates"))
236
505
  console.log()
237
506
  }
package/bun.lock DELETED
@@ -1,38 +0,0 @@
1
- {
2
- "lockfileVersion": 1,
3
- "configVersion": 1,
4
- "workspaces": {
5
- "": {
6
- "name": "nimbus-cli",
7
- "dependencies": {
8
- "@clack/prompts": "^0.11.0",
9
- "picocolors": "^1.1.1",
10
- },
11
- "devDependencies": {
12
- "@types/bun": "latest",
13
- },
14
- "peerDependencies": {
15
- "typescript": "^5",
16
- },
17
- },
18
- },
19
- "packages": {
20
- "@clack/core": ["@clack/core@0.5.0", "", { "dependencies": { "picocolors": "^1.0.0", "sisteransi": "^1.0.5" } }, "sha512-p3y0FIOwaYRUPRcMO7+dlmLh8PSRcrjuTndsiA0WAFbWES0mLZlrjVoBRZ9DzkPFJZG6KGkJmoEAY0ZcVWTkow=="],
21
-
22
- "@clack/prompts": ["@clack/prompts@0.11.0", "", { "dependencies": { "@clack/core": "0.5.0", "picocolors": "^1.0.0", "sisteransi": "^1.0.5" } }, "sha512-pMN5FcrEw9hUkZA4f+zLlzivQSeQf5dRGJjSUbvVYDLvpKCdQx5OaknvKzgbtXOizhP+SJJJjqEbOe55uKKfAw=="],
23
-
24
- "@types/bun": ["@types/bun@1.3.6", "", { "dependencies": { "bun-types": "1.3.6" } }, "sha512-uWCv6FO/8LcpREhenN1d1b6fcspAB+cefwD7uti8C8VffIv0Um08TKMn98FynpTiU38+y2dUO55T11NgDt8VAA=="],
25
-
26
- "@types/node": ["@types/node@25.0.10", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg=="],
27
-
28
- "bun-types": ["bun-types@1.3.6", "", { "dependencies": { "@types/node": "*" } }, "sha512-OlFwHcnNV99r//9v5IIOgQ9Uk37gZqrNMCcqEaExdkVq3Avwqok1bJFmvGMCkCE0FqzdY8VMOZpfpR3lwI+CsQ=="],
29
-
30
- "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
31
-
32
- "sisteransi": ["sisteransi@1.0.5", "", {}, "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg=="],
33
-
34
- "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
35
-
36
- "undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="],
37
- }
38
- }