1use clap::{Parser, Subcommand};
2use std::path::PathBuf;
3
4mod skills {
5 include!(concat!(env!("OUT_DIR"), "/skills.rs"));
6
7 pub fn get(name: &str) -> Option<&'static str> {
8 SKILL_DIRS.iter().find(|s| s.name == name).map(|s| s.body)
9 }
10}
11
12#[derive(Parser, Debug)]
13#[command(
14 name = "dial9",
15 about = "Trace browser and viewer for dial9-tokio-telemetry"
16)]
17pub struct Cli {
18 #[command(subcommand)]
19 command: Commands,
20}
21
22#[derive(Subcommand, Debug)]
23enum Commands {
24 Agents {
26 #[command(subcommand)]
27 action: Option<AgentsAction>,
28 },
29 Serve {
31 #[arg(long, default_value = "3000")]
33 port: u16,
34
35 #[arg(long)]
37 bucket: Option<String>,
38
39 #[arg(long)]
41 prefix: Option<String>,
42
43 #[arg(long, conflicts_with = "bucket")]
45 local_dir: Option<PathBuf>,
46
47 #[arg(long)]
49 dev: bool,
50 },
51}
52
53#[derive(Subcommand, Debug)]
54enum AgentsAction {
55 Toolkit {
57 path: PathBuf,
59 },
60 Skill {
62 name: String,
64 },
65 Skills {
67 path: PathBuf,
69 },
70}
71
72pub async fn run() -> anyhow::Result<()> {
74 let cli = Cli::parse();
75
76 match cli.command {
77 Commands::Agents { action } => match action {
78 None => print!("{}", skills::HEADER),
79 Some(AgentsAction::Toolkit { path }) => {
80 std::fs::create_dir_all(&path)?;
81 for (name, content) in skills::TOOLKIT_FILES {
82 std::fs::write(path.join(name), content)?;
83 }
84 let abs = std::fs::canonicalize(&path)?;
85 eprintln!("Toolkit written to {}", abs.display());
86 eprintln!(
87 "Run: node {}/analyze.js <trace.bin or directory>",
88 abs.display()
89 );
90 }
91 Some(AgentsAction::Skill { name }) => match skills::get(&name) {
92 Some(content) => print!("{}", content),
93 None => {
94 eprintln!("Unknown skill: {name}");
95 eprintln!("Available skills:");
96 for skill in skills::SKILL_DIRS {
97 eprintln!(" {:24} {}", skill.name, skill.description);
98 }
99 std::process::exit(1);
100 }
101 },
102 Some(AgentsAction::Skills { path }) => {
103 for skill in skills::SKILL_DIRS {
104 let skill_dir = path.join(skill.name);
105 for (rel_path, content) in skill.files {
106 let file_path = skill_dir.join(rel_path);
107 if let Some(parent) = file_path.parent() {
108 std::fs::create_dir_all(parent)?;
109 }
110 std::fs::write(&file_path, content)?;
111 }
112 }
113 let abs = std::fs::canonicalize(&path)?;
114 eprintln!("Skills unpacked to {}", abs.display());
115 eprintln!("Add to .kiro/skills/ or point your agent at this directory.");
116 }
117 },
118 Commands::Serve {
119 port,
120 bucket,
121 prefix,
122 local_dir,
123 dev,
124 } => {
125 return crate::serve(port, bucket, prefix, local_dir, dev).await;
126 }
127 }
128 Ok(())
129}