@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.
Files changed (54) hide show
  1. package/cli/audit/ai-slop-detector.js +32 -48
  2. package/cli/audit/ast-visitors.js +10 -13
  3. package/cli/audit/extended-visitors.js +10 -4
  4. package/cli/audit/history.js +47 -15
  5. package/cli/audit/pattern-detector.js +182 -0
  6. package/cli/audit/prompts.js +165 -71
  7. package/cli/audit/reporter-banner.js +4 -16
  8. package/cli/audit/reporter-grades.js +28 -16
  9. package/cli/audit/reporter-grouping.d.ts +6 -0
  10. package/cli/audit/reporter-grouping.js +48 -5
  11. package/cli/audit/reporter-markdown.js +31 -16
  12. package/cli/audit/reporter-sections.d.ts +1 -0
  13. package/cli/audit/reporter-sections.js +83 -13
  14. package/cli/audit/reporter-utils.js +116 -1
  15. package/cli/audit/reporter.d.ts +13 -4
  16. package/cli/audit/reporter.js +34 -5
  17. package/cli/audit/roadmap.js +236 -0
  18. package/cli/audit/rules-predicates.d.ts +22 -0
  19. package/cli/audit/rules-predicates.js +139 -0
  20. package/cli/audit/rules.js +6 -2
  21. package/cli/audit/social-gh.js +2 -6
  22. package/cli/audit/social-publisher.js +1 -1
  23. package/cli/audit/social.js +25 -9
  24. package/cli/audit/types.d.ts +52 -5
  25. package/cli/audit.js +89 -14
  26. package/cli/badge.js +9 -2
  27. package/cli/build/detector.js +68 -0
  28. package/cli/build/executor.js +58 -0
  29. package/cli/build/grouper.js +78 -0
  30. package/cli/build/parser-matchers.js +116 -0
  31. package/cli/build/parser.js +80 -0
  32. package/cli/build/reporter.js +66 -0
  33. package/cli/build/types.d.ts +58 -0
  34. package/cli/build/types.js +1 -0
  35. package/cli/build.d.ts +16 -0
  36. package/cli/build.js +59 -0
  37. package/cli/generator.js +4 -1
  38. package/cli/help.js +14 -0
  39. package/cli/index.js +43 -15
  40. package/cli/installer-templates.js +43 -4
  41. package/cli/license.js +59 -6
  42. package/cli/navigator-actions.js +114 -90
  43. package/cli/navigator-banner.js +4 -2
  44. package/cli/navigator-grades.js +28 -0
  45. package/cli/navigator-menu.js +32 -13
  46. package/cli/navigator-share.js +1 -1
  47. package/cli/navigator.d.ts +10 -1
  48. package/cli/navigator.js +5 -3
  49. package/cli/scaffold.js +24 -9
  50. package/cli/terminal.js +6 -5
  51. package/cli/theme.d.ts +41 -0
  52. package/cli/theme.js +84 -0
  53. package/docs/CHANGELOG.md +68 -0
  54. package/package.json +3 -2
@@ -1,74 +1,132 @@
1
- const CYAN = '\x1b[38;2;98;201;255m';
2
- const BOLD = '\x1b[1m';
3
- const DIM = '\x1b[2m';
4
- const RESET = '\x1b[0m';
5
-
6
- export const buildGradeFPrompt = (report) => {
7
- const { violations, hotspots } = report;
8
- const critical = violations.filter((v) => v.severity === 'CRITICAL');
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
- if (critical.length === 0 && extremeMonoliths.length === 0) return '';
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
- critical.forEach((v, i) => {
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. Every new molecule component must stay under 100 lines.');
37
- lines.push('2. Top-level page views must be 10-20 line declarative Table-of-Contents templates assembling components via named slots.');
38
- lines.push('3. Zero synthetic or mock data: return live data or explicit empty states.');
39
- lines.push('4. Zero render-hack setTimeout: replace with await nextTick() or flush: post.');
40
- lines.push('5. Output surgical diffs or modular replacements. Preserve existing external exports.');
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 { violations, hotspots } = report;
47
- const high = violations.filter((v) => v.severity === 'HIGH');
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
- if (high.length === 0 && severeMonoliths.length === 0) return '';
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
- high.forEach((v, i) => {
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 { violations, hotspots } = report;
84
- const medium = violations.filter((v) => v.severity === 'MEDIUM');
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
- if (medium.length === 0 && warningMonoliths.length === 0) return '';
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
- medium.forEach((v, i) => {
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 { violations } = report;
121
- const low = violations.filter((v) => v.severity === 'LOW');
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
- if (low.length === 0) return '';
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
- low.forEach((v, i) => {
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
- if (slopViolations.length === 0) return '';
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
- slopViolations.forEach((v, i) => {
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
- if (monolithHotspots.length === 0) return '';
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. Molecular Limits: Molecule capsules must strictly remain under 100 lines per file.');
191
- lines.push('2. Table-of-Contents Views: Top-level page views must be 10 to 20 line declarative Table of Contents assembling molecules via named slots.');
192
- lines.push('3. Composable Return Contracts: Custom hooks/composables must strictly limit returns to 3 to 5 properties (State + Status + Actions).');
193
- lines.push('4. Type Co-location: Co-locate granular types/*.d.ts files inside each feature capsule (< 100 lines per type file) instead of creating type monoliths.');
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
- if (sections.length === 0) return '';
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(' \x1b[1m\x1b[37mThe Secret Sauce to \x1b[38;2;98;201;255mVibe Coding\x1b[0m');
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
- let r;
39
- let g;
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
- const promptF = buildGradeFPrompt(report);
71
- if (promptF) {
72
- lines.push(formatPromptBox('🤖 AI AGENT REFACTORING PROMPT (GRADE F HAZARDS)', promptF));
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
- const promptD = buildGradeDPrompt(report);
127
- if (promptD) {
128
- lines.push(formatPromptBox('🤖 AI AGENT REFACTORING PROMPT (GRADE D DEBTS)', promptD));
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
- const promptC = buildGradeCPrompt(report);
173
- if (promptC) {
174
- lines.push(formatPromptBox('🤖 AI AGENT REFACTORING PROMPT (GRADE C DEBTS)', promptC));
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
- const promptB = buildGradeBPrompt(report);
206
- if (promptB) {
207
- lines.push(formatPromptBox('🤖 AI AGENT REFACTORING PROMPT (GRADE B HYGIENE)', promptB));
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
- lines.push(` ${sevColor}[#${idx + 1} ${rg.severity}]${RESET} [${rg.rule}] ${YELLOW}${v.filePath}:${v.line}:${v.column}${RESET}`);
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
- for (const d of rg.directories) {
203
- const locs = formatCompactLocations(d.violations, 4);
204
- lines.push(` 📁 ${d.directory} (${d.count}): ${locs}`);
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