@ryuenn3123/agentic-senior-core 2.5.7 → 2.5.9
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/state/memory-continuity-benchmark.json +1 -1
- package/.cursorrules +1 -1
- package/.windsurfrules +1 -1
- package/README.md +19 -0
- package/lib/cli/architect.mjs +493 -0
- package/lib/cli/commands/init.mjs +222 -72
- package/lib/cli/compiler.mjs +2 -0
- package/lib/cli/constants.mjs +2 -0
- package/lib/cli/utils.mjs +6 -3
- package/package.json +1 -1
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.5.
|
|
3
|
+
Generated by Agentic-Senior-Core CLI v2.5.9
|
|
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/.windsurfrules
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# AGENTIC-SENIOR-CORE DYNAMIC GOVERNANCE RULESET
|
|
2
2
|
|
|
3
|
-
Generated by Agentic-Senior-Core CLI v2.5.
|
|
3
|
+
Generated by Agentic-Senior-Core CLI v2.5.9
|
|
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
|
@@ -22,12 +22,20 @@ npx @ryuenn3123/agentic-senior-core init
|
|
|
22
22
|
|
|
23
23
|
That one command initializes your project with compiled rules, review checklists, and state context.
|
|
24
24
|
|
|
25
|
+
Golden Standard mode is now the default path: init applies the recommended quality profile automatically, without a beginner/balanced/strict prompt on first run.
|
|
26
|
+
|
|
25
27
|
Optional team default path:
|
|
26
28
|
|
|
27
29
|
```bash
|
|
28
30
|
npx @ryuenn3123/agentic-senior-core init --profile-pack startup
|
|
29
31
|
```
|
|
30
32
|
|
|
33
|
+
Project-description-first path (AI as Architect with veto control):
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
npx @ryuenn3123/agentic-senior-core init --project-description "Machine learning API for fraud detection"
|
|
37
|
+
```
|
|
38
|
+
|
|
31
39
|
---
|
|
32
40
|
|
|
33
41
|
## Before / After
|
|
@@ -74,6 +82,17 @@ If you see `Property $schema is not allowed`, keep `.vscode/mcp.json` without `$
|
|
|
74
82
|
|
|
75
83
|
---
|
|
76
84
|
|
|
85
|
+
## Upgrade Existing Governance Pack
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
npx @ryuenn3123/agentic-senior-core upgrade --dry-run
|
|
89
|
+
npx @ryuenn3123/agentic-senior-core upgrade --yes
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Use `--dry-run` first to preview changes safely, then apply with `--yes`.
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
77
96
|
## Terminology Mapping (Final)
|
|
78
97
|
|
|
79
98
|
| Canonical Enterprise Term | Developer-Facing Alias | Usage Rule |
|
|
@@ -0,0 +1,493 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
|
|
5
|
+
import { BLUEPRINT_RECOMMENDATIONS } from './constants.mjs';
|
|
6
|
+
import { ensureDirectory, pathExists, toTitleCase } from './utils.mjs';
|
|
7
|
+
|
|
8
|
+
export const ARCHITECT_DEFAULT_TOKEN_BUDGET = 900;
|
|
9
|
+
export const ARCHITECT_DEFAULT_TIMEOUT_MS = 1500;
|
|
10
|
+
export const ARCHITECT_MIN_TOKEN_BUDGET = 200;
|
|
11
|
+
export const ARCHITECT_MAX_TOKEN_BUDGET = 4000;
|
|
12
|
+
export const ARCHITECT_MIN_TIMEOUT_MS = 200;
|
|
13
|
+
export const ARCHITECT_MAX_TIMEOUT_MS = 10000;
|
|
14
|
+
|
|
15
|
+
const ARCHITECT_PREFERENCE_FILE_PATH = process.env.AGENTIC_ARCHITECT_PREF_FILE
|
|
16
|
+
? path.resolve(process.env.AGENTIC_ARCHITECT_PREF_FILE)
|
|
17
|
+
: path.join(os.homedir(), '.agentic-senior-core', 'architect-preferences.json');
|
|
18
|
+
|
|
19
|
+
const STACK_SIGNAL_WEIGHTS = {
|
|
20
|
+
'typescript.md': [
|
|
21
|
+
{ term: 'typescript', weight: 0.9 },
|
|
22
|
+
{ term: 'javascript', weight: 0.8 },
|
|
23
|
+
{ term: 'node', weight: 0.55 },
|
|
24
|
+
{ term: 'next.js', weight: 0.7 },
|
|
25
|
+
{ term: 'nextjs', weight: 0.7 },
|
|
26
|
+
{ term: 'react', weight: 0.45 },
|
|
27
|
+
{ term: 'web app', weight: 0.45 },
|
|
28
|
+
{ term: 'frontend', weight: 0.4 },
|
|
29
|
+
{ term: 'dashboard', weight: 0.35 },
|
|
30
|
+
],
|
|
31
|
+
'python.md': [
|
|
32
|
+
{ term: 'python', weight: 0.95 },
|
|
33
|
+
{ term: 'fastapi', weight: 0.8 },
|
|
34
|
+
{ term: 'machine learning', weight: 0.8 },
|
|
35
|
+
{ term: 'ml', weight: 0.4 },
|
|
36
|
+
{ term: 'data', weight: 0.45 },
|
|
37
|
+
{ term: 'ai', weight: 0.45 },
|
|
38
|
+
{ term: 'automation', weight: 0.35 },
|
|
39
|
+
{ term: 'analytics', weight: 0.35 },
|
|
40
|
+
],
|
|
41
|
+
'java.md': [
|
|
42
|
+
{ term: 'java', weight: 0.9 },
|
|
43
|
+
{ term: 'spring', weight: 0.75 },
|
|
44
|
+
{ term: 'enterprise', weight: 0.45 },
|
|
45
|
+
{ term: 'bank', weight: 0.35 },
|
|
46
|
+
{ term: 'regulated', weight: 0.35 },
|
|
47
|
+
{ term: 'jvm', weight: 0.35 },
|
|
48
|
+
],
|
|
49
|
+
'php.md': [
|
|
50
|
+
{ term: 'php', weight: 0.9 },
|
|
51
|
+
{ term: 'laravel', weight: 0.8 },
|
|
52
|
+
{ term: 'cms', weight: 0.4 },
|
|
53
|
+
{ term: 'wordpress', weight: 0.35 },
|
|
54
|
+
],
|
|
55
|
+
'go.md': [
|
|
56
|
+
{ term: 'go', weight: 0.5 },
|
|
57
|
+
{ term: 'golang', weight: 0.9 },
|
|
58
|
+
{ term: 'high throughput', weight: 0.55 },
|
|
59
|
+
{ term: 'microservice', weight: 0.45 },
|
|
60
|
+
{ term: 'kubernetes', weight: 0.45 },
|
|
61
|
+
{ term: 'latency', weight: 0.35 },
|
|
62
|
+
{ term: 'concurrency', weight: 0.35 },
|
|
63
|
+
],
|
|
64
|
+
'csharp.md': [
|
|
65
|
+
{ term: '.net', weight: 0.9 },
|
|
66
|
+
{ term: 'dotnet', weight: 0.9 },
|
|
67
|
+
{ term: 'c#', weight: 0.75 },
|
|
68
|
+
{ term: 'asp.net', weight: 0.8 },
|
|
69
|
+
{ term: 'microsoft', weight: 0.35 },
|
|
70
|
+
],
|
|
71
|
+
'rust.md': [
|
|
72
|
+
{ term: 'rust', weight: 0.9 },
|
|
73
|
+
{ term: 'systems', weight: 0.45 },
|
|
74
|
+
{ term: 'performance', weight: 0.35 },
|
|
75
|
+
{ term: 'memory safety', weight: 0.4 },
|
|
76
|
+
{ term: 'low latency', weight: 0.4 },
|
|
77
|
+
],
|
|
78
|
+
'ruby.md': [
|
|
79
|
+
{ term: 'ruby', weight: 0.9 },
|
|
80
|
+
{ term: 'rails', weight: 0.8 },
|
|
81
|
+
{ term: 'monolith', weight: 0.35 },
|
|
82
|
+
{ term: 'crud', weight: 0.3 },
|
|
83
|
+
],
|
|
84
|
+
'react-native.md': [
|
|
85
|
+
{ term: 'react native', weight: 0.9 },
|
|
86
|
+
{ term: 'mobile', weight: 0.4 },
|
|
87
|
+
{ term: 'android', weight: 0.35 },
|
|
88
|
+
{ term: 'ios', weight: 0.35 },
|
|
89
|
+
{ term: 'cross-platform', weight: 0.4 },
|
|
90
|
+
],
|
|
91
|
+
'flutter.md': [
|
|
92
|
+
{ term: 'flutter', weight: 0.95 },
|
|
93
|
+
{ term: 'dart', weight: 0.8 },
|
|
94
|
+
{ term: 'mobile', weight: 0.4 },
|
|
95
|
+
{ term: 'android', weight: 0.35 },
|
|
96
|
+
{ term: 'ios', weight: 0.35 },
|
|
97
|
+
{ term: 'cross-platform', weight: 0.4 },
|
|
98
|
+
],
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
const STACK_TRADEOFF_SUMMARIES = {
|
|
102
|
+
'typescript.md': 'great fullstack velocity, but dependency churn and runtime startup need discipline.',
|
|
103
|
+
'python.md': 'fast AI and API delivery, but strict latency targets may need extra optimization.',
|
|
104
|
+
'java.md': 'strong enterprise reliability, but setup and boilerplate are heavier.',
|
|
105
|
+
'php.md': 'rapid web product iteration, but architecture boundaries must be guarded early.',
|
|
106
|
+
'go.md': 'excellent throughput and operational simplicity, but abstractions are intentionally minimal.',
|
|
107
|
+
'csharp.md': 'strong for Microsoft-centered teams, but cross-platform tooling choices should be explicit.',
|
|
108
|
+
'rust.md': 'top performance and safety, but development ramp-up is steeper.',
|
|
109
|
+
'ruby.md': 'high productivity for product teams, but scaling strategy should be planned early.',
|
|
110
|
+
'react-native.md': 'single codebase for mobile, but native edge cases still require platform attention.',
|
|
111
|
+
'flutter.md': 'consistent UI across mobile platforms, but ecosystem fit should be checked per package.',
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
function clampNumericValue(value, minimumValue, maximumValue) {
|
|
115
|
+
return Math.min(Math.max(value, minimumValue), maximumValue);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function estimateTokenUsage(textValue) {
|
|
119
|
+
return Math.ceil(String(textValue || '').length / 4);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function resolveConfidenceLabel(confidenceScore) {
|
|
123
|
+
if (confidenceScore >= 0.85) {
|
|
124
|
+
return 'high';
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (confidenceScore >= 0.7) {
|
|
128
|
+
return 'medium';
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return 'low';
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function resolveRecommendedBlueprintFileName(stackFileName, blueprintFileNames) {
|
|
135
|
+
const recommendedBlueprintFileName = BLUEPRINT_RECOMMENDATIONS[stackFileName] || null;
|
|
136
|
+
if (recommendedBlueprintFileName && blueprintFileNames.includes(recommendedBlueprintFileName)) {
|
|
137
|
+
return recommendedBlueprintFileName;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return blueprintFileNames[0] || null;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function buildFallbackRecommendation({
|
|
144
|
+
stackFileNames,
|
|
145
|
+
blueprintFileNames,
|
|
146
|
+
tokenBudget,
|
|
147
|
+
timeoutMs,
|
|
148
|
+
usedTokens,
|
|
149
|
+
elapsedMs,
|
|
150
|
+
timeoutTriggered,
|
|
151
|
+
}) {
|
|
152
|
+
const fallbackStackFileName = stackFileNames.includes('typescript.md')
|
|
153
|
+
? 'typescript.md'
|
|
154
|
+
: stackFileNames[0] || 'typescript.md';
|
|
155
|
+
const fallbackBlueprintFileName = resolveRecommendedBlueprintFileName(fallbackStackFileName, blueprintFileNames);
|
|
156
|
+
|
|
157
|
+
return {
|
|
158
|
+
projectDescription: '',
|
|
159
|
+
recommendedStackFileName: fallbackStackFileName,
|
|
160
|
+
recommendedBlueprintFileName: fallbackBlueprintFileName,
|
|
161
|
+
confidenceLabel: 'low',
|
|
162
|
+
confidenceScore: 0.45,
|
|
163
|
+
rationaleSentences: [
|
|
164
|
+
`I recommend ${toTitleCase(fallbackStackFileName)} with ${toTitleCase(fallbackBlueprintFileName)} as a safe fallback path.`,
|
|
165
|
+
'The architecture recommendation budget was constrained before a stronger stack signal could be computed.',
|
|
166
|
+
`Main trade-off: ${STACK_TRADEOFF_SUMMARIES[fallbackStackFileName] || 'validate ecosystem fit before production rollout.'}`,
|
|
167
|
+
],
|
|
168
|
+
alternatives: [],
|
|
169
|
+
uncertaintyNotes: [
|
|
170
|
+
timeoutTriggered
|
|
171
|
+
? 'Timeout guardrail triggered before recommendation analysis completed.'
|
|
172
|
+
: 'Input signals were not strong enough for a high-confidence architecture recommendation.',
|
|
173
|
+
],
|
|
174
|
+
signalSummary: 'fallback mode',
|
|
175
|
+
failureModes: {
|
|
176
|
+
lowConfidence: true,
|
|
177
|
+
dataConflict: false,
|
|
178
|
+
repeatedOverride: false,
|
|
179
|
+
},
|
|
180
|
+
researchBudget: {
|
|
181
|
+
tokenBudget,
|
|
182
|
+
timeoutMs,
|
|
183
|
+
usedTokens: Math.min(usedTokens, tokenBudget),
|
|
184
|
+
elapsedMs: Math.min(elapsedMs, timeoutMs),
|
|
185
|
+
tokenBudgetCapped: usedTokens >= tokenBudget,
|
|
186
|
+
timeoutTriggered,
|
|
187
|
+
},
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export function recommendArchitecture({
|
|
192
|
+
projectDescription,
|
|
193
|
+
projectDetection,
|
|
194
|
+
stackFileNames,
|
|
195
|
+
blueprintFileNames,
|
|
196
|
+
tokenBudget = ARCHITECT_DEFAULT_TOKEN_BUDGET,
|
|
197
|
+
timeoutMs = ARCHITECT_DEFAULT_TIMEOUT_MS,
|
|
198
|
+
}) {
|
|
199
|
+
const startedAt = Date.now();
|
|
200
|
+
const boundedTokenBudget = clampNumericValue(tokenBudget, ARCHITECT_MIN_TOKEN_BUDGET, ARCHITECT_MAX_TOKEN_BUDGET);
|
|
201
|
+
const boundedTimeoutMs = clampNumericValue(timeoutMs, ARCHITECT_MIN_TIMEOUT_MS, ARCHITECT_MAX_TIMEOUT_MS);
|
|
202
|
+
const normalizedDescription = String(projectDescription || '').trim().toLowerCase();
|
|
203
|
+
const effectiveDescriptionSeed = normalizedDescription || 'general software project';
|
|
204
|
+
let effectiveDescription = effectiveDescriptionSeed;
|
|
205
|
+
let usedTokens = estimateTokenUsage(effectiveDescription) + 120;
|
|
206
|
+
const uncertaintyNotes = [];
|
|
207
|
+
|
|
208
|
+
if (usedTokens > boundedTokenBudget) {
|
|
209
|
+
effectiveDescription = effectiveDescription.slice(0, Math.max(120, boundedTokenBudget * 4));
|
|
210
|
+
usedTokens = boundedTokenBudget;
|
|
211
|
+
uncertaintyNotes.push('Token budget guardrail trimmed input context before recommendation.');
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if ((Date.now() - startedAt) > boundedTimeoutMs) {
|
|
215
|
+
return buildFallbackRecommendation({
|
|
216
|
+
stackFileNames,
|
|
217
|
+
blueprintFileNames,
|
|
218
|
+
tokenBudget: boundedTokenBudget,
|
|
219
|
+
timeoutMs: boundedTimeoutMs,
|
|
220
|
+
usedTokens,
|
|
221
|
+
elapsedMs: Date.now() - startedAt,
|
|
222
|
+
timeoutTriggered: true,
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const detectionScoreByStackFileName = new Map();
|
|
227
|
+
for (const rankedCandidate of projectDetection?.rankedCandidates || []) {
|
|
228
|
+
const confidenceScore = Number(rankedCandidate.confidenceScore) || 0;
|
|
229
|
+
if (confidenceScore <= 0) {
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
detectionScoreByStackFileName.set(
|
|
234
|
+
rankedCandidate.stackFileName,
|
|
235
|
+
confidenceScore * 1.75
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const scoredStackCandidates = stackFileNames.map((stackFileName) => {
|
|
240
|
+
const configuredSignals = STACK_SIGNAL_WEIGHTS[stackFileName] || [];
|
|
241
|
+
const matchedSignals = [];
|
|
242
|
+
let keywordSignalScore = 0;
|
|
243
|
+
|
|
244
|
+
for (const configuredSignal of configuredSignals) {
|
|
245
|
+
if (!effectiveDescription.includes(configuredSignal.term)) {
|
|
246
|
+
continue;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
keywordSignalScore += configuredSignal.weight;
|
|
250
|
+
matchedSignals.push(configuredSignal.term);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const detectionSignalScore = detectionScoreByStackFileName.get(stackFileName) || 0;
|
|
254
|
+
const totalScore = 0.2 + keywordSignalScore + detectionSignalScore;
|
|
255
|
+
|
|
256
|
+
return {
|
|
257
|
+
stackFileName,
|
|
258
|
+
totalScore,
|
|
259
|
+
keywordSignalScore,
|
|
260
|
+
detectionSignalScore,
|
|
261
|
+
matchedSignals,
|
|
262
|
+
};
|
|
263
|
+
}).sort((leftCandidate, rightCandidate) => rightCandidate.totalScore - leftCandidate.totalScore);
|
|
264
|
+
|
|
265
|
+
if (scoredStackCandidates.length === 0) {
|
|
266
|
+
return buildFallbackRecommendation({
|
|
267
|
+
stackFileNames,
|
|
268
|
+
blueprintFileNames,
|
|
269
|
+
tokenBudget: boundedTokenBudget,
|
|
270
|
+
timeoutMs: boundedTimeoutMs,
|
|
271
|
+
usedTokens,
|
|
272
|
+
elapsedMs: Date.now() - startedAt,
|
|
273
|
+
timeoutTriggered: false,
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const strongestCandidate = scoredStackCandidates[0];
|
|
278
|
+
const secondCandidate = scoredStackCandidates[1] || null;
|
|
279
|
+
const scoreGap = secondCandidate
|
|
280
|
+
? strongestCandidate.totalScore - secondCandidate.totalScore
|
|
281
|
+
: strongestCandidate.totalScore;
|
|
282
|
+
|
|
283
|
+
let confidenceScore = 0.55
|
|
284
|
+
+ Math.min(strongestCandidate.totalScore / 8, 0.25)
|
|
285
|
+
+ Math.min(Math.max(scoreGap, 0) / 3, 0.18);
|
|
286
|
+
|
|
287
|
+
if (strongestCandidate.matchedSignals.length === 0) {
|
|
288
|
+
confidenceScore -= 0.2;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
if (secondCandidate && scoreGap < 0.18) {
|
|
292
|
+
confidenceScore -= 0.1;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
confidenceScore = clampNumericValue(confidenceScore, 0.35, 0.97);
|
|
296
|
+
|
|
297
|
+
const confidenceLabel = resolveConfidenceLabel(confidenceScore);
|
|
298
|
+
const lowConfidence = confidenceScore < 0.7;
|
|
299
|
+
const dataConflict = Boolean(secondCandidate && scoreGap < 0.18);
|
|
300
|
+
|
|
301
|
+
if (lowConfidence) {
|
|
302
|
+
uncertaintyNotes.push('Low confidence: description did not map strongly to a single stack profile.');
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
if (dataConflict) {
|
|
306
|
+
uncertaintyNotes.push('Data conflict: top stack candidates are close, so trade-offs need manual confirmation.');
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
const recommendedStackFileName = strongestCandidate.stackFileName;
|
|
310
|
+
const recommendedBlueprintFileName = resolveRecommendedBlueprintFileName(recommendedStackFileName, blueprintFileNames);
|
|
311
|
+
const signalSummary = strongestCandidate.matchedSignals.length > 0
|
|
312
|
+
? strongestCandidate.matchedSignals.slice(0, 4).join(', ')
|
|
313
|
+
: 'limited direct stack keywords';
|
|
314
|
+
|
|
315
|
+
const rationaleSentences = [
|
|
316
|
+
`I recommend ${toTitleCase(recommendedStackFileName)} with ${toTitleCase(recommendedBlueprintFileName)} for this project.`,
|
|
317
|
+
`The strongest evidence in your description is ${signalSummary}, combined with available repository signals.`,
|
|
318
|
+
`Main trade-off: ${STACK_TRADEOFF_SUMMARIES[recommendedStackFileName] || 'validate ecosystem fit before production rollout.'}`,
|
|
319
|
+
];
|
|
320
|
+
|
|
321
|
+
if (lowConfidence) {
|
|
322
|
+
rationaleSentences.push('Confidence is low, so review alternatives before finalizing architecture.');
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
const alternatives = scoredStackCandidates
|
|
326
|
+
.slice(1, 3)
|
|
327
|
+
.map((stackCandidate) => {
|
|
328
|
+
const alternativeBlueprintFileName = resolveRecommendedBlueprintFileName(stackCandidate.stackFileName, blueprintFileNames);
|
|
329
|
+
return {
|
|
330
|
+
stackFileName: stackCandidate.stackFileName,
|
|
331
|
+
blueprintFileName: alternativeBlueprintFileName,
|
|
332
|
+
oneLineTradeoff: STACK_TRADEOFF_SUMMARIES[stackCandidate.stackFileName]
|
|
333
|
+
|| 'validate fit with your runtime and team constraints.',
|
|
334
|
+
};
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
const elapsedMs = Date.now() - startedAt;
|
|
338
|
+
if (elapsedMs > boundedTimeoutMs) {
|
|
339
|
+
return buildFallbackRecommendation({
|
|
340
|
+
stackFileNames,
|
|
341
|
+
blueprintFileNames,
|
|
342
|
+
tokenBudget: boundedTokenBudget,
|
|
343
|
+
timeoutMs: boundedTimeoutMs,
|
|
344
|
+
usedTokens,
|
|
345
|
+
elapsedMs,
|
|
346
|
+
timeoutTriggered: true,
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
return {
|
|
351
|
+
projectDescription: String(projectDescription || '').trim(),
|
|
352
|
+
recommendedStackFileName,
|
|
353
|
+
recommendedBlueprintFileName,
|
|
354
|
+
confidenceLabel,
|
|
355
|
+
confidenceScore: Number(confidenceScore.toFixed(2)),
|
|
356
|
+
rationaleSentences,
|
|
357
|
+
alternatives,
|
|
358
|
+
uncertaintyNotes,
|
|
359
|
+
signalSummary,
|
|
360
|
+
failureModes: {
|
|
361
|
+
lowConfidence,
|
|
362
|
+
dataConflict,
|
|
363
|
+
repeatedOverride: false,
|
|
364
|
+
},
|
|
365
|
+
researchBudget: {
|
|
366
|
+
tokenBudget: boundedTokenBudget,
|
|
367
|
+
timeoutMs: boundedTimeoutMs,
|
|
368
|
+
usedTokens: Math.min(usedTokens, boundedTokenBudget),
|
|
369
|
+
elapsedMs,
|
|
370
|
+
tokenBudgetCapped: usedTokens >= boundedTokenBudget,
|
|
371
|
+
timeoutTriggered: false,
|
|
372
|
+
},
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
export function formatArchitectureRecommendation(architectureRecommendation) {
|
|
377
|
+
const outputLines = [
|
|
378
|
+
'\nArchitecture recommendation (project-description-first):',
|
|
379
|
+
`- Stack: ${toTitleCase(architectureRecommendation.recommendedStackFileName)}`,
|
|
380
|
+
`- Blueprint: ${toTitleCase(architectureRecommendation.recommendedBlueprintFileName)}`,
|
|
381
|
+
`- Confidence: ${architectureRecommendation.confidenceLabel} (${architectureRecommendation.confidenceScore})`,
|
|
382
|
+
'- Rationale:',
|
|
383
|
+
...architectureRecommendation.rationaleSentences.map((sentence, sentenceIndex) => ` ${sentenceIndex + 1}. ${sentence}`),
|
|
384
|
+
'- Alternatives:',
|
|
385
|
+
];
|
|
386
|
+
|
|
387
|
+
if (architectureRecommendation.alternatives.length === 0) {
|
|
388
|
+
outputLines.push(' 1. No strong alternatives detected from current input signals.');
|
|
389
|
+
} else {
|
|
390
|
+
outputLines.push(
|
|
391
|
+
...architectureRecommendation.alternatives.map(
|
|
392
|
+
(alternative, alternativeIndex) => ` ${alternativeIndex + 1}. ${toTitleCase(alternative.stackFileName)} + ${toTitleCase(alternative.blueprintFileName)}: ${alternative.oneLineTradeoff}`
|
|
393
|
+
)
|
|
394
|
+
);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
if (architectureRecommendation.uncertaintyNotes.length > 0) {
|
|
398
|
+
outputLines.push('- Uncertainty notes:');
|
|
399
|
+
outputLines.push(
|
|
400
|
+
...architectureRecommendation.uncertaintyNotes.map(
|
|
401
|
+
(uncertaintyNote, uncertaintyIndex) => ` ${uncertaintyIndex + 1}. ${uncertaintyNote}`
|
|
402
|
+
)
|
|
403
|
+
);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
const cautionLabels = [];
|
|
407
|
+
if (architectureRecommendation.failureModes.lowConfidence) {
|
|
408
|
+
cautionLabels.push('low-confidence');
|
|
409
|
+
}
|
|
410
|
+
if (architectureRecommendation.failureModes.dataConflict) {
|
|
411
|
+
cautionLabels.push('data-conflict');
|
|
412
|
+
}
|
|
413
|
+
if (architectureRecommendation.failureModes.repeatedOverride) {
|
|
414
|
+
cautionLabels.push('repeated-override');
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
if (cautionLabels.length > 0) {
|
|
418
|
+
outputLines.push(`- Caution labels: ${cautionLabels.join(', ')}`);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
outputLines.push(
|
|
422
|
+
`- Research guardrails: ${architectureRecommendation.researchBudget.usedTokens}/${architectureRecommendation.researchBudget.tokenBudget} tokens, ${architectureRecommendation.researchBudget.elapsedMs}ms/${architectureRecommendation.researchBudget.timeoutMs}ms`
|
|
423
|
+
);
|
|
424
|
+
|
|
425
|
+
return outputLines.join('\n');
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
export async function readArchitectPreferenceState() {
|
|
429
|
+
if (!(await pathExists(ARCHITECT_PREFERENCE_FILE_PATH))) {
|
|
430
|
+
return null;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
try {
|
|
434
|
+
const rawPreferenceContent = await fs.readFile(ARCHITECT_PREFERENCE_FILE_PATH, 'utf8');
|
|
435
|
+
const parsedPreferencePayload = JSON.parse(rawPreferenceContent);
|
|
436
|
+
const parsedPreferenceState = parsedPreferencePayload?.preference;
|
|
437
|
+
|
|
438
|
+
if (!parsedPreferenceState?.preferredStackFileName) {
|
|
439
|
+
return null;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
return {
|
|
443
|
+
preferredStackFileName: parsedPreferenceState.preferredStackFileName,
|
|
444
|
+
preferredBlueprintFileName: parsedPreferenceState.preferredBlueprintFileName || null,
|
|
445
|
+
overrideCount: Number(parsedPreferenceState.overrideCount) || 0,
|
|
446
|
+
lastOverrideAt: parsedPreferenceState.lastOverrideAt || null,
|
|
447
|
+
};
|
|
448
|
+
} catch {
|
|
449
|
+
return null;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
export function createUpdatedArchitectPreference(currentPreferenceState, {
|
|
454
|
+
selectedStackFileName,
|
|
455
|
+
selectedBlueprintFileName,
|
|
456
|
+
}) {
|
|
457
|
+
const currentOverrideCount = Number(currentPreferenceState?.overrideCount) || 0;
|
|
458
|
+
|
|
459
|
+
return {
|
|
460
|
+
preferredStackFileName: selectedStackFileName,
|
|
461
|
+
preferredBlueprintFileName: selectedBlueprintFileName,
|
|
462
|
+
overrideCount: currentOverrideCount + 1,
|
|
463
|
+
lastOverrideAt: new Date().toISOString(),
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
export function shouldApplyRepeatedOverridePreference(preferenceState, recommendedStackFileName) {
|
|
468
|
+
if (!preferenceState?.preferredStackFileName) {
|
|
469
|
+
return false;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
if ((Number(preferenceState.overrideCount) || 0) < 2) {
|
|
473
|
+
return false;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
return preferenceState.preferredStackFileName !== recommendedStackFileName;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
export async function writeArchitectPreferenceState(preferenceState) {
|
|
480
|
+
const preferencePayload = {
|
|
481
|
+
schemaVersion: '1.0.0',
|
|
482
|
+
updatedAt: new Date().toISOString(),
|
|
483
|
+
preference: {
|
|
484
|
+
preferredStackFileName: preferenceState.preferredStackFileName,
|
|
485
|
+
preferredBlueprintFileName: preferenceState.preferredBlueprintFileName,
|
|
486
|
+
overrideCount: preferenceState.overrideCount,
|
|
487
|
+
lastOverrideAt: preferenceState.lastOverrideAt,
|
|
488
|
+
},
|
|
489
|
+
};
|
|
490
|
+
|
|
491
|
+
await ensureDirectory(path.dirname(ARCHITECT_PREFERENCE_FILE_PATH));
|
|
492
|
+
await fs.writeFile(ARCHITECT_PREFERENCE_FILE_PATH, JSON.stringify(preferencePayload, null, 2) + '\n', 'utf8');
|
|
493
|
+
}
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
AGENT_CONTEXT_DIR,
|
|
12
12
|
INIT_PRESETS,
|
|
13
13
|
PROFILE_PRESETS,
|
|
14
|
+
GOLDEN_STANDARD_PROFILE_NAME,
|
|
14
15
|
BLUEPRINT_RECOMMENDATIONS,
|
|
15
16
|
PROJECT_SCOPE_CHOICES,
|
|
16
17
|
PROJECT_SCOPE_STACK_FILTERS,
|
|
@@ -61,6 +62,20 @@ import {
|
|
|
61
62
|
writeMemoryContinuityState,
|
|
62
63
|
} from '../memory-continuity.mjs';
|
|
63
64
|
import { evaluateSkillDomainCompatibility } from '../compatibility.mjs';
|
|
65
|
+
import {
|
|
66
|
+
ARCHITECT_DEFAULT_TOKEN_BUDGET,
|
|
67
|
+
ARCHITECT_DEFAULT_TIMEOUT_MS,
|
|
68
|
+
ARCHITECT_MIN_TOKEN_BUDGET,
|
|
69
|
+
ARCHITECT_MAX_TOKEN_BUDGET,
|
|
70
|
+
ARCHITECT_MIN_TIMEOUT_MS,
|
|
71
|
+
ARCHITECT_MAX_TIMEOUT_MS,
|
|
72
|
+
recommendArchitecture,
|
|
73
|
+
formatArchitectureRecommendation,
|
|
74
|
+
readArchitectPreferenceState,
|
|
75
|
+
createUpdatedArchitectPreference,
|
|
76
|
+
shouldApplyRepeatedOverridePreference,
|
|
77
|
+
writeArchitectPreferenceState,
|
|
78
|
+
} from '../architect.mjs';
|
|
64
79
|
|
|
65
80
|
export { REPO_ROOT } from '../constants.mjs';
|
|
66
81
|
|
|
@@ -82,6 +97,9 @@ export function parseInitArguments(commandArguments) {
|
|
|
82
97
|
docsLang: 'en',
|
|
83
98
|
docsLangProvided: false,
|
|
84
99
|
projectConfig: undefined,
|
|
100
|
+
projectDescription: '',
|
|
101
|
+
architectTokenBudget: ARCHITECT_DEFAULT_TOKEN_BUDGET,
|
|
102
|
+
architectTimeoutMs: ARCHITECT_DEFAULT_TIMEOUT_MS,
|
|
85
103
|
runtimeEnv: 'auto',
|
|
86
104
|
runtimeEnvProvided: false,
|
|
87
105
|
};
|
|
@@ -241,6 +259,55 @@ export function parseInitArguments(commandArguments) {
|
|
|
241
259
|
continue;
|
|
242
260
|
}
|
|
243
261
|
|
|
262
|
+
if (currentArgument === '--project-description') {
|
|
263
|
+
parsedInitOptions.projectDescription = commandArguments[argumentIndex + 1] || '';
|
|
264
|
+
argumentIndex += 1;
|
|
265
|
+
continue;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
if (currentArgument.startsWith('--project-description=')) {
|
|
269
|
+
parsedInitOptions.projectDescription = currentArgument.split('=')[1] || '';
|
|
270
|
+
continue;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
if (currentArgument === '--architect-token-budget') {
|
|
274
|
+
const rawTokenBudget = Number.parseInt(commandArguments[argumentIndex + 1], 10);
|
|
275
|
+
if (Number.isNaN(rawTokenBudget)) {
|
|
276
|
+
throw new Error('--architect-token-budget must be a number');
|
|
277
|
+
}
|
|
278
|
+
parsedInitOptions.architectTokenBudget = rawTokenBudget;
|
|
279
|
+
argumentIndex += 1;
|
|
280
|
+
continue;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
if (currentArgument.startsWith('--architect-token-budget=')) {
|
|
284
|
+
const rawTokenBudget = Number.parseInt(currentArgument.split('=')[1], 10);
|
|
285
|
+
if (Number.isNaN(rawTokenBudget)) {
|
|
286
|
+
throw new Error('--architect-token-budget must be a number');
|
|
287
|
+
}
|
|
288
|
+
parsedInitOptions.architectTokenBudget = rawTokenBudget;
|
|
289
|
+
continue;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
if (currentArgument === '--architect-timeout-ms') {
|
|
293
|
+
const rawTimeoutMs = Number.parseInt(commandArguments[argumentIndex + 1], 10);
|
|
294
|
+
if (Number.isNaN(rawTimeoutMs)) {
|
|
295
|
+
throw new Error('--architect-timeout-ms must be a number');
|
|
296
|
+
}
|
|
297
|
+
parsedInitOptions.architectTimeoutMs = rawTimeoutMs;
|
|
298
|
+
argumentIndex += 1;
|
|
299
|
+
continue;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
if (currentArgument.startsWith('--architect-timeout-ms=')) {
|
|
303
|
+
const rawTimeoutMs = Number.parseInt(currentArgument.split('=')[1], 10);
|
|
304
|
+
if (Number.isNaN(rawTimeoutMs)) {
|
|
305
|
+
throw new Error('--architect-timeout-ms must be a number');
|
|
306
|
+
}
|
|
307
|
+
parsedInitOptions.architectTimeoutMs = rawTimeoutMs;
|
|
308
|
+
continue;
|
|
309
|
+
}
|
|
310
|
+
|
|
244
311
|
if (currentArgument === '--runtime-env') {
|
|
245
312
|
parsedInitOptions.runtimeEnv = commandArguments[argumentIndex + 1] || 'auto';
|
|
246
313
|
parsedInitOptions.runtimeEnvProvided = true;
|
|
@@ -271,6 +338,18 @@ export function parseInitArguments(commandArguments) {
|
|
|
271
338
|
throw new Error('--runtime-env must be one of: auto, linux-wsl, linux, windows, macos');
|
|
272
339
|
}
|
|
273
340
|
|
|
341
|
+
if (!Number.isInteger(parsedInitOptions.architectTokenBudget)
|
|
342
|
+
|| parsedInitOptions.architectTokenBudget < ARCHITECT_MIN_TOKEN_BUDGET
|
|
343
|
+
|| parsedInitOptions.architectTokenBudget > ARCHITECT_MAX_TOKEN_BUDGET) {
|
|
344
|
+
throw new Error(`--architect-token-budget must be an integer between ${ARCHITECT_MIN_TOKEN_BUDGET} and ${ARCHITECT_MAX_TOKEN_BUDGET}`);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
if (!Number.isInteger(parsedInitOptions.architectTimeoutMs)
|
|
348
|
+
|| parsedInitOptions.architectTimeoutMs < ARCHITECT_MIN_TIMEOUT_MS
|
|
349
|
+
|| parsedInitOptions.architectTimeoutMs > ARCHITECT_MAX_TIMEOUT_MS) {
|
|
350
|
+
throw new Error(`--architect-timeout-ms must be an integer between ${ARCHITECT_MIN_TIMEOUT_MS} and ${ARCHITECT_MAX_TIMEOUT_MS}`);
|
|
351
|
+
}
|
|
352
|
+
|
|
274
353
|
parsedInitOptions.docsLang = normalizedDocsLanguage;
|
|
275
354
|
parsedInitOptions.runtimeEnv = normalizedRuntimeEnvironment;
|
|
276
355
|
parsedInitOptions.tokenAgent = normalizeAgentName(parsedInitOptions.tokenAgent);
|
|
@@ -546,25 +625,32 @@ export async function runInitCommand(targetDirectoryArgument, initOptions = {})
|
|
|
546
625
|
|| selectedRuntimeEnvironmentKey;
|
|
547
626
|
}
|
|
548
627
|
|
|
628
|
+
const hasExplicitProfileSelection = Boolean(
|
|
629
|
+
initOptions.profile
|
|
630
|
+
|| selectedPreset?.profile
|
|
631
|
+
|| initOptions.newbie
|
|
632
|
+
|| selectedProfilePack?.defaultProfileName
|
|
633
|
+
);
|
|
634
|
+
|
|
549
635
|
const selectedProfileName = initOptions.profile
|
|
550
636
|
? initOptions.profile
|
|
551
637
|
: selectedPreset?.profile
|
|
552
638
|
? selectedPreset.profile
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
'How much guidance do you want?',
|
|
559
|
-
Object.values(PROFILE_PRESETS).map((profilePreset) => `${profilePreset.displayName} — ${profilePreset.description}`),
|
|
560
|
-
userInterface
|
|
561
|
-
)).split('-')[0];
|
|
639
|
+
: initOptions.newbie
|
|
640
|
+
? 'beginner'
|
|
641
|
+
: selectedProfilePack?.defaultProfileName
|
|
642
|
+
? selectedProfilePack.defaultProfileName
|
|
643
|
+
: GOLDEN_STANDARD_PROFILE_NAME;
|
|
562
644
|
|
|
563
645
|
const selectedProfile = PROFILE_PRESETS[selectedProfileName];
|
|
564
646
|
if (!selectedProfile) {
|
|
565
647
|
throw new Error(`Unknown profile: ${selectedProfileName}`);
|
|
566
648
|
}
|
|
567
649
|
|
|
650
|
+
if (!hasExplicitProfileSelection) {
|
|
651
|
+
console.log(`Golden Standard mode enabled. Using ${selectedProfile.displayName} profile by default.`);
|
|
652
|
+
}
|
|
653
|
+
|
|
568
654
|
console.log(`\nSelected profile: ${selectedProfile.displayName}`);
|
|
569
655
|
console.log(`This profile will block these review severities in CI: ${formatBlockingSeverities(selectedProfile.blockingSeverities)}.`);
|
|
570
656
|
|
|
@@ -587,7 +673,11 @@ export async function runInitCommand(targetDirectoryArgument, initOptions = {})
|
|
|
587
673
|
let selectedAdditionalStackFileNames = [];
|
|
588
674
|
let selectedAdditionalBlueprintFileNames = [];
|
|
589
675
|
|
|
590
|
-
|
|
676
|
+
let architectureRecommendation = null;
|
|
677
|
+
let architectPreferenceState = await readArchitectPreferenceState();
|
|
678
|
+
let architectPreferenceUpdated = false;
|
|
679
|
+
|
|
680
|
+
const shouldRunArchitectureRecommendation = !selectedStackFileNameFromOption
|
|
591
681
|
&& !selectedPreset?.stack
|
|
592
682
|
&& !shouldAutoApplyDetectedStack
|
|
593
683
|
&& !selectedProfilePack?.defaultStackFileName
|
|
@@ -601,82 +691,113 @@ export async function runInitCommand(targetDirectoryArgument, initOptions = {})
|
|
|
601
691
|
selectedAdditionalStackFileNames = projectDetection.secondaryStackFileNames || [];
|
|
602
692
|
}
|
|
603
693
|
|
|
604
|
-
if (
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
userInterface
|
|
694
|
+
if (shouldRunArchitectureRecommendation) {
|
|
695
|
+
let architectureProjectDescription = String(initOptions.projectDescription || '').trim();
|
|
696
|
+
|
|
697
|
+
if (!architectureProjectDescription && isInteractiveSession) {
|
|
698
|
+
architectureProjectDescription = (await userInterface.question(
|
|
699
|
+
'\nDescribe your project in one short paragraph for architecture recommendation: '
|
|
700
|
+
)).trim();
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
if (!architectureProjectDescription) {
|
|
704
|
+
architectureProjectDescription = `A software project named ${path.basename(resolvedTargetDirectoryPath)}.`;
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
architectureRecommendation = recommendArchitecture({
|
|
708
|
+
projectDescription: architectureProjectDescription,
|
|
709
|
+
projectDetection,
|
|
710
|
+
stackFileNames,
|
|
711
|
+
blueprintFileNames,
|
|
712
|
+
tokenBudget: initOptions.architectTokenBudget,
|
|
713
|
+
timeoutMs: initOptions.architectTimeoutMs,
|
|
714
|
+
});
|
|
715
|
+
|
|
716
|
+
architectureRecommendation.userVeto = {
|
|
717
|
+
applied: false,
|
|
718
|
+
selectedStackFileName: architectureRecommendation.recommendedStackFileName,
|
|
719
|
+
selectedBlueprintFileName: architectureRecommendation.recommendedBlueprintFileName,
|
|
720
|
+
source: 'recommendation',
|
|
721
|
+
};
|
|
722
|
+
|
|
723
|
+
console.log(formatArchitectureRecommendation(architectureRecommendation));
|
|
724
|
+
|
|
725
|
+
const shouldSkipRecommendationDebate = shouldApplyRepeatedOverridePreference(
|
|
726
|
+
architectPreferenceState,
|
|
727
|
+
architectureRecommendation.recommendedStackFileName
|
|
609
728
|
);
|
|
610
|
-
const selectedProjectScopeKey = resolveProjectScopeKeyFromLabel(selectedProjectScopeLabel);
|
|
611
|
-
const scopeStackFilter = PROJECT_SCOPE_STACK_FILTERS[selectedProjectScopeKey];
|
|
612
|
-
const scopedStackFileNames = filterStackFileNamesByCandidates(stackFileNames, scopeStackFilter);
|
|
613
729
|
|
|
614
|
-
if (
|
|
615
|
-
|
|
616
|
-
|
|
730
|
+
if (shouldSkipRecommendationDebate) {
|
|
731
|
+
architectureRecommendation.failureModes.repeatedOverride = true;
|
|
732
|
+
selectedManualStackFileName = stackFileNames.includes(architectPreferenceState.preferredStackFileName)
|
|
733
|
+
? architectPreferenceState.preferredStackFileName
|
|
734
|
+
: architectureRecommendation.recommendedStackFileName;
|
|
735
|
+
selectedManualBlueprintFileName = blueprintFileNames.includes(architectPreferenceState.preferredBlueprintFileName)
|
|
736
|
+
? architectPreferenceState.preferredBlueprintFileName
|
|
737
|
+
: architectureRecommendation.recommendedBlueprintFileName;
|
|
738
|
+
architectureRecommendation.userVeto = {
|
|
739
|
+
applied: true,
|
|
740
|
+
selectedStackFileName: selectedManualStackFileName,
|
|
741
|
+
selectedBlueprintFileName: selectedManualBlueprintFileName,
|
|
742
|
+
source: 'saved-preference',
|
|
743
|
+
};
|
|
744
|
+
console.log(
|
|
745
|
+
`Repeated override preference detected. Applying ${toTitleCase(selectedManualStackFileName)} + ${toTitleCase(selectedManualBlueprintFileName)} without additional debate.`
|
|
746
|
+
);
|
|
747
|
+
} else if (!isInteractiveSession) {
|
|
748
|
+
selectedManualStackFileName = architectureRecommendation.recommendedStackFileName;
|
|
749
|
+
selectedManualBlueprintFileName = architectureRecommendation.recommendedBlueprintFileName;
|
|
750
|
+
} else {
|
|
751
|
+
const shouldApplyRecommendedArchitecture = await askYesNo(
|
|
752
|
+
'Apply this architecture recommendation?',
|
|
617
753
|
userInterface,
|
|
618
|
-
|
|
754
|
+
true
|
|
619
755
|
);
|
|
620
756
|
|
|
621
|
-
if (
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
const
|
|
626
|
-
'
|
|
627
|
-
|
|
757
|
+
if (shouldApplyRecommendedArchitecture) {
|
|
758
|
+
selectedManualStackFileName = architectureRecommendation.recommendedStackFileName;
|
|
759
|
+
selectedManualBlueprintFileName = architectureRecommendation.recommendedBlueprintFileName;
|
|
760
|
+
} else {
|
|
761
|
+
const vetoStackFileName = await askStackSelection(
|
|
762
|
+
'User veto received. Select stack to apply immediately:',
|
|
763
|
+
stackFileNames,
|
|
628
764
|
userInterface
|
|
629
765
|
);
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
766
|
+
|
|
767
|
+
const vetoBlueprintCandidates = filterBlueprintFileNamesByCandidates(
|
|
768
|
+
blueprintFileNames,
|
|
769
|
+
[BLUEPRINT_RECOMMENDATIONS[vetoStackFileName]].filter(Boolean)
|
|
770
|
+
);
|
|
771
|
+
|
|
772
|
+
const vetoBlueprintFileName = await askBlueprintSelection(
|
|
773
|
+
'Select blueprint to apply immediately (no further debate):',
|
|
774
|
+
vetoBlueprintCandidates,
|
|
633
775
|
userInterface
|
|
634
776
|
);
|
|
635
777
|
|
|
636
|
-
selectedManualStackFileName =
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
778
|
+
selectedManualStackFileName = vetoStackFileName;
|
|
779
|
+
selectedManualBlueprintFileName = vetoBlueprintFileName;
|
|
780
|
+
architectureRecommendation.userVeto = {
|
|
781
|
+
applied: true,
|
|
782
|
+
selectedStackFileName: vetoStackFileName,
|
|
783
|
+
selectedBlueprintFileName: vetoBlueprintFileName,
|
|
784
|
+
source: 'interactive-veto',
|
|
785
|
+
};
|
|
786
|
+
|
|
787
|
+
architectPreferenceState = createUpdatedArchitectPreference(architectPreferenceState, {
|
|
788
|
+
selectedStackFileName: vetoStackFileName,
|
|
789
|
+
selectedBlueprintFileName: vetoBlueprintFileName,
|
|
790
|
+
});
|
|
791
|
+
architectPreferenceUpdated = true;
|
|
640
792
|
|
|
641
|
-
if (
|
|
642
|
-
|
|
643
|
-
blueprintFileNames,
|
|
644
|
-
WEB_FRONTEND_BLUEPRINT_CANDIDATES
|
|
645
|
-
);
|
|
646
|
-
const selectableBackendBlueprints = filterBlueprintFileNamesByCandidates(
|
|
647
|
-
blueprintFileNames,
|
|
648
|
-
WEB_BACKEND_BLUEPRINT_CANDIDATES
|
|
649
|
-
);
|
|
650
|
-
|
|
651
|
-
const selectedFrontendBlueprintFileName = await askBlueprintSelection(
|
|
652
|
-
'Which frontend blueprint should guide UI architecture?',
|
|
653
|
-
selectableFrontendBlueprints,
|
|
654
|
-
userInterface
|
|
655
|
-
);
|
|
656
|
-
const selectedBackendBlueprintFileName = await askBlueprintSelection(
|
|
657
|
-
'Which backend blueprint should guide service architecture?',
|
|
658
|
-
selectableBackendBlueprints,
|
|
659
|
-
userInterface
|
|
660
|
-
);
|
|
661
|
-
|
|
662
|
-
selectedManualBlueprintFileName = selectedBackendBlueprintFileName;
|
|
663
|
-
if (selectedFrontendBlueprintFileName && selectedFrontendBlueprintFileName !== selectedBackendBlueprintFileName) {
|
|
664
|
-
selectedAdditionalBlueprintFileNames = [selectedFrontendBlueprintFileName];
|
|
665
|
-
}
|
|
793
|
+
if (architectPreferenceState.overrideCount >= 2) {
|
|
794
|
+
architectureRecommendation.failureModes.repeatedOverride = true;
|
|
666
795
|
}
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
scopedStackFileNames,
|
|
671
|
-
userInterface
|
|
796
|
+
|
|
797
|
+
console.log(
|
|
798
|
+
`Veto applied. Proceeding with ${toTitleCase(vetoStackFileName)} + ${toTitleCase(vetoBlueprintFileName)} without recommendation loops.`
|
|
672
799
|
);
|
|
673
800
|
}
|
|
674
|
-
} else {
|
|
675
|
-
selectedManualStackFileName = await askStackSelection(
|
|
676
|
-
'Which stack should this governance pack target?',
|
|
677
|
-
scopedStackFileNames,
|
|
678
|
-
userInterface
|
|
679
|
-
);
|
|
680
801
|
}
|
|
681
802
|
}
|
|
682
803
|
|
|
@@ -716,6 +837,20 @@ export async function runInitCommand(targetDirectoryArgument, initOptions = {})
|
|
|
716
837
|
)
|
|
717
838
|
];
|
|
718
839
|
|
|
840
|
+
if (architectureRecommendation) {
|
|
841
|
+
architectureRecommendation.appliedStackFileName = selectedResolvedStackFileName;
|
|
842
|
+
architectureRecommendation.appliedBlueprintFileName = selectedResolvedBlueprintFileName;
|
|
843
|
+
|
|
844
|
+
if (!architectureRecommendation.userVeto) {
|
|
845
|
+
architectureRecommendation.userVeto = {
|
|
846
|
+
applied: false,
|
|
847
|
+
selectedStackFileName: selectedResolvedStackFileName,
|
|
848
|
+
selectedBlueprintFileName: selectedResolvedBlueprintFileName,
|
|
849
|
+
source: 'recommendation',
|
|
850
|
+
};
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
|
|
719
854
|
const derivedAdditionalBlueprintFileNames = deriveAdditionalBlueprintFileNamesFromStacks(
|
|
720
855
|
selectedAdditionalStackFileNames,
|
|
721
856
|
blueprintFileNames,
|
|
@@ -908,8 +1043,13 @@ export async function runInitCommand(targetDirectoryArgument, initOptions = {})
|
|
|
908
1043
|
adapters: memoryContinuityState?.adapters || [],
|
|
909
1044
|
stateFile: isMemoryContinuityEnabled ? '.agent-context/state/memory-continuity.json' : null,
|
|
910
1045
|
},
|
|
1046
|
+
architectRecommendation: architectureRecommendation,
|
|
911
1047
|
});
|
|
912
1048
|
|
|
1049
|
+
if (architectPreferenceUpdated && architectPreferenceState) {
|
|
1050
|
+
await writeArchitectPreferenceState(architectPreferenceState);
|
|
1051
|
+
}
|
|
1052
|
+
|
|
913
1053
|
console.log('\nInitialization complete.');
|
|
914
1054
|
console.log(`- Target directory: ${resolvedTargetDirectoryPath}`);
|
|
915
1055
|
console.log(`- Profile: ${selectedProfile.displayName}`);
|
|
@@ -919,6 +1059,16 @@ export async function runInitCommand(targetDirectoryArgument, initOptions = {})
|
|
|
919
1059
|
if (selectedProfilePack) {
|
|
920
1060
|
console.log(`- Team profile pack: ${selectedProfilePack.displayName}`);
|
|
921
1061
|
}
|
|
1062
|
+
if (architectureRecommendation) {
|
|
1063
|
+
console.log(
|
|
1064
|
+
`- Architect recommendation: ${toTitleCase(architectureRecommendation.recommendedStackFileName)} + ${toTitleCase(architectureRecommendation.recommendedBlueprintFileName)} (${architectureRecommendation.confidenceLabel})`
|
|
1065
|
+
);
|
|
1066
|
+
if (architectureRecommendation.userVeto?.applied) {
|
|
1067
|
+
console.log(
|
|
1068
|
+
`- User veto path: applied (${toTitleCase(architectureRecommendation.userVeto.selectedStackFileName)} + ${toTitleCase(architectureRecommendation.userVeto.selectedBlueprintFileName)})`
|
|
1069
|
+
);
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
922
1072
|
console.log(`- Stack: ${toTitleCase(selectedResolvedStackFileName)}`);
|
|
923
1073
|
if (selectedAdditionalStackFileNames.length > 0) {
|
|
924
1074
|
console.log(`- Additional stacks: ${selectedAdditionalStackFileNames.map((stackFileName) => toTitleCase(stackFileName)).join(', ')}`);
|
package/lib/cli/compiler.mjs
CHANGED
|
@@ -55,6 +55,7 @@ export async function writeOnboardingReport({
|
|
|
55
55
|
operationMode = 'init',
|
|
56
56
|
tokenOptimization = undefined,
|
|
57
57
|
memoryContinuity = undefined,
|
|
58
|
+
architectRecommendation = null,
|
|
58
59
|
}) {
|
|
59
60
|
const onboardingReportPath = path.join(targetDirectoryPath, '.agent-context', 'state', 'onboarding-report.json');
|
|
60
61
|
const resolvedTokenOptimization = typeof tokenOptimization === 'undefined'
|
|
@@ -86,6 +87,7 @@ export async function writeOnboardingReport({
|
|
|
86
87
|
runtimeEnvironment,
|
|
87
88
|
tokenOptimization: resolvedTokenOptimization,
|
|
88
89
|
memoryContinuity: resolvedMemoryContinuity,
|
|
90
|
+
architectRecommendation,
|
|
89
91
|
autoDetection: {
|
|
90
92
|
recommendedStack: projectDetection.recommendedStackFileName,
|
|
91
93
|
recommendedAdditionalStacks: projectDetection.secondaryStackFileNames || [],
|
package/lib/cli/constants.mjs
CHANGED
package/lib/cli/utils.mjs
CHANGED
|
@@ -29,7 +29,7 @@ export function printUsage() {
|
|
|
29
29
|
console.log('');
|
|
30
30
|
console.log('Usage:');
|
|
31
31
|
console.log(' agentic-senior-core launch');
|
|
32
|
-
console.log(' agentic-senior-core init [target-directory] [--preset <name>] [--profile <beginner|balanced|strict>] [--profile-pack <name>] [--stack <name>] [--blueprint <name>] [--ci <true|false>] [--newbie] [--token-optimize] [--no-token-optimize] [--token-agent <name>] [--memory-continuity] [--no-memory-continuity] [--scaffold-docs] [--no-scaffold-docs] [--docs-lang <en|id>] [--project-config <path>] [--runtime-env <auto|linux-wsl|linux|windows|macos>]');
|
|
32
|
+
console.log(' agentic-senior-core init [target-directory] [--preset <name>] [--profile <beginner|balanced|strict>] [--profile-pack <name>] [--stack <name>] [--blueprint <name>] [--project-description <text>] [--architect-token-budget <number>] [--architect-timeout-ms <number>] [--ci <true|false>] [--newbie] [--token-optimize] [--no-token-optimize] [--token-agent <name>] [--memory-continuity] [--no-memory-continuity] [--scaffold-docs] [--no-scaffold-docs] [--docs-lang <en|id>] [--project-config <path>] [--runtime-env <auto|linux-wsl|linux|windows|macos>]');
|
|
33
33
|
console.log(' agentic-senior-core upgrade [target-directory] [--dry-run] [--yes] [--mcp-template]');
|
|
34
34
|
console.log(' agentic-senior-core optimize [target-directory] [--agent <copilot|claude|cursor|windsurf|gemini|codex|cline>] [--enable|--disable] [--show]');
|
|
35
35
|
console.log(' agentic-senior-core mcp');
|
|
@@ -40,12 +40,15 @@ export function printUsage() {
|
|
|
40
40
|
console.log('Options:');
|
|
41
41
|
console.log(' --help Show help');
|
|
42
42
|
console.log(' --version Show CLI version');
|
|
43
|
-
console.log(' --profile
|
|
43
|
+
console.log(' --profile Legacy override for beginner, balanced, or strict (default init path is Golden Standard)');
|
|
44
44
|
console.log(` --preset Use a plug-and-play starter preset (${presetNames})`);
|
|
45
45
|
console.log(' --profile-pack Apply a team profile pack (startup, regulated, platform)');
|
|
46
|
-
console.log(' --newbie
|
|
46
|
+
console.log(' --newbie Legacy alias for --profile beginner');
|
|
47
47
|
console.log(' --stack Override stack selection');
|
|
48
48
|
console.log(' --blueprint Override blueprint selection');
|
|
49
|
+
console.log(' --project-description Architecture intent text used for stack/blueprint recommendation');
|
|
50
|
+
console.log(' --architect-token-budget Max token estimate used by recommendation research (default: 900)');
|
|
51
|
+
console.log(' --architect-timeout-ms Max recommendation research time in milliseconds (default: 1500)');
|
|
49
52
|
console.log(' --ci Override CI/CD guardrails (true|false)');
|
|
50
53
|
console.log(' --token-optimize Explicitly enable token optimization policy during init (default behavior)');
|
|
51
54
|
console.log(' --token-agent Set token optimization agent target (copilot, claude, cursor, windsurf, gemini, codex, cline)');
|