@arcadialdev/arcality 3.0.0 → 3.0.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.
- package/package.json +1 -1
- package/scripts/gen-and-run.mjs +950 -176
- package/scripts/init.mjs +221 -178
- package/tests/_helpers/agentic-runner.bundle.spec.js +285 -52
package/scripts/init.mjs
CHANGED
|
@@ -7,18 +7,17 @@
|
|
|
7
7
|
// 4. Create project via POST /api/v1/projects
|
|
8
8
|
// 5. Write arcality.config
|
|
9
9
|
|
|
10
|
-
import 'dotenv/config';
|
|
11
|
-
import fs from 'node:fs';
|
|
12
|
-
import path from 'node:path';
|
|
13
|
-
import { fileURLToPath } from 'url';
|
|
14
|
-
import { intro, outro, text, password as passwordPrompt, spinner, note, isCancel, cancel, confirm } from '@clack/prompts';
|
|
15
|
-
import chalk from 'chalk';
|
|
16
|
-
import
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
createConfig,
|
|
10
|
+
import 'dotenv/config';
|
|
11
|
+
import fs from 'node:fs';
|
|
12
|
+
import path from 'node:path';
|
|
13
|
+
import { fileURLToPath } from 'url';
|
|
14
|
+
import { intro, outro, text, password as passwordPrompt, spinner, note, isCancel, cancel, confirm } from '@clack/prompts';
|
|
15
|
+
import chalk from 'chalk';
|
|
16
|
+
import {
|
|
17
|
+
configExists,
|
|
18
|
+
loadProjectConfig,
|
|
19
|
+
saveProjectConfig,
|
|
20
|
+
createConfig,
|
|
22
21
|
} from '../src/configManager.mjs';
|
|
23
22
|
import { getApiUrl } from '../src/configLoader.mjs';
|
|
24
23
|
|
|
@@ -26,14 +25,14 @@ const __filename = fileURLToPath(import.meta.url);
|
|
|
26
25
|
const __dirname = path.dirname(__filename);
|
|
27
26
|
const PACKAGE_ROOT = process.env.ARCALITY_ROOT || path.resolve(__dirname, '..');
|
|
28
27
|
|
|
29
|
-
// ── Helpers ──
|
|
28
|
+
// ── Helpers ──
|
|
30
29
|
|
|
31
|
-
function getVersion() {
|
|
32
|
-
try {
|
|
33
|
-
const pkg = JSON.parse(fs.readFileSync(path.join(PACKAGE_ROOT, 'package.json'), 'utf8'));
|
|
34
|
-
return pkg.version || 'unknown';
|
|
35
|
-
} catch {
|
|
36
|
-
return 'unknown';
|
|
30
|
+
function getVersion() {
|
|
31
|
+
try {
|
|
32
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(PACKAGE_ROOT, 'package.json'), 'utf8'));
|
|
33
|
+
return pkg.version || 'unknown';
|
|
34
|
+
} catch {
|
|
35
|
+
return 'unknown';
|
|
37
36
|
}
|
|
38
37
|
}
|
|
39
38
|
|
|
@@ -56,75 +55,93 @@ function detectFramework(projectRoot) {
|
|
|
56
55
|
}
|
|
57
56
|
}
|
|
58
57
|
|
|
59
|
-
function isValidUrl(url) {
|
|
60
|
-
try {
|
|
61
|
-
const u = new URL(url);
|
|
62
|
-
return u.protocol === 'http:' || u.protocol === 'https:';
|
|
63
|
-
} catch {
|
|
64
|
-
return false;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
58
|
+
function isValidUrl(url) {
|
|
59
|
+
try {
|
|
60
|
+
const u = new URL(url);
|
|
61
|
+
return u.protocol === 'http:' || u.protocol === 'https:';
|
|
62
|
+
} catch {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function showInitBanner(version, projectRoot) {
|
|
68
|
+
const sentinelAscii = `
|
|
69
|
+
█████ ██████ ██████ █████ ██ ██ ████████ ██ ██
|
|
70
|
+
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
|
71
|
+
███████ ██████ ██ ███████ ██ ██ ██ ████
|
|
72
|
+
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
|
73
|
+
██ ██ ██ ██ ██████ ██ ██ ███████ ██ ██ ██`;
|
|
74
|
+
|
|
75
|
+
intro(chalk.gray(sentinelAscii));
|
|
76
|
+
|
|
77
|
+
note(
|
|
78
|
+
chalk.gray('◉ Modo de Configuración: ') + chalk.bold.white('ACTIVO') + '\n' +
|
|
79
|
+
chalk.gray('◉ Enlace con Backend: ') + chalk.bold.white('PREPARANDO') + '\n' +
|
|
80
|
+
chalk.gray('◉ Generador de Contexto: ') + chalk.bold.white('DISPONIBLE') + '\n' +
|
|
81
|
+
chalk.gray('─'.repeat(50)) + '\n' +
|
|
82
|
+
chalk.gray('Versión del Sistema: ') + chalk.white(`v${version}`),
|
|
83
|
+
'Estado Central de Arcality'
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
note(
|
|
87
|
+
chalk.gray('Nodo Objetivo: ') + chalk.white(process.env.NODE_ENV ?? 'development') + '\n' +
|
|
88
|
+
chalk.gray('Proyecto Local: ') + chalk.white(projectRoot) + '\n' +
|
|
89
|
+
chalk.gray('Comando: ') + chalk.white('arcality init'),
|
|
90
|
+
'Inicialización del Proyecto'
|
|
91
|
+
);
|
|
92
|
+
}
|
|
67
93
|
|
|
68
94
|
// ── Main ──
|
|
69
95
|
|
|
70
|
-
async function main() {
|
|
71
|
-
const version = getVersion();
|
|
72
|
-
const projectRoot = process.cwd();
|
|
73
|
-
|
|
74
|
-
console.clear();
|
|
75
|
-
|
|
76
|
-
intro(chalk.cyanBright(logo));
|
|
77
|
-
|
|
78
|
-
note(
|
|
79
|
-
chalk.magentaBright('Project Initialization') + '\n' +
|
|
80
|
-
chalk.gray('─'.repeat(50)) + '\n' +
|
|
81
|
-
chalk.bold.white('📦 Version: ') + chalk.yellow(version) + '\n' +
|
|
82
|
-
chalk.bold.white('📁 Project: ') + chalk.yellow(projectRoot),
|
|
83
|
-
'arcality init'
|
|
84
|
-
);
|
|
96
|
+
async function main() {
|
|
97
|
+
const version = getVersion();
|
|
98
|
+
const projectRoot = process.cwd();
|
|
99
|
+
|
|
100
|
+
console.clear();
|
|
101
|
+
showInitBanner(version, projectRoot);
|
|
85
102
|
|
|
86
103
|
// ── Check for existing config ──
|
|
87
|
-
if (configExists(projectRoot)) {
|
|
88
|
-
const existing = loadProjectConfig(projectRoot);
|
|
89
|
-
if (existing) {
|
|
90
|
-
note(
|
|
91
|
-
chalk.yellow('⚠️
|
|
92
|
-
chalk.white('
|
|
93
|
-
chalk.white(' Base URL: ') + chalk.cyan(existing.project?.baseUrl || 'unknown') + '\n' +
|
|
94
|
-
chalk.white('
|
|
95
|
-
'
|
|
96
|
-
);
|
|
97
|
-
|
|
98
|
-
const overwrite = await confirm({
|
|
99
|
-
message: '
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
if (isCancel(overwrite) || !overwrite) {
|
|
103
|
-
outro(chalk.cyan('
|
|
104
|
-
return;
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
}
|
|
104
|
+
if (configExists(projectRoot)) {
|
|
105
|
+
const existing = loadProjectConfig(projectRoot);
|
|
106
|
+
if (existing) {
|
|
107
|
+
note(
|
|
108
|
+
chalk.yellow('⚠️ Ya existe un arcality.config en este proyecto.') + '\n\n' +
|
|
109
|
+
chalk.white(' Proyecto: ') + chalk.cyan(existing.project?.name || 'unknown') + '\n' +
|
|
110
|
+
chalk.white(' Base URL: ') + chalk.cyan(existing.project?.baseUrl || 'unknown') + '\n' +
|
|
111
|
+
chalk.white(' ID del Proyecto: ') + chalk.gray(existing.projectId || 'desconocido'),
|
|
112
|
+
'Configuración Existente'
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
const overwrite = await confirm({
|
|
116
|
+
message: '¿Deseas reconfigurar? Esto creará un NUEVO proyecto en backend.',
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
if (isCancel(overwrite) || !overwrite) {
|
|
120
|
+
outro(chalk.cyan('Configuración sin cambios. Usa `arcality run` para iniciar.'));
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
108
125
|
|
|
109
126
|
// ── Step 1: API Key ──
|
|
110
127
|
const apiKey = await text({
|
|
111
|
-
message: chalk.cyan('🔑
|
|
128
|
+
message: chalk.cyan('🔑 Ingresa tu API Key de Arcality:'),
|
|
112
129
|
placeholder: 'arc_live_xxxxx',
|
|
113
130
|
validate: (v) => {
|
|
114
|
-
if (!v || !v.trim()) return 'API Key
|
|
115
|
-
if (!v.startsWith('arc_')) return 'API Key
|
|
131
|
+
if (!v || !v.trim()) return 'La API Key es obligatoria.';
|
|
132
|
+
if (!v.startsWith('arc_')) return 'La API Key debe iniciar con "arc_"';
|
|
116
133
|
},
|
|
117
134
|
});
|
|
118
135
|
|
|
119
136
|
if (isCancel(apiKey)) {
|
|
120
|
-
cancel('
|
|
137
|
+
cancel('Configuración cancelada.');
|
|
121
138
|
process.exit(0);
|
|
122
139
|
}
|
|
123
140
|
|
|
124
141
|
// ── Step 2: Validate API Key (uses internal API URL) ──
|
|
125
142
|
const apiUrl = getApiUrl();
|
|
126
143
|
const s = spinner();
|
|
127
|
-
s.start('
|
|
144
|
+
s.start('Validando API Key...');
|
|
128
145
|
|
|
129
146
|
let organizationId = null;
|
|
130
147
|
|
|
@@ -140,12 +157,12 @@ async function main() {
|
|
|
140
157
|
if (!res.ok) {
|
|
141
158
|
const errText = await res.text().catch(() => '');
|
|
142
159
|
const errorMap = {
|
|
143
|
-
401: '❌
|
|
144
|
-
403: `❌
|
|
145
|
-
};
|
|
146
|
-
const msg = errorMap[res.status] || `❌
|
|
147
|
-
s.stop(chalk.red(msg));
|
|
148
|
-
console.log(chalk.yellow('\n
|
|
160
|
+
401: '❌ API Key inválida. Revísala e inténtalo de nuevo.',
|
|
161
|
+
403: `❌ Autenticación fallida (HTTP 403). El servidor respondió: ${errText.slice(0, 150)}`,
|
|
162
|
+
};
|
|
163
|
+
const msg = errorMap[res.status] || `❌ Error del servidor (HTTP ${res.status}): ${errText.slice(0, 150)}`;
|
|
164
|
+
s.stop(chalk.red(msg));
|
|
165
|
+
console.log(chalk.yellow('\n Presiona Enter para volver al menú...'));
|
|
149
166
|
// Salida limpia (código 0) para que el menú padre no interprete esto como crash
|
|
150
167
|
await new Promise(r => setTimeout(r, 3000));
|
|
151
168
|
process.exit(0);
|
|
@@ -154,77 +171,77 @@ async function main() {
|
|
|
154
171
|
const data = await res.json();
|
|
155
172
|
organizationId = data.organizationId || data.organization_id || null;
|
|
156
173
|
|
|
157
|
-
s.stop(chalk.green('✅ API Key
|
|
174
|
+
s.stop(chalk.green('✅ API Key validada correctamente'));
|
|
158
175
|
|
|
159
176
|
if (data.plan) {
|
|
160
177
|
note(
|
|
161
|
-
chalk.bold.white('📋 Plan: ') + chalk.cyan(data.plan) + '\n' +
|
|
162
|
-
chalk.bold.white('🏢
|
|
163
|
-
'
|
|
178
|
+
chalk.bold.white('📋 Plan: ') + chalk.cyan(data.plan) + '\n' +
|
|
179
|
+
chalk.bold.white('🏢 Organización: ') + chalk.cyan(organizationId || 'N/A'),
|
|
180
|
+
'Información de la Cuenta'
|
|
164
181
|
);
|
|
165
182
|
}
|
|
166
183
|
} catch (err) {
|
|
167
|
-
s.stop(chalk.red(`❌
|
|
168
|
-
console.log(chalk.yellow('
|
|
184
|
+
s.stop(chalk.red(`❌ No se pudo conectar al backend de Arcality: ${err.message}`));
|
|
185
|
+
console.log(chalk.yellow(' Asegúrate de que el backend esté activo y accesible.'));
|
|
169
186
|
await new Promise(r => setTimeout(r, 3000));
|
|
170
187
|
process.exit(0);
|
|
171
188
|
}
|
|
172
189
|
|
|
173
190
|
// ── Step 3: Project details ──
|
|
174
191
|
const projectName = await text({
|
|
175
|
-
message: chalk.cyan('📋
|
|
176
|
-
placeholder: '
|
|
177
|
-
validate: (v) => {
|
|
178
|
-
if (!v || v.trim().length < 2) return '
|
|
179
|
-
},
|
|
180
|
-
});
|
|
181
|
-
if (isCancel(projectName)) { cancel('
|
|
182
|
-
|
|
183
|
-
const baseUrl = await text({
|
|
184
|
-
message: chalk.cyan('🌐
|
|
185
|
-
placeholder: 'https://staging.example.com',
|
|
186
|
-
validate: (v) => {
|
|
187
|
-
if (!v || !v.trim()) return '
|
|
188
|
-
if (!isValidUrl(v)) return '
|
|
189
|
-
},
|
|
190
|
-
});
|
|
191
|
-
if (isCancel(baseUrl)) { cancel('
|
|
192
|
-
|
|
193
|
-
const hasLogin = await confirm({
|
|
194
|
-
message: chalk.cyan('🔐
|
|
195
|
-
});
|
|
196
|
-
if (isCancel(hasLogin)) { cancel('
|
|
192
|
+
message: chalk.cyan('📋 Nombre del proyecto:'),
|
|
193
|
+
placeholder: 'Mi Portal QA',
|
|
194
|
+
validate: (v) => {
|
|
195
|
+
if (!v || v.trim().length < 2) return 'El nombre del proyecto es obligatorio (mínimo 2 caracteres).';
|
|
196
|
+
},
|
|
197
|
+
});
|
|
198
|
+
if (isCancel(projectName)) { cancel('Configuración cancelada.'); process.exit(0); }
|
|
199
|
+
|
|
200
|
+
const baseUrl = await text({
|
|
201
|
+
message: chalk.cyan('🌐 URL base de la aplicación a probar:'),
|
|
202
|
+
placeholder: 'https://staging.example.com',
|
|
203
|
+
validate: (v) => {
|
|
204
|
+
if (!v || !v.trim()) return 'La URL base es obligatoria.';
|
|
205
|
+
if (!isValidUrl(v)) return 'URL inválida. Usa http:// o https://';
|
|
206
|
+
},
|
|
207
|
+
});
|
|
208
|
+
if (isCancel(baseUrl)) { cancel('Configuración cancelada.'); process.exit(0); }
|
|
209
|
+
|
|
210
|
+
const hasLogin = await confirm({
|
|
211
|
+
message: chalk.cyan('🔐 ¿Tu aplicación requiere inicio de sesión/autenticación?'),
|
|
212
|
+
});
|
|
213
|
+
if (isCancel(hasLogin)) { cancel('Configuración cancelada.'); process.exit(0); }
|
|
197
214
|
|
|
198
215
|
let username = '';
|
|
199
216
|
let password = '';
|
|
200
217
|
|
|
201
218
|
if (hasLogin) {
|
|
202
|
-
username = await text({
|
|
203
|
-
message: chalk.cyan('👤
|
|
204
|
-
validate: (v) => {
|
|
205
|
-
if (!v || !v.trim()) return '
|
|
206
|
-
},
|
|
207
|
-
});
|
|
208
|
-
if (isCancel(username)) { cancel('
|
|
209
|
-
|
|
210
|
-
password = await passwordPrompt({
|
|
211
|
-
message: chalk.cyan('🔒
|
|
212
|
-
validate: (v) => {
|
|
213
|
-
if (!v || !v.length) return '
|
|
214
|
-
},
|
|
215
|
-
});
|
|
216
|
-
if (isCancel(password)) { cancel('
|
|
219
|
+
username = await text({
|
|
220
|
+
message: chalk.cyan('👤 Usuario o correo de acceso:'),
|
|
221
|
+
validate: (v) => {
|
|
222
|
+
if (!v || !v.trim()) return 'El usuario es obligatorio.';
|
|
223
|
+
},
|
|
224
|
+
});
|
|
225
|
+
if (isCancel(username)) { cancel('Configuración cancelada.'); process.exit(0); }
|
|
226
|
+
|
|
227
|
+
password = await passwordPrompt({
|
|
228
|
+
message: chalk.cyan('🔒 Contraseña de acceso:'),
|
|
229
|
+
validate: (v) => {
|
|
230
|
+
if (!v || !v.length) return 'La contraseña es obligatoria.';
|
|
231
|
+
},
|
|
232
|
+
});
|
|
233
|
+
if (isCancel(password)) { cancel('Configuración cancelada.'); process.exit(0); }
|
|
217
234
|
}
|
|
218
235
|
|
|
219
236
|
// ── Step 4: Detect framework ──
|
|
220
237
|
const frameworkDetected = detectFramework(projectRoot);
|
|
221
238
|
if (frameworkDetected) {
|
|
222
|
-
console.log(chalk.gray(` 🛠️ Framework
|
|
239
|
+
console.log(chalk.gray(` 🛠️ Framework detectado: ${chalk.magenta(frameworkDetected)}`));
|
|
223
240
|
}
|
|
224
241
|
|
|
225
242
|
// ── Step 5: Create project in backend ──
|
|
226
243
|
const sCreate = spinner();
|
|
227
|
-
sCreate.start('
|
|
244
|
+
sCreate.start('Creando proyecto en el backend de Arcality...');
|
|
228
245
|
|
|
229
246
|
let projectId = null;
|
|
230
247
|
try {
|
|
@@ -254,8 +271,8 @@ async function main() {
|
|
|
254
271
|
// If response is HTML (e.g. Cloudflare 521), show a clean message
|
|
255
272
|
const isHtml = errText.trim().startsWith('<');
|
|
256
273
|
const displayError = isHtml
|
|
257
|
-
? `
|
|
258
|
-
: `HTTP ${res.status}: ${errText.slice(0, 200)}`;
|
|
274
|
+
? `El servidor backend de Arcality no está disponible temporalmente (HTTP ${res.status}). El proyecto se guardará solo de forma local.`
|
|
275
|
+
: `HTTP ${res.status}: ${errText.slice(0, 200)}`;
|
|
259
276
|
|
|
260
277
|
sCreate.stop(chalk.yellow(`⚠️ ${displayError}`));
|
|
261
278
|
// Don't exit — write the config with a local-only project ID so user can still run
|
|
@@ -264,12 +281,12 @@ async function main() {
|
|
|
264
281
|
const created = await res.json();
|
|
265
282
|
projectId = created.id || created.Id;
|
|
266
283
|
organizationId = organizationId || created.organizationId || created.organization_id || null;
|
|
267
|
-
sCreate.stop(chalk.green(`✅
|
|
268
|
-
}
|
|
269
|
-
} catch (err) {
|
|
270
|
-
sCreate.stop(chalk.yellow(`⚠️
|
|
271
|
-
projectId = projectId || `local_${Date.now()}`;
|
|
272
|
-
}
|
|
284
|
+
sCreate.stop(chalk.green(`✅ Proyecto creado: "${projectName.trim()}" (${projectId})`));
|
|
285
|
+
}
|
|
286
|
+
} catch (err) {
|
|
287
|
+
sCreate.stop(chalk.yellow(`⚠️ No se pudo conectar con el servidor de Arcality: ${err.message}. Se continuará en modo local.`));
|
|
288
|
+
projectId = projectId || `local_${Date.now()}`;
|
|
289
|
+
}
|
|
273
290
|
|
|
274
291
|
// ── Step 6: Write arcality.config ──
|
|
275
292
|
const config = createConfig({
|
|
@@ -286,60 +303,86 @@ async function main() {
|
|
|
286
303
|
|
|
287
304
|
saveProjectConfig(config, projectRoot);
|
|
288
305
|
|
|
289
|
-
// ── Step 7: Custom QA Context ──
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
306
|
+
// ── Step 7: Custom QA Context ──
|
|
307
|
+
note(
|
|
308
|
+
chalk.white('El archivo ') + chalk.cyan('.arcality/qa-context.md') + chalk.white(' se inyecta al prompt del agente antes de cada misión.') + '\n' +
|
|
309
|
+
chalk.white('Úsalo para reglas del negocio, navegación especial y anti-patrones que Arcality deba respetar siempre.'),
|
|
310
|
+
'QA Context Local'
|
|
311
|
+
);
|
|
312
|
+
|
|
313
|
+
const wantsContext = await confirm({
|
|
314
|
+
message: chalk.cyan('📄 ¿Deseas generar un archivo local QA Context (.arcality/qa-context.md) para enseñarle al agente reglas del negocio?'),
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
if (!isCancel(wantsContext) && wantsContext) {
|
|
318
|
+
const arcalityDir = path.join(projectRoot, '.arcality');
|
|
319
|
+
if (!fs.existsSync(arcalityDir)) {
|
|
297
320
|
fs.mkdirSync(arcalityDir, { recursive: true });
|
|
298
321
|
}
|
|
299
322
|
|
|
300
|
-
const qaContextPath = path.join(arcalityDir, 'qa-context.md');
|
|
301
|
-
const qaTemplate = `#
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
##
|
|
310
|
-
|
|
311
|
-
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
323
|
+
const qaContextPath = path.join(arcalityDir, 'qa-context.md');
|
|
324
|
+
const qaTemplate = `# Arcality QA Context
|
|
325
|
+
<!--
|
|
326
|
+
Este archivo se inyecta antes de cada mision.
|
|
327
|
+
Escribe reglas cortas, especificas y accionables.
|
|
328
|
+
Usa un bullet por regla real.
|
|
329
|
+
Los bullets que empiecen con "Ejemplo:" se ignoran hasta que los reemplaces.
|
|
330
|
+
-->
|
|
331
|
+
|
|
332
|
+
## Reglas del Dominio
|
|
333
|
+
<!-- Restricciones de negocio, validaciones duras, formatos, limites -->
|
|
334
|
+
- Ejemplo: Un usuario no puede registrar mas de 24 horas en un mismo dia.
|
|
335
|
+
- Ejemplo: El campo "Numero de empleado" debe conservar ceros a la izquierda.
|
|
336
|
+
|
|
337
|
+
## Navegacion y UI Especial
|
|
338
|
+
<!-- Pasos raros de la interfaz, cargas lentas, modales, menus no obvios -->
|
|
339
|
+
- Ejemplo: Despues de elegir un proyecto, espera a que cargue la tabla antes de registrar horas.
|
|
340
|
+
- Ejemplo: El menu de acciones siempre se abre desde el icono ":" de la primera fila.
|
|
341
|
+
|
|
342
|
+
## Casos Prohibidos y Anti-patrones
|
|
343
|
+
<!-- Cosas que Arcality nunca debe intentar -->
|
|
344
|
+
- Ejemplo: No uses "Continuar con Microsoft".
|
|
345
|
+
- Ejemplo: No cierres el modal con Escape; usa el boton "Cancelar".
|
|
346
|
+
|
|
347
|
+
## Datos y Credenciales de Prueba
|
|
348
|
+
<!-- Usuarios de prueba, ambientes, datos obligatorios o datos que deben evitarse -->
|
|
349
|
+
- Ejemplo: Usa el usuario QA_TIMESHEET_01 para pruebas de captura diaria.
|
|
350
|
+
- Ejemplo: No reutilices folios ya aprobados.
|
|
351
|
+
|
|
352
|
+
## Resultado Esperado y Validaciones Clave
|
|
353
|
+
<!-- Como saber que la mision realmente termino bien -->
|
|
354
|
+
- Ejemplo: La prueba termina cuando el registro aparece en la tabla "Timesheet - Hoy" de /welcome.
|
|
355
|
+
- Ejemplo: Si aparece la leyenda "Expirado" en el proyecto, la prueba sigue siendo valida.
|
|
356
|
+
`;
|
|
357
|
+
if (!fs.existsSync(qaContextPath)) {
|
|
358
|
+
fs.writeFileSync(qaContextPath, qaTemplate);
|
|
359
|
+
note(chalk.green(`✅ QA Context creado en: ${qaContextPath}`), 'Contexto Guardado');
|
|
360
|
+
} else {
|
|
361
|
+
note(chalk.cyan(`ℹ️ QA Context ya existente: ${qaContextPath}`), 'Contexto Detectado');
|
|
362
|
+
}
|
|
363
|
+
}
|
|
321
364
|
|
|
322
365
|
// ── Summary ──
|
|
323
366
|
note(
|
|
324
|
-
chalk.green('✅ arcality.config
|
|
325
|
-
chalk.bold.white('
|
|
326
|
-
chalk.bold.white(' Base
|
|
327
|
-
chalk.bold.white(' Framework: ') + chalk.magenta(config.project.frameworkDetected || 'N/A') + '\n' +
|
|
328
|
-
chalk.bold.white('
|
|
329
|
-
chalk.bold.white('
|
|
330
|
-
'
|
|
331
|
-
);
|
|
332
|
-
|
|
333
|
-
outro(
|
|
334
|
-
chalk.cyanBright('🚀
|
|
335
|
-
chalk.bold.white('arcality run') +
|
|
336
|
-
chalk.cyanBright('
|
|
337
|
-
chalk.bold.white('arcality') +
|
|
338
|
-
chalk.cyanBright('
|
|
339
|
-
);
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
main().catch(err => {
|
|
343
|
-
console.error(chalk.red(' ❌ Error
|
|
344
|
-
process.exit(1);
|
|
345
|
-
});
|
|
367
|
+
chalk.green('✅ arcality.config creado correctamente') + '\n\n' +
|
|
368
|
+
chalk.bold.white(' Proyecto: ') + chalk.cyan(config.project.name) + '\n' +
|
|
369
|
+
chalk.bold.white(' URL Base: ') + chalk.cyan(config.project.baseUrl) + '\n' +
|
|
370
|
+
chalk.bold.white(' Framework: ') + chalk.magenta(config.project.frameworkDetected || 'N/A') + '\n' +
|
|
371
|
+
chalk.bold.white(' ID del Proyecto: ') + chalk.gray(config.projectId) + '\n' +
|
|
372
|
+
chalk.bold.white(' Carpeta de Misiones: ') + chalk.yellow(config.runtime.yamlOutputDir),
|
|
373
|
+
'Configuración Guardada'
|
|
374
|
+
);
|
|
375
|
+
|
|
376
|
+
outro(
|
|
377
|
+
chalk.cyanBright('🚀 Listo. Ejecuta ') +
|
|
378
|
+
chalk.bold.white('arcality run') +
|
|
379
|
+
chalk.cyanBright(' o ') +
|
|
380
|
+
chalk.bold.white('arcality') +
|
|
381
|
+
chalk.cyanBright(' para comenzar a probar.')
|
|
382
|
+
);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
main().catch(err => {
|
|
386
|
+
console.error(chalk.red(' ❌ Error durante init:'), err.message);
|
|
387
|
+
process.exit(1);
|
|
388
|
+
});
|