@chemx/starter-kit 26.9.9-786 → 26.9.11-362
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/ast-visitors.js +243 -0
- package/cli/audit/discussion-store.js +39 -0
- package/cli/audit/history.d.ts +63 -0
- package/cli/audit/history.js +352 -0
- package/cli/audit/metrics.js +179 -0
- package/cli/audit/prompts.js +174 -0
- package/cli/audit/reporter-grades.js +259 -0
- package/cli/audit/reporter-markdown.js +136 -0
- package/cli/audit/reporter-utils.js +107 -0
- package/cli/audit/reporter.js +459 -0
- package/cli/audit/rules-helpers.js +109 -0
- package/cli/audit/rules-registry.js +97 -0
- package/cli/audit/rules.js +123 -0
- package/cli/audit/social-constants.js +4 -0
- package/cli/audit/social-gh.js +126 -0
- package/cli/audit/social-git.js +214 -0
- package/cli/audit/social-http.js +222 -0
- package/cli/audit/social-publisher.js +193 -0
- package/cli/audit/social.d.ts +66 -0
- package/cli/audit/social.js +254 -0
- package/cli/audit/types.d.ts +131 -0
- package/cli/audit.d.ts +3 -24
- package/cli/audit.js +195 -210
- package/cli/help.d.ts +1 -0
- package/cli/help.js +136 -0
- package/cli/index.js +128 -566
- package/cli/installer-templates.js +80 -0
- package/cli/installer.d.ts +20 -0
- package/cli/installer.js +56 -0
- package/cli/license.js +320 -0
- package/cli/navigator-conversion.js +100 -0
- package/cli/navigator-grades.js +70 -0
- package/cli/navigator-paged.js +86 -0
- package/cli/navigator-share.js +129 -0
- package/cli/navigator.d.ts +24 -0
- package/cli/navigator.js +400 -0
- package/cli/scaffold.js +148 -0
- package/cli/terminal.js +126 -0
- package/docs/CHANGELOG.md +73 -0
- package/package.json +11 -10
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DISCUSSION_CATEGORY,
|
|
3
|
+
DISCUSSION_CATEGORY_SLUG,
|
|
4
|
+
ORG_DISCUSSIONS_URL,
|
|
5
|
+
DEFAULT_DISCUSSION_REPO
|
|
6
|
+
} from './social-constants.js';
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
getStoredDiscussion,
|
|
10
|
+
saveStoredDiscussion,
|
|
11
|
+
clearStoredDiscussion
|
|
12
|
+
} from './discussion-store.js';
|
|
13
|
+
|
|
14
|
+
import {
|
|
15
|
+
findMatchedCategory,
|
|
16
|
+
publishDiscussionViaHttp,
|
|
17
|
+
viewDiscussionViaHttp,
|
|
18
|
+
postDiscussionCommentViaHttp,
|
|
19
|
+
editDiscussionViaHttp
|
|
20
|
+
} from './social-http.js';
|
|
21
|
+
|
|
22
|
+
import {
|
|
23
|
+
publishDiscussionViaGh,
|
|
24
|
+
viewDiscussionViaGh,
|
|
25
|
+
postDiscussionCommentViaGh,
|
|
26
|
+
editDiscussionViaGh,
|
|
27
|
+
findExistingDiscussionViaGh
|
|
28
|
+
} from './social-gh.js';
|
|
29
|
+
|
|
30
|
+
export {
|
|
31
|
+
DISCUSSION_CATEGORY,
|
|
32
|
+
DISCUSSION_CATEGORY_SLUG,
|
|
33
|
+
ORG_DISCUSSIONS_URL,
|
|
34
|
+
DEFAULT_DISCUSSION_REPO
|
|
35
|
+
} from './social-constants.js';
|
|
36
|
+
|
|
37
|
+
export {
|
|
38
|
+
getStoredDiscussion,
|
|
39
|
+
saveStoredDiscussion,
|
|
40
|
+
clearStoredDiscussion
|
|
41
|
+
} from './discussion-store.js';
|
|
42
|
+
|
|
43
|
+
export {
|
|
44
|
+
findMatchedCategory,
|
|
45
|
+
publishDiscussionViaHttp,
|
|
46
|
+
viewDiscussionViaHttp,
|
|
47
|
+
postDiscussionCommentViaHttp,
|
|
48
|
+
editDiscussionViaHttp
|
|
49
|
+
} from './social-http.js';
|
|
50
|
+
|
|
51
|
+
export {
|
|
52
|
+
publishDiscussionViaGh,
|
|
53
|
+
viewDiscussionViaGh,
|
|
54
|
+
postDiscussionCommentViaGh,
|
|
55
|
+
editDiscussionViaGh,
|
|
56
|
+
findExistingDiscussionViaGh
|
|
57
|
+
} from './social-gh.js';
|
|
58
|
+
|
|
59
|
+
export const formatArchiveComment = (previousTitle, previousBody) => {
|
|
60
|
+
const archiveTimestamp = new Date().toUTCString();
|
|
61
|
+
return [
|
|
62
|
+
'## 📜 Previous Audit Snapshot (Archived)',
|
|
63
|
+
'',
|
|
64
|
+
`> **Archived on:** ${archiveTimestamp}`,
|
|
65
|
+
`> **Previous Status:** ${previousTitle || 'Prior Report'}`,
|
|
66
|
+
'',
|
|
67
|
+
'---',
|
|
68
|
+
'',
|
|
69
|
+
previousBody
|
|
70
|
+
].join('\n');
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
export const publishDiscussion = async (repo, title, body, category = DISCUSSION_CATEGORY) => {
|
|
74
|
+
const envToken = process.env.GH_TOKEN || process.env.GITHUB_TOKEN;
|
|
75
|
+
if (envToken) {
|
|
76
|
+
const httpResult = await publishDiscussionViaHttp(envToken, repo, title, body, category);
|
|
77
|
+
if (httpResult.success) {
|
|
78
|
+
return httpResult;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return publishDiscussionViaGh(repo, title, body, category);
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
export const publishOrUpdateDiscussion = async (
|
|
86
|
+
repo = DEFAULT_DISCUSSION_REPO,
|
|
87
|
+
title,
|
|
88
|
+
body,
|
|
89
|
+
category = DISCUSSION_CATEGORY,
|
|
90
|
+
options = {}
|
|
91
|
+
) => {
|
|
92
|
+
const envToken = process.env.GH_TOKEN || process.env.GITHUB_TOKEN;
|
|
93
|
+
const stored = getStoredDiscussion();
|
|
94
|
+
const targetRepo = repo || DEFAULT_DISCUSSION_REPO;
|
|
95
|
+
|
|
96
|
+
let existingDiscussion = null;
|
|
97
|
+
|
|
98
|
+
if (stored && stored.number && (!stored.repo || stored.repo === targetRepo)) {
|
|
99
|
+
if (envToken) {
|
|
100
|
+
existingDiscussion = await viewDiscussionViaHttp(envToken, targetRepo, stored.number);
|
|
101
|
+
}
|
|
102
|
+
if (!existingDiscussion) {
|
|
103
|
+
existingDiscussion = viewDiscussionViaGh(targetRepo, stored.number);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (!existingDiscussion && options.projectName) {
|
|
108
|
+
existingDiscussion = findExistingDiscussionViaGh(targetRepo, options.projectName);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (existingDiscussion && existingDiscussion.number) {
|
|
112
|
+
const discussionNumber = existingDiscussion.number;
|
|
113
|
+
const discussionId = existingDiscussion.id || stored?.discussionId;
|
|
114
|
+
const previousBody = existingDiscussion.body || '';
|
|
115
|
+
|
|
116
|
+
const hasPreviousBody = previousBody.trim().length > 0;
|
|
117
|
+
if (hasPreviousBody) {
|
|
118
|
+
const archiveComment = formatArchiveComment(existingDiscussion.title, previousBody);
|
|
119
|
+
|
|
120
|
+
if (envToken && discussionId) {
|
|
121
|
+
await postDiscussionCommentViaHttp(envToken, discussionId, archiveComment);
|
|
122
|
+
} else {
|
|
123
|
+
postDiscussionCommentViaGh(targetRepo, discussionNumber, archiveComment);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
let editSuccess = false;
|
|
128
|
+
if (envToken && discussionId) {
|
|
129
|
+
editSuccess = await editDiscussionViaHttp(envToken, discussionId, title, body);
|
|
130
|
+
}
|
|
131
|
+
if (!editSuccess) {
|
|
132
|
+
editSuccess = editDiscussionViaGh(targetRepo, discussionNumber, title, body);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const fallbackUrl = `https://github.com/${targetRepo}/discussions/${discussionNumber}`;
|
|
136
|
+
const discussionUrl = existingDiscussion.url || stored?.url || fallbackUrl;
|
|
137
|
+
|
|
138
|
+
if (editSuccess) {
|
|
139
|
+
saveStoredDiscussion({
|
|
140
|
+
repo: targetRepo,
|
|
141
|
+
number: discussionNumber,
|
|
142
|
+
discussionId,
|
|
143
|
+
url: discussionUrl,
|
|
144
|
+
projectName: options.projectName || stored?.projectName || '',
|
|
145
|
+
website: options.website || stored?.website || '',
|
|
146
|
+
title,
|
|
147
|
+
lastPublishedAt: new Date().toISOString()
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
return {
|
|
151
|
+
success: true,
|
|
152
|
+
url: discussionUrl,
|
|
153
|
+
updated: true,
|
|
154
|
+
discussionNumber,
|
|
155
|
+
error: null
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const pubResult = await publishDiscussion(targetRepo, title, body, category);
|
|
161
|
+
if (pubResult.success && pubResult.url) {
|
|
162
|
+
const match = pubResult.url.match(/\/discussions\/(\d+)/);
|
|
163
|
+
const parsedNumber = match ? parseInt(match[1], 10) : null;
|
|
164
|
+
const discussionNumber = pubResult.number || parsedNumber;
|
|
165
|
+
|
|
166
|
+
saveStoredDiscussion({
|
|
167
|
+
repo: targetRepo,
|
|
168
|
+
number: discussionNumber,
|
|
169
|
+
discussionId: pubResult.id || null,
|
|
170
|
+
url: pubResult.url,
|
|
171
|
+
projectName: options.projectName || '',
|
|
172
|
+
website: options.website || '',
|
|
173
|
+
title,
|
|
174
|
+
lastPublishedAt: new Date().toISOString()
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
return {
|
|
178
|
+
success: true,
|
|
179
|
+
url: pubResult.url,
|
|
180
|
+
updated: false,
|
|
181
|
+
discussionNumber,
|
|
182
|
+
error: null
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return {
|
|
187
|
+
success: false,
|
|
188
|
+
url: null,
|
|
189
|
+
updated: false,
|
|
190
|
+
discussionNumber: null,
|
|
191
|
+
error: pubResult.error
|
|
192
|
+
};
|
|
193
|
+
};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { AuditReport } from './types';
|
|
2
|
+
import type { AuditSnapshot } from './history';
|
|
3
|
+
|
|
4
|
+
export interface DiscussionContent {
|
|
5
|
+
readonly title: string;
|
|
6
|
+
readonly category: string;
|
|
7
|
+
readonly categorySlug: string;
|
|
8
|
+
readonly body: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface GitRepoInfo {
|
|
12
|
+
readonly owner: string;
|
|
13
|
+
readonly repo: string;
|
|
14
|
+
readonly nameWithOwner: string;
|
|
15
|
+
readonly url: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export declare const DISCUSSION_CATEGORY: string;
|
|
19
|
+
export declare const DISCUSSION_CATEGORY_SLUG: string;
|
|
20
|
+
export declare const ORG_DISCUSSIONS_URL: string;
|
|
21
|
+
export declare const DEFAULT_DISCUSSION_REPO: string;
|
|
22
|
+
|
|
23
|
+
export declare function parseGitRemoteUrl(url: string): GitRepoInfo | null;
|
|
24
|
+
export declare function detectGitRepoInfo(cwd?: string): GitRepoInfo;
|
|
25
|
+
export declare function detectGitHubUser(preferredUser?: string): string;
|
|
26
|
+
export declare function copyToClipboard(text: string): boolean;
|
|
27
|
+
export declare function resolveBadgeColor(score: number): string;
|
|
28
|
+
export declare function resolveHotspotTierText(lineCount: number): string;
|
|
29
|
+
export declare function resolvePillarProgressionBadge(isImproved: boolean, beforeStatus: string, afterStatus: string): string;
|
|
30
|
+
export declare function formatWebUrl(url: string): string;
|
|
31
|
+
export declare function generateDiscussionContent(report: AuditReport, username: string, projectName?: string, repoUrl?: string, liveUrl?: string): DiscussionContent;
|
|
32
|
+
export declare function generateTransformationDiscussionContent(beforeSnapshot: AuditSnapshot, afterSnapshot: AuditSnapshot, username: string, projectName?: string, repoUrl?: string, liveUrl?: string): DiscussionContent;
|
|
33
|
+
export declare function publishDiscussionViaGh(repo: string, title: string, body: string, category?: string): { success: boolean; url: string | null; error?: string | null };
|
|
34
|
+
export declare function publishDiscussionViaHttp(token: string, repo: string, title: string, body: string, categoryName?: string): Promise<{ success: boolean; url: string | null; error?: string | null }>;
|
|
35
|
+
export declare function publishDiscussion(repo: string, title: string, body: string, category?: string): Promise<{ success: boolean; url: string | null; error?: string | null }>;
|
|
36
|
+
|
|
37
|
+
export interface StoredDiscussion {
|
|
38
|
+
readonly repo: string;
|
|
39
|
+
readonly number: number | null;
|
|
40
|
+
readonly discussionId?: string | null;
|
|
41
|
+
readonly url: string;
|
|
42
|
+
readonly projectName?: string;
|
|
43
|
+
readonly website?: string;
|
|
44
|
+
readonly title: string;
|
|
45
|
+
readonly lastPublishedAt: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface DiscussionPublishResult {
|
|
49
|
+
readonly success: boolean;
|
|
50
|
+
readonly url: string | null;
|
|
51
|
+
readonly updated?: boolean;
|
|
52
|
+
readonly discussionNumber?: number | null;
|
|
53
|
+
readonly error?: string | null;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export declare function getStoredDiscussion(cwd?: string): StoredDiscussion | null;
|
|
57
|
+
export declare function saveStoredDiscussion(data: StoredDiscussion, cwd?: string): boolean;
|
|
58
|
+
export declare function clearStoredDiscussion(cwd?: string): boolean;
|
|
59
|
+
export declare function formatArchiveComment(previousTitle: string, previousBody: string): string;
|
|
60
|
+
export declare function publishOrUpdateDiscussion(
|
|
61
|
+
repo?: string,
|
|
62
|
+
title?: string,
|
|
63
|
+
body?: string,
|
|
64
|
+
category?: string,
|
|
65
|
+
options?: { readonly projectName?: string; readonly website?: string }
|
|
66
|
+
): Promise<DiscussionPublishResult>;
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import { groupViolationsBySeverity } from './reporter.js';
|
|
2
|
+
import { PILLAR_EMOJIS } from './reporter-utils.js';
|
|
3
|
+
|
|
4
|
+
export {
|
|
5
|
+
parseGitRemoteUrl,
|
|
6
|
+
detectGitRepoInfo,
|
|
7
|
+
detectGitHubUser,
|
|
8
|
+
copyToClipboard
|
|
9
|
+
} from './social-git.js';
|
|
10
|
+
|
|
11
|
+
export {
|
|
12
|
+
publishDiscussionViaHttp,
|
|
13
|
+
publishDiscussionViaGh,
|
|
14
|
+
publishDiscussion,
|
|
15
|
+
publishOrUpdateDiscussion,
|
|
16
|
+
viewDiscussionViaGh,
|
|
17
|
+
viewDiscussionViaHttp,
|
|
18
|
+
postDiscussionCommentViaGh,
|
|
19
|
+
postDiscussionCommentViaHttp,
|
|
20
|
+
editDiscussionViaGh,
|
|
21
|
+
editDiscussionViaHttp,
|
|
22
|
+
findExistingDiscussionViaGh,
|
|
23
|
+
formatArchiveComment,
|
|
24
|
+
getStoredDiscussion,
|
|
25
|
+
saveStoredDiscussion,
|
|
26
|
+
clearStoredDiscussion
|
|
27
|
+
} from './social-publisher.js';
|
|
28
|
+
|
|
29
|
+
export const DISCUSSION_CATEGORY = 'npx chemx audit';
|
|
30
|
+
export const DISCUSSION_CATEGORY_SLUG = 'npx-chemx-audit';
|
|
31
|
+
export const ORG_DISCUSSIONS_URL = 'https://github.com/orgs/Chemical-X-Protocol/discussions';
|
|
32
|
+
export const DEFAULT_DISCUSSION_REPO = 'Chemical-X-Protocol/.github';
|
|
33
|
+
|
|
34
|
+
export const resolveBadgeColor = (score) => {
|
|
35
|
+
if (score >= 90) return '06b6d4';
|
|
36
|
+
if (score >= 70) return 'f59e0b';
|
|
37
|
+
return 'ef4444';
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export const resolveHotspotTierText = (lineCount) => {
|
|
41
|
+
if (lineCount >= 2000) return '🔴 **CRITICAL (>= 2,000 LOC)**';
|
|
42
|
+
if (lineCount >= 1000) return '🟠 **SEVERE (>= 1,000 LOC)**';
|
|
43
|
+
if (lineCount > 500) return '🟡 **WARNING (> 500 LOC)**';
|
|
44
|
+
return '🟢 Compliant';
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export const resolvePillarProgressionBadge = (isImproved, beforeStatus, afterStatus) => {
|
|
48
|
+
if (isImproved) return '🟢 **RESOLVED**';
|
|
49
|
+
if (beforeStatus === afterStatus) return '⚪ **UNCHANGED**';
|
|
50
|
+
return '🔴 **DEGRADED**';
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const isExtremeMonolith = (h) => h.lineCount >= 2000;
|
|
54
|
+
const isSevereMonolith = (h) => h.lineCount >= 1000 && h.lineCount < 2000;
|
|
55
|
+
const isWarningMonolith = (h) => h.lineCount >= 500 && h.lineCount < 1000;
|
|
56
|
+
|
|
57
|
+
export const formatWebUrl = (url) => {
|
|
58
|
+
if (!url || typeof url !== 'string') return '';
|
|
59
|
+
const trimmed = url.trim();
|
|
60
|
+
if (!trimmed) return '';
|
|
61
|
+
const href = /^https?:\/\//i.test(trimmed) ? trimmed : `https://${trimmed}`;
|
|
62
|
+
return `[${href}](${href})`;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export const generateDiscussionContent = (report, username, projectName = 'Codebase', repoUrl = '', liveUrl = '') => {
|
|
66
|
+
const { health, metrics, pillars, hotspots, contextAnalysis, violations } = report;
|
|
67
|
+
const isHighScoring = health.score >= 80;
|
|
68
|
+
const badgeColor = resolveBadgeColor(health.score);
|
|
69
|
+
const encodedGrade = encodeURIComponent(`${health.score}/100 (${health.grade})`);
|
|
70
|
+
|
|
71
|
+
const category = DISCUSSION_CATEGORY;
|
|
72
|
+
const categorySlug = DISCUSSION_CATEGORY_SLUG;
|
|
73
|
+
const prefix = isHighScoring ? '[Showcase]' : '[Teardown]';
|
|
74
|
+
const title = `${prefix} ${projectName} : MHI ${health.score}/100 [Grade: ${health.grade}]`;
|
|
75
|
+
|
|
76
|
+
const lines = [];
|
|
77
|
+
lines.push(`# ${isHighScoring ? 'Crystalline' : 'Architecture'} Audit Report: ${projectName}`);
|
|
78
|
+
lines.push('');
|
|
79
|
+
lines.push(`[](https://chemicalx.xophz.com)`);
|
|
80
|
+
lines.push('');
|
|
81
|
+
lines.push(`* **Audited by**: @${username}`);
|
|
82
|
+
const resolvedRepoUrl = repoUrl || (/^[\w\-.]+\/[\w\-.]+$/.test(projectName) ? `https://github.com/${projectName}` : '');
|
|
83
|
+
if (resolvedRepoUrl) {
|
|
84
|
+
lines.push(`* **Repository**: [${resolvedRepoUrl}](${resolvedRepoUrl})`);
|
|
85
|
+
}
|
|
86
|
+
if (liveUrl) {
|
|
87
|
+
const formattedWeb = formatWebUrl(liveUrl);
|
|
88
|
+
if (formattedWeb) {
|
|
89
|
+
lines.push(`* **Website**: ${formattedWeb}`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
lines.push(`* **Molecular Health Index**: **${health.score} / 100** (Grade: **${health.grade}** - ${health.label})`);
|
|
93
|
+
lines.push(`* **Source Files Analyzed**: ${metrics.scannedFiles} files (${metrics.totalLoc} total LOC)`);
|
|
94
|
+
lines.push(`* **Token Reduction Potential**: **${contextAnalysis.potentialSavingsPct}%** (Estimated ${contextAnalysis.estimatedTokens.toLocaleString()} tokens)`);
|
|
95
|
+
lines.push('');
|
|
96
|
+
lines.push('---');
|
|
97
|
+
lines.push('');
|
|
98
|
+
lines.push('## 1. 7-Pillar Architectural Matrix');
|
|
99
|
+
lines.push('');
|
|
100
|
+
lines.push('| Pillar | Status | Hazards |');
|
|
101
|
+
lines.push('| :--- | :---: | :---: |');
|
|
102
|
+
|
|
103
|
+
const resolveStatusDisplay = (status) => {
|
|
104
|
+
if (status === 'PASSED') return '🟢 **PASSED**';
|
|
105
|
+
if (status === 'WARN') return '🟡 **WARN**';
|
|
106
|
+
return '🔴 **FAILED**';
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
const resolveHazardsDisplay = (count, status) => {
|
|
110
|
+
if (count === 0) return '🟢 0';
|
|
111
|
+
if (status === 'FAILED') return `🔴 **${count}**`;
|
|
112
|
+
return `🟡 **${count}**`;
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
for (const [pillarName, data] of Object.entries(pillars)) {
|
|
116
|
+
const icon = PILLAR_EMOJIS[pillarName] || '🏛️';
|
|
117
|
+
const statusText = resolveStatusDisplay(data.status);
|
|
118
|
+
const hazardsText = resolveHazardsDisplay(data.violations, data.status);
|
|
119
|
+
lines.push(`| ${icon} ${pillarName} | ${statusText} | ${hazardsText} |`);
|
|
120
|
+
}
|
|
121
|
+
lines.push('');
|
|
122
|
+
|
|
123
|
+
const { critical, high, medium, low } = groupViolationsBySeverity(violations);
|
|
124
|
+
const extremeMonoliths = hotspots.filter(isExtremeMonolith).length;
|
|
125
|
+
const severeMonoliths = hotspots.filter(isSevereMonolith).length;
|
|
126
|
+
const warningMonoliths = hotspots.filter(isWarningMonolith).length;
|
|
127
|
+
const totalMonoliths = extremeMonoliths + severeMonoliths + warningMonoliths;
|
|
128
|
+
|
|
129
|
+
lines.push('---');
|
|
130
|
+
lines.push('');
|
|
131
|
+
lines.push('## 2. Monolith & Architectural Hazard Summary');
|
|
132
|
+
lines.push('');
|
|
133
|
+
lines.push(`* **Monolith Files (> 500 LOC)**: **${totalMonoliths} files**`);
|
|
134
|
+
if (totalMonoliths > 0) {
|
|
135
|
+
lines.push(` * Warning Tier (500 - 999 LOC): ${warningMonoliths} files`);
|
|
136
|
+
lines.push(` * Severe Tier (1,000 - 1,999 LOC): ${severeMonoliths} files`);
|
|
137
|
+
lines.push(` * Extreme Monoliths (2,000+ LOC): ${extremeMonoliths} files`);
|
|
138
|
+
}
|
|
139
|
+
lines.push(`* **Hazard Breakdown**: Critical: **${critical.length}** | High/Med: **${high.length + medium.length}** | Low: **${low.length}**`);
|
|
140
|
+
lines.push('');
|
|
141
|
+
|
|
142
|
+
lines.push('---');
|
|
143
|
+
lines.push('');
|
|
144
|
+
if (isHighScoring) {
|
|
145
|
+
lines.push('### Community Takeaway');
|
|
146
|
+
lines.push('This project follows Chemical X Molecular Architecture principles with isolated capsules and self-cleaning hooks.');
|
|
147
|
+
} else {
|
|
148
|
+
lines.push('### Seeking Refactoring Feedback');
|
|
149
|
+
lines.push('Looking for recommendations on breaking down flagged monolithic debts into crystalline molecule capsules (< 100 LOC). Any advice is welcome!');
|
|
150
|
+
}
|
|
151
|
+
lines.push('');
|
|
152
|
+
lines.push('*Audited using [Chemical X Protocol Starter Kit](https://github.com/Chemical-X-Protocol/awesome-secret-sauce).*');
|
|
153
|
+
|
|
154
|
+
return {
|
|
155
|
+
title,
|
|
156
|
+
category,
|
|
157
|
+
categorySlug,
|
|
158
|
+
body: lines.join('\n')
|
|
159
|
+
};
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
export const generateTransformationDiscussionContent = (
|
|
163
|
+
beforeSnapshot,
|
|
164
|
+
afterSnapshot,
|
|
165
|
+
username,
|
|
166
|
+
projectName = 'Codebase',
|
|
167
|
+
repoUrl = '',
|
|
168
|
+
liveUrl = ''
|
|
169
|
+
) => {
|
|
170
|
+
const scoreBefore = beforeSnapshot.health.score;
|
|
171
|
+
const scoreAfter = afterSnapshot.health.score;
|
|
172
|
+
const scoreDelta = scoreAfter - scoreBefore;
|
|
173
|
+
|
|
174
|
+
const critDelta = afterSnapshot.violations.critical - beforeSnapshot.violations.critical;
|
|
175
|
+
const totalDelta = afterSnapshot.violations.total - beforeSnapshot.violations.total;
|
|
176
|
+
const monoDelta = afterSnapshot.monoliths.total - beforeSnapshot.monoliths.total;
|
|
177
|
+
const tokensDelta = afterSnapshot.tokens.estimatedExcessTokens - beforeSnapshot.tokens.estimatedExcessTokens;
|
|
178
|
+
|
|
179
|
+
const category = DISCUSSION_CATEGORY;
|
|
180
|
+
const categorySlug = DISCUSSION_CATEGORY_SLUG;
|
|
181
|
+
const title = `[Transformation] ${projectName} : ${beforeSnapshot.health.grade} (${scoreBefore}) -> ${afterSnapshot.health.grade} (${scoreAfter})`;
|
|
182
|
+
|
|
183
|
+
const formatDelta = (val, invertGood = false) => {
|
|
184
|
+
if (val === 0) return '0 (No change)';
|
|
185
|
+
const isGood = invertGood ? val < 0 : val > 0;
|
|
186
|
+
const sign = val > 0 ? `+${val}` : `${val}`;
|
|
187
|
+
return isGood ? `🟢 **${sign}**` : `🔴 **${sign}**`;
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
const lines = [];
|
|
191
|
+
lines.push(`# 🚀 Architectural Transformation: ${projectName}`);
|
|
192
|
+
lines.push('');
|
|
193
|
+
lines.push(`[](https://chemicalx.xophz.com)`);
|
|
194
|
+
lines.push('');
|
|
195
|
+
lines.push(`* **Audited by**: @${username}`);
|
|
196
|
+
const resolvedRepoUrl = repoUrl || (/^[\w\-.]+\/[\w\-.]+$/.test(projectName) ? `https://github.com/${projectName}` : '');
|
|
197
|
+
if (resolvedRepoUrl) {
|
|
198
|
+
lines.push(`* **Repository**: [${resolvedRepoUrl}](${resolvedRepoUrl})`);
|
|
199
|
+
}
|
|
200
|
+
if (liveUrl) {
|
|
201
|
+
const formattedWeb = formatWebUrl(liveUrl);
|
|
202
|
+
if (formattedWeb) {
|
|
203
|
+
lines.push(`* **Website**: ${formattedWeb}`);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
lines.push(`* **Transformation Summary**: Upgraded codebase from **Grade ${beforeSnapshot.health.grade} (${scoreBefore}/100)** to **Grade ${afterSnapshot.health.grade} (${scoreAfter}/100)**.`);
|
|
207
|
+
lines.push('');
|
|
208
|
+
lines.push('---');
|
|
209
|
+
lines.push('');
|
|
210
|
+
lines.push('## 1. Before vs. After Metric Comparison');
|
|
211
|
+
lines.push('');
|
|
212
|
+
lines.push('| Metric | Before (Baseline) | After (Refactored) | Delta |');
|
|
213
|
+
lines.push('| :--- | :---: | :---: | :---: |');
|
|
214
|
+
lines.push(`| **Molecular Health (MHI)** | ${scoreBefore} / 100 (${beforeSnapshot.health.grade}) | ${scoreAfter} / 100 (${afterSnapshot.health.grade}) | ${formatDelta(scoreDelta)} |`);
|
|
215
|
+
lines.push(`| **Critical Hazards** | ${beforeSnapshot.violations.critical} | ${afterSnapshot.violations.critical} | ${formatDelta(critDelta, true)} |`);
|
|
216
|
+
lines.push(`| **Total Violations** | ${beforeSnapshot.violations.total} | ${afterSnapshot.violations.total} | ${formatDelta(totalDelta, true)} |`);
|
|
217
|
+
lines.push(`| **Monolith Files (>500 LOC)** | ${beforeSnapshot.monoliths.total} | ${afterSnapshot.monoliths.total} | ${formatDelta(monoDelta, true)} |`);
|
|
218
|
+
lines.push(`| **Excess Token Burn** | ${beforeSnapshot.tokens.estimatedExcessTokens.toLocaleString()} tok | ${afterSnapshot.tokens.estimatedExcessTokens.toLocaleString()} tok | ${formatDelta(tokensDelta, true)} |`);
|
|
219
|
+
lines.push('');
|
|
220
|
+
lines.push('---');
|
|
221
|
+
lines.push('');
|
|
222
|
+
lines.push('## 2. 7-Pillar Progression');
|
|
223
|
+
lines.push('');
|
|
224
|
+
lines.push('| Pillar | Before Status | After Status | Progression |');
|
|
225
|
+
lines.push('| :--- | :---: | :---: | :---: |');
|
|
226
|
+
|
|
227
|
+
const allPillars = new Set([
|
|
228
|
+
...Object.keys(beforeSnapshot.pillars || {}),
|
|
229
|
+
...Object.keys(afterSnapshot.pillars || {})
|
|
230
|
+
]);
|
|
231
|
+
|
|
232
|
+
for (const pillar of allPillars) {
|
|
233
|
+
const b = beforeSnapshot.pillars?.[pillar] || { status: 'UNKNOWN', violations: 0 };
|
|
234
|
+
const a = afterSnapshot.pillars?.[pillar] || { status: 'UNKNOWN', violations: 0 };
|
|
235
|
+
const icon = PILLAR_EMOJIS[pillar] || '🏛️';
|
|
236
|
+
const isImproved = a.violations < b.violations || (b.status !== 'PASSED' && a.status === 'PASSED');
|
|
237
|
+
const badge = resolvePillarProgressionBadge(isImproved, b.status, a.status);
|
|
238
|
+
lines.push(`| ${icon} ${pillar} | ${b.status} | ${a.status} | ${badge} |`);
|
|
239
|
+
}
|
|
240
|
+
lines.push('');
|
|
241
|
+
lines.push('---');
|
|
242
|
+
lines.push('');
|
|
243
|
+
lines.push('### Community Takeaway');
|
|
244
|
+
lines.push('Refactored using Chemical X Molecular Architecture standards. Monoliths decomposed into crystalline domain capsules (< 100 LOC) with self-cleaning hooks.');
|
|
245
|
+
lines.push('');
|
|
246
|
+
lines.push('*Transformation tracked via [Chemical X Protocol Starter Kit](https://github.com/Chemical-X-Protocol/awesome-secret-sauce).*');
|
|
247
|
+
|
|
248
|
+
return {
|
|
249
|
+
title,
|
|
250
|
+
category,
|
|
251
|
+
categorySlug,
|
|
252
|
+
body: lines.join('\n')
|
|
253
|
+
};
|
|
254
|
+
};
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
export type SeverityLevel = 'CRITICAL' | 'HIGH' | 'MEDIUM' | 'LOW';
|
|
2
|
+
export type PillarStatus = 'PASSED' | 'WARN' | 'FAILED';
|
|
3
|
+
export type RiskLevel = 'LOW' | 'MODERATE' | 'HIGH';
|
|
4
|
+
|
|
5
|
+
export interface HazardViolation {
|
|
6
|
+
readonly filePath: string;
|
|
7
|
+
readonly line: number;
|
|
8
|
+
readonly column: number;
|
|
9
|
+
readonly hazard: string;
|
|
10
|
+
readonly rule: string;
|
|
11
|
+
readonly severity: SeverityLevel;
|
|
12
|
+
readonly pillar: string;
|
|
13
|
+
readonly directive: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface FileStat {
|
|
17
|
+
readonly fullPath: string;
|
|
18
|
+
readonly relativePath: string;
|
|
19
|
+
readonly lineCount: number;
|
|
20
|
+
readonly charCount: number;
|
|
21
|
+
readonly isMolecule: boolean;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface CodebaseMetrics {
|
|
25
|
+
readonly scannedFiles: number;
|
|
26
|
+
readonly totalLoc: number;
|
|
27
|
+
readonly avgLoc: number;
|
|
28
|
+
readonly largestFile: {
|
|
29
|
+
readonly filePath: string;
|
|
30
|
+
readonly lineCount: number;
|
|
31
|
+
};
|
|
32
|
+
readonly moleculeCount: number;
|
|
33
|
+
readonly moleculeCompliantCount: number;
|
|
34
|
+
readonly moleculeCompliantPct: number;
|
|
35
|
+
readonly hookCount: number;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface MolecularHealthScore {
|
|
39
|
+
readonly score: number;
|
|
40
|
+
readonly grade: string;
|
|
41
|
+
readonly label: string;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export type QuantumHealthScore = MolecularHealthScore;
|
|
45
|
+
|
|
46
|
+
export interface PillarData {
|
|
47
|
+
readonly pillarKey: string;
|
|
48
|
+
violations: number;
|
|
49
|
+
critical: number;
|
|
50
|
+
high: number;
|
|
51
|
+
medium: number;
|
|
52
|
+
low: number;
|
|
53
|
+
status: PillarStatus;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface ContextTokenAnalysis {
|
|
57
|
+
readonly estimatedTokens: number;
|
|
58
|
+
readonly estimatedExcessTokens: number;
|
|
59
|
+
readonly potentialSavingsPct: number;
|
|
60
|
+
readonly riskLevel: RiskLevel;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface HotspotFile {
|
|
64
|
+
readonly filePath: string;
|
|
65
|
+
readonly violationCount: number;
|
|
66
|
+
readonly lineCount: number;
|
|
67
|
+
readonly isMonolith: boolean;
|
|
68
|
+
readonly monolithTier?: 'CRITICAL' | 'SEVERE' | 'WARNING' | null;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export interface AuditReport {
|
|
72
|
+
readonly scannedFiles: number;
|
|
73
|
+
readonly totalViolations: number;
|
|
74
|
+
readonly metrics: CodebaseMetrics;
|
|
75
|
+
readonly health: MolecularHealthScore;
|
|
76
|
+
readonly pillars: Record<string, PillarData>;
|
|
77
|
+
readonly contextAnalysis: ContextTokenAnalysis;
|
|
78
|
+
readonly hotspots: readonly HotspotFile[];
|
|
79
|
+
readonly violations: readonly HazardViolation[];
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export interface AuditOptions {
|
|
83
|
+
readonly dir?: string;
|
|
84
|
+
readonly json?: boolean;
|
|
85
|
+
readonly markdown?: boolean;
|
|
86
|
+
readonly outputFile?: string;
|
|
87
|
+
readonly strict?: boolean;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export interface SeverityGroupedViolations {
|
|
91
|
+
readonly critical: readonly HazardViolation[];
|
|
92
|
+
readonly high: readonly HazardViolation[];
|
|
93
|
+
readonly medium: readonly HazardViolation[];
|
|
94
|
+
readonly low: readonly HazardViolation[];
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export declare function groupViolationsBySeverity(violations: readonly HazardViolation[]): SeverityGroupedViolations;
|
|
98
|
+
export declare function auditCode(content: string, filePath: string, relativePath: string): HazardViolation[];
|
|
99
|
+
export declare function auditFile(filePath: string, relativePath: string): HazardViolation[];
|
|
100
|
+
export declare function scanDirectory(targetDir: string, baseDir: string): HazardViolation[];
|
|
101
|
+
export declare function runAudit(targetDir?: string, options?: AuditOptions): AuditReport;
|
|
102
|
+
export declare function formatTerminalReport(report: AuditReport): string;
|
|
103
|
+
export declare function generateMarkdownReport(report: AuditReport): string;
|
|
104
|
+
export declare function resolveTopSectionColor(report: AuditReport): string;
|
|
105
|
+
export declare function formatScorecardSection(report: AuditReport, themeColor?: string | null): string;
|
|
106
|
+
export declare function formatCriticalSection(report: AuditReport): string;
|
|
107
|
+
export declare function formatHighMediumSection(report: AuditReport): string;
|
|
108
|
+
export declare function formatLowSection(report: AuditReport): string;
|
|
109
|
+
export declare function formatPillarsSection(report: AuditReport, themeColor?: string | null): string;
|
|
110
|
+
export declare function formatHotspotsSection(report: AuditReport, themeColor?: string | null): string;
|
|
111
|
+
export declare function formatContextAnalysisSection(report: AuditReport, themeColor?: string | null): string;
|
|
112
|
+
export declare function formatFailuresSection(report: AuditReport): string;
|
|
113
|
+
export declare function formatPassesSection(report: AuditReport): string;
|
|
114
|
+
export declare function formatGradeFSection(report: AuditReport): string;
|
|
115
|
+
export declare function formatGradeDSection(report: AuditReport): string;
|
|
116
|
+
export declare function formatGradeCSection(report: AuditReport): string;
|
|
117
|
+
export declare function formatGradeBSection(report: AuditReport): string;
|
|
118
|
+
export declare function formatGradeASection(report: AuditReport): string;
|
|
119
|
+
export declare function getChemicalXAsciiBanner(gradeOrReport?: string | AuditReport | null): string;
|
|
120
|
+
|
|
121
|
+
export * from './social';
|
|
122
|
+
|
|
123
|
+
export declare function buildGradeFPrompt(report: AuditReport): string;
|
|
124
|
+
export declare function buildGradeDPrompt(report: AuditReport): string;
|
|
125
|
+
export declare function buildGradeCPrompt(report: AuditReport): string;
|
|
126
|
+
export declare function buildGradeBPrompt(report: AuditReport): string;
|
|
127
|
+
export declare function buildMasterPrompt(report: AuditReport): string;
|
|
128
|
+
export declare function formatPromptBox(title: string, promptText: string): string;
|
|
129
|
+
|
|
130
|
+
export * from './history';
|
|
131
|
+
|
package/cli/audit.d.ts
CHANGED
|
@@ -1,25 +1,4 @@
|
|
|
1
|
-
export
|
|
2
|
-
|
|
3
|
-
line: number;
|
|
4
|
-
hazard: string;
|
|
5
|
-
rule: string;
|
|
6
|
-
directive: string;
|
|
7
|
-
}
|
|
1
|
+
export * from './audit/types';
|
|
2
|
+
import defaultAudit from './audit/types';
|
|
8
3
|
|
|
9
|
-
export
|
|
10
|
-
scannedFiles: number;
|
|
11
|
-
totalViolations: number;
|
|
12
|
-
violations: HazardViolation[];
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export declare function auditFile(filePath: string, relativePath: string): HazardViolation[];
|
|
16
|
-
export declare function scanDirectory(targetDir: string, baseDir: string): HazardViolation[];
|
|
17
|
-
export declare function runAudit(targetDir?: string): AuditReport;
|
|
18
|
-
|
|
19
|
-
declare const _default: {
|
|
20
|
-
auditFile: typeof auditFile;
|
|
21
|
-
scanDirectory: typeof scanDirectory;
|
|
22
|
-
runAudit: typeof runAudit;
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
export default _default;
|
|
4
|
+
export default defaultAudit;
|