@envisiongroup/create-alakazam 0.1.1

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 (27) hide show
  1. package/LICENSE +5 -0
  2. package/README.md +56 -0
  3. package/bin/create-alakazam.mjs +7 -0
  4. package/package.json +28 -0
  5. package/src/args.mjs +72 -0
  6. package/src/createAlakazamCli.mjs +115 -0
  7. package/src/projectName.mjs +33 -0
  8. package/src/prompts.mjs +63 -0
  9. package/src/registry.mjs +62 -0
  10. package/src/template.mjs +135 -0
  11. package/templates/default/.env.sample +55 -0
  12. package/templates/default/README.md +66 -0
  13. package/templates/default/gitignore +7 -0
  14. package/templates/default/package.json +36 -0
  15. package/templates/default/src/mastra/access/role-rules.ts +31 -0
  16. package/templates/default/src/mastra/agents/azure-claude-sandbox-agent/azure-claude-sandbox-agent.ts +108 -0
  17. package/templates/default/src/mastra/agents/azure-openai-sandbox-agent/azure-openai-sandbox-agent.ts +111 -0
  18. package/templates/default/src/mastra/agents/azure-openai-sandbox-agent/prompts/index.ts +42 -0
  19. package/templates/default/src/mastra/agents/user-context-agent/prompts/index.ts +93 -0
  20. package/templates/default/src/mastra/agents/user-context-agent/tools/get-my-profile-tool.ts +62 -0
  21. package/templates/default/src/mastra/agents/user-context-agent/user-context-agent.ts +87 -0
  22. package/templates/default/src/mastra/agents/weather-agent/weather-agent.ts +68 -0
  23. package/templates/default/src/mastra/index.ts +47 -0
  24. package/templates/default/src/mastra/scorers/weather-scorer.ts +92 -0
  25. package/templates/default/src/mastra/tools/weather-tool.ts +110 -0
  26. package/templates/default/src/mastra/workflows/weather-workflow.ts +198 -0
  27. package/templates/default/tsconfig.json +16 -0
package/LICENSE ADDED
@@ -0,0 +1,5 @@
1
+ Copyright (c) 2026 Envision Group.
2
+
3
+ This software may be used, copied, modified, and distributed only with prior written permission from Envision Group.
4
+
5
+ All other rights reserved.
package/README.md ADDED
@@ -0,0 +1,56 @@
1
+ # @envisiongroup/create-alakazam
2
+
3
+ CLI para generar un proyecto [Mastra](https://mastra.ai) cliente sobre
4
+ [`@envisiongroup/alakazam`](https://www.npmjs.com/package/@envisiongroup/alakazam):
5
+ agents hub con auth Entra ID, storage multi-dialecto, rutas de chat/catálogo/
6
+ adjuntos y observability ya cableadas vía `createAgentsHub()`.
7
+
8
+ ```bash
9
+ npm create @envisiongroup/alakazam mi-proyecto
10
+ # o
11
+ pnpm create @envisiongroup/alakazam mi-proyecto
12
+ ```
13
+
14
+ ## Qué genera
15
+
16
+ Un proyecto Mastra mínimo pero funcional:
17
+
18
+ - **`weather-agent`** — agente mínimo de referencia: `hubAgent()` (catálogo +
19
+ acceso), `getAzureClient()`, tool propia, scorers, `Memory`.
20
+ - **`azure-openai-sandbox-agent`** (opcional) — patrones avanzados listos para
21
+ copiar: emisión de reasoning al cliente (`emitReasoning`), procesado de
22
+ adjuntos (uploads), generación de archivos con `code_interpreter` y links de
23
+ descarga (downloads).
24
+ - **`azure-claude-sandbox-agent`** (opcional) — provider alternativo: Claude
25
+ vía Azure AI Foundry, adjuntos PDF/imágenes nativos, fail-closed de uploads
26
+ Office.
27
+ - **`user-context-agent`** (opcional) — contexto del usuario autenticado:
28
+ `requestContextSchema`, instrucciones dinámicas con la identidad y tools
29
+ condicionales (sin Microsoft Graph: funciona out-of-the-box).
30
+ - Tool, workflow, scorers (prebuilt + LLM-judged), `role-rules.ts` y
31
+ `.env.sample` documentado.
32
+
33
+ ## Flags
34
+
35
+ | Flag | Descripción |
36
+ | ------------------------ | ----------------------------------------------------------------------------------------------------- |
37
+ | `--minimal` | Solo la suite weather, sin los tres agents de ejemplo avanzados. |
38
+ | `--yes`, `-y` | Acepta defaults sin preguntar (nombre `mi-agents-hub` si no se pasa uno). |
39
+ | `--dir <ruta>` | Directorio destino (default: `./<nombre>`). |
40
+ | `--alakazam-version <v>` | Versión de `@envisiongroup/alakazam` a estampar. Default: última publicada en el registry (`^x.y.z`). |
41
+
42
+ ## Requisitos del proyecto generado
43
+
44
+ - Node >= 22.13.
45
+ - Token npm con acceso al scope `@envisiongroup` en `~/.npmrc`
46
+ (`@envisiongroup/alakazam` se publica con `access: restricted`).
47
+ - Rellenar `.env` (Azure OpenAI + App Registration de Entra ID) antes de
48
+ `pnpm dev`.
49
+
50
+ ## Desarrollo de este paquete (monorepo Alakazam)
51
+
52
+ ```bash
53
+ pnpm --filter @envisiongroup/create-alakazam test # tests del scaffold
54
+ pnpm --filter @envisiongroup/create-alakazam smoke # scaffold + install + typecheck real
55
+ pnpm sandbox:shell -- --verify # sandbox local con el tarball de alakazam (pre-publish)
56
+ ```
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env node
2
+ import { main } from '../src/createAlakazamCli.mjs';
3
+
4
+ main(process.argv.slice(2)).catch((error) => {
5
+ process.stderr.write(`\n✗ ${error.message}\n\n`);
6
+ process.exitCode = 1;
7
+ });
package/package.json ADDED
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "@envisiongroup/create-alakazam",
3
+ "version": "0.1.1",
4
+ "type": "module",
5
+ "description": "CLI para generar proyectos Mastra cliente sobre @envisiongroup/alakazam (agents hub con auth, storage y rutas ya cableadas).",
6
+ "license": "SEE LICENSE IN LICENSE",
7
+ "engines": {
8
+ "node": ">=22.13.0"
9
+ },
10
+ "bin": {
11
+ "create-alakazam": "./bin/create-alakazam.mjs"
12
+ },
13
+ "files": [
14
+ "bin",
15
+ "src",
16
+ "templates",
17
+ "!src/**/__tests__"
18
+ ],
19
+ "publishConfig": {
20
+ "access": "public",
21
+ "registry": "https://registry.npmjs.org/"
22
+ },
23
+ "scripts": {
24
+ "test": "node --test src/__tests__/*.test.mjs",
25
+ "smoke": "node scripts/smoke.mjs",
26
+ "pack:dry-run": "pnpm pack --dry-run"
27
+ }
28
+ }
package/src/args.mjs ADDED
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Parseo de argv del CLI (sin dependencias).
3
+ *
4
+ * Uso:
5
+ * create-alakazam [nombre-proyecto] [flags]
6
+ *
7
+ * Flags:
8
+ * --minimal Sin agents de ejemplo avanzados (solo weather-agent).
9
+ * --yes, -y No preguntar: acepta defaults.
10
+ * --dir <ruta> Directorio destino (default: ./<nombre-proyecto>).
11
+ * --alakazam-version <v> Versión de @envisiongroup/alakazam a estampar
12
+ * (default: última publicada en el registry).
13
+ * --help, -h Muestra ayuda.
14
+ */
15
+ export function parseArgs(argv) {
16
+ const args = {
17
+ name: null,
18
+ minimal: false,
19
+ yes: false,
20
+ dir: null,
21
+ alakazamVersion: null,
22
+ help: false,
23
+ };
24
+
25
+ for (let i = 0; i < argv.length; i += 1) {
26
+ const arg = argv[i];
27
+ switch (arg) {
28
+ case '--minimal':
29
+ args.minimal = true;
30
+ break;
31
+ case '--yes':
32
+ case '-y':
33
+ args.yes = true;
34
+ break;
35
+ case '--dir':
36
+ args.dir = argv[++i] ?? null;
37
+ break;
38
+ case '--alakazam-version':
39
+ args.alakazamVersion = argv[++i] ?? null;
40
+ break;
41
+ case '--help':
42
+ case '-h':
43
+ args.help = true;
44
+ break;
45
+ default:
46
+ if (arg.startsWith('-')) {
47
+ throw new Error(`flag desconocido: ${arg}`);
48
+ }
49
+ if (args.name !== null) {
50
+ throw new Error(
51
+ `nombre de proyecto duplicado: "${args.name}" y "${arg}"`,
52
+ );
53
+ }
54
+ args.name = arg;
55
+ }
56
+ }
57
+
58
+ return args;
59
+ }
60
+
61
+ export const USAGE = `
62
+ Uso: npm create @envisiongroup/alakazam [nombre-proyecto] [flags]
63
+
64
+ Genera un proyecto Mastra cliente sobre @envisiongroup/alakazam.
65
+
66
+ Flags:
67
+ --minimal Solo la suite weather (sin los agents de ejemplo avanzados)
68
+ --yes, -y Acepta defaults sin preguntar
69
+ --dir <ruta> Directorio destino (default: ./<nombre-proyecto>)
70
+ --alakazam-version <v> Versión de alakazam a estampar (default: última del registry)
71
+ --help, -h Esta ayuda
72
+ `;
@@ -0,0 +1,115 @@
1
+ /**
2
+ * Flujo principal de `npm create @envisiongroup/alakazam`.
3
+ *
4
+ * Slim por diseño (a diferencia de create-ditto): un solo template, sin
5
+ * variantes combinatorias ni matriz de compatibilidad. La versión de
6
+ * @envisiongroup/alakazam se estampa desde el registry en el momento del
7
+ * scaffold — eso ES la reproducibilidad aquí.
8
+ */
9
+ import path from 'node:path';
10
+ import { fileURLToPath } from 'node:url';
11
+
12
+ import { parseArgs, USAGE } from './args.mjs';
13
+ import { validateProjectName } from './projectName.mjs';
14
+ import { askConfirm, askText } from './prompts.mjs';
15
+ import { resolveAlakazamVersion } from './registry.mjs';
16
+ import { scaffoldTemplate } from './template.mjs';
17
+
18
+ const packageRoot = path.resolve(
19
+ path.dirname(fileURLToPath(import.meta.url)),
20
+ '..',
21
+ );
22
+ const TEMPLATE_DIR = path.join(packageRoot, 'templates', 'default');
23
+
24
+ const warn = (message) => process.stderr.write(`⚠ ${message}\n`);
25
+ const info = (message) => process.stdout.write(`${message}\n`);
26
+
27
+ async function resolveProjectName(args) {
28
+ if (args.name) {
29
+ const problem = validateProjectName(args.name);
30
+ if (problem) {
31
+ throw new Error(problem);
32
+ }
33
+ return args.name;
34
+ }
35
+ if (args.yes) {
36
+ return 'mi-agents-hub';
37
+ }
38
+ if (!process.stdin.isTTY) {
39
+ throw new Error(
40
+ 'falta el nombre del proyecto y no hay TTY para preguntar. ' +
41
+ 'Pásalo como argumento: npm create @envisiongroup/alakazam mi-proyecto',
42
+ );
43
+ }
44
+ return askText('Nombre del proyecto', {
45
+ defaultValue: 'mi-agents-hub',
46
+ validate: validateProjectName,
47
+ });
48
+ }
49
+
50
+ async function resolveMinimal(args) {
51
+ if (args.minimal || args.yes || !process.stdin.isTTY) {
52
+ return args.minimal;
53
+ }
54
+ const withExamples = await askConfirm(
55
+ '¿Incluir agents de ejemplo avanzados (adjuntos, generación de archivos, reasoning y contexto de usuario)?',
56
+ { defaultValue: true },
57
+ );
58
+ return !withExamples;
59
+ }
60
+
61
+ function printNextSteps({ projectName, targetDir, minimal }) {
62
+ const relativeTarget = path.relative(process.cwd(), targetDir) || '.';
63
+ info('');
64
+ info(`✓ Proyecto ${projectName} creado en ${relativeTarget}`);
65
+ info('');
66
+ info('Siguientes pasos:');
67
+ info(` cd ${relativeTarget}`);
68
+ info(
69
+ ' cp .env.sample .env # rellena AZURE_MODEL_*, AZURE_API_* y OPENAI_API_KEY',
70
+ );
71
+ info(
72
+ ' pnpm install # requiere token npm del scope @envisiongroup',
73
+ );
74
+ info(' pnpm dev # Mastra Studio en http://localhost:4111');
75
+ info('');
76
+ info(
77
+ minimal
78
+ ? 'Incluye solo la suite weather (agente, tool, workflow y scorer). Los agents de ejemplo avanzados se pueden copiar después desde el repo del paquete create-alakazam.'
79
+ : 'Incluye la suite weather (mínimo) y tres agents de ejemplo: azure-openai-sandbox-agent (adjuntos, archivos, reasoning), azure-claude-sandbox-agent (provider Claude vía Foundry) y user-context-agent (contexto del usuario autenticado). Borra lo que no necesites.',
80
+ );
81
+ info('');
82
+ }
83
+
84
+ export async function main(argv, { cwd = process.cwd() } = {}) {
85
+ const args = parseArgs(argv);
86
+ if (args.help) {
87
+ info(USAGE);
88
+ return null;
89
+ }
90
+
91
+ const projectName = await resolveProjectName(args);
92
+ const minimal = await resolveMinimal(args);
93
+ const targetDir = path.resolve(cwd, args.dir ?? projectName);
94
+ const alakazamVersion = resolveAlakazamVersion({
95
+ override: args.alakazamVersion,
96
+ warn,
97
+ });
98
+
99
+ const { copied } = scaffoldTemplate({
100
+ templateDir: TEMPLATE_DIR,
101
+ targetDir,
102
+ projectName,
103
+ alakazamVersion,
104
+ minimal,
105
+ });
106
+
107
+ if (!copied.length) {
108
+ throw new Error(
109
+ 'el template no produjo ningún archivo (¿template vacío?)',
110
+ );
111
+ }
112
+
113
+ printNextSteps({ projectName, targetDir, minimal });
114
+ return { projectName, targetDir, minimal, alakazamVersion, copied };
115
+ }
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Validación del nombre de proyecto (será el `name` del package.json generado
3
+ * y el nombre del directorio destino por defecto).
4
+ *
5
+ * Reglas: npm unscoped name — minúsculas, sin espacios, empieza por letra o
6
+ * número, permite `-`, `_`, `.` en medio.
7
+ */
8
+ const VALID_NAME = /^[a-z0-9][a-z0-9._-]*$/;
9
+
10
+ const FORBIDDEN_NAMES = new Set(['node_modules', 'favicon.ico']);
11
+
12
+ export function validateProjectName(name) {
13
+ if (!name || typeof name !== 'string') {
14
+ return 'el nombre no puede estar vacío';
15
+ }
16
+ if (name.length > 214) {
17
+ return 'el nombre excede 214 caracteres (límite npm)';
18
+ }
19
+ if (name !== name.toLowerCase()) {
20
+ return `el nombre debe ir en minúsculas: "${name}" → "${name.toLowerCase()}"`;
21
+ }
22
+ if (!VALID_NAME.test(name)) {
23
+ return `nombre inválido: "${name}". Usa letras minúsculas, números y - _ . (sin espacios ni mayúsculas)`;
24
+ }
25
+ if (FORBIDDEN_NAMES.has(name)) {
26
+ return `"${name}" es un nombre reservado`;
27
+ }
28
+ return null;
29
+ }
30
+
31
+ export function isValidProjectName(name) {
32
+ return validateProjectName(name) === null;
33
+ }
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Prompts interactivos mínimos sobre node:readline (cero dependencias).
3
+ */
4
+ import readline from 'node:readline/promises';
5
+
6
+ function createInterface() {
7
+ return readline.createInterface({
8
+ input: process.stdin,
9
+ output: process.stdout,
10
+ });
11
+ }
12
+
13
+ export async function askText(
14
+ question,
15
+ { defaultValue = null, validate = null } = {},
16
+ ) {
17
+ const rl = createInterface();
18
+ try {
19
+ for (;;) {
20
+ const suffix = defaultValue ? ` (${defaultValue})` : '';
21
+ const answer = (await rl.question(`${question}${suffix}: `)).trim();
22
+ const value = answer || defaultValue || '';
23
+ if (!value) {
24
+ process.stdout.write(' valor requerido.\n');
25
+ continue;
26
+ }
27
+ if (validate) {
28
+ const problem = validate(value);
29
+ if (problem) {
30
+ process.stdout.write(` ${problem}\n`);
31
+ continue;
32
+ }
33
+ }
34
+ return value;
35
+ }
36
+ } finally {
37
+ rl.close();
38
+ }
39
+ }
40
+
41
+ export async function askConfirm(question, { defaultValue = true } = {}) {
42
+ const rl = createInterface();
43
+ try {
44
+ const hint = defaultValue ? 'S/n' : 's/N';
45
+ for (;;) {
46
+ const answer = (await rl.question(`${question} (${hint}): `))
47
+ .trim()
48
+ .toLowerCase();
49
+ if (!answer) {
50
+ return defaultValue;
51
+ }
52
+ if (['s', 'si', 'sí', 'y', 'yes'].includes(answer)) {
53
+ return true;
54
+ }
55
+ if (['n', 'no'].includes(answer)) {
56
+ return false;
57
+ }
58
+ process.stdout.write(' responde s o n.\n');
59
+ }
60
+ } finally {
61
+ rl.close();
62
+ }
63
+ }
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Resolución de la versión de @envisiongroup/alakazam a estampar en el
3
+ * package.json generado.
4
+ *
5
+ * Prioridad:
6
+ * 1. `--alakazam-version` (override explícito; acepta "0.2.0", "^0.2.0",
7
+ * "latest" o incluso "file:/ruta" para desarrollo local del paquete).
8
+ * 2. `npm view @envisiongroup/alakazam version` — la última publicada.
9
+ * Requiere el token npm del scope configurado (paquete restringido).
10
+ * 3. DEFAULT_ALAKAZAM_VERSION con warning (registry inalcanzable).
11
+ *
12
+ * Las versiones semver "desnudas" se estampan con caret (^x.y.z): el consumidor
13
+ * recibe patches/minors del paquete sin regenerar el proyecto.
14
+ */
15
+ import { spawnSync } from 'node:child_process';
16
+
17
+ export const ALAKAZAM_PACKAGE = '@envisiongroup/alakazam';
18
+ export const DEFAULT_ALAKAZAM_VERSION = '^0.1.0';
19
+
20
+ const BARE_SEMVER = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/;
21
+
22
+ function npmBinary() {
23
+ return process.platform === 'win32' ? 'npm.cmd' : 'npm';
24
+ }
25
+
26
+ function normalizeVersionSpec(spec) {
27
+ return BARE_SEMVER.test(spec) ? `^${spec}` : spec;
28
+ }
29
+
30
+ /**
31
+ * @param {object} options
32
+ * @param {string|null} options.override valor de --alakazam-version
33
+ * @param {(msg: string) => void} options.warn
34
+ * @param {typeof spawnSync} [options.spawn] inyectable para tests
35
+ * @returns {string} version spec lista para package.json
36
+ */
37
+ export function resolveAlakazamVersion({
38
+ override = null,
39
+ warn,
40
+ spawn = spawnSync,
41
+ }) {
42
+ if (override) {
43
+ return normalizeVersionSpec(override.trim());
44
+ }
45
+
46
+ const result = spawn(npmBinary(), ['view', ALAKAZAM_PACKAGE, 'version'], {
47
+ encoding: 'utf8',
48
+ stdio: ['ignore', 'pipe', 'ignore'],
49
+ });
50
+
51
+ const version = result.status === 0 ? result.stdout.trim() : null;
52
+ if (version && BARE_SEMVER.test(version)) {
53
+ return `^${version}`;
54
+ }
55
+
56
+ warn(
57
+ `no se pudo consultar el registry para ${ALAKAZAM_PACKAGE} ` +
58
+ `(¿falta el token npm del scope @envisiongroup?). ` +
59
+ `Se estampa ${DEFAULT_ALAKAZAM_VERSION}; revísalo antes de instalar.`,
60
+ );
61
+ return DEFAULT_ALAKAZAM_VERSION;
62
+ }
@@ -0,0 +1,135 @@
1
+ /**
2
+ * Copia del template al directorio destino.
3
+ *
4
+ * Convenciones del template (templates/default):
5
+ *
6
+ * - `__PROJECT_NAME__` y `__ALAKAZAM_VERSION__` se sustituyen en TODO archivo
7
+ * de texto (package.json, README, fuentes).
8
+ * - `gitignore` (sin punto) se renombra a `.gitignore` en el destino — npm
9
+ * interpreta los `.gitignore` del tarball como reglas de empaquetado, así
10
+ * que no puede viajar con punto.
11
+ * - Líneas que terminan en `// __EXAMPLES__`: código de ejemplo avanzado.
12
+ * En modo minimal se elimina la línea entera; en modo completo solo se
13
+ * quita el marcador. Los directorios de agents de ejemplo avanzados
14
+ * (`EXAMPLES_DIRS`: azure-openai-sandbox, azure-claude-sandbox y
15
+ * user-context) se excluyen por completo en minimal.
16
+ */
17
+ import fs from 'node:fs';
18
+ import path from 'node:path';
19
+
20
+ export const EXAMPLES_MARKER = '__EXAMPLES__';
21
+ export const EXAMPLES_DIRS = [
22
+ path.join('src', 'mastra', 'agents', 'azure-openai-sandbox-agent'),
23
+ path.join('src', 'mastra', 'agents', 'azure-claude-sandbox-agent'),
24
+ path.join('src', 'mastra', 'agents', 'user-context-agent'),
25
+ ];
26
+
27
+ const TEXT_EXTENSIONS = new Set([
28
+ '.ts',
29
+ '.tsx',
30
+ '.js',
31
+ '.mjs',
32
+ '.json',
33
+ '.md',
34
+ '.sample',
35
+ '.txt',
36
+ '',
37
+ ]);
38
+
39
+ function isTextFile(filePath) {
40
+ return TEXT_EXTENSIONS.has(path.extname(filePath));
41
+ }
42
+
43
+ function processLine(line, minimal) {
44
+ if (!line.includes(EXAMPLES_MARKER)) {
45
+ return line;
46
+ }
47
+ if (minimal) {
48
+ return null; // línea eliminada
49
+ }
50
+ // Modo completo: quitar el marcador y el espacio/comentario sobrante.
51
+ return line.replace(/\s*\/\/\s*__EXAMPLES__\s*$/, '');
52
+ }
53
+
54
+ function transformContents(
55
+ contents,
56
+ { minimal, projectName, alakazamVersion },
57
+ ) {
58
+ const lines = contents.split('\n');
59
+ const kept = [];
60
+ for (const line of lines) {
61
+ const processed = processLine(line, minimal);
62
+ if (processed !== null) {
63
+ kept.push(processed);
64
+ }
65
+ }
66
+ return kept
67
+ .join('\n')
68
+ .replaceAll('__PROJECT_NAME__', projectName)
69
+ .replaceAll('__ALAKAZAM_VERSION__', alakazamVersion);
70
+ }
71
+
72
+ function* walkTemplate(dir) {
73
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
74
+ const entryPath = path.join(dir, entry.name);
75
+ if (entry.isDirectory()) {
76
+ yield* walkTemplate(entryPath);
77
+ continue;
78
+ }
79
+ yield entryPath;
80
+ }
81
+ }
82
+
83
+ /**
84
+ * @returns {{ copied: string[], skippedExamples: boolean }}
85
+ */
86
+ export function scaffoldTemplate({
87
+ templateDir,
88
+ targetDir,
89
+ projectName,
90
+ alakazamVersion,
91
+ minimal = false,
92
+ }) {
93
+ if (fs.existsSync(targetDir)) {
94
+ throw new Error(
95
+ `el directorio destino ya existe: ${targetDir}. Elige otro nombre o bórralo primero.`,
96
+ );
97
+ }
98
+
99
+ const copied = [];
100
+ const examplesRoots = EXAMPLES_DIRS.map((dir) =>
101
+ path.join(templateDir, dir),
102
+ );
103
+
104
+ for (const sourcePath of walkTemplate(templateDir)) {
105
+ if (
106
+ minimal &&
107
+ examplesRoots.some((root) => sourcePath.startsWith(root + path.sep))
108
+ ) {
109
+ continue;
110
+ }
111
+
112
+ const relative = path.relative(templateDir, sourcePath);
113
+ const destName = relative === 'gitignore' ? '.gitignore' : relative;
114
+ const destPath = path.join(targetDir, destName);
115
+
116
+ fs.mkdirSync(path.dirname(destPath), { recursive: true });
117
+
118
+ if (isTextFile(sourcePath)) {
119
+ const contents = fs.readFileSync(sourcePath, 'utf8');
120
+ fs.writeFileSync(
121
+ destPath,
122
+ transformContents(contents, {
123
+ minimal,
124
+ projectName,
125
+ alakazamVersion,
126
+ }),
127
+ );
128
+ } else {
129
+ fs.copyFileSync(sourcePath, destPath);
130
+ }
131
+ copied.push(destName);
132
+ }
133
+
134
+ return { copied, skippedExamples: minimal };
135
+ }
@@ -0,0 +1,55 @@
1
+ # ── Modelo (Azure OpenAI / Foundry) ─────────────────────────────────────────
2
+ # Las lee getAzureClient() de @envisiongroup/alakazam/services/azure-model.
3
+ # Cubre tanto los deployments gpt-* como claude-* (azure-claude-sandbox-agent
4
+ # va por la capa de compatibilidad de Foundry, sin config extra).
5
+ AZURE_MODEL_API_KEY=<api-key-del-recurso-azure-openai>
6
+ AZURE_MODEL_RESOURCE_NAME=<nombre-del-recurso-azure-openai>
7
+ # Deployment que usa weather-agent (opcional; default del código: gpt-4.1-mini):
8
+ # AZURE_MODEL_DEPLOYMENT=gpt-4.1-mini
9
+
10
+ # ── Scorers de ejemplo ──────────────────────────────────────────────────────
11
+ # El scorer LLM-judged de weather-agent usa el model router con
12
+ # 'openai/gpt-5-mini'. Solo la necesitas si corres scorers/evals.
13
+ # OPENAI_API_KEY=<api-key-de-openai>
14
+
15
+ # ── Storage (memoria/telemetría de agentes) ─────────────────────────────────
16
+ # Local (LibSQL, archivo):
17
+ DATABASE_URL=file:./mastra.db
18
+ # Producción PostgreSQL:
19
+ # DATABASE_URL=postgresql://user:password@host:5432/dbname?sslmode=require
20
+ # (con PostgreSQL/SQL Server instala además los peers opcionales:
21
+ # pnpm add pg @mastra/pg — o — pnpm add mssql @mastra/mssql)
22
+
23
+ # ── Auth: Microsoft Entra ID (backend Mastra) ───────────────────────────────
24
+ # App Registration de la API que valida los Bearer tokens de los clientes.
25
+ AZURE_API_TENANT_ID=<tenant-id>
26
+ AZURE_API_CLIENT_ID=<api-client-id>
27
+ AZURE_REQUIRED_SCOPE=access_as_user
28
+ # Requerido solo si usas On-Behalf-Of para llamar Microsoft Graph como el
29
+ # usuario autenticado (p. ej. enviar correo):
30
+ # AZURE_API_CLIENT_SECRET=<api-client-secret>
31
+ # Opcional (default https://graph.microsoft.com/.default):
32
+ # AZURE_GRAPH_SCOPE=https://graph.microsoft.com/.default
33
+
34
+ # Flags de auth:
35
+ # true → sin auth en ninguna ruta. Solo emergencias/debug. false en producción.
36
+ AUTH_DISABLED=false
37
+ # true → bypass de auth solo para Mastra Studio (same-origin, sin Bearer).
38
+ # Recomendado en local. En producción debe quedar sin setear (o false).
39
+ STUDIO_AUTH_BYPASS=true
40
+
41
+ # ── CORS ────────────────────────────────────────────────────────────────────
42
+ # Lista de orígenes permitidos separados por coma (tu SPA en local):
43
+ CORS_ALLOWED_ORIGIN=http://localhost:5173
44
+
45
+ # ── Client tools ────────────────────────────────────────────────────────────
46
+ # true → el servidor resuelve description/schemas de client tools desde el
47
+ # catálogo propio (modo allowlist). false → el cliente envía las definiciones.
48
+ CLIENT_TOOLS_ALLOWLIST_ENABLED=false
49
+
50
+ # ── Uploads ─────────────────────────────────────────────────────────────────
51
+ # Secret HMAC para firmar markers de adjuntos en el thread. Sin esta env el
52
+ # processor falla al subir/rehidratar archivos (PDF y Office).
53
+ # Genera uno con: openssl rand -hex 32
54
+ # Rotar invalida los adjuntos persistidos en threads previos al cambio.
55
+ UPLOAD_MARKER_SIGNING_SECRET=<hex-de-al-menos-32-bytes>