@nerviq/cli 1.11.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 +97 -19
- package/bin/cli.js +618 -182
- package/package.json +2 -2
- package/src/activity.js +49 -9
- package/src/adoption-advisor.js +299 -0
- package/src/aider/techniques.js +16 -11
- package/src/analyze.js +128 -0
- package/src/anti-patterns.js +13 -0
- package/src/audit.js +97 -22
- package/src/behavioral-drift.js +801 -0
- 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 +59 -43
- package/src/hook-validation.js +342 -0
- package/src/index.js +5 -0
- package/src/integrations.js +42 -5
- 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/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/setup.js +34 -0
- 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 -5607
- package/src/watch.js +18 -0
- package/src/windsurf/techniques.js +17 -12
package/src/analyze.js
CHANGED
|
@@ -12,6 +12,9 @@ const { detectDomainPacks } = require('./domain-packs');
|
|
|
12
12
|
const { detectCodexDomainPacks } = require('./codex/domain-packs');
|
|
13
13
|
const { recommendMcpPacks } = require('./mcp-packs');
|
|
14
14
|
const { collectClaudeDenyRules } = require('./permission-rules');
|
|
15
|
+
const { buildRepoArchetypeProfile } = require('./repo-archetype');
|
|
16
|
+
const { buildOperatingProfile } = require('./operating-profile');
|
|
17
|
+
const { buildAdoptionAdvisor } = require('./adoption-advisor');
|
|
15
18
|
|
|
16
19
|
const COLORS = {
|
|
17
20
|
reset: '\x1b[0m',
|
|
@@ -498,6 +501,30 @@ async function analyzeProject(options) {
|
|
|
498
501
|
? detectCodexDomainPacks(ctx, stacks, assets)
|
|
499
502
|
: detectDomainPacks(ctx, stacks, assets);
|
|
500
503
|
const recommendedMcpPacks = platform === 'claude' ? recommendMcpPacks(stacks, recommendedDomainPacks, { ctx, assets }) : [];
|
|
504
|
+
const repoArchetype = buildRepoArchetypeProfile({
|
|
505
|
+
ctx,
|
|
506
|
+
platform,
|
|
507
|
+
stacks,
|
|
508
|
+
assets,
|
|
509
|
+
recommendedDomainPacks,
|
|
510
|
+
recommendedMcpPacks,
|
|
511
|
+
maturity,
|
|
512
|
+
});
|
|
513
|
+
const recommendedOperatingProfile = buildOperatingProfile({
|
|
514
|
+
dir: options.dir,
|
|
515
|
+
platform,
|
|
516
|
+
repoArchetype,
|
|
517
|
+
recommendedDomainPacks,
|
|
518
|
+
recommendedMcpPacks,
|
|
519
|
+
});
|
|
520
|
+
const adoptionGuidance = buildAdoptionAdvisor({
|
|
521
|
+
platform,
|
|
522
|
+
repoArchetype,
|
|
523
|
+
recommendedOperatingProfile,
|
|
524
|
+
recommendedDomainPacks,
|
|
525
|
+
recommendedMcpPacks,
|
|
526
|
+
env: options.env || {},
|
|
527
|
+
});
|
|
501
528
|
|
|
502
529
|
const report = {
|
|
503
530
|
platform,
|
|
@@ -511,16 +538,28 @@ async function analyzeProject(options) {
|
|
|
511
538
|
stacks: stacks.map(s => s.label),
|
|
512
539
|
domains: recommendedDomainPacks.map(pack => pack.label),
|
|
513
540
|
maturity,
|
|
541
|
+
archetype: repoArchetype.label,
|
|
542
|
+
workflow: repoArchetype.primaryWorkflow.label,
|
|
543
|
+
riskLevel: repoArchetype.riskProfile.label,
|
|
544
|
+
operatingProfile: recommendedOperatingProfile.label,
|
|
545
|
+
adoptionPlan: adoptionGuidance.summary.label,
|
|
514
546
|
score: auditResult.score,
|
|
515
547
|
organicScore: auditResult.organicScore,
|
|
516
548
|
checkCount: auditResult.checkCount,
|
|
517
549
|
},
|
|
518
550
|
platformScopeNote: auditResult.platformScopeNote || null,
|
|
519
551
|
platformCaveats: auditResult.platformCaveats || [],
|
|
552
|
+
repoArchetype,
|
|
553
|
+
recommendedOperatingProfile,
|
|
554
|
+
adoptionGuidance,
|
|
520
555
|
detectedArchitecture: {
|
|
521
556
|
repoType: stacks.length > 0 ? 'stack-detected repo' : 'generic repo',
|
|
522
557
|
mainDirectories: mainDirs,
|
|
523
558
|
stackSignals: stacks.map(s => s.key),
|
|
559
|
+
stackFamily: repoArchetype.stackFamily.label,
|
|
560
|
+
topology: repoArchetype.topology.label,
|
|
561
|
+
workflow: repoArchetype.primaryWorkflow.label,
|
|
562
|
+
riskLevel: repoArchetype.riskProfile.label,
|
|
524
563
|
},
|
|
525
564
|
existingPlatformAssets: assets,
|
|
526
565
|
strengthsPreserved: toStrengths(auditResult.results),
|
|
@@ -585,11 +624,14 @@ function printAnalysis(report, options = {}) {
|
|
|
585
624
|
} else {
|
|
586
625
|
console.log(c(` Platform: ${report.platformLabel}`, 'dim'));
|
|
587
626
|
}
|
|
627
|
+
console.log(c(` Archetype: ${report.repoArchetype.label} | Workflow: ${report.repoArchetype.primaryWorkflow.label} | Risk: ${report.repoArchetype.riskProfile.label}`, 'dim'));
|
|
588
628
|
console.log(c(` Maturity: ${report.projectSummary.maturity} | Score: ${report.projectSummary.score}/100 | Organic: ${report.projectSummary.organicScore}/100`, 'dim'));
|
|
589
629
|
console.log('');
|
|
590
630
|
|
|
591
631
|
console.log(c(' Detected Architecture', 'blue'));
|
|
632
|
+
console.log(c(` Stack family: ${report.repoArchetype.stackFamily.label} | Topology: ${report.repoArchetype.topology.label} | Confidence: ${report.repoArchetype.confidence}`, 'dim'));
|
|
592
633
|
console.log(c(` Main directories: ${report.detectedArchitecture.mainDirectories.join(', ') || 'No strong structure detected yet'}`, 'dim'));
|
|
634
|
+
console.log(c(` Signals: ${report.repoArchetype.signals.join(' | ') || 'No strong archetype signals yet'}`, 'dim'));
|
|
593
635
|
console.log('');
|
|
594
636
|
|
|
595
637
|
console.log(c(` Existing ${report.existingPlatformAssets.label} Assets`, 'blue'));
|
|
@@ -683,6 +725,36 @@ function printAnalysis(report, options = {}) {
|
|
|
683
725
|
console.log('');
|
|
684
726
|
}
|
|
685
727
|
|
|
728
|
+
if (report.recommendedOperatingProfile) {
|
|
729
|
+
console.log(c(' Recommended Operating Profile', 'blue'));
|
|
730
|
+
console.log(` ${report.recommendedOperatingProfile.label}`);
|
|
731
|
+
console.log(c(` Permission: ${report.recommendedOperatingProfile.permissionProfile.label} | Governance pack: ${report.recommendedOperatingProfile.governancePack.label}`, 'dim'));
|
|
732
|
+
console.log(c(` CI shape: ${report.recommendedOperatingProfile.ciShape.label} | Platforms: ${(report.recommendedOperatingProfile.platformSupport.recommended || []).join(', ') || report.platformLabel}`, 'dim'));
|
|
733
|
+
console.log(c(` Hooks: ${report.recommendedOperatingProfile.hooks.map((hook) => hook.key).join(', ')}`, 'dim'));
|
|
734
|
+
console.log(c(` Verification: ${report.recommendedOperatingProfile.verification.required.join(', ')}`, 'dim'));
|
|
735
|
+
console.log('');
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
if (report.adoptionGuidance && Array.isArray(report.adoptionGuidance.items) && report.adoptionGuidance.items.length > 0) {
|
|
739
|
+
console.log(c(' Adopt / Defer / Ignore', 'blue'));
|
|
740
|
+
console.log(c(` ${report.adoptionGuidance.summary.label}`, 'dim'));
|
|
741
|
+
const groups = [
|
|
742
|
+
['adopt', 'Adopt now'],
|
|
743
|
+
['defer', 'Defer until prerequisites are ready'],
|
|
744
|
+
['ignore', 'Ignore for this repo shape'],
|
|
745
|
+
];
|
|
746
|
+
for (const [decision, label] of groups) {
|
|
747
|
+
const items = report.adoptionGuidance.items.filter((item) => item.decision === decision).slice(0, 3);
|
|
748
|
+
if (items.length === 0) continue;
|
|
749
|
+
console.log(` ${label}`);
|
|
750
|
+
for (const item of items) {
|
|
751
|
+
console.log(` - ${item.label}`);
|
|
752
|
+
console.log(c(` ${item.why}`, 'dim'));
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
console.log('');
|
|
756
|
+
}
|
|
757
|
+
|
|
686
758
|
if (report.suggestedRolloutOrder.length > 0) {
|
|
687
759
|
console.log(c(' Suggested Rollout Order', 'blue'));
|
|
688
760
|
report.suggestedRolloutOrder.forEach((item, index) => {
|
|
@@ -710,6 +782,11 @@ function exportMarkdown(report) {
|
|
|
710
782
|
if (report.platform === 'claude') {
|
|
711
783
|
lines.push(`**Domain Packs:** ${report.projectSummary.domains.join(', ') || 'Baseline General'}`);
|
|
712
784
|
}
|
|
785
|
+
lines.push(`**Archetype:** ${report.repoArchetype.label}`);
|
|
786
|
+
lines.push(`**Workflow:** ${report.repoArchetype.primaryWorkflow.label}`);
|
|
787
|
+
lines.push(`**Risk posture:** ${report.repoArchetype.riskProfile.label}`);
|
|
788
|
+
lines.push(`**Operating profile:** ${report.recommendedOperatingProfile.label}`);
|
|
789
|
+
lines.push(`**Adoption plan:** ${report.adoptionGuidance.summary.label}`);
|
|
713
790
|
lines.push(`**Maturity:** ${report.projectSummary.maturity}`);
|
|
714
791
|
lines.push('');
|
|
715
792
|
|
|
@@ -730,6 +807,57 @@ function exportMarkdown(report) {
|
|
|
730
807
|
}
|
|
731
808
|
lines.push('');
|
|
732
809
|
|
|
810
|
+
lines.push('## Repo Archetype');
|
|
811
|
+
lines.push('');
|
|
812
|
+
lines.push(`- **Label:** ${report.repoArchetype.label}`);
|
|
813
|
+
lines.push(`- **Summary:** ${report.repoArchetype.summary}`);
|
|
814
|
+
lines.push(`- **Stack family:** ${report.repoArchetype.stackFamily.label}`);
|
|
815
|
+
lines.push(`- **Topology:** ${report.repoArchetype.topology.label}`);
|
|
816
|
+
lines.push(`- **Primary workflow:** ${report.repoArchetype.primaryWorkflow.label}`);
|
|
817
|
+
lines.push(`- **Risk posture:** ${report.repoArchetype.riskProfile.label}`);
|
|
818
|
+
lines.push(`- **Confidence:** ${report.repoArchetype.confidence}`);
|
|
819
|
+
if (report.repoArchetype.signals.length > 0) {
|
|
820
|
+
lines.push(`- **Signals:** ${report.repoArchetype.signals.join(', ')}`);
|
|
821
|
+
}
|
|
822
|
+
lines.push('');
|
|
823
|
+
|
|
824
|
+
lines.push('## Recommended Operating Profile');
|
|
825
|
+
lines.push('');
|
|
826
|
+
lines.push(`- **Label:** ${report.recommendedOperatingProfile.label}`);
|
|
827
|
+
lines.push(`- **Summary:** ${report.recommendedOperatingProfile.summary}`);
|
|
828
|
+
lines.push(`- **Permission profile:** ${report.recommendedOperatingProfile.permissionProfile.label}`);
|
|
829
|
+
lines.push(`- **Governance pack:** ${report.recommendedOperatingProfile.governancePack.label}`);
|
|
830
|
+
lines.push(`- **Platform support:** ${(report.recommendedOperatingProfile.platformSupport.recommended || []).join(', ') || report.platformLabel}`);
|
|
831
|
+
if (report.recommendedOperatingProfile.platformSupport.optionalExpansion) {
|
|
832
|
+
lines.push(`- **Optional expansion:** ${report.recommendedOperatingProfile.platformSupport.optionalExpansion}`);
|
|
833
|
+
}
|
|
834
|
+
lines.push(`- **CI shape:** ${report.recommendedOperatingProfile.ciShape.label}`);
|
|
835
|
+
lines.push(`- **Verification:** ${report.recommendedOperatingProfile.verification.required.join(', ')}`);
|
|
836
|
+
lines.push(`- **Hooks:** ${report.recommendedOperatingProfile.hooks.map((hook) => hook.key).join(', ')}`);
|
|
837
|
+
lines.push('');
|
|
838
|
+
|
|
839
|
+
lines.push('## Adopt / Defer / Ignore');
|
|
840
|
+
lines.push('');
|
|
841
|
+
const decisionGroups = [
|
|
842
|
+
['adopt', 'Adopt now'],
|
|
843
|
+
['defer', 'Defer'],
|
|
844
|
+
['ignore', 'Ignore'],
|
|
845
|
+
];
|
|
846
|
+
for (const [decision, label] of decisionGroups) {
|
|
847
|
+
const items = report.adoptionGuidance.items.filter((item) => item.decision === decision);
|
|
848
|
+
if (items.length === 0) continue;
|
|
849
|
+
lines.push(`### ${label}`);
|
|
850
|
+
lines.push('');
|
|
851
|
+
for (const item of items) {
|
|
852
|
+
lines.push(`- **${item.label}** — ${item.why}`);
|
|
853
|
+
lines.push(` Evidence: ${item.evidence.join(' | ')}`);
|
|
854
|
+
lines.push(` Prerequisites: ${item.prerequisites.length > 0 ? item.prerequisites.join(' | ') : 'None'}`);
|
|
855
|
+
lines.push(` Expected benefit: ${item.expectedBenefit}`);
|
|
856
|
+
lines.push(` Rollback safety: ${item.rollbackSafety}`);
|
|
857
|
+
}
|
|
858
|
+
lines.push('');
|
|
859
|
+
}
|
|
860
|
+
|
|
733
861
|
if (report.strengthsPreserved.length > 0) {
|
|
734
862
|
lines.push('## Strengths Preserved (don\'t change these)');
|
|
735
863
|
lines.push('');
|
package/src/anti-patterns.js
CHANGED
|
@@ -10,6 +10,7 @@ const {
|
|
|
10
10
|
} = require('./instruction-surfaces');
|
|
11
11
|
const { collectClaudeDenyRules } = require('./permission-rules');
|
|
12
12
|
const { containsEmbeddedSecret } = require('./secret-patterns');
|
|
13
|
+
const { containsPromptInjectionPattern } = require('./prompt-injection');
|
|
13
14
|
|
|
14
15
|
const ANTI_PATTERNS = [
|
|
15
16
|
{
|
|
@@ -218,6 +219,18 @@ const ANTI_PATTERNS = [
|
|
|
218
219
|
return !hasTestInMd && !hasTestScript;
|
|
219
220
|
},
|
|
220
221
|
},
|
|
222
|
+
{
|
|
223
|
+
id: 'AP023',
|
|
224
|
+
name: 'Suspicious prompt-injection phrases in repo instructions',
|
|
225
|
+
severity: 'high',
|
|
226
|
+
description: 'Instruction surfaces that say things like "ignore previous instructions", "bypass guardrails", or "score 100/100" create confusion and downstream trust problems, even when the static audit itself is not LLM-driven.',
|
|
227
|
+
platforms: ['claude', 'codex', 'cursor', 'windsurf', 'copilot', 'gemini', 'aider', 'opencode'],
|
|
228
|
+
fix: 'Remove adversarial phrases from repo instructions and replace them with an explicit trust-boundary note about treating repo/web/MCP content as untrusted data.',
|
|
229
|
+
detect: (ctx) => {
|
|
230
|
+
const content = getRepoInstructionBundle(ctx);
|
|
231
|
+
return containsPromptInjectionPattern(content);
|
|
232
|
+
},
|
|
233
|
+
},
|
|
221
234
|
{
|
|
222
235
|
id: 'AP015',
|
|
223
236
|
name: 'All permissions allowed',
|
package/src/audit.js
CHANGED
|
@@ -64,8 +64,9 @@ function formatLocation(file, line) {
|
|
|
64
64
|
return line ? `${file}:${line}` : file;
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
const IMPACT_ORDER = { critical: 3, high: 2, medium: 1, low: 0 };
|
|
68
|
-
const WEIGHTS = { critical: 15, high: 10, medium: 5, low: 2 };
|
|
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];
|
|
69
70
|
const LARGE_INSTRUCTION_WARN_TOKENS = 12000;
|
|
70
71
|
const LARGE_INSTRUCTION_SKIP_TOKENS = 240000;
|
|
71
72
|
const CATEGORY_MODULES = {
|
|
@@ -659,7 +660,7 @@ function getRecommendationPriorityScore(item, outcomeSummaryByKey = {}, fpFeedba
|
|
|
659
660
|
return raw * getFpFeedbackMultiplier(fpFeedbackByKey, item.key);
|
|
660
661
|
}
|
|
661
662
|
|
|
662
|
-
function buildTopNextActions(failed, limit = 5, outcomeSummaryByKey = {}, options = {}) {
|
|
663
|
+
function buildTopNextActions(failed, limit = 5, outcomeSummaryByKey = {}, options = {}) {
|
|
663
664
|
const pool = getPrioritizedFailed(failed);
|
|
664
665
|
const fpByKey = options.fpFeedbackByKey || null;
|
|
665
666
|
|
|
@@ -724,11 +725,65 @@ function buildTopNextActions(failed, limit = 5, outcomeSummaryByKey = {}, option
|
|
|
724
725
|
avgScoreDelta: feedback.avgScoreDelta,
|
|
725
726
|
} : null,
|
|
726
727
|
});
|
|
727
|
-
});
|
|
728
|
-
}
|
|
729
|
-
|
|
730
|
-
function
|
|
731
|
-
|
|
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 = {};
|
|
732
787
|
|
|
733
788
|
for (const item of applicable) {
|
|
734
789
|
const category = item.category || 'unknown';
|
|
@@ -915,6 +970,9 @@ function printLiteAudit(result, dir) {
|
|
|
915
970
|
scoreExplanation = t('audit.early');
|
|
916
971
|
}
|
|
917
972
|
console.log(colorize(` ${scoreExplanation}`, 'dim'));
|
|
973
|
+
if (result.scoreCoaching) {
|
|
974
|
+
console.log(colorize(` Milestone: ${result.scoreCoaching.summary}`, 'magenta'));
|
|
975
|
+
}
|
|
918
976
|
console.log(colorize(' Score type: live repo audit (current files only, not snapshot history or benchmark projection).', 'dim'));
|
|
919
977
|
|
|
920
978
|
if (result.platformScopeNote) {
|
|
@@ -1159,7 +1217,7 @@ async function audit(options) {
|
|
|
1159
1217
|
const recommendedDomainPacks = spec.platform === 'codex'
|
|
1160
1218
|
? detectCodexDomainPacks(ctx, stacks, getCodexDomainPackSignals(ctx))
|
|
1161
1219
|
: [];
|
|
1162
|
-
const result = {
|
|
1220
|
+
const result = {
|
|
1163
1221
|
platform: spec.platform,
|
|
1164
1222
|
platformLabel: spec.platformLabel,
|
|
1165
1223
|
platformVersion: spec.platformVersion,
|
|
@@ -1182,9 +1240,18 @@ async function audit(options) {
|
|
|
1182
1240
|
deprecatedReason: r.deprecatedReason || null,
|
|
1183
1241
|
sunsetDate: r.sunsetDate || null,
|
|
1184
1242
|
})),
|
|
1185
|
-
categoryScores,
|
|
1186
|
-
|
|
1187
|
-
|
|
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,
|
|
1188
1255
|
recommendationOutcomes: {
|
|
1189
1256
|
totalEntries: outcomeSummary.totalEntries,
|
|
1190
1257
|
keysTracked: outcomeSummary.keys,
|
|
@@ -1214,11 +1281,12 @@ async function audit(options) {
|
|
|
1214
1281
|
result.detectedConfigFiles = configFiles;
|
|
1215
1282
|
|
|
1216
1283
|
result.suggestedNextCommand = inferSuggestedNextCommand(result);
|
|
1217
|
-
result.liteSummary = {
|
|
1218
|
-
topNextActions: topNextActions.slice(0, 3),
|
|
1219
|
-
nextCommand: result.suggestedNextCommand,
|
|
1220
|
-
platformCaveats: platformCaveats.slice(0, 2),
|
|
1221
|
-
|
|
1284
|
+
result.liteSummary = {
|
|
1285
|
+
topNextActions: topNextActions.slice(0, 3),
|
|
1286
|
+
nextCommand: result.suggestedNextCommand,
|
|
1287
|
+
platformCaveats: platformCaveats.slice(0, 2),
|
|
1288
|
+
scoreCoaching: result.scoreCoaching,
|
|
1289
|
+
};
|
|
1222
1290
|
|
|
1223
1291
|
// Silent mode: skip all output, just return result
|
|
1224
1292
|
if (silent) {
|
|
@@ -1313,11 +1381,18 @@ async function audit(options) {
|
|
|
1313
1381
|
console.log('');
|
|
1314
1382
|
|
|
1315
1383
|
// Score
|
|
1316
|
-
console.log(` ${progressBar(score)} ${colorize(`${score}/100`, 'bold')}`);
|
|
1317
|
-
if (isScaffolded && scaffoldedPassed.length > 0) {
|
|
1318
|
-
console.log(colorize(` Organic: ${organicScore}/100 (without nerviq generated files)`, 'dim'));
|
|
1319
|
-
}
|
|
1320
|
-
|
|
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('');
|
|
1321
1396
|
|
|
1322
1397
|
// Passed
|
|
1323
1398
|
if (passed.length > 0) {
|