@chemx/starter-kit 26.9.12-107 → 26.9.12-118
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/prompts.js
CHANGED
|
@@ -1,36 +1,19 @@
|
|
|
1
|
-
import { groupViolationsByRule } from './reporter-grouping.js';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
let current = root;
|
|
18
|
-
for (const seg of segments) {
|
|
19
|
-
const segName = seg.endsWith('/') ? seg : `${seg}/`;
|
|
20
|
-
if (!current.dirs.has(segName)) {
|
|
21
|
-
current.dirs.set(segName, { dirs: new Map(), files: new Map() });
|
|
22
|
-
}
|
|
23
|
-
current = current.dirs.get(segName);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
if (!current.files.has(fileName)) {
|
|
27
|
-
current.files.set(fileName, []);
|
|
28
|
-
}
|
|
29
|
-
const loc = v.column ? `${v.line}:${v.column}` : `${v.line}`;
|
|
30
|
-
current.files.get(fileName).push(loc);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
return root;
|
|
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
|
|
34
17
|
};
|
|
35
18
|
|
|
36
19
|
const renderTreeLines = (node, depth = 0) => {
|
|
@@ -39,7 +22,7 @@ const renderTreeLines = (node, depth = 0) => {
|
|
|
39
22
|
|
|
40
23
|
const sortedDirs = Array.from(node.dirs.entries()).sort((a, b) => a[0].localeCompare(b[0]));
|
|
41
24
|
for (const [dirName, childNode] of sortedDirs) {
|
|
42
|
-
lines.push(`${indent}
|
|
25
|
+
lines.push(`${indent}📁 ${BOLD}${dirName}${RESET}`);
|
|
43
26
|
const childLines = renderTreeLines(childNode, depth + 1);
|
|
44
27
|
lines.push(...childLines);
|
|
45
28
|
}
|
|
@@ -47,7 +30,7 @@ const renderTreeLines = (node, depth = 0) => {
|
|
|
47
30
|
const sortedFiles = Array.from(node.files.entries()).sort((a, b) => a[0].localeCompare(b[0]));
|
|
48
31
|
for (const [fileName, locs] of sortedFiles) {
|
|
49
32
|
const uniqueLocs = Array.from(new Set(locs)).join(', ');
|
|
50
|
-
lines.push(`${indent}- \`${fileName}:${uniqueLocs}\``);
|
|
33
|
+
lines.push(`${indent}- \`${YELLOW}${fileName}:${uniqueLocs}${RESET}\``);
|
|
51
34
|
}
|
|
52
35
|
|
|
53
36
|
return lines;
|
|
@@ -61,10 +44,11 @@ export const formatGroupedPromptViolations = (violations = []) => {
|
|
|
61
44
|
const lines = [];
|
|
62
45
|
|
|
63
46
|
ruleGroups.forEach((rg, idx) => {
|
|
47
|
+
const sevColor = SEVERITY_COLORS[rg.severity] || YELLOW;
|
|
64
48
|
const countLabel = rg.total === 1 ? '1 item' : `${rg.total} items`;
|
|
65
|
-
lines.push(`${idx + 1}. [${rg.rule}] (${countLabel})`);
|
|
66
|
-
lines.push(` Hazard:
|
|
67
|
-
lines.push(` Directive: ${rg.directive}`);
|
|
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}`);
|
|
68
52
|
lines.push(' Locations:');
|
|
69
53
|
|
|
70
54
|
const tree = buildPathTree(rg.violations);
|
|
@@ -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
|
|
package/cli/audit/reporter.js
CHANGED
package/docs/CHANGELOG.md
CHANGED
|
@@ -20,7 +20,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|
|
20
20
|
|
|
21
21
|
### Added
|
|
22
22
|
- Expanded AST static analysis engine with Pillars 8 to 11 (`cli/audit/extended-visitors.js`): added automated rules and visitors for Accessibility & Semantic Integrity (`A11Y_CLICKABLE_NON_SEMANTIC`, `A11Y_IMAGE_MISSING_ALT`), Security & Content Safety (`SECURITY_RAW_HTML_INJECTION`, `SECURITY_HARDCODED_SECRET`), Testing Discipline (`TEST_FAKE_GREEN`, `TEST_MISSING_COLOCATED`), and Naming Conventions (`NAMING_BARE_BOOLEAN`, `NAMING_HANDLER_PREFIX`).
|
|
23
|
-
- Implemented nested bullet tree path-chain formatter (`formatGroupedPromptViolations` in `cli/audit/prompts.js`), clustering violations by rule and rendering folder hierarchy steps as indented bullet chains (
|
|
23
|
+
- Implemented nested bullet tree path-chain formatter (`formatGroupedPromptViolations` in `cli/audit/prompts.js` and `renderGroupedViolationsTerminal` in `cli/audit/reporter-grouping.js`), clustering violations by rule and rendering folder hierarchy steps as indented bullet chains with `📁` folder emojis and ANSI severity colors (`📁 app/` -> `📁 components/` -> `📁 molecules/` -> leaf files with line hits) to eliminate boilerplate repetitions and slash AI prompt token consumption by up to 85%.
|
|
24
24
|
- Consolidated monolith refactoring action directives across `buildHotspotsPrompt`, `buildGradeFPrompt`, `buildGradeDPrompt`, and `buildGradeCPrompt`, stating action requirements once per section.
|
|
25
25
|
- Added `{ excludeAiSlop }` filter options to grade prompt builders to prevent duplicate slop violation listings in `buildMasterPrompt`.
|
|
26
26
|
- Added `{ includePrompt: false }` flag to `formatHotspotsSection` and `formatAiSlopSection` when rendered within full terminal report (`cli/audit/reporter.js`), preventing mid-report prompt box duplication before the master prompt.
|