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
6 changes: 4 additions & 2 deletions crates/vera-cli/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -553,8 +553,10 @@ pub enum Commands {
#[command(flatten)]
filters: crate::helpers::SearchFilterArgs,

/// Maximum number of results (default: 20).
#[arg(long, short = 'n')]
/// Maximum number of results (default: 20). A trailing bare `-n` is
/// accepted and treated as the default, since line numbers are
/// always printed.
#[arg(long, short = 'n', num_args = 0..=1, default_missing_value = "20")]
limit: Option<usize>,

/// Case-insensitive matching.
Expand Down
34 changes: 19 additions & 15 deletions crates/vera-cli/src/commands/agent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1181,25 +1181,29 @@ fn sync_with_options(
const VERA_SNIPPET_BEGIN_MARKER: &str = "<!-- vera:begin -->";
const VERA_SNIPPET_END_MARKER: &str = "<!-- vera:end -->";
const AGENTS_MD_SNIPPET_HEADING: &str = "## Code Search";
const AGENTS_MD_SNIPPET_INTRO: &str = "Before reading files to answer \"where is X\", \"how does Y work\", or \"find Z\", search first with Vera.";
const AGENTS_MD_SNIPPET_INTRO: &str = "Your first search in this repository goes through Vera, not grep, rg, or file reads: run `vera overview` when you have not searched this repository yet, then `vera search \"<what the code does>\"`. Fall back to grep or rg only after a Vera call has missed.";

const AGENTS_MD_SNIPPET: &str = r#"## Code Search

<!-- vera:begin -->

Before reading files to answer "where is X", "how does Y work", or "find Z", search first with Vera.

- `vera search "query"` for semantic code search. Describe behavior: "JWT validation", not "auth". If one phrasing misses, try 2-3 varied queries or add `--intent "goal"`.
- `vera grep "pattern"` for exact text or regex in indexed files
- `vera references <symbol>` for callers and `vera references <symbol> --callees` for callees; add `--receiver <name>` when several definitions share a name
- `vera structural definitions <symbol>`, `vera structural env <NAME>`, `vera structural routes`, or `vera structural impls <symbol>` for common structural tasks
- `vera explain-path path/to/file` to explain why a file is or is not indexed
- `vera overview` for a project summary (languages, entry points, hotspots). Add `--changed`, `--since <rev>`, or `--base <rev>` to scope it to modified files.
- `vera stats --json` for index health, including tree-sitter error, parse-failure, and Tier 0 fallback counts
- `vera search --deep "query"` for RAG-fusion query expansion + merged ranking
- Narrow `vera search` or `vera grep` with `--lang`, `--path`, `--type`, or `--scope docs`
- `vera watch .` to auto-update the index, or `vera update .` after edits (`vera index .` if `.vera/` is missing)
- For detailed usage, query patterns, and troubleshooting, read the Vera skill file installed by `vera agent install`
Your first search in this repository goes through Vera, not grep, rg, or file reads: run `vera overview` when you have not searched this repository yet, then `vera search "<what the code does>"`. Fall back to grep or rg only after a Vera call has missed.

Route by question type:

- How or where something works: `vera search "request teardown ordering"`. Describe behavior ("JWT validation"), not nouns ("auth"). If a phrasing misses, add `--intent "<goal>"` or use `--deep`.
- The question names a function, class, or config key: `vera structural definitions <symbol>` for the definition, `vera references <symbol>` for callers (`--callees` for callees). One call returns ranked definitions or call sites, not raw matching lines.
- Exact text or regex: `vera grep "pattern"`. Line numbers are always shown; cap results with `--limit <N>`.
- Enumerate every X (signals, routes, env reads, implementations): `vera structural routes`, `vera structural env <NAME>`, `vera structural impls <symbol>`, or `vera grep "X" --lang <lang>`.

Working with results:

- Hits are `path:start-end kind:name` plus the code. Cite from the hit; open a file only for lines the hit did not include.
- If the top hit is a usage site, re-run with the symbol name or `vera structural definitions <symbol>` instead of trying more phrasings.
- `--path` is relative to the repository root (`--path src/flask`, not an absolute path). Narrow with `--lang`, `--path`, `--type`, or `--scope docs`; widen with `--limit 8`.
- Vera indexes this repository only. For dependency sources (site-packages, uv or pip caches) use rg, then return to Vera for repository code.
- A stale-index warning does not invalidate hits. After editing files, run `vera update .` from wherever you are (it uses the repository root's index; `vera index .` from the repository root if none exists).
- `vera explain-path <file>`, `vera stats --json`, and detailed usage are in the Vera skill installed by `vera agent install`.
<!-- vera:end -->
"#;

Expand Down Expand Up @@ -1772,7 +1776,7 @@ mod tests {
#[test]
fn refresh_vera_snippet_in_markdown_skips_edited_legacy_section() {
let edited = legacy_agents_md_snippet().replace(
"- `vera grep \"pattern\"` for exact text or regex in indexed files",
"- Exact text or regex: `vera grep \"pattern\"`. Line numbers are always shown; cap results with `--limit <N>`.",
"- Use my preferred search tool instead",
);
let existing = format!(
Expand Down
10 changes: 7 additions & 3 deletions crates/vera-cli/src/commands/overview.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,18 @@ pub fn run(
.map_err(|e| anyhow::anyhow!("failed to get current directory: {e}"))?;
let config = state::load_runtime_config()?;

// An ancestor's `.vera/` index covers this directory too; without one the
// existing no-index behavior is unchanged.
let repo_root = crate::helpers::resolve_index_root(&cwd).unwrap_or_else(|| cwd.clone());

let exact_paths = if let Some(scope) = git_scope.as_ref() {
Some(vera_core::git_scope::resolve_scope(&cwd, scope)?)
Some(vera_core::git_scope::resolve_scope(&repo_root, scope)?)
} else {
None
};
warn_if_index_stale(&cwd, &config.indexing);
warn_if_index_stale(&repo_root, &config.indexing);

let overview = stats::collect_overview_filtered(&cwd, exact_paths.as_ref())?;
let overview = stats::collect_overview_filtered(&repo_root, exact_paths.as_ref())?;

if json_output {
let json = serde_json::to_string_pretty(&overview)
Expand Down
2 changes: 1 addition & 1 deletion crates/vera-cli/src/commands/search.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ pub fn run(

let cwd = std::env::current_dir()
.map_err(|e| anyhow::anyhow!("failed to get current directory: {e}"))?;
if !vera_core::indexing::index_dir(&cwd).exists()
if crate::helpers::find_index_root(&cwd).is_none()
&& should_offer_auto_index(
json_output,
std::io::stdin().is_terminal() && std::io::stderr().is_terminal(),
Expand Down
5 changes: 4 additions & 1 deletion crates/vera-cli/src/commands/stats.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@
pub fn run(json_output: bool) -> anyhow::Result<()> {
let cwd = std::env::current_dir()
.map_err(|e| anyhow::anyhow!("failed to get current directory: {e}"))?;
// Match the read commands: an ancestor `.vera/` index covers this
// subdirectory too.
let repo_root = crate::helpers::resolve_index_root(&cwd).unwrap_or_else(|| cwd.clone());

let stats = vera_core::stats::collect_stats(&cwd)?;
let stats = vera_core::stats::collect_stats(&repo_root)?;

if json_output {
let json = serde_json::to_string_pretty(&stats)
Expand Down
16 changes: 16 additions & 0 deletions crates/vera-cli/src/commands/update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,22 @@ pub fn run(path: &str, json_output: bool, options: CommandOptions) -> anyhow::Re
Hint: vera update expects a directory path, not a file."
);
}
// Read commands resolve an ancestor `.vera/` index so they work from
// subdirectories; update must operate on the same root or it would
// rebuild `<subdir>/.vera` as a nested partial index that shadows the
// real one for that subtree. Canonicalize first so the walk sees real
// parents instead of the raw CLI path's lexical parents ("." would
// never walk past the empty path).
let start = repo_path.canonicalize()?;
let repo_path = match crate::helpers::find_index_root(&start) {
Some(root) => {
if root != start {
eprintln!("note: using index at {}", root.display());
}
root
}
None => repo_path.to_path_buf(),
};

let rt = tokio::runtime::Runtime::new()
.map_err(|e| anyhow::anyhow!("failed to create async runtime: {e}"))?;
Expand Down
Loading
Loading