@chemx/starter-kit 26.9.12-272 → 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.
- package/cli/audit/ai-slop-detector.js +25 -41
- package/cli/audit/ast-visitors.js +10 -13
- package/cli/audit/history.js +45 -11
- package/cli/audit/pattern-detector.js +182 -0
- package/cli/audit/prompts.js +13 -10
- package/cli/audit/reporter-grades.js +28 -16
- package/cli/audit/reporter-markdown.js +31 -16
- package/cli/audit/reporter-sections.js +14 -4
- package/cli/audit/reporter-utils.js +75 -0
- package/cli/audit/reporter.d.ts +13 -4
- package/cli/audit/reporter.js +29 -3
- package/cli/audit/roadmap.js +236 -0
- package/cli/audit/rules-predicates.d.ts +17 -0
- package/cli/audit/rules-predicates.js +107 -0
- package/cli/audit/rules.js +6 -2
- package/cli/audit/social-gh.js +2 -6
- package/cli/audit/social-publisher.js +1 -1
- package/cli/audit/social.js +25 -9
- package/cli/audit/types.d.ts +43 -1
- package/cli/audit.js +47 -7
- package/cli/build/detector.js +68 -0
- package/cli/build/executor.js +58 -0
- package/cli/build/grouper.js +78 -0
- package/cli/build/parser-matchers.js +116 -0
- package/cli/build/parser.js +80 -0
- package/cli/build/reporter.js +66 -0
- package/cli/build/types.d.ts +58 -0
- package/cli/build/types.js +1 -0
- package/cli/build.d.ts +16 -0
- package/cli/build.js +55 -0
- package/cli/help.js +14 -0
- package/cli/index.js +38 -19
- package/cli/installer-templates.js +42 -3
- package/cli/license.js +10 -7
- package/cli/navigator-actions.js +34 -6
- package/cli/navigator-banner.js +4 -2
- package/cli/navigator-menu.js +18 -0
- package/cli/navigator-share.js +1 -1
- package/cli/navigator.d.ts +8 -1
- package/cli/navigator.js +3 -3
- package/docs/CHANGELOG.md +36 -0
- package/package.json +3 -2
|
@@ -4,6 +4,9 @@ import {
|
|
|
4
4
|
resolveHealthHearts,
|
|
5
5
|
resolveMarkdownStatusIcon,
|
|
6
6
|
resolveMarkdownMonolithText,
|
|
7
|
+
formatPillarReactionBadgesMarkdown,
|
|
8
|
+
formatPillarShieldBadges,
|
|
9
|
+
resolveBadgeColor,
|
|
7
10
|
PILLAR_EMOJIS,
|
|
8
11
|
NUMBER_EMOJIS
|
|
9
12
|
} from './reporter-utils.js';
|
|
@@ -11,14 +14,30 @@ import {
|
|
|
11
14
|
formatDirectoryRollupMarkdown,
|
|
12
15
|
renderGroupedViolationsMarkdown
|
|
13
16
|
} from './reporter-grouping-markdown.js';
|
|
17
|
+
import { formatRoadmapMarkdown } from './roadmap.js';
|
|
14
18
|
|
|
15
19
|
export const generateMarkdownReport = (report) => {
|
|
16
20
|
const { metrics, health, pillars, contextAnalysis, hotspots, violations, aiSlop } = report;
|
|
17
21
|
const { critical, high, medium, low } = groupViolationsBySeverity(violations);
|
|
18
22
|
const lines = [];
|
|
19
23
|
|
|
24
|
+
const badgeColor = resolveBadgeColor(health.score);
|
|
25
|
+
const encodedGrade = encodeURIComponent(`${health.score}/100 (${health.grade})`);
|
|
26
|
+
const slopScore = aiSlop?.score ?? 100;
|
|
27
|
+
const slopGrade = aiSlop?.grade ?? 'A+';
|
|
28
|
+
const slopLabel = aiSlop?.label ?? 'Pure Artisanal';
|
|
29
|
+
const slopBadgeColor = resolveBadgeColor(slopScore);
|
|
30
|
+
const encodedSlop = encodeURIComponent(`${slopScore}/100 (${slopGrade})`);
|
|
31
|
+
|
|
20
32
|
lines.push('# Chemical X Protocol: Architectural Audit Report');
|
|
21
33
|
lines.push('');
|
|
34
|
+
lines.push(`[](https://chemicalx.xophz.com) [](https://chemicalx.xophz.com) [](https://chemicalx.xophz.com)`);
|
|
35
|
+
lines.push('');
|
|
36
|
+
const pillarShields = formatPillarShieldBadges(pillars);
|
|
37
|
+
if (pillarShields) {
|
|
38
|
+
lines.push(pillarShields);
|
|
39
|
+
lines.push('');
|
|
40
|
+
}
|
|
22
41
|
lines.push('> *Comprehensive static analysis report enforcing Chemical X Molecular Architecture standards.*');
|
|
23
42
|
lines.push('');
|
|
24
43
|
lines.push('## 1. Executive Summary & Scorecard');
|
|
@@ -28,9 +47,6 @@ export const generateMarkdownReport = (report) => {
|
|
|
28
47
|
const hearts = resolveHealthHearts(health?.grade, health?.score, false);
|
|
29
48
|
lines.push(`| **Life / Health Meter** | ${hearts} (Grade **${health.grade}**) | ${health.score} / 100 (${health.label}) |`);
|
|
30
49
|
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
50
|
lines.push(`| **AI Slop Index (ASI)** | **${slopScore} / 100** | Grade: **${slopGrade}** (${slopLabel}) |`);
|
|
35
51
|
lines.push(`| **Scanned Files** | ${metrics.scannedFiles} source files | Verified |`);
|
|
36
52
|
lines.push(`| **Total Lines of Code** | ${metrics.totalLoc} lines of code | Avg ${metrics.avgLoc} lines/file |`);
|
|
@@ -86,19 +102,25 @@ export const generateMarkdownReport = (report) => {
|
|
|
86
102
|
lines.push(formatDirectoryRollupMarkdown(violations));
|
|
87
103
|
|
|
88
104
|
if (critical.length > 0) {
|
|
89
|
-
lines.push('### Critical Hazards (Immediate Action Required)');
|
|
105
|
+
lines.push('### Grade F: Critical Hazards & Extreme Monoliths (Immediate Action Required)');
|
|
90
106
|
lines.push('');
|
|
91
107
|
lines.push(renderGroupedViolationsMarkdown(critical, { showSeverity: false }));
|
|
92
108
|
}
|
|
93
109
|
|
|
94
|
-
if (high.length > 0
|
|
95
|
-
lines.push('### High
|
|
110
|
+
if (high.length > 0) {
|
|
111
|
+
lines.push('### Grade D: High Severity Debts & Severe Monoliths');
|
|
96
112
|
lines.push('');
|
|
97
|
-
lines.push(renderGroupedViolationsMarkdown(
|
|
113
|
+
lines.push(renderGroupedViolationsMarkdown(high, { showSeverity: false }));
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (medium.length > 0) {
|
|
117
|
+
lines.push('### Grade C: Medium Severity Debts & Monolithic Drift');
|
|
118
|
+
lines.push('');
|
|
119
|
+
lines.push(renderGroupedViolationsMarkdown(medium, { showSeverity: false }));
|
|
98
120
|
}
|
|
99
121
|
|
|
100
122
|
if (low.length > 0) {
|
|
101
|
-
lines.push('### Low
|
|
123
|
+
lines.push('### Grade B: Low Severity Hygiene & Minor Debts');
|
|
102
124
|
lines.push('');
|
|
103
125
|
lines.push(renderGroupedViolationsMarkdown(low, { showSeverity: false }));
|
|
104
126
|
}
|
|
@@ -122,14 +144,7 @@ export const generateMarkdownReport = (report) => {
|
|
|
122
144
|
|
|
123
145
|
lines.push('---');
|
|
124
146
|
lines.push('');
|
|
125
|
-
lines.push(
|
|
126
|
-
lines.push('');
|
|
127
|
-
lines.push('1. **Decompose Monoliths**: Break down any file exceeding 500 lines into single-responsibility capsules.');
|
|
128
|
-
lines.push('2. **Cap Molecule Size**: Ensure all molecule components (`m-*`) remain strictly under 100 lines.');
|
|
129
|
-
lines.push('3. **Two-Stage Boolean Logic**: Replace complex multi-clause expressions with atomic boolean variables.');
|
|
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.');
|
|
132
|
-
lines.push('');
|
|
147
|
+
lines.push(formatRoadmapMarkdown(report));
|
|
133
148
|
|
|
134
149
|
const masterPrompt = buildMasterPrompt(report);
|
|
135
150
|
if (masterPrompt) {
|
|
@@ -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
|
-
|
|
157
|
-
|
|
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
|
|
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}`);
|
|
@@ -129,6 +129,49 @@ export const PILLAR_EMOJIS = {
|
|
|
129
129
|
'Naming Conventions': '🏷️'
|
|
130
130
|
};
|
|
131
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(`[](https://chemicalx.xophz.com)`);
|
|
171
|
+
}
|
|
172
|
+
return badges.join(' ');
|
|
173
|
+
};
|
|
174
|
+
|
|
132
175
|
export const NUMBER_EMOJIS = ['1️⃣', '2️⃣', '3️⃣', '4️⃣', '5️⃣', '6️⃣', '7️⃣', '8️⃣', '9️⃣', '🔟'];
|
|
133
176
|
|
|
134
177
|
export const resolvePriorityBadge = (idx) => {
|
|
@@ -147,3 +190,35 @@ export const resolveMarkdownMonolithText = (lineCount) => {
|
|
|
147
190
|
if (lineCount > 500) return '🟡 **WARNING (> 500 lines of code)**';
|
|
148
191
|
return '🟢 Compliant';
|
|
149
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
|
+
};
|
package/cli/audit/reporter.d.ts
CHANGED
|
@@ -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;
|
package/cli/audit/reporter.js
CHANGED
|
@@ -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,
|
|
@@ -112,9 +135,12 @@ export const formatTerminalReport = (report) => {
|
|
|
112
135
|
lines.push(formatHotspotsSection(report, topColor, { includePrompt: false }));
|
|
113
136
|
lines.push(formatDirectoryDistributionSection(report, topColor));
|
|
114
137
|
lines.push(formatAiSlopSection(report, topColor, { includePrompt: false }));
|
|
115
|
-
lines.push(
|
|
116
|
-
lines.push(
|
|
117
|
-
lines.push(
|
|
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 }));
|
|
118
144
|
|
|
119
145
|
const masterPrompt = buildMasterPrompt(report);
|
|
120
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
|
+
};
|