@ryuenn3123/agentic-senior-core 3.0.14 → 3.0.16

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/prompts/bootstrap-design.md +30 -16
  2. package/.agent-context/prompts/init-project.md +4 -0
  3. package/.agent-context/rules/architecture.md +13 -0
  4. package/.agent-context/rules/docker-runtime.md +12 -0
  5. package/.agent-context/rules/efficiency-vs-hype.md +17 -6
  6. package/.agent-context/rules/frontend-architecture.md +5 -0
  7. package/.agent-context/state/memory-continuity-benchmark.json +1 -1
  8. package/.agent-context/state/onboarding-report.json +0 -1
  9. package/.cursorrules +66 -29
  10. package/.gemini/instructions.md +1 -1
  11. package/.github/copilot-instructions.md +1 -1
  12. package/.instructions.md +4 -3
  13. package/.windsurfrules +66 -29
  14. package/AGENTS.md +1 -1
  15. package/lib/cli/architect.mjs +71 -784
  16. package/lib/cli/commands/init.mjs +32 -98
  17. package/lib/cli/commands/optimize.mjs +0 -4
  18. package/lib/cli/commands/upgrade.mjs +2 -5
  19. package/lib/cli/compiler.mjs +3 -11
  20. package/lib/cli/constants.mjs +3 -73
  21. package/lib/cli/detector/design-evidence.mjs +427 -0
  22. package/lib/cli/detector.mjs +13 -116
  23. package/lib/cli/init-options.mjs +0 -118
  24. package/lib/cli/project-scaffolder/constants.mjs +67 -0
  25. package/lib/cli/project-scaffolder/design-contract.mjs +554 -0
  26. package/lib/cli/project-scaffolder/discovery.mjs +315 -0
  27. package/lib/cli/project-scaffolder/prompt-builders.mjs +196 -0
  28. package/lib/cli/project-scaffolder/storage.mjs +154 -0
  29. package/lib/cli/project-scaffolder.mjs +32 -1160
  30. package/lib/cli/utils.mjs +2 -11
  31. package/package.json +1 -1
  32. package/scripts/frontend-usability-audit.mjs +53 -0
  33. package/scripts/validate/config.mjs +401 -0
  34. package/scripts/validate/coverage-checks.mjs +429 -0
  35. package/scripts/validate.mjs +44 -754
  36. package/lib/cli/init-architecture-flow.mjs +0 -233
  37. package/lib/cli/profile-packs.mjs +0 -108
@@ -1,233 +0,0 @@
1
- import path from 'node:path';
2
-
3
- import {
4
- PROJECT_SCOPE_CHOICES,
5
- BLUEPRINT_RECOMMENDATIONS,
6
- } from './constants.mjs';
7
-
8
- import {
9
- recommendArchitecture,
10
- formatArchitectureRecommendation,
11
- createUpdatedArchitectPreference,
12
- shouldApplyRepeatedOverridePreference,
13
- } from './architect.mjs';
14
-
15
- import {
16
- askChoice,
17
- askYesNo,
18
- toTitleCase,
19
- } from './utils.mjs';
20
-
21
- import {
22
- filterStackFileNamesByCandidates,
23
- filterBlueprintFileNamesByCandidates,
24
- resolveProjectScopeKeyFromLabel,
25
- resolveScopeStackCandidates,
26
- resolveScopeBlueprintCandidates,
27
- } from './init-selection.mjs';
28
-
29
- const DEFAULT_PROJECT_SCOPE_KEY = 'both';
30
- const DEFAULT_PROJECT_SCOPE_LABEL = PROJECT_SCOPE_CHOICES.find(
31
- (scopeChoice) => scopeChoice.key === DEFAULT_PROJECT_SCOPE_KEY
32
- )?.label || 'Both (frontend + backend)';
33
-
34
- export async function resolveArchitectureSelection({
35
- shouldRunArchitectureRecommendation,
36
- initOptions,
37
- projectDetection,
38
- stackFileNames,
39
- blueprintFileNames,
40
- userInterface,
41
- isInteractiveSession,
42
- initialSelectedProjectScopeKey,
43
- initialSelectedProjectScopeLabel,
44
- initialSelectedManualStackFileName,
45
- initialSelectedManualBlueprintFileName,
46
- architectPreferenceState,
47
- askStackSelection,
48
- askBlueprintSelection,
49
- detectionTransparency,
50
- }) {
51
- let selectedProjectScopeKey = initialSelectedProjectScopeKey || DEFAULT_PROJECT_SCOPE_KEY;
52
- let selectedProjectScopeLabel = initialSelectedProjectScopeLabel || DEFAULT_PROJECT_SCOPE_LABEL;
53
- let selectedManualStackFileName = initialSelectedManualStackFileName || null;
54
- let selectedManualBlueprintFileName = initialSelectedManualBlueprintFileName || null;
55
- let architectureRecommendation = null;
56
- let nextArchitectPreferenceState = architectPreferenceState;
57
- let architectPreferenceUpdated = false;
58
-
59
- if (!shouldRunArchitectureRecommendation) {
60
- return {
61
- selectedProjectScopeKey,
62
- selectedProjectScopeLabel,
63
- selectedManualStackFileName,
64
- selectedManualBlueprintFileName,
65
- architectureProjectDescription: '',
66
- architectureRecommendation,
67
- architectPreferenceState: nextArchitectPreferenceState,
68
- architectPreferenceUpdated,
69
- };
70
- }
71
-
72
- let architectureProjectDescription = String(initOptions.projectDescription || '').trim();
73
- const isFreshProjectFlow = !projectDetection.hasExistingProjectFiles;
74
-
75
- if (isFreshProjectFlow && isInteractiveSession) {
76
- const selectedProjectScopeInput = await askChoice(
77
- 'Project domain (frontend only, backend only, or both):',
78
- PROJECT_SCOPE_CHOICES.map((scopeChoice) => scopeChoice.label),
79
- userInterface
80
- );
81
-
82
- selectedProjectScopeKey = resolveProjectScopeKeyFromLabel(selectedProjectScopeInput);
83
- selectedProjectScopeLabel = selectedProjectScopeInput;
84
-
85
- if (!architectureProjectDescription) {
86
- architectureProjectDescription = (await userInterface.question(
87
- '\nProject description (free text): '
88
- )).trim();
89
- }
90
- }
91
-
92
- if (!architectureProjectDescription && isInteractiveSession && !isFreshProjectFlow) {
93
- architectureProjectDescription = (await userInterface.question(
94
- '\nDescribe your project in one short paragraph for architecture recommendation: '
95
- )).trim();
96
- }
97
-
98
- if (!architectureProjectDescription) {
99
- architectureProjectDescription = `A software project named ${path.basename(initOptions.targetDirectoryPath)}.`;
100
- }
101
-
102
- const scopeStackCandidates = filterStackFileNamesByCandidates(
103
- stackFileNames,
104
- resolveScopeStackCandidates(selectedProjectScopeKey)
105
- );
106
- const scopeBlueprintCandidates = filterBlueprintFileNamesByCandidates(
107
- blueprintFileNames,
108
- resolveScopeBlueprintCandidates(selectedProjectScopeKey)
109
- );
110
-
111
- architectureRecommendation = recommendArchitecture({
112
- projectDescription: architectureProjectDescription,
113
- projectDetection,
114
- stackFileNames: scopeStackCandidates,
115
- blueprintFileNames: scopeBlueprintCandidates,
116
- tokenBudget: initOptions.architectTokenBudget,
117
- timeoutMs: initOptions.architectTimeoutMs,
118
- researchMode: initOptions.architectResearchMode,
119
- enableRealtimeResearch: initOptions.enableRealtimeResearch,
120
- realtimeSignalFilePath: initOptions.architectRealtimeSignalFile,
121
- });
122
-
123
- architectureRecommendation.projectDomain = {
124
- key: selectedProjectScopeKey,
125
- label: selectedProjectScopeLabel,
126
- };
127
-
128
- architectureRecommendation.userVeto = {
129
- applied: false,
130
- selectedStackFileName: architectureRecommendation.recommendedStackFileName,
131
- selectedBlueprintFileName: architectureRecommendation.recommendedBlueprintFileName,
132
- source: 'recommendation',
133
- };
134
-
135
- console.log(formatArchitectureRecommendation(architectureRecommendation));
136
-
137
- const shouldSkipRecommendationDebate = shouldApplyRepeatedOverridePreference(
138
- nextArchitectPreferenceState,
139
- architectureRecommendation.recommendedStackFileName
140
- );
141
-
142
- if (isFreshProjectFlow) {
143
- selectedManualStackFileName = architectureRecommendation.recommendedStackFileName;
144
- selectedManualBlueprintFileName = architectureRecommendation.recommendedBlueprintFileName;
145
-
146
- if (detectionTransparency) {
147
- detectionTransparency.quickConfirmation.response = 'fresh-project-two-question';
148
- detectionTransparency.decision.mode = 'fresh-project-two-question-auto';
149
- }
150
- } else if (shouldSkipRecommendationDebate) {
151
- architectureRecommendation.failureModes.repeatedOverride = true;
152
- selectedManualStackFileName = stackFileNames.includes(nextArchitectPreferenceState.preferredStackFileName)
153
- ? nextArchitectPreferenceState.preferredStackFileName
154
- : architectureRecommendation.recommendedStackFileName;
155
- selectedManualBlueprintFileName = blueprintFileNames.includes(nextArchitectPreferenceState.preferredBlueprintFileName)
156
- ? nextArchitectPreferenceState.preferredBlueprintFileName
157
- : architectureRecommendation.recommendedBlueprintFileName;
158
- architectureRecommendation.userVeto = {
159
- applied: true,
160
- selectedStackFileName: selectedManualStackFileName,
161
- selectedBlueprintFileName: selectedManualBlueprintFileName,
162
- source: 'saved-preference',
163
- };
164
- console.log(
165
- `Repeated override preference detected. Applying ${toTitleCase(selectedManualStackFileName)} + ${toTitleCase(selectedManualBlueprintFileName)} without additional debate.`
166
- );
167
- } else if (!isInteractiveSession) {
168
- selectedManualStackFileName = architectureRecommendation.recommendedStackFileName;
169
- selectedManualBlueprintFileName = architectureRecommendation.recommendedBlueprintFileName;
170
- } else {
171
- const shouldApplyRecommendedArchitecture = await askYesNo(
172
- 'Apply this architecture recommendation?',
173
- userInterface,
174
- true
175
- );
176
-
177
- if (shouldApplyRecommendedArchitecture) {
178
- selectedManualStackFileName = architectureRecommendation.recommendedStackFileName;
179
- selectedManualBlueprintFileName = architectureRecommendation.recommendedBlueprintFileName;
180
- } else {
181
- const vetoStackFileName = await askStackSelection(
182
- 'User veto received. Select stack to apply immediately:',
183
- stackFileNames,
184
- userInterface
185
- );
186
-
187
- const vetoBlueprintCandidates = filterBlueprintFileNamesByCandidates(
188
- blueprintFileNames,
189
- [BLUEPRINT_RECOMMENDATIONS[vetoStackFileName]].filter(Boolean)
190
- );
191
-
192
- const vetoBlueprintFileName = await askBlueprintSelection(
193
- 'Select blueprint to apply immediately (no further debate):',
194
- vetoBlueprintCandidates,
195
- userInterface
196
- );
197
-
198
- selectedManualStackFileName = vetoStackFileName;
199
- selectedManualBlueprintFileName = vetoBlueprintFileName;
200
- architectureRecommendation.userVeto = {
201
- applied: true,
202
- selectedStackFileName: vetoStackFileName,
203
- selectedBlueprintFileName: vetoBlueprintFileName,
204
- source: 'interactive-veto',
205
- };
206
-
207
- nextArchitectPreferenceState = createUpdatedArchitectPreference(nextArchitectPreferenceState, {
208
- selectedStackFileName: vetoStackFileName,
209
- selectedBlueprintFileName: vetoBlueprintFileName,
210
- });
211
- architectPreferenceUpdated = true;
212
-
213
- if (nextArchitectPreferenceState.overrideCount >= 2) {
214
- architectureRecommendation.failureModes.repeatedOverride = true;
215
- }
216
-
217
- console.log(
218
- `Veto applied. Proceeding with ${toTitleCase(vetoStackFileName)} + ${toTitleCase(vetoBlueprintFileName)} without recommendation loops.`
219
- );
220
- }
221
- }
222
-
223
- return {
224
- selectedProjectScopeKey,
225
- selectedProjectScopeLabel,
226
- selectedManualStackFileName,
227
- selectedManualBlueprintFileName,
228
- architectureProjectDescription,
229
- architectureRecommendation,
230
- architectPreferenceState: nextArchitectPreferenceState,
231
- architectPreferenceUpdated,
232
- };
233
- }
@@ -1,108 +0,0 @@
1
- /**
2
- * Profile Pack Parser — Team profile pack loading and matching.
3
- * Depends on: constants.mjs, utils.mjs
4
- */
5
- import fs from 'node:fs/promises';
6
- import path from 'node:path';
7
-
8
- import {
9
- PROFILE_PACK_REQUIRED_FIELDS,
10
- PROFILE_PACKS_DIRECTORY_NAME,
11
- FALLBACK_PROFILE_PACK_DEFINITIONS,
12
- } from './constants.mjs';
13
-
14
- import {
15
- pathExists,
16
- collectFileNames,
17
- normalizeChoiceInput,
18
- matchProfileNameFromInput,
19
- parseBooleanSetting,
20
- parseBlockingSeverities,
21
- } from './utils.mjs';
22
-
23
- function cloneProfilePackDefinition(profilePackDefinition) {
24
- return {
25
- ...profilePackDefinition,
26
- blockingSeverities: Array.isArray(profilePackDefinition.blockingSeverities)
27
- ? [...profilePackDefinition.blockingSeverities]
28
- : [],
29
- };
30
- }
31
-
32
- export function parseProfilePackContent(fileName, profilePackContent) {
33
- const parsedFields = {};
34
- const profilePackLines = profilePackContent.split(/\r?\n/);
35
-
36
- for (const profilePackLine of profilePackLines) {
37
- const lineMatch = profilePackLine.match(/^([A-Za-z][A-Za-z0-9 ]+):\s*(.+)$/);
38
- if (!lineMatch) {
39
- continue;
40
- }
41
-
42
- const fieldName = lineMatch[1].trim();
43
- const fieldValue = lineMatch[2].trim();
44
- parsedFields[fieldName] = fieldValue;
45
- }
46
-
47
- for (const requiredFieldName of PROFILE_PACK_REQUIRED_FIELDS) {
48
- if (!parsedFields[requiredFieldName]) {
49
- throw new Error(`Profile pack ${fileName} is missing required field: ${requiredFieldName}`);
50
- }
51
- }
52
-
53
- const defaultProfileName = matchProfileNameFromInput(parsedFields.defaultProfile);
54
- if (!defaultProfileName) {
55
- throw new Error(`Profile pack ${fileName} has invalid defaultProfile: ${parsedFields.defaultProfile}`);
56
- }
57
-
58
- return {
59
- fileName,
60
- slug: normalizeChoiceInput(parsedFields.slug),
61
- displayName: parsedFields.displayName,
62
- description: parsedFields.description,
63
- defaultProfileName,
64
- defaultStackFileName: parsedFields.defaultStack.trim(),
65
- defaultBlueprintFileName: parsedFields.defaultBlueprint.trim(),
66
- defaultCi: parseBooleanSetting(parsedFields.ciGuardrails, `${fileName} ciGuardrails`),
67
- lockCi: parseBooleanSetting(parsedFields.lockCi, `${fileName} lockCi`),
68
- blockingSeverities: parseBlockingSeverities(parsedFields.blockingSeverities, fileName),
69
- owner: parsedFields.owner || null,
70
- lastUpdated: parsedFields.lastUpdated || null,
71
- };
72
- }
73
-
74
- export async function collectProfilePacks(targetDirectoryPath) {
75
- const profilePackDirectoryPath = path.join(targetDirectoryPath, '.agent-context', PROFILE_PACKS_DIRECTORY_NAME);
76
- if (!(await pathExists(profilePackDirectoryPath))) {
77
- return FALLBACK_PROFILE_PACK_DEFINITIONS.map(cloneProfilePackDefinition);
78
- }
79
-
80
- const profilePackFileNames = await collectFileNames(profilePackDirectoryPath);
81
- if (profilePackFileNames.length === 0) {
82
- return FALLBACK_PROFILE_PACK_DEFINITIONS.map(cloneProfilePackDefinition);
83
- }
84
-
85
- const profilePackDefinitions = [];
86
-
87
- for (const profilePackFileName of profilePackFileNames) {
88
- const profilePackFilePath = path.join(profilePackDirectoryPath, profilePackFileName);
89
- const profilePackContent = await fs.readFile(profilePackFilePath, 'utf8');
90
- profilePackDefinitions.push(parseProfilePackContent(profilePackFileName, profilePackContent));
91
- }
92
-
93
- return profilePackDefinitions;
94
- }
95
-
96
- export function findProfilePackByInput(profilePackInput, profilePackDefinitions) {
97
- const normalizedProfilePackInput = normalizeChoiceInput(profilePackInput);
98
-
99
- return profilePackDefinitions.find((profilePackDefinition) => {
100
- const normalizedFileName = normalizeChoiceInput(profilePackDefinition.fileName.replace(/\.md$/i, ''));
101
- const normalizedSlug = normalizeChoiceInput(profilePackDefinition.slug);
102
- const normalizedDisplayName = normalizeChoiceInput(profilePackDefinition.displayName);
103
-
104
- return normalizedProfilePackInput === normalizedFileName
105
- || normalizedProfilePackInput === normalizedSlug
106
- || normalizedProfilePackInput === normalizedDisplayName;
107
- }) || null;
108
- }