basemind 0.7.0

Full AI context layer over MCP — tree-sitter code-map, document RAG (PDF/Office/HTML/email + OCR + reranker), shared agent memory, on-demand web crawl, git history + blame + per-symbol diff. 300+ languages, 10+ coding-agent harnesses, content-addressed Fjall + LanceDB.
//! Governance subcommands: `mine`, `proposals`, `accept`, `reject`.
//!
//! These dispatch through the in-process MCP server, same pattern as `cli/memory.rs`.
//! The tool shims compile regardless of the `memory` feature (returning a "feature not
//! enabled" MCP error); this CLI module therefore always compiles too.

use std::io::Write;

use anyhow::Result;
use clap::Subcommand;

use crate::mcp::BasemindServer;
use crate::mcp::params::*;

use super::render::emit;
use super::run_tool;

#[derive(Subcommand, Debug)]
pub enum GovernanceCmd {
    /// Mine co-change skill proposals from recent git history.
    Mine {
        /// Number of recent commits to inspect (default 200, max 2000).
        #[arg(long)]
        window: Option<u32>,
        /// Minimum co-change count for a pair to be emitted (default 5).
        #[arg(long)]
        min_support: Option<u32>,
        /// Minimum confidence (support / anchor_freq) for a pair (default 0.6).
        #[arg(long)]
        min_confidence: Option<f32>,
        /// Skip commits touching more than N files (default 25).
        #[arg(long)]
        max_files_per_commit: Option<u32>,
    },
    /// List pending governance proposals.
    Proposals {
        /// Filter by kind: `skill` or `memory` (default: all).
        #[arg(long)]
        kind: Option<String>,
        /// Maximum results to return (default 100).
        #[arg(long)]
        limit: Option<u32>,
    },
    /// Accept a proposal and promote it to a searchable skill memory.
    Accept {
        /// Proposal id (as returned by `proposals`).
        id: String,
        /// Override the auto-derived memory key.
        #[arg(long)]
        key: Option<String>,
    },
    /// Reject a proposal and suppress it from future mining runs.
    Reject {
        /// Proposal id (as returned by `proposals`).
        id: String,
        /// Optional human-readable reason (logged only, not persisted).
        #[arg(long)]
        reason: Option<String>,
    },
}

pub async fn run(
    server: &BasemindServer,
    cmd: GovernanceCmd,
    json: bool,
    out: &mut impl Write,
) -> Result<()> {
    match cmd {
        GovernanceCmd::Mine {
            window,
            min_support,
            min_confidence,
            max_files_per_commit,
        } => {
            let p = ProposalsMineParams {
                window,
                min_support,
                min_confidence,
                max_files_per_commit,
            };
            let r = run_tool("proposals_mine", server.proposals_mine(Parameters(p)).await)?;
            emit("proposals_mine", &r, json, out)
        }
        GovernanceCmd::Proposals { kind, limit } => {
            let p = ProposalsListParams {
                kind,
                limit,
                cursor: None,
            };
            let r = run_tool("proposals_list", server.proposals_list(Parameters(p)).await)?;
            emit("proposals_list", &r, json, out)
        }
        GovernanceCmd::Accept { id, key } => {
            let p = ProposalAcceptParams { id, key };
            let r = run_tool(
                "proposal_accept",
                server.proposal_accept(Parameters(p)).await,
            )?;
            emit("proposal_accept", &r, json, out)
        }
        GovernanceCmd::Reject { id, reason } => {
            let p = ProposalRejectParams { id, reason };
            let r = run_tool(
                "proposal_reject",
                server.proposal_reject(Parameters(p)).await,
            )?;
            emit("proposal_reject", &r, json, out)
        }
    }
}