dwiki 2026.3.1

Unofficial CLI for the DeepWiki MCP server — query repository documentation from your terminal or coding agent
use clap::{Parser, Subcommand, ValueEnum};

const AFTER_HELP: &str = "\
EXAMPLES:
    # Check whether a repo is indexed before spending tokens
    dwiki check tokio-rs/tokio

    # List all available wiki topics
    dwiki read tokio-rs/tokio

    # Read a specific topic
    dwiki read tokio-rs/tokio \"Runtime\"

    # Ask a free-form question (AI-powered)
    dwiki ask tokio-rs/tokio \"How does the scheduler work?\"

    # Search the docs for a keyword
    dwiki search tokio-rs/tokio \"spawn_blocking\"

    # Machine-readable JSON output (recommended for agents)
    dwiki check tokio-rs/tokio --output json
    dwiki ask tokio-rs/tokio \"What is a JoinHandle?\" --output json

    # Private repo via Devin endpoint
    dwiki --url https://mcp.devin.ai/mcp --token $TOKEN ask myorg/repo \"...\"

ENVIRONMENT:
    DEEPWIKI_URL     Override MCP server URL (default: https://mcp.deepwiki.com/mcp)
    DEEPWIKI_TOKEN   Bearer token for private-repo endpoints

DISCLAIMER:
    dwiki is an unofficial, third-party tool and is not affiliated with or
    endorsed by DeepWiki or Cognition AI. Use at your own risk.";

#[derive(Parser)]
#[command(
    name = "dwiki",
    about = "Unofficial CLI for the DeepWiki MCP server",
    long_about = "\
dwiki is an unofficial CLI tool that wraps the DeepWiki MCP server \
(https://mcp.deepwiki.com). It is designed for use inside coding agents \
and developer toolchains that need programmatic access to repository \
documentation.\n\
\n\
It exposes three DeepWiki MCP tools as subcommands:\n\
  read_wiki_structure  →  dwiki read <repo>\n\
  read_wiki_contents   →  dwiki read <repo> <topic>\n\
  ask_question         →  dwiki ask <repo> <question>\n\
\n\
The `search` subcommand is a convenience wrapper around ask_question.\n\
The `check` subcommand performs an HTTP GET to https://deepwiki.com/<repo>\n\
to verify index status before making MCP calls.\n\
\n\
This tool is NOT affiliated with DeepWiki or Cognition AI.",
    version,
    after_help = AFTER_HELP
)]
pub struct Cli {
    /// MCP server URL
    ///
    /// Streamable HTTP endpoint of the DeepWiki MCP server.
    /// Override with DEEPWIKI_URL to point at a different instance
    /// (e.g. https://mcp.devin.ai/mcp for private repositories).
    #[arg(
        long,
        global = true,
        env = "DEEPWIKI_URL",
        default_value = "https://mcp.deepwiki.com/mcp",
        value_name = "URL"
    )]
    pub url: String,

    /// Bearer token for authentication
    ///
    /// Required when using private-repository endpoints such as
    /// https://mcp.devin.ai/mcp. Pass via DEEPWIKI_TOKEN to avoid
    /// leaking credentials in shell history.
    #[arg(
        long,
        global = true,
        env = "DEEPWIKI_TOKEN",
        value_name = "TOKEN"
    )]
    pub token: Option<String>,

    /// Output format [default: text]
    ///
    /// Use `json` for structured, machine-readable output that is easy
    /// to parse in agent pipelines. All commands support both formats.
    #[arg(
        long,
        global = true,
        default_value = "text",
        value_enum,
        value_name = "FORMAT"
    )]
    pub output: OutputFormat,

    /// Request timeout in seconds [default: 30]
    ///
    /// AI-powered `ask` queries can take 20–40 seconds for complex
    /// questions. Set to 60 or higher when running in slow networks.
    #[arg(
        long,
        global = true,
        default_value = "30",
        value_name = "SECONDS"
    )]
    pub timeout: u64,

    #[command(subcommand)]
    pub command: Commands,
}

#[derive(Subcommand)]
pub enum Commands {
    /// Ask an AI-powered question about a repository  [MCP: ask_question]
    ///
    /// Sends <question> to the DeepWiki `ask_question` MCP tool and
    /// prints the answer. The response is generated by an AI model that
    /// has indexed the repository's source code and documentation.
    ///
    /// Best for:
    ///   - Understanding how a specific feature works
    ///   - Locating where something is implemented
    ///   - Debugging or root-cause analysis
    ///
    /// Examples:
    ///   dwiki ask tokio-rs/tokio "How does spawn_blocking differ from spawn?"
    ///   dwiki ask rust-lang/rust "Where is the borrow checker implemented?" --output json
    Ask {
        /// Repository to query, in "owner/repo" format
        ///
        /// Must be a public GitHub repository already indexed by DeepWiki.
        /// Run `dwiki check <repo>` first to confirm it is indexed.
        #[arg(value_name = "OWNER/REPO")]
        repo: String,

        /// Question to ask (free-form natural language)
        #[arg(value_name = "QUESTION")]
        question: String,
    },

    /// Read wiki structure or a specific page  [MCP: read_wiki_structure / read_wiki_contents]
    ///
    /// Without <topic>: calls read_wiki_structure and returns a list of
    ///   all documentation topics available for the repository.
    ///
    /// With <topic>: calls read_wiki_contents and returns the full
    ///   documentation page for that topic.
    ///
    /// Best for:
    ///   - Discovering what documentation exists (no-topic form)
    ///   - Loading deterministic, full-text documentation pages
    ///   - Faster and cheaper than `ask` when you know the topic name
    ///
    /// Examples:
    ///   dwiki read tokio-rs/tokio
    ///   dwiki read tokio-rs/tokio "Getting Started"
    ///   dwiki read tokio-rs/tokio "Runtime" --output json
    Read {
        /// Repository to query, in "owner/repo" format
        #[arg(value_name = "OWNER/REPO")]
        repo: String,

        /// Topic name to read (omit to list all topics)
        ///
        /// Topic names are returned by `dwiki read <repo>` (no topic).
        /// Enclose multi-word topics in quotes: "Getting Started".
        #[arg(value_name = "TOPIC")]
        topic: Option<String>,
    },

    /// Check whether a repository is indexed on DeepWiki
    ///
    /// Sends an HTTP GET request to https://deepwiki.com/<repo> and
    /// reports whether the page returns HTTP 200 (indexed) or not.
    ///
    /// Exit codes:
    ///   0  — repository IS indexed; safe to call ask/read/search
    ///   1  — repository is NOT indexed (or network error)
    ///
    /// Best for:
    ///   - Guard-clause before running expensive `ask` calls
    ///   - CI/CD checks to detect when a new repo has been indexed
    ///
    /// Examples:
    ///   dwiki check tokio-rs/tokio
    ///   dwiki check myorg/newrepo --output json
    Check {
        /// Repository to check, in "owner/repo" format
        #[arg(value_name = "OWNER/REPO")]
        repo: String,
    },

    /// Search repository documentation for a keyword or concept  [MCP: ask_question]
    ///
    /// Wraps `ask_question` with a search-oriented system prompt that
    /// asks the model to enumerate all matching topics, functions,
    /// modules, and documentation sections.
    ///
    /// Prefer `ask` for targeted questions and `search` for open-ended
    /// keyword exploration.
    ///
    /// Examples:
    ///   dwiki search tokio-rs/tokio "channel"
    ///   dwiki search rust-lang/rust "lifetime elision" --output json
    Search {
        /// Repository to search, in "owner/repo" format
        #[arg(value_name = "OWNER/REPO")]
        repo: String,

        /// Keyword, symbol name, or concept to search for
        #[arg(value_name = "QUERY")]
        query: String,
    },
}

#[derive(Clone, ValueEnum, Debug, PartialEq)]
pub enum OutputFormat {
    /// Plain human-readable text (default)
    Text,
    /// Structured JSON — recommended for agent pipelines
    Json,
}