agentstatedeveloper_cli/commands/
scopes.rs1use anyhow::Result;
11use clap::{Args, Subcommand};
12use serde_json::json;
13
14use agentstatedeveloper_core::load_scope_aliases;
15
16use crate::config::Config;
17
18#[derive(Debug, Subcommand)]
19pub enum ScopesCmd {
20 List(ListArgs),
22}
23
24#[derive(Debug, Args)]
25pub struct ListArgs {
26 #[arg(long)]
28 pub json: bool,
29}
30
31pub fn run(cfg: &Config, cmd: ScopesCmd) -> Result<()> {
32 match cmd {
33 ScopesCmd::List(args) => run_list(cfg, args),
34 }
35}
36
37fn run_list(cfg: &Config, args: ListArgs) -> Result<()> {
38 let aliases = load_scope_aliases(&cfg.db_path);
39
40 if args.json {
41 let payload = json!({
42 "scopes": aliases,
43 "count": aliases.len(),
44 "scopes_file": cfg
45 .db_path
46 .parent()
47 .unwrap_or(std::path::Path::new("."))
48 .join("scopes.toml")
49 .display()
50 .to_string(),
51 });
52 println!("{}", serde_json::to_string_pretty(&payload)?);
53 return Ok(());
54 }
55
56 if aliases.is_empty() {
57 println!("No named scopes defined.");
58 println!();
59 println!("Define them in .asd/scopes.toml to make `--scope <name>` work across");
60 println!("search, prepare-change, investigate, impact, checklist, and since:");
61 println!();
62 println!(" # .asd/scopes.toml");
63 println!(" audio-engine = [\"Packages/AudioEngine/**\"]");
64 println!(" drift-pad = [\"App/**/DriftPad*\", \"Packages/SequencerCore/**\"]");
65 println!();
66 println!("Without a scope file you can still pass globs directly:");
67 println!(" asd search 'master volume' --paths 'Packages/AudioEngine/**'");
68 return Ok(());
69 }
70
71 println!("Named scopes ({}):", aliases.len());
72 let mut names: Vec<&String> = aliases.keys().collect();
73 names.sort();
74 for name in names {
75 let globs = &aliases[name];
76 println!(" {name}");
77 for g in globs {
78 println!(" - {g}");
79 }
80 }
81 println!();
82 println!("Use with: --scope <name> (works on search, prepare-change, investigate,");
83 println!("impact, checklist, since.)");
84 Ok(())
85}