sdd-layer 0.21.5

Spec-Driven Development CLI and agent harness
use anyhow::{bail, Result};
use serde::Serialize;

#[derive(Clone, Debug, Serialize)]
pub struct AgentManifest {
    pub id: &'static str,
    pub role: &'static str,
    pub stages: &'static [&'static str],
    pub skills: &'static [&'static str],
    pub commands: &'static [&'static str],
    pub mcp_tools: &'static [&'static str],
    pub permissions: &'static [&'static str],
    pub provider_policy: &'static str,
    pub worktree_policy: &'static str,
    pub checkpoints: &'static [&'static str],
    pub inputs: &'static [&'static str],
    pub outputs: &'static [&'static str],
    pub fallback: &'static str,
}

pub fn agent_manifest(agent_id: &str) -> Result<AgentManifest> {
    match agent_id {
        "sdd-orchestrator" => Ok(sdd_orchestrator_manifest()),
        other => bail!("unknown SDD agent `{other}`"),
    }
}

pub fn sdd_orchestrator_manifest() -> AgentManifest {
    AgentManifest {
        id: "sdd-orchestrator",
        role: "Agente principal do SDD Layer para conduzir demandas por Discovery, Risk, Idea, PRD, Tech Spec, Tasks, Refinement opcional, Execution, ADR, Review e Memory.",
        stages: &[
            "project-discovery",
            "risk",
            "idea",
            "prd",
            "techspec",
            "tasks",
            "refinement",
            "execution",
            "adr",
            "review",
            "memory",
        ],
        skills: &[
            "orchestration",
            "output-quality",
            "provider-compatibility",
            "prompt-craft",
            "humanizer",
            "product-discovery-frameworks",
            "project-discovery",
            "risk-classifier",
            "code-intelligence",
            "traceability",
            "checkpoints",
            "execution-discipline",
            "code-review",
            "frontend-design",
            "interface-design",
            "visual-review",
            "design-extraction",
            "impeccable-design",
            "systematic-debugging",
            "subagent-orchestration",
            "reflexion-loop",
            "memory",
        ],
        commands: &[
            "sdd workflow run agentic-sdd-loop --input \"<demanda>\" --max-iterations 3 --json",
            "sdd orchestration \"<ideia>\" --name \"<ciclo>\"",
            "sdd context build --name \"<ciclo>\" --stage <stage> --write",
            "sdd trace summary --orchestration \"<ciclo>\" --json",
            "sdd clients doctor --strict",
            "sdd capabilities doctor --targets all",
        ],
        mcp_tools: &[
            "sdd_trace_list",
            "sdd_trace_show",
            "sdd_trace_summary",
            "sdd_artifact_status",
            "sdd_context_build",
            "sdd_context_bundle",
            "sdd_clients_doctor",
            "sdd_project_status",
            "sdd_search",
            "sdd_runtime_adapters",
            "sdd_optimize_status",
            "sdd_context_handoff",
            "sdd_readiness_summary",
            "sdd_capabilities_status",
            "sdd_agents_manifest",
        ],
        permissions: &[
            "read artifacts",
            "read traces",
            "read context packs",
            "read capability catalog",
            "request explicit human checkpoint approval",
        ],
        provider_policy: "Provider-neutral: use configured providers when available, keep Markdown/CLI artifact fallback, never copy secrets into prompts, traces, artifacts or memory.",
        worktree_policy: "Isolamento config-driven via `orchestration.isolation` no sdd.config.yaml: `local` (default — checkout normal no branch da orquestração), `worktree` (cópias isoladas em `.worktree/<key>/`) ou `cloud` (reservado, ainda sem integração — o `next` falha se selecionado). NÃO pergunte ao usuário a cada orquestração: rode `sdd orchestration next \"$ARGUMENTS\"` direto; use `--worktree yes` ou `--worktree no` somente como override pontual quando o usuário pedir explicitamente. O destino dos artefatos vem de `orchestration.artifacts` (`local` default em docs/<slug>/, ou `cloud` para providers integrados — Jira/Confluence/GitHub — com o Markdown local como espelho).",
        checkpoints: &[
            "prd",
            "techspec",
            "refinement when risk requires",
            "merge",
            "deploy when risk requires",
        ],
        inputs: &[
            "texto livre",
            "card id",
            "webhook payload",
            "artifact store local",
            "context pack",
            "trace summary",
        ],
        outputs: &[
            "docs/<slug>/traceability-map.yaml",
            "docs/<slug>/<stage>.md",
            ".sdd/events.jsonl",
            ".sdd/workflows/*.json",
            ".sdd/acp/sessions/*.json",
        ],
        fallback: "Quando ACP, MCP, subagents ou tool calling estiverem indisponíveis, rode comandos `sdd` pelo terminal e persista artefatos Markdown em `docs/<slug>/`.",
    }
}

pub fn sdd_orchestrator_agent_markdown() -> String {
    let manifest = sdd_orchestrator_manifest();
    render_agent_manifest_markdown(&manifest)
}

pub fn render_agent_manifest_markdown(manifest: &AgentManifest) -> String {
    let mut out = String::new();
    out.push_str("---\n");
    out.push_str(&format!("id: {}\n", manifest.id));
    out.push_str("kind: agent\n");
    out.push_str("protocols:\n");
    out.push_str("  - sdd-cli\n");
    out.push_str("  - mcp-read-only\n");
    out.push_str("  - acp-v1\n");
    out.push_str("---\n\n");
    out.push_str(&format!("# Agent - {}\n\n", manifest.id));
    out.push_str("## Role\n\n");
    out.push_str(manifest.role);
    out.push_str("\n\n## Stages\n\n");
    push_list(&mut out, manifest.stages);
    out.push_str("\n## Skills\n\n");
    push_list(&mut out, manifest.skills);
    out.push_str("\n## Commands\n\n");
    push_list(&mut out, manifest.commands);
    out.push_str("\n## MCP Tools\n\n");
    push_list(&mut out, manifest.mcp_tools);
    out.push_str("\n## Permissions\n\n");
    push_list(&mut out, manifest.permissions);
    out.push_str("\n## Provider Policy\n\n");
    out.push_str(manifest.provider_policy);
    out.push_str("\n\n## Isolamento de execução e destino de artefatos (config-driven)\n\n");
    out.push_str(manifest.worktree_policy);
    out.push_str("\n\n## Checkpoints\n\n");
    push_list(&mut out, manifest.checkpoints);
    out.push_str("\n## Inputs\n\n");
    push_list(&mut out, manifest.inputs);
    out.push_str("\n## Outputs\n\n");
    push_list(&mut out, manifest.outputs);
    out.push_str("\n## Fallback\n\n");
    out.push_str(manifest.fallback);
    out.push('\n');
    out
}

fn push_list(out: &mut String, items: &[&str]) {
    for item in items {
        out.push_str(&format!("- `{item}`\n"));
    }
}