@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.
@@ -1,36 +1,19 @@
1
- import { groupViolationsByRule } from './reporter-grouping.js';
2
-
3
- const CYAN = '\x1b[38;2;98;201;255m';
4
- const BOLD = '\x1b[1m';
5
- const DIM = '\x1b[2m';
6
- const RESET = '\x1b[0m';
7
-
8
- const buildPathTree = (violations) => {
9
- const root = { dirs: new Map(), files: new Map() };
10
-
11
- for (const v of violations) {
12
- const rawPath = v.filePath || '';
13
- const normalized = rawPath.replace(/\\/g, '/').replace(/^\.\//, '');
14
- const segments = normalized.split('/');
15
- const fileName = segments.pop();
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}* ${dirName}`);
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: ${rg.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
- 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
 
@@ -49,6 +49,7 @@ export {
49
49
  resolveDirectory,
50
50
  groupViolationsByDirectory,
51
51
  groupViolationsByRule,
52
+ buildPathTree,
52
53
  formatCompactLocations,
53
54
  renderGroupedViolationsTerminal,
54
55
  formatDirectoryDistributionSection
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 (`* app/` -> `* components/` -> `* molecules/` -> leaf files with line hits) to eliminate boilerplate repetitions and slash AI prompt token consumption by up to 85%.
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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chemx/starter-kit",
3
- "version": "26.9.12-107",
3
+ "version": "26.9.12-118",
4
4
  "description": "Chemical X Protocol: Private drop-in architecture starter kit and capsule generator",
5
5
  "type": "module",
6
6
  "bin": {