@nerviq/cli 1.10.0 → 1.12.0
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/README.md +176 -47
- package/bin/cli.js +842 -287
- package/package.json +2 -2
- package/src/activity.js +225 -59
- package/src/adoption-advisor.js +299 -0
- package/src/aider/freshness.js +28 -25
- package/src/aider/techniques.js +16 -11
- package/src/analyze.js +131 -1
- package/src/anti-patterns.js +17 -2
- package/src/audit.js +197 -96
- package/src/behavioral-drift.js +801 -0
- package/src/benchmark.js +15 -10
- package/src/continuous-ops.js +681 -0
- package/src/cost-tracking.js +61 -0
- package/src/cursor/techniques.js +17 -12
- package/src/deep-review.js +83 -0
- package/src/diff-only.js +280 -0
- package/src/doctor.js +118 -55
- package/src/governance.js +72 -50
- package/src/hook-validation.js +342 -0
- package/src/index.js +7 -1
- package/src/integrations.js +144 -60
- package/src/mcp-validation.js +337 -0
- package/src/opencode/techniques.js +12 -7
- package/src/operating-profile.js +574 -0
- package/src/org.js +97 -13
- package/src/permission-rules.js +218 -0
- package/src/plans.js +192 -8
- package/src/platform-change-manifest.js +86 -0
- package/src/policy-layers.js +210 -0
- package/src/profiles.js +4 -1
- package/src/prompt-injection.js +74 -0
- package/src/repo-archetype.js +386 -0
- package/src/secret-patterns.js +9 -0
- package/src/server.js +398 -3
- package/src/setup.js +36 -2
- package/src/source-urls.js +132 -132
- package/src/supplemental-checks.js +13 -12
- package/src/techniques/api.js +407 -0
- package/src/techniques/automation.js +316 -0
- package/src/techniques/compliance.js +257 -0
- package/src/techniques/hygiene.js +294 -0
- package/src/techniques/instructions.js +243 -0
- package/src/techniques/observability.js +226 -0
- package/src/techniques/optimization.js +142 -0
- package/src/techniques/quality.js +317 -0
- package/src/techniques/security.js +237 -0
- package/src/techniques/shared.js +443 -0
- package/src/techniques/stacks.js +2294 -0
- package/src/techniques/tools.js +106 -0
- package/src/techniques/workflow.js +413 -0
- package/src/techniques.js +78 -5611
- package/src/terminology.js +73 -0
- package/src/token-estimate.js +35 -0
- package/src/watch.js +18 -0
- package/src/windsurf/techniques.js +17 -12
- package/src/workspace.js +105 -8
package/src/audit.js
CHANGED
|
@@ -24,16 +24,18 @@ const { AiderProjectContext } = require('./aider/context');
|
|
|
24
24
|
const { OPENCODE_TECHNIQUES } = require('./opencode/techniques');
|
|
25
25
|
const { OpenCodeProjectContext } = require('./opencode/context');
|
|
26
26
|
const { getBadgeMarkdown } = require('./badge');
|
|
27
|
-
const { sendInsights, getLocalInsights } = require('./insights');
|
|
28
|
-
const { getRecommendationOutcomeSummary, getRecommendationAdjustment } = require('./activity');
|
|
29
|
-
const { getFeedbackSummary } = require('./feedback');
|
|
30
|
-
const { formatSarif } = require('./formatters/sarif');
|
|
31
|
-
const { formatOtelMetrics } = require('./formatters/otel');
|
|
32
|
-
const {
|
|
33
|
-
const {
|
|
34
|
-
const {
|
|
35
|
-
const {
|
|
36
|
-
const {
|
|
27
|
+
const { sendInsights, getLocalInsights } = require('./insights');
|
|
28
|
+
const { getRecommendationOutcomeSummary, getRecommendationAdjustment } = require('./activity');
|
|
29
|
+
const { getFeedbackSummary } = require('./feedback');
|
|
30
|
+
const { formatSarif } = require('./formatters/sarif');
|
|
31
|
+
const { formatOtelMetrics } = require('./formatters/otel');
|
|
32
|
+
const { collectAuditTerminology, formatTerminologyLines } = require('./terminology');
|
|
33
|
+
const { loadPlugins, mergePluginChecks } = require('./plugins');
|
|
34
|
+
const { hasWorkspaceConfig, detectWorkspaceGlobs, detectWorkspaces } = require('./workspace');
|
|
35
|
+
const { detectDeprecationWarnings } = require('./deprecation');
|
|
36
|
+
const { estimateTokenCount } = require('./token-estimate');
|
|
37
|
+
const { version: packageVersion } = require('../package.json');
|
|
38
|
+
const { t } = require('./i18n');
|
|
37
39
|
|
|
38
40
|
const COLORS = {
|
|
39
41
|
reset: '\x1b[0m',
|
|
@@ -62,10 +64,11 @@ function formatLocation(file, line) {
|
|
|
62
64
|
return line ? `${file}:${line}` : file;
|
|
63
65
|
}
|
|
64
66
|
|
|
65
|
-
const IMPACT_ORDER = { critical: 3, high: 2, medium: 1, low: 0 };
|
|
66
|
-
const WEIGHTS = { critical: 15, high: 10, medium: 5, low: 2 };
|
|
67
|
-
const
|
|
68
|
-
const
|
|
67
|
+
const IMPACT_ORDER = { critical: 3, high: 2, medium: 1, low: 0 };
|
|
68
|
+
const WEIGHTS = { critical: 15, high: 10, medium: 5, low: 2 };
|
|
69
|
+
const SCORE_MILESTONES = [50, 70, 90, 100];
|
|
70
|
+
const LARGE_INSTRUCTION_WARN_TOKENS = 12000;
|
|
71
|
+
const LARGE_INSTRUCTION_SKIP_TOKENS = 240000;
|
|
69
72
|
const CATEGORY_MODULES = {
|
|
70
73
|
memory: 'CLAUDE.md',
|
|
71
74
|
quality: 'verification',
|
|
@@ -358,9 +361,13 @@ function getAuditSpec(platform = 'claude') {
|
|
|
358
361
|
};
|
|
359
362
|
}
|
|
360
363
|
|
|
361
|
-
function normalizeRelativePath(filePath) {
|
|
362
|
-
return String(filePath || '').replace(/\\/g, '/').replace(/^\.\//, '');
|
|
363
|
-
}
|
|
364
|
+
function normalizeRelativePath(filePath) {
|
|
365
|
+
return String(filePath || '').replace(/\\/g, '/').replace(/^\.\//, '');
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
function formatCount(value) {
|
|
369
|
+
return Number(value || 0).toLocaleString('en-US');
|
|
370
|
+
}
|
|
364
371
|
|
|
365
372
|
function addPath(target, filePath) {
|
|
366
373
|
if (!filePath || typeof filePath !== 'string') return;
|
|
@@ -454,25 +461,27 @@ function instructionFileCandidates(spec, ctx) {
|
|
|
454
461
|
return [...candidates];
|
|
455
462
|
}
|
|
456
463
|
|
|
457
|
-
function inspectInstructionFiles(spec, ctx) {
|
|
458
|
-
const warnings = [];
|
|
459
|
-
|
|
460
|
-
for (const filePath of instructionFileCandidates(spec, ctx)) {
|
|
461
|
-
const
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
464
|
+
function inspectInstructionFiles(spec, ctx) {
|
|
465
|
+
const warnings = [];
|
|
466
|
+
|
|
467
|
+
for (const filePath of instructionFileCandidates(spec, ctx)) {
|
|
468
|
+
const content = typeof ctx.fileContent === 'function' ? ctx.fileContent(filePath) : null;
|
|
469
|
+
const byteCount = typeof ctx.fileSizeBytes === 'function' ? ctx.fileSizeBytes(filePath) : null;
|
|
470
|
+
const tokenCount = typeof content === 'string' ? estimateTokenCount(content) : null;
|
|
471
|
+
if (!Number.isFinite(tokenCount) || tokenCount <= LARGE_INSTRUCTION_WARN_TOKENS) continue;
|
|
472
|
+
|
|
473
|
+
warnings.push({
|
|
474
|
+
file: normalizeRelativePath(filePath),
|
|
475
|
+
byteCount,
|
|
476
|
+
tokenCount,
|
|
477
|
+
lineCount: typeof content === 'string' ? content.split(/\r?\n/).length : null,
|
|
478
|
+
skipped: tokenCount > LARGE_INSTRUCTION_SKIP_TOKENS,
|
|
479
|
+
severity: tokenCount > LARGE_INSTRUCTION_SKIP_TOKENS ? 'critical' : 'warning',
|
|
480
|
+
message: tokenCount > LARGE_INSTRUCTION_SKIP_TOKENS
|
|
481
|
+
? 'Instruction file exceeds ~240,000 tokens and will be skipped during audit.'
|
|
482
|
+
: 'Instruction file exceeds ~12,000 tokens. Audit will continue, but this file may reduce runtime clarity.',
|
|
483
|
+
});
|
|
484
|
+
}
|
|
476
485
|
|
|
477
486
|
return warnings;
|
|
478
487
|
}
|
|
@@ -651,7 +660,7 @@ function getRecommendationPriorityScore(item, outcomeSummaryByKey = {}, fpFeedba
|
|
|
651
660
|
return raw * getFpFeedbackMultiplier(fpFeedbackByKey, item.key);
|
|
652
661
|
}
|
|
653
662
|
|
|
654
|
-
function buildTopNextActions(failed, limit = 5, outcomeSummaryByKey = {}, options = {}) {
|
|
663
|
+
function buildTopNextActions(failed, limit = 5, outcomeSummaryByKey = {}, options = {}) {
|
|
655
664
|
const pool = getPrioritizedFailed(failed);
|
|
656
665
|
const fpByKey = options.fpFeedbackByKey || null;
|
|
657
666
|
|
|
@@ -716,11 +725,65 @@ function buildTopNextActions(failed, limit = 5, outcomeSummaryByKey = {}, option
|
|
|
716
725
|
avgScoreDelta: feedback.avgScoreDelta,
|
|
717
726
|
} : null,
|
|
718
727
|
});
|
|
719
|
-
});
|
|
720
|
-
}
|
|
721
|
-
|
|
722
|
-
function
|
|
723
|
-
|
|
728
|
+
});
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
function getNextScoreMilestone(score) {
|
|
732
|
+
return SCORE_MILESTONES.find((milestone) => score < milestone) || null;
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
function buildScoreCoaching({ score, earnedPoints, maxPoints, failed, outcomeSummaryByKey = {}, platform, fpFeedbackByKey = null }) {
|
|
736
|
+
if (!Array.isArray(failed) || failed.length === 0 || !Number.isFinite(maxPoints) || maxPoints <= 0) {
|
|
737
|
+
return null;
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
const nextMilestone = getNextScoreMilestone(score);
|
|
741
|
+
if (!nextMilestone) return null;
|
|
742
|
+
|
|
743
|
+
const targetEarnedPoints = Math.ceil((nextMilestone / 100) * maxPoints);
|
|
744
|
+
const pointsNeeded = Math.max(0, targetEarnedPoints - earnedPoints);
|
|
745
|
+
if (pointsNeeded <= 0) return null;
|
|
746
|
+
|
|
747
|
+
const rankedActions = buildTopNextActions(failed, failed.length, outcomeSummaryByKey, { platform, fpFeedbackByKey });
|
|
748
|
+
if (rankedActions.length === 0) return null;
|
|
749
|
+
|
|
750
|
+
const failedByKey = new Map(failed.map((item) => [item.key, item]));
|
|
751
|
+
const selected = [];
|
|
752
|
+
let recoveredPoints = 0;
|
|
753
|
+
|
|
754
|
+
for (const action of rankedActions) {
|
|
755
|
+
const source = failedByKey.get(action.key);
|
|
756
|
+
if (!source) continue;
|
|
757
|
+
selected.push({
|
|
758
|
+
key: source.key,
|
|
759
|
+
name: source.name,
|
|
760
|
+
impact: source.impact,
|
|
761
|
+
weight: WEIGHTS[source.impact] || 0,
|
|
762
|
+
});
|
|
763
|
+
recoveredPoints += WEIGHTS[source.impact] || 0;
|
|
764
|
+
if (recoveredPoints >= pointsNeeded) break;
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
if (selected.length === 0) return null;
|
|
768
|
+
|
|
769
|
+
const fixesNeeded = selected.length;
|
|
770
|
+
const projectedScore = Math.round(((earnedPoints + recoveredPoints) / maxPoints) * 100);
|
|
771
|
+
const summary = `You're ${fixesNeeded} ${fixesNeeded === 1 ? 'fix' : 'fixes'} away from ${nextMilestone}/100.`;
|
|
772
|
+
|
|
773
|
+
return {
|
|
774
|
+
currentScore: score,
|
|
775
|
+
nextMilestone,
|
|
776
|
+
pointsNeeded,
|
|
777
|
+
fixesNeeded,
|
|
778
|
+
projectedScore: Math.min(100, projectedScore),
|
|
779
|
+
summary,
|
|
780
|
+
recommendedKeys: selected.map((item) => item.key),
|
|
781
|
+
recommendedNames: selected.map((item) => item.name),
|
|
782
|
+
};
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
function computeCategoryScores(applicable, passed) {
|
|
786
|
+
const grouped = {};
|
|
724
787
|
|
|
725
788
|
for (const item of applicable) {
|
|
726
789
|
const category = item.category || 'unknown';
|
|
@@ -907,6 +970,9 @@ function printLiteAudit(result, dir) {
|
|
|
907
970
|
scoreExplanation = t('audit.early');
|
|
908
971
|
}
|
|
909
972
|
console.log(colorize(` ${scoreExplanation}`, 'dim'));
|
|
973
|
+
if (result.scoreCoaching) {
|
|
974
|
+
console.log(colorize(` Milestone: ${result.scoreCoaching.summary}`, 'magenta'));
|
|
975
|
+
}
|
|
910
976
|
console.log(colorize(' Score type: live repo audit (current files only, not snapshot history or benchmark projection).', 'dim'));
|
|
911
977
|
|
|
912
978
|
if (result.platformScopeNote) {
|
|
@@ -921,11 +987,11 @@ function printLiteAudit(result, dir) {
|
|
|
921
987
|
console.log(colorize(` - ${item.title}: ${item.message}`, 'dim'));
|
|
922
988
|
});
|
|
923
989
|
}
|
|
924
|
-
if (result.largeInstructionFiles && result.largeInstructionFiles.length > 0) {
|
|
925
|
-
result.largeInstructionFiles.slice(0, 2).forEach((item) => {
|
|
926
|
-
console.log(colorize(` Large file: ${item.file} (
|
|
927
|
-
});
|
|
928
|
-
}
|
|
990
|
+
if (result.largeInstructionFiles && result.largeInstructionFiles.length > 0) {
|
|
991
|
+
result.largeInstructionFiles.slice(0, 2).forEach((item) => {
|
|
992
|
+
console.log(colorize(` Large file: ${item.file} (~${formatCount(item.tokenCount)} tokens)`, 'yellow'));
|
|
993
|
+
});
|
|
994
|
+
}
|
|
929
995
|
console.log('');
|
|
930
996
|
|
|
931
997
|
if (result.failed === 0) {
|
|
@@ -957,15 +1023,23 @@ function printLiteAudit(result, dir) {
|
|
|
957
1023
|
console.log('');
|
|
958
1024
|
let usagePatterns;
|
|
959
1025
|
try { usagePatterns = require('./usage-patterns'); } catch { usagePatterns = null; }
|
|
960
|
-
result.liteSummary.topNextActions.forEach((item, index) => {
|
|
961
|
-
const tier = item.impact === 'critical' ? '🔴' : item.impact === 'high' ? '🟡' : '🔵';
|
|
962
|
-
const suppressed = usagePatterns && usagePatterns.getPriorityAdjustment(dir, item.key) === 'suppress';
|
|
963
|
-
const suffix = suppressed ? colorize(' (suppressed)', 'dim') : '';
|
|
964
|
-
console.log(` ${index + 1}. ${tier} ${colorize(item.name, 'bold')}${suffix}`);
|
|
965
|
-
console.log(colorize(` ${item.fix}`, 'dim'));
|
|
966
|
-
});
|
|
967
|
-
console.log('');
|
|
968
|
-
|
|
1026
|
+
result.liteSummary.topNextActions.forEach((item, index) => {
|
|
1027
|
+
const tier = item.impact === 'critical' ? '🔴' : item.impact === 'high' ? '🟡' : '🔵';
|
|
1028
|
+
const suppressed = usagePatterns && usagePatterns.getPriorityAdjustment(dir, item.key) === 'suppress';
|
|
1029
|
+
const suffix = suppressed ? colorize(' (suppressed)', 'dim') : '';
|
|
1030
|
+
console.log(` ${index + 1}. ${tier} ${colorize(item.name, 'bold')}${suffix}`);
|
|
1031
|
+
console.log(colorize(` ${item.fix}`, 'dim'));
|
|
1032
|
+
});
|
|
1033
|
+
console.log('');
|
|
1034
|
+
const liteTerminology = formatTerminologyLines(collectAuditTerminology(result));
|
|
1035
|
+
if (liteTerminology.length > 0) {
|
|
1036
|
+
liteTerminology.forEach((line) => {
|
|
1037
|
+
const color = line.startsWith(' Terms used here:') ? 'blue' : 'dim';
|
|
1038
|
+
console.log(colorize(line, color));
|
|
1039
|
+
});
|
|
1040
|
+
console.log('');
|
|
1041
|
+
}
|
|
1042
|
+
console.log(` Ready? Run: ${colorize(result.suggestedNextCommand, 'bold')}`);
|
|
969
1043
|
if (result.platform === 'codex') {
|
|
970
1044
|
console.log(colorize(' Note: Codex now supports no-write advisory flows via augment and suggest-only before setup/apply.', 'dim'));
|
|
971
1045
|
}
|
|
@@ -1054,12 +1128,12 @@ async function audit(options) {
|
|
|
1054
1128
|
key: 'largeInstructionFile',
|
|
1055
1129
|
id: null,
|
|
1056
1130
|
name: 'Large instruction file warning',
|
|
1057
|
-
category: 'performance',
|
|
1058
|
-
impact: 'medium',
|
|
1059
|
-
rating: null,
|
|
1060
|
-
fix: 'Split oversized instruction files so they stay under
|
|
1061
|
-
sourceUrl: null,
|
|
1062
|
-
confidence: 'high',
|
|
1131
|
+
category: 'performance',
|
|
1132
|
+
impact: 'medium',
|
|
1133
|
+
rating: null,
|
|
1134
|
+
fix: 'Split oversized instruction files so they stay under ~12,000 tokens, and keep any single instruction file below ~240,000 tokens.',
|
|
1135
|
+
sourceUrl: null,
|
|
1136
|
+
confidence: 'high',
|
|
1063
1137
|
file: largeInstructionFiles[0].file,
|
|
1064
1138
|
line: null,
|
|
1065
1139
|
passed: null,
|
|
@@ -1127,12 +1201,13 @@ async function audit(options) {
|
|
|
1127
1201
|
...largeInstructionFiles.map((item) => ({
|
|
1128
1202
|
kind: 'large-instruction-file',
|
|
1129
1203
|
severity: item.severity,
|
|
1130
|
-
message: item.message,
|
|
1131
|
-
file: item.file,
|
|
1132
|
-
lineCount: item.lineCount,
|
|
1133
|
-
byteCount: item.byteCount,
|
|
1134
|
-
|
|
1135
|
-
|
|
1204
|
+
message: item.message,
|
|
1205
|
+
file: item.file,
|
|
1206
|
+
lineCount: item.lineCount,
|
|
1207
|
+
byteCount: item.byteCount,
|
|
1208
|
+
tokenCount: item.tokenCount,
|
|
1209
|
+
skipped: item.skipped,
|
|
1210
|
+
})),
|
|
1136
1211
|
...deprecationWarnings.map((item) => ({
|
|
1137
1212
|
kind: 'deprecated-feature',
|
|
1138
1213
|
severity: 'warning',
|
|
@@ -1142,7 +1217,7 @@ async function audit(options) {
|
|
|
1142
1217
|
const recommendedDomainPacks = spec.platform === 'codex'
|
|
1143
1218
|
? detectCodexDomainPacks(ctx, stacks, getCodexDomainPackSignals(ctx))
|
|
1144
1219
|
: [];
|
|
1145
|
-
const result = {
|
|
1220
|
+
const result = {
|
|
1146
1221
|
platform: spec.platform,
|
|
1147
1222
|
platformLabel: spec.platformLabel,
|
|
1148
1223
|
platformVersion: spec.platformVersion,
|
|
@@ -1165,9 +1240,18 @@ async function audit(options) {
|
|
|
1165
1240
|
deprecatedReason: r.deprecatedReason || null,
|
|
1166
1241
|
sunsetDate: r.sunsetDate || null,
|
|
1167
1242
|
})),
|
|
1168
|
-
categoryScores,
|
|
1169
|
-
|
|
1170
|
-
|
|
1243
|
+
categoryScores,
|
|
1244
|
+
scoreCoaching: buildScoreCoaching({
|
|
1245
|
+
score,
|
|
1246
|
+
earnedPoints: earnedScore,
|
|
1247
|
+
maxPoints: maxScore,
|
|
1248
|
+
failed,
|
|
1249
|
+
outcomeSummaryByKey: outcomeSummary.byKey,
|
|
1250
|
+
platform: spec.platform,
|
|
1251
|
+
fpFeedbackByKey: fpFeedback.byKey,
|
|
1252
|
+
}),
|
|
1253
|
+
quickWins: quickWins.map(({ key, name, impact, fix, category, sourceUrl }) => ({ key, name, impact, category, fix, sourceUrl })),
|
|
1254
|
+
topNextActions,
|
|
1171
1255
|
recommendationOutcomes: {
|
|
1172
1256
|
totalEntries: outcomeSummary.totalEntries,
|
|
1173
1257
|
keysTracked: outcomeSummary.keys,
|
|
@@ -1197,11 +1281,12 @@ async function audit(options) {
|
|
|
1197
1281
|
result.detectedConfigFiles = configFiles;
|
|
1198
1282
|
|
|
1199
1283
|
result.suggestedNextCommand = inferSuggestedNextCommand(result);
|
|
1200
|
-
result.liteSummary = {
|
|
1201
|
-
topNextActions: topNextActions.slice(0, 3),
|
|
1202
|
-
nextCommand: result.suggestedNextCommand,
|
|
1203
|
-
platformCaveats: platformCaveats.slice(0, 2),
|
|
1204
|
-
|
|
1284
|
+
result.liteSummary = {
|
|
1285
|
+
topNextActions: topNextActions.slice(0, 3),
|
|
1286
|
+
nextCommand: result.suggestedNextCommand,
|
|
1287
|
+
platformCaveats: platformCaveats.slice(0, 2),
|
|
1288
|
+
scoreCoaching: result.scoreCoaching,
|
|
1289
|
+
};
|
|
1205
1290
|
|
|
1206
1291
|
// Silent mode: skip all output, just return result
|
|
1207
1292
|
if (silent) {
|
|
@@ -1260,13 +1345,13 @@ async function audit(options) {
|
|
|
1260
1345
|
console.log('');
|
|
1261
1346
|
}
|
|
1262
1347
|
|
|
1263
|
-
if (largeInstructionFiles.length > 0) {
|
|
1264
|
-
console.log(colorize(' Large instruction files', 'yellow'));
|
|
1265
|
-
for (const item of largeInstructionFiles) {
|
|
1266
|
-
const sizeKb = Math.round(item.byteCount / 1024);
|
|
1267
|
-
console.log(colorize(` ${item.file} (
|
|
1268
|
-
console.log(colorize(` → ${item.message}`, 'dim'));
|
|
1269
|
-
}
|
|
1348
|
+
if (largeInstructionFiles.length > 0) {
|
|
1349
|
+
console.log(colorize(' Large instruction files', 'yellow'));
|
|
1350
|
+
for (const item of largeInstructionFiles) {
|
|
1351
|
+
const sizeKb = Number.isFinite(item.byteCount) ? Math.round(item.byteCount / 1024) : '?';
|
|
1352
|
+
console.log(colorize(` ${item.file} (~${formatCount(item.tokenCount)} tokens, ${item.lineCount || '?'} lines, ${sizeKb}KB)`, 'bold'));
|
|
1353
|
+
console.log(colorize(` → ${item.message}`, 'dim'));
|
|
1354
|
+
}
|
|
1270
1355
|
console.log('');
|
|
1271
1356
|
}
|
|
1272
1357
|
|
|
@@ -1296,11 +1381,18 @@ async function audit(options) {
|
|
|
1296
1381
|
console.log('');
|
|
1297
1382
|
|
|
1298
1383
|
// Score
|
|
1299
|
-
console.log(` ${progressBar(score)} ${colorize(`${score}/100`, 'bold')}`);
|
|
1300
|
-
if (isScaffolded && scaffoldedPassed.length > 0) {
|
|
1301
|
-
console.log(colorize(` Organic: ${organicScore}/100 (without nerviq generated files)`, 'dim'));
|
|
1302
|
-
}
|
|
1303
|
-
|
|
1384
|
+
console.log(` ${progressBar(score)} ${colorize(`${score}/100`, 'bold')}`);
|
|
1385
|
+
if (isScaffolded && scaffoldedPassed.length > 0) {
|
|
1386
|
+
console.log(colorize(` Organic: ${organicScore}/100 (without nerviq generated files)`, 'dim'));
|
|
1387
|
+
}
|
|
1388
|
+
if (result.scoreCoaching) {
|
|
1389
|
+
const fastestPath = result.scoreCoaching.recommendedNames.slice(0, 3).join(', ');
|
|
1390
|
+
console.log(colorize(` Milestone: ${result.scoreCoaching.summary}`, 'magenta'));
|
|
1391
|
+
if (fastestPath) {
|
|
1392
|
+
console.log(colorize(` Fastest path: ${fastestPath}`, 'dim'));
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1395
|
+
console.log('');
|
|
1304
1396
|
|
|
1305
1397
|
// Passed
|
|
1306
1398
|
if (passed.length > 0) {
|
|
@@ -1366,8 +1458,8 @@ async function audit(options) {
|
|
|
1366
1458
|
}
|
|
1367
1459
|
|
|
1368
1460
|
// Top next actions
|
|
1369
|
-
if (topNextActions.length > 0) {
|
|
1370
|
-
console.log(colorize(' ⚡ Top 5 Next Actions', 'magenta'));
|
|
1461
|
+
if (topNextActions.length > 0) {
|
|
1462
|
+
console.log(colorize(' ⚡ Top 5 Next Actions', 'magenta'));
|
|
1371
1463
|
for (let i = 0; i < topNextActions.length; i++) {
|
|
1372
1464
|
const item = topNextActions[i];
|
|
1373
1465
|
console.log(` ${i + 1}. ${colorize(item.name, 'bold')}`);
|
|
@@ -1383,11 +1475,20 @@ async function audit(options) {
|
|
|
1383
1475
|
console.log(colorize(` Feedback: accepted ${item.feedback.accepted}, rejected ${item.feedback.rejected}, positive ${item.feedback.positive}, negative ${item.feedback.negative}${avgDelta}`, 'dim'));
|
|
1384
1476
|
}
|
|
1385
1477
|
console.log(colorize(` Fix: ${item.fix}`, 'dim'));
|
|
1386
|
-
}
|
|
1387
|
-
console.log('');
|
|
1388
|
-
}
|
|
1389
|
-
|
|
1390
|
-
|
|
1478
|
+
}
|
|
1479
|
+
console.log('');
|
|
1480
|
+
}
|
|
1481
|
+
|
|
1482
|
+
const terminology = formatTerminologyLines(collectAuditTerminology(result));
|
|
1483
|
+
if (terminology.length > 0) {
|
|
1484
|
+
terminology.forEach((line) => {
|
|
1485
|
+
const color = line.startsWith(' Terms used here:') ? 'blue' : 'dim';
|
|
1486
|
+
console.log(colorize(line, color));
|
|
1487
|
+
});
|
|
1488
|
+
console.log('');
|
|
1489
|
+
}
|
|
1490
|
+
|
|
1491
|
+
// Summary
|
|
1391
1492
|
console.log(colorize(' ─────────────────────────────────────', 'dim'));
|
|
1392
1493
|
const deprecatedNote = deprecated.length > 0 ? colorize(`, ${deprecated.length} deprecated`, 'dim') : '';
|
|
1393
1494
|
console.log(` ${colorize(`${passed.length}/${applicable.length}`, 'bold')} checks passing${skipped.length > 0 ? colorize(` (${skipped.length} not applicable${deprecatedNote})`, 'dim') : (deprecatedNote ? colorize(` (${deprecatedNote})`, 'dim') : '')}`);
|