@ryuenn3123/agentic-senior-core 3.0.17 → 3.0.20
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 +84 -94
- package/.agent-context/prompts/init-project.md +32 -100
- package/.agent-context/prompts/refactor.md +22 -44
- package/.agent-context/prompts/review-code.md +28 -52
- package/.agent-context/review-checklists/architecture-review.md +31 -62
- package/.agent-context/review-checklists/pr-checklist.md +74 -108
- package/.agent-context/rules/api-docs.md +18 -206
- package/.agent-context/rules/architecture.md +40 -207
- package/.agent-context/rules/database-design.md +10 -199
- package/.agent-context/rules/docker-runtime.md +5 -5
- package/.agent-context/rules/efficiency-vs-hype.md +11 -149
- package/.agent-context/rules/error-handling.md +9 -231
- package/.agent-context/rules/event-driven.md +17 -221
- package/.agent-context/rules/frontend-architecture.md +66 -119
- package/.agent-context/rules/git-workflow.md +1 -1
- package/.agent-context/rules/microservices.md +28 -161
- package/.agent-context/rules/naming-conv.md +8 -138
- package/.agent-context/rules/performance.md +9 -175
- package/.agent-context/rules/realtime.md +11 -44
- package/.agent-context/rules/security.md +11 -295
- package/.agent-context/rules/testing.md +9 -174
- package/.agent-context/state/benchmark-analysis.json +3 -3
- package/.agent-context/state/memory-continuity-benchmark.json +1 -1
- package/.agent-context/state/onboarding-report.json +71 -11
- package/.agents/workflows/init-project.md +7 -24
- package/.agents/workflows/refactor.md +7 -24
- package/.agents/workflows/review-code.md +7 -24
- package/.cursorrules +22 -21
- package/.gemini/instructions.md +2 -2
- package/.github/copilot-instructions.md +2 -2
- package/.instructions.md +112 -213
- package/.windsurfrules +22 -21
- package/AGENTS.md +4 -4
- package/CONTRIBUTING.md +13 -22
- package/README.md +6 -20
- package/lib/cli/commands/init.mjs +102 -148
- package/lib/cli/commands/launch.mjs +3 -3
- package/lib/cli/commands/optimize.mjs +14 -4
- package/lib/cli/commands/upgrade.mjs +25 -23
- package/lib/cli/compiler.mjs +96 -62
- package/lib/cli/constants.mjs +28 -136
- package/lib/cli/detector/design-evidence.mjs +189 -6
- package/lib/cli/detector.mjs +6 -7
- package/lib/cli/init-detection-flow.mjs +10 -93
- package/lib/cli/init-selection.mjs +2 -68
- package/lib/cli/project-scaffolder/constants.mjs +1 -1
- package/lib/cli/project-scaffolder/design-contract.mjs +438 -335
- package/lib/cli/project-scaffolder/discovery.mjs +36 -82
- package/lib/cli/project-scaffolder/prompt-builders.mjs +55 -63
- package/lib/cli/project-scaffolder/storage.mjs +0 -4
- package/lib/cli/token-optimization.mjs +1 -1
- package/lib/cli/utils.mjs +75 -9
- package/package.json +2 -2
- package/scripts/detection-benchmark.mjs +4 -15
- package/scripts/documentation-boundary-audit.mjs +9 -9
- package/scripts/explain-on-demand-audit.mjs +11 -11
- package/scripts/forbidden-content-check.mjs +9 -9
- package/scripts/frontend-usability-audit.mjs +57 -36
- package/scripts/llm-judge.mjs +1 -1
- package/scripts/mcp-server/constants.mjs +60 -0
- package/scripts/mcp-server/tool-registry.mjs +149 -0
- package/scripts/mcp-server/tools.mjs +446 -0
- package/scripts/mcp-server.mjs +23 -661
- package/scripts/release-gate/audit-checks.mjs +426 -0
- package/scripts/release-gate/constants.mjs +53 -0
- package/scripts/release-gate/runtime.mjs +63 -0
- package/scripts/release-gate/static-checks.mjs +182 -0
- package/scripts/release-gate.mjs +13 -794
- package/scripts/rules-guardian-audit.mjs +14 -13
- package/scripts/single-source-lazy-loading-audit.mjs +3 -3
- package/scripts/sync-thin-adapters.mjs +5 -5
- package/scripts/ui-design-judge/constants.mjs +24 -0
- package/scripts/ui-design-judge/design-execution-summary.mjs +259 -0
- package/scripts/ui-design-judge/git-input.mjs +131 -0
- package/scripts/ui-design-judge/prompting.mjs +73 -0
- package/scripts/ui-design-judge/providers.mjs +102 -0
- package/scripts/ui-design-judge/reporting.mjs +182 -0
- package/scripts/ui-design-judge/rubric-calibration.mjs +214 -0
- package/scripts/ui-design-judge/rubric-goldset.json +188 -0
- package/scripts/ui-design-judge.mjs +166 -771
- package/scripts/ui-rubric-calibration.mjs +35 -0
- package/scripts/validate/config.mjs +198 -55
- package/scripts/validate/coverage-checks.mjs +32 -7
- package/scripts/validate.mjs +8 -4
- package/lib/cli/architect.mjs +0 -431
package/lib/cli/architect.mjs
DELETED
|
@@ -1,431 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs/promises';
|
|
2
|
-
import { existsSync, readFileSync } from 'node:fs';
|
|
3
|
-
import os from 'node:os';
|
|
4
|
-
import path from 'node:path';
|
|
5
|
-
import { fileURLToPath } from 'node:url';
|
|
6
|
-
|
|
7
|
-
import { BLUEPRINT_RECOMMENDATIONS } from './constants.mjs';
|
|
8
|
-
import { ensureDirectory, pathExists, toTitleCase } from './utils.mjs';
|
|
9
|
-
|
|
10
|
-
const ARCHITECT_MODULE_FILE_PATH = fileURLToPath(import.meta.url);
|
|
11
|
-
const ARCHITECT_PACKAGE_ROOT = path.resolve(path.dirname(ARCHITECT_MODULE_FILE_PATH), '..', '..');
|
|
12
|
-
|
|
13
|
-
const ARCHITECT_PREFERENCE_FILE_PATH = process.env.AGENTIC_ARCHITECT_PREF_FILE
|
|
14
|
-
? path.resolve(process.env.AGENTIC_ARCHITECT_PREF_FILE)
|
|
15
|
-
: path.join(os.homedir(), '.agentic-senior-core', 'architect-preferences.json');
|
|
16
|
-
|
|
17
|
-
// Keyword hints — low-confidence bias only, not authoritative research.
|
|
18
|
-
const STACK_SIGNAL_WEIGHTS = {
|
|
19
|
-
'typescript.md': [
|
|
20
|
-
{ term: 'typescript', weight: 0.9 },
|
|
21
|
-
{ term: 'javascript', weight: 0.8 },
|
|
22
|
-
{ term: 'node', weight: 0.55 },
|
|
23
|
-
{ term: 'next.js', weight: 0.7 },
|
|
24
|
-
{ term: 'nextjs', weight: 0.7 },
|
|
25
|
-
{ term: 'react', weight: 0.45 },
|
|
26
|
-
{ term: 'web app', weight: 0.45 },
|
|
27
|
-
{ term: 'frontend', weight: 0.4 },
|
|
28
|
-
{ term: 'dashboard', weight: 0.35 },
|
|
29
|
-
],
|
|
30
|
-
'python.md': [
|
|
31
|
-
{ term: 'python', weight: 0.95 },
|
|
32
|
-
{ term: 'fastapi', weight: 0.8 },
|
|
33
|
-
{ term: 'machine learning', weight: 0.8 },
|
|
34
|
-
{ term: 'ml', weight: 0.4 },
|
|
35
|
-
{ term: 'data', weight: 0.45 },
|
|
36
|
-
{ term: 'ai', weight: 0.45 },
|
|
37
|
-
{ term: 'automation', weight: 0.35 },
|
|
38
|
-
{ term: 'analytics', weight: 0.35 },
|
|
39
|
-
],
|
|
40
|
-
'java.md': [
|
|
41
|
-
{ term: 'java', weight: 0.9 },
|
|
42
|
-
{ term: 'spring', weight: 0.75 },
|
|
43
|
-
{ term: 'enterprise', weight: 0.45 },
|
|
44
|
-
{ term: 'bank', weight: 0.35 },
|
|
45
|
-
{ term: 'regulated', weight: 0.35 },
|
|
46
|
-
{ term: 'jvm', weight: 0.35 },
|
|
47
|
-
],
|
|
48
|
-
'php.md': [
|
|
49
|
-
{ term: 'php', weight: 0.9 },
|
|
50
|
-
{ term: 'laravel', weight: 0.8 },
|
|
51
|
-
{ term: 'cms', weight: 0.4 },
|
|
52
|
-
{ term: 'wordpress', weight: 0.35 },
|
|
53
|
-
],
|
|
54
|
-
'go.md': [
|
|
55
|
-
{ term: 'go', weight: 0.5 },
|
|
56
|
-
{ term: 'golang', weight: 0.9 },
|
|
57
|
-
{ term: 'high throughput', weight: 0.55 },
|
|
58
|
-
{ term: 'microservice', weight: 0.45 },
|
|
59
|
-
{ term: 'kubernetes', weight: 0.45 },
|
|
60
|
-
{ term: 'latency', weight: 0.35 },
|
|
61
|
-
{ term: 'concurrency', weight: 0.35 },
|
|
62
|
-
],
|
|
63
|
-
'csharp.md': [
|
|
64
|
-
{ term: '.net', weight: 0.9 },
|
|
65
|
-
{ term: 'dotnet', weight: 0.9 },
|
|
66
|
-
{ term: 'c#', weight: 0.75 },
|
|
67
|
-
{ term: 'asp.net', weight: 0.8 },
|
|
68
|
-
{ term: 'microsoft', weight: 0.35 },
|
|
69
|
-
],
|
|
70
|
-
'rust.md': [
|
|
71
|
-
{ term: 'rust', weight: 0.9 },
|
|
72
|
-
{ term: 'systems', weight: 0.45 },
|
|
73
|
-
{ term: 'performance', weight: 0.35 },
|
|
74
|
-
{ term: 'memory safety', weight: 0.4 },
|
|
75
|
-
{ term: 'low latency', weight: 0.4 },
|
|
76
|
-
],
|
|
77
|
-
'ruby.md': [
|
|
78
|
-
{ term: 'ruby', weight: 0.9 },
|
|
79
|
-
{ term: 'rails', weight: 0.8 },
|
|
80
|
-
{ term: 'monolith', weight: 0.35 },
|
|
81
|
-
{ term: 'crud', weight: 0.3 },
|
|
82
|
-
],
|
|
83
|
-
'react-native.md': [
|
|
84
|
-
{ term: 'react native', weight: 0.9 },
|
|
85
|
-
{ term: 'mobile', weight: 0.4 },
|
|
86
|
-
{ term: 'android', weight: 0.35 },
|
|
87
|
-
{ term: 'ios', weight: 0.35 },
|
|
88
|
-
{ term: 'cross-platform', weight: 0.4 },
|
|
89
|
-
],
|
|
90
|
-
'flutter.md': [
|
|
91
|
-
{ term: 'flutter', weight: 0.95 },
|
|
92
|
-
{ term: 'dart', weight: 0.8 },
|
|
93
|
-
{ term: 'mobile', weight: 0.4 },
|
|
94
|
-
{ term: 'android', weight: 0.35 },
|
|
95
|
-
{ term: 'ios', weight: 0.35 },
|
|
96
|
-
{ term: 'cross-platform', weight: 0.4 },
|
|
97
|
-
],
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
const STACK_TRADEOFF_SUMMARIES = {
|
|
101
|
-
'typescript.md': 'great fullstack velocity, but dependency churn and runtime startup need discipline.',
|
|
102
|
-
'python.md': 'fast AI and API delivery, but strict latency targets may need extra optimization.',
|
|
103
|
-
'java.md': 'strong enterprise reliability, but setup and boilerplate are heavier.',
|
|
104
|
-
'php.md': 'rapid web product iteration, but architecture boundaries must be guarded early.',
|
|
105
|
-
'go.md': 'excellent throughput and operational simplicity, but abstractions are intentionally minimal.',
|
|
106
|
-
'csharp.md': 'strong for Microsoft-centered teams, but cross-platform tooling choices should be explicit.',
|
|
107
|
-
'rust.md': 'top performance and safety, but development ramp-up is steeper.',
|
|
108
|
-
'ruby.md': 'high productivity for product teams, but scaling strategy should be planned early.',
|
|
109
|
-
'react-native.md': 'single codebase for mobile, but native edge cases still require platform attention.',
|
|
110
|
-
'flutter.md': 'consistent UI across mobile platforms, but ecosystem fit should be checked per package.',
|
|
111
|
-
};
|
|
112
|
-
|
|
113
|
-
function resolveConfidenceLabel(confidenceScore) {
|
|
114
|
-
if (confidenceScore >= 0.85) {
|
|
115
|
-
return 'high';
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
if (confidenceScore >= 0.7) {
|
|
119
|
-
return 'medium';
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
return 'low';
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
function resolveRecommendedBlueprintFileName(stackFileName, blueprintFileNames) {
|
|
126
|
-
const recommendedBlueprintFileName = BLUEPRINT_RECOMMENDATIONS[stackFileName] || null;
|
|
127
|
-
if (recommendedBlueprintFileName && blueprintFileNames.includes(recommendedBlueprintFileName)) {
|
|
128
|
-
return recommendedBlueprintFileName;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
return blueprintFileNames[0] || null;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* Generates a repo-grounded architecture brief based on project description
|
|
136
|
-
* keywords and repository marker detection. This is an offline brief —
|
|
137
|
-
* for ecosystem-level research, the consuming agent should perform live
|
|
138
|
-
* web research rather than relying on stale local snapshots.
|
|
139
|
-
*/
|
|
140
|
-
export function recommendArchitecture({
|
|
141
|
-
projectDescription,
|
|
142
|
-
projectDetection,
|
|
143
|
-
stackFileNames,
|
|
144
|
-
blueprintFileNames,
|
|
145
|
-
}) {
|
|
146
|
-
const normalizedDescription = String(projectDescription || '').trim().toLowerCase();
|
|
147
|
-
const effectiveDescription = normalizedDescription || 'general software project';
|
|
148
|
-
const uncertaintyNotes = [];
|
|
149
|
-
|
|
150
|
-
// Repo marker detection scoring (grounded in actual project files).
|
|
151
|
-
const detectionScoreByStackFileName = new Map();
|
|
152
|
-
for (const rankedCandidate of projectDetection?.rankedCandidates || []) {
|
|
153
|
-
const confidenceScore = Number(rankedCandidate.confidenceScore) || 0;
|
|
154
|
-
if (confidenceScore <= 0) {
|
|
155
|
-
continue;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
detectionScoreByStackFileName.set(
|
|
159
|
-
rankedCandidate.stackFileName,
|
|
160
|
-
confidenceScore * 1.75
|
|
161
|
-
);
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
// Keyword hint scoring (low-confidence bias, not research).
|
|
165
|
-
const scoredStackCandidates = stackFileNames.map((stackFileName) => {
|
|
166
|
-
const configuredSignals = STACK_SIGNAL_WEIGHTS[stackFileName] || [];
|
|
167
|
-
const matchedKeywords = [];
|
|
168
|
-
let keywordScore = 0;
|
|
169
|
-
|
|
170
|
-
for (const configuredSignal of configuredSignals) {
|
|
171
|
-
if (!effectiveDescription.includes(configuredSignal.term)) {
|
|
172
|
-
continue;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
keywordScore += configuredSignal.weight;
|
|
176
|
-
matchedKeywords.push(configuredSignal.term);
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
const detectionScore = detectionScoreByStackFileName.get(stackFileName) || 0;
|
|
180
|
-
const totalScore = 0.2 + keywordScore + detectionScore;
|
|
181
|
-
|
|
182
|
-
return {
|
|
183
|
-
stackFileName,
|
|
184
|
-
totalScore,
|
|
185
|
-
keywordScore,
|
|
186
|
-
detectionScore,
|
|
187
|
-
matchedKeywords,
|
|
188
|
-
};
|
|
189
|
-
}).sort((leftCandidate, rightCandidate) => rightCandidate.totalScore - leftCandidate.totalScore);
|
|
190
|
-
|
|
191
|
-
// Fallback when no candidates can be scored.
|
|
192
|
-
if (scoredStackCandidates.length === 0) {
|
|
193
|
-
const fallbackStackFileName = stackFileNames.includes('typescript.md')
|
|
194
|
-
? 'typescript.md'
|
|
195
|
-
: stackFileNames[0] || 'typescript.md';
|
|
196
|
-
const fallbackBlueprintFileName = resolveRecommendedBlueprintFileName(fallbackStackFileName, blueprintFileNames);
|
|
197
|
-
|
|
198
|
-
return {
|
|
199
|
-
briefType: 'offline',
|
|
200
|
-
projectDescription: String(projectDescription || '').trim(),
|
|
201
|
-
recommendedStackFileName: fallbackStackFileName,
|
|
202
|
-
recommendedBlueprintFileName: fallbackBlueprintFileName,
|
|
203
|
-
confidenceLabel: 'low',
|
|
204
|
-
confidenceScore: 0.35,
|
|
205
|
-
rationaleSentences: [
|
|
206
|
-
`Defaulting to ${toTitleCase(fallbackStackFileName)} with ${toTitleCase(fallbackBlueprintFileName)} as a safe fallback.`,
|
|
207
|
-
'No keyword or detection signals were strong enough for a grounded recommendation.',
|
|
208
|
-
'For ecosystem-level validation, perform live web research before committing to this stack.',
|
|
209
|
-
],
|
|
210
|
-
alternatives: [],
|
|
211
|
-
uncertaintyNotes: [
|
|
212
|
-
'This is an offline brief based on keyword hints and repo markers only.',
|
|
213
|
-
'Ecosystem fitness was not verified — the agent should research before finalizing.',
|
|
214
|
-
],
|
|
215
|
-
signalSummary: 'fallback (no strong signals)',
|
|
216
|
-
failureModes: {
|
|
217
|
-
lowConfidence: true,
|
|
218
|
-
dataConflict: false,
|
|
219
|
-
repeatedOverride: false,
|
|
220
|
-
},
|
|
221
|
-
};
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
const strongestCandidate = scoredStackCandidates[0];
|
|
225
|
-
const secondCandidate = scoredStackCandidates[1] || null;
|
|
226
|
-
const scoreGap = secondCandidate
|
|
227
|
-
? strongestCandidate.totalScore - secondCandidate.totalScore
|
|
228
|
-
: strongestCandidate.totalScore;
|
|
229
|
-
|
|
230
|
-
let confidenceScore = 0.55
|
|
231
|
-
+ Math.min(strongestCandidate.totalScore / 8, 0.25)
|
|
232
|
-
+ Math.min(Math.max(scoreGap, 0) / 3, 0.18);
|
|
233
|
-
|
|
234
|
-
if (strongestCandidate.matchedKeywords.length === 0) {
|
|
235
|
-
confidenceScore -= 0.2;
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
if (secondCandidate && scoreGap < 0.18) {
|
|
239
|
-
confidenceScore -= 0.1;
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
confidenceScore = Math.min(Math.max(confidenceScore, 0.35), 0.97);
|
|
243
|
-
|
|
244
|
-
const confidenceLabel = resolveConfidenceLabel(confidenceScore);
|
|
245
|
-
const lowConfidence = confidenceScore < 0.7;
|
|
246
|
-
const dataConflict = Boolean(secondCandidate && scoreGap < 0.18);
|
|
247
|
-
|
|
248
|
-
if (lowConfidence) {
|
|
249
|
-
uncertaintyNotes.push('Low confidence: description did not map strongly to a single stack profile.');
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
if (dataConflict) {
|
|
253
|
-
uncertaintyNotes.push('Data conflict: top stack candidates are close, so trade-offs need manual confirmation.');
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
// Offline briefs should always be transparent about their grounding source.
|
|
257
|
-
uncertaintyNotes.push(
|
|
258
|
-
'This brief is grounded in repo markers and keyword hints only. '
|
|
259
|
-
+ 'For ecosystem-level validation, the agent should perform live web research.'
|
|
260
|
-
);
|
|
261
|
-
|
|
262
|
-
const recommendedStackFileName = strongestCandidate.stackFileName;
|
|
263
|
-
const recommendedBlueprintFileName = resolveRecommendedBlueprintFileName(recommendedStackFileName, blueprintFileNames);
|
|
264
|
-
|
|
265
|
-
const signalSummaryParts = [];
|
|
266
|
-
if (strongestCandidate.matchedKeywords.length > 0) {
|
|
267
|
-
signalSummaryParts.push(`keywords: ${strongestCandidate.matchedKeywords.slice(0, 4).join(', ')}`);
|
|
268
|
-
}
|
|
269
|
-
if (strongestCandidate.detectionScore > 0) {
|
|
270
|
-
signalSummaryParts.push(`repo detection: ${strongestCandidate.detectionScore.toFixed(2)}`);
|
|
271
|
-
}
|
|
272
|
-
const signalSummary = signalSummaryParts.length > 0
|
|
273
|
-
? signalSummaryParts.join('; ')
|
|
274
|
-
: 'weak signals only';
|
|
275
|
-
|
|
276
|
-
const rationaleSentences = [
|
|
277
|
-
`Stack brief: ${toTitleCase(recommendedStackFileName)} with ${toTitleCase(recommendedBlueprintFileName)}.`,
|
|
278
|
-
`Grounding signals: ${signalSummary}.`,
|
|
279
|
-
`Main trade-off: ${STACK_TRADEOFF_SUMMARIES[recommendedStackFileName] || 'validate ecosystem fit before production rollout.'}`,
|
|
280
|
-
];
|
|
281
|
-
|
|
282
|
-
if (lowConfidence) {
|
|
283
|
-
rationaleSentences.push('Confidence is low — review alternatives and perform live research before finalizing.');
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
const alternatives = scoredStackCandidates
|
|
287
|
-
.slice(1, 3)
|
|
288
|
-
.map((stackCandidate) => {
|
|
289
|
-
const alternativeBlueprintFileName = resolveRecommendedBlueprintFileName(stackCandidate.stackFileName, blueprintFileNames);
|
|
290
|
-
return {
|
|
291
|
-
stackFileName: stackCandidate.stackFileName,
|
|
292
|
-
blueprintFileName: alternativeBlueprintFileName,
|
|
293
|
-
oneLineTradeoff: STACK_TRADEOFF_SUMMARIES[stackCandidate.stackFileName]
|
|
294
|
-
|| 'validate fit with your runtime and team constraints.',
|
|
295
|
-
evidenceSummary: `keyword=${stackCandidate.keywordScore.toFixed(2)}, detection=${stackCandidate.detectionScore.toFixed(2)}`,
|
|
296
|
-
};
|
|
297
|
-
});
|
|
298
|
-
|
|
299
|
-
return {
|
|
300
|
-
briefType: 'offline',
|
|
301
|
-
projectDescription: String(projectDescription || '').trim(),
|
|
302
|
-
recommendedStackFileName,
|
|
303
|
-
recommendedBlueprintFileName,
|
|
304
|
-
confidenceLabel,
|
|
305
|
-
confidenceScore: Number(confidenceScore.toFixed(2)),
|
|
306
|
-
rationaleSentences,
|
|
307
|
-
alternatives,
|
|
308
|
-
uncertaintyNotes,
|
|
309
|
-
signalSummary,
|
|
310
|
-
failureModes: {
|
|
311
|
-
lowConfidence,
|
|
312
|
-
dataConflict,
|
|
313
|
-
repeatedOverride: false,
|
|
314
|
-
},
|
|
315
|
-
};
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
export function formatArchitectureRecommendation(architectureRecommendation) {
|
|
319
|
-
const outputLines = [
|
|
320
|
-
'\nArchitecture brief (offline, repo-grounded):',
|
|
321
|
-
`- Stack: ${toTitleCase(architectureRecommendation.recommendedStackFileName)}`,
|
|
322
|
-
`- Blueprint: ${toTitleCase(architectureRecommendation.recommendedBlueprintFileName)}`,
|
|
323
|
-
`- Confidence: ${architectureRecommendation.confidenceLabel} (${architectureRecommendation.confidenceScore})`,
|
|
324
|
-
'- Rationale:',
|
|
325
|
-
...architectureRecommendation.rationaleSentences.map((sentence, sentenceIndex) => ` ${sentenceIndex + 1}. ${sentence}`),
|
|
326
|
-
'- Alternatives:',
|
|
327
|
-
];
|
|
328
|
-
|
|
329
|
-
if (architectureRecommendation.alternatives.length === 0) {
|
|
330
|
-
outputLines.push(' 1. No strong alternatives detected from current input signals.');
|
|
331
|
-
} else {
|
|
332
|
-
outputLines.push(
|
|
333
|
-
...architectureRecommendation.alternatives.map(
|
|
334
|
-
(alternative, alternativeIndex) => ` ${alternativeIndex + 1}. ${toTitleCase(alternative.stackFileName)} + ${toTitleCase(alternative.blueprintFileName)}: ${alternative.oneLineTradeoff}`
|
|
335
|
-
)
|
|
336
|
-
);
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
if (architectureRecommendation.uncertaintyNotes.length > 0) {
|
|
340
|
-
outputLines.push('- Uncertainty notes:');
|
|
341
|
-
outputLines.push(
|
|
342
|
-
...architectureRecommendation.uncertaintyNotes.map(
|
|
343
|
-
(uncertaintyNote, uncertaintyIndex) => ` ${uncertaintyIndex + 1}. ${uncertaintyNote}`
|
|
344
|
-
)
|
|
345
|
-
);
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
const cautionLabels = [];
|
|
349
|
-
if (architectureRecommendation.failureModes.lowConfidence) {
|
|
350
|
-
cautionLabels.push('low-confidence');
|
|
351
|
-
}
|
|
352
|
-
if (architectureRecommendation.failureModes.dataConflict) {
|
|
353
|
-
cautionLabels.push('data-conflict');
|
|
354
|
-
}
|
|
355
|
-
if (architectureRecommendation.failureModes.repeatedOverride) {
|
|
356
|
-
cautionLabels.push('repeated-override');
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
if (cautionLabels.length > 0) {
|
|
360
|
-
outputLines.push(`- Caution labels: ${cautionLabels.join(', ')}`);
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
return outputLines.join('\n');
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
export async function readArchitectPreferenceState() {
|
|
367
|
-
if (!(await pathExists(ARCHITECT_PREFERENCE_FILE_PATH))) {
|
|
368
|
-
return null;
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
try {
|
|
372
|
-
const rawPreferenceContent = await fs.readFile(ARCHITECT_PREFERENCE_FILE_PATH, 'utf8');
|
|
373
|
-
const parsedPreferencePayload = JSON.parse(rawPreferenceContent);
|
|
374
|
-
const parsedPreferenceState = parsedPreferencePayload?.preference;
|
|
375
|
-
|
|
376
|
-
if (!parsedPreferenceState?.preferredStackFileName) {
|
|
377
|
-
return null;
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
return {
|
|
381
|
-
preferredStackFileName: parsedPreferenceState.preferredStackFileName,
|
|
382
|
-
preferredBlueprintFileName: parsedPreferenceState.preferredBlueprintFileName || null,
|
|
383
|
-
overrideCount: Number(parsedPreferenceState.overrideCount) || 0,
|
|
384
|
-
lastOverrideAt: parsedPreferenceState.lastOverrideAt || null,
|
|
385
|
-
};
|
|
386
|
-
} catch {
|
|
387
|
-
return null;
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
export function createUpdatedArchitectPreference(currentPreferenceState, {
|
|
392
|
-
selectedStackFileName,
|
|
393
|
-
selectedBlueprintFileName,
|
|
394
|
-
}) {
|
|
395
|
-
const currentOverrideCount = Number(currentPreferenceState?.overrideCount) || 0;
|
|
396
|
-
|
|
397
|
-
return {
|
|
398
|
-
preferredStackFileName: selectedStackFileName,
|
|
399
|
-
preferredBlueprintFileName: selectedBlueprintFileName,
|
|
400
|
-
overrideCount: currentOverrideCount + 1,
|
|
401
|
-
lastOverrideAt: new Date().toISOString(),
|
|
402
|
-
};
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
export function shouldApplyRepeatedOverridePreference(preferenceState, recommendedStackFileName) {
|
|
406
|
-
if (!preferenceState?.preferredStackFileName) {
|
|
407
|
-
return false;
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
if ((Number(preferenceState.overrideCount) || 0) < 2) {
|
|
411
|
-
return false;
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
return preferenceState.preferredStackFileName !== recommendedStackFileName;
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
export async function writeArchitectPreferenceState(preferenceState) {
|
|
418
|
-
const preferencePayload = {
|
|
419
|
-
schemaVersion: '1.0.0',
|
|
420
|
-
updatedAt: new Date().toISOString(),
|
|
421
|
-
preference: {
|
|
422
|
-
preferredStackFileName: preferenceState.preferredStackFileName,
|
|
423
|
-
preferredBlueprintFileName: preferenceState.preferredBlueprintFileName,
|
|
424
|
-
overrideCount: preferenceState.overrideCount,
|
|
425
|
-
lastOverrideAt: preferenceState.lastOverrideAt,
|
|
426
|
-
},
|
|
427
|
-
};
|
|
428
|
-
|
|
429
|
-
await ensureDirectory(path.dirname(ARCHITECT_PREFERENCE_FILE_PATH));
|
|
430
|
-
await fs.writeFile(ARCHITECT_PREFERENCE_FILE_PATH, JSON.stringify(preferencePayload, null, 2) + '\n', 'utf8');
|
|
431
|
-
}
|