1use anyhow::{Context, Result};
7
8use crate::schema::{Response, SchemaData};
9
10pub struct SchemaOpts;
11
12pub fn execute(_opts: SchemaOpts) -> Result<()> {
17 let schema_path = schema_file_path()?;
20
21 let raw = std::fs::read_to_string(&schema_path)
22 .with_context(|| format!("failed to read schema file: {}", schema_path.display()))?;
23
24 let schema: serde_json::Value =
25 serde_json::from_str(&raw).with_context(|| "schema file is not valid JSON")?;
26
27 let generated_at = schema
29 .get("$comment")
30 .and_then(|v| v.as_str())
31 .unwrap_or("unknown")
32 .to_string();
33
34 let data = SchemaData {
35 schema_format: "json-schema-draft-07".to_string(),
36 schema,
37 generated_at,
38 };
39
40 Response::new("schema", data).print();
41 Ok(())
42}
43
44fn schema_file_path() -> Result<std::path::PathBuf> {
48 let exe = std::env::current_exe().context("cannot determine binary path")?;
49 let bin_dir = exe.parent().context("binary has no parent directory")?;
50
51 for candidate in &[
55 bin_dir.to_path_buf(),
56 bin_dir.join(".."),
57 bin_dir.join("../.."),
58 bin_dir.join("../../.."),
59 ] {
60 let p = candidate.join("schema").join("agent-exec.schema.json");
61 if p.exists() {
62 return Ok(p.canonicalize().unwrap_or(p));
63 }
64 }
65
66 let cwd_path = std::path::Path::new("schema").join("agent-exec.schema.json");
68 if cwd_path.exists() {
69 return Ok(cwd_path);
70 }
71
72 anyhow::bail!(
73 "schema file not found; expected schema/agent-exec.schema.json relative to the binary or current directory"
74 )
75}