create-agentic-pdlc 2.1.4 → 2.1.5

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.
@@ -70,6 +70,18 @@ If any of these files are missing, you are in **Setup Mode**. Do not proceed wit
70
70
 
71
71
  ---
72
72
 
73
+ ## UPDATE MODE
74
+
75
+ If the user says anything like "update the pipeline", "update the board", "update agentic-pdlc", or "configure the agents", run:
76
+
77
+ ```bash
78
+ npx create-agentic-pdlc --update
79
+ ```
80
+
81
+ This detects which optional agents (Jules, QA Agent, Sentinel) are already configured in the project and interactively configures the missing ones. It does **not** overwrite user-owned files (`AGENTS.md`, agent config files).
82
+
83
+ ---
84
+
73
85
  ## EXECUTION MODE
74
86
 
75
87
  If `AGENTS.md` and `docs/pdlc.md` are present, you are in **Execution Mode**.
package/bin/cli.js CHANGED
@@ -59,7 +59,19 @@ const i18n = {
59
59
  missing_claude: t('❌ Could not find instruction file at ', '❌ Não foi possível encontrar o arquivo de instrução em ', '❌ No se pudo encontrar el archivo de instrucción en '),
60
60
  cursor_rules_written: t('✅ Default cursor rules written to .cursorrules', '✅ Regras padrão do cursor salvas em .cursorrules', '✅ Reglas por defecto de cursor guardadas en .cursorrules'),
61
61
  setup_done: t('🎉 All set! Continue the setup with your agent:', '🎉 Aqui tá pronto! Continue o setup com o seu agente:', '🎉 ¡Listo! Continúa el setup con tu agente:'),
62
- setup_done_hint: t('>>> Tell it to read and execute the .agentic-setup.md file!', '>>> Diga a ele para ler e executar o arquivo .agentic-setup.md!', '>>> Dile que lea y ejecute el archivo .agentic-setup.md!')
62
+ setup_done_hint: t('>>> Tell it to read and execute the .agentic-setup.md file!', '>>> Diga a ele para ler e executar o arquivo .agentic-setup.md!', '>>> Dile que lea y ejecute el archivo .agentic-setup.md!'),
63
+ update_title: t('agentic-pdlc — Agent Configuration Status', 'agentic-pdlc — Status de Configuração dos Agentes', 'agentic-pdlc — Estado de Configuración de Agentes'),
64
+ update_no_context: t('❌ No .agentic-pdlc/cli-context.json found. Run npx create-agentic-pdlc first.', '❌ Arquivo .agentic-pdlc/cli-context.json não encontrado. Rode npx create-agentic-pdlc primeiro.', '❌ Archivo .agentic-pdlc/cli-context.json no encontrado. Ejecuta npx create-agentic-pdlc primero.'),
65
+ update_all_ok: t('All agents configured!', 'Todos os agentes configurados!', '¡Todos los agentes configurados!'),
66
+ update_ask_configure: t('Configure missing agents? (Y/n): ', 'Configurar agentes faltantes? (S/n): ', '¿Configurar agentes faltantes? (S/n): '),
67
+ update_skipped: t('Skipped.', 'Pulado.', 'Omitido.'),
68
+ update_jules_header: t('— Jules (autonomous implementation agent) —', '— Jules (agente de implementação autônomo) —', '— Jules (agente de implementación autónomo) —'),
69
+ update_jules_ask: t(' Which agent? (a) @google-labs-jules (b) Other (c) Skip: ', ' Qual agente? (a) @google-labs-jules (b) Outro (c) Pular: ', ' ¿Qué agente? (a) @google-labs-jules (b) Otro (c) Omitir: '),
70
+ update_jules_ask_handle: t(' Agent handle (e.g. @my-agent): ', ' Handle do agente (ex: @meu-agente): ', ' Handle del agente (ej: @mi-agente): '),
71
+ update_qa_header: t('— QA Agent (AC verification via Gemini — free tier) —', '— QA Agent (verificação de ACs via Gemini — free tier) —', '— QA Agent (verificación de ACs via Gemini — free tier) —'),
72
+ update_qa_ask: t(' Activate? Requires GEMINI_API_KEY secret. (Y/n): ', ' Ativar? Requer secret GEMINI_API_KEY. (S/n): ', ' ¿Activar? Requiere el secret GEMINI_API_KEY. (S/n): '),
73
+ update_sentinel_header: t('— Sentinel (architecture audit via Gemini Code Assist) —', '— Sentinel (auditoria de arquitetura via Gemini Code Assist) —', '— Sentinel (auditoría de arquitectura via Gemini Code Assist) —'),
74
+ update_sentinel_ask: t(' Activate? Requires Gemini Code Assist CI job. (Y/n): ', ' Ativar? Requer CI job do Gemini Code Assist. (S/n): ', ' ¿Activar? Requiere CI job de Gemini Code Assist. (S/n): '),
63
75
  };
64
76
 
65
77
  const cyan = '\x1b[36m';
@@ -373,4 +385,189 @@ async function runSetup() {
373
385
  rl.close();
374
386
  }
375
387
 
376
- runSetup();
388
+ // ─── Update Mode helpers ──────────────────────────────────────────────────────
389
+
390
+ function detectAgentState(dir) {
391
+ const state = { jules: false, julesHandle: null, qaAgent: false, sentinel: false };
392
+
393
+ const atPath = path.join(dir, '.github', 'workflows', 'agent-trigger.yml');
394
+ if (fs.existsSync(atPath)) {
395
+ const content = fs.readFileSync(atPath, 'utf8');
396
+ if (!content.includes('{{AGENT_HANDLE}}') && !content.includes('{{IMPLEMENTATION_AGENT_LABEL}}')) {
397
+ const match = content.match(/(@[\w-]+)/);
398
+ if (match) { state.jules = true; state.julesHandle = match[1]; }
399
+ }
400
+ }
401
+
402
+ const paPath = path.join(dir, '.github', 'workflows', 'project-automation.yml');
403
+ if (fs.existsSync(paPath)) {
404
+ const content = fs.readFileSync(paPath, 'utf8');
405
+ state.qaAgent = /^ move-card-on-qa-pass:/m.test(content);
406
+ state.sentinel = /^ move-violation-to-board:/m.test(content);
407
+ }
408
+
409
+ return state;
410
+ }
411
+
412
+ function uncommentYamlJob(content, jobCommentedLine) {
413
+ if (!content.includes(jobCommentedLine)) return content;
414
+ const lines = content.split('\n');
415
+ const output = [];
416
+ let state = 'before';
417
+
418
+ for (const line of lines) {
419
+ if (state === 'before') {
420
+ if (line === jobCommentedLine) {
421
+ state = 'in-job';
422
+ output.push(line.replace(/^(\s{2})# ?/, '$1'));
423
+ } else if (/^\s{2}# (OPTIONAL:|When )/.test(line)) {
424
+ state = 'in-preamble';
425
+ } else {
426
+ output.push(line);
427
+ }
428
+ } else if (state === 'in-preamble') {
429
+ if (line === jobCommentedLine) {
430
+ state = 'in-job';
431
+ output.push(line.replace(/^(\s{2})# ?/, '$1'));
432
+ }
433
+ } else if (state === 'in-job') {
434
+ if (/^\s{2}#/.test(line)) {
435
+ output.push(line.replace(/^(\s{2})# ?/, '$1'));
436
+ } else {
437
+ state = 'after';
438
+ output.push(line);
439
+ }
440
+ } else {
441
+ output.push(line);
442
+ }
443
+ }
444
+
445
+ return output.join('\n');
446
+ }
447
+
448
+ function activateQaAgent(paPath) {
449
+ let content = fs.readFileSync(paPath, 'utf8');
450
+ content = uncommentYamlJob(content, ' # move-card-on-qa-pass:');
451
+
452
+ // Change STATUS_CODE_REVIEW_PR → STATUS_TESTING in move-card-on-pr-open only
453
+ const variantBIdx = content.indexOf('# 💡 VARIANT B');
454
+ if (variantBIdx !== -1) {
455
+ const before = content.slice(0, variantBIdx);
456
+ const after = content.slice(variantBIdx).replace('process.env.STATUS_CODE_REVIEW_PR', () => 'process.env.STATUS_TESTING');
457
+ content = before + after;
458
+ }
459
+
460
+ fs.writeFileSync(paPath, content, 'utf8');
461
+ }
462
+
463
+ function activateSentinel(paPath) {
464
+ let content = fs.readFileSync(paPath, 'utf8');
465
+ content = uncommentYamlJob(content, ' # move-violation-to-board:');
466
+ fs.writeFileSync(paPath, content, 'utf8');
467
+ }
468
+
469
+ function configureJules(atPath, handle, label) {
470
+ let content = fs.readFileSync(atPath, 'utf8');
471
+ const name = handle.replace('@', '');
472
+ content = content.replace(/\{\{IMPLEMENTATION_AGENT_NAME\}\}/g, () => name);
473
+ content = content.replace(/\{\{IMPLEMENTATION_AGENT_LABEL\}\}/g, () => label);
474
+ content = content.replace(/\{\{AGENT_HANDLE\}\}/g, () => handle);
475
+ fs.writeFileSync(atPath, content, 'utf8');
476
+ }
477
+
478
+ async function runUpdate() {
479
+ const contextPath = path.join(targetDir, '.agentic-pdlc', 'cli-context.json');
480
+ if (!fs.existsSync(contextPath)) {
481
+ console.error(`\n${red}${i18n.update_no_context}${reset}\n`);
482
+ rl.close();
483
+ process.exit(1);
484
+ }
485
+
486
+ const state = detectAgentState(targetDir);
487
+ const sep = '─'.repeat(55);
488
+
489
+ console.log(`\n${cyan}${sep}${reset}`);
490
+ console.log(`${cyan} ${i18n.update_title}${reset}`);
491
+ console.log(`${cyan}${sep}${reset}\n`);
492
+
493
+ const julesSuffix = state.julesHandle ? ` (${state.julesHandle})` : '';
494
+ console.log(` ${state.jules ? green + '✅' : red + '❌'} Jules${julesSuffix} — ${state.jules ? t('configured','configurado','configurado') : t('not configured','não configurado','no configurado')}${reset}`);
495
+ console.log(` ${state.qaAgent ? green + '✅' : red + '❌'} QA Agent — ${state.qaAgent ? t('active (Variant B)','ativo (Variant B)','activo (Variant B)') : t('not active (Variant A)','não ativo (Variant A)','no activo (Variant A)')}${reset}`);
496
+ console.log(` ${state.sentinel ? green + '✅' : red + '❌'} Sentinel — ${state.sentinel ? t('active','ativo','activo') : t('not configured','não configurado','no configurado')}${reset}`);
497
+
498
+ if (state.jules && state.qaAgent && state.sentinel) {
499
+ console.log(`\n${green}${i18n.update_all_ok}${reset}\n`);
500
+ rl.close();
501
+ return;
502
+ }
503
+
504
+ console.log(`\n${cyan}${sep}${reset}`);
505
+ const configureAnswer = await askQuestion(i18n.update_ask_configure);
506
+ const shouldConfigure = !['n', 'no', 'não', 'nao'].includes(configureAnswer.trim().toLowerCase());
507
+
508
+ if (!shouldConfigure) {
509
+ console.log(`\n${i18n.update_skipped}\n`);
510
+ rl.close();
511
+ return;
512
+ }
513
+
514
+ const paPath = path.join(targetDir, '.github', 'workflows', 'project-automation.yml');
515
+ const atPath = path.join(targetDir, '.github', 'workflows', 'agent-trigger.yml');
516
+ const results = [];
517
+
518
+ if (!state.jules && fs.existsSync(atPath)) {
519
+ console.log(`\n${cyan}${i18n.update_jules_header}${reset}`);
520
+ const choice = (await askQuestion(i18n.update_jules_ask)).trim().toLowerCase();
521
+ if (choice === 'a' || choice === '') {
522
+ configureJules(atPath, '@google-labs-jules', 'jules');
523
+ results.push(t('✅ Jules configured (@google-labs-jules)', '✅ Jules configurado (@google-labs-jules)', '✅ Jules configurado (@google-labs-jules)'));
524
+ } else if (choice === 'b') {
525
+ const handle = (await askQuestion(i18n.update_jules_ask_handle)).trim();
526
+ configureJules(atPath, handle, handle.replace('@', '').toLowerCase());
527
+ results.push(t(`✅ Agent configured (${handle})`, `✅ Agente configurado (${handle})`, `✅ Agente configurado (${handle})`));
528
+ } else {
529
+ results.push(t('⏭ Jules — skipped', '⏭ Jules — pulado', '⏭ Jules — omitido'));
530
+ }
531
+ }
532
+
533
+ if (!state.qaAgent && fs.existsSync(paPath)) {
534
+ console.log(`\n${cyan}${i18n.update_qa_header}${reset}`);
535
+ const answer = (await askQuestion(i18n.update_qa_ask)).trim().toLowerCase();
536
+ if (!['n', 'no', 'não', 'nao'].includes(answer)) {
537
+ activateQaAgent(paPath);
538
+ results.push(t(
539
+ '✅ QA Agent configured — Variant B activated\n Next: gh secret set GEMINI_API_KEY --body "<your-key>"',
540
+ '✅ QA Agent configurado — Variant B ativado\n Próximo: gh secret set GEMINI_API_KEY --body "<sua-chave>"',
541
+ '✅ QA Agent configurado — Variant B activado\n Siguiente: gh secret set GEMINI_API_KEY --body "<tu-clave>"'
542
+ ));
543
+ } else {
544
+ results.push(t('⏭ QA Agent — skipped', '⏭ QA Agent — pulado', '⏭ QA Agent — omitido'));
545
+ }
546
+ }
547
+
548
+ if (!state.sentinel && fs.existsSync(paPath)) {
549
+ console.log(`\n${cyan}${i18n.update_sentinel_header}${reset}`);
550
+ const answer = (await askQuestion(i18n.update_sentinel_ask)).trim().toLowerCase();
551
+ if (!['n', 'no', 'não', 'nao'].includes(answer)) {
552
+ activateSentinel(paPath);
553
+ results.push(t('✅ Sentinel configured', '✅ Sentinel configurado', '✅ Sentinel configurado'));
554
+ } else {
555
+ results.push(t('⏭ Sentinel — skipped', '⏭ Sentinel — pulado', '⏭ Sentinel — omitido'));
556
+ }
557
+ }
558
+
559
+ console.log(`\n${cyan}${sep}${reset}`);
560
+ for (const r of results) console.log(` ${r}`);
561
+ console.log(`${cyan}${sep}${reset}\n`);
562
+
563
+ rl.close();
564
+ }
565
+
566
+ // ─── Entry point ──────────────────────────────────────────────────────────────
567
+
568
+ const args = process.argv.slice(2);
569
+ if (args.includes('--update')) {
570
+ runUpdate().catch(err => { console.error(err.message); rl.close(); process.exit(1); });
571
+ } else {
572
+ runSetup().catch(err => { console.error(err.message); rl.close(); process.exit(1); });
573
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-agentic-pdlc",
3
- "version": "2.1.4",
3
+ "version": "2.1.5",
4
4
  "description": "Agentic PDLC Framework - Conversational setup for your AI coding assistants",
5
5
  "type": "commonjs",
6
6
  "bin": {