Skip to main content

agent_exec/
schema_cmd.rs

1//! Implementation of the `schema` subcommand.
2//!
3//! Reads `schema/agent-exec.schema.json` from the same directory as the
4//! binary and returns it as a JSON response.
5
6use anyhow::{Context, Result};
7
8use crate::schema::{Response, SchemaData};
9
10pub struct SchemaOpts;
11
12/// Execute the `schema` subcommand.
13///
14/// Loads the bundled JSON Schema from a path relative to the binary and
15/// prints a JSON envelope to stdout.
16pub fn execute(_opts: SchemaOpts) -> Result<()> {
17    // Locate schema relative to the binary directory so installs work
18    // regardless of the working directory.
19    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    // Extract generated_at from the schema's `$comment` or use a static fallback.
28    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
44/// Resolve the schema file path.
45///
46/// Looks for `schema/agent-exec.schema.json` relative to the binary.
47fn 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    // During `cargo test` the binary is placed in target/debug/deps/;
52    // try both the binary dir and its parent so that the schema file at
53    // `<repo>/schema/agent-exec.schema.json` is found under `target/debug/`.
54    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    // Fallback: relative to CWD (useful in development).
67    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}