codetether-agent 4.7.0-a-002.4

A2A-native AI coding agent for the CodeTether ecosystem
Documentation
use std::path::Path;

use serde_json::{Value, json};

use super::ScoreInput;
use crate::tool::Tool;
use crate::tool::tetherscript::TetherScriptPluginTool;

const SCRIPT: &str = ".codetether/forage_score.tether";

pub(super) fn score_with_tetherscript(input: &ScoreInput<'_>) -> Option<f64> {
    if !Path::new(SCRIPT).exists() {
        return None;
    }
    tokio::task::block_in_place(|| {
        tokio::runtime::Handle::current().block_on(async { run_script(input).await })
    })
}

async fn run_script(input: &ScoreInput<'_>) -> Option<f64> {
    let result = TetherScriptPluginTool::new()
        .execute(json!({ "path": SCRIPT, "hook": "score", "args": [event(input)] }))
        .await
        .ok()?;
    result.metadata.get("value").and_then(score_value)
}

fn event(input: &ScoreInput<'_>) -> Value {
    json!({
        "base_score": input.base_score,
        "remaining": input.remaining,
        "moonshot_alignment": input.moonshot_alignment,
        "okr": { "id": input.okr.id, "title": input.okr.title },
        "key_result": { "id": input.kr.id, "title": input.kr.title }
    })
}

fn score_value(value: &Value) -> Option<f64> {
    match value {
        Value::Number(number) => number.as_f64(),
        Value::Object(map) => map.get("ok").and_then(Value::as_f64),
        _ => None,
    }
}