@nerviq/cli 1.13.0 → 1.15.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 +5 -2
- package/bin/cli.js +1067 -1008
- package/package.json +1 -1
- package/src/audit/recommendations.js +46 -0
- package/src/audit.js +127 -127
- package/src/context.js +23 -1
- package/src/harmony/cli.js +235 -0
- package/src/index.js +2 -0
- package/src/mcp-server.js +631 -314
- package/src/techniques/hygiene.js +2 -2
- package/src/techniques/quality.js +6 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nerviq/cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.15.0",
|
|
4
4
|
"description": "The intelligent nervous system for AI coding agents — 2,441 checks (8 platforms × ~300 governance rules), 10 languages, 62 domain packs. Audit, align, and amplify.",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -379,6 +379,7 @@ function buildTopNextActions(failed, limit = 5, outcomeSummaryByKey = {}, option
|
|
|
379
379
|
sourceUrl,
|
|
380
380
|
module: CATEGORY_MODULES[category] || category,
|
|
381
381
|
fix,
|
|
382
|
+
remediation_command: getRemediationCommand(key, category, options.platform),
|
|
382
383
|
priorityScore,
|
|
383
384
|
why: ACTION_RATIONALES[key] || fix,
|
|
384
385
|
risk: riskFromImpact(impact),
|
|
@@ -399,6 +400,51 @@ function buildTopNextActions(failed, limit = 5, outcomeSummaryByKey = {}, option
|
|
|
399
400
|
});
|
|
400
401
|
}
|
|
401
402
|
|
|
403
|
+
/**
|
|
404
|
+
* Map check keys/categories to a shell command an agent can run to fix the issue.
|
|
405
|
+
* Returns null when no automated fix is available.
|
|
406
|
+
*/
|
|
407
|
+
function getRemediationCommand(key, category, platform) {
|
|
408
|
+
const plat = platform || 'claude';
|
|
409
|
+
|
|
410
|
+
// Key-specific remediation commands
|
|
411
|
+
const KEY_COMMANDS = {
|
|
412
|
+
claudeMd: 'npx @nerviq/cli setup',
|
|
413
|
+
agentsMd: 'npx @nerviq/cli setup --platform codex',
|
|
414
|
+
geminiMd: 'npx @nerviq/cli setup --platform gemini',
|
|
415
|
+
copilotInstructions: 'npx @nerviq/cli setup --platform copilot',
|
|
416
|
+
cursorRules: 'npx @nerviq/cli setup --platform cursor',
|
|
417
|
+
windsurfRules: 'npx @nerviq/cli setup --platform windsurf',
|
|
418
|
+
aiderConfig: 'npx @nerviq/cli setup --platform aider',
|
|
419
|
+
opencodeConfig: 'npx @nerviq/cli setup --platform opencode',
|
|
420
|
+
settingsPermissions: 'npx @nerviq/cli plan --only permissions',
|
|
421
|
+
permissionDeny: 'npx @nerviq/cli plan --only permissions',
|
|
422
|
+
noBypassPermissions: 'npx @nerviq/cli plan --only permissions',
|
|
423
|
+
secretsProtection: 'npx @nerviq/cli plan --only permissions',
|
|
424
|
+
verificationLoop: `npx @nerviq/cli augment --platform ${plat}`,
|
|
425
|
+
lintCommand: `npx @nerviq/cli augment --platform ${plat}`,
|
|
426
|
+
testCommand: `npx @nerviq/cli augment --platform ${plat}`,
|
|
427
|
+
buildCommand: `npx @nerviq/cli augment --platform ${plat}`,
|
|
428
|
+
hookExists: 'npx @nerviq/cli plan --only hooks',
|
|
429
|
+
preCommitHook: 'npx @nerviq/cli plan --only hooks',
|
|
430
|
+
commandsExist: 'npx @nerviq/cli plan --only commands',
|
|
431
|
+
mcpServers: 'npx @nerviq/cli plan --mcp-pack context7',
|
|
432
|
+
};
|
|
433
|
+
|
|
434
|
+
if (KEY_COMMANDS[key]) return KEY_COMMANDS[key];
|
|
435
|
+
|
|
436
|
+
// Category-level fallback
|
|
437
|
+
const CATEGORY_COMMANDS = {
|
|
438
|
+
memory: `npx @nerviq/cli setup --platform ${plat}`,
|
|
439
|
+
security: `npx @nerviq/cli plan --only permissions --platform ${plat}`,
|
|
440
|
+
automation: `npx @nerviq/cli plan --only hooks --platform ${plat}`,
|
|
441
|
+
workflow: `npx @nerviq/cli plan --only commands --platform ${plat}`,
|
|
442
|
+
tools: `npx @nerviq/cli plan --mcp-pack context7 --platform ${plat}`,
|
|
443
|
+
};
|
|
444
|
+
|
|
445
|
+
return CATEGORY_COMMANDS[category] || `npx @nerviq/cli augment --platform ${plat}`;
|
|
446
|
+
}
|
|
447
|
+
|
|
402
448
|
function getNextScoreMilestone(score) {
|
|
403
449
|
return SCORE_MILESTONES.find((milestone) => score < milestone) || null;
|
|
404
450
|
}
|
package/src/audit.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Audit engine - evaluates project against NERVIQ technique database.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
const { TECHNIQUES: CLAUDE_TECHNIQUES, STACKS, STACK_CATEGORY_DETECTORS } = require('./techniques');
|
|
5
|
+
const { TECHNIQUES: CLAUDE_TECHNIQUES, STACKS, STACK_CATEGORY_DETECTORS } = require('./techniques');
|
|
6
6
|
const { ProjectContext } = require('./context');
|
|
7
7
|
const { CODEX_TECHNIQUES } = require('./codex/techniques');
|
|
8
8
|
const { detectCodexDomainPacks } = require('./codex/domain-packs');
|
|
@@ -23,28 +23,28 @@ const { AiderProjectContext } = require('./aider/context');
|
|
|
23
23
|
const { OPENCODE_TECHNIQUES } = require('./opencode/techniques');
|
|
24
24
|
const { OpenCodeProjectContext } = require('./opencode/context');
|
|
25
25
|
const { getBadgeMarkdown } = require('./badge');
|
|
26
|
-
const { sendInsights, getLocalInsights } = require('./insights');
|
|
27
|
-
const { getRecommendationOutcomeSummary } = require('./activity');
|
|
28
|
-
const { getFeedbackSummary } = require('./feedback');
|
|
29
|
-
const { formatSarif } = require('./formatters/sarif');
|
|
30
|
-
const { formatOtelMetrics } = require('./formatters/otel');
|
|
31
|
-
const { collectAuditTerminology, formatTerminologyLines } = require('./terminology');
|
|
32
|
-
const { loadPlugins, mergePluginChecks } = require('./plugins');
|
|
33
|
-
const { detectDeprecationWarnings } = require('./deprecation');
|
|
34
|
-
const { buildWorkspaceHint, formatCount, guardSkippedInstructionFiles, inspectInstructionFiles } = require('./audit/instruction-files');
|
|
35
|
-
const {
|
|
36
|
-
WEIGHTS,
|
|
37
|
-
buildScoreCoaching,
|
|
38
|
-
buildTopNextActions,
|
|
39
|
-
confidenceLabel,
|
|
40
|
-
computeCategoryScores,
|
|
41
|
-
getFpFeedbackMultiplier,
|
|
42
|
-
getQuickWins,
|
|
43
|
-
getRecommendationPriorityScore,
|
|
44
|
-
inferSuggestedNextCommand,
|
|
45
|
-
} = require('./audit/recommendations');
|
|
46
|
-
const { version: packageVersion } = require('../package.json');
|
|
47
|
-
const { t } = require('./i18n');
|
|
26
|
+
const { sendInsights, getLocalInsights } = require('./insights');
|
|
27
|
+
const { getRecommendationOutcomeSummary } = require('./activity');
|
|
28
|
+
const { getFeedbackSummary } = require('./feedback');
|
|
29
|
+
const { formatSarif } = require('./formatters/sarif');
|
|
30
|
+
const { formatOtelMetrics } = require('./formatters/otel');
|
|
31
|
+
const { collectAuditTerminology, formatTerminologyLines } = require('./terminology');
|
|
32
|
+
const { loadPlugins, mergePluginChecks } = require('./plugins');
|
|
33
|
+
const { detectDeprecationWarnings } = require('./deprecation');
|
|
34
|
+
const { buildWorkspaceHint, formatCount, guardSkippedInstructionFiles, inspectInstructionFiles } = require('./audit/instruction-files');
|
|
35
|
+
const {
|
|
36
|
+
WEIGHTS,
|
|
37
|
+
buildScoreCoaching,
|
|
38
|
+
buildTopNextActions,
|
|
39
|
+
confidenceLabel,
|
|
40
|
+
computeCategoryScores,
|
|
41
|
+
getFpFeedbackMultiplier,
|
|
42
|
+
getQuickWins,
|
|
43
|
+
getRecommendationPriorityScore,
|
|
44
|
+
inferSuggestedNextCommand,
|
|
45
|
+
} = require('./audit/recommendations');
|
|
46
|
+
const { version: packageVersion } = require('../package.json');
|
|
47
|
+
const { t } = require('./i18n');
|
|
48
48
|
|
|
49
49
|
const COLORS = {
|
|
50
50
|
reset: '\x1b[0m',
|
|
@@ -68,10 +68,10 @@ function progressBar(score, max = 100, width = 20) {
|
|
|
68
68
|
return colorize('█'.repeat(filled), color) + colorize('░'.repeat(empty), 'dim');
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
function formatLocation(file, line) {
|
|
72
|
-
if (!file) return null;
|
|
73
|
-
return line ? `${file}:${line}` : file;
|
|
74
|
-
}
|
|
71
|
+
function formatLocation(file, line) {
|
|
72
|
+
if (!file) return null;
|
|
73
|
+
return line ? `${file}:${line}` : file;
|
|
74
|
+
}
|
|
75
75
|
|
|
76
76
|
function getAuditSpec(platform = 'claude') {
|
|
77
77
|
if (platform === 'codex') {
|
|
@@ -153,7 +153,7 @@ function getAuditSpec(platform = 'claude') {
|
|
|
153
153
|
};
|
|
154
154
|
}
|
|
155
155
|
|
|
156
|
-
function getPlatformScopeNote(spec, ctx) {
|
|
156
|
+
function getPlatformScopeNote(spec, ctx) {
|
|
157
157
|
if (spec.platform !== 'codex') {
|
|
158
158
|
return null;
|
|
159
159
|
}
|
|
@@ -242,7 +242,7 @@ function printLiteAudit(result, dir) {
|
|
|
242
242
|
console.log(colorize(` Found: ${result.detectedConfigFiles.join(', ')}`, 'dim'));
|
|
243
243
|
}
|
|
244
244
|
console.log('');
|
|
245
|
-
console.log(` ${t('audit.score', { score: colorize(`${result.score}/100`, 'bold'), passed: result.passed, total: result.passed + result.failed })}`);
|
|
245
|
+
console.log(` ${t('audit.score', { score: colorize(`${result.score}/100`, 'bold'), passed: result.passed, total: result.passed + result.failed })}`);
|
|
246
246
|
|
|
247
247
|
// Score explanation line (lite mode only)
|
|
248
248
|
const _critCount = (result.results || []).filter(r => r.passed === false && r.impact === 'critical').length;
|
|
@@ -265,14 +265,14 @@ function printLiteAudit(result, dir) {
|
|
|
265
265
|
scoreExplanation = t('audit.basic', { category: weakestCategory });
|
|
266
266
|
} else {
|
|
267
267
|
scoreExplanation = t('audit.early');
|
|
268
|
-
}
|
|
269
|
-
console.log(colorize(` ${scoreExplanation}`, 'dim'));
|
|
270
|
-
if (result.scoreCoaching) {
|
|
271
|
-
console.log(colorize(` Milestone: ${result.scoreCoaching.summary}`, 'magenta'));
|
|
272
|
-
}
|
|
273
|
-
console.log(colorize(' Score type: live repo audit (current files only, not snapshot history or benchmark projection).', 'dim'));
|
|
274
|
-
|
|
275
|
-
if (result.platformScopeNote) {
|
|
268
|
+
}
|
|
269
|
+
console.log(colorize(` ${scoreExplanation}`, 'dim'));
|
|
270
|
+
if (result.scoreCoaching) {
|
|
271
|
+
console.log(colorize(` Milestone: ${result.scoreCoaching.summary}`, 'magenta'));
|
|
272
|
+
}
|
|
273
|
+
console.log(colorize(' Score type: live repo audit (current files only, not snapshot history or benchmark projection).', 'dim'));
|
|
274
|
+
|
|
275
|
+
if (result.platformScopeNote) {
|
|
276
276
|
console.log(colorize(` Scope: ${result.platformScopeNote.message}`, 'dim'));
|
|
277
277
|
}
|
|
278
278
|
if (result.workspaceHint && result.workspaceHint.workspaces.length > 0) {
|
|
@@ -284,11 +284,11 @@ function printLiteAudit(result, dir) {
|
|
|
284
284
|
console.log(colorize(` - ${item.title}: ${item.message}`, 'dim'));
|
|
285
285
|
});
|
|
286
286
|
}
|
|
287
|
-
if (result.largeInstructionFiles && result.largeInstructionFiles.length > 0) {
|
|
288
|
-
result.largeInstructionFiles.slice(0, 2).forEach((item) => {
|
|
289
|
-
console.log(colorize(` Large file: ${item.file} (~${formatCount(item.tokenCount)} tokens)`, 'yellow'));
|
|
290
|
-
});
|
|
291
|
-
}
|
|
287
|
+
if (result.largeInstructionFiles && result.largeInstructionFiles.length > 0) {
|
|
288
|
+
result.largeInstructionFiles.slice(0, 2).forEach((item) => {
|
|
289
|
+
console.log(colorize(` Large file: ${item.file} (~${formatCount(item.tokenCount)} tokens)`, 'yellow'));
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
292
|
console.log('');
|
|
293
293
|
|
|
294
294
|
if (result.failed === 0) {
|
|
@@ -320,23 +320,23 @@ function printLiteAudit(result, dir) {
|
|
|
320
320
|
console.log('');
|
|
321
321
|
let usagePatterns;
|
|
322
322
|
try { usagePatterns = require('./usage-patterns'); } catch { usagePatterns = null; }
|
|
323
|
-
result.liteSummary.topNextActions.forEach((item, index) => {
|
|
324
|
-
const tier = item.impact === 'critical' ? '🔴' : item.impact === 'high' ? '🟡' : '🔵';
|
|
325
|
-
const suppressed = usagePatterns && usagePatterns.getPriorityAdjustment(dir, item.key) === 'suppress';
|
|
326
|
-
const suffix = suppressed ? colorize(' (suppressed)', 'dim') : '';
|
|
327
|
-
console.log(` ${index + 1}. ${tier} ${colorize(item.name, 'bold')}${suffix}`);
|
|
328
|
-
console.log(colorize(` ${item.fix}`, 'dim'));
|
|
329
|
-
});
|
|
330
|
-
console.log('');
|
|
331
|
-
const liteTerminology = formatTerminologyLines(collectAuditTerminology(result));
|
|
332
|
-
if (liteTerminology.length > 0) {
|
|
333
|
-
liteTerminology.forEach((line) => {
|
|
334
|
-
const color = line.startsWith(' Terms used here:') ? 'blue' : 'dim';
|
|
335
|
-
console.log(colorize(line, color));
|
|
336
|
-
});
|
|
337
|
-
console.log('');
|
|
338
|
-
}
|
|
339
|
-
console.log(` Ready? Run: ${colorize(result.suggestedNextCommand, 'bold')}`);
|
|
323
|
+
result.liteSummary.topNextActions.forEach((item, index) => {
|
|
324
|
+
const tier = item.impact === 'critical' ? '🔴' : item.impact === 'high' ? '🟡' : '🔵';
|
|
325
|
+
const suppressed = usagePatterns && usagePatterns.getPriorityAdjustment(dir, item.key) === 'suppress';
|
|
326
|
+
const suffix = suppressed ? colorize(' (suppressed)', 'dim') : '';
|
|
327
|
+
console.log(` ${index + 1}. ${tier} ${colorize(item.name, 'bold')}${suffix}`);
|
|
328
|
+
console.log(colorize(` ${item.fix}`, 'dim'));
|
|
329
|
+
});
|
|
330
|
+
console.log('');
|
|
331
|
+
const liteTerminology = formatTerminologyLines(collectAuditTerminology(result));
|
|
332
|
+
if (liteTerminology.length > 0) {
|
|
333
|
+
liteTerminology.forEach((line) => {
|
|
334
|
+
const color = line.startsWith(' Terms used here:') ? 'blue' : 'dim';
|
|
335
|
+
console.log(colorize(line, color));
|
|
336
|
+
});
|
|
337
|
+
console.log('');
|
|
338
|
+
}
|
|
339
|
+
console.log(` Ready? Run: ${colorize(result.suggestedNextCommand, 'bold')}`);
|
|
340
340
|
if (result.platform === 'codex') {
|
|
341
341
|
console.log(colorize(' Note: Codex now supports no-write advisory flows via augment and suggest-only before setup/apply.', 'dim'));
|
|
342
342
|
}
|
|
@@ -387,7 +387,7 @@ async function audit(options) {
|
|
|
387
387
|
'supply-chain', 'api-versioning', 'caching', 'rate-limiting', 'feature-flags',
|
|
388
388
|
'docs-quality', 'monorepo', 'performance-budget', 'realtime', 'graphql',
|
|
389
389
|
'testing-strategy', 'code-quality', 'api-design', 'database', 'authentication',
|
|
390
|
-
'monitoring', 'dependency-management', 'cost-optimization',
|
|
390
|
+
'monitoring', 'dependency-management', 'cost-optimization', 'devops',
|
|
391
391
|
]);
|
|
392
392
|
const includeGenericQuality = options.verbose;
|
|
393
393
|
|
|
@@ -425,12 +425,12 @@ async function audit(options) {
|
|
|
425
425
|
key: 'largeInstructionFile',
|
|
426
426
|
id: null,
|
|
427
427
|
name: 'Large instruction file warning',
|
|
428
|
-
category: 'performance',
|
|
429
|
-
impact: 'medium',
|
|
430
|
-
rating: null,
|
|
431
|
-
fix: 'Split oversized instruction files so they stay under ~12,000 tokens, and keep any single instruction file below ~240,000 tokens.',
|
|
432
|
-
sourceUrl: null,
|
|
433
|
-
confidence: 'high',
|
|
428
|
+
category: 'performance',
|
|
429
|
+
impact: 'medium',
|
|
430
|
+
rating: null,
|
|
431
|
+
fix: 'Split oversized instruction files so they stay under ~12,000 tokens, and keep any single instruction file below ~240,000 tokens.',
|
|
432
|
+
sourceUrl: null,
|
|
433
|
+
confidence: 'high',
|
|
434
434
|
file: largeInstructionFiles[0].file,
|
|
435
435
|
line: null,
|
|
436
436
|
passed: null,
|
|
@@ -498,13 +498,13 @@ async function audit(options) {
|
|
|
498
498
|
...largeInstructionFiles.map((item) => ({
|
|
499
499
|
kind: 'large-instruction-file',
|
|
500
500
|
severity: item.severity,
|
|
501
|
-
message: item.message,
|
|
502
|
-
file: item.file,
|
|
503
|
-
lineCount: item.lineCount,
|
|
504
|
-
byteCount: item.byteCount,
|
|
505
|
-
tokenCount: item.tokenCount,
|
|
506
|
-
skipped: item.skipped,
|
|
507
|
-
})),
|
|
501
|
+
message: item.message,
|
|
502
|
+
file: item.file,
|
|
503
|
+
lineCount: item.lineCount,
|
|
504
|
+
byteCount: item.byteCount,
|
|
505
|
+
tokenCount: item.tokenCount,
|
|
506
|
+
skipped: item.skipped,
|
|
507
|
+
})),
|
|
508
508
|
...deprecationWarnings.map((item) => ({
|
|
509
509
|
kind: 'deprecated-feature',
|
|
510
510
|
severity: 'warning',
|
|
@@ -514,7 +514,7 @@ async function audit(options) {
|
|
|
514
514
|
const recommendedDomainPacks = spec.platform === 'codex'
|
|
515
515
|
? detectCodexDomainPacks(ctx, stacks, getCodexDomainPackSignals(ctx))
|
|
516
516
|
: [];
|
|
517
|
-
const result = {
|
|
517
|
+
const result = {
|
|
518
518
|
platform: spec.platform,
|
|
519
519
|
platformLabel: spec.platformLabel,
|
|
520
520
|
platformVersion: spec.platformVersion,
|
|
@@ -537,18 +537,18 @@ async function audit(options) {
|
|
|
537
537
|
deprecatedReason: r.deprecatedReason || null,
|
|
538
538
|
sunsetDate: r.sunsetDate || null,
|
|
539
539
|
})),
|
|
540
|
-
categoryScores,
|
|
541
|
-
scoreCoaching: buildScoreCoaching({
|
|
542
|
-
score,
|
|
543
|
-
earnedPoints: earnedScore,
|
|
544
|
-
maxPoints: maxScore,
|
|
545
|
-
failed,
|
|
546
|
-
outcomeSummaryByKey: outcomeSummary.byKey,
|
|
547
|
-
platform: spec.platform,
|
|
548
|
-
fpFeedbackByKey: fpFeedback.byKey,
|
|
549
|
-
}),
|
|
550
|
-
quickWins: quickWins.map(({ key, name, impact, fix, category, sourceUrl }) => ({ key, name, impact, category, fix, sourceUrl })),
|
|
551
|
-
topNextActions,
|
|
540
|
+
categoryScores,
|
|
541
|
+
scoreCoaching: buildScoreCoaching({
|
|
542
|
+
score,
|
|
543
|
+
earnedPoints: earnedScore,
|
|
544
|
+
maxPoints: maxScore,
|
|
545
|
+
failed,
|
|
546
|
+
outcomeSummaryByKey: outcomeSummary.byKey,
|
|
547
|
+
platform: spec.platform,
|
|
548
|
+
fpFeedbackByKey: fpFeedback.byKey,
|
|
549
|
+
}),
|
|
550
|
+
quickWins: quickWins.map(({ key, name, impact, fix, category, sourceUrl }) => ({ key, name, impact, category, fix, sourceUrl })),
|
|
551
|
+
topNextActions,
|
|
552
552
|
recommendationOutcomes: {
|
|
553
553
|
totalEntries: outcomeSummary.totalEntries,
|
|
554
554
|
keysTracked: outcomeSummary.keys,
|
|
@@ -578,12 +578,12 @@ async function audit(options) {
|
|
|
578
578
|
result.detectedConfigFiles = configFiles;
|
|
579
579
|
|
|
580
580
|
result.suggestedNextCommand = inferSuggestedNextCommand(result);
|
|
581
|
-
result.liteSummary = {
|
|
582
|
-
topNextActions: topNextActions.slice(0, 3),
|
|
583
|
-
nextCommand: result.suggestedNextCommand,
|
|
584
|
-
platformCaveats: platformCaveats.slice(0, 2),
|
|
585
|
-
scoreCoaching: result.scoreCoaching,
|
|
586
|
-
};
|
|
581
|
+
result.liteSummary = {
|
|
582
|
+
topNextActions: topNextActions.slice(0, 3),
|
|
583
|
+
nextCommand: result.suggestedNextCommand,
|
|
584
|
+
platformCaveats: platformCaveats.slice(0, 2),
|
|
585
|
+
scoreCoaching: result.scoreCoaching,
|
|
586
|
+
};
|
|
587
587
|
|
|
588
588
|
// Silent mode: skip all output, just return result
|
|
589
589
|
if (silent) {
|
|
@@ -642,13 +642,13 @@ async function audit(options) {
|
|
|
642
642
|
console.log('');
|
|
643
643
|
}
|
|
644
644
|
|
|
645
|
-
if (largeInstructionFiles.length > 0) {
|
|
646
|
-
console.log(colorize(' Large instruction files', 'yellow'));
|
|
647
|
-
for (const item of largeInstructionFiles) {
|
|
648
|
-
const sizeKb = Number.isFinite(item.byteCount) ? Math.round(item.byteCount / 1024) : '?';
|
|
649
|
-
console.log(colorize(` ${item.file} (~${formatCount(item.tokenCount)} tokens, ${item.lineCount || '?'} lines, ${sizeKb}KB)`, 'bold'));
|
|
650
|
-
console.log(colorize(` → ${item.message}`, 'dim'));
|
|
651
|
-
}
|
|
645
|
+
if (largeInstructionFiles.length > 0) {
|
|
646
|
+
console.log(colorize(' Large instruction files', 'yellow'));
|
|
647
|
+
for (const item of largeInstructionFiles) {
|
|
648
|
+
const sizeKb = Number.isFinite(item.byteCount) ? Math.round(item.byteCount / 1024) : '?';
|
|
649
|
+
console.log(colorize(` ${item.file} (~${formatCount(item.tokenCount)} tokens, ${item.lineCount || '?'} lines, ${sizeKb}KB)`, 'bold'));
|
|
650
|
+
console.log(colorize(` → ${item.message}`, 'dim'));
|
|
651
|
+
}
|
|
652
652
|
console.log('');
|
|
653
653
|
}
|
|
654
654
|
|
|
@@ -678,18 +678,18 @@ async function audit(options) {
|
|
|
678
678
|
console.log('');
|
|
679
679
|
|
|
680
680
|
// Score
|
|
681
|
-
console.log(` ${progressBar(score)} ${colorize(`${score}/100`, 'bold')}`);
|
|
682
|
-
if (isScaffolded && scaffoldedPassed.length > 0) {
|
|
683
|
-
console.log(colorize(` Organic: ${organicScore}/100 (without nerviq generated files)`, 'dim'));
|
|
684
|
-
}
|
|
685
|
-
if (result.scoreCoaching) {
|
|
686
|
-
const fastestPath = result.scoreCoaching.recommendedNames.slice(0, 3).join(', ');
|
|
687
|
-
console.log(colorize(` Milestone: ${result.scoreCoaching.summary}`, 'magenta'));
|
|
688
|
-
if (fastestPath) {
|
|
689
|
-
console.log(colorize(` Fastest path: ${fastestPath}`, 'dim'));
|
|
690
|
-
}
|
|
691
|
-
}
|
|
692
|
-
console.log('');
|
|
681
|
+
console.log(` ${progressBar(score)} ${colorize(`${score}/100`, 'bold')}`);
|
|
682
|
+
if (isScaffolded && scaffoldedPassed.length > 0) {
|
|
683
|
+
console.log(colorize(` Organic: ${organicScore}/100 (without nerviq generated files)`, 'dim'));
|
|
684
|
+
}
|
|
685
|
+
if (result.scoreCoaching) {
|
|
686
|
+
const fastestPath = result.scoreCoaching.recommendedNames.slice(0, 3).join(', ');
|
|
687
|
+
console.log(colorize(` Milestone: ${result.scoreCoaching.summary}`, 'magenta'));
|
|
688
|
+
if (fastestPath) {
|
|
689
|
+
console.log(colorize(` Fastest path: ${fastestPath}`, 'dim'));
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
console.log('');
|
|
693
693
|
|
|
694
694
|
// Passed
|
|
695
695
|
if (passed.length > 0) {
|
|
@@ -755,8 +755,8 @@ async function audit(options) {
|
|
|
755
755
|
}
|
|
756
756
|
|
|
757
757
|
// Top next actions
|
|
758
|
-
if (topNextActions.length > 0) {
|
|
759
|
-
console.log(colorize(' ⚡ Top 5 Next Actions', 'magenta'));
|
|
758
|
+
if (topNextActions.length > 0) {
|
|
759
|
+
console.log(colorize(' ⚡ Top 5 Next Actions', 'magenta'));
|
|
760
760
|
for (let i = 0; i < topNextActions.length; i++) {
|
|
761
761
|
const item = topNextActions[i];
|
|
762
762
|
console.log(` ${i + 1}. ${colorize(item.name, 'bold')}`);
|
|
@@ -772,20 +772,20 @@ async function audit(options) {
|
|
|
772
772
|
console.log(colorize(` Feedback: accepted ${item.feedback.accepted}, rejected ${item.feedback.rejected}, positive ${item.feedback.positive}, negative ${item.feedback.negative}${avgDelta}`, 'dim'));
|
|
773
773
|
}
|
|
774
774
|
console.log(colorize(` Fix: ${item.fix}`, 'dim'));
|
|
775
|
-
}
|
|
776
|
-
console.log('');
|
|
777
|
-
}
|
|
778
|
-
|
|
779
|
-
const terminology = formatTerminologyLines(collectAuditTerminology(result));
|
|
780
|
-
if (terminology.length > 0) {
|
|
781
|
-
terminology.forEach((line) => {
|
|
782
|
-
const color = line.startsWith(' Terms used here:') ? 'blue' : 'dim';
|
|
783
|
-
console.log(colorize(line, color));
|
|
784
|
-
});
|
|
785
|
-
console.log('');
|
|
786
|
-
}
|
|
787
|
-
|
|
788
|
-
// Summary
|
|
775
|
+
}
|
|
776
|
+
console.log('');
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
const terminology = formatTerminologyLines(collectAuditTerminology(result));
|
|
780
|
+
if (terminology.length > 0) {
|
|
781
|
+
terminology.forEach((line) => {
|
|
782
|
+
const color = line.startsWith(' Terms used here:') ? 'blue' : 'dim';
|
|
783
|
+
console.log(colorize(line, color));
|
|
784
|
+
});
|
|
785
|
+
console.log('');
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
// Summary
|
|
789
789
|
console.log(colorize(' ─────────────────────────────────────', 'dim'));
|
|
790
790
|
const deprecatedNote = deprecated.length > 0 ? colorize(`, ${deprecated.length} deprecated`, 'dim') : '';
|
|
791
791
|
console.log(` ${colorize(`${passed.length}/${applicable.length}`, 'bold')} checks passing${skipped.length > 0 ? colorize(` (${skipped.length} not applicable${deprecatedNote})`, 'dim') : (deprecatedNote ? colorize(` (${deprecatedNote})`, 'dim') : '')}`);
|
package/src/context.js
CHANGED
|
@@ -74,10 +74,32 @@ class ProjectContext {
|
|
|
74
74
|
|
|
75
75
|
/**
|
|
76
76
|
* Return the contents of the project's CLAUDE.md (root or .claude/ location).
|
|
77
|
+
* If CLAUDE.md contains only a reference to another file (e.g., "AGENTS.md"),
|
|
78
|
+
* follows that reference and returns the referenced file's content appended.
|
|
77
79
|
* @returns {string|null} File content or null if not found.
|
|
78
80
|
*/
|
|
79
81
|
claudeMdContent() {
|
|
80
|
-
|
|
82
|
+
const raw = this.fileContent('CLAUDE.md') || this.fileContent('.claude/CLAUDE.md');
|
|
83
|
+
if (!raw) return null;
|
|
84
|
+
|
|
85
|
+
// If the file is very short and looks like a file reference, follow it.
|
|
86
|
+
// Pattern: a single line that is just a filename (e.g., "AGENTS.md" or "docs/CODING.md")
|
|
87
|
+
const trimmed = raw.trim();
|
|
88
|
+
if (trimmed.length < 200 && /^[a-zA-Z0-9_./-]+\.(md|txt|rst)$/m.test(trimmed)) {
|
|
89
|
+
const lines = trimmed.split(/\r?\n/).map(l => l.trim()).filter(Boolean);
|
|
90
|
+
let combined = raw;
|
|
91
|
+
for (const line of lines) {
|
|
92
|
+
if (/^[a-zA-Z0-9_./-]+\.(md|txt|rst)$/.test(line)) {
|
|
93
|
+
const referenced = this.fileContent(line);
|
|
94
|
+
if (referenced) {
|
|
95
|
+
combined += '\n' + referenced;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return combined;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return raw;
|
|
81
103
|
}
|
|
82
104
|
|
|
83
105
|
/**
|