@chemx/starter-kit 26.9.12-93 → 26.9.13-272

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 (49) hide show
  1. package/cli/audit/ai-slop-detector.js +25 -41
  2. package/cli/audit/ast-visitors.js +10 -13
  3. package/cli/audit/history.js +45 -11
  4. package/cli/audit/pattern-detector.js +182 -0
  5. package/cli/audit/prompts.js +129 -72
  6. package/cli/audit/reporter-banner.js +4 -16
  7. package/cli/audit/reporter-grades.js +28 -16
  8. package/cli/audit/reporter-grouping.d.ts +6 -0
  9. package/cli/audit/reporter-grouping.js +48 -5
  10. package/cli/audit/reporter-markdown.js +31 -16
  11. package/cli/audit/reporter-sections.js +28 -12
  12. package/cli/audit/reporter-utils.js +88 -1
  13. package/cli/audit/reporter.d.ts +13 -4
  14. package/cli/audit/reporter.js +32 -5
  15. package/cli/audit/roadmap.js +236 -0
  16. package/cli/audit/rules-predicates.d.ts +17 -0
  17. package/cli/audit/rules-predicates.js +107 -0
  18. package/cli/audit/rules.js +6 -2
  19. package/cli/audit/social-gh.js +2 -6
  20. package/cli/audit/social-publisher.js +1 -1
  21. package/cli/audit/social.js +25 -9
  22. package/cli/audit/types.d.ts +52 -5
  23. package/cli/audit.js +76 -7
  24. package/cli/badge.js +9 -2
  25. package/cli/build/detector.js +68 -0
  26. package/cli/build/executor.js +58 -0
  27. package/cli/build/grouper.js +78 -0
  28. package/cli/build/parser-matchers.js +116 -0
  29. package/cli/build/parser.js +80 -0
  30. package/cli/build/reporter.js +66 -0
  31. package/cli/build/types.d.ts +58 -0
  32. package/cli/build/types.js +1 -0
  33. package/cli/build.d.ts +16 -0
  34. package/cli/build.js +55 -0
  35. package/cli/help.js +14 -0
  36. package/cli/index.js +39 -14
  37. package/cli/installer-templates.js +43 -4
  38. package/cli/license.js +10 -7
  39. package/cli/navigator-actions.js +34 -6
  40. package/cli/navigator-banner.js +4 -2
  41. package/cli/navigator-menu.js +18 -0
  42. package/cli/navigator-share.js +1 -1
  43. package/cli/navigator.d.ts +8 -1
  44. package/cli/navigator.js +3 -3
  45. package/cli/terminal.js +6 -5
  46. package/cli/theme.d.ts +41 -0
  47. package/cli/theme.js +84 -0
  48. package/docs/CHANGELOG.md +46 -0
  49. package/package.json +3 -2
@@ -13,7 +13,9 @@ import {
13
13
  resolveHealthHearts,
14
14
  resolveRiskColor,
15
15
  resolveHotspotBadge,
16
- resolveTopSectionColor
16
+ resolveTopSectionColor,
17
+ PILLAR_EMOJIS,
18
+ formatPillarReactionBadgesTerminal
17
19
  } from './reporter-utils.js';
18
20
  import { getAsciiGradeLines, getReportCardAsciiLines } from './reporter-ascii.js';
19
21
  import { renderGroupedViolationsTerminal } from './reporter-grouping.js';
@@ -153,10 +155,18 @@ export const formatPillarsSection = (report, themeColor = null) => {
153
155
  lines.push(`${sectionColor}======================================================================${RESET}`);
154
156
  lines.push(`${BOLD}${sectionColor} 7-PILLAR ARCHITECTURAL COMPLIANCE MATRIX${RESET}`);
155
157
  lines.push(`${sectionColor}======================================================================${RESET}`);
156
- lines.push(` ${DIM}Pillar Name Status Violations${RESET}`);
157
- lines.push(` ${DIM}------------------------------------------------------------------${RESET}`);
158
+
159
+ const reactionStrip = formatPillarReactionBadgesTerminal(pillars);
160
+ if (reactionStrip) {
161
+ lines.push(` ${BOLD}Reaction Badges:${RESET} ${reactionStrip}`);
162
+ lines.push(`${sectionColor}----------------------------------------------------------------------${RESET}`);
163
+ }
164
+
165
+ lines.push(` ${DIM}Pillar Name Status Violations${RESET}`);
166
+ lines.push(` ${DIM}------------------------------------------------------------------------${RESET}`);
158
167
  for (const [pillarName, data] of Object.entries(pillars)) {
159
- const padName = pillarName.padEnd(45, ' ');
168
+ const icon = PILLAR_EMOJIS[pillarName] || '🏛️';
169
+ const padName = `[ ${icon} ] ${pillarName}`.padEnd(51, ' ');
160
170
  const badge = getStatusBadge(data.status).padEnd(16, ' ');
161
171
  const countStr = data.violations === 0 ? `${GREEN}0${RESET}` : `${RED}${data.violations}${RESET}`;
162
172
  lines.push(` ${padName} ${badge} ${countStr}`);
@@ -166,7 +176,8 @@ export const formatPillarsSection = (report, themeColor = null) => {
166
176
  return lines.join('\n');
167
177
  };
168
178
 
169
- export const formatHotspotsSection = (report, themeColor = null) => {
179
+ export const formatHotspotsSection = (report, themeColor = null, options = {}) => {
180
+ const { includePrompt = true } = typeof options === 'boolean' ? { includePrompt: options } : options;
170
181
  const { hotspots } = report;
171
182
  const sectionColor = themeColor || resolveTopSectionColor(report);
172
183
  const lines = [];
@@ -184,9 +195,11 @@ export const formatHotspotsSection = (report, themeColor = null) => {
184
195
  }
185
196
  }
186
197
 
187
- const promptHotspots = buildHotspotsPrompt(report);
188
- if (promptHotspots) {
189
- lines.push(formatPromptBox('🤖 AI AGENT REFACTORING PROMPT (MONOLITH DECOMPOSITION)', promptHotspots));
198
+ if (includePrompt) {
199
+ const promptHotspots = buildHotspotsPrompt(report);
200
+ if (promptHotspots) {
201
+ lines.push(formatPromptBox('🤖 AI AGENT REFACTORING PROMPT (MONOLITH DECOMPOSITION)', promptHotspots));
202
+ }
190
203
  }
191
204
 
192
205
  lines.push(`${sectionColor}======================================================================${RESET}\n`);
@@ -221,7 +234,8 @@ export const formatContextAnalysisSection = (report, themeColor = null) => {
221
234
  return lines.join('\n');
222
235
  };
223
236
 
224
- export const formatAiSlopSection = (report, themeColor = null) => {
237
+ export const formatAiSlopSection = (report, themeColor = null, options = {}) => {
238
+ const { includePrompt = true } = typeof options === 'boolean' ? { includePrompt: options } : options;
225
239
  const { aiSlop, violations = [] } = report;
226
240
  const slopViolations = violations.filter(isSlopViolation);
227
241
  const sectionColor = themeColor || resolveTopSectionColor(report);
@@ -243,9 +257,11 @@ export const formatAiSlopSection = (report, themeColor = null) => {
243
257
  lines.push(renderGroupedViolationsTerminal(slopViolations));
244
258
  }
245
259
 
246
- const promptSlop = buildAiSlopPrompt(report);
247
- if (promptSlop) {
248
- lines.push(formatPromptBox('🤖 AI AGENT REFACTORING PROMPT (AI SLOP & AUTHENTICITY)', promptSlop));
260
+ if (includePrompt) {
261
+ const promptSlop = buildAiSlopPrompt(report);
262
+ if (promptSlop) {
263
+ lines.push(formatPromptBox('🤖 AI AGENT REFACTORING PROMPT (AI SLOP & AUTHENTICITY)', promptSlop));
264
+ }
249
265
  }
250
266
 
251
267
  lines.push(`${sectionColor}======================================================================${RESET}\n`);
@@ -1,4 +1,16 @@
1
- export const CYAN = '\x1b[38;2;98;201;255m';
1
+ export {
2
+ CHEMX_COLORS,
3
+ CHEMX_RGB,
4
+ getChemicalXGradientColor,
5
+ formatChemicalXGradient
6
+ } from '../theme.js';
7
+
8
+ export const CYAN = '\x1b[38;2;56;189;248m';
9
+ export const PINK = '\x1b[38;2;244;63;133m';
10
+ export const PURPLE = '\x1b[38;2;168;85;247m';
11
+ export const MINT = '\x1b[38;2;45;212;191m';
12
+ export const LIME = '\x1b[38;2;163;230;53m';
13
+ export const GOLD = '\x1b[38;2;251;191;36m';
2
14
  export const GREEN = '\x1b[32m';
3
15
  export const YELLOW = '\x1b[33m';
4
16
  export const RED = '\x1b[31m';
@@ -117,6 +129,49 @@ export const PILLAR_EMOJIS = {
117
129
  'Naming Conventions': '🏷️'
118
130
  };
119
131
 
132
+ export const PILLAR_SHORT_NAMES = {
133
+ 'Line Budgets & Monolith Decomposition': 'Monoliths',
134
+ 'Control Flow & Boolean Logic': 'Logic',
135
+ 'Reactivity & Composable Contracts': 'Reactivity',
136
+ 'Type Architecture & Data Integrity': 'Types',
137
+ 'Design System & Styling Hygiene': 'Styling',
138
+ 'Timers & Macro-Task Discipline': 'Timers',
139
+ 'Global Hygiene & Typography': 'Hygiene',
140
+ 'Accessibility & Semantic Integrity': 'A11y',
141
+ 'Security & Content Safety': 'Security',
142
+ 'Testing Discipline': 'Testing',
143
+ 'Naming Conventions': 'Naming'
144
+ };
145
+
146
+ export const resolveBadgeColor = (score) => {
147
+ if (score >= 90) return '06b6d4';
148
+ if (score >= 70) return 'f59e0b';
149
+ return 'ef4444';
150
+ };
151
+
152
+ export const formatPillarShieldBadges = (pillars = {}) => {
153
+ const badges = [];
154
+ for (const [pillarName, data] of Object.entries(pillars)) {
155
+ const icon = PILLAR_EMOJIS[pillarName] || '🏛️';
156
+ const shortName = PILLAR_SHORT_NAMES[pillarName] || pillarName.split(' ')[0];
157
+ const rawLabel = `${icon} ${shortName}`.replace(/-/g, '--').replace(/ /g, '_');
158
+ const label = encodeURIComponent(rawLabel);
159
+ let statusText = 'PASS';
160
+ let color = '06b6d4';
161
+ if (data?.status === 'WARN') {
162
+ const v = data?.violations || 0;
163
+ statusText = encodeURIComponent(`WARN ${v}`.replace(/-/g, '--').replace(/ /g, '_'));
164
+ color = 'f59e0b';
165
+ } else if (data?.status === 'FAILED') {
166
+ const v = data?.violations || 0;
167
+ statusText = encodeURIComponent(`FAIL ${v}`.replace(/-/g, '--').replace(/ /g, '_'));
168
+ color = 'ef4444';
169
+ }
170
+ badges.push(`[![${shortName}](https://img.shields.io/badge/${label}-${statusText}-${color}?style=for-the-badge)](https://chemicalx.xophz.com)`);
171
+ }
172
+ return badges.join(' ');
173
+ };
174
+
120
175
  export const NUMBER_EMOJIS = ['1️⃣', '2️⃣', '3️⃣', '4️⃣', '5️⃣', '6️⃣', '7️⃣', '8️⃣', '9️⃣', '🔟'];
121
176
 
122
177
  export const resolvePriorityBadge = (idx) => {
@@ -135,3 +190,35 @@ export const resolveMarkdownMonolithText = (lineCount) => {
135
190
  if (lineCount > 500) return '🟡 **WARNING (> 500 lines of code)**';
136
191
  return '🟢 Compliant';
137
192
  };
193
+
194
+ export const formatPillarReactionBadgesTerminal = (pillars = {}) => {
195
+ const badges = [];
196
+ for (const [pillarName, data] of Object.entries(pillars)) {
197
+ const icon = PILLAR_EMOJIS[pillarName] || '🏛️';
198
+ const violations = data?.violations || 0;
199
+ if (data?.status === 'PASSED') {
200
+ badges.push(`${GREEN}[ ${icon} PASS ]${RESET}`);
201
+ } else if (data?.status === 'WARN') {
202
+ badges.push(`${YELLOW}[ ${icon} WARN (${violations}) ]${RESET}`);
203
+ } else {
204
+ badges.push(`${RED}[ ${icon} FAIL (${violations}) ]${RESET}`);
205
+ }
206
+ }
207
+ return badges.join(' ');
208
+ };
209
+
210
+ export const formatPillarReactionBadgesMarkdown = (pillars = {}) => {
211
+ const badges = [];
212
+ for (const [pillarName, data] of Object.entries(pillars)) {
213
+ const icon = PILLAR_EMOJIS[pillarName] || '🏛️';
214
+ const violations = data?.violations || 0;
215
+ if (data?.status === 'PASSED') {
216
+ badges.push(`\`[ ${icon} PASS ]\``);
217
+ } else if (data?.status === 'WARN') {
218
+ badges.push(`\`[ ${icon} WARN: ${violations} ]\``);
219
+ } else {
220
+ badges.push(`\`[ ${icon} FAIL: ${violations} ]\``);
221
+ }
222
+ }
223
+ return badges.join(' ');
224
+ };
@@ -6,11 +6,20 @@ export * from './reporter-sections';
6
6
  export * from './reporter-summary';
7
7
  export * from './reporter-grouping';
8
8
 
9
+ export interface FormatGradeOptions {
10
+ readonly includePrompt?: boolean;
11
+ }
12
+
9
13
  export declare function formatTerminalReport(report: AuditReport): string;
10
14
  export declare function generateMarkdownReport(report: AuditReport): string;
11
15
  export declare function resolveTopSectionColor(report: AuditReport): string;
12
- export declare function formatGradeFSection(report: AuditReport): string;
13
- export declare function formatGradeDSection(report: AuditReport): string;
14
- export declare function formatGradeCSection(report: AuditReport): string;
15
- export declare function formatGradeBSection(report: AuditReport): string;
16
+ export declare function formatGradeFSection(report: AuditReport, options?: FormatGradeOptions): string;
17
+ export declare function formatGradeDSection(report: AuditReport, options?: FormatGradeOptions): string;
18
+ export declare function formatGradeCSection(report: AuditReport, options?: FormatGradeOptions): string;
19
+ export declare function formatGradeBSection(report: AuditReport, options?: FormatGradeOptions): string;
16
20
  export declare function formatGradeASection(report: AuditReport): string;
21
+ export declare function formatPillarReactionBadgesTerminal(pillars?: Record<string, unknown>): string;
22
+ export declare function formatPillarReactionBadgesMarkdown(pillars?: Record<string, unknown>): string;
23
+ export declare const PILLAR_SHORT_NAMES: Record<string, string>;
24
+ export declare function resolveBadgeColor(score: number): string;
25
+ export declare function formatPillarShieldBadges(pillars?: Record<string, unknown>): string;
@@ -20,6 +20,24 @@ import {
20
20
  formatContextAnalysisSection,
21
21
  formatAiSlopSection
22
22
  } from './reporter-sections.js';
23
+ import {
24
+ formatRoadmapSection,
25
+ formatRoadmapMarkdown,
26
+ buildSelfHealingRoadmapPrompt
27
+ } from './roadmap.js';
28
+ import {
29
+ formatGradeASection,
30
+ formatGradeBSection,
31
+ formatGradeCSection,
32
+ formatGradeDSection,
33
+ formatGradeFSection
34
+ } from './reporter-grades.js';
35
+
36
+ export {
37
+ formatRoadmapSection,
38
+ formatRoadmapMarkdown,
39
+ buildSelfHealingRoadmapPrompt
40
+ };
23
41
 
24
42
  export {
25
43
  groupViolationsBySeverity,
@@ -32,7 +50,12 @@ export {
32
50
  resolveTopSectionColor,
33
51
  resolveMarkdownStatusIcon,
34
52
  resolveMarkdownMonolithText,
53
+ formatPillarReactionBadgesTerminal,
54
+ formatPillarReactionBadgesMarkdown,
55
+ formatPillarShieldBadges,
56
+ resolveBadgeColor,
35
57
  PILLAR_EMOJIS,
58
+ PILLAR_SHORT_NAMES,
36
59
  NUMBER_EMOJIS,
37
60
  resolvePriorityBadge,
38
61
  CYAN,
@@ -49,6 +72,7 @@ export {
49
72
  resolveDirectory,
50
73
  groupViolationsByDirectory,
51
74
  groupViolationsByRule,
75
+ buildPathTree,
52
76
  formatCompactLocations,
53
77
  renderGroupedViolationsTerminal,
54
78
  formatDirectoryDistributionSection
@@ -108,12 +132,15 @@ export const formatTerminalReport = (report) => {
108
132
  lines.push(formatScorecardSection(report, topColor));
109
133
  lines.push(formatContextAnalysisSection(report, topColor));
110
134
  lines.push(formatPillarsSection(report, topColor));
111
- lines.push(formatHotspotsSection(report, topColor));
135
+ lines.push(formatHotspotsSection(report, topColor, { includePrompt: false }));
112
136
  lines.push(formatDirectoryDistributionSection(report, topColor));
113
- lines.push(formatAiSlopSection(report, topColor));
114
- lines.push(formatCriticalSection(report));
115
- lines.push(formatHighMediumSection(report));
116
- lines.push(formatLowSection(report));
137
+ lines.push(formatAiSlopSection(report, topColor, { includePrompt: false }));
138
+ lines.push(formatRoadmapSection(report, topColor));
139
+ lines.push(formatGradeASection(report));
140
+ lines.push(formatGradeBSection(report, { includePrompt: false }));
141
+ lines.push(formatGradeCSection(report, { includePrompt: false }));
142
+ lines.push(formatGradeDSection(report, { includePrompt: false }));
143
+ lines.push(formatGradeFSection(report, { includePrompt: false }));
117
144
 
118
145
  const masterPrompt = buildMasterPrompt(report);
119
146
  if (masterPrompt) {
@@ -0,0 +1,236 @@
1
+ /**
2
+ * Chemical X Protocol: Self-Healing Remediation Roadmap
3
+ * Calculates and formats the optimal sequence of architectural fixes.
4
+ */
5
+
6
+ import {
7
+ CYAN,
8
+ GREEN,
9
+ YELLOW,
10
+ RED,
11
+ ORANGE,
12
+ DIM,
13
+ BOLD,
14
+ RESET,
15
+ resolveTopSectionColor,
16
+ groupViolationsBySeverity
17
+ } from './reporter-utils.js';
18
+
19
+ export const buildRemediationRoadmap = (report) => {
20
+ const { hotspots = [], violations = [], patterns = [] } = report;
21
+ const { critical, high, medium, low } = groupViolationsBySeverity(violations);
22
+
23
+ const extremeMonoliths = hotspots.filter((h) => h.lineCount >= 2000);
24
+ const severeMonoliths = hotspots.filter((h) => h.lineCount >= 1000 && h.lineCount < 2000);
25
+ const warningMonoliths = hotspots.filter((h) => h.lineCount >= 500 && h.lineCount < 1000);
26
+
27
+ const phases = [];
28
+
29
+ // Phase 1: Pattern Recognition & Canonical Harvesting
30
+ phases.push({
31
+ step: 1,
32
+ phase: 'PHASE 1: PATTERN HARVESTING & CANONICAL EXTRACTION',
33
+ roi: 'MAXIMUM (Compounding Reuse)',
34
+ rationale: 'Extract reusable UI structures and shared domain types before slicing monoliths to eliminate redundant bespoke code.',
35
+ items: patterns.length > 0
36
+ ? patterns.map((p) => ({
37
+ title: p.label,
38
+ target: p.suggestedCapsule,
39
+ action: p.recommendation,
40
+ locations: p.occurrences.slice(0, 3).map((o) => `${o.filePath}:${o.line}`)
41
+ }))
42
+ : [
43
+ {
44
+ title: 'Cross-Monolith Pattern Survey',
45
+ target: 'src/components/molecules/',
46
+ action: 'Audit shared card layouts, common status unions, and repeated filter predicates across candidate files before code splitting.',
47
+ locations: []
48
+ }
49
+ ]
50
+ });
51
+
52
+ // Phase 2: Domain Foundations & Type Architecture
53
+ const typeViolations = violations.filter((v) => v.rule === 'TYPE_MONOLITH' || v.pillar?.includes('Pillar 4'));
54
+ phases.push({
55
+ step: 2,
56
+ phase: 'PHASE 2: DOMAIN FOUNDATIONS & TYPE INTEGRITY',
57
+ roi: 'HIGH (Compile-Time Guardrails)',
58
+ rationale: 'Co-locate granular types/*.d.ts capsules to provide strict discriminated unions for decomposing components.',
59
+ items: typeViolations.length > 0
60
+ ? typeViolations.slice(0, 5).map((v) => ({
61
+ title: v.hazard,
62
+ target: `${v.filePath} -> types/*.d.ts`,
63
+ action: v.directive,
64
+ locations: [`${v.filePath}:${v.line}`]
65
+ }))
66
+ : [
67
+ {
68
+ title: 'Domain Type Co-location',
69
+ target: 'types/*.d.ts',
70
+ action: 'Ensure molecule capsules own co-located type declarations (< 100 lines each) with zero root type dumping.',
71
+ locations: []
72
+ }
73
+ ]
74
+ });
75
+
76
+ // Phase 3: Monolith Decomposition (Grade F & D)
77
+ const topMonoliths = [...extremeMonoliths, ...severeMonoliths, ...warningMonoliths].slice(0, 6);
78
+ phases.push({
79
+ step: 3,
80
+ phase: 'PHASE 3: MONOLITH DECOMPOSITION (HOTSPOTS)',
81
+ roi: 'VERY HIGH (Context Window Unlocking)',
82
+ rationale: 'Decompose giant views into 10-20 line Table-of-Contents templates by importing canonical capsules harvested in Phase 1.',
83
+ items: topMonoliths.map((m) => ({
84
+ title: `${m.filePath} (${m.lineCount} lines, ${m.violationCount} hazards)`,
85
+ target: m.filePath,
86
+ action: 'Decompose into crystalline single-responsibility capsules (< 100 lines per molecule) reusing Phase 1 components.',
87
+ locations: [`${m.filePath}:1`]
88
+ }))
89
+ });
90
+
91
+ // Phase 4: Component Hygiene & Polish (Grade C & B)
92
+ phases.push({
93
+ step: 4,
94
+ phase: 'PHASE 4: COMPONENT HYGIENE & DESIGN SYSTEM POLISH',
95
+ roi: 'MEDIUM (Visual & Syntax Standardization)',
96
+ rationale: 'Clean up inline styles into mixins (@include glass), two-stage booleans, typography, and logging on the newly lean capsules.',
97
+ items: [
98
+ {
99
+ title: `Medium Severity Debts (${medium.length} items)`,
100
+ target: 'Component stylesheets & logic',
101
+ action: 'Replace raw inline styles with atom props or SCSS mixins (@include glass); extract anonymous callbacks into named handlers.',
102
+ locations: []
103
+ },
104
+ {
105
+ title: `Hygiene & Typography Debts (${low.length} items)`,
106
+ target: 'Copy, comments & logs',
107
+ action: 'Eliminate em dashes (use hyphens or colons), route console logs through debug proxy, and ensure FontAwesome SVG compliance.',
108
+ locations: []
109
+ }
110
+ ]
111
+ });
112
+
113
+ // Phase 5: Verification & Gatekeeping
114
+ phases.push({
115
+ step: 5,
116
+ phase: 'PHASE 5: AUTOMATED GATEKEEPING & CI ENFORCEMENT',
117
+ roi: 'PERMANENT (Zero-Entropy Protection)',
118
+ rationale: 'Verify the newly refactored codebase passes with Grade A / Score >= 90 and install pre-commit guardrails.',
119
+ items: [
120
+ {
121
+ title: 'Run Architectural Re-Audit',
122
+ target: 'chemx audit',
123
+ action: 'Execute npx chemx audit --strict to confirm zero monolith hazards remain.',
124
+ locations: []
125
+ },
126
+ {
127
+ title: 'Install Pre-Commit & CI Gatekeepers',
128
+ target: '.git/hooks/pre-commit & .github/workflows/',
129
+ action: 'Run npx chemx install to lock in line budgets and prevent future monolith regressions.',
130
+ locations: []
131
+ },
132
+ {
133
+ title: 'Branch, Commit & Pull Request Workflow',
134
+ target: 'git checkout -b chem-x/NAMEOFIMPROVEMENT',
135
+ action: 'Change to a branch prefixed with chem-x/NAMEOFIMPROVEMENT to make changes, commit the branch, and create a PR to main.',
136
+ locations: []
137
+ }
138
+ ]
139
+ });
140
+
141
+ return phases;
142
+ };
143
+
144
+ export const formatRoadmapSection = (report, themeColor = null) => {
145
+ const sectionColor = themeColor || resolveTopSectionColor(report);
146
+ const phases = buildRemediationRoadmap(report);
147
+ const lines = [];
148
+
149
+ lines.push('');
150
+ lines.push(`${sectionColor}======================================================================${RESET}`);
151
+ lines.push(`${BOLD}${sectionColor} SELF-HEALING REMEDIATION ROADMAP (OPTIMAL FIX SEQUENCE)${RESET}`);
152
+ lines.push(`${DIM} Fix in this sequence to harvest patterns first and compound code reuse${RESET}`);
153
+ lines.push(`${sectionColor}======================================================================${RESET}`);
154
+
155
+ phases.forEach((p) => {
156
+ lines.push(`\n ${CYAN}${BOLD}[STEP ${p.step}] ${p.phase}${RESET}`);
157
+ lines.push(` ${DIM}ROI: ${p.roi}${RESET}`);
158
+ lines.push(` ${YELLOW}Strategy: ${p.rationale}${RESET}`);
159
+
160
+ p.items.forEach((item, idx) => {
161
+ lines.push(` ${idx + 1}. ${BOLD}${item.title}${RESET}`);
162
+ lines.push(` Target: ${CYAN}${item.target}${RESET}`);
163
+ lines.push(` Action: ${item.action}`);
164
+ if (item.locations.length > 0) {
165
+ lines.push(` Locations: ${DIM}${item.locations.join(', ')}${RESET}`);
166
+ }
167
+ });
168
+ });
169
+
170
+ lines.push(`\n${sectionColor}======================================================================${RESET}\n`);
171
+ return lines.join('\n');
172
+ };
173
+
174
+ export const formatRoadmapMarkdown = (report) => {
175
+ const phases = buildRemediationRoadmap(report);
176
+ const lines = [];
177
+
178
+ lines.push('## 🗺 Recommended Remediation Sequence (Self-Healing Roadmap)\n');
179
+ lines.push('> Follow this sequence strictly: harvesting patterns first creates reusable building blocks that eliminate duplicate work during subsequent monolith slicing.\n');
180
+
181
+ phases.forEach((p) => {
182
+ lines.push(`### Step ${p.step}: ${p.phase}`);
183
+ lines.push(`**ROI**: ${p.roi} | **Strategy**: ${p.rationale}\n`);
184
+
185
+ p.items.forEach((item) => {
186
+ lines.push(`- [ ] **${item.title}**`);
187
+ lines.push(` - **Target**: \`${item.target}\``);
188
+ lines.push(` - **Action**: ${item.action}`);
189
+ if (item.locations.length > 0) {
190
+ lines.push(` - **Locations**: \`${item.locations.join('`, `')}\``);
191
+ }
192
+ });
193
+ lines.push('');
194
+ });
195
+
196
+ return lines.join('\n');
197
+ };
198
+
199
+ export const buildSelfHealingRoadmapPrompt = (report) => {
200
+ const phases = buildRemediationRoadmap(report);
201
+ const lines = [];
202
+
203
+ lines.push('Act as a Principal Systems Architect. Execute a self-healing architectural remediation of our codebase by following this strict multi-phase sequence in order:\n');
204
+
205
+ lines.push('### MANDATORY GIT BRANCHING & PR WORKFLOW:');
206
+ lines.push('1. Before modifying any files, change to a dedicated feature branch prefixed with `chem-x/NAMEOFIMPROVEMENT`:');
207
+ lines.push(' `git checkout -b chem-x/NAMEOFIMPROVEMENT`');
208
+ lines.push('2. Execute each remediation phase sequentially on this branch.');
209
+ lines.push('3. Commit the branch with atomic, intention-revealing commit messages.');
210
+ lines.push('4. Create a Pull Request (PR) to `main` upon completing the remediation phases.\n');
211
+
212
+ phases.forEach((p) => {
213
+ lines.push(`### STEP ${p.step}: ${p.phase}`);
214
+ lines.push(`Goal: ${p.rationale}\n`);
215
+ p.items.forEach((item, idx) => {
216
+ lines.push(`${idx + 1}. Task: ${item.title}`);
217
+ lines.push(` Target: ${item.target}`);
218
+ lines.push(` Execution: ${item.action}`);
219
+ if (item.locations.length > 0) {
220
+ lines.push(` Files: ${item.locations.join(', ')}`);
221
+ }
222
+ });
223
+ lines.push('');
224
+ });
225
+
226
+ lines.push('### EXECUTION DISCIPLINE:');
227
+ lines.push('1. Branch First: Change to a new branch prefixed with `chem-x/NAMEOFIMPROVEMENT` (e.g. `git checkout -b chem-x/<name-of-improvement>`) before making any changes.');
228
+ lines.push('2. Complete Step 1 (Pattern Harvesting) completely before touching any monolithic file in Step 3.');
229
+ lines.push('3. Spliced views in Step 3 must directly import and bind to the canonical capsules created in Step 1.');
230
+ lines.push('4. Every new molecule component must stay under 100 lines.');
231
+ lines.push('5. Top-level page views must be 10-20 line declarative Table-of-Contents templates assembling molecules via named slots.');
232
+ lines.push('6. Zero synthetic or mock data; zero em dashes (use hyphens or colons).');
233
+ lines.push('7. PR to Main: Commit the branch and create a Pull Request to main.');
234
+
235
+ return lines.join('\n');
236
+ };
@@ -0,0 +1,17 @@
1
+ import type * as babelTypes from '@babel/types';
2
+
3
+ export declare function isComponentPath(path: string): boolean;
4
+ export declare function isCodeLine(line: string): boolean;
5
+ export declare function isConsoleCallStatement(stmt: unknown, t: typeof babelTypes): boolean;
6
+ export declare function isShallowCatchBody(body: readonly unknown[], t: typeof babelTypes): boolean;
7
+ export declare function hasAnyTypeAnnotation(param: unknown, t: typeof babelTypes): boolean;
8
+ export declare function isRedundantPassthroughReturn(curr: unknown, next: unknown, t: typeof babelTypes): boolean;
9
+ export declare function isHookIdentifier(idNode: unknown): boolean;
10
+ export declare function isCustomHookFunction(astPath: unknown): boolean;
11
+ export declare function resolveStartLine(primaryNode?: unknown, fallbackNode?: unknown, defaultLine?: number): number;
12
+ export declare function isZeroDelayTimeout(callee: unknown, delayArg: unknown, t: typeof babelTypes): boolean;
13
+ export declare function isUnguardedConsoleCall(callee: unknown, t: typeof babelTypes): boolean;
14
+ export declare function matchesDiscussionTitle(titleLower: string, targetLower: string): boolean;
15
+ export declare function isGradeBelowMinimum(currentGrade: string, minGrade?: string | null): boolean;
16
+ export declare function evaluateAuditFailure(conditions?: readonly boolean[]): boolean;
17
+ export declare function isNonInteractiveSession(rawArgs: readonly string[], env?: Record<string, string | undefined>): boolean;
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Chemical X Protocol: Canonical Predicates for AST & CLI Control Flow
3
+ * Reusable single-concept booleans and higher-order predicate helpers.
4
+ */
5
+
6
+ const COMPONENT_PATH_SEGMENTS = ['molecules', 'components', '/m-', '/views/', '/pages/'];
7
+ const CONSOLE_LOG_METHODS = ['log', 'info', 'warn'];
8
+ const NON_INTERACTIVE_FLAGS = ['--non-interactive', '--no-interactive', '--ci'];
9
+ const GRADE_RANKS = { 'A+': 5, 'A': 4, 'B': 3, 'C': 2, 'D': 1, 'F': 0 };
10
+
11
+ export const isComponentPath = (path) => {
12
+ return COMPONENT_PATH_SEGMENTS.some((segment) => path.includes(segment));
13
+ };
14
+
15
+ export const isCodeLine = (line) => {
16
+ if (!line) return false;
17
+ const isLineComment = line.startsWith('//');
18
+ const isBlockComment = line.startsWith('/*');
19
+ return !isLineComment && !isBlockComment;
20
+ };
21
+
22
+ export const isConsoleCallStatement = (stmt, t) => {
23
+ if (!t.isExpressionStatement(stmt)) return false;
24
+ const expr = stmt.expression;
25
+ if (!t.isCallExpression(expr)) return false;
26
+ const callee = expr.callee;
27
+ if (!t.isMemberExpression(callee)) return false;
28
+ return t.isIdentifier(callee.object, { name: 'console' });
29
+ };
30
+
31
+ export const isShallowCatchBody = (body, t) => {
32
+ if (body.length === 0) return true;
33
+ if (body.length === 1) return isConsoleCallStatement(body[0], t);
34
+ return false;
35
+ };
36
+
37
+ export const hasAnyTypeAnnotation = (param, t) => {
38
+ if (!param || !t.isIdentifier(param)) return false;
39
+ const typeAnn = param.typeAnnotation;
40
+ if (!typeAnn || !t.isTSTypeAnnotation(typeAnn)) return false;
41
+ return t.isTSAnyKeyword(typeAnn.typeAnnotation);
42
+ };
43
+
44
+ export const isRedundantPassthroughReturn = (curr, next, t) => {
45
+ if (!t.isVariableDeclaration(curr) || curr.declarations.length !== 1) return false;
46
+ const decl = curr.declarations[0];
47
+ if (!t.isIdentifier(decl.id)) return false;
48
+ if (!t.isReturnStatement(next) || !t.isIdentifier(next.argument)) return false;
49
+ return decl.id.name === next.argument.name;
50
+ };
51
+
52
+ export const isHookIdentifier = (idNode) => {
53
+ return Boolean(idNode?.name && /^use[A-Z0-9]/.test(idNode.name));
54
+ };
55
+
56
+ export const isCustomHookFunction = (astPath) => {
57
+ const hasHookId = isHookIdentifier(astPath.node.id);
58
+ const hasParentHookId = isHookIdentifier(astPath.parentPath?.node?.id);
59
+ return hasHookId || hasParentHookId;
60
+ };
61
+
62
+ export const resolveStartLine = (primaryNode, fallbackNode, defaultLine = 1) => {
63
+ return primaryNode?.loc?.start?.line ?? fallbackNode?.loc?.start?.line ?? defaultLine;
64
+ };
65
+
66
+ export const isZeroDelayTimeout = (callee, delayArg, t) => {
67
+ const isTimeoutId = t.isIdentifier(callee, { name: 'setTimeout' });
68
+ const isZeroNumeric = t.isNumericLiteral(delayArg) && delayArg.value === 0;
69
+ return isTimeoutId && isZeroNumeric;
70
+ };
71
+
72
+ export const isUnguardedConsoleCall = (callee, t) => {
73
+ if (!t.isMemberExpression(callee)) return false;
74
+ if (!t.isIdentifier(callee.object, { name: 'console' })) return false;
75
+ if (!t.isIdentifier(callee.property)) return false;
76
+ return CONSOLE_LOG_METHODS.includes(callee.property.name);
77
+ };
78
+
79
+ export const matchesDiscussionTitle = (titleLower, targetLower) => {
80
+ const patterns = [
81
+ ` ${targetLower} :`,
82
+ ` ${targetLower} [`,
83
+ `: ${targetLower}`,
84
+ `${targetLower} audit`
85
+ ];
86
+ return patterns.some((p) => titleLower.includes(p));
87
+ };
88
+
89
+ export const isGradeBelowMinimum = (currentGrade, minGrade) => {
90
+ if (!minGrade) return false;
91
+ const currentRank = GRADE_RANKS[currentGrade];
92
+ const minRank = GRADE_RANKS[minGrade];
93
+ const hasValidRanks = currentRank !== undefined && minRank !== undefined;
94
+ return hasValidRanks && currentRank < minRank;
95
+ };
96
+
97
+ export const evaluateAuditFailure = (conditions = []) => {
98
+ return conditions.some(Boolean);
99
+ };
100
+
101
+ export const isNonInteractiveSession = (rawArgs, env = process.env) => {
102
+ const hasCliFlag = NON_INTERACTIVE_FLAGS.some((flag) => rawArgs.includes(flag));
103
+ const hasOutputFlag = rawArgs.some((arg) => arg.startsWith('--output=') || arg.startsWith('-o='));
104
+ const hasFormatFlag = rawArgs.includes('--markdown') || rawArgs.includes('--md') || rawArgs.includes('--json');
105
+ const hasCiEnv = Boolean(env.CI || env.GIT_DIR);
106
+ return hasCliFlag || hasOutputFlag || hasFormatFlag || hasCiEnv;
107
+ };
@@ -18,10 +18,11 @@ import {
18
18
  checkExtendedTextPatterns,
19
19
  createExtendedVisitors
20
20
  } from './extended-visitors.js';
21
+ import { createPatternVisitors } from './pattern-detector.js';
21
22
 
22
23
  export { PILLARS, RULE_REGISTRY };
23
24
 
24
- export const auditCode = (content, filePath, relativePath) => {
25
+ export const auditCode = (content, filePath, relativePath, options = {}) => {
25
26
  const violations = [];
26
27
  const lines = content.split('\n');
27
28
  const lineCount = lines.length;
@@ -132,9 +133,12 @@ export const auditCode = (content, filePath, relativePath) => {
132
133
  const visitors = createAstVisitors({ relativePath, violations });
133
134
  const slopVisitors = createAiSlopVisitors({ relativePath, violations });
134
135
  const extendedVisitors = createExtendedVisitors({ relativePath, violations });
136
+ const patternVisitors = options.patternRegistry
137
+ ? createPatternVisitors(options.patternRegistry, relativePath)
138
+ : {};
135
139
 
136
140
  const mergedVisitors = { ...visitors };
137
- for (const visitorSet of [slopVisitors, extendedVisitors]) {
141
+ for (const visitorSet of [slopVisitors, extendedVisitors, patternVisitors]) {
138
142
  for (const [key, fn] of Object.entries(visitorSet)) {
139
143
  if (mergedVisitors[key]) {
140
144
  const orig = mergedVisitors[key];