use std::io::Write;
use anyhow::Result;
use clap::Subcommand;
use crate::mcp::BasemindServer;
use crate::mcp::params::*;
use crate::path::{RelPath, normalize_query_path};
use super::render::{Emit, emit};
use super::run_tool;
fn resolve_path(server: &BasemindServer, path: &str) -> RelPath {
match normalize_query_path(path, &server.state.shared.root) {
Some(rel) => RelPath::from(rel),
None => RelPath::from(path),
}
}
#[derive(Subcommand, Debug)]
pub enum CodeCmd {
Outline {
path: String,
#[arg(long)]
l2: bool,
},
Symbols {
name: String,
#[arg(long)]
kind: Option<String>,
#[arg(long)]
limit: Option<u32>,
},
Grep {
pattern: String,
#[arg(long)]
language: Option<String>,
#[arg(long)]
path_contains: Option<String>,
#[arg(long)]
limit: Option<u32>,
#[arg(long = "no-context")]
no_context: bool,
},
Files {
#[arg(long)]
path_contains: Option<String>,
#[arg(long)]
language: Option<String>,
#[arg(long)]
limit: Option<u32>,
},
Find {
query: String,
#[arg(long)]
path_prefix: Option<String>,
#[arg(long)]
language: Option<String>,
#[arg(long)]
limit: Option<u32>,
},
Definition {
path: String,
line: u32,
#[arg(long, default_value_t = 0)]
column: u32,
},
References {
name: String,
#[arg(long)]
limit: Option<u32>,
},
Callers {
path: String,
name: String,
#[arg(long)]
kind: Option<String>,
#[arg(long)]
limit: Option<u32>,
},
Implementations {
trait_name: String,
#[arg(long)]
language: Option<String>,
#[arg(long)]
limit: Option<u32>,
},
Dependents { module: String },
Expand {
path: String,
name: String,
#[arg(long)]
kind: Option<String>,
},
Semantic {
query: String,
#[arg(long)]
limit: Option<u32>,
#[arg(long)]
lane: Option<String>,
#[arg(long)]
rerank: bool,
#[arg(long)]
rerank_preset: Option<String>,
#[arg(long)]
format: Option<String>,
},
Chunk {
path: String,
#[arg(long)]
chunk_id: Option<String>,
#[arg(long)]
byte_start: Option<u32>,
},
}
pub async fn run(server: &BasemindServer, cmd: CodeCmd, opts: &Emit, out: &mut impl Write) -> Result<()> {
let p = match cmd {
CodeCmd::Outline { path, l2 } => CodeParams {
path: Some(resolve_path(server, &path)),
l2: Some(l2),
..CodeParams::new(CodeMode::Outline)
},
CodeCmd::Symbols { name, kind, limit } => CodeParams {
name: Some(name),
kind,
limit,
..CodeParams::new(CodeMode::Symbols)
},
CodeCmd::Grep {
pattern,
language,
path_contains,
limit,
no_context,
} => CodeParams {
pattern: Some(pattern),
language,
path_contains,
limit,
include_context: Some(!no_context),
..CodeParams::new(CodeMode::Grep)
},
CodeCmd::Files {
path_contains,
language,
limit,
} => CodeParams {
path_contains,
language,
limit,
..CodeParams::new(CodeMode::Files)
},
CodeCmd::Find {
query,
path_prefix,
language,
limit,
} => CodeParams {
query: Some(query),
path_prefix,
language,
limit,
..CodeParams::new(CodeMode::Find)
},
CodeCmd::Definition { path, line, column } => CodeParams {
path: Some(resolve_path(server, &path)),
line: Some(line),
column: Some(column),
..CodeParams::new(CodeMode::Definition)
},
CodeCmd::References { name, limit } => CodeParams {
name: Some(name),
limit,
..CodeParams::new(CodeMode::References)
},
CodeCmd::Callers {
path,
name,
kind,
limit,
} => CodeParams {
path: Some(resolve_path(server, &path)),
name: Some(name),
kind,
limit,
..CodeParams::new(CodeMode::Callers)
},
CodeCmd::Implementations {
trait_name,
language,
limit,
} => CodeParams {
trait_name: Some(trait_name),
language,
limit,
..CodeParams::new(CodeMode::Implementations)
},
CodeCmd::Dependents { module } => CodeParams {
module: Some(module),
..CodeParams::new(CodeMode::Dependents)
},
CodeCmd::Expand { path, name, kind } => CodeParams {
path: Some(resolve_path(server, &path)),
name: Some(name),
kind,
..CodeParams::new(CodeMode::Expand)
},
CodeCmd::Semantic {
query,
limit,
lane,
rerank,
rerank_preset,
format,
} => CodeParams {
query: Some(query),
limit,
lane,
rerank: rerank.then_some(true),
rerank_preset,
format,
..CodeParams::new(CodeMode::Semantic)
},
CodeCmd::Chunk {
path,
chunk_id,
byte_start,
} => CodeParams {
path: Some(resolve_path(server, &path)),
chunk_id,
byte_start,
..CodeParams::new(CodeMode::Chunk)
},
};
let key = p.mode.telemetry_key();
let r = run_tool(key, server.code(Parameters(Lenient(p))).await)?;
emit(key, &r, opts, out)
}