Skip to content

Add agent-ready analysis outputs - #418

Merged
panbanda merged 1 commit into
mainfrom
feat/agent-analysis-enhancements
May 31, 2026
Merged

Add agent-ready analysis outputs#418
panbanda merged 1 commit into
mainfrom
feat/agent-analysis-enhancements

Conversation

@panbanda

@panbanda panbanda commented May 29, 2026

Copy link
Copy Markdown
Owner

Summary

  • add an Omen-native agent context pack for CLI and MCP clients with languages, top symbols, risks, and navigation hints
  • add SARIF output support for analysis findings
  • add --changed-since filtering for analyzer commands and all
  • harden default file discovery against common generated/vendor directories
  • add benchmark coverage for agent context generation

Verification

  • cargo fmt --check
  • cargo test
  • cargo clippy --all-targets --all-features -- -D warnings
  • git diff --check

Notes

  • No Graphify wrapper or import path is included; this is Omen-native functionality.

Summary by CodeRabbit

  • New Features

    • Added --changed-since argument to analyze only files modified since a specified git reference.
    • Added SARIF output format support for vulnerability reporting across all analyzers.
    • Added context analysis providing repository summary with detected languages, top symbols, security risks, and navigation hints.
    • Added context tool to MCP server for remote analysis requests.
  • Improvements

    • Automatic exclusion of common generated directories (node_modules, target, dist, coverage, etc.) from analysis.

Review Change Stack

@coderabbitai

coderabbitai Bot commented May 29, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

PR adds a Context data model and builder that aggregates language/symbol/risk metadata, introduces SARIF output format support across all commands, and implements --changed-since filtering via git-diff to limit analysis to modified files. Includes FileSet filtering, CLI parsing, MCP tool integration, and test coverage.

Changes

Context, SARIF output, and changed-since filtering

Layer / File(s) Summary
FileSet filtering for changed-since
src/core/file_set.rs
filter_by_paths method and is_default_ignored_path helper enable filtering FileSet by explicit paths or ignoring common generated directories when walking the repo.
CLI argument parsing
src/cli/mod.rs
--changed-since flag accepts a git ref to limit analysis scope; Sarif enum variant joins OutputFormat alongside JSON/Text/Markdown; tests verify both parse correctly.
Context data model and builder
src/context.rs, src/lib.rs
Context, LanguageSummary, SymbolSummary, and RiskSummary structs hold analysis metadata. build_context aggregates languages (via file detection), top symbols (from repomap), and risks (from satd/complexity) with capped limits, then generates navigation hints. Exported via pub mod context.
SARIF output formatting
src/output/mod.rs
Format::Sarif variant and format_sarif function recursively extract finding-like JSON entries, map severity to SARIF levels, and emit SARIF 2.1.0 JSON structure with rules and results arrays.
Main CLI integration: context and changed-since
src/main.rs
run_context now calls build_context directly. changed_files_since helper runs git diff to list modified paths. filtered_file_set applies changed-since filtering when flag provided. SARIF output routed across All/Context/Search/Mutation commands. Markdown rendering for context includes Languages, Top Symbols, and Hints sections.
MCP server context tool
src/mcp/mod.rs
tools/list includes new context tool with path/max_symbols/max_risks schema. handle_context dispatches from handle_tool_call, calls build_context, and returns pretty-printed JSON in MCP response format.
Integration tests and benchmarks
benches/analyzers.rs, tests/integration_tests.rs
bench_context measures context building for repo sizes [10, 50] with sample size 20. Integration tests validate SARIF version/runs metadata, context JSON structure (hints/top_symbols/languages), and --changed-since HEAD correctly filters to only modified files.

Possibly related PRs


Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (1 error)

Check name Status Explanation Resolution
Pr Test Coverage & Quality Check ❌ Error Two unaddressed review comments: (1) SARIF defaults message/line for any object with file/path key; (2) Risk truncation after SATD append, so complexity risks drop when SATD fills quota. Add explicit finding field check to sarif_finding_from_object. Build combined risk ranking before truncation with dual-metric sorting.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed Title accurately summarizes the main change: adding agent-ready analysis outputs including context packs and SARIF support.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/agent-analysis-enhancements

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions

github-actions Bot commented May 29, 2026

Copy link
Copy Markdown
Contributor

Omen Analysis

Diff Risk

Risk Score 55%
Risk Level MEDIUM
Files Modified 9
Lines Added +687
Lines Deleted -22
Commits 2
Risk Factors
Factor Score
lines_added 0.16
entropy 0.1276
file_churn 0.1
num_files 0.048
file_complexity 0.0398
commits 0.0140
lines_deleted 0.0088
ownership_diffusion 0
Recommendations
  • Large PR with 687 lines added - consider splitting into smaller changes
  • Touches historically volatile files - changes here often introduce bugs
  • Elevated risk: Add additional reviewers or testing

Repository Health

Health Score 77.7868902157319 / 100
Grade C
Files Analyzed 102
Critical Issues 1
Score Components
Component Score Weight
complexity 92 1
duplication 42.2 0.8
cohesion 94.5 0.6
tdg 85.2 0.6
coupling 52.8 0.4
satd 91.2 0.4
smells 100 0.2
Tips for AI agents

Use these commands to investigate and improve low-scoring areas.

Run full analysis:

omen -f json score    # health score with component breakdown
omen -f json diff     # PR risk analysis
omen hotspot          # high-churn + high-complexity files

Coupling (score: 52.8235294117647) -- needs attention

omen graph && omen smells

Break cyclic dependencies by introducing interfaces or extracting shared types. Reduce fan-out from hub modules by splitting responsibilities.


Duplication (score: 42.16365910309094) -- needs attention

omen clones

Look for Type-1 (exact) and Type-2 (renamed) clones. Extract shared logic into reusable functions or modules. Prioritize clones in high-churn files.


General workflow for improving scores:

  1. Run the relevant analyzer command to identify specific files
  2. Focus on the highest-weight components first (complexity 25%, duplication 20%, cohesion 15%, TDG 15%)
  3. Make targeted improvements -- small refactors that reduce complexity or eliminate duplication
  4. Re-run omen score to verify improvement

@panbanda
panbanda force-pushed the feat/agent-analysis-enhancements branch from f3070a3 to c214cb1 Compare May 29, 2026 21:53
@panbanda

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 29, 2026

Copy link
Copy Markdown
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@panbanda

Copy link
Copy Markdown
Owner Author

@coderabbitai review

1 similar comment
@panbanda

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 30, 2026

Copy link
Copy Markdown
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/main.rs (1)

721-735: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Text output may double-print context.

Lines 721-732 print text headers, then line 733 calls format.format(&context, ...). If Text formatter also outputs the context fields, output will be duplicated.

Proposed fix
         Format::Text => {
             println!("Repository Context");
             println!("==================");
             println!("Max Tokens: {}", args.max_tokens);
             println!("Depth: {}", args.depth);
             if let Some(ref target) = args.target {
                 println!("Target: {}", target.display());
             }
             if let Some(ref symbol) = args.symbol {
                 println!("Symbol: {}", symbol);
             }
             println!();
-            format.format(&context, &mut stdout())?;
         }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main.rs` around lines 721 - 735, The Text branch currently prints
repository headers (the println! calls using args.max_tokens, args.depth,
args.target, args.symbol) and then calls format.format(&context, &mut stdout()),
causing duplicate context output when the Text formatter also prints those
fields; fix by removing the manual println! header block and rely on
format.format(&context, &mut stdout()) to render the full text output (or
alternatively move those header prints into the Text formatter implementation),
updating the Format::Text arm to only call format.format to avoid
double-printing.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/context.rs`:
- Around line 104-147: summarize_risks currently appends SATD entries first and
then complexity entries, and only calls risks.truncate(limit) at the end so
complexity risks get dropped when SATD fills the quota; also complexity sorting
uses only metrics.cognitive and can miss severe cyclomatic outliers. Change
summarize_risks to build a single combined collection (e.g., Vec<(score,
RiskSummary)> or Vec<RankedRisk>), push SATD-derived RiskSummary items (from
satd::Analyzer::default().analyze) and complexity-derived RiskSummary items
(from complexity::Analyzer::default().analyze) into it, compute a numeric
ranking score for each entry that considers severity plus both metrics.cognitive
and metrics.cyclomatic (or use a tuple key like (severity_rank, cognitive,
cyclomatic)), sort the combined collection by that score in descending order,
then take the top limit entries and return their RiskSummary values (instead of
truncating after merging).

In `@src/output/mod.rs`:
- Around line 106-150: The SARIF predicate is too permissive: update
sarif_finding_from_object (used by collect_sarif_findings) to only treat an
object as a finding when it contains an explicit finding indicator (e.g., a
non-default message field like "message"/"text"/"reason"/"name"/"marker", a
"ruleId"/"result" field, or an explicit numeric line like
"line"/"start_line"/"line_start") in addition to a file/path; do not rely solely
on defaulting message or line to create a SarifFinding; require the presence of
at least one explicit message-like key or an explicit line number before
returning Some(SarifFinding) and otherwise return None.

---

Outside diff comments:
In `@src/main.rs`:
- Around line 721-735: The Text branch currently prints repository headers (the
println! calls using args.max_tokens, args.depth, args.target, args.symbol) and
then calls format.format(&context, &mut stdout()), causing duplicate context
output when the Text formatter also prints those fields; fix by removing the
manual println! header block and rely on format.format(&context, &mut stdout())
to render the full text output (or alternatively move those header prints into
the Text formatter implementation), updating the Format::Text arm to only call
format.format to avoid double-printing.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: cfae81e9-7223-4d5d-9548-7673a414e89e

📥 Commits

Reviewing files that changed from the base of the PR and between 4529430 and c214cb1.

📒 Files selected for processing (9)
  • benches/analyzers.rs
  • src/cli/mod.rs
  • src/context.rs
  • src/core/file_set.rs
  • src/lib.rs
  • src/main.rs
  • src/mcp/mod.rs
  • src/output/mod.rs
  • tests/integration_tests.rs

Comment thread src/context.rs
Comment on lines +104 to +147
fn summarize_risks(ctx: &AnalysisContext<'_>, limit: usize) -> Vec<RiskSummary> {
let mut risks = Vec::new();

if let Ok(satd) = satd::Analyzer::default().analyze(ctx) {
risks.extend(satd.items.into_iter().map(|item| RiskSummary {
kind: "satd".to_string(),
file: item.file,
line: item.line,
message: item.text,
severity: format!("{:?}", item.severity).to_lowercase(),
}));
}

if let Ok(complexity) = complexity::Analyzer::default().analyze(ctx) {
let mut functions: Vec<_> = complexity
.files
.into_iter()
.flat_map(|file| file.functions)
.collect();
functions.sort_by_key(|func| std::cmp::Reverse(func.metrics.cognitive));
risks.extend(functions.into_iter().take(limit).filter_map(|func| {
if func.metrics.cognitive < 15 && func.metrics.cyclomatic < 10 {
return None;
}
Some(RiskSummary {
kind: "complexity".to_string(),
file: func.file,
line: func.start_line,
message: format!(
"{} has cyclomatic {} and cognitive {} complexity",
func.name, func.metrics.cyclomatic, func.metrics.cognitive
),
severity: if func.metrics.cognitive >= 30 || func.metrics.cyclomatic >= 20 {
"high"
} else {
"medium"
}
.to_string(),
})
}));
}

risks.truncate(limit);
risks

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Rank risks before truncating.

SATD items are appended first and truncate(limit) runs only once at the end, so complexity risks disappear whenever SATD already fills the quota. This also sorts complexity only by cognitive, which can drop the worst cyclomatic outliers. Build one combined ranking, then take limit.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/context.rs` around lines 104 - 147, summarize_risks currently appends
SATD entries first and then complexity entries, and only calls
risks.truncate(limit) at the end so complexity risks get dropped when SATD fills
the quota; also complexity sorting uses only metrics.cognitive and can miss
severe cyclomatic outliers. Change summarize_risks to build a single combined
collection (e.g., Vec<(score, RiskSummary)> or Vec<RankedRisk>), push
SATD-derived RiskSummary items (from satd::Analyzer::default().analyze) and
complexity-derived RiskSummary items (from
complexity::Analyzer::default().analyze) into it, compute a numeric ranking
score for each entry that considers severity plus both metrics.cognitive and
metrics.cyclomatic (or use a tuple key like (severity_rank, cognitive,
cyclomatic)), sort the combined collection by that score in descending order,
then take the top limit entries and return their RiskSummary values (instead of
truncating after merging).

Comment thread src/output/mod.rs
Comment on lines +106 to +150
fn collect_sarif_findings(value: &Value, findings: &mut Vec<SarifFinding>) {
match value {
Value::Object(map) => {
if let Some(finding) = sarif_finding_from_object(map) {
findings.push(finding);
}
for child in map.values() {
collect_sarif_findings(child, findings);
}
}
Value::Array(items) => {
for item in items {
collect_sarif_findings(item, findings);
}
}
_ => {}
}
}

fn sarif_finding_from_object(map: &serde_json::Map<String, Value>) -> Option<SarifFinding> {
let file = ["file", "path", "file_path"]
.iter()
.find_map(|key| map.get(*key).and_then(Value::as_str))?;
let line = ["line", "start_line", "line_start"]
.iter()
.find_map(|key| map.get(*key).and_then(Value::as_u64))
.unwrap_or(1)
.max(1);
let message = ["text", "reason", "message", "name", "marker"]
.iter()
.find_map(|key| map.get(*key).and_then(Value::as_str))
.unwrap_or("Omen finding")
.to_string();
let level = map
.get("severity")
.and_then(Value::as_str)
.map(sarif_level)
.unwrap_or("warning");

Some(SarifFinding {
file: file.to_string(),
line,
level,
message,
})

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Tighten the SARIF finding predicate.

Any object with file/path becomes a SARIF result here, even summary/container nodes. Because message and line are defaulted, those false positives turn into bogus findings at line 1. Require an explicit finding field as well, not just a path.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/output/mod.rs` around lines 106 - 150, The SARIF predicate is too
permissive: update sarif_finding_from_object (used by collect_sarif_findings) to
only treat an object as a finding when it contains an explicit finding indicator
(e.g., a non-default message field like
"message"/"text"/"reason"/"name"/"marker", a "ruleId"/"result" field, or an
explicit numeric line like "line"/"start_line"/"line_start") in addition to a
file/path; do not rely solely on defaulting message or line to create a
SarifFinding; require the presence of at least one explicit message-like key or
an explicit line number before returning Some(SarifFinding) and otherwise return
None.

@panbanda
panbanda merged commit f842988 into main May 31, 2026
8 checks passed
@panbanda
panbanda deleted the feat/agent-analysis-enhancements branch May 31, 2026 18:43
@github-actions github-actions Bot mentioned this pull request May 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant