@ryuenn3123/agentic-senior-core 2.0.24 → 2.0.26

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.
@@ -0,0 +1,54 @@
1
+ # Frontend Excellence Rubric (Designer-Grade)
2
+
3
+ Use this rubric to prevent template-like UI output and enforce intentional product design quality.
4
+
5
+ ## Scoring Model
6
+
7
+ Score each dimension from 1 to 5.
8
+ - 1: weak or generic quality
9
+ - 3: acceptable production baseline
10
+ - 5: standout quality comparable to top manual design teams
11
+
12
+ Release recommendation:
13
+ - Minimum average score: 4.0
14
+ - No dimension below: 3
15
+
16
+ ## 1. Visual Direction and Identity
17
+ - [ ] The page has a clear visual direction, not a generic starter layout.
18
+ - [ ] Composition, rhythm, and hierarchy feel intentional across sections.
19
+ - [ ] The output avoids obvious design-template repetition.
20
+
21
+ ## 2. Typography Quality
22
+ - [ ] Font pairing is intentional and role-based (display, body, utility).
23
+ - [ ] Type scale is coherent across mobile and desktop.
24
+ - [ ] Line length, spacing, and emphasis improve readability and scanning.
25
+
26
+ ## 3. Color System Diversity and Contrast
27
+ - [ ] Color palette is purposeful, with semantic roles and contrast-safe pairings.
28
+ - [ ] The result is not a default AI palette or copied trendy scheme without adaptation.
29
+ - [ ] Accent usage supports product intent and interaction priority.
30
+
31
+ ## 4. Interaction Choreography
32
+ - [ ] Motion supports comprehension and hierarchy, not decorative noise.
33
+ - [ ] Entrance and transition timing are consistent and measured.
34
+ - [ ] Reduced-motion fallback preserves usability.
35
+
36
+ ## 5. Responsiveness and Layout Intelligence
37
+ - [ ] Mobile layout is designed, not desktop squeezed into a narrow viewport.
38
+ - [ ] Breakpoint transitions preserve hierarchy, spacing rhythm, and action clarity.
39
+ - [ ] Navigation and key CTA remain explicit across viewport sizes.
40
+
41
+ ## 6. UX Narrative and Conversion Clarity
42
+ - [ ] First viewport communicates value proposition and primary action immediately.
43
+ - [ ] Error, empty, and loading states provide clear next actions.
44
+ - [ ] User journey avoids dead ends and hidden critical actions.
45
+
46
+ ## Benchmark Expectation
47
+ - Treat MiniMax frontend references as baseline, not target ceiling.
48
+ - Target visual and interaction quality aligned with top award-grade manual design workflows (Awwwards-level reference quality).
49
+ - Prefer original composition and branded design systems over template cloning.
50
+
51
+ ## Evidence for Release
52
+ - [ ] Rubric scorecard attached to release artifact.
53
+ - [ ] Screenshot set across key breakpoints attached.
54
+ - [ ] Accessibility and performance evidence attached alongside rubric score.
@@ -25,4 +25,5 @@ Use this checklist to enforce mandatory frontend parity aligned with benchmark-d
25
25
  ## Release Evidence
26
26
  - [ ] Frontend parity checklist artifact is attached to release evidence.
27
27
  - [ ] Frontend usability audit report is attached to release evidence.
28
+ - [ ] Frontend excellence rubric scorecard is attached to release evidence.
28
29
  - [ ] Any parity waiver includes owner, expiry, and risk statement.
@@ -31,3 +31,4 @@ Run this checklist before claiming frontend work is production-ready.
31
31
  - [ ] Frontend architecture decision is documented.
32
32
  - [ ] Visual baseline update process is documented.
33
33
  - [ ] Release note includes usability and responsiveness evidence.
34
+ - [ ] Frontend excellence rubric scorecard is attached with release evidence.
@@ -0,0 +1,29 @@
1
+ # Docker Runtime Strategy (Dynamic Generation Required)
2
+
3
+ Use this rule when Docker is enabled in project context.
4
+
5
+ ## 1. Dynamic Generation Only
6
+ - Do not copy generic Docker templates blindly.
7
+ - Generate Docker assets based on actual stack, package manager, and runtime dependencies in the repository.
8
+ - Re-evaluate Docker instructions when dependencies, build tools, or runtime assumptions change.
9
+
10
+ ## 2. Separate Development and Production Lanes
11
+ - Development lane and production lane are separate concerns.
12
+ - Development lane priorities: fast rebuild, hot reload support, debugger-friendly startup, local volume strategy.
13
+ - Production lane priorities: minimal image size, reproducible build, non-root runtime, strict startup command.
14
+
15
+ ## 3. Security and Supply Chain
16
+ - Use minimal trusted base images with explicit versions.
17
+ - Use multi-stage builds for production images when possible.
18
+ - Avoid baking secrets into image layers.
19
+ - Keep runtime image free from build-only tooling.
20
+
21
+ ## 4. Operational Clarity
22
+ - Docker instructions must document expected entrypoint and exposed ports.
23
+ - Local development command and production deployment command must be explicit.
24
+ - If Docker is not selected for the project, do not force containerization tasks.
25
+
26
+ ## 5. Review Requirements
27
+ - Verify the generated Docker workflow matches selected runtime environment (Linux/WSL, Windows, macOS).
28
+ - Verify development and production instructions are not mixed into one unsafe image path.
29
+ - Ensure API and service health checks are compatible with container startup behavior.
@@ -16,6 +16,7 @@ The frontend domain covers component architecture, state management, performance
16
16
  - Anti-Slop Enforcer (ABOVE LINE) - Detect forbidden visual patterns and style drift
17
17
  - Accessibility Auditor (ABOVE LINE) - Detect contrast failures, ARIA issues, and keyboard navigation gaps
18
18
  - Performance Budget Enforcer (ABOVE LINE) - Bundle size gates and LCP/FID/CLS thresholds
19
+ - Frontend Excellence Rubric (ABOVE LINE) - Designer-grade scoring for visual direction, typography, color diversity, and interaction choreography
19
20
 
20
21
  ## Recommended Reading Order
21
22
 
@@ -4,6 +4,7 @@ Default tier: `advance`
4
4
 
5
5
  ## Purpose
6
6
  Deliver frontend experiences that are visually intentional, accessible, and efficient.
7
+ Target designer-grade quality comparable to high-signal manual design workflows.
7
8
 
8
9
  ## In Scope
9
10
  - UI architecture and component composition
@@ -21,6 +22,8 @@ Deliver frontend experiences that are visually intentional, accessible, and effi
21
22
  - Reduced-motion fallback
22
23
  - Accessibility baseline pass
23
24
  - Responsive behavior verified on mobile and desktop
25
+ - Visual direction is explicit and not a generic AI template pattern
26
+ - Typography and color system choices are intentional and contrast-safe
24
27
  - First viewport communicates value proposition and primary action clearly
25
28
  - Error and empty states provide explicit next actions
26
29
  - Performance budget and regression thresholds are documented per release
@@ -29,6 +32,7 @@ Deliver frontend experiences that are visually intentional, accessible, and effi
29
32
  - Usability audit result
30
33
  - Visual regression output
31
34
  - Accessibility checklist result
35
+ - Frontend excellence rubric scorecard
32
36
  - Release notes for motion and interaction changes
33
37
  - Performance trend snapshot and remediation notes when regressions occur
34
38
 
package/.cursorrules CHANGED
@@ -1,6 +1,6 @@
1
1
  # AGENTIC-SENIOR-CORE DYNAMIC GOVERNANCE RULESET
2
2
 
3
- Generated by Agentic-Senior-Core CLI v2.0.24
3
+ Generated by Agentic-Senior-Core CLI v2.0.26
4
4
  Timestamp: 2026-04-15T00:14:51.184Z
5
5
  Selected profile: beginner
6
6
  Selected policy file: .agent-context/policies/llm-judge-threshold.json
@@ -30,9 +30,16 @@ jobs:
30
30
  node ./scripts/detection-benchmark.mjs > detection-benchmark-report.json
31
31
  test -s detection-benchmark-report.json
32
32
 
33
+ - name: Run benchmark anti-regression gate
34
+ run: |
35
+ node ./scripts/benchmark-gate.mjs > benchmark-gate-report.json
36
+ test -s benchmark-gate-report.json
37
+
33
38
  - name: Upload benchmark artifact
34
39
  if: always()
35
40
  uses: actions/upload-artifact@v4
36
41
  with:
37
42
  name: detection-benchmark-report
38
- path: detection-benchmark-report.json
43
+ path: |
44
+ detection-benchmark-report.json
45
+ benchmark-gate-report.json
package/.windsurfrules CHANGED
@@ -1,6 +1,6 @@
1
1
  # AGENTIC-SENIOR-CORE DYNAMIC GOVERNANCE RULESET
2
2
 
3
- Generated by Agentic-Senior-Core CLI v2.0.24
3
+ Generated by Agentic-Senior-Core CLI v2.0.26
4
4
  Timestamp: 2026-04-15T00:14:51.184Z
5
5
  Selected profile: beginner
6
6
  Selected policy file: .agent-context/policies/llm-judge-threshold.json
package/README.md CHANGED
@@ -142,6 +142,13 @@ npx @ryuenn3123/agentic-senior-core init --newbie
142
142
 
143
143
  - `init` creates governance files **in your project folder** (the folder where you run the command).
144
144
  - `init` does not copy repository workflows from this project into your target repository.
145
+ - For fresh projects, `init` asks what you are building first (API, web, mobile, CLI, library) and filters stack choices to match that scope.
146
+ - For mobile scope, stack choices are limited to mobile-relevant options (`react-native`, `flutter`).
147
+ - For existing projects, `init` auto-applies detected stack signals (including additional stack signals for polyglot repositories) so you do not need to re-select language manually.
148
+ - For web projects, `init` can capture separate frontend and backend stacks plus separate frontend/backend blueprints, and keeps this dual architecture context in the onboarding report.
149
+ - `init` detects runtime environment (Linux/WSL, Windows, macOS) and supports explicit override with `--runtime-env`.
150
+ - Project discovery now captures Docker strategy (none, development-only, production-only, both) and keeps development and production container intent separated.
151
+ - Docker setup is expected to be generated dynamically by AI from real project context, not fixed static templates.
145
152
  - `init` project discovery accepts answers in any language; prompts stay in English, but non-English answers are supported.
146
153
  - Generated docs default to English for consistency; use `--docs-lang` only when you explicitly need a different output language.
147
154
  - After docs scaffolding, CLI prints prompt starter examples so users can iterate by prompt without rewriting full project context.
@@ -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