@ryuenn3123/agentic-senior-core 3.0.15 → 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.
- package/.agent-context/prompts/bootstrap-design.md +25 -18
- package/.agent-context/rules/architecture.md +13 -0
- package/.agent-context/state/memory-continuity-benchmark.json +1 -1
- package/.agent-context/state/onboarding-report.json +0 -1
- package/.cursorrules +66 -29
- package/.windsurfrules +66 -29
- package/lib/cli/architect.mjs +71 -784
- package/lib/cli/commands/init.mjs +28 -98
- package/lib/cli/commands/optimize.mjs +0 -4
- package/lib/cli/commands/upgrade.mjs +2 -5
- package/lib/cli/compiler.mjs +1 -11
- package/lib/cli/constants.mjs +3 -73
- package/lib/cli/detector/design-evidence.mjs +427 -0
- package/lib/cli/detector.mjs +13 -116
- package/lib/cli/init-options.mjs +0 -118
- package/lib/cli/project-scaffolder/constants.mjs +67 -0
- package/lib/cli/project-scaffolder/design-contract.mjs +554 -0
- package/lib/cli/project-scaffolder/discovery.mjs +315 -0
- package/lib/cli/project-scaffolder/prompt-builders.mjs +196 -0
- package/lib/cli/project-scaffolder/storage.mjs +154 -0
- package/lib/cli/project-scaffolder.mjs +32 -1210
- package/lib/cli/utils.mjs +2 -11
- package/package.json +1 -1
- package/scripts/frontend-usability-audit.mjs +53 -0
- package/scripts/validate/config.mjs +401 -0
- package/scripts/validate/coverage-checks.mjs +429 -0
- package/scripts/validate.mjs +44 -854
- package/lib/cli/init-architecture-flow.mjs +0 -233
- 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
|
-
}
|