Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
## v0.3.0 - Polish

- [x] Python ecosystem support (`requirements.txt`, `pyproject.toml`)
- [ ] Severity filtering (`--min-severity`)
- [x] Severity filtering (`--min-severity`)
- [ ] File/path exclusion patterns
- [ ] Performance optimization for large repositories
- [ ] More comprehensive test fixtures
Expand Down
25 changes: 21 additions & 4 deletions src/cli/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,13 @@ Usage:

Scan options:
--json Output findings as JSON
--min-severity <level> Filter by minimum severity (info, warning, error)

Examples:
opk scan
opk scan ./src
opk scan --json
opk scan ./src --json`;
opk scan ./src --min-severity error`;

process.stdout.write(help + '\n');
}
Expand Down Expand Up @@ -66,15 +67,31 @@ async function main(): Promise<void> {
}

let scanPath = process.cwd();
let minSeverity: 'info' | 'warning' | 'error' = 'info';

for (let i = 1; i < args.length; i++) {
const arg = args[i];
if (arg !== '--json' && !arg.startsWith('-')) {
if (arg === '--min-severity') {
if (i + 1 < args.length) {
const val = args[i + 1];
if (val === 'info' || val === 'warning' || val === 'error') {
minSeverity = val;
i++; // skip value
continue;
} else {
process.stderr.write(`Error: Invalid --min-severity value: ${val}. Expected info, warning, or error.\n`);
process.exit(2);
}
} else {
process.stderr.write('Error: --min-severity requires a value (info, warning, or error).\n');
process.exit(2);
}
} else if (arg !== '--json' && !arg.startsWith('-')) {
scanPath = path.resolve(arg);
break;
}
}

const result = await scan(scanPath);
const result = await scan(scanPath, minSeverity);

if (useJson) {
process.stdout.write(formatJson(result) + '\n');
Expand Down
15 changes: 13 additions & 2 deletions src/scanner/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,10 @@ function loadConfig(rootDir: string): OpkConfig | null {
}
}

export async function scan(rootDir: string): Promise<ScanResult> {
export async function scan(
rootDir: string,
minSeverity: 'info' | 'warning' | 'error' = 'info'
): Promise<ScanResult> {
const startTime = Date.now();
const absoluteRoot = path.resolve(rootDir);

Expand Down Expand Up @@ -112,8 +115,16 @@ export async function scan(rootDir: string): Promise<ScanResult> {

const durationMs = Date.now() - startTime;

const severityLevels = { info: 0, warning: 1, error: 2 };
const minLevel = severityLevels[minSeverity] ?? 0;

const filteredFindings = allFindings.filter((finding) => {
const findingLevel = severityLevels[finding.severity] ?? 0;
return findingLevel >= minLevel;
});

return {
findings: allFindings,
findings: filteredFindings,
scannedFiles: files.length,
rulesRun,
durationMs,
Expand Down
14 changes: 14 additions & 0 deletions tests/scanner/scanner.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,18 @@ test('Scanner Integration', async (t) => {
// There are no other findings in this folder.
assert.strictEqual(result.findings.length, 0);
});

await t.test('should filter findings by minimum severity', async () => {
// We scan prompt-artifacts which contains OPK-002 (warning).
const rootDir = path.join(FIXTURES_DIR, 'prompt-artifacts');

// Default (info) should include warnings
const defaultResult = await scan(rootDir);
assert.ok(defaultResult.findings.length > 0);
assert.strictEqual(defaultResult.findings[0].severity, 'warning');

// Error minSeverity should filter out warnings
const errorResult = await scan(rootDir, 'error');
assert.strictEqual(errorResult.findings.length, 0);
});
});
Loading