@chemx/starter-kit 26.9.12-93 → 26.9.13-383
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/cli/audit/ai-slop-detector.js +32 -48
- package/cli/audit/ast-visitors.js +10 -13
- package/cli/audit/extended-visitors.js +10 -4
- package/cli/audit/history.js +47 -15
- package/cli/audit/pattern-detector.js +182 -0
- package/cli/audit/prompts.js +165 -71
- package/cli/audit/reporter-banner.js +4 -16
- package/cli/audit/reporter-grades.js +28 -16
- package/cli/audit/reporter-grouping.d.ts +6 -0
- package/cli/audit/reporter-grouping.js +48 -5
- package/cli/audit/reporter-markdown.js +31 -16
- package/cli/audit/reporter-sections.d.ts +1 -0
- package/cli/audit/reporter-sections.js +83 -13
- package/cli/audit/reporter-utils.js +116 -1
- package/cli/audit/reporter.d.ts +13 -4
- package/cli/audit/reporter.js +34 -5
- package/cli/audit/roadmap.js +236 -0
- package/cli/audit/rules-predicates.d.ts +22 -0
- package/cli/audit/rules-predicates.js +139 -0
- package/cli/audit/rules.js +6 -2
- package/cli/audit/social-gh.js +2 -6
- package/cli/audit/social-publisher.js +1 -1
- package/cli/audit/social.js +25 -9
- package/cli/audit/types.d.ts +52 -5
- package/cli/audit.js +89 -14
- package/cli/badge.js +9 -2
- package/cli/build/detector.js +68 -0
- package/cli/build/executor.js +58 -0
- package/cli/build/grouper.js +78 -0
- package/cli/build/parser-matchers.js +116 -0
- package/cli/build/parser.js +80 -0
- package/cli/build/reporter.js +66 -0
- package/cli/build/types.d.ts +58 -0
- package/cli/build/types.js +1 -0
- package/cli/build.d.ts +16 -0
- package/cli/build.js +59 -0
- package/cli/generator.js +4 -1
- package/cli/help.js +14 -0
- package/cli/index.js +43 -15
- package/cli/installer-templates.js +43 -4
- package/cli/license.js +59 -6
- package/cli/navigator-actions.js +114 -90
- package/cli/navigator-banner.js +4 -2
- package/cli/navigator-grades.js +28 -0
- package/cli/navigator-menu.js +32 -13
- package/cli/navigator-share.js +1 -1
- package/cli/navigator.d.ts +10 -1
- package/cli/navigator.js +5 -3
- package/cli/scaffold.js +24 -9
- package/cli/terminal.js +6 -5
- package/cli/theme.d.ts +41 -0
- package/cli/theme.js +84 -0
- package/docs/CHANGELOG.md +68 -0
- package/package.json +3 -2
package/cli/audit/prompts.js
CHANGED
|
@@ -1,74 +1,132 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
1
|
+
import { groupViolationsByRule, buildPathTree } from './reporter-grouping.js';
|
|
2
|
+
import {
|
|
3
|
+
CYAN,
|
|
4
|
+
YELLOW,
|
|
5
|
+
RED,
|
|
6
|
+
ORANGE,
|
|
7
|
+
DIM,
|
|
8
|
+
BOLD,
|
|
9
|
+
RESET
|
|
10
|
+
} from './reporter-utils.js';
|
|
11
|
+
|
|
12
|
+
const SEVERITY_COLORS = {
|
|
13
|
+
CRITICAL: RED,
|
|
14
|
+
HIGH: ORANGE,
|
|
15
|
+
MEDIUM: YELLOW,
|
|
16
|
+
LOW: DIM
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const renderTreeLines = (node, depth = 0) => {
|
|
20
|
+
const lines = [];
|
|
21
|
+
const indent = ' ' + ' '.repeat(depth);
|
|
22
|
+
|
|
23
|
+
const sortedDirs = Array.from(node.dirs.entries()).sort((a, b) => a[0].localeCompare(b[0]));
|
|
24
|
+
for (const [dirName, childNode] of sortedDirs) {
|
|
25
|
+
lines.push(`${indent}📁 ${BOLD}${dirName}${RESET}`);
|
|
26
|
+
const childLines = renderTreeLines(childNode, depth + 1);
|
|
27
|
+
lines.push(...childLines);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const sortedFiles = Array.from(node.files.entries()).sort((a, b) => a[0].localeCompare(b[0]));
|
|
31
|
+
for (const [fileName, locs] of sortedFiles) {
|
|
32
|
+
const uniqueLocs = Array.from(new Set(locs)).join(', ');
|
|
33
|
+
lines.push(`${indent}- \`${YELLOW}${fileName}:${uniqueLocs}${RESET}\``);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return lines;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export const formatGroupedPromptViolations = (violations = []) => {
|
|
40
|
+
const hasNoViolations = violations.length === 0;
|
|
41
|
+
if (hasNoViolations) return [];
|
|
42
|
+
|
|
43
|
+
const ruleGroups = groupViolationsByRule(violations);
|
|
44
|
+
const lines = [];
|
|
45
|
+
|
|
46
|
+
ruleGroups.forEach((rg, idx) => {
|
|
47
|
+
const sevColor = SEVERITY_COLORS[rg.severity] || YELLOW;
|
|
48
|
+
const countLabel = rg.total === 1 ? '1 item' : `${rg.total} items`;
|
|
49
|
+
lines.push(`${idx + 1}. ${sevColor}[${rg.rule}]${RESET} ${BOLD}(${countLabel})${RESET}`);
|
|
50
|
+
lines.push(` Hazard: ${rg.hazard}`);
|
|
51
|
+
lines.push(` Directive: ${CYAN}${rg.directive}${RESET}`);
|
|
52
|
+
lines.push(' Locations:');
|
|
53
|
+
|
|
54
|
+
const tree = buildPathTree(rg.violations);
|
|
55
|
+
const treeLines = renderTreeLines(tree, 0);
|
|
56
|
+
lines.push(...treeLines);
|
|
57
|
+
lines.push('');
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
return lines;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export const buildGradeFPrompt = (report, options = {}) => {
|
|
64
|
+
const { excludeAiSlop = false } = options;
|
|
65
|
+
const { violations = [], hotspots = [] } = report;
|
|
66
|
+
const critical = violations
|
|
67
|
+
.filter((v) => v.severity === 'CRITICAL')
|
|
68
|
+
.filter((v) => (excludeAiSlop ? !v.isAiSlop : true));
|
|
9
69
|
const extremeMonoliths = hotspots.filter((h) => h.lineCount >= 2000);
|
|
10
70
|
|
|
11
|
-
|
|
71
|
+
const hasNoCritical = critical.length === 0;
|
|
72
|
+
const hasNoMonoliths = extremeMonoliths.length === 0;
|
|
73
|
+
if (hasNoCritical && hasNoMonoliths) return '';
|
|
12
74
|
|
|
13
75
|
const lines = [];
|
|
14
76
|
lines.push('Act as a Principal Systems Architect. Surgically refactor the following Grade F Critical Context Hazards in our codebase according to Chemical X Molecular Architecture Standards:\n');
|
|
15
77
|
|
|
16
78
|
if (extremeMonoliths.length > 0) {
|
|
17
79
|
lines.push('### EXTREME MONOLITHS (>= 2,000 lines of code) : MONOLITH DECOMPOSITION');
|
|
80
|
+
lines.push('Action: Decompose into crystalline single-responsibility capsules (< 100 lines per molecule). Convert top-level view into a declarative Table-of-Contents view.\n');
|
|
18
81
|
extremeMonoliths.forEach((h, i) => {
|
|
19
82
|
lines.push(`${i + 1}. File: \`${h.filePath}\` (${h.lineCount} lines)`);
|
|
20
|
-
lines.push(' Action: Decompose this monolith into crystalline single-responsibility capsules (< 100 lines per molecule). Convert top-level view into a declarative Table-of-Contents view.');
|
|
21
83
|
});
|
|
22
84
|
lines.push('');
|
|
23
85
|
}
|
|
24
86
|
|
|
25
87
|
if (critical.length > 0) {
|
|
26
88
|
lines.push('### CRITICAL AST VIOLATIONS');
|
|
27
|
-
|
|
28
|
-
lines.push(`${i + 1}. \`${v.filePath}:${v.line}:${v.column}\` [${v.rule}]`);
|
|
29
|
-
lines.push(` Hazard: ${v.hazard}`);
|
|
30
|
-
lines.push(` Directive: ${v.directive}`);
|
|
31
|
-
});
|
|
32
|
-
lines.push('');
|
|
89
|
+
lines.push(...formatGroupedPromptViolations(critical));
|
|
33
90
|
}
|
|
34
91
|
|
|
35
92
|
lines.push('### STRICT EXECUTION RULES:');
|
|
36
|
-
lines.push('1.
|
|
37
|
-
lines.push('2.
|
|
38
|
-
lines.push('3.
|
|
39
|
-
lines.push('4. Zero
|
|
40
|
-
lines.push('5.
|
|
93
|
+
lines.push('1. Pre-Split Pattern Discovery: Survey cross-file patterns before slicing; extract canonical shared capsules first to avoid proliferating duplicate one-off patterns.');
|
|
94
|
+
lines.push('2. Every new molecule component must stay under 100 lines.');
|
|
95
|
+
lines.push('3. Top-level page views must be 10-20 line declarative Table-of-Contents templates assembling components via named slots.');
|
|
96
|
+
lines.push('4. Zero synthetic or mock data: return live data or explicit empty states.');
|
|
97
|
+
lines.push('5. Zero render-hack setTimeout: replace with await nextTick() or flush: post.');
|
|
98
|
+
lines.push('6. Output surgical diffs or modular replacements. Preserve existing external exports.');
|
|
41
99
|
|
|
42
100
|
return lines.join('\n');
|
|
43
101
|
};
|
|
44
102
|
|
|
45
|
-
export const buildGradeDPrompt = (report) => {
|
|
46
|
-
const {
|
|
47
|
-
const
|
|
103
|
+
export const buildGradeDPrompt = (report, options = {}) => {
|
|
104
|
+
const { excludeAiSlop = false } = options;
|
|
105
|
+
const { violations = [], hotspots = [] } = report;
|
|
106
|
+
const high = violations
|
|
107
|
+
.filter((v) => v.severity === 'HIGH')
|
|
108
|
+
.filter((v) => (excludeAiSlop ? !v.isAiSlop : true));
|
|
48
109
|
const severeMonoliths = hotspots.filter((h) => h.lineCount >= 1000 && h.lineCount < 2000);
|
|
49
110
|
|
|
50
|
-
|
|
111
|
+
const hasNoHigh = high.length === 0;
|
|
112
|
+
const hasNoMonoliths = severeMonoliths.length === 0;
|
|
113
|
+
if (hasNoHigh && hasNoMonoliths) return '';
|
|
51
114
|
|
|
52
115
|
const lines = [];
|
|
53
116
|
lines.push('Act as a Principal Systems Architect. Refactor the following Grade D High-Severity Architectural Debts according to Chemical X Molecular Architecture Standards:\n');
|
|
54
117
|
|
|
55
118
|
if (severeMonoliths.length > 0) {
|
|
56
119
|
lines.push('### SEVERE MONOLITHS (1,000 - 1,999 lines of code)');
|
|
120
|
+
lines.push('Action: Extract sub-features into isolated molecule capsules (< 100 lines of code) and domain composables.\n');
|
|
57
121
|
severeMonoliths.forEach((h, i) => {
|
|
58
122
|
lines.push(`${i + 1}. File: \`${h.filePath}\` (${h.lineCount} lines)`);
|
|
59
|
-
lines.push(' Action: Extract sub-features into isolated molecule capsules (< 100 lines of code) and domain composables.');
|
|
60
123
|
});
|
|
61
124
|
lines.push('');
|
|
62
125
|
}
|
|
63
126
|
|
|
64
127
|
if (high.length > 0) {
|
|
65
128
|
lines.push('### HIGH SEVERITY VIOLATIONS');
|
|
66
|
-
|
|
67
|
-
lines.push(`${i + 1}. \`${v.filePath}:${v.line}:${v.column}\` [${v.rule}]`);
|
|
68
|
-
lines.push(` Hazard: ${v.hazard}`);
|
|
69
|
-
lines.push(` Directive: ${v.directive}`);
|
|
70
|
-
});
|
|
71
|
-
lines.push('');
|
|
129
|
+
lines.push(...formatGroupedPromptViolations(high));
|
|
72
130
|
}
|
|
73
131
|
|
|
74
132
|
lines.push('### STRICT EXECUTION RULES:');
|
|
@@ -79,33 +137,33 @@ export const buildGradeDPrompt = (report) => {
|
|
|
79
137
|
return lines.join('\n');
|
|
80
138
|
};
|
|
81
139
|
|
|
82
|
-
export const buildGradeCPrompt = (report) => {
|
|
83
|
-
const {
|
|
84
|
-
const
|
|
140
|
+
export const buildGradeCPrompt = (report, options = {}) => {
|
|
141
|
+
const { excludeAiSlop = false } = options;
|
|
142
|
+
const { violations = [], hotspots = [] } = report;
|
|
143
|
+
const medium = violations
|
|
144
|
+
.filter((v) => v.severity === 'MEDIUM')
|
|
145
|
+
.filter((v) => (excludeAiSlop ? !v.isAiSlop : true));
|
|
85
146
|
const warningMonoliths = hotspots.filter((h) => h.lineCount >= 500 && h.lineCount < 1000);
|
|
86
147
|
|
|
87
|
-
|
|
148
|
+
const hasNoMedium = medium.length === 0;
|
|
149
|
+
const hasNoMonoliths = warningMonoliths.length === 0;
|
|
150
|
+
if (hasNoMedium && hasNoMonoliths) return '';
|
|
88
151
|
|
|
89
152
|
const lines = [];
|
|
90
153
|
lines.push('Act as a Senior Frontend Engineer. Refactor the following Grade C Medium-Severity Technical Debts according to Chemical X Molecular Architecture Standards:\n');
|
|
91
154
|
|
|
92
155
|
if (warningMonoliths.length > 0) {
|
|
93
156
|
lines.push('### WARNING MONOLITHS (500 - 999 lines of code)');
|
|
157
|
+
lines.push('Action: Bring file under 500 line budget by extracting helper functions, types, and child molecules.\n');
|
|
94
158
|
warningMonoliths.forEach((h, i) => {
|
|
95
159
|
lines.push(`${i + 1}. File: \`${h.filePath}\` (${h.lineCount} lines)`);
|
|
96
|
-
lines.push(' Action: Bring file under 500 line budget by extracting helper functions, types, and child molecules.');
|
|
97
160
|
});
|
|
98
161
|
lines.push('');
|
|
99
162
|
}
|
|
100
163
|
|
|
101
164
|
if (medium.length > 0) {
|
|
102
165
|
lines.push('### MEDIUM SEVERITY VIOLATIONS');
|
|
103
|
-
|
|
104
|
-
lines.push(`${i + 1}. \`${v.filePath}:${v.line}:${v.column}\` [${v.rule}]`);
|
|
105
|
-
lines.push(` Hazard: ${v.hazard}`);
|
|
106
|
-
lines.push(` Directive: ${v.directive}`);
|
|
107
|
-
});
|
|
108
|
-
lines.push('');
|
|
166
|
+
lines.push(...formatGroupedPromptViolations(medium));
|
|
109
167
|
}
|
|
110
168
|
|
|
111
169
|
lines.push('### STRICT EXECUTION RULES:');
|
|
@@ -116,22 +174,21 @@ export const buildGradeCPrompt = (report) => {
|
|
|
116
174
|
return lines.join('\n');
|
|
117
175
|
};
|
|
118
176
|
|
|
119
|
-
export const buildGradeBPrompt = (report) => {
|
|
120
|
-
const {
|
|
121
|
-
const
|
|
177
|
+
export const buildGradeBPrompt = (report, options = {}) => {
|
|
178
|
+
const { excludeAiSlop = false } = options;
|
|
179
|
+
const { violations = [] } = report;
|
|
180
|
+
const low = violations
|
|
181
|
+
.filter((v) => v.severity === 'LOW')
|
|
182
|
+
.filter((v) => (excludeAiSlop ? !v.isAiSlop : true));
|
|
122
183
|
|
|
123
|
-
|
|
184
|
+
const hasNoLow = low.length === 0;
|
|
185
|
+
if (hasNoLow) return '';
|
|
124
186
|
|
|
125
187
|
const lines = [];
|
|
126
188
|
lines.push('Act as a Clean Code Specialist. Clean up the following Grade B Low-Severity Hygiene Issues according to Chemical X standards:\n');
|
|
127
189
|
|
|
128
190
|
lines.push('### LOW HYGIENE VIOLATIONS');
|
|
129
|
-
|
|
130
|
-
lines.push(`${i + 1}. \`${v.filePath}:${v.line}:${v.column}\` [${v.rule}]`);
|
|
131
|
-
lines.push(` Hazard: ${v.hazard}`);
|
|
132
|
-
lines.push(` Directive: ${v.directive}`);
|
|
133
|
-
});
|
|
134
|
-
lines.push('');
|
|
191
|
+
lines.push(...formatGroupedPromptViolations(low));
|
|
135
192
|
|
|
136
193
|
lines.push('### STRICT EXECUTION RULES:');
|
|
137
194
|
lines.push('1. Typography Hygiene: Replace all em dashes with standard hyphens (-) or colons (:).');
|
|
@@ -145,18 +202,14 @@ export const buildAiSlopPrompt = (report) => {
|
|
|
145
202
|
const { violations = [] } = report;
|
|
146
203
|
const slopViolations = violations.filter((v) => Boolean(v.isAiSlop));
|
|
147
204
|
|
|
148
|
-
|
|
205
|
+
const hasNoSlop = slopViolations.length === 0;
|
|
206
|
+
if (hasNoSlop) return '';
|
|
149
207
|
|
|
150
208
|
const lines = [];
|
|
151
209
|
lines.push('Act as a Clean Code Specialist and Code Authenticity Guardian. Eliminate the following AI Slop and conversational artifacts from our codebase according to Chemical X standards:\n');
|
|
152
210
|
|
|
153
211
|
lines.push('### AI SLOP & CODE AUTHENTICITY VIOLATIONS');
|
|
154
|
-
|
|
155
|
-
lines.push(`${i + 1}. \`${v.filePath}:${v.line}:${v.column}\` [${v.rule}]`);
|
|
156
|
-
lines.push(` Hazard: ${v.hazard}`);
|
|
157
|
-
lines.push(` Directive: ${v.directive}`);
|
|
158
|
-
});
|
|
159
|
-
lines.push('');
|
|
212
|
+
lines.push(...formatGroupedPromptViolations(slopViolations));
|
|
160
213
|
|
|
161
214
|
lines.push('### STRICT EXECUTION RULES:');
|
|
162
215
|
lines.push('1. Conversational Residue: Completely remove leaked AI conversational preambles, assistant markdown code fences, and pleasantry comments.');
|
|
@@ -173,24 +226,64 @@ export const buildHotspotsPrompt = (report) => {
|
|
|
173
226
|
const { hotspots = [] } = report;
|
|
174
227
|
const monolithHotspots = hotspots.filter((h) => h.isMonolith || h.lineCount > 500);
|
|
175
228
|
|
|
176
|
-
|
|
229
|
+
const hasNoMonoliths = monolithHotspots.length === 0;
|
|
230
|
+
if (hasNoMonoliths) return '';
|
|
177
231
|
|
|
178
232
|
const lines = [];
|
|
179
233
|
lines.push('Act as a Principal Systems Architect. Surgically decompose the following monolithic hotspot files according to Chemical X Molecular Architecture Standards:\n');
|
|
180
234
|
|
|
181
235
|
lines.push('### MONOLITHIC REFACTORING HOTSPOTS');
|
|
236
|
+
lines.push('Action: Decompose into single-responsibility crystalline molecule capsules (< 100 lines) and dedicated domain composables.\n');
|
|
182
237
|
monolithHotspots.forEach((h, i) => {
|
|
183
238
|
const tier = h.monolithTier ? `[${h.monolithTier} MONOLITH]` : '[MONOLITH]';
|
|
184
239
|
lines.push(`${i + 1}. File: \`${h.filePath}\` (${h.lineCount} lines, ${h.violationCount} hazards) ${tier}`);
|
|
185
|
-
lines.push(' Action: Decompose into single-responsibility crystalline molecule capsules (< 100 lines) and dedicated domain composables.');
|
|
186
240
|
});
|
|
187
241
|
lines.push('');
|
|
188
242
|
|
|
189
243
|
lines.push('### STRICT EXECUTION RULES:');
|
|
190
|
-
lines.push('1.
|
|
191
|
-
lines.push('2.
|
|
192
|
-
lines.push('3.
|
|
193
|
-
lines.push('4.
|
|
244
|
+
lines.push('1. Branch First: Change to a new branch prefixed with `chem-x/NAMEOFIMPROVEMENT` before making changes, then commit the branch and create a PR to main.');
|
|
245
|
+
lines.push('2. Pre-Split Pattern Discovery: Audit recurring UI layouts, state machines, and predicates across monoliths first; extract canonical shared capsules before slicing to prevent bespoke pattern duplication.');
|
|
246
|
+
lines.push('3. Molecular Limits: Molecule capsules must strictly remain under 100 lines per file.');
|
|
247
|
+
lines.push('4. Table-of-Contents Views: Top-level page views must be 10 to 20 line declarative Table of Contents assembling molecules via named slots.');
|
|
248
|
+
lines.push('5. Composable Return Contracts: Custom hooks/composables must strictly limit returns to 3 to 5 properties (State + Status + Actions).');
|
|
249
|
+
lines.push('6. Type Co-location: Co-locate granular types/*.d.ts files inside each feature capsule (< 100 lines per type file) instead of creating type monoliths.');
|
|
250
|
+
lines.push('7. Zero breaking changes to external component APIs, route exports, or existing props.');
|
|
251
|
+
|
|
252
|
+
return lines.join('\n');
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
export const buildPillarPrompt = (report, pillarName) => {
|
|
256
|
+
const { violations = [], hotspots = [] } = report;
|
|
257
|
+
const pillarViolations = violations.filter((v) => v.pillar === pillarName);
|
|
258
|
+
const isPillar1 = pillarName === 'Line Budgets & Monolith Decomposition';
|
|
259
|
+
const relevantHotspots = isPillar1 ? hotspots.filter((h) => h.isMonolith || h.lineCount > 500) : [];
|
|
260
|
+
|
|
261
|
+
const hasNoViolations = pillarViolations.length === 0;
|
|
262
|
+
const hasNoHotspots = relevantHotspots.length === 0;
|
|
263
|
+
if (hasNoViolations && hasNoHotspots) return '';
|
|
264
|
+
|
|
265
|
+
const lines = [];
|
|
266
|
+
lines.push(`Act as a Principal Systems Architect. Surgically refactor the following ${pillarName} violations according to Chemical X Molecular Architecture Standards:\n`);
|
|
267
|
+
|
|
268
|
+
if (relevantHotspots.length > 0) {
|
|
269
|
+
lines.push('### MONOLITHIC REFACTORING HOTSPOTS');
|
|
270
|
+
lines.push('Action: Decompose into crystalline molecule capsules (< 100 lines) and declarative Table-of-Contents views.\n');
|
|
271
|
+
relevantHotspots.forEach((h, i) => {
|
|
272
|
+
lines.push(`${i + 1}. File: \`${h.filePath}\` (${h.lineCount} lines, ${h.violationCount} hazards)`);
|
|
273
|
+
});
|
|
274
|
+
lines.push('');
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (pillarViolations.length > 0) {
|
|
278
|
+
lines.push(`### ${pillarName.toUpperCase()} HAZARD VIOLATIONS`);
|
|
279
|
+
lines.push(...formatGroupedPromptViolations(pillarViolations));
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
lines.push('### STRICT EXECUTION RULES:');
|
|
283
|
+
lines.push('1. Branch First: Change to a new branch prefixed with `chem-x/NAMEOFIMPROVEMENT` before making changes.');
|
|
284
|
+
lines.push('2. Pre-Split Pattern Discovery: Survey cross-file patterns before slicing; extract canonical shared capsules first.');
|
|
285
|
+
lines.push('3. Molecular Capsule Limit: Maximum 100 lines per molecule capsule file.');
|
|
286
|
+
lines.push('4. Table-of-Contents Views: Top-level page views must be 10 to 20 line declarative templates assembling components via named slots.');
|
|
194
287
|
lines.push('5. Zero breaking changes to external component APIs, route exports, or existing props.');
|
|
195
288
|
|
|
196
289
|
return lines.join('\n');
|
|
@@ -198,15 +291,16 @@ export const buildHotspotsPrompt = (report) => {
|
|
|
198
291
|
|
|
199
292
|
export const buildMasterPrompt = (report) => {
|
|
200
293
|
const sections = [
|
|
201
|
-
buildGradeFPrompt(report),
|
|
202
|
-
buildGradeDPrompt(report),
|
|
203
|
-
buildGradeCPrompt(report),
|
|
204
|
-
buildGradeBPrompt(report),
|
|
294
|
+
buildGradeFPrompt(report, { excludeAiSlop: true }),
|
|
295
|
+
buildGradeDPrompt(report, { excludeAiSlop: true }),
|
|
296
|
+
buildGradeCPrompt(report, { excludeAiSlop: true }),
|
|
297
|
+
buildGradeBPrompt(report, { excludeAiSlop: true }),
|
|
205
298
|
buildAiSlopPrompt(report),
|
|
206
299
|
buildHotspotsPrompt(report)
|
|
207
300
|
].filter(Boolean);
|
|
208
301
|
|
|
209
|
-
|
|
302
|
+
const hasNoSections = sections.length === 0;
|
|
303
|
+
if (hasNoSections) return '';
|
|
210
304
|
|
|
211
305
|
const header = `Act as a Principal Systems Architect. Execute a phased architectural refactoring of our codebase according to Chemical X Molecular Architecture Standards.\n\n`;
|
|
212
306
|
return header + sections.join('\n\n---\n\n');
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { resolveGradeColor } from './reporter-utils.js';
|
|
2
2
|
import { getAsciiGradeLines } from './reporter-ascii.js';
|
|
3
|
+
import { getChemicalXGradientColor, ANSI } from '../theme.js';
|
|
3
4
|
|
|
4
5
|
const BANNER_ART = [
|
|
5
6
|
' ██████╗██╗ ██╗███████╗███╗ ███╗██╗ ██████╗ █████╗ ██╗ ██╗ ██╗',
|
|
@@ -18,7 +19,7 @@ export const getChemicalXAsciiBanner = (gradeOrReport = null) => {
|
|
|
18
19
|
: gradeOrReport;
|
|
19
20
|
|
|
20
21
|
const lines = [];
|
|
21
|
-
lines.push(
|
|
22
|
+
lines.push(` ${ANSI.BOLD}${ANSI.GOLD}The Secret Sauce to Vibe Coding!${ANSI.RESET}`);
|
|
22
23
|
|
|
23
24
|
const gColor = grade ? resolveGradeColor(grade) : '';
|
|
24
25
|
const gradeLines = grade ? getAsciiGradeLines(grade, gColor, true) : [];
|
|
@@ -35,21 +36,8 @@ export const getChemicalXAsciiBanner = (gradeOrReport = null) => {
|
|
|
35
36
|
continue;
|
|
36
37
|
}
|
|
37
38
|
const t = i / (MAX_BANNER_LEN - 1);
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
let b;
|
|
41
|
-
if (t < 0.5) {
|
|
42
|
-
const factor = t * 2;
|
|
43
|
-
r = Math.round(244 * (1 - factor) + 192 * factor);
|
|
44
|
-
g = Math.round(114 * (1 - factor) + 132 * factor);
|
|
45
|
-
b = Math.round(182 * (1 - factor) + 252 * factor);
|
|
46
|
-
} else {
|
|
47
|
-
const factor = (t - 0.5) * 2;
|
|
48
|
-
r = Math.round(192 * (1 - factor) + 129 * factor);
|
|
49
|
-
g = Math.round(132 * (1 - factor) + 140 * factor);
|
|
50
|
-
b = Math.round(252 * (1 - factor) + 248 * factor);
|
|
51
|
-
}
|
|
52
|
-
out += `\x1b[38;2;${r};${g};${b}m\x1b[1m${ch}\x1b[0m`;
|
|
39
|
+
const { ansi } = getChemicalXGradientColor(t);
|
|
40
|
+
out += `${ansi}${ANSI.BOLD}${ch}${ANSI.RESET}`;
|
|
53
41
|
}
|
|
54
42
|
|
|
55
43
|
if (isSideBySide && gradeLines.length > lineIdx) {
|
|
@@ -19,7 +19,8 @@ import {
|
|
|
19
19
|
import { getReportCardAsciiLines } from './reporter-ascii.js';
|
|
20
20
|
import { renderGroupedViolationsTerminal } from './reporter-grouping.js';
|
|
21
21
|
|
|
22
|
-
export const formatGradeFSection = (report) => {
|
|
22
|
+
export const formatGradeFSection = (report, options = {}) => {
|
|
23
|
+
const { includePrompt = true } = options;
|
|
23
24
|
const { violations, hotspots, pillars } = report;
|
|
24
25
|
const { critical } = groupViolationsBySeverity(violations);
|
|
25
26
|
const extremeMonoliths = hotspots.filter((h) => h.lineCount >= 2000);
|
|
@@ -67,16 +68,19 @@ export const formatGradeFSection = (report) => {
|
|
|
67
68
|
}
|
|
68
69
|
}
|
|
69
70
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
71
|
+
if (includePrompt) {
|
|
72
|
+
const promptF = buildGradeFPrompt(report);
|
|
73
|
+
if (promptF) {
|
|
74
|
+
lines.push(formatPromptBox('🤖 AI AGENT REFACTORING PROMPT (GRADE F HAZARDS)', promptF));
|
|
75
|
+
}
|
|
73
76
|
}
|
|
74
77
|
|
|
75
78
|
lines.push(`${RED}======================================================================${RESET}\n`);
|
|
76
79
|
return lines.join('\n');
|
|
77
80
|
};
|
|
78
81
|
|
|
79
|
-
export const formatGradeDSection = (report) => {
|
|
82
|
+
export const formatGradeDSection = (report, options = {}) => {
|
|
83
|
+
const { includePrompt = true } = options;
|
|
80
84
|
const { violations, hotspots, pillars } = report;
|
|
81
85
|
const { high } = groupViolationsBySeverity(violations);
|
|
82
86
|
const severeMonoliths = hotspots.filter((h) => h.lineCount >= 1000 && h.lineCount < 2000);
|
|
@@ -123,16 +127,19 @@ export const formatGradeDSection = (report) => {
|
|
|
123
127
|
}
|
|
124
128
|
}
|
|
125
129
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
130
|
+
if (includePrompt) {
|
|
131
|
+
const promptD = buildGradeDPrompt(report);
|
|
132
|
+
if (promptD) {
|
|
133
|
+
lines.push(formatPromptBox('🤖 AI AGENT REFACTORING PROMPT (GRADE D DEBTS)', promptD));
|
|
134
|
+
}
|
|
129
135
|
}
|
|
130
136
|
|
|
131
137
|
lines.push(`${ORANGE}======================================================================${RESET}\n`);
|
|
132
138
|
return lines.join('\n');
|
|
133
139
|
};
|
|
134
140
|
|
|
135
|
-
export const formatGradeCSection = (report) => {
|
|
141
|
+
export const formatGradeCSection = (report, options = {}) => {
|
|
142
|
+
const { includePrompt = true } = options;
|
|
136
143
|
const { violations, hotspots } = report;
|
|
137
144
|
const { medium } = groupViolationsBySeverity(violations);
|
|
138
145
|
const warningMonoliths = hotspots.filter((h) => h.lineCount >= 500 && h.lineCount < 1000);
|
|
@@ -169,16 +176,19 @@ export const formatGradeCSection = (report) => {
|
|
|
169
176
|
}
|
|
170
177
|
}
|
|
171
178
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
179
|
+
if (includePrompt) {
|
|
180
|
+
const promptC = buildGradeCPrompt(report);
|
|
181
|
+
if (promptC) {
|
|
182
|
+
lines.push(formatPromptBox('🤖 AI AGENT REFACTORING PROMPT (GRADE C DEBTS)', promptC));
|
|
183
|
+
}
|
|
175
184
|
}
|
|
176
185
|
|
|
177
186
|
lines.push(`${CYAN}======================================================================${RESET}\n`);
|
|
178
187
|
return lines.join('\n');
|
|
179
188
|
};
|
|
180
189
|
|
|
181
|
-
export const formatGradeBSection = (report) => {
|
|
190
|
+
export const formatGradeBSection = (report, options = {}) => {
|
|
191
|
+
const { includePrompt = true } = options;
|
|
182
192
|
const { violations } = report;
|
|
183
193
|
const { low } = groupViolationsBySeverity(violations);
|
|
184
194
|
const lines = [];
|
|
@@ -202,9 +212,11 @@ export const formatGradeBSection = (report) => {
|
|
|
202
212
|
lines.push(renderGroupedViolationsTerminal(low));
|
|
203
213
|
}
|
|
204
214
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
215
|
+
if (includePrompt) {
|
|
216
|
+
const promptB = buildGradeBPrompt(report);
|
|
217
|
+
if (promptB) {
|
|
218
|
+
lines.push(formatPromptBox('🤖 AI AGENT REFACTORING PROMPT (GRADE B HYGIENE)', promptB));
|
|
219
|
+
}
|
|
208
220
|
}
|
|
209
221
|
|
|
210
222
|
lines.push(`${YELLOW}======================================================================${RESET}\n`);
|
|
@@ -29,9 +29,15 @@ export interface RuleHazardSummary {
|
|
|
29
29
|
readonly directories: readonly RuleDirectoryOccurrence[];
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
export interface PathTreeNode {
|
|
33
|
+
readonly dirs: Map<string, PathTreeNode>;
|
|
34
|
+
readonly files: Map<string, string[]>;
|
|
35
|
+
}
|
|
36
|
+
|
|
32
37
|
export declare function resolveDirectory(filePath?: string): string;
|
|
33
38
|
export declare function groupViolationsByDirectory(violations?: readonly HazardViolation[]): readonly DirectoryHazardSummary[];
|
|
34
39
|
export declare function groupViolationsByRule(violations?: readonly HazardViolation[]): readonly RuleHazardSummary[];
|
|
40
|
+
export declare function buildPathTree(violations?: readonly HazardViolation[]): PathTreeNode;
|
|
35
41
|
export declare function formatCompactLocations(violations: readonly HazardViolation[], maxShown?: number): string;
|
|
36
42
|
export declare function renderGroupedViolationsTerminal(violations: readonly HazardViolation[]): string;
|
|
37
43
|
export declare function formatDirectoryDistributionSection(report: AuditReport, themeColor?: string | null): string;
|
|
@@ -157,6 +157,34 @@ const resolveSeverityColor = (severity) => {
|
|
|
157
157
|
return DIM;
|
|
158
158
|
};
|
|
159
159
|
|
|
160
|
+
export const buildPathTree = (violations = []) => {
|
|
161
|
+
const root = { dirs: new Map(), files: new Map() };
|
|
162
|
+
|
|
163
|
+
for (const v of violations) {
|
|
164
|
+
const rawPath = v.filePath || '';
|
|
165
|
+
const normalized = rawPath.replace(/\\/g, '/').replace(/^\.\//, '');
|
|
166
|
+
const segments = normalized.split('/');
|
|
167
|
+
const fileName = segments.pop();
|
|
168
|
+
|
|
169
|
+
let current = root;
|
|
170
|
+
for (const seg of segments) {
|
|
171
|
+
const segName = seg.endsWith('/') ? seg : `${seg}/`;
|
|
172
|
+
if (!current.dirs.has(segName)) {
|
|
173
|
+
current.dirs.set(segName, { dirs: new Map(), files: new Map() });
|
|
174
|
+
}
|
|
175
|
+
current = current.dirs.get(segName);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (!current.files.has(fileName)) {
|
|
179
|
+
current.files.set(fileName, []);
|
|
180
|
+
}
|
|
181
|
+
const loc = v.column ? `${v.line}:${v.column}` : `${v.line}`;
|
|
182
|
+
current.files.get(fileName).push(loc);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return root;
|
|
186
|
+
};
|
|
187
|
+
|
|
160
188
|
export const formatCompactLocations = (violations, maxShown = 4) => {
|
|
161
189
|
const formatLocationItem = (v) => {
|
|
162
190
|
const base = path.basename(v.filePath);
|
|
@@ -186,7 +214,8 @@ export const renderGroupedViolationsTerminal = (violations) => {
|
|
|
186
214
|
|
|
187
215
|
if (isSingleOccurrence) {
|
|
188
216
|
const v = rg.violations[0];
|
|
189
|
-
|
|
217
|
+
const colStr = v.column ? `:${v.column}` : '';
|
|
218
|
+
lines.push(` ${sevColor}[#${idx + 1} ${rg.severity}]${RESET} [${rg.rule}] ${YELLOW}${v.filePath}:${v.line}${colStr}${RESET}`);
|
|
190
219
|
lines.push(` Hazard: ${rg.hazard}`);
|
|
191
220
|
lines.push(` Directive: ${CYAN}${rg.directive}${RESET}\n`);
|
|
192
221
|
return;
|
|
@@ -199,10 +228,24 @@ export const renderGroupedViolationsTerminal = (violations) => {
|
|
|
199
228
|
lines.push(` Directive: ${CYAN}${rg.directive}${RESET}`);
|
|
200
229
|
lines.push(` Locations:`);
|
|
201
230
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
231
|
+
const tree = buildPathTree(rg.violations);
|
|
232
|
+
const renderTerminalTree = (node, depth = 0) => {
|
|
233
|
+
const indent = ' ' + ' '.repeat(depth);
|
|
234
|
+
|
|
235
|
+
const sortedDirs = Array.from(node.dirs.entries()).sort((a, b) => a[0].localeCompare(b[0]));
|
|
236
|
+
for (const [dirName, childNode] of sortedDirs) {
|
|
237
|
+
lines.push(`${indent}📁 ${BOLD}${dirName}${RESET}`);
|
|
238
|
+
renderTerminalTree(childNode, depth + 1);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const sortedFiles = Array.from(node.files.entries()).sort((a, b) => a[0].localeCompare(b[0]));
|
|
242
|
+
for (const [fileName, locs] of sortedFiles) {
|
|
243
|
+
const uniqueLocs = Array.from(new Set(locs)).join(', ');
|
|
244
|
+
lines.push(`${indent}- ${YELLOW}${fileName}:${uniqueLocs}${RESET}`);
|
|
245
|
+
}
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
renderTerminalTree(tree, 0);
|
|
206
249
|
lines.push('');
|
|
207
250
|
});
|
|
208
251
|
|