maestro-bundle 1.8.0 → 2.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/src/cli.mjs CHANGED
@@ -14,7 +14,7 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
14
14
  // ============================================================
15
15
  const BUNDLES = {
16
16
  "ai-agents": {
17
- name: "Sistema Multi-Agente com AI",
17
+ name: "Multi-Agent AI System",
18
18
  desc: "Python + LangChain + LangGraph + FastAPI + pgvector",
19
19
  },
20
20
  "jhipster-monorepo": {
@@ -26,7 +26,7 @@ const BUNDLES = {
26
26
  desc: "Java 21 + Spring Boot + Angular + Kafka + Consul + K8s",
27
27
  },
28
28
  "data-pipeline": {
29
- name: "Pipeline de Dados e ML",
29
+ name: "Data & ML Pipeline",
30
30
  desc: "Python + Pandas + Scikit-learn + MLflow + Airflow",
31
31
  },
32
32
  "frontend-spa": {
@@ -34,8 +34,8 @@ const BUNDLES = {
34
34
  desc: "React + TypeScript + Tailwind + Vite",
35
35
  },
36
36
  "ai-agents-deep": {
37
- name: "Deep Agent (tipo Claude Code)",
38
- desc: "Python + Deep Agents SDK + LangGraph + Subagentes + Skills",
37
+ name: "Deep Agent (Claude Code-like)",
38
+ desc: "Python + Deep Agents SDK + LangGraph + Subagents + Skills",
39
39
  },
40
40
  };
41
41
 
@@ -44,8 +44,8 @@ const BUNDLES = {
44
44
  //
45
45
  // Claude Code:
46
46
  // Instruções: CLAUDE.md (raiz) com @AGENTS.md
47
- // Skills: .claude/skills/<nome>/SKILL.md
48
- // Rules: .claude/rules/<nome>.md
47
+ // Skills: .claude/skills/name/SKILL.md
48
+ // Rules: .claude/rules/name.md
49
49
  //
50
50
  // Cursor:
51
51
  // Instruções: .cursor/rules/ (rules .mdc ou .md)
@@ -57,7 +57,7 @@ const BUNDLES = {
57
57
  //
58
58
  // Copilot:
59
59
  // Instruções: .github/copilot-instructions.md
60
- // Rules: .github/instructions/<nome>.instructions.md
60
+ // Rules: .github/instructions/name.instructions.md
61
61
  //
62
62
  // Windsurf:
63
63
  // Instruções: .windsurfrules (raiz)
@@ -109,27 +109,27 @@ const EDITORS = {
109
109
  // ============================================================
110
110
  function showHelp() {
111
111
  console.log("");
112
- console.log(chalk.bold(" maestro-bundle") + " — Instala bundles de governança para projetos com AI");
112
+ console.log(chalk.bold(" maestro-bundle") + " — Install governance bundles for AI-powered projects");
113
113
  console.log("");
114
- console.log(chalk.dim(" Uso:"));
115
- console.log(` npx maestro-bundle ${chalk.green("<bundle>")} ${chalk.yellow("<editor>")} ${chalk.dim("[diretório]")}`);
114
+ console.log(chalk.dim(" Usage:"));
115
+ console.log(` npx maestro-bundle ${chalk.green("<bundle>")} ${chalk.yellow("<editor>")} ${chalk.dim("[directory]")}`);
116
116
  console.log("");
117
117
  console.log(chalk.dim(" Bundles:"));
118
118
  for (const [key, info] of Object.entries(BUNDLES)) {
119
119
  console.log(` ${chalk.green(key.padEnd(26))} ${info.desc}`);
120
120
  }
121
121
  console.log("");
122
- console.log(chalk.dim(" Editores:"));
123
- console.log(` ${chalk.yellow("claude".padEnd(12))} CLAUDE.md + .claude/skills/<nome>/SKILL.md`);
124
- console.log(` ${chalk.yellow("cursor".padEnd(12))} AGENTS.md + .cursor/skills/<nome>/SKILL.md`);
125
- console.log(` ${chalk.yellow("codex".padEnd(12))} AGENTS.md (tudo em um arquivo)`);
122
+ console.log(chalk.dim(" Editors:"));
123
+ console.log(` ${chalk.yellow("claude".padEnd(12))} CLAUDE.md + .claude/skills/name/SKILL.md`);
124
+ console.log(` ${chalk.yellow("cursor".padEnd(12))} AGENTS.md + .cursor/skills/name/SKILL.md`);
125
+ console.log(` ${chalk.yellow("codex".padEnd(12))} AGENTS.md (all in one file)`);
126
126
  console.log(` ${chalk.yellow("copilot".padEnd(12))} .github/copilot-instructions.md + .github/instructions/`);
127
- console.log(` ${chalk.yellow("windsurf".padEnd(12))} .windsurfrules (tudo em um arquivo)`);
128
- console.log(` ${chalk.yellow("all".padEnd(12))} Instala para todos os editores no mesmo repo`);
127
+ console.log(` ${chalk.yellow("windsurf".padEnd(12))} .windsurfrules (all in one file)`);
128
+ console.log(` ${chalk.yellow("all".padEnd(12))} Install for all editors in the same repo`);
129
129
  console.log("");
130
- console.log(chalk.dim(" Exemplos:"));
130
+ console.log(chalk.dim(" Examples:"));
131
131
  console.log(` npx maestro-bundle ai-agents claude`);
132
- console.log(` npx maestro-bundle jhipster-monorepo cursor ./meu-projeto`);
132
+ console.log(` npx maestro-bundle jhipster-monorepo cursor ./my-project`);
133
133
  console.log(` npx maestro-bundle frontend-spa all`);
134
134
  console.log("");
135
135
  }
@@ -190,7 +190,7 @@ function installForEditor(editorKey, agentsMd, skills, targetDir) {
190
190
  }
191
191
  }
192
192
 
193
- // --- Skills (Claude Code: .claude/skills/<nome>/SKILL.md) ---
193
+ // --- Skills (Claude Code: .claude/skills/name/SKILL.md) ---
194
194
  if (editor.skillsDir) {
195
195
  const skillsPath = join(targetDir, editor.skillsDir);
196
196
  ensureDir(skillsPath);
@@ -256,12 +256,12 @@ async function main() {
256
256
  const targetDir = resolve(args[2] || ".");
257
257
 
258
258
  if (!BUNDLES[bundleName]) {
259
- console.error(chalk.red(`\n Bundle "${bundleName}" não encontrado.\n`));
259
+ console.error(chalk.red(`\n Bundle "${bundleName}" not found.\n`));
260
260
  showHelp();
261
261
  process.exit(1);
262
262
  }
263
263
  if (!EDITORS[editorArg]) {
264
- console.error(chalk.red(`\n Editor "${editorArg}" não encontrado.\n`));
264
+ console.error(chalk.red(`\n Editor "${editorArg}" not found.\n`));
265
265
  showHelp();
266
266
  process.exit(1);
267
267
  }
@@ -272,7 +272,7 @@ async function main() {
272
272
  const bundleDir = join(templatesDir, `bundle-${bundleName}`);
273
273
 
274
274
  if (!existsSync(bundleDir)) {
275
- console.error(chalk.red(`\n Templates de "${bundleName}" não encontrados.\n`));
275
+ console.error(chalk.red(`\n Templates for "${bundleName}" not founds.\n`));
276
276
  process.exit(1);
277
277
  }
278
278
 
@@ -295,12 +295,12 @@ async function main() {
295
295
  console.log("");
296
296
  console.log(chalk.bold(` Bundle: ${chalk.green(bundleInfo.name)}`));
297
297
  console.log(chalk.bold(` Editor: ${chalk.yellow(editorNames)}`));
298
- console.log(chalk.dim(` Destino: ${targetDir}`));
298
+ console.log(chalk.dim(` Target: ${targetDir}`));
299
299
  console.log("");
300
300
 
301
- // 1. Instalar para cada editor
301
+ // 1. Install for each editor
302
302
  for (const editorKey of editorsToInstall) {
303
- const spinner = ora(`Instalando para ${EDITORS[editorKey].name}`).start();
303
+ const spinner = ora(`Installing for ${EDITORS[editorKey].name}`).start();
304
304
  const results = installForEditor(editorKey, agentsMd, skills, targetDir);
305
305
  spinner.succeed(`${EDITORS[editorKey].name}: ${results.join(", ")}`);
306
306
  }
@@ -311,57 +311,57 @@ async function main() {
311
311
  const prdDest = join(targetDir, "PRD.md");
312
312
  if (!existsSync(prdDest)) {
313
313
  cpSync(prdTemplate, prdDest);
314
- const spinnerPrd = ora("PRD.md template instalado").start();
315
- spinnerPrd.succeed("PRD.md template instalado (preencha com os requisitos do produto)");
314
+ const spinnerPrd = ora("PRD.md template installed").start();
315
+ spinnerPrd.succeed("PRD.md template installed (fill in your product requirements)");
316
316
  }
317
317
  }
318
318
 
319
- // 3. Skills canônicas (sempre, para Deep Agents e referência)
320
- const spinner2 = ora("Instalando skills canônicas").start();
319
+ // 3. Skills canonical (always, for Deep Agents and reference)
320
+ const spinner2 = ora("Installing canonical skills").start();
321
321
  const skillsDest = join(targetDir, "skills");
322
322
  ensureDir(skillsDest);
323
323
  for (const skill of skills) {
324
324
  copyDir(skill.dir, join(skillsDest, skill.name));
325
325
  }
326
- spinner2.succeed(`${skills.length} skills canônicas em skills/`);
326
+ spinner2.succeed(`${skills.length} canonical skills in skills/`);
327
327
 
328
- // 3. LangChain Skills (para bundles de AI)
328
+ // 3. LangChain Skills (for AI bundles)
329
329
  if (bundleName === "ai-agents" || bundleName === "ai-agents-deep") {
330
- const spinnerLc = ora("Instalando LangChain Skills (langchain-ai/langchain-skills)").start();
330
+ const spinnerLc = ora("Installing LangChain Skills (langchain-ai/langchain-skills)").start();
331
331
  try {
332
- // Instalar todas as 11 skills do LangChain para o editor escolhido
332
+ // Install all 11 LangChain skills for the chosen editor
333
333
  const agentFlag = primaryEditor === "cursor" ? "cursor" : primaryEditor === "codex" ? "codex" : "claude-code";
334
334
  execSync(
335
335
  `npx skills add langchain-ai/langchain-skills --agent ${agentFlag} --skill "*" --yes`,
336
336
  { stdio: "pipe", timeout: 120000, cwd: targetDir, shell: true }
337
337
  );
338
- spinnerLc.succeed("11 LangChain Skills instaladas (framework-selection, langchain-*, langgraph-*, deep-agents-*)");
338
+ spinnerLc.succeed("11 LangChain Skills installed (framework-selection, langchain-*, langgraph-*, deep-agents-*)");
339
339
  } catch {
340
- // Fallback: tentar sem --agent
340
+ // Fallback: try without --agent
341
341
  try {
342
342
  execSync(
343
343
  `npx skills add langchain-ai/langchain-skills --skill "*" --yes`,
344
344
  { stdio: "pipe", timeout: 120000, cwd: targetDir, shell: true }
345
345
  );
346
- spinnerLc.succeed("11 LangChain Skills instaladas");
346
+ spinnerLc.succeed("11 LangChain Skills installed");
347
347
  } catch {
348
- spinnerLc.warn("Instale manualmente as LangChain Skills:");
348
+ spinnerLc.warn("Install LangChain Skills manually:");
349
349
  console.log(chalk.dim(" npx skills add langchain-ai/langchain-skills --skill '*' --yes"));
350
350
  }
351
351
  }
352
352
  }
353
353
 
354
354
  // 4. References
355
- const spinner3 = ora("Instalando references").start();
355
+ const spinner3 = ora("Installing references").start();
356
356
  const refsSrc = join(bundleDir, "references");
357
357
  ensureDir(join(targetDir, "references"));
358
358
  if (existsSync(refsSrc)) {
359
359
  copyDir(refsSrc, join(targetDir, "references"));
360
360
  }
361
- spinner3.succeed("references/ pronto");
361
+ spinner3.succeed("references/ ready");
362
362
 
363
- // 4. GitHub Spec Kit — instalar CLI + inicializar no projeto
364
- // Mapear editor para flag --ai do specify
363
+ // 4. GitHub Spec Kit — install CLI + initialize in project
364
+ // Map editor to specify --ai flag
365
365
  const aiFlags = {
366
366
  claude: "claude",
367
367
  cursor: "cursor-agent",
@@ -369,22 +369,22 @@ async function main() {
369
369
  copilot: "copilot",
370
370
  windsurf: "windsurf",
371
371
  };
372
- // Usar o primeiro editor como --ai (ou claude como default)
372
+ // Use first editor as --ai (claude as default)
373
373
  const primaryEditor = editorsToInstall[0];
374
374
  const aiFlag = aiFlags[primaryEditor] || "claude";
375
375
 
376
- // 4a. Instalar specify-cli
376
+ // 4a. Install specify-cli
377
377
  const SPECKIT_VERSION = "v0.4.3";
378
- const spinner4 = ora("Instalando GitHub Spec Kit (specify-cli)").start();
378
+ const spinner4 = ora("Installing GitHub Spec Kit (specify-cli)").start();
379
379
  let specifyInstalled = false;
380
380
 
381
- // Verificar se está instalado (specify não aceita --version, usar --help)
381
+ // Check if already installed (specify doesn't accept --version, use --help)
382
382
  try {
383
383
  execSync("specify --help", { stdio: "ignore" });
384
384
  specifyInstalled = true;
385
- spinner4.succeed("specify-cli instalado");
385
+ spinner4.succeed("specify-cli already installed");
386
386
  } catch {
387
- // Não instaladoinstalar
387
+ // Not installedinstalling
388
388
  try {
389
389
  execSync(`uv tool install specify-cli --from "git+https://github.com/github/spec-kit.git@${SPECKIT_VERSION}"`, {
390
390
  stdio: "pipe", timeout: 120000,
@@ -392,27 +392,27 @@ async function main() {
392
392
  specifyInstalled = true;
393
393
  spinner4.succeed(`specify-cli ${SPECKIT_VERSION} instalado`);
394
394
  } catch (err) {
395
- // Pode estar instalado mas uv retorna erro, checar de novo
395
+ // May already be installed but uv returned error, check again
396
396
  try {
397
397
  execSync("specify --help", { stdio: "ignore" });
398
398
  specifyInstalled = true;
399
- spinner4.succeed("specify-cli instalado");
399
+ spinner4.succeed("specify-cli already installed");
400
400
  } catch {
401
- spinner4.warn("Não foi possível instalar. Rode manualmente:");
401
+ spinner4.warn("Could not install automatically. Run manually:");
402
402
  console.log(chalk.dim(` uv tool install specify-cli --from "git+https://github.com/github/spec-kit.git@${SPECKIT_VERSION}"`));
403
403
  }
404
404
  }
405
405
  }
406
406
 
407
- // 4b. Rodar specify init no projeto para criar .specify/ e registrar /speckit.* commands
407
+ // 4b. Run specify init to create .specify/ and register /speckit.* commands
408
408
  if (specifyInstalled) {
409
- const spinner4c = ora(`Inicializando Spec Kit no projeto (--ai ${aiFlag})`).start();
409
+ const spinner4c = ora(`Initializing Spec Kit in project (--ai ${aiFlag})`).start();
410
410
  const specifyEnv = { ...process.env, PYTHONIOENCODING: "utf-8", PYTHONUTF8: "1" };
411
411
  let specInitOk = false;
412
412
 
413
- // Precisa de "y" piped pois specify pede confirmação se dir não vazio
414
- // --script sh para evitar dependência de pwsh no Windows
415
- // --ai-skills necessário para codex
413
+ // Need "y" piped because specify asks confirmation if dir not empty
414
+ // --script sh to avoid pwsh dependency on Windows
415
+ // --ai-skills required for codex
416
416
  const extraFlags = primaryEditor === "codex" ? " --ai-skills" : "";
417
417
  const initCmds = [
418
418
  `echo y | specify init . --ai ${aiFlag}${extraFlags} --script sh --force`,
@@ -433,20 +433,20 @@ async function main() {
433
433
  } catch { /* try next */ }
434
434
  }
435
435
  if (specInitOk) {
436
- spinner4c.succeed(`Spec Kit inicializado (/speckit.* commands disponíveis)`);
436
+ spinner4c.succeed(`Spec Kit initialized (/speckit.* commands available)`);
437
437
  } else {
438
- spinner4c.warn("Inicialize manualmente no terminal:");
438
+ spinner4c.warn("Initialize manually in your terminal:");
439
439
  console.log(chalk.dim(` cd ${targetDir}`));
440
440
  console.log(chalk.dim(` specify init . --ai ${aiFlag} --script sh`));
441
441
  }
442
442
 
443
- // 4c. Copiar constitution.md do bundle para dentro do .specify/memory/
443
+ // 4c. Copy bundle constitution.md to .specify/memory/
444
444
  const specifyMemoryDir = join(targetDir, ".specify", "memory");
445
445
  const bundleConstitution = join(bundleDir, ".spec", "constitution.md");
446
446
  if (existsSync(bundleConstitution)) {
447
447
  ensureDir(specifyMemoryDir);
448
448
  const constitutionDest = join(specifyMemoryDir, "constitution.md");
449
- // Append os princípios do bundle ao constitution gerado pelo specify
449
+ // Append bundle principles to constitution generated by specify
450
450
  if (existsSync(constitutionDest)) {
451
451
  const existing = readFileSync(constitutionDest, "utf-8");
452
452
  const bundleContent = readFileSync(bundleConstitution, "utf-8");
@@ -454,8 +454,8 @@ async function main() {
454
454
  } else {
455
455
  cpSync(bundleConstitution, constitutionDest);
456
456
  }
457
- const spinner4d = ora("Constitution do bundle integrado ao Spec Kit").start();
458
- spinner4d.succeed("Constitution do bundle integrado ao Spec Kit");
457
+ const spinner4d = ora("Bundle constitution integrated with Spec Kit").start();
458
+ spinner4d.succeed("Bundle constitution integrated with Spec Kit");
459
459
  }
460
460
  }
461
461
 
@@ -463,19 +463,19 @@ async function main() {
463
463
  console.log("");
464
464
  console.log(chalk.green.bold(" Pronto!"));
465
465
  console.log("");
466
- console.log(" Estrutura instalada:");
466
+ console.log(" Files installed:");
467
467
 
468
468
  for (const editorKey of editorsToInstall) {
469
469
  const e = EDITORS[editorKey];
470
470
  console.log(` ${chalk.yellow(e.name)}:`);
471
471
  if (editorKey === "claude") {
472
472
  console.log(` ${chalk.cyan("CLAUDE.md")} → @AGENTS.md`);
473
- console.log(` ${chalk.cyan(".claude/skills/")} (${skills.length} skills com SKILL.md)`);
473
+ console.log(` ${chalk.cyan(".claude/skills/")} (${skills.length} skills with SKILL.md)`);
474
474
  } else if (editorKey === "cursor") {
475
- console.log(` ${chalk.cyan("AGENTS.md")} (instruções gerais)`);
476
- console.log(` ${chalk.cyan(".cursor/skills/")} (${skills.length} skills com SKILL.md)`);
475
+ console.log(` ${chalk.cyan("AGENTS.md")} (general instructions)`);
476
+ console.log(` ${chalk.cyan(".cursor/skills/")} (${skills.length} skills with SKILL.md)`);
477
477
  } else if (editorKey === "codex") {
478
- console.log(` ${chalk.cyan("AGENTS.md")} (tudo em um arquivo)`);
478
+ console.log(` ${chalk.cyan("AGENTS.md")} (all in one file)`);
479
479
  } else if (editorKey === "copilot") {
480
480
  console.log(` ${chalk.cyan(".github/copilot-instructions.md")}`);
481
481
  console.log(` ${chalk.cyan(".github/instructions/")} (${skills.length} .instructions.md)`);
@@ -483,18 +483,18 @@ async function main() {
483
483
  console.log(` ${chalk.cyan(".windsurfrules")}`);
484
484
  }
485
485
  }
486
- console.log(` ${chalk.cyan("skills/")} (${skills.length} canônicas para Deep Agents)`);
486
+ console.log(` ${chalk.cyan("skills/")} (${skills.length} canonical for Deep Agents)`);
487
487
  console.log(` ${chalk.cyan(".specify/")} (GitHub Spec Kit — /speckit.* commands)`);
488
488
  console.log("");
489
- console.log(" Comandos SDD disponíveis no editor:");
490
- console.log(` ${chalk.cyan("/speckit.constitution")} — Definir princípios do projeto`);
491
- console.log(` ${chalk.cyan("/speckit.specify")} — Especificar O QUE e POR QUÊ`);
492
- console.log(` ${chalk.cyan("/speckit.plan")} — Planejar arquitetura e stack`);
493
- console.log(` ${chalk.cyan("/speckit.tasks")} — Quebrar em tasks atômicas`);
494
- console.log(` ${chalk.cyan("/speckit.implement")} — Executar as tasks`);
489
+ console.log(" SDD commands available in your editor:");
490
+ console.log(` ${chalk.cyan("/speckit.constitution")} — Define project principles`);
491
+ console.log(` ${chalk.cyan("/speckit.specify")} — Specify WHAT and WHY`);
492
+ console.log(` ${chalk.cyan("/speckit.plan")} — Plan architecture and stack`);
493
+ console.log(` ${chalk.cyan("/speckit.tasks")} — Break into atomic tasks`);
494
+ console.log(` ${chalk.cyan("/speckit.implement")} — Execute tasks`);
495
495
  console.log("");
496
- console.log(" Próximo passo:");
497
- console.log(" Abra o projeto no editor AI e use " + chalk.cyan("/speckit.specify") + " para começar");
496
+ console.log(" Next step:");
497
+ console.log(" Open your project in your AI editor and use " + chalk.cyan("/speckit.specify") + " to get started");
498
498
  console.log("");
499
499
  }
500
500
 
@@ -1,33 +1,33 @@
1
- # Constitution — Projeto de Agentes AI
1
+ # Constitution — AI Agents Project
2
2
 
3
- ## Princípios
3
+ ## Principles
4
4
 
5
- 1. **Spec primeiro, código depois** — Toda demanda passa pelo fluxo SDD antes de implementação
6
- 2. **Agente governado** — Todo agente segue seu AGENTS.md e skills, sem "vibing coding"
7
- 3. **Observável** — Toda execução de agente é rastreada no Langfuse
8
- 4. **Avaliável** — Todo agente tem evals com golden dataset antes de ir para produção
9
- 5. **Context-aware** — Gerenciar janela de contexto com as 4 estratégias (Write, Select, Compress, Isolate)
5
+ 1. **Spec first, code later** — Every demand goes through the SDD flow before implementation
6
+ 2. **Governed agent** — Every agent follows its AGENTS.md and skills, no "vibing coding"
7
+ 3. **Observable** — Every agent execution is traced in Langfuse
8
+ 4. **Evaluable** — Every agent has evals with golden dataset before going to production
9
+ 5. **Context-aware** — Manage context window with the 4 strategies (Write, Select, Compress, Isolate)
10
10
 
11
- ## Padrões de desenvolvimento
11
+ ## Development Standards
12
12
 
13
- - Clean Architecture para separar domínio de infraestrutura
14
- - Entidades ricas com comportamento (não anêmicas)
15
- - Value Objects para validação
16
- - Testes: >= 80% cobertura, evals para agentes
13
+ - Clean Architecture to separate domain from infrastructure
14
+ - Rich entities with behavior (not anemic)
15
+ - Value Objects for validation
16
+ - Tests: >= 80% coverage, evals for agents
17
17
  - Python 3.11+, type hints, Black + Ruff
18
18
 
19
- ## Padrões de agentes
19
+ ## Agent Standards
20
20
 
21
- - System prompts versionados, nunca hardcoded
22
- - Tools com schemas Pydantic
23
- - Human-in-the-loop para operações destrutivas
24
- - Timeout e limite de iterações em loops
25
- - Memória de longo prazo via LangGraph Store
21
+ - System prompts versioned, never hardcoded
22
+ - Tools with Pydantic schemas
23
+ - Human-in-the-loop for destructive operations
24
+ - Timeout and iteration limits on loops
25
+ - Long-term memory via LangGraph Store
26
26
 
27
- ## Padrões de qualidade
27
+ ## Quality Standards
28
28
 
29
- - Code review obrigatório
30
- - Commits seguem Conventional Commits
31
- - Branches seguem estratégia feature/fix/hotfix
32
- - Nunca commitar secrets
33
- - Rate limiting em todas as APIs
29
+ - Code review mandatory
30
+ - Commits follow Conventional Commits
31
+ - Branches follow feature/fix/hotfix strategy
32
+ - Never commit secrets
33
+ - Rate limiting on all APIs