murliv1.0.2
{ } github
§ Guides

Quickstart

A minimal working integration for each language and framework. The only change from a standard application is replacing one function call.

gomain.go · cobra
package main

import (
	"github.com/murli-cli/murli-go"
	murliCobra "github.com/murli-cli/murli-go/cobra"
	"github.com/spf13/cobra"
)

var queryCmd = &cobra.Command{
	Use:   "query <text>",
	Short: "Search the index",
	RunE: func(cmd *cobra.Command, args []string) error {
		w := murliCobra.NewWriter(cmd)
		w.Progress("searching...")
		results := []map[string]any{
			{"path": "/docs/woodworking", "score": 0.95},
		}
		w.WriteSuccess("Found 1 result", results)
		return nil
	},
}

func main() {
	root := &cobra.Command{Use: "mytool"}
	root.AddCommand(queryCmd)
	murliCobra.Annotate(queryCmd, murli.Metadata{
		AgentDescription: "Search the document index.",
		Idempotent:       true,
	})
	_ = murliCobra.Execute(root) // replaces root.Execute()
}
gomain.go · urfave/cli v2
package main

import (
	"os"
	"github.com/murli-cli/murli-go"
	murliCli "github.com/murli-cli/murli-go/cli/v2"
	"github.com/urfave/cli/v2"
)

func main() {
	queryCmd := &cli.Command{
		Name:  "query",
		Usage: "Search the index",
		Action: func(ctx *cli.Context) error {
			w := murliCli.NewWriter(ctx)
			w.Progress("searching...")
			results := []map[string]any{
				{"path": "/docs/woodworking", "score": 0.95},
			}
			w.WriteSuccess("Found 1 result", results)
			return nil
		},
	}
	murliCli.Annotate(queryCmd, murli.Metadata{AgentDescription: "Search the document index.", Idempotent: true})
	app := &cli.App{Name: "mytool", Commands: []*cli.Command{queryCmd}}
	_ = murliCli.Run(app, os.Args) // replaces app.Run(os.Args)
}
gomain.go · urfave/cli v3
package main

import (
	"context"
	"os"
	"github.com/murli-cli/murli-go"
	murliCli "github.com/murli-cli/murli-go/cli/v3"
	"github.com/urfave/cli/v3"
)

func main() {
	queryCmd := &cli.Command{
		Name:  "query",
		Usage: "Search the index",
		Action: func(ctx context.Context, cmd *cli.Command) error {
			w := murliCli.NewWriter(cmd)
			w.Progress("searching...")
			results := []map[string]any{
				{"path": "/docs/woodworking", "score": 0.95},
			}
			w.WriteSuccess("Found 1 result", results)
			return nil
		},
	}
	murliCli.Annotate(queryCmd, murli.Metadata{AgentDescription: "Search the document index.", Idempotent: true})
	app := &cli.Command{Name: "mytool", Commands: []*cli.Command{queryCmd}}
	_ = murliCli.Run(app, os.Args) // replaces app.Run(context.Background(), os.Args)
}
rustmain.rs · clap (derive)
use clap::{CommandFactory, Parser};
use murli::clap::GlobalArgs;
use murli::Writer;
use serde_json::json;

#[derive(Parser)]
#[command(name = "mytool", about = "My search tool")]
struct Cli {
    #[command(flatten)]
    murli: GlobalArgs,       // --agent --schema --force --dry-run --output --profile
    #[command(subcommand)]
    command: Commands,
}

#[derive(clap::Subcommand)]
enum Commands {
    Query { text: String },
}

fn main() {
    let args = Cli::parse();
    murli::clap::handle_builtins(&args.murli, &Cli::command(), None);
    let mut writer = Writer::from_args(&args.murli);

    match args.command {
        Commands::Query { text } => {
            writer.log("searching...");
            let results = json!([{"path": "/docs/woodworking", "score": 0.95}]);
            writer.write_success("Found 1 result", &results);
        }
    }
}
tomlCargo.toml
[dependencies]
murli      = { version = "0.1", features = ["clap"] }
clap       = { version = "4", features = ["derive"] }
serde_json = "1"

See the Rust reference for the clap builder API and argh adapter.

pythoncli.py · click
import click
import murli

@click.group()
def cli(): pass

murli.enable(cli)  # adds --agent, --schema, --force, --dry-run, --output, --profile
                   # mounts describe, doctor, profile subcommands

@cli.command()
@click.argument("text")
@murli.pass_writer
def query(writer, text):
    writer.log("searching...")
    results = [{"path": "/docs/woodworking", "score": 0.95}]
    writer.write_success("Found 1 result", results)

if __name__ == "__main__":
    cli()
pythoncli.py · typer
import typer
import murli

app = typer.Typer()
murli.enable(app)

@app.command()
def query(ctx: typer.Context, text: str):
    writer = murli.get_writer(ctx)
    writer.log("searching...")
    results = [{"path": "/docs/woodworking", "score": 0.95}]
    writer.write_success("Found 1 result", results)

if __name__ == "__main__":
    app()
pythoncli.py · argparse
import argparse
import murli

parser = argparse.ArgumentParser()
murli.enable(parser)
parser.add_argument("text")

args, writer = murli.parse(parser)
writer.log("searching...")
results = [{"path": "/docs/woodworking", "score": 0.95}]
writer.write_success("Found 1 result", results)

What this gives you immediately

FeatureHow to invoke
Dual-audience outputRun at a TTY for formatted output; pipe to any command for JSON
--agentForces JSON output at a TTY
--schemaPrints command schema as JSON and exits
describeDumps full command tree: mytool describe
doctorDev-only health check (Go: -tags murlidev)
profileNamed flag profiles: mytool profile list
--force / --yesNon-interactive confirmation bypass
--dry-runDry-run mode (handler checks w.IsDryRun() / writer.is_dry_run())