Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
b36a642
feat: Add support for RdJson output formats.
ayo6706 Nov 26, 2025
c2bc531
feat: Add core evaluation orchestrator, CLI commands, schemas, and Gi…
ayo6706 Dec 1, 2025
bd10dcb
Create vectorlint.ini
ayo6706 Dec 1, 2025
23de209
Clean up github action integration with vectorlint
ayo6706 Dec 1, 2025
d7d9300
Merge branch 'main' into ft/reviewdog
ayo6706 Dec 1, 2025
1fed801
update key to anthropic
ayo6706 Dec 2, 2025
f2f780e
chore: pass LLM_PROVIDER to vectorlint-action
ayo6706 Dec 2, 2025
cfcf6a8
fix: prioritize CLI --prompts option over config file
ayo6706 Dec 2, 2025
fa24290
update vectorlint.ini
ayo6706 Dec 2, 2025
8b9d99e
Update env to include openai
ayo6706 Dec 2, 2025
61dbc41
Update vectorlint action to include perplexity
ayo6706 Dec 2, 2025
85c2c97
Add openai model and temperature to vectorlint action
ayo6706 Dec 2, 2025
aaa0547
Create test bad article to test vectorlint with reviewdog
ayo6706 Dec 2, 2025
15ef073
Update command for output file
ayo6706 Dec 2, 2025
a60eea7
Update json formatter to support rdjson
ayo6706 Dec 2, 2025
fdf0fd5
Eslint clean up
ayo6706 Dec 2, 2025
8f15e86
Implement rdjson formatter and clean up integrations
ayo6706 Dec 3, 2025
951067c
Merge branch 'main' into ft/reviewdog
ayo6706 Dec 3, 2025
fa746a7
fix micromatch ESM import
ayo6706 Dec 3, 2025
d585fce
Merge branch 'main' into ft/reviewdog
ayo6706 Dec 9, 2025
31f742e
feat: add support for 'rdjson' output format and formatter in evaluat…
ayo6706 Dec 9, 2025
2f90553
eslint fix
ayo6706 Dec 9, 2025
aaf0fe7
update vectorlint action
ayo6706 Dec 9, 2025
cdf1ea5
feat: implement silent mode for machine-readable output formats
ayo6706 Dec 9, 2025
0d49ab6
Update vectorlint.ini to new version
ayo6706 Dec 9, 2025
ac48b8c
Revert to old implementation
ayo6706 Dec 9, 2025
12dd4cc
refactor: remove output file option and update types for consistency
ayo6706 Dec 9, 2025
fa18b23
fix: change logging from console.log to console.error for directive l…
ayo6706 Dec 9, 2025
cf0acf6
fix: change logging from console.log to console.error to resolve std …
ayo6706 Dec 9, 2025
6131be0
Revert llm provider logs and remove verbose parameter in vectorlint.yml
ayo6706 Dec 9, 2025
c3b9149
eslint fix
ayo6706 Dec 9, 2025
c9c39c4
test: add RDJSON formatter tests for valid output
ayo6706 Dec 9, 2025
912333f
fix: update vectorlint.yml to remove evals path and add missing param…
ayo6706 Dec 9, 2025
f52a160
fix: update README.md for reviewdog integration
ayo6706 Dec 9, 2025
2f33f4b
Update readme file
ayo6706 Dec 9, 2025
d056593
Delete vectorlint github action workflow
ayo6706 Dec 10, 2025
2ca1d35
eslint fix
ayo6706 Dec 10, 2025
f968394
Delete vectorlint github action docs
ayo6706 Dec 10, 2025
3dd672f
Delete test bad article
ayo6706 Dec 10, 2025
39fa6fb
Refactor output format handling to use enum and update schemas accord…
ayo6706 Dec 10, 2025
95fe6d6
Update JsonFormatter to retrieve version from package.json and fix se…
ayo6706 Dec 10, 2025
969be4d
Refactor output format handling to use enum values in reportIssue and…
ayo6706 Dec 10, 2025
e827f5b
Rename eval to rule
ayo6706 Dec 11, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/cli/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export function registerMainCommand(program: Command): void {
.option('--show-prompt', 'Print full prompt and injected content')
.option('--show-prompt-trunc', 'Print truncated prompt/content previews (500 chars)')
.option('--debug-json', 'Print full JSON response from the API')
.option('--output <format>', 'Output format: line (default), json, or vale-json', 'line')
.option('--output <format>', 'Output format: line (default), json, or vale-json, rdjson', 'line')
.option('--config <path>', 'Path to custom vectorlint.ini config file')
.argument('[paths...]', 'files or directories to check (optional)')
.action(async (paths: string[] = []) => {
Expand Down
68 changes: 46 additions & 22 deletions src/cli/orchestrator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@ import type { PromptFile } from '../prompts/prompt-loader';
import { ScanPathResolver } from '../boundaries/scan-path-resolver';
import { ValeJsonFormatter, type JsonIssue } from '../output/vale-json-formatter';
import { JsonFormatter, type Issue, type ScoreComponent } from '../output/json-formatter';
import { RdJsonFormatter } from '../output/rdjson-formatter';
import { printFileHeader, printIssueRow, printEvaluationSummaries, type EvaluationSummary } from '../output/reporter';
import { locateEvidenceWithMatch } from '../output/location';
import { checkTarget } from '../prompts/target';
import { isSubjectiveResult } from '../prompts/schema';
import { handleUnknownError, MissingDependencyError } from '../errors/index';
import { createEvaluator } from '../evaluators/index';
import { Type, Severity } from '../evaluators/types';
import { OutputFormat } from './types';
import type {
EvaluationOptions, EvaluationResult, ErrorTrackingResult,
ReportIssueParams, ExtractMatchTextParams, LocationMatch, ProcessViolationsParams,
Expand Down Expand Up @@ -56,10 +58,10 @@ async function runWithConcurrency<T, R>(
function reportIssue(params: ReportIssueParams): void {
const { file, line, column, severity, summary, ruleName, outputFormat, jsonFormatter, suggestion, scoreText, match } = params;

if (outputFormat === 'line') {
if (outputFormat === OutputFormat.Line) {
const locStr = `${line}:${column}`;
printIssueRow(locStr, severity, summary, ruleName, suggestion ? { suggestion } : {});
} else if (outputFormat === 'vale-json') {
} else if (outputFormat === OutputFormat.ValeJson) {
const issue: JsonIssue = {
file,
line,
Expand All @@ -73,7 +75,8 @@ function reportIssue(params: ReportIssueParams): void {
...(scoreText !== undefined ? { score: scoreText } : {}),
};
(jsonFormatter as ValeJsonFormatter).addIssue(issue);
} else if (outputFormat === 'json') {
} else if (outputFormat === OutputFormat.Json || outputFormat === OutputFormat.RdJson) {

const matchLen = match ? match.length : 0;
const endColumn = column + matchLen;
const issue: Issue = {
Expand All @@ -82,11 +85,11 @@ function reportIssue(params: ReportIssueParams): void {
span: [column, endColumn],
severity,
message: summary,
eval: ruleName,
rule: ruleName,
match: match || '',
...(suggestion ? { suggestion } : {})
};
(jsonFormatter as JsonFormatter).addIssue(file, issue);
(jsonFormatter as JsonFormatter | RdJsonFormatter).addIssue(file, issue);
}
}

Expand Down Expand Up @@ -261,7 +264,7 @@ function extractAndReportCriterion(params: ProcessCriterionParams): ProcessCrite
};
}

const got = result.criteria.find(c => c.name === nameKey);
const got = result.criteria.find(c => c.name === nameKey || c.name.toLowerCase() === nameKey.toLowerCase());
if (!got) {
return {
errors: 0,
Expand Down Expand Up @@ -387,15 +390,29 @@ function validateCriteriaCompleteness(params: ValidationParams): boolean {
const expectedNames = new Set<string>((meta.criteria || []).map((c) => String(c.name || c.id || '')));
const returnedNames = new Set(result.criteria.map((c: { name: string }) => c.name));

// Create normalized maps for case-insensitive lookup
const expectedNormalized = new Set<string>();
const expectedOriginalMap = new Map<string, string>();
for (const name of expectedNames) {
if (!returnedNames.has(name)) {
console.error(`Missing criterion in model output: ${name}`);
const norm = name.toLowerCase();
expectedNormalized.add(norm);
expectedOriginalMap.set(norm, name);
}

const returnedNormalized = new Set<string>();
for (const name of returnedNames) {
returnedNormalized.add(name.toLowerCase());
}

for (const norm of expectedNormalized) {
if (!returnedNormalized.has(norm)) {
console.error(`Missing criterion in model output: ${expectedOriginalMap.get(norm)}`);
hadErrors = true;
}
}

for (const name of returnedNames) {
if (!expectedNames.has(name)) {
if (!expectedNormalized.has(name.toLowerCase())) {
console.warn(`[vectorlint] Extra criterion returned by model (ignored): ${name}`);
}
}
Expand All @@ -412,7 +429,11 @@ function validateScores(params: ValidationParams): boolean {

for (const exp of meta.criteria || []) {
const nameKey = String(exp.name || exp.id || '');
const got = result.criteria.find(c => c.name === nameKey);
const got = result.criteria.find(
c => c.name === nameKey
||
c.name.toLowerCase() === nameKey.toLowerCase()
);
if (!got) continue;

const score = Number(got.score);
Expand Down Expand Up @@ -459,7 +480,7 @@ function routePromptResult(params: ProcessPromptResultParams): ErrorTrackingResu
jsonFormatter
});
hadOperationalErrors = hadOperationalErrors || violationResult.hadOperationalErrors;
} else if ((outputFormat === 'json' || outputFormat === 'vale-json') && result.message) {
} else if ((outputFormat === OutputFormat.Json || outputFormat === OutputFormat.ValeJson) && result.message) {
// For JSON, if there's a message but no violations, report it as a general issue
reportIssue({
file: relFile,
Expand Down Expand Up @@ -526,8 +547,8 @@ function routePromptResult(params: ProcessPromptResultParams): ErrorTrackingResu
}
}

if (outputFormat === 'json' && scoreComponents.length > 0) {
(jsonFormatter as JsonFormatter).addEvaluationScore(relFile, {
if (outputFormat === OutputFormat.Json && scoreComponents.length > 0) {
(jsonFormatter as JsonFormatter | RdJsonFormatter).addEvaluationScore(relFile, {
id: promptId || promptFile.filename.replace(/\.md$/, ''),
scores: scoreComponents
});
Expand Down Expand Up @@ -590,7 +611,7 @@ async function runPromptEvaluation(params: RunPromptEvaluationParams): Promise<R
*/
async function evaluateFile(params: EvaluateFileParams): Promise<EvaluateFileResult> {
const { file, options, jsonFormatter } = params;
const { prompts, provider, searchProvider, concurrency, scanPaths, outputFormat = 'line' } = options;
const { prompts, provider, searchProvider, concurrency, scanPaths, outputFormat = OutputFormat.Line } = options;

let hadOperationalErrors = false;
let hadSeverityErrors = false;
Expand All @@ -602,7 +623,7 @@ async function evaluateFile(params: EvaluateFileParams): Promise<EvaluateFileRes
const content = readFileSync(file, 'utf-8');
const relFile = path.relative(process.cwd(), file) || file;

if (outputFormat === 'line') {
if (outputFormat === OutputFormat.Line) {
printFileHeader(relFile);
}

Expand Down Expand Up @@ -702,7 +723,7 @@ async function evaluateFile(params: EvaluateFileParams): Promise<EvaluateFileRes
}
}

if (outputFormat === 'line') {
if (outputFormat === OutputFormat.Line) {

printEvaluationSummaries(allScores);
console.log('');
Expand All @@ -726,7 +747,7 @@ export async function evaluateFiles(
targets: string[],
options: EvaluationOptions
): Promise<EvaluationResult> {
const { outputFormat = 'line' } = options;
const { outputFormat = OutputFormat.Line } = options;

let hadOperationalErrors = false;
let hadSeverityErrors = false;
Expand All @@ -735,9 +756,11 @@ export async function evaluateFiles(
let totalWarnings = 0;
let requestFailures = 0;

let jsonFormatter: ValeJsonFormatter | JsonFormatter;
if (outputFormat === 'json') {
let jsonFormatter: ValeJsonFormatter | JsonFormatter | RdJsonFormatter;
if (outputFormat === OutputFormat.Json) {
jsonFormatter = new JsonFormatter();
} else if (outputFormat === OutputFormat.RdJson) {
jsonFormatter = new RdJsonFormatter();
} else {
jsonFormatter = new ValeJsonFormatter();
}
Expand All @@ -758,9 +781,10 @@ export async function evaluateFiles(
}
}

// Output results based on format
if (outputFormat === 'json' || outputFormat === 'vale-json') {
console.log(jsonFormatter.toJson());
// Output results based on format (always to stdout for JSON formats)
if (outputFormat === OutputFormat.Json || outputFormat === OutputFormat.ValeJson || outputFormat === OutputFormat.RdJson) {
const jsonStr = jsonFormatter.toJson();
console.log(jsonStr);
}

return {
Expand Down
20 changes: 14 additions & 6 deletions src/cli/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,17 @@ import type { FilePatternConfig } from '../boundaries/file-section-parser';
import type { EvaluationSummary } from '../output/reporter';
import { ValeJsonFormatter } from '../output/vale-json-formatter';
import { JsonFormatter, type ScoreComponent } from '../output/json-formatter';
import { RdJsonFormatter } from '../output/rdjson-formatter';
import type { EvaluationResult as PromptEvaluationResult, SubjectiveResult } from '../prompts/schema';
import { Severity } from '../evaluators/types';

export enum OutputFormat {
Line = 'line',
Json = 'json',
ValeJson = 'vale-json',
RdJson = 'rdjson'
}

export interface EvaluationOptions {
prompts: PromptFile[];
rulesPath: string;
Expand All @@ -17,7 +25,7 @@ export interface EvaluationOptions {
concurrency: number;
verbose: boolean;
scanPaths: FilePatternConfig[];
outputFormat?: 'line' | 'json' | 'vale-json';
outputFormat?: OutputFormat;
}

export interface EvaluationResult {
Expand All @@ -40,8 +48,8 @@ export interface ErrorTrackingResult {
export interface EvaluationContext {
content: string;
relFile: string;
outputFormat: 'line' | 'json' | 'vale-json';
jsonFormatter: ValeJsonFormatter | JsonFormatter;
outputFormat: OutputFormat;
jsonFormatter: ValeJsonFormatter | JsonFormatter | RdJsonFormatter;
}

export interface ReportIssueParams {
Expand All @@ -51,8 +59,8 @@ export interface ReportIssueParams {
severity: Severity
summary: string;
ruleName: string;
outputFormat: 'line' | 'json' | 'vale-json';
jsonFormatter: ValeJsonFormatter | JsonFormatter;
outputFormat: OutputFormat;
jsonFormatter: ValeJsonFormatter | JsonFormatter | RdJsonFormatter;
suggestion?: string;
scoreText?: string;
match?: string;
Expand Down Expand Up @@ -124,7 +132,7 @@ export type RunPromptEvaluationResult =
export interface EvaluateFileParams {
file: string;
options: EvaluationOptions;
jsonFormatter: ValeJsonFormatter | JsonFormatter;
jsonFormatter: ValeJsonFormatter | JsonFormatter | RdJsonFormatter;
}

export interface EvaluateFileResult extends ErrorTrackingResult {
Expand Down
Loading