@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.
- package/.agent-context/review-checklists/frontend-excellence-rubric.md +54 -0
- package/.agent-context/review-checklists/frontend-skill-parity.md +1 -0
- package/.agent-context/review-checklists/frontend-usability.md +1 -0
- package/.agent-context/rules/docker-runtime.md +29 -0
- package/.agent-context/skills/frontend/README.md +1 -0
- package/.agent-context/skills/frontend.md +4 -0
- package/.cursorrules +1 -1
- package/.github/workflows/benchmark-detection.yml +8 -1
- package/.windsurfrules +1 -1
- package/README.md +7 -0
- package/lib/cli/commands/init.mjs +358 -16
- package/lib/cli/commands/optimize.mjs +12 -0
- package/lib/cli/commands/upgrade.mjs +30 -1
- package/lib/cli/compiler.mjs +55 -1
- package/lib/cli/constants.mjs +83 -0
- package/lib/cli/detector.mjs +11 -1
- package/lib/cli/project-scaffolder.mjs +183 -2
- package/lib/cli/skill-selector.mjs +60 -38
- package/lib/cli/templates/architecture-decision-record.md.tmpl +39 -0
- package/lib/cli/templates/flow-overview.md.tmpl +12 -0
- package/lib/cli/templates/project-brief.md.id.tmpl +2 -0
- package/lib/cli/templates/project-brief.md.tmpl +26 -0
- package/lib/cli/utils.mjs +2 -1
- package/package.json +1 -1
- package/scripts/frontend-usability-audit.mjs +21 -0
- package/scripts/release-gate.mjs +108 -0
- package/scripts/validate.mjs +2 -0
|
@@ -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.
|
|
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:
|
|
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.
|
|
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
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
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
|
-
|| (
|
|
664
|
+
|| (shouldAutoApplyDetectedStack ? projectDetection.recommendedStackFileName : null)
|
|
370
665
|
|| selectedProfilePack?.defaultStackFileName
|
|
371
666
|
|| selectedProfile.defaultStackFileName
|
|
372
|
-
||
|
|
373
|
-
|
|
374
|
-
await askChoice('Which stack should this governance pack target?', stackDisplayChoices, userInterface)
|
|
375
|
-
)
|
|
376
|
-
];
|
|
667
|
+
|| selectedManualStackFileName
|
|
668
|
+
|| stackFileNames[0];
|
|
377
669
|
|
|
378
|
-
|
|
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
|
|