agentstatedeveloper_cli/commands/
recipe.rs1use anyhow::Result;
6use clap::{Args, Subcommand};
7
8use agentstatedeveloper_core::{
9 AsgIndexStore, Engine, FtsFilters, SearchFtsDb,
10 recipes::{classify_test_migration, migrate_stale_tests},
11};
12
13use crate::config::Config;
14
15#[derive(Debug, Subcommand)]
16pub enum RecipeCmd {
17 #[command(name = "classify-test-migration")]
21 ClassifyTestMigration(ClassifyArgs),
22
23 #[command(name = "migrate-stale-tests")]
28 MigrateStaleTests(ClassifyArgs),
29}
30
31#[derive(Debug, Args)]
32pub struct ClassifyArgs {
33 #[arg(required = true, num_args = 1..)]
35 pub query: Vec<String>,
36
37 #[arg(long, default_value = "50")]
39 pub limit: usize,
40}
41
42pub fn run(cfg: &Config, cmd: RecipeCmd) -> Result<()> {
43 match cmd {
44 RecipeCmd::ClassifyTestMigration(args) => run_recipe(cfg, args, |e, i, q, query| {
45 classify_test_migration(e, i, q, query)
46 }),
47 RecipeCmd::MigrateStaleTests(args) => run_recipe(cfg, args, |e, i, q, query| {
48 migrate_stale_tests(e, i, q, query)
49 }),
50 }
51}
52
53fn run_recipe<F>(cfg: &Config, args: ClassifyArgs, build: F) -> Result<()>
56where
57 F: FnOnce(
58 &Engine,
59 &AsgIndexStore,
60 &[String],
61 &str,
62 ) -> agentstatedeveloper_core::recipes::Recipe,
63{
64 let query = args.query.join(" ");
65 let engine = Engine::open_sqlite(&cfg.db_path)?;
66 let index = AsgIndexStore::from_engine(&engine);
67
68 let fts = SearchFtsDb::open(&cfg.db_path).ok();
69 let candidate_qnames: Vec<String> = if let Some(fts) = fts {
70 let filters = FtsFilters {
71 kind: None,
72 language: None,
73 include_tests: true,
74 tests_only: true,
75 exclude_terms: vec![],
76 paths_filter: vec![],
77 exclude_paths: vec![],
78 exclude_languages: vec![],
79 };
80 fts.search(&query, &filters, args.limit)
81 .unwrap_or_default()
82 .into_iter()
83 .map(|h| h.qname)
84 .collect()
85 } else {
86 Vec::new()
87 };
88
89 let recipe = build(&engine, &index, &candidate_qnames, &query);
90 println!("{}", serde_json::to_string_pretty(&recipe)?);
91 Ok(())
92}