@ryuenn3123/agentic-senior-core 2.0.25 → 2.0.27

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 (37) hide show
  1. package/.agent-context/review-checklists/frontend-excellence-rubric.md +54 -0
  2. package/.agent-context/review-checklists/frontend-skill-parity.md +1 -0
  3. package/.agent-context/review-checklists/frontend-usability.md +1 -0
  4. package/.agent-context/rules/docker-runtime.md +29 -0
  5. package/.agent-context/skills/frontend/README.md +1 -0
  6. package/.agent-context/skills/frontend.md +4 -0
  7. package/.agent-context/state/benchmark-evidence-bundle.json +672 -22
  8. package/.agent-context/state/benchmark-history.json +75 -0
  9. package/.agent-context/state/benchmark-trend-report.csv +5 -0
  10. package/.agent-context/state/benchmark-trend-report.json +140 -0
  11. package/.agent-context/state/benchmark-watchlist.json +3 -3
  12. package/.agent-context/state/memory-adapter-contract.json +52 -0
  13. package/.agent-context/state/memory-continuity-benchmark.json +132 -0
  14. package/.agent-context/state/memory-schema-v1.json +88 -0
  15. package/.cursorrules +1 -1
  16. package/.windsurfrules +1 -1
  17. package/README.md +29 -0
  18. package/lib/cli/commands/init.mjs +358 -16
  19. package/lib/cli/commands/optimize.mjs +12 -0
  20. package/lib/cli/commands/upgrade.mjs +30 -1
  21. package/lib/cli/compiler.mjs +55 -1
  22. package/lib/cli/constants.mjs +83 -0
  23. package/lib/cli/detector.mjs +11 -1
  24. package/lib/cli/memory-continuity.mjs +266 -0
  25. package/lib/cli/project-scaffolder.mjs +174 -1
  26. package/lib/cli/skill-selector.mjs +60 -38
  27. package/lib/cli/templates/architecture-decision-record.md.tmpl +39 -0
  28. package/lib/cli/templates/flow-overview.md.tmpl +12 -0
  29. package/lib/cli/templates/project-brief.md.id.tmpl +2 -0
  30. package/lib/cli/templates/project-brief.md.tmpl +26 -0
  31. package/lib/cli/utils.mjs +2 -1
  32. package/package.json +2 -1
  33. package/scripts/benchmark-evidence-bundle.mjs +493 -16
  34. package/scripts/frontend-usability-audit.mjs +21 -0
  35. package/scripts/memory-continuity-benchmark.mjs +322 -0
  36. package/scripts/release-gate.mjs +30 -0
  37. package/scripts/validate.mjs +5 -0
@@ -12,6 +12,13 @@ import {
12
12
  INIT_PRESETS,
13
13
  PROFILE_PRESETS,
14
14
  BLUEPRINT_RECOMMENDATIONS,
15
+ PROJECT_SCOPE_CHOICES,
16
+ PROJECT_SCOPE_STACK_FILTERS,
17
+ WEB_FRONTEND_STACK_CANDIDATES,
18
+ WEB_BACKEND_STACK_CANDIDATES,
19
+ WEB_FRONTEND_BLUEPRINT_CANDIDATES,
20
+ WEB_BACKEND_BLUEPRINT_CANDIDATES,
21
+ RUNTIME_ENVIRONMENT_CHOICES,
15
22
  } from '../constants.mjs';
16
23
 
17
24
  import {
@@ -70,6 +77,8 @@ export function parseInitArguments(commandArguments) {
70
77
  docsLang: 'en',
71
78
  docsLangProvided: false,
72
79
  projectConfig: undefined,
80
+ runtimeEnv: 'auto',
81
+ runtimeEnvProvided: false,
73
82
  };
74
83
 
75
84
  for (let argumentIndex = 0; argumentIndex < commandArguments.length; argumentIndex++) {
@@ -212,6 +221,19 @@ export function parseInitArguments(commandArguments) {
212
221
  continue;
213
222
  }
214
223
 
224
+ if (currentArgument === '--runtime-env') {
225
+ parsedInitOptions.runtimeEnv = commandArguments[argumentIndex + 1] || 'auto';
226
+ parsedInitOptions.runtimeEnvProvided = true;
227
+ argumentIndex += 1;
228
+ continue;
229
+ }
230
+
231
+ if (currentArgument.startsWith('--runtime-env=')) {
232
+ parsedInitOptions.runtimeEnv = currentArgument.split('=')[1] || 'auto';
233
+ parsedInitOptions.runtimeEnvProvided = true;
234
+ continue;
235
+ }
236
+
215
237
  throw new Error(`Unknown option: ${currentArgument}`);
216
238
  }
217
239
 
@@ -224,12 +246,163 @@ export function parseInitArguments(commandArguments) {
224
246
  throw new Error('--docs-lang must be one of: en, id');
225
247
  }
226
248
 
249
+ const normalizedRuntimeEnvironment = normalizeRuntimeEnvironmentKey(parsedInitOptions.runtimeEnv || 'auto');
250
+ if (!normalizedRuntimeEnvironment) {
251
+ throw new Error('--runtime-env must be one of: auto, linux-wsl, linux, windows, macos');
252
+ }
253
+
227
254
  parsedInitOptions.docsLang = normalizedDocsLanguage;
255
+ parsedInitOptions.runtimeEnv = normalizedRuntimeEnvironment;
228
256
  parsedInitOptions.tokenAgent = normalizeAgentName(parsedInitOptions.tokenAgent);
229
257
 
230
258
  return parsedInitOptions;
231
259
  }
232
260
 
261
+ export function filterStackFileNamesByCandidates(allStackFileNames, preferredStackFileNames) {
262
+ if (!Array.isArray(preferredStackFileNames) || preferredStackFileNames.length === 0) {
263
+ return allStackFileNames;
264
+ }
265
+
266
+ const filteredStackFileNames = preferredStackFileNames.filter((stackFileName) => allStackFileNames.includes(stackFileName));
267
+ return filteredStackFileNames.length > 0 ? filteredStackFileNames : allStackFileNames;
268
+ }
269
+
270
+ export function filterBlueprintFileNamesByCandidates(allBlueprintFileNames, preferredBlueprintFileNames) {
271
+ if (!Array.isArray(preferredBlueprintFileNames) || preferredBlueprintFileNames.length === 0) {
272
+ return allBlueprintFileNames;
273
+ }
274
+
275
+ const filteredBlueprintFileNames = preferredBlueprintFileNames.filter(
276
+ (blueprintFileName) => allBlueprintFileNames.includes(blueprintFileName)
277
+ );
278
+
279
+ return filteredBlueprintFileNames.length > 0 ? filteredBlueprintFileNames : allBlueprintFileNames;
280
+ }
281
+
282
+ export function normalizeRuntimeEnvironmentKey(rawRuntimeEnvironmentKey) {
283
+ const normalizedRuntimeEnvironmentKey = normalizeChoiceInput(String(rawRuntimeEnvironmentKey || 'auto'));
284
+ const supportedRuntimeEnvironmentKeys = new Set(['auto', 'linux-wsl', 'linux', 'windows', 'macos']);
285
+ return supportedRuntimeEnvironmentKeys.has(normalizedRuntimeEnvironmentKey) ? normalizedRuntimeEnvironmentKey : null;
286
+ }
287
+
288
+ export function resolveRuntimeEnvironmentKeyFromLabel(selectedRuntimeEnvironmentLabel) {
289
+ const runtimeEnvironmentEntry = RUNTIME_ENVIRONMENT_CHOICES.find(
290
+ (runtimeEnvironmentChoice) => runtimeEnvironmentChoice.label === selectedRuntimeEnvironmentLabel
291
+ );
292
+
293
+ return runtimeEnvironmentEntry?.key || null;
294
+ }
295
+
296
+ export function resolveRuntimeEnvironmentLabelFromKey(runtimeEnvironmentKey) {
297
+ const runtimeEnvironmentEntry = RUNTIME_ENVIRONMENT_CHOICES.find(
298
+ (runtimeEnvironmentChoice) => runtimeEnvironmentChoice.key === runtimeEnvironmentKey
299
+ );
300
+
301
+ return runtimeEnvironmentEntry?.label || runtimeEnvironmentKey;
302
+ }
303
+
304
+ export function detectRuntimeEnvironment() {
305
+ const isWslEnvironment = Boolean(process.env.WSL_DISTRO_NAME || process.env.WSL_INTEROP || process.env.__IS_WSL_TEST__);
306
+
307
+ if (isWslEnvironment) {
308
+ return {
309
+ key: 'linux-wsl',
310
+ label: resolveRuntimeEnvironmentLabelFromKey('linux-wsl'),
311
+ shellFamily: 'bash',
312
+ isAutoDetected: true,
313
+ source: 'WSL environment markers',
314
+ };
315
+ }
316
+
317
+ if (process.platform === 'win32') {
318
+ return {
319
+ key: 'windows',
320
+ label: resolveRuntimeEnvironmentLabelFromKey('windows'),
321
+ shellFamily: 'powershell',
322
+ isAutoDetected: true,
323
+ source: 'process.platform',
324
+ };
325
+ }
326
+
327
+ if (process.platform === 'darwin') {
328
+ return {
329
+ key: 'macos',
330
+ label: resolveRuntimeEnvironmentLabelFromKey('macos'),
331
+ shellFamily: 'bash',
332
+ isAutoDetected: true,
333
+ source: 'process.platform',
334
+ };
335
+ }
336
+
337
+ return {
338
+ key: 'linux',
339
+ label: resolveRuntimeEnvironmentLabelFromKey('linux'),
340
+ shellFamily: 'bash',
341
+ isAutoDetected: true,
342
+ source: 'process.platform',
343
+ };
344
+ }
345
+
346
+ export function resolveProjectScopeKeyFromLabel(selectedProjectScopeLabel) {
347
+ const projectScopeEntry = PROJECT_SCOPE_CHOICES.find((scopeChoice) => scopeChoice.label === selectedProjectScopeLabel);
348
+ return projectScopeEntry?.key || 'other';
349
+ }
350
+
351
+ export function normalizeAdditionalStackSelection(selectedStackFileName, additionalStackFileNames) {
352
+ if (!Array.isArray(additionalStackFileNames) || additionalStackFileNames.length === 0) {
353
+ return [];
354
+ }
355
+
356
+ return Array.from(new Set(additionalStackFileNames.filter((stackFileName) => stackFileName && stackFileName !== selectedStackFileName)));
357
+ }
358
+
359
+ export function normalizeAdditionalBlueprintSelection(selectedBlueprintFileName, additionalBlueprintFileNames) {
360
+ if (!Array.isArray(additionalBlueprintFileNames) || additionalBlueprintFileNames.length === 0) {
361
+ return [];
362
+ }
363
+
364
+ return Array.from(new Set(additionalBlueprintFileNames.filter(
365
+ (blueprintFileName) => blueprintFileName && blueprintFileName !== selectedBlueprintFileName
366
+ )));
367
+ }
368
+
369
+ function deriveAdditionalBlueprintFileNamesFromStacks(additionalStackFileNames, allBlueprintFileNames, selectedBlueprintFileName) {
370
+ const derivedBlueprintFileNames = [];
371
+
372
+ for (const additionalStackFileName of additionalStackFileNames || []) {
373
+ const mappedBlueprintFileName = BLUEPRINT_RECOMMENDATIONS[additionalStackFileName];
374
+ if (!mappedBlueprintFileName) {
375
+ continue;
376
+ }
377
+
378
+ if (!allBlueprintFileNames.includes(mappedBlueprintFileName)) {
379
+ continue;
380
+ }
381
+
382
+ if (mappedBlueprintFileName === selectedBlueprintFileName) {
383
+ continue;
384
+ }
385
+
386
+ derivedBlueprintFileNames.push(mappedBlueprintFileName);
387
+ }
388
+
389
+ return Array.from(new Set(derivedBlueprintFileNames));
390
+ }
391
+
392
+ async function askStackSelection(promptMessage, selectableStackFileNames, userInterface) {
393
+ const stackDisplayChoices = selectableStackFileNames.map((stackFileName) => toTitleCase(stackFileName));
394
+ const selectedDisplayChoice = await askChoice(promptMessage, stackDisplayChoices, userInterface);
395
+ const selectedIndex = stackDisplayChoices.indexOf(selectedDisplayChoice);
396
+ return selectableStackFileNames[selectedIndex] || selectableStackFileNames[0] || null;
397
+ }
398
+
399
+ async function askBlueprintSelection(promptMessage, selectableBlueprintFileNames, userInterface) {
400
+ const blueprintDisplayChoices = selectableBlueprintFileNames.map((blueprintFileName) => toTitleCase(blueprintFileName));
401
+ const selectedDisplayChoice = await askChoice(promptMessage, blueprintDisplayChoices, userInterface);
402
+ const selectedIndex = blueprintDisplayChoices.indexOf(selectedDisplayChoice);
403
+ return selectableBlueprintFileNames[selectedIndex] || selectableBlueprintFileNames[0] || null;
404
+ }
405
+
233
406
  export async function runInitCommand(targetDirectoryArgument, initOptions = {}) {
234
407
  const resolvedTargetDirectoryPath = path.resolve(targetDirectoryArgument || '.');
235
408
  const isTokenOptimizationEnabled = typeof initOptions.tokenOptimize === 'boolean'
@@ -326,6 +499,30 @@ export async function runInitCommand(targetDirectoryArgument, initOptions = {})
326
499
  console.log('The target directory is empty, so I will guide you through a fresh setup.');
327
500
  }
328
501
 
502
+ const detectedRuntimeEnvironment = detectRuntimeEnvironment();
503
+ let selectedRuntimeEnvironmentKey = initOptions.runtimeEnv === 'auto'
504
+ ? detectedRuntimeEnvironment.key
505
+ : initOptions.runtimeEnv;
506
+
507
+ if (isInteractiveSession && !initOptions.runtimeEnvProvided) {
508
+ const defaultRuntimeEnvironmentLabel = resolveRuntimeEnvironmentLabelFromKey(selectedRuntimeEnvironmentKey);
509
+ const orderedRuntimeEnvironmentChoices = [
510
+ defaultRuntimeEnvironmentLabel,
511
+ ...RUNTIME_ENVIRONMENT_CHOICES
512
+ .map((runtimeEnvironmentChoice) => runtimeEnvironmentChoice.label)
513
+ .filter((runtimeEnvironmentLabel) => runtimeEnvironmentLabel !== defaultRuntimeEnvironmentLabel),
514
+ ];
515
+
516
+ const selectedRuntimeEnvironmentLabel = await askChoice(
517
+ `Runtime environment (auto-detected: ${defaultRuntimeEnvironmentLabel}).`,
518
+ orderedRuntimeEnvironmentChoices,
519
+ userInterface
520
+ );
521
+
522
+ selectedRuntimeEnvironmentKey = resolveRuntimeEnvironmentKeyFromLabel(selectedRuntimeEnvironmentLabel)
523
+ || selectedRuntimeEnvironmentKey;
524
+ }
525
+
329
526
  const selectedProfileName = initOptions.profile
330
527
  ? initOptions.profile
331
528
  : selectedPreset?.profile
@@ -353,29 +550,130 @@ export async function runInitCommand(targetDirectoryArgument, initOptions = {})
353
550
  console.log(`Pack defaults: stack ${toTitleCase(selectedProfilePack.defaultStackFileName)}, blueprint ${toTitleCase(selectedProfilePack.defaultBlueprintFileName)}.`);
354
551
  }
355
552
 
356
- const shouldApplyDetectedStack = projectDetection.recommendedStackFileName && !selectedStackFileNameFromOption
357
- ? await askYesNo(
358
- `Use the detected stack recommendation (${toTitleCase(projectDetection.recommendedStackFileName)})?`,
359
- userInterface,
360
- projectDetection.confidenceLabel === 'high'
361
- )
362
- : false;
553
+ const hasReliableDetectedStack = projectDetection.hasExistingProjectFiles
554
+ && projectDetection.recommendedStackFileName
555
+ && (projectDetection.confidenceLabel === 'high' || projectDetection.confidenceLabel === 'medium');
556
+
557
+ const shouldAutoApplyDetectedStack = hasReliableDetectedStack
558
+ && !selectedStackFileNameFromOption
559
+ && !selectedPreset?.stack
560
+ && !selectedProfilePack?.defaultStackFileName;
561
+
562
+ let selectedManualStackFileName = null;
563
+ let selectedManualBlueprintFileName = null;
564
+ let selectedAdditionalStackFileNames = [];
565
+ let selectedAdditionalBlueprintFileNames = [];
566
+
567
+ const shouldAskManualStackSelection = !selectedStackFileNameFromOption
568
+ && !selectedPreset?.stack
569
+ && !shouldAutoApplyDetectedStack
570
+ && !selectedProfilePack?.defaultStackFileName
571
+ && !selectedProfile.defaultStackFileName;
572
+
573
+ if (shouldAutoApplyDetectedStack) {
574
+ console.log(`Using detected stack automatically for this existing project: ${toTitleCase(projectDetection.recommendedStackFileName)}.`);
575
+ if (projectDetection.secondaryStackFileNames?.length) {
576
+ console.log(`Detected additional stack signals: ${projectDetection.secondaryStackFileNames.map((stackFileName) => toTitleCase(stackFileName)).join(', ')}.`);
577
+ }
578
+ selectedAdditionalStackFileNames = projectDetection.secondaryStackFileNames || [];
579
+ }
580
+
581
+ if (shouldAskManualStackSelection) {
582
+ const selectedProjectScopeLabel = await askChoice(
583
+ 'What are you building?',
584
+ PROJECT_SCOPE_CHOICES.map((scopeChoice) => scopeChoice.label),
585
+ userInterface
586
+ );
587
+ const selectedProjectScopeKey = resolveProjectScopeKeyFromLabel(selectedProjectScopeLabel);
588
+ const scopeStackFilter = PROJECT_SCOPE_STACK_FILTERS[selectedProjectScopeKey];
589
+ const scopedStackFileNames = filterStackFileNamesByCandidates(stackFileNames, scopeStackFilter);
590
+
591
+ if (selectedProjectScopeKey === 'web-application') {
592
+ const usesSplitWebStacks = await askYesNo(
593
+ 'For this web project, do frontend and backend use different primary stacks?',
594
+ userInterface,
595
+ false
596
+ );
597
+
598
+ if (usesSplitWebStacks) {
599
+ const selectableFrontendStacks = filterStackFileNamesByCandidates(scopedStackFileNames, WEB_FRONTEND_STACK_CANDIDATES);
600
+ const selectableBackendStacks = filterStackFileNamesByCandidates(scopedStackFileNames, WEB_BACKEND_STACK_CANDIDATES);
601
+
602
+ const selectedFrontendStackFileName = await askStackSelection(
603
+ 'Which frontend stack should this governance pack target?',
604
+ selectableFrontendStacks,
605
+ userInterface
606
+ );
607
+ const selectedBackendStackFileName = await askStackSelection(
608
+ 'Which backend stack should this governance pack target?',
609
+ selectableBackendStacks,
610
+ userInterface
611
+ );
612
+
613
+ selectedManualStackFileName = selectedBackendStackFileName;
614
+ if (selectedFrontendStackFileName && selectedFrontendStackFileName !== selectedBackendStackFileName) {
615
+ selectedAdditionalStackFileNames = [selectedFrontendStackFileName];
616
+ }
617
+
618
+ if (!selectedBlueprintFileNameFromOption && !selectedPreset?.blueprint) {
619
+ const selectableFrontendBlueprints = filterBlueprintFileNamesByCandidates(
620
+ blueprintFileNames,
621
+ WEB_FRONTEND_BLUEPRINT_CANDIDATES
622
+ );
623
+ const selectableBackendBlueprints = filterBlueprintFileNamesByCandidates(
624
+ blueprintFileNames,
625
+ WEB_BACKEND_BLUEPRINT_CANDIDATES
626
+ );
627
+
628
+ const selectedFrontendBlueprintFileName = await askBlueprintSelection(
629
+ 'Which frontend blueprint should guide UI architecture?',
630
+ selectableFrontendBlueprints,
631
+ userInterface
632
+ );
633
+ const selectedBackendBlueprintFileName = await askBlueprintSelection(
634
+ 'Which backend blueprint should guide service architecture?',
635
+ selectableBackendBlueprints,
636
+ userInterface
637
+ );
638
+
639
+ selectedManualBlueprintFileName = selectedBackendBlueprintFileName;
640
+ if (selectedFrontendBlueprintFileName && selectedFrontendBlueprintFileName !== selectedBackendBlueprintFileName) {
641
+ selectedAdditionalBlueprintFileNames = [selectedFrontendBlueprintFileName];
642
+ }
643
+ }
644
+ } else {
645
+ selectedManualStackFileName = await askStackSelection(
646
+ 'Which stack should this governance pack target?',
647
+ scopedStackFileNames,
648
+ userInterface
649
+ );
650
+ }
651
+ } else {
652
+ selectedManualStackFileName = await askStackSelection(
653
+ 'Which stack should this governance pack target?',
654
+ scopedStackFileNames,
655
+ userInterface
656
+ );
657
+ }
658
+ }
363
659
 
364
- const stackDisplayChoices = stackFileNames.map((stackFileName) => toTitleCase(stackFileName));
365
660
  const blueprintDisplayChoices = blueprintFileNames.map((blueprintFileName) => toTitleCase(blueprintFileName));
366
661
 
367
662
  const selectedResolvedStackFileName = selectedStackFileNameFromOption
368
663
  || selectedPreset?.stack
369
- || (shouldApplyDetectedStack ? projectDetection.recommendedStackFileName : null)
664
+ || (shouldAutoApplyDetectedStack ? projectDetection.recommendedStackFileName : null)
370
665
  || selectedProfilePack?.defaultStackFileName
371
666
  || selectedProfile.defaultStackFileName
372
- || stackFileNames[
373
- stackDisplayChoices.indexOf(
374
- await askChoice('Which stack should this governance pack target?', stackDisplayChoices, userInterface)
375
- )
376
- ];
667
+ || selectedManualStackFileName
668
+ || stackFileNames[0];
377
669
 
378
- const recommendedBlueprintFileName = shouldApplyDetectedStack
670
+ selectedAdditionalStackFileNames = normalizeAdditionalStackSelection(
671
+ selectedResolvedStackFileName,
672
+ selectedAdditionalStackFileNames
673
+ );
674
+
675
+ const recommendedBlueprintFileName = projectDetection.recommendedBlueprintFileName
676
+ && shouldAutoApplyDetectedStack
379
677
  ? projectDetection.recommendedBlueprintFileName
380
678
  : BLUEPRINT_RECOMMENDATIONS[selectedResolvedStackFileName] || null;
381
679
 
@@ -385,6 +683,7 @@ export async function runInitCommand(targetDirectoryArgument, initOptions = {})
385
683
 
386
684
  const selectedResolvedBlueprintFileName = selectedBlueprintFileNameFromOption
387
685
  || selectedPreset?.blueprint
686
+ || selectedManualBlueprintFileName
388
687
  || recommendedBlueprintFileName
389
688
  || selectedProfilePack?.defaultBlueprintFileName
390
689
  || selectedProfile.defaultBlueprintFileName
@@ -394,9 +693,24 @@ export async function runInitCommand(targetDirectoryArgument, initOptions = {})
394
693
  )
395
694
  ];
396
695
 
696
+ const derivedAdditionalBlueprintFileNames = deriveAdditionalBlueprintFileNamesFromStacks(
697
+ selectedAdditionalStackFileNames,
698
+ blueprintFileNames,
699
+ selectedResolvedBlueprintFileName
700
+ );
701
+
702
+ selectedAdditionalBlueprintFileNames = normalizeAdditionalBlueprintSelection(
703
+ selectedResolvedBlueprintFileName,
704
+ selectedAdditionalBlueprintFileNames.length > 0
705
+ ? selectedAdditionalBlueprintFileNames
706
+ : derivedAdditionalBlueprintFileNames
707
+ );
708
+
397
709
  const selectedSkillDomainNames = inferSkillDomainNamesFromSelection(
398
710
  selectedResolvedStackFileName,
399
- selectedResolvedBlueprintFileName
711
+ selectedResolvedBlueprintFileName,
712
+ selectedAdditionalStackFileNames,
713
+ selectedAdditionalBlueprintFileNames
400
714
  );
401
715
 
402
716
  const includeCiGuardrails = typeof initOptions.ci === 'boolean'
@@ -494,7 +808,11 @@ export async function runInitCommand(targetDirectoryArgument, initOptions = {})
494
808
  discoveryAnswers,
495
809
  {
496
810
  stackFileName: selectedResolvedStackFileName,
811
+ additionalStackFileNames: selectedAdditionalStackFileNames,
497
812
  blueprintFileName: selectedResolvedBlueprintFileName,
813
+ additionalBlueprintFileNames: selectedAdditionalBlueprintFileNames,
814
+ runtimeEnvironmentKey: selectedRuntimeEnvironmentKey,
815
+ runtimeEnvironmentLabel: resolveRuntimeEnvironmentLabelFromKey(selectedRuntimeEnvironmentKey),
498
816
  },
499
817
  {
500
818
  docsLanguage: selectedDocsLanguage,
@@ -513,7 +831,9 @@ export async function runInitCommand(targetDirectoryArgument, initOptions = {})
513
831
  selectedProfileName,
514
832
  selectedProfilePack,
515
833
  selectedStackFileName: selectedResolvedStackFileName,
834
+ selectedAdditionalStackFileNames,
516
835
  selectedBlueprintFileName: selectedResolvedBlueprintFileName,
836
+ selectedAdditionalBlueprintFileNames,
517
837
  includeCiGuardrails,
518
838
  });
519
839
 
@@ -526,12 +846,21 @@ export async function runInitCommand(targetDirectoryArgument, initOptions = {})
526
846
  selectedProfilePack,
527
847
  selectedPreset: initOptions.preset || null,
528
848
  selectedStackFileName: selectedResolvedStackFileName,
849
+ selectedAdditionalStackFileNames,
529
850
  selectedBlueprintFileName: selectedResolvedBlueprintFileName,
851
+ selectedAdditionalBlueprintFileNames,
530
852
  includeCiGuardrails,
531
853
  setupDurationMs,
532
854
  projectDetection,
533
855
  selectedSkillDomains: selectedSkillDomainNames,
534
856
  compatibilityWarnings,
857
+ runtimeEnvironment: {
858
+ selected: selectedRuntimeEnvironmentKey,
859
+ selectedLabel: resolveRuntimeEnvironmentLabelFromKey(selectedRuntimeEnvironmentKey),
860
+ detected: detectedRuntimeEnvironment.key,
861
+ detectedLabel: detectedRuntimeEnvironment.label,
862
+ detectionSource: detectedRuntimeEnvironment.source,
863
+ },
535
864
  operationMode: 'init',
536
865
  });
537
866
 
@@ -545,7 +874,14 @@ export async function runInitCommand(targetDirectoryArgument, initOptions = {})
545
874
  console.log(`- Team profile pack: ${selectedProfilePack.displayName}`);
546
875
  }
547
876
  console.log(`- Stack: ${toTitleCase(selectedResolvedStackFileName)}`);
877
+ if (selectedAdditionalStackFileNames.length > 0) {
878
+ console.log(`- Additional stacks: ${selectedAdditionalStackFileNames.map((stackFileName) => toTitleCase(stackFileName)).join(', ')}`);
879
+ }
548
880
  console.log(`- Blueprint: ${toTitleCase(selectedResolvedBlueprintFileName)}`);
881
+ if (selectedAdditionalBlueprintFileNames.length > 0) {
882
+ console.log(`- Additional blueprints: ${selectedAdditionalBlueprintFileNames.map((blueprintFileName) => toTitleCase(blueprintFileName)).join(', ')}`);
883
+ }
884
+ console.log(`- Runtime environment: ${resolveRuntimeEnvironmentLabelFromKey(selectedRuntimeEnvironmentKey)} (detected: ${detectedRuntimeEnvironment.label})`);
549
885
  console.log(`- CI/CD guardrails: ${includeCiGuardrails ? 'enabled' : 'disabled'}`);
550
886
  console.log(`- Blocking severities: ${formatBlockingSeverities(selectedProfile.blockingSeverities)}`);
551
887
  console.log(`- Setup time: ${formatDuration(setupDurationMs)}`);
@@ -563,6 +899,12 @@ export async function runInitCommand(targetDirectoryArgument, initOptions = {})
563
899
  }
564
900
  console.log('\nPlain-language summary:');
565
901
  console.log(`I prepared a ${selectedProfile.displayName.toLowerCase()} governance pack for a ${toTitleCase(selectedResolvedStackFileName)} project using the ${toTitleCase(selectedResolvedBlueprintFileName)} blueprint.`);
902
+ if (selectedAdditionalStackFileNames.length > 0) {
903
+ console.log(`I also included additional stack context for ${selectedAdditionalStackFileNames.map((stackFileName) => toTitleCase(stackFileName)).join(', ')}.`);
904
+ }
905
+ if (selectedAdditionalBlueprintFileNames.length > 0) {
906
+ console.log(`I also included additional blueprint context for ${selectedAdditionalBlueprintFileNames.map((blueprintFileName) => toTitleCase(blueprintFileName)).join(', ')}.`);
907
+ }
566
908
  if (scaffoldingResult) {
567
909
  console.log(`I also generated project documentation (${scaffoldingResult.docsLanguage}) based on your project description. AI agents will use docs/ as project context.`);
568
910
 
@@ -119,7 +119,17 @@ export async function runOptimizeCommand(targetDirectoryArgument, optimizeOption
119
119
  ? onboardingReport.selectedProfile
120
120
  : 'balanced';
121
121
  const selectedStackFileName = normalizeMarkdownFileName(onboardingReport.selectedStack, 'typescript.md');
122
+ const selectedAdditionalStackFileNames = Array.isArray(onboardingReport.selectedAdditionalStacks)
123
+ ? onboardingReport.selectedAdditionalStacks
124
+ .map((stackFileName) => normalizeMarkdownFileName(stackFileName, ''))
125
+ .filter((stackFileName) => stackFileName && stackFileName !== selectedStackFileName)
126
+ : [];
122
127
  const selectedBlueprintFileName = normalizeMarkdownFileName(onboardingReport.selectedBlueprint, 'api-nextjs.md');
128
+ const selectedAdditionalBlueprintFileNames = Array.isArray(onboardingReport.selectedAdditionalBlueprints)
129
+ ? onboardingReport.selectedAdditionalBlueprints
130
+ .map((blueprintFileName) => normalizeMarkdownFileName(blueprintFileName, ''))
131
+ .filter((blueprintFileName) => blueprintFileName && blueprintFileName !== selectedBlueprintFileName)
132
+ : [];
123
133
  const includeCiGuardrails = typeof onboardingReport.ciGuardrailsEnabled === 'boolean'
124
134
  ? onboardingReport.ciGuardrailsEnabled
125
135
  : true;
@@ -128,7 +138,9 @@ export async function runOptimizeCommand(targetDirectoryArgument, optimizeOption
128
138
  targetDirectoryPath: resolvedTargetDirectoryPath,
129
139
  selectedProfileName,
130
140
  selectedStackFileName,
141
+ selectedAdditionalStackFileNames,
131
142
  selectedBlueprintFileName,
143
+ selectedAdditionalBlueprintFileNames,
132
144
  includeCiGuardrails,
133
145
  });
134
146
 
@@ -119,17 +119,35 @@ export async function runUpgradeCommand(targetDirectoryArgument, upgradeOptions
119
119
  ? existingOnboardingReport.selectedStack
120
120
  : projectDetection.recommendedStackFileName || 'typescript.md';
121
121
 
122
+ const selectedAdditionalStackFileNames = Array.isArray(existingOnboardingReport?.selectedAdditionalStacks)
123
+ ? Array.from(new Set(existingOnboardingReport.selectedAdditionalStacks.filter(
124
+ (stackFileName) => stackFileNames.includes(stackFileName) && stackFileName !== selectedStackFileName
125
+ )))
126
+ : Array.from(new Set((projectDetection.secondaryStackFileNames || []).filter(
127
+ (stackFileName) => stackFileNames.includes(stackFileName) && stackFileName !== selectedStackFileName
128
+ )));
129
+
122
130
  const selectedBlueprintFileName = blueprintFileNames.includes(existingOnboardingReport?.selectedBlueprint)
123
131
  ? existingOnboardingReport.selectedBlueprint
124
132
  : BLUEPRINT_RECOMMENDATIONS[selectedStackFileName] || 'api-nextjs.md';
125
133
 
134
+ const selectedAdditionalBlueprintFileNames = Array.isArray(existingOnboardingReport?.selectedAdditionalBlueprints)
135
+ ? Array.from(new Set(existingOnboardingReport.selectedAdditionalBlueprints.filter(
136
+ (blueprintFileName) => blueprintFileNames.includes(blueprintFileName) && blueprintFileName !== selectedBlueprintFileName
137
+ )))
138
+ : Array.from(new Set(selectedAdditionalStackFileNames
139
+ .map((stackFileName) => BLUEPRINT_RECOMMENDATIONS[stackFileName] || null)
140
+ .filter((blueprintFileName) => blueprintFileName && blueprintFileName !== selectedBlueprintFileName)));
141
+
126
142
  const includeCiGuardrails = typeof existingOnboardingReport?.ciGuardrailsEnabled === 'boolean'
127
143
  ? existingOnboardingReport.ciGuardrailsEnabled
128
144
  : true;
129
145
 
130
146
  const selectedSkillDomainNames = inferSkillDomainNamesFromSelection(
131
147
  selectedStackFileName,
132
- selectedBlueprintFileName
148
+ selectedBlueprintFileName,
149
+ selectedAdditionalStackFileNames,
150
+ selectedAdditionalBlueprintFileNames
133
151
  );
134
152
  const compatibilityWarnings = await evaluateSkillDomainCompatibility(
135
153
  resolvedTargetDirectoryPath,
@@ -154,7 +172,9 @@ export async function runUpgradeCommand(targetDirectoryArgument, upgradeOptions
154
172
  targetDirectoryPath: resolvedTargetDirectoryPath,
155
173
  selectedProfileName,
156
174
  selectedStackFileName,
175
+ selectedAdditionalStackFileNames,
157
176
  selectedBlueprintFileName,
177
+ selectedAdditionalBlueprintFileNames,
158
178
  includeCiGuardrails,
159
179
  });
160
180
 
@@ -166,7 +186,13 @@ export async function runUpgradeCommand(targetDirectoryArgument, upgradeOptions
166
186
  console.log(`- Target directory: ${resolvedTargetDirectoryPath}`);
167
187
  console.log(`- Profile: ${toTitleCase(selectedProfileName)}`);
168
188
  console.log(`- Stack: ${toTitleCase(selectedStackFileName)}`);
189
+ if (selectedAdditionalStackFileNames.length > 0) {
190
+ console.log(`- Additional stacks: ${selectedAdditionalStackFileNames.map((stackFileName) => toTitleCase(stackFileName)).join(', ')}`);
191
+ }
169
192
  console.log(`- Blueprint: ${toTitleCase(selectedBlueprintFileName)}`);
193
+ if (selectedAdditionalBlueprintFileNames.length > 0) {
194
+ console.log(`- Additional blueprints: ${selectedAdditionalBlueprintFileNames.map((blueprintFileName) => toTitleCase(blueprintFileName)).join(', ')}`);
195
+ }
170
196
  console.log(`- CI/CD guardrails: ${includeCiGuardrails ? 'enabled' : 'disabled'}`);
171
197
  console.log(`- Existing rules lines: ${currentRuleLineCount}`);
172
198
  console.log(`- Planned rules lines: ${plannedRuleLineCount}`);
@@ -213,12 +239,15 @@ export async function runUpgradeCommand(targetDirectoryArgument, upgradeOptions
213
239
  selectedProfileName,
214
240
  selectedProfilePack: existingOnboardingReport?.selectedProfilePack || null,
215
241
  selectedStackFileName,
242
+ selectedAdditionalStackFileNames,
216
243
  selectedBlueprintFileName,
244
+ selectedAdditionalBlueprintFileNames,
217
245
  includeCiGuardrails,
218
246
  setupDurationMs,
219
247
  projectDetection,
220
248
  selectedSkillDomains: selectedSkillDomainNames,
221
249
  compatibilityWarnings,
250
+ runtimeEnvironment: existingOnboardingReport?.runtimeEnvironment || null,
222
251
  operationMode: 'upgrade',
223
252
  });
224
253