erne-universal 0.13.3 → 0.13.4
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/dashboard/server.js +136 -61
- package/package.json +1 -1
package/dashboard/server.js
CHANGED
|
@@ -991,93 +991,168 @@ const server = http.createServer(async (req, res) => {
|
|
|
991
991
|
try {
|
|
992
992
|
const projectDir = process.env.ERNE_PROJECT_DIR || process.cwd();
|
|
993
993
|
const erneRoot = path.resolve(__dirname, '..');
|
|
994
|
+
|
|
995
|
+
// Step 1: Run quick audit for score + findings
|
|
994
996
|
const { runAudit } = require(path.join(erneRoot, 'lib', 'audit'));
|
|
995
997
|
const result = runAudit(projectDir);
|
|
996
998
|
|
|
997
|
-
//
|
|
999
|
+
// Step 2: Run deep scan to produce audit-data.json (drives doc generation)
|
|
1000
|
+
const docsDir = path.join(projectDir, 'erne-docs');
|
|
1001
|
+
fs.mkdirSync(docsDir, { recursive: true });
|
|
1002
|
+
try {
|
|
1003
|
+
const { runScan } = require(path.join(erneRoot, 'lib', 'audit-scanner'));
|
|
1004
|
+
const auditData = runScan(projectDir, { skipDepHealth: false, maxFiles: 500 });
|
|
1005
|
+
fs.writeFileSync(path.join(docsDir, 'audit-data.json'), JSON.stringify(auditData, null, 2));
|
|
1006
|
+
} catch { /* scanner may fail on some projects */ }
|
|
1007
|
+
|
|
1008
|
+
// Step 3: Generate all 12 markdown docs from audit-data.json
|
|
998
1009
|
try {
|
|
999
|
-
const
|
|
1000
|
-
fs.
|
|
1001
|
-
const data = JSON.parse(fs.readFileSync(path.join(docsDir, 'audit-data.json'), 'utf-8'));
|
|
1010
|
+
const dataPath = path.join(docsDir, 'audit-data.json');
|
|
1011
|
+
const data = fs.existsSync(dataPath) ? JSON.parse(fs.readFileSync(dataPath, 'utf-8')) : {};
|
|
1002
1012
|
const now = new Date().toISOString();
|
|
1003
1013
|
const write = (name, lines) => fs.writeFileSync(path.join(docsDir, name), lines.join('\n'));
|
|
1004
1014
|
|
|
1005
|
-
// audit-report
|
|
1015
|
+
// audit-report (always generate)
|
|
1006
1016
|
const ar = [`# Audit Report\nGenerated: ${now}\n`];
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
);
|
|
1017
|
+
ar.push(`Score: ${result.jsonReport?.score ?? data.meta?.score ?? 'N/A'}`);
|
|
1018
|
+
ar.push(`Components: ${data.components?.length ?? 0}`);
|
|
1019
|
+
ar.push(`Hooks: ${data.hooks?.length ?? 0}`);
|
|
1020
|
+
if (result.jsonReport?.strengths?.length) {
|
|
1021
|
+
ar.push(`\n## Strengths (${result.jsonReport.strengths.length})\n`);
|
|
1022
|
+
for (const s of result.jsonReport.strengths) ar.push(`- ${s.title}`);
|
|
1023
|
+
}
|
|
1024
|
+
if (result.jsonReport?.findings?.length) {
|
|
1025
|
+
ar.push(`\n## Findings (${result.jsonReport.findings.length})\n`);
|
|
1026
|
+
for (const f of result.jsonReport.findings) ar.push(`- [${f.severity}] ${f.title}: ${f.detail || ''}`);
|
|
1027
|
+
}
|
|
1013
1028
|
write('audit-report.md', ar);
|
|
1014
1029
|
|
|
1015
|
-
// stack-detection
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1030
|
+
// stack-detection (always generate)
|
|
1031
|
+
const sd = [`# Stack Detection\nGenerated: ${now}\n`];
|
|
1032
|
+
const stack = data.config || result.jsonReport?.stack || {};
|
|
1033
|
+
for (const [k, v] of Object.entries(stack))
|
|
1034
|
+
sd.push(`- **${k}**: ${typeof v === 'object' ? JSON.stringify(v) : v}`);
|
|
1035
|
+
if (result.jsonReport?.meta) {
|
|
1036
|
+
sd.push('');
|
|
1037
|
+
for (const [k, v] of Object.entries(result.jsonReport.meta))
|
|
1038
|
+
sd.push(`- **${k}**: ${v}`);
|
|
1021
1039
|
}
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
dr.push(`- ${o.name}: ${o.current} → ${o.latest}`);
|
|
1032
|
-
}
|
|
1033
|
-
write('dependency-report.md', dr);
|
|
1040
|
+
write('stack-detection.md', sd);
|
|
1041
|
+
|
|
1042
|
+
// dependency-report (always generate)
|
|
1043
|
+
const dr = [`# Dependency Report\nGenerated: ${now}\n`];
|
|
1044
|
+
const deps = data.dependencies || {};
|
|
1045
|
+
dr.push(`Total: ${deps.production?.length ?? 0} production, ${deps.dev?.length ?? 0} dev\n`);
|
|
1046
|
+
if (deps.production?.length) {
|
|
1047
|
+
dr.push('## Production Dependencies\n');
|
|
1048
|
+
for (const d of deps.production.slice(0, 50)) dr.push(`- ${d.name || d}: ${d.version || ''}`);
|
|
1034
1049
|
}
|
|
1050
|
+
if (deps.outdated?.length) {
|
|
1051
|
+
dr.push(`\n## Outdated (${deps.outdated.length})\n`);
|
|
1052
|
+
for (const o of deps.outdated.slice(0, 30)) dr.push(`- ${o.name}: ${o.current} → ${o.latest}`);
|
|
1053
|
+
}
|
|
1054
|
+
write('dependency-report.md', dr);
|
|
1035
1055
|
|
|
1036
|
-
// dead-code
|
|
1056
|
+
// dead-code (always generate)
|
|
1057
|
+
const dc = [`# Dead Code Report\nGenerated: ${now}\n`];
|
|
1037
1058
|
if (data.deadCode?.length) {
|
|
1038
|
-
|
|
1039
|
-
for (const d of data.deadCode.slice(0, 50))
|
|
1040
|
-
|
|
1041
|
-
|
|
1059
|
+
dc.push(`Found: ${data.deadCode.length} unused exports\n`);
|
|
1060
|
+
for (const d of data.deadCode.slice(0, 50)) dc.push(`- \`${d.name}\` in ${d.file} (${d.type})`);
|
|
1061
|
+
} else {
|
|
1062
|
+
dc.push('No dead code detected.');
|
|
1042
1063
|
}
|
|
1064
|
+
write('dead-code.md', dc);
|
|
1043
1065
|
|
|
1044
|
-
// todos
|
|
1066
|
+
// todos (always generate)
|
|
1067
|
+
const td = [`# TODOs & Tech Debt\nGenerated: ${now}\n`];
|
|
1045
1068
|
if (data.techDebt?.length) {
|
|
1046
|
-
const td = [`# TODOs & Tech Debt\nGenerated: ${now}\n`];
|
|
1047
1069
|
for (const t of data.techDebt.slice(0, 50))
|
|
1048
1070
|
td.push(`- **${t.type || 'TODO'}** ${t.file}:${t.line || '?'} — ${t.text || ''}`);
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
// changelog
|
|
1053
|
-
if (data.gitHistory?.length) {
|
|
1054
|
-
const cl = [`# Changelog\nGenerated: ${now}\n`];
|
|
1055
|
-
for (const c of data.gitHistory.slice(0, 30))
|
|
1056
|
-
cl.push(`- ${(c.hash || '').slice(0, 7)} ${c.message || ''} (${c.date || ''})`);
|
|
1057
|
-
write('changelog.md', cl);
|
|
1071
|
+
} else {
|
|
1072
|
+
td.push('No TODOs or tech debt markers found.');
|
|
1058
1073
|
}
|
|
1074
|
+
write('todos.md', td);
|
|
1059
1075
|
|
|
1060
|
-
// type-coverage
|
|
1061
|
-
|
|
1062
|
-
|
|
1076
|
+
// type-coverage (always generate)
|
|
1077
|
+
const tc = [`# Type Coverage\nGenerated: ${now}\n`];
|
|
1078
|
+
if (data.typeSafety && Object.keys(data.typeSafety).length) {
|
|
1063
1079
|
for (const [k, v] of Object.entries(data.typeSafety))
|
|
1064
1080
|
tc.push(`- **${k}**: ${typeof v === 'object' ? JSON.stringify(v) : v}`);
|
|
1065
|
-
|
|
1081
|
+
} else {
|
|
1082
|
+
tc.push('No TypeScript coverage data available.');
|
|
1066
1083
|
}
|
|
1084
|
+
write('type-coverage.md', tc);
|
|
1067
1085
|
|
|
1068
|
-
//
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
}
|
|
1076
|
-
write('architecture.md', arch);
|
|
1086
|
+
// test-coverage (always generate)
|
|
1087
|
+
const testc = [`# Test Coverage\nGenerated: ${now}\n`];
|
|
1088
|
+
if (data.testCoverage) {
|
|
1089
|
+
for (const [k, v] of Object.entries(data.testCoverage))
|
|
1090
|
+
testc.push(`- **${k}**: ${typeof v === 'object' ? JSON.stringify(v) : v}`);
|
|
1091
|
+
} else {
|
|
1092
|
+
testc.push('No test coverage data. Configure a testing framework to enable coverage reports.');
|
|
1077
1093
|
}
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1094
|
+
write('test-coverage.md', testc);
|
|
1095
|
+
|
|
1096
|
+
// security-report (always generate)
|
|
1097
|
+
const sr = [`# Security Report\nGenerated: ${now}\n`];
|
|
1098
|
+
const secFindings = (result.jsonReport?.findings || []).filter(f => f.category === 'Security');
|
|
1099
|
+
if (secFindings.length) {
|
|
1100
|
+
for (const f of secFindings) sr.push(`- [${f.severity}] ${f.title}: ${f.detail || ''}`);
|
|
1101
|
+
} else {
|
|
1102
|
+
sr.push('No security issues detected.');
|
|
1103
|
+
}
|
|
1104
|
+
write('security-report.md', sr);
|
|
1105
|
+
|
|
1106
|
+
// performance-report (always generate)
|
|
1107
|
+
const pr = [`# Performance Report\nGenerated: ${now}\n`];
|
|
1108
|
+
const perfFindings = (result.jsonReport?.findings || []).filter(f => f.category === 'Performance');
|
|
1109
|
+
if (perfFindings.length) {
|
|
1110
|
+
for (const f of perfFindings) pr.push(`- [${f.severity}] ${f.title}: ${f.detail || ''}`);
|
|
1111
|
+
} else {
|
|
1112
|
+
pr.push('No performance issues detected.');
|
|
1113
|
+
}
|
|
1114
|
+
write('performance-report.md', pr);
|
|
1115
|
+
|
|
1116
|
+
// architecture (always generate)
|
|
1117
|
+
const arch = [`# Architecture\nGenerated: ${now}\n`];
|
|
1118
|
+
if (data.structure?.dirs) {
|
|
1119
|
+
arch.push(`## Directories (${data.structure.dirs.length})\n`);
|
|
1120
|
+
for (const d of data.structure.dirs.slice(0, 30)) arch.push(`- ${d}`);
|
|
1121
|
+
}
|
|
1122
|
+
if (data.routes?.length) {
|
|
1123
|
+
arch.push(`\n## Routes (${data.routes.length})\n`);
|
|
1124
|
+
for (const r of data.routes.slice(0, 30)) arch.push(`- ${r.path || r.file || r}`);
|
|
1125
|
+
}
|
|
1126
|
+
if (data.screens?.length) {
|
|
1127
|
+
arch.push(`\n## Screens (${data.screens.length})\n`);
|
|
1128
|
+
for (const s of data.screens.slice(0, 30)) arch.push(`- ${s.name || s.file || s}`);
|
|
1129
|
+
}
|
|
1130
|
+
if (!data.structure && !data.routes && !data.screens) {
|
|
1131
|
+
arch.push('Run a deeper audit scan for architecture analysis.');
|
|
1132
|
+
}
|
|
1133
|
+
write('architecture.md', arch);
|
|
1134
|
+
|
|
1135
|
+
// api-surface (always generate)
|
|
1136
|
+
const api = [`# API Surface\nGenerated: ${now}\n`];
|
|
1137
|
+
if (data.apiLayer?.length) {
|
|
1138
|
+
for (const a of data.apiLayer.slice(0, 50))
|
|
1139
|
+
api.push(`- \`${a.method || 'GET'} ${a.path || a.url || a.name}\` in ${a.file || 'unknown'}`);
|
|
1140
|
+
} else {
|
|
1141
|
+
api.push('No API endpoints detected.');
|
|
1142
|
+
}
|
|
1143
|
+
write('api-surface.md', api);
|
|
1144
|
+
|
|
1145
|
+
// changelog (always generate)
|
|
1146
|
+
const cl = [`# Changelog\nGenerated: ${now}\n`];
|
|
1147
|
+
if (data.gitHistory?.length) {
|
|
1148
|
+
for (const c of data.gitHistory.slice(0, 50))
|
|
1149
|
+
cl.push(`- ${(c.hash || '').slice(0, 7)} ${c.message || ''} (${c.date || ''})`);
|
|
1150
|
+
} else {
|
|
1151
|
+
cl.push('No git history data available.');
|
|
1152
|
+
}
|
|
1153
|
+
write('changelog.md', cl);
|
|
1154
|
+
|
|
1155
|
+
} catch { /* doc generation is best-effort */ }
|
|
1081
1156
|
|
|
1082
1157
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
1083
1158
|
res.end(JSON.stringify(result.jsonReport));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "erne-universal",
|
|
3
|
-
"version": "0.13.
|
|
3
|
+
"version": "0.13.4",
|
|
4
4
|
"description": "Complete AI coding agent harness for React Native and Expo \u2014 13 specialized agents, autonomous worker mode, visual debugging, smart routing",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react-native",
|