summaryrefslogblamecommitdiff
path: root/makima/src/daemon/api/directive.rs
blob: 48762d60a1b891a38d14f6f722fc960ccdd7e4ca (plain) (tree)
































































































































































                                                                                                




























































































































































































































































































                                                                                
 
//! Directive API methods.

use uuid::Uuid;

use super::client::{ApiClient, ApiError};
use super::supervisor::JsonValue;

impl ApiClient {
    /// Create a new directive.
    pub async fn create_directive(
        &self,
        goal: &str,
        repository_url: Option<&str>,
        autonomy_level: &str,
    ) -> Result<JsonValue, ApiError> {
        #[derive(serde::Serialize)]
        #[serde(rename_all = "camelCase")]
        struct CreateRequest<'a> {
            goal: &'a str,
            repository_url: Option<&'a str>,
            autonomy_level: &'a str,
        }
        let req = CreateRequest {
            goal,
            repository_url,
            autonomy_level,
        };
        self.post("/api/v1/directives", &req).await
    }

    /// List all directives for the authenticated user.
    pub async fn list_directives(
        &self,
        status: Option<&str>,
        limit: i32,
    ) -> Result<JsonValue, ApiError> {
        let mut params = Vec::new();
        if let Some(s) = status {
            params.push(format!("status={}", s));
        }
        params.push(format!("limit={}", limit));
        let query_string = format!("?{}", params.join("&"));
        self.get(&format!("/api/v1/directives{}", query_string))
            .await
    }

    /// Get a directive by ID (includes progress info).
    pub async fn get_directive(&self, directive_id: Uuid) -> Result<JsonValue, ApiError> {
        self.get(&format!("/api/v1/directives/{}", directive_id))
            .await
    }

    /// Archive a directive.
    pub async fn archive_directive(&self, directive_id: Uuid) -> Result<JsonValue, ApiError> {
        self.delete_with_response(&format!("/api/v1/directives/{}", directive_id))
            .await
    }

    /// Start a directive (plans and begins execution).
    pub async fn start_directive(&self, directive_id: Uuid) -> Result<JsonValue, ApiError> {
        self.post_empty(&format!("/api/v1/directives/{}/start", directive_id))
            .await
    }

    /// Pause a directive.
    pub async fn pause_directive(&self, directive_id: Uuid) -> Result<JsonValue, ApiError> {
        self.post_empty(&format!("/api/v1/directives/{}/pause", directive_id))
            .await
    }

    /// Resume a paused directive.
    pub async fn resume_directive(&self, directive_id: Uuid) -> Result<JsonValue, ApiError> {
        self.post_empty(&format!("/api/v1/directives/{}/resume", directive_id))
            .await
    }

    /// Stop a directive.
    pub async fn stop_directive(&self, directive_id: Uuid) -> Result<JsonValue, ApiError> {
        self.post_empty(&format!("/api/v1/directives/{}/stop", directive_id))
            .await
    }

    /// Get the current chain and steps for a directive.
    pub async fn get_directive_chain(&self, directive_id: Uuid) -> Result<JsonValue, ApiError> {
        self.get(&format!("/api/v1/directives/{}/chain", directive_id))
            .await
    }

    /// Get directive DAG structure for visualization.
    pub async fn get_directive_graph(&self, directive_id: Uuid) -> Result<JsonValue, ApiError> {
        self.get(&format!("/api/v1/directives/{}/chain/graph", directive_id))
            .await
    }

    /// List events for a directive.
    pub async fn list_directive_events(
        &self,
        directive_id: Uuid,
        limit: i32,
    ) -> Result<JsonValue, ApiError> {
        self.get(&format!(
            "/api/v1/directives/{}/events?limit={}",
            directive_id, limit
        ))
        .await
    }

    /// List pending approvals for a directive.
    pub async fn list_directive_approvals(
        &self,
        directive_id: Uuid,
    ) -> Result<JsonValue, ApiError> {
        self.get(&format!("/api/v1/directives/{}/approvals", directive_id))
            .await
    }

    /// Approve an approval request.
    pub async fn approve_directive_request(
        &self,
        directive_id: Uuid,
        approval_id: Uuid,
        response: Option<&str>,
    ) -> Result<JsonValue, ApiError> {
        #[derive(serde::Serialize)]
        #[serde(rename_all = "camelCase")]
        struct ApprovalRequest<'a> {
            response: Option<&'a str>,
        }
        let req = ApprovalRequest { response };
        self.post(
            &format!(
                "/api/v1/directives/{}/approvals/{}/approve",
                directive_id, approval_id
            ),
            &req,
        )
        .await
    }

    /// Deny an approval request.
    pub async fn deny_directive_request(
        &self,
        directive_id: Uuid,
        approval_id: Uuid,
        response: Option<&str>,
    ) -> Result<JsonValue, ApiError> {
        #[derive(serde::Serialize)]
        #[serde(rename_all = "camelCase")]
        struct ApprovalRequest<'a> {
            response: Option<&'a str>,
        }
        let req = ApprovalRequest { response };
        self.post(
            &format!(
                "/api/v1/directives/{}/approvals/{}/deny",
                directive_id, approval_id
            ),
            &req,
        )
        .await
    }

    // =========================================================================
    // Chain operations
    // =========================================================================

    /// Force chain regeneration (replan).
    pub async fn replan_directive_chain(
        &self,
        directive_id: Uuid,
    ) -> Result<JsonValue, ApiError> {
        self.post_empty(&format!(
            "/api/v1/directives/{}/chain/replan",
            directive_id
        ))
        .await
    }

    // =========================================================================
    // Step management
    // =========================================================================

    /// Add a step to a directive's chain.
    pub async fn add_directive_step(
        &self,
        directive_id: Uuid,
        name: &str,
        description: Option<&str>,
        step_type: Option<&str>,
        depends_on: Option<Vec<Uuid>>,
    ) -> Result<JsonValue, ApiError> {
        #[derive(serde::Serialize)]
        #[serde(rename_all = "camelCase")]
        struct AddStepReq<'a> {
            name: &'a str,
            #[serde(skip_serializing_if = "Option::is_none")]
            description: Option<&'a str>,
            #[serde(skip_serializing_if = "Option::is_none")]
            step_type: Option<&'a str>,
            #[serde(skip_serializing_if = "Option::is_none")]
            depends_on: Option<Vec<Uuid>>,
        }
        let req = AddStepReq {
            name,
            description,
            step_type,
            depends_on,
        };
        self.post(
            &format!("/api/v1/directives/{}/chain/steps", directive_id),
            &req,
        )
        .await
    }

    /// Get a step by ID.
    pub async fn get_directive_step(
        &self,
        directive_id: Uuid,
        step_id: Uuid,
    ) -> Result<JsonValue, ApiError> {
        self.get(&format!(
            "/api/v1/directives/{}/chain/steps/{}",
            directive_id, step_id
        ))
        .await
    }

    /// Update a step.
    pub async fn update_directive_step(
        &self,
        directive_id: Uuid,
        step_id: Uuid,
        update: serde_json::Value,
    ) -> Result<JsonValue, ApiError> {
        self.put(
            &format!(
                "/api/v1/directives/{}/chain/steps/{}",
                directive_id, step_id
            ),
            &update,
        )
        .await
    }

    /// Delete a step.
    pub async fn delete_directive_step(
        &self,
        directive_id: Uuid,
        step_id: Uuid,
    ) -> Result<(), ApiError> {
        self.delete(&format!(
            "/api/v1/directives/{}/chain/steps/{}",
            directive_id, step_id
        ))
        .await
    }

    /// Skip a step.
    pub async fn skip_directive_step(
        &self,
        directive_id: Uuid,
        step_id: Uuid,
    ) -> Result<JsonValue, ApiError> {
        self.post_empty(&format!(
            "/api/v1/directives/{}/chain/steps/{}/skip",
            directive_id, step_id
        ))
        .await
    }

    /// Force re-evaluation of a step.
    pub async fn evaluate_directive_step(
        &self,
        directive_id: Uuid,
        step_id: Uuid,
    ) -> Result<JsonValue, ApiError> {
        self.post_empty(&format!(
            "/api/v1/directives/{}/chain/steps/{}/evaluate",
            directive_id, step_id
        ))
        .await
    }

    /// Trigger manual rework for a step.
    pub async fn rework_directive_step(
        &self,
        directive_id: Uuid,
        step_id: Uuid,
        instructions: Option<&str>,
    ) -> Result<JsonValue, ApiError> {
        #[derive(serde::Serialize)]
        #[serde(rename_all = "camelCase")]
        struct ReworkReq<'a> {
            instructions: Option<&'a str>,
        }
        let req = ReworkReq { instructions };
        self.post(
            &format!(
                "/api/v1/directives/{}/chain/steps/{}/rework",
                directive_id, step_id
            ),
            &req,
        )
        .await
    }

    // =========================================================================
    // Evaluations
    // =========================================================================

    /// List evaluations for a directive.
    pub async fn list_directive_evaluations(
        &self,
        directive_id: Uuid,
    ) -> Result<JsonValue, ApiError> {
        self.get(&format!(
            "/api/v1/directives/{}/evaluations",
            directive_id
        ))
        .await
    }

    // =========================================================================
    // Verifiers
    // =========================================================================

    /// List verifiers for a directive.
    pub async fn list_directive_verifiers(
        &self,
        directive_id: Uuid,
    ) -> Result<JsonValue, ApiError> {
        self.get(&format!("/api/v1/directives/{}/verifiers", directive_id))
            .await
    }

    /// Add a verifier to a directive.
    pub async fn add_directive_verifier(
        &self,
        directive_id: Uuid,
        name: &str,
        verifier_type: &str,
        command: Option<&str>,
    ) -> Result<JsonValue, ApiError> {
        #[derive(serde::Serialize)]
        #[serde(rename_all = "camelCase")]
        struct CreateVerifierReq<'a> {
            name: &'a str,
            verifier_type: &'a str,
            #[serde(skip_serializing_if = "Option::is_none")]
            command: Option<&'a str>,
        }
        let req = CreateVerifierReq {
            name,
            verifier_type,
            command,
        };
        self.post(
            &format!("/api/v1/directives/{}/verifiers", directive_id),
            &req,
        )
        .await
    }

    /// Update a verifier.
    pub async fn update_directive_verifier(
        &self,
        directive_id: Uuid,
        verifier_id: Uuid,
        update: serde_json::Value,
    ) -> Result<JsonValue, ApiError> {
        self.put(
            &format!(
                "/api/v1/directives/{}/verifiers/{}",
                directive_id, verifier_id
            ),
            &update,
        )
        .await
    }

    /// Auto-detect verifiers based on repository content.
    pub async fn auto_detect_directive_verifiers(
        &self,
        directive_id: Uuid,
    ) -> Result<JsonValue, ApiError> {
        self.post_empty(&format!(
            "/api/v1/directives/{}/verifiers/auto-detect",
            directive_id
        ))
        .await
    }

    // =========================================================================
    // Requirements & Spec
    // =========================================================================

    /// Update directive requirements.
    pub async fn update_directive_requirements(
        &self,
        directive_id: Uuid,
        requirements: serde_json::Value,
    ) -> Result<JsonValue, ApiError> {
        #[derive(serde::Serialize)]
        #[serde(rename_all = "camelCase")]
        struct UpdateReq {
            requirements: serde_json::Value,
        }
        let req = UpdateReq { requirements };
        self.put(
            &format!("/api/v1/directives/{}/requirements", directive_id),
            &req,
        )
        .await
    }

    /// Update directive acceptance criteria.
    pub async fn update_directive_criteria(
        &self,
        directive_id: Uuid,
        acceptance_criteria: serde_json::Value,
    ) -> Result<JsonValue, ApiError> {
        #[derive(serde::Serialize)]
        #[serde(rename_all = "camelCase")]
        struct UpdateReq {
            acceptance_criteria: serde_json::Value,
        }
        let req = UpdateReq { acceptance_criteria };
        self.put(
            &format!("/api/v1/directives/{}/criteria", directive_id),
            &req,
        )
        .await
    }

    /// Generate a specification from the directive's goal.
    pub async fn generate_directive_spec(
        &self,
        directive_id: Uuid,
    ) -> Result<JsonValue, ApiError> {
        self.post_empty(&format!(
            "/api/v1/directives/{}/generate-spec",
            directive_id
        ))
        .await
    }
}