@chemx/starter-kit 26.9.11-481 → 26.9.12-107
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 +262 -0
- package/cli/audit/extended-visitors.js +359 -0
- package/cli/audit/history.js +19 -2
- package/cli/audit/metrics.js +69 -0
- package/cli/audit/prompts.js +175 -48
- package/cli/audit/reporter-banner.d.ts +3 -0
- package/cli/audit/reporter-banner.js +74 -0
- package/cli/audit/reporter-grades.js +11 -26
- package/cli/audit/reporter-grouping-markdown.js +83 -0
- package/cli/audit/reporter-grouping.d.ts +42 -0
- package/cli/audit/reporter-grouping.js +243 -0
- package/cli/audit/reporter-markdown.js +34 -22
- package/cli/audit/reporter-sections.d.ts +10 -0
- package/cli/audit/reporter-sections.js +259 -0
- package/cli/audit/reporter-summary.d.ts +4 -0
- package/cli/audit/reporter-summary.js +142 -0
- package/cli/audit/reporter-utils.js +13 -1
- package/cli/audit/reporter.d.ts +16 -0
- package/cli/audit/reporter.js +47 -420
- package/cli/audit/rules-helpers.js +16 -0
- package/cli/audit/rules-registry.js +88 -1
- package/cli/audit/rules.js +32 -1
- package/cli/audit/social-git.js +133 -131
- package/cli/audit/social.js +15 -2
- package/cli/audit/types.d.ts +28 -27
- package/cli/audit.js +56 -3
- package/cli/badge.js +262 -0
- package/cli/generator-templates.js +264 -0
- package/cli/generator.d.ts +10 -0
- package/cli/generator.js +151 -0
- package/cli/help.js +17 -7
- package/cli/index.js +26 -16
- package/cli/installer.d.ts +2 -0
- package/cli/installer.js +52 -38
- package/cli/license.js +17 -4
- package/cli/navigator-actions.js +63 -1
- package/cli/navigator-banner.js +17 -6
- package/cli/navigator-grades.js +9 -0
- package/cli/navigator-menu.js +36 -0
- package/cli/navigator-share.js +7 -4
- package/cli/navigator.d.ts +5 -1
- package/cli/navigator.js +49 -12
- package/cli/scaffold.js +3 -54
- package/cli/terminal.js +4 -1
- package/docs/CHANGELOG.md +50 -0
- package/package.json +1 -1
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { HazardViolation, SeverityLevel, AuditReport } from './types';
|
|
2
|
+
|
|
3
|
+
export interface DirectoryHazardSummary {
|
|
4
|
+
readonly directory: string;
|
|
5
|
+
readonly total: number;
|
|
6
|
+
readonly critical: number;
|
|
7
|
+
readonly high: number;
|
|
8
|
+
readonly medium: number;
|
|
9
|
+
readonly low: number;
|
|
10
|
+
readonly violations: readonly HazardViolation[];
|
|
11
|
+
readonly topRules: readonly { readonly rule: string; readonly count: number }[];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface RuleDirectoryOccurrence {
|
|
15
|
+
readonly directory: string;
|
|
16
|
+
readonly count: number;
|
|
17
|
+
readonly violations: readonly HazardViolation[];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface RuleHazardSummary {
|
|
21
|
+
readonly rule: string;
|
|
22
|
+
readonly severity: SeverityLevel;
|
|
23
|
+
readonly pillar: string;
|
|
24
|
+
readonly directive: string;
|
|
25
|
+
readonly hazard: string;
|
|
26
|
+
readonly isAiSlop: boolean;
|
|
27
|
+
readonly total: number;
|
|
28
|
+
readonly violations: readonly HazardViolation[];
|
|
29
|
+
readonly directories: readonly RuleDirectoryOccurrence[];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export declare function resolveDirectory(filePath?: string): string;
|
|
33
|
+
export declare function groupViolationsByDirectory(violations?: readonly HazardViolation[]): readonly DirectoryHazardSummary[];
|
|
34
|
+
export declare function groupViolationsByRule(violations?: readonly HazardViolation[]): readonly RuleHazardSummary[];
|
|
35
|
+
export declare function formatCompactLocations(violations: readonly HazardViolation[], maxShown?: number): string;
|
|
36
|
+
export declare function renderGroupedViolationsTerminal(violations: readonly HazardViolation[]): string;
|
|
37
|
+
export declare function formatDirectoryDistributionSection(report: AuditReport, themeColor?: string | null): string;
|
|
38
|
+
export declare function formatDirectoryRollupMarkdown(violations?: readonly HazardViolation[]): string;
|
|
39
|
+
export declare function renderGroupedViolationsMarkdown(
|
|
40
|
+
violations?: readonly HazardViolation[],
|
|
41
|
+
options?: { readonly showSeverity?: boolean }
|
|
42
|
+
): string;
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import {
|
|
3
|
+
CYAN,
|
|
4
|
+
GREEN,
|
|
5
|
+
YELLOW,
|
|
6
|
+
RED,
|
|
7
|
+
ORANGE,
|
|
8
|
+
DIM,
|
|
9
|
+
BOLD,
|
|
10
|
+
RESET,
|
|
11
|
+
resolveTopSectionColor
|
|
12
|
+
} from './reporter-utils.js';
|
|
13
|
+
|
|
14
|
+
export const resolveDirectory = (filePath) => {
|
|
15
|
+
const hasFilePath = Boolean(filePath);
|
|
16
|
+
if (!hasFilePath) return './';
|
|
17
|
+
const normalized = filePath.replace(/\\/g, '/');
|
|
18
|
+
const lastSlash = normalized.lastIndexOf('/');
|
|
19
|
+
const isRoot = lastSlash === -1;
|
|
20
|
+
if (isRoot) return './';
|
|
21
|
+
return normalized.slice(0, lastSlash + 1);
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export const groupViolationsByDirectory = (violations = []) => {
|
|
25
|
+
const dirMap = new Map();
|
|
26
|
+
|
|
27
|
+
for (const v of violations) {
|
|
28
|
+
const dir = resolveDirectory(v.filePath);
|
|
29
|
+
let group = dirMap.get(dir);
|
|
30
|
+
if (!group) {
|
|
31
|
+
group = {
|
|
32
|
+
directory: dir,
|
|
33
|
+
total: 0,
|
|
34
|
+
critical: 0,
|
|
35
|
+
high: 0,
|
|
36
|
+
medium: 0,
|
|
37
|
+
low: 0,
|
|
38
|
+
violations: [],
|
|
39
|
+
rulesMap: new Map()
|
|
40
|
+
};
|
|
41
|
+
dirMap.set(dir, group);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
group.total += 1;
|
|
45
|
+
group.violations.push(v);
|
|
46
|
+
if (v.severity === 'CRITICAL') group.critical += 1;
|
|
47
|
+
else if (v.severity === 'HIGH') group.high += 1;
|
|
48
|
+
else if (v.severity === 'MEDIUM') group.medium += 1;
|
|
49
|
+
else group.low += 1;
|
|
50
|
+
|
|
51
|
+
const count = group.rulesMap.get(v.rule) || 0;
|
|
52
|
+
group.rulesMap.set(v.rule, count + 1);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const createDirectorySummary = (g) => {
|
|
56
|
+
const topRules = Array.from(g.rulesMap.entries())
|
|
57
|
+
.map(([rule, count]) => ({ rule, count }))
|
|
58
|
+
.sort((a, b) => b.count - a.count);
|
|
59
|
+
|
|
60
|
+
return {
|
|
61
|
+
directory: g.directory,
|
|
62
|
+
total: g.total,
|
|
63
|
+
critical: g.critical,
|
|
64
|
+
high: g.high,
|
|
65
|
+
medium: g.medium,
|
|
66
|
+
low: g.low,
|
|
67
|
+
violations: g.violations,
|
|
68
|
+
topRules
|
|
69
|
+
};
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const result = Array.from(dirMap.values()).map(createDirectorySummary);
|
|
73
|
+
|
|
74
|
+
const calculateDirectoryWeight = (g) => g.critical * 1000 + g.high * 100 + g.medium * 10 + g.low;
|
|
75
|
+
|
|
76
|
+
const compareDirectoryRisk = (a, b) => {
|
|
77
|
+
const weightDiff = calculateDirectoryWeight(b) - calculateDirectoryWeight(a);
|
|
78
|
+
const hasWeightDiff = weightDiff !== 0;
|
|
79
|
+
if (hasWeightDiff) return weightDiff;
|
|
80
|
+
return b.total - a.total;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
result.sort(compareDirectoryRisk);
|
|
84
|
+
return result;
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
export const groupViolationsByRule = (violations = []) => {
|
|
88
|
+
const ruleMap = new Map();
|
|
89
|
+
|
|
90
|
+
for (const v of violations) {
|
|
91
|
+
let group = ruleMap.get(v.rule);
|
|
92
|
+
if (!group) {
|
|
93
|
+
group = {
|
|
94
|
+
rule: v.rule,
|
|
95
|
+
severity: v.severity,
|
|
96
|
+
pillar: v.pillar,
|
|
97
|
+
directive: v.directive,
|
|
98
|
+
hazard: v.hazard,
|
|
99
|
+
isAiSlop: Boolean(v.isAiSlop),
|
|
100
|
+
total: 0,
|
|
101
|
+
violations: [],
|
|
102
|
+
dirsMap: new Map()
|
|
103
|
+
};
|
|
104
|
+
ruleMap.set(v.rule, group);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
group.total += 1;
|
|
108
|
+
group.violations.push(v);
|
|
109
|
+
|
|
110
|
+
const dir = resolveDirectory(v.filePath);
|
|
111
|
+
let dirViolations = group.dirsMap.get(dir);
|
|
112
|
+
if (!dirViolations) {
|
|
113
|
+
dirViolations = [];
|
|
114
|
+
group.dirsMap.set(dir, dirViolations);
|
|
115
|
+
}
|
|
116
|
+
dirViolations.push(v);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const createRuleSummary = (g) => {
|
|
120
|
+
const directories = Array.from(g.dirsMap.entries()).map(([directory, list]) => ({
|
|
121
|
+
directory,
|
|
122
|
+
count: list.length,
|
|
123
|
+
violations: list
|
|
124
|
+
}));
|
|
125
|
+
|
|
126
|
+
return {
|
|
127
|
+
rule: g.rule,
|
|
128
|
+
severity: g.severity,
|
|
129
|
+
pillar: g.pillar,
|
|
130
|
+
directive: g.directive,
|
|
131
|
+
hazard: g.hazard,
|
|
132
|
+
isAiSlop: g.isAiSlop,
|
|
133
|
+
total: g.total,
|
|
134
|
+
violations: g.violations,
|
|
135
|
+
directories
|
|
136
|
+
};
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
const result = Array.from(ruleMap.values()).map(createRuleSummary);
|
|
140
|
+
|
|
141
|
+
const SEVERITY_RANKS = { CRITICAL: 4, HIGH: 3, MEDIUM: 2, LOW: 1 };
|
|
142
|
+
const compareRuleSeverity = (a, b) => {
|
|
143
|
+
const rankDiff = (SEVERITY_RANKS[b.severity] || 0) - (SEVERITY_RANKS[a.severity] || 0);
|
|
144
|
+
const hasRankDiff = rankDiff !== 0;
|
|
145
|
+
if (hasRankDiff) return rankDiff;
|
|
146
|
+
return b.total - a.total;
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
result.sort(compareRuleSeverity);
|
|
150
|
+
return result;
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
const resolveSeverityColor = (severity) => {
|
|
154
|
+
if (severity === 'CRITICAL') return RED;
|
|
155
|
+
if (severity === 'HIGH') return ORANGE;
|
|
156
|
+
if (severity === 'MEDIUM') return YELLOW;
|
|
157
|
+
return DIM;
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
export const formatCompactLocations = (violations, maxShown = 4) => {
|
|
161
|
+
const formatLocationItem = (v) => {
|
|
162
|
+
const base = path.basename(v.filePath);
|
|
163
|
+
return `${YELLOW}${base}:${v.line}:${v.column}${RESET}`;
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
const items = violations.slice(0, maxShown).map(formatLocationItem);
|
|
167
|
+
const remaining = violations.length - maxShown;
|
|
168
|
+
const hasRemaining = remaining > 0;
|
|
169
|
+
if (hasRemaining) {
|
|
170
|
+
items.push(`${DIM}(+${remaining} more)${RESET}`);
|
|
171
|
+
}
|
|
172
|
+
return items.join(', ');
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
export const renderGroupedViolationsTerminal = (violations) => {
|
|
176
|
+
const hasNoViolations = violations.length === 0;
|
|
177
|
+
if (hasNoViolations) return '';
|
|
178
|
+
|
|
179
|
+
const ruleGroups = groupViolationsByRule(violations);
|
|
180
|
+
const lines = [];
|
|
181
|
+
|
|
182
|
+
ruleGroups.forEach((rg, idx) => {
|
|
183
|
+
const sevColor = resolveSeverityColor(rg.severity);
|
|
184
|
+
const countLabel = rg.total === 1 ? '1 item' : `${rg.total} items`;
|
|
185
|
+
const isSingleOccurrence = rg.total === 1;
|
|
186
|
+
|
|
187
|
+
if (isSingleOccurrence) {
|
|
188
|
+
const v = rg.violations[0];
|
|
189
|
+
lines.push(` ${sevColor}[#${idx + 1} ${rg.severity}]${RESET} [${rg.rule}] ${YELLOW}${v.filePath}:${v.line}:${v.column}${RESET}`);
|
|
190
|
+
lines.push(` Hazard: ${rg.hazard}`);
|
|
191
|
+
lines.push(` Directive: ${CYAN}${rg.directive}${RESET}\n`);
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const dirCount = rg.directories.length;
|
|
196
|
+
const dirLabel = dirCount === 1 ? '1 directory' : `${dirCount} directories`;
|
|
197
|
+
lines.push(` ${sevColor}[#${idx + 1} ${rg.severity}]${RESET} [${rg.rule}] ${BOLD}${countLabel}${RESET} ${DIM}across ${dirLabel}${RESET}`);
|
|
198
|
+
lines.push(` Hazard: ${rg.hazard}`);
|
|
199
|
+
lines.push(` Directive: ${CYAN}${rg.directive}${RESET}`);
|
|
200
|
+
lines.push(` Locations:`);
|
|
201
|
+
|
|
202
|
+
for (const d of rg.directories) {
|
|
203
|
+
const locs = formatCompactLocations(d.violations, 4);
|
|
204
|
+
lines.push(` 📁 ${d.directory} (${d.count}): ${locs}`);
|
|
205
|
+
}
|
|
206
|
+
lines.push('');
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
return lines.join('\n');
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
export const formatDirectoryDistributionSection = (report, themeColor = null) => {
|
|
213
|
+
const { violations = [] } = report;
|
|
214
|
+
const sectionColor = themeColor || resolveTopSectionColor(report);
|
|
215
|
+
const dirGroups = groupViolationsByDirectory(violations);
|
|
216
|
+
const lines = [];
|
|
217
|
+
|
|
218
|
+
lines.push('');
|
|
219
|
+
lines.push(`${sectionColor}======================================================================${RESET}`);
|
|
220
|
+
lines.push(`${BOLD}${sectionColor} HAZARD DISTRIBUTION BY DIRECTORY${RESET}`);
|
|
221
|
+
lines.push(`${sectionColor}======================================================================${RESET}`);
|
|
222
|
+
|
|
223
|
+
const hasNoViolations = dirGroups.length === 0;
|
|
224
|
+
if (hasNoViolations) {
|
|
225
|
+
lines.push(` ${GREEN}✔ Zero violations detected across all project directories.${RESET}`);
|
|
226
|
+
} else {
|
|
227
|
+
lines.push(` ${DIM}Directory Total Crit High Med Low${RESET}`);
|
|
228
|
+
lines.push(` ${DIM}------------------------------------------------------------------${RESET}`);
|
|
229
|
+
for (const g of dirGroups) {
|
|
230
|
+
const isLongDir = g.directory.length > 36;
|
|
231
|
+
const padDir = isLongDir ? `${g.directory.slice(0, 33)}...` : g.directory.padEnd(36, ' ');
|
|
232
|
+
const totalStr = `${BOLD}${g.total}${RESET}`.padStart(6, ' ');
|
|
233
|
+
const critStr = (g.critical > 0 ? `${RED}${g.critical}${RESET}` : `${DIM}0${RESET}`).padStart(5, ' ');
|
|
234
|
+
const highStr = (g.high > 0 ? `${ORANGE}${g.high}${RESET}` : `${DIM}0${RESET}`).padStart(5, ' ');
|
|
235
|
+
const medStr = (g.medium > 0 ? `${YELLOW}${g.medium}${RESET}` : `${DIM}0${RESET}`).padStart(5, ' ');
|
|
236
|
+
const lowStr = (g.low > 0 ? `${DIM}${g.low}${RESET}` : `${DIM}0${RESET}`).padStart(5, ' ');
|
|
237
|
+
lines.push(` ${padDir} ${totalStr} ${critStr} ${highStr} ${medStr} ${lowStr}`);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
lines.push(`${sectionColor}======================================================================${RESET}\n`);
|
|
241
|
+
|
|
242
|
+
return lines.join('\n');
|
|
243
|
+
};
|
|
@@ -7,9 +7,13 @@ import {
|
|
|
7
7
|
PILLAR_EMOJIS,
|
|
8
8
|
NUMBER_EMOJIS
|
|
9
9
|
} from './reporter-utils.js';
|
|
10
|
+
import {
|
|
11
|
+
formatDirectoryRollupMarkdown,
|
|
12
|
+
renderGroupedViolationsMarkdown
|
|
13
|
+
} from './reporter-grouping-markdown.js';
|
|
10
14
|
|
|
11
15
|
export const generateMarkdownReport = (report) => {
|
|
12
|
-
const { metrics, health, pillars, contextAnalysis, hotspots, violations } = report;
|
|
16
|
+
const { metrics, health, pillars, contextAnalysis, hotspots, violations, aiSlop } = report;
|
|
13
17
|
const { critical, high, medium, low } = groupViolationsBySeverity(violations);
|
|
14
18
|
const lines = [];
|
|
15
19
|
|
|
@@ -24,6 +28,10 @@ export const generateMarkdownReport = (report) => {
|
|
|
24
28
|
const hearts = resolveHealthHearts(health?.grade, health?.score, false);
|
|
25
29
|
lines.push(`| **Life / Health Meter** | ${hearts} (Grade **${health.grade}**) | ${health.score} / 100 (${health.label}) |`);
|
|
26
30
|
lines.push(`| **Molecular Health Index** | **${health.score} / 100** | Grade: **${health.grade}** (${health.label}) |`);
|
|
31
|
+
const slopScore = aiSlop?.score ?? 100;
|
|
32
|
+
const slopGrade = aiSlop?.grade ?? 'A+';
|
|
33
|
+
const slopLabel = aiSlop?.label ?? 'Pure Artisanal';
|
|
34
|
+
lines.push(`| **AI Slop Index (ASI)** | **${slopScore} / 100** | Grade: **${slopGrade}** (${slopLabel}) |`);
|
|
27
35
|
lines.push(`| **Scanned Files** | ${metrics.scannedFiles} source files | Verified |`);
|
|
28
36
|
lines.push(`| **Total Lines of Code** | ${metrics.totalLoc} lines of code | Avg ${metrics.avgLoc} lines/file |`);
|
|
29
37
|
lines.push(`| **Largest File** | \`${metrics.largestFile.filePath || 'None'}\` | ${metrics.largestFile.lineCount} lines |`);
|
|
@@ -69,61 +77,65 @@ export const generateMarkdownReport = (report) => {
|
|
|
69
77
|
lines.push('');
|
|
70
78
|
lines.push('---');
|
|
71
79
|
lines.push('');
|
|
72
|
-
lines.push('## 4. Architectural Hazard Inventory (Grouped by Severity)');
|
|
80
|
+
lines.push('## 4. Architectural Hazard Inventory (Grouped by Directory & Severity)');
|
|
73
81
|
lines.push('');
|
|
74
82
|
|
|
75
83
|
if (violations.length === 0) {
|
|
76
84
|
lines.push('Zero context hazard violations detected across all line budgets, hooks, and AST rules.');
|
|
77
85
|
} else {
|
|
86
|
+
lines.push(formatDirectoryRollupMarkdown(violations));
|
|
87
|
+
|
|
78
88
|
if (critical.length > 0) {
|
|
79
89
|
lines.push('### Critical Hazards (Immediate Action Required)');
|
|
80
90
|
lines.push('');
|
|
81
|
-
lines.push(
|
|
82
|
-
lines.push('| :--- | :--- | :--- | :--- |');
|
|
83
|
-
for (const v of critical) {
|
|
84
|
-
lines.push(`| \`${v.filePath}:${v.line}:${v.column}\` | \`${v.rule}\` | ${v.hazard} | ${v.directive} |`);
|
|
85
|
-
}
|
|
86
|
-
lines.push('');
|
|
91
|
+
lines.push(renderGroupedViolationsMarkdown(critical, { showSeverity: false }));
|
|
87
92
|
}
|
|
88
93
|
|
|
89
94
|
if (high.length > 0 || medium.length > 0) {
|
|
90
95
|
lines.push('### High & Medium Hazards (Architecture Debts)');
|
|
91
96
|
lines.push('');
|
|
92
|
-
lines.push(
|
|
93
|
-
lines.push('| :--- | :---: | :--- | :--- | :--- |');
|
|
94
|
-
for (const v of [...high, ...medium]) {
|
|
95
|
-
lines.push(`| \`${v.filePath}:${v.line}:${v.column}\` | ${v.severity} | \`${v.rule}\` | ${v.hazard} | ${v.directive} |`);
|
|
96
|
-
}
|
|
97
|
-
lines.push('');
|
|
97
|
+
lines.push(renderGroupedViolationsMarkdown([...high, ...medium], { showSeverity: true }));
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
if (low.length > 0) {
|
|
101
101
|
lines.push('### Low & Hygiene Issues (Typography & Logging)');
|
|
102
102
|
lines.push('');
|
|
103
|
-
lines.push(
|
|
104
|
-
lines.push('| :--- | :--- | :--- | :--- |');
|
|
105
|
-
for (const v of low) {
|
|
106
|
-
lines.push(`| \`${v.filePath}:${v.line}:${v.column}\` | \`${v.rule}\` | ${v.hazard} | ${v.directive} |`);
|
|
107
|
-
}
|
|
108
|
-
lines.push('');
|
|
103
|
+
lines.push(renderGroupedViolationsMarkdown(low, { showSeverity: false }));
|
|
109
104
|
}
|
|
110
105
|
}
|
|
111
106
|
|
|
107
|
+
const slopViolations = violations.filter((v) => v.isAiSlop);
|
|
108
|
+
lines.push('---');
|
|
109
|
+
lines.push('');
|
|
110
|
+
lines.push('## 5. AI Slop & Code Authenticity Audit');
|
|
111
|
+
lines.push('');
|
|
112
|
+
lines.push(`* **AI Slop Index (ASI)**: **${slopScore} / 100** (Grade: **${slopGrade}** - ${slopLabel})`);
|
|
113
|
+
lines.push(`* **Slop Hazards Flagged**: **${slopViolations.length}**`);
|
|
114
|
+
lines.push('');
|
|
115
|
+
|
|
116
|
+
if (slopViolations.length === 0) {
|
|
117
|
+
lines.push('🟢 Zero AI slop detected. Codebase demonstrates pure artisanal craftsmanship with no LLM conversational residue or echo comments.');
|
|
118
|
+
} else {
|
|
119
|
+
lines.push(renderGroupedViolationsMarkdown(slopViolations, { showSeverity: true }));
|
|
120
|
+
}
|
|
121
|
+
lines.push('');
|
|
122
|
+
|
|
112
123
|
lines.push('---');
|
|
113
124
|
lines.push('');
|
|
114
|
-
lines.push('##
|
|
125
|
+
lines.push('## 6. Remediation Roadmap');
|
|
115
126
|
lines.push('');
|
|
116
127
|
lines.push('1. **Decompose Monoliths**: Break down any file exceeding 500 lines into single-responsibility capsules.');
|
|
117
128
|
lines.push('2. **Cap Molecule Size**: Ensure all molecule components (`m-*`) remain strictly under 100 lines.');
|
|
118
129
|
lines.push('3. **Two-Stage Boolean Logic**: Replace complex multi-clause expressions with atomic boolean variables.');
|
|
119
130
|
lines.push('4. **Lifecycle-Safe Timers**: Migrate raw `setInterval` and `setTimeout` calls to self-cleaning composables.');
|
|
131
|
+
lines.push('5. **Purge AI Slop**: Remove conversational residue, eliminate echo comments, and inline single-use passthrough assignments.');
|
|
120
132
|
lines.push('');
|
|
121
133
|
|
|
122
134
|
const masterPrompt = buildMasterPrompt(report);
|
|
123
135
|
if (masterPrompt) {
|
|
124
136
|
lines.push('---');
|
|
125
137
|
lines.push('');
|
|
126
|
-
lines.push('##
|
|
138
|
+
lines.push('## 7. 🤖 Copy-Paste AI Agent Refactoring Directives');
|
|
127
139
|
lines.push('');
|
|
128
140
|
lines.push('Feed the prompt below directly into your AI coding agent (Cursor, Windsurf, Claude, Copilot, or Antigravity) to surgically refactor flagged hazards:');
|
|
129
141
|
lines.push('');
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { AuditReport } from './types';
|
|
2
|
+
|
|
3
|
+
export declare function formatScorecardSection(report: AuditReport, themeColor?: string | null): string;
|
|
4
|
+
export declare function formatCriticalSection(report: AuditReport): string;
|
|
5
|
+
export declare function formatHighMediumSection(report: AuditReport): string;
|
|
6
|
+
export declare function formatLowSection(report: AuditReport): string;
|
|
7
|
+
export declare function formatPillarsSection(report: AuditReport, themeColor?: string | null): string;
|
|
8
|
+
export declare function formatHotspotsSection(report: AuditReport, themeColor?: string | null): string;
|
|
9
|
+
export declare function formatContextAnalysisSection(report: AuditReport, themeColor?: string | null): string;
|
|
10
|
+
export declare function formatAiSlopSection(report: AuditReport, themeColor?: string | null): string;
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CYAN,
|
|
3
|
+
GREEN,
|
|
4
|
+
YELLOW,
|
|
5
|
+
RED,
|
|
6
|
+
ORANGE,
|
|
7
|
+
DIM,
|
|
8
|
+
BOLD,
|
|
9
|
+
RESET,
|
|
10
|
+
groupViolationsBySeverity,
|
|
11
|
+
getStatusBadge,
|
|
12
|
+
resolveGradeColor,
|
|
13
|
+
resolveHealthHearts,
|
|
14
|
+
resolveRiskColor,
|
|
15
|
+
resolveHotspotBadge,
|
|
16
|
+
resolveTopSectionColor
|
|
17
|
+
} from './reporter-utils.js';
|
|
18
|
+
import { getAsciiGradeLines, getReportCardAsciiLines } from './reporter-ascii.js';
|
|
19
|
+
import { renderGroupedViolationsTerminal } from './reporter-grouping.js';
|
|
20
|
+
import {
|
|
21
|
+
buildAiSlopPrompt,
|
|
22
|
+
buildHotspotsPrompt,
|
|
23
|
+
formatPromptBox
|
|
24
|
+
} from './prompts.js';
|
|
25
|
+
|
|
26
|
+
const isSlopViolation = (v) => Boolean(v.isAiSlop);
|
|
27
|
+
|
|
28
|
+
const formatHotspotItem = (h, idx) => {
|
|
29
|
+
const monolithBadge = resolveHotspotBadge(h.lineCount);
|
|
30
|
+
return ` [#${idx + 1}] ${YELLOW}${h.filePath}${RESET} (${h.lineCount} lines, ${h.violationCount} hazards)${monolithBadge}`;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export const formatScorecardSection = (report, themeColor = null) => {
|
|
34
|
+
const { metrics, health } = report;
|
|
35
|
+
const sectionColor = themeColor || resolveTopSectionColor(report);
|
|
36
|
+
const gradeColor = resolveGradeColor(health?.score ?? health?.grade ?? 'A');
|
|
37
|
+
const lines = [];
|
|
38
|
+
|
|
39
|
+
lines.push('');
|
|
40
|
+
for (const rcLine of getReportCardAsciiLines(sectionColor)) {
|
|
41
|
+
lines.push(` ${rcLine}`);
|
|
42
|
+
}
|
|
43
|
+
lines.push(`${sectionColor}======================================================================${RESET}`);
|
|
44
|
+
lines.push(`${BOLD}${sectionColor} MOLECULAR HEALTH INDEX & CODEBASE OVERVIEW${RESET}`);
|
|
45
|
+
lines.push(`${sectionColor}======================================================================${RESET}`);
|
|
46
|
+
|
|
47
|
+
const asciiGradeLines = getAsciiGradeLines(health?.grade || 'A', gradeColor);
|
|
48
|
+
if (asciiGradeLines.length > 0) {
|
|
49
|
+
lines.push('');
|
|
50
|
+
for (const asciiLine of asciiGradeLines) {
|
|
51
|
+
lines.push(` ${asciiLine}`);
|
|
52
|
+
}
|
|
53
|
+
lines.push('');
|
|
54
|
+
lines.push(`${sectionColor}----------------------------------------------------------------------${RESET}`);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const hearts = resolveHealthHearts(health?.grade, health?.score);
|
|
58
|
+
lines.push(` Life / Health Meter: ${hearts} ${gradeColor}${BOLD}${health.score} / 100${RESET} (Grade: ${gradeColor}${BOLD}${health.grade}${RESET} - ${health.label})`);
|
|
59
|
+
|
|
60
|
+
if (report.aiSlop) {
|
|
61
|
+
const slopGradeColor = resolveGradeColor(report.aiSlop.grade);
|
|
62
|
+
const slopHearts = resolveHealthHearts(report.aiSlop.grade, report.aiSlop.score);
|
|
63
|
+
lines.push(` AI Slop Index (ASI): ${slopHearts} ${slopGradeColor}${BOLD}${report.aiSlop.score} / 100${RESET} (Grade: ${slopGradeColor}${BOLD}${report.aiSlop.grade}${RESET} - ${report.aiSlop.label})`);
|
|
64
|
+
}
|
|
65
|
+
lines.push(` Files Scanned: ${BOLD}${metrics.scannedFiles}${RESET} source files`);
|
|
66
|
+
lines.push(` Total Lines of Code: ${BOLD}${metrics.totalLoc}${RESET} lines of code (avg: ${metrics.avgLoc} lines/file)`);
|
|
67
|
+
lines.push(` Largest File: ${BOLD}${metrics.largestFile.filePath || 'None'}${RESET} (${metrics.largestFile.lineCount} lines)`);
|
|
68
|
+
lines.push(` Molecule Capsules: ${metrics.moleculeCount} found (${metrics.moleculeCompliantPct}% compliant < 100 lines of code)`);
|
|
69
|
+
lines.push(` Custom Hooks: ${metrics.hookCount} detected`);
|
|
70
|
+
lines.push(`${sectionColor}======================================================================${RESET}\n`);
|
|
71
|
+
|
|
72
|
+
return lines.join('\n');
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
export const formatCriticalSection = (report) => {
|
|
76
|
+
const { violations } = report;
|
|
77
|
+
const { critical } = groupViolationsBySeverity(violations);
|
|
78
|
+
const lines = [];
|
|
79
|
+
|
|
80
|
+
lines.push('');
|
|
81
|
+
lines.push(`${RED}======================================================================${RESET}`);
|
|
82
|
+
lines.push(`${BOLD}${RED} CRITICAL HAZARDS (${critical.length} TOTAL) : IMMEDIATE ACTION REQUIRED${RESET}`);
|
|
83
|
+
lines.push(`${DIM} Violations that break Molecular line budgets or introduce severe AST bugs${RESET}`);
|
|
84
|
+
lines.push(`${RED}======================================================================${RESET}`);
|
|
85
|
+
|
|
86
|
+
if (critical.length === 0) {
|
|
87
|
+
lines.push(` ${GREEN}✔ Zero critical hazards detected. No monoliths or breaking AST patterns.${RESET}`);
|
|
88
|
+
} else {
|
|
89
|
+
lines.push(renderGroupedViolationsTerminal(critical));
|
|
90
|
+
}
|
|
91
|
+
lines.push(`${RED}======================================================================${RESET}\n`);
|
|
92
|
+
|
|
93
|
+
return lines.join('\n');
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
export const formatHighMediumSection = (report) => {
|
|
97
|
+
const { violations } = report;
|
|
98
|
+
const { high, medium } = groupViolationsBySeverity(violations);
|
|
99
|
+
const totalCount = high.length + medium.length;
|
|
100
|
+
const lines = [];
|
|
101
|
+
|
|
102
|
+
lines.push('');
|
|
103
|
+
lines.push(`${ORANGE}======================================================================${RESET}`);
|
|
104
|
+
lines.push(`${BOLD}${ORANGE} HIGH & MEDIUM HAZARDS (${totalCount} TOTAL) : ARCHITECTURE DEBTS${RESET}`);
|
|
105
|
+
lines.push(`${DIM} Saturation, complex control flow booleans, and raw inline styles${RESET}`);
|
|
106
|
+
lines.push(`${ORANGE}======================================================================${RESET}`);
|
|
107
|
+
|
|
108
|
+
if (totalCount === 0) {
|
|
109
|
+
lines.push(` ${GREEN}✔ Zero high or medium architecture hazards detected.${RESET}`);
|
|
110
|
+
} else {
|
|
111
|
+
if (high.length > 0) {
|
|
112
|
+
lines.push(`\n ${BOLD}--- High Severity (${high.length} items) ---${RESET}\n`);
|
|
113
|
+
lines.push(renderGroupedViolationsTerminal(high));
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (medium.length > 0) {
|
|
117
|
+
lines.push(`\n ${BOLD}--- Medium Severity (${medium.length} items) ---${RESET}\n`);
|
|
118
|
+
lines.push(renderGroupedViolationsTerminal(medium));
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
lines.push(`${ORANGE}======================================================================${RESET}\n`);
|
|
122
|
+
|
|
123
|
+
return lines.join('\n');
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
export const formatLowSection = (report) => {
|
|
127
|
+
const { violations } = report;
|
|
128
|
+
const { low } = groupViolationsBySeverity(violations);
|
|
129
|
+
const lines = [];
|
|
130
|
+
|
|
131
|
+
lines.push('');
|
|
132
|
+
lines.push(`${YELLOW}======================================================================${RESET}`);
|
|
133
|
+
lines.push(`${BOLD}${YELLOW} LOW & HYGIENE ISSUES (${low.length} TOTAL) : TYPOGRAPHY & LOGGING${RESET}`);
|
|
134
|
+
lines.push(`${DIM} Em dash typography violations and unguarded console statements${RESET}`);
|
|
135
|
+
lines.push(`${YELLOW}======================================================================${RESET}`);
|
|
136
|
+
|
|
137
|
+
if (low.length === 0) {
|
|
138
|
+
lines.push(` ${GREEN}✔ Zero typography or logging hygiene issues detected.${RESET}`);
|
|
139
|
+
} else {
|
|
140
|
+
lines.push(renderGroupedViolationsTerminal(low));
|
|
141
|
+
}
|
|
142
|
+
lines.push(`${YELLOW}======================================================================${RESET}\n`);
|
|
143
|
+
|
|
144
|
+
return lines.join('\n');
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
export const formatPillarsSection = (report, themeColor = null) => {
|
|
148
|
+
const { pillars } = report;
|
|
149
|
+
const sectionColor = themeColor || resolveTopSectionColor(report);
|
|
150
|
+
const lines = [];
|
|
151
|
+
|
|
152
|
+
lines.push('');
|
|
153
|
+
lines.push(`${sectionColor}======================================================================${RESET}`);
|
|
154
|
+
lines.push(`${BOLD}${sectionColor} 7-PILLAR ARCHITECTURAL COMPLIANCE MATRIX${RESET}`);
|
|
155
|
+
lines.push(`${sectionColor}======================================================================${RESET}`);
|
|
156
|
+
lines.push(` ${DIM}Pillar Name Status Violations${RESET}`);
|
|
157
|
+
lines.push(` ${DIM}------------------------------------------------------------------${RESET}`);
|
|
158
|
+
for (const [pillarName, data] of Object.entries(pillars)) {
|
|
159
|
+
const padName = pillarName.padEnd(45, ' ');
|
|
160
|
+
const badge = getStatusBadge(data.status).padEnd(16, ' ');
|
|
161
|
+
const countStr = data.violations === 0 ? `${GREEN}0${RESET}` : `${RED}${data.violations}${RESET}`;
|
|
162
|
+
lines.push(` ${padName} ${badge} ${countStr}`);
|
|
163
|
+
}
|
|
164
|
+
lines.push(`${sectionColor}======================================================================${RESET}\n`);
|
|
165
|
+
|
|
166
|
+
return lines.join('\n');
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
export const formatHotspotsSection = (report, themeColor = null, options = {}) => {
|
|
170
|
+
const { includePrompt = true } = typeof options === 'boolean' ? { includePrompt: options } : options;
|
|
171
|
+
const { hotspots } = report;
|
|
172
|
+
const sectionColor = themeColor || resolveTopSectionColor(report);
|
|
173
|
+
const lines = [];
|
|
174
|
+
|
|
175
|
+
lines.push('');
|
|
176
|
+
lines.push(`${sectionColor}======================================================================${RESET}`);
|
|
177
|
+
lines.push(`${BOLD}${sectionColor} TOP REFACTORING HOTSPOTS (PRIORITY RANKING)${RESET}`);
|
|
178
|
+
lines.push(`${sectionColor}======================================================================${RESET}`);
|
|
179
|
+
|
|
180
|
+
if (hotspots.length === 0) {
|
|
181
|
+
lines.push(` ${GREEN}✔ Zero hotspot files. All files stay within architectural budgets.${RESET}`);
|
|
182
|
+
} else {
|
|
183
|
+
for (let idx = 0; idx < hotspots.length; idx++) {
|
|
184
|
+
lines.push(formatHotspotItem(hotspots[idx], idx));
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (includePrompt) {
|
|
189
|
+
const promptHotspots = buildHotspotsPrompt(report);
|
|
190
|
+
if (promptHotspots) {
|
|
191
|
+
lines.push(formatPromptBox('🤖 AI AGENT REFACTORING PROMPT (MONOLITH DECOMPOSITION)', promptHotspots));
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
lines.push(`${sectionColor}======================================================================${RESET}\n`);
|
|
196
|
+
|
|
197
|
+
return lines.join('\n');
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
export const formatContextAnalysisSection = (report, themeColor = null) => {
|
|
201
|
+
const { contextAnalysis } = report;
|
|
202
|
+
const sectionColor = themeColor || resolveTopSectionColor(report);
|
|
203
|
+
const riskColor = resolveRiskColor(contextAnalysis.riskLevel);
|
|
204
|
+
const lines = [];
|
|
205
|
+
|
|
206
|
+
lines.push('');
|
|
207
|
+
lines.push(`${sectionColor}======================================================================${RESET}`);
|
|
208
|
+
lines.push(`${BOLD}${sectionColor} CONTEXT ROT & TOKEN BURN ANALYTICS${RESET}`);
|
|
209
|
+
lines.push(`${sectionColor}======================================================================${RESET}`);
|
|
210
|
+
lines.push(` Estimated Codebase Tokens: ~${contextAnalysis.estimatedTokens.toLocaleString()} tokens`);
|
|
211
|
+
lines.push(` Estimated Monolith Bloat: ~${contextAnalysis.estimatedExcessTokens.toLocaleString()} tokens`);
|
|
212
|
+
lines.push(` Target Architecture Cut: ${BOLD}${contextAnalysis.potentialSavingsPct}%${RESET} token reduction potential`);
|
|
213
|
+
lines.push(` Agent Hallucination Risk: ${riskColor}${BOLD}${contextAnalysis.riskLevel}${RESET}`);
|
|
214
|
+
if (contextAnalysis.excessCostPerPass !== undefined) {
|
|
215
|
+
const costPass = contextAnalysis.excessCostPerPass.toFixed(3);
|
|
216
|
+
const costMonth = contextAnalysis.monthlyWastePerDev.toFixed(2);
|
|
217
|
+
const costWeek = contextAnalysis.weeklyWastePerDev.toFixed(2);
|
|
218
|
+
lines.push(` Pricing Model Baseline: ${contextAnalysis.pricingModel}`);
|
|
219
|
+
lines.push(` Monolith Bloat per AI Turn: \x1b[38;5;208;1m$${costPass}\x1b[0m / prompt turn`);
|
|
220
|
+
lines.push(` Projected Dev Context Tax: \x1b[31;1m$${costMonth}/mo\x1b[0m per engineer ($${costWeek}/wk)`);
|
|
221
|
+
}
|
|
222
|
+
lines.push(`${sectionColor}======================================================================${RESET}\n`);
|
|
223
|
+
|
|
224
|
+
return lines.join('\n');
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
export const formatAiSlopSection = (report, themeColor = null, options = {}) => {
|
|
228
|
+
const { includePrompt = true } = typeof options === 'boolean' ? { includePrompt: options } : options;
|
|
229
|
+
const { aiSlop, violations = [] } = report;
|
|
230
|
+
const slopViolations = violations.filter(isSlopViolation);
|
|
231
|
+
const sectionColor = themeColor || resolveTopSectionColor(report);
|
|
232
|
+
const slopColor = resolveGradeColor(aiSlop?.grade || 'A+');
|
|
233
|
+
const lines = [];
|
|
234
|
+
|
|
235
|
+
lines.push('');
|
|
236
|
+
lines.push(`${sectionColor}======================================================================${RESET}`);
|
|
237
|
+
lines.push(`${BOLD}${sectionColor} AI SLOP INDEX & CODE AUTHENTICITY AUDIT${RESET}`);
|
|
238
|
+
lines.push(`${DIM} Deterministic detection of LLM conversational residue and echo comments${RESET}`);
|
|
239
|
+
lines.push(`${sectionColor}======================================================================${RESET}`);
|
|
240
|
+
lines.push(` AI Slop Score: ${slopColor}${BOLD}${aiSlop?.score ?? 100} / 100${RESET} (Grade: ${slopColor}${BOLD}${aiSlop?.grade ?? 'A+'}${RESET} - ${aiSlop?.label ?? 'Pure Artisanal'})`);
|
|
241
|
+
lines.push(` Slop Hazards Flagged: ${slopViolations.length > 0 ? `${RED}${BOLD}${slopViolations.length}${RESET}` : `${GREEN}0 (Artisanal Clean)${RESET}`}`);
|
|
242
|
+
lines.push(`${sectionColor}----------------------------------------------------------------------${RESET}`);
|
|
243
|
+
|
|
244
|
+
if (slopViolations.length === 0) {
|
|
245
|
+
lines.push(` ${GREEN}✔ Zero AI slop detected. Codebase is free of conversational residue and echo comments.${RESET}`);
|
|
246
|
+
} else {
|
|
247
|
+
lines.push(renderGroupedViolationsTerminal(slopViolations));
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
if (includePrompt) {
|
|
251
|
+
const promptSlop = buildAiSlopPrompt(report);
|
|
252
|
+
if (promptSlop) {
|
|
253
|
+
lines.push(formatPromptBox('🤖 AI AGENT REFACTORING PROMPT (AI SLOP & AUTHENTICITY)', promptSlop));
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
lines.push(`${sectionColor}======================================================================${RESET}\n`);
|
|
258
|
+
return lines.join('\n');
|
|
259
|
+
};
|