incur

package module
v0.0.0-...-dbeecf7 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 3, 2026 License: MIT Imports: 29 Imported by: 0

README

incur

Go Reference MIT License

Features . Quickprompt . Install . Usage . Walkthrough . API Reference . License

incur for Go

incur is a Go framework for building agent-friendly command-line tools.

This repository is a third-party, unaffiliated Go fork of wevm/incur, the original TypeScript framework. The design, naming, and product ideas are upstream's work. This fork exists so Go programs can use the same style of CLI without depending on Node.

It was rewritten from TypeScript to Go by an LLM and is maintained as production software for real use. Upstream TypeScript behavior is ported or mapped at case level, with parity tracked in TEST_PARITY.md and TEST_PARITY_CASES.md.

If you want the original TypeScript project, use upstream: https://github.com/wevm/incur.

Features

  • Agent discovery: built-in skills add, skills list, mcp add, --llms, and --llms-full so agents can discover your CLI without hand-written tool definitions.
  • Session savings: skill splitting, command manifests, and TOON output keep token use low across discovery, invocation, and responses.
  • Call-to-actions: return suggested next commands after a run so agents can continue without guessing.
  • TOON output: token-efficient default output format, with JSON, YAML, Markdown, and JSONL alternatives.
  • Well-formed I/O: JSON Schemas for positional arguments, options, environment variables, and output.
  • Global options: --format, --full-output, --help, --json, --schema, token pagination, filtering, --llms, --mcp, and --version.
  • HTTP gateway: mount any net/http handler behind a CLI command with curl-style flags.
  • Serve CLIs as APIs: expose CLI commands as a standard http.Handler, including MCP over HTTP at /mcp.
  • OpenAPI import: generate commands, args, options, and descriptions from an OpenAPI 3 spec.
  • Streaming: stream strings, objects, final CTAs, and errors from channel-based command handlers.
  • Middleware: composable before/after hooks with shared vars and typed-by-schema validation.
  • Config files: load structured JSON option defaults from project or user config.
  • Light Go API surface: incur.New(), .Command(), .Group(), .Serve(), .Handler(), and .Fetch().

Quickprompt

After you build a CLI with incur, prompt your agent with one of these.

Skills (recommended - lighter on tokens)

Run `<your-cli> skills add`, then show me how to use this CLI.

MCP

Run `<your-cli> mcp add`, then use the registered MCP tools when working with this project.

Manifest only

Run `<your-cli> --llms`, inspect the command manifest, then suggest the right command for my task.

Install

Requires Go 1.25 or newer.

go get github.com/smithersai/incur

Install the helper binary for schema/code generation:

go install github.com/smithersai/incur/cmd/incur@latest

Usage

Root-command CLI

Use WithRootCommand for CLIs that do one thing.

package main

import (
	"os"

	"github.com/smithersai/incur"
)

func main() {
	cli := incur.New("greet",
		incur.WithDescription("A greeting CLI"),
		incur.WithRootCommand(&incur.CommandDef{
			Description: "Greet someone",
			ArgsSchema: &incur.JSONSchema{
				Type: "object",
				Properties: map[string]*incur.JSONSchema{
					"name": {Type: "string", Description: "Name to greet"},
				},
				Required: []string{"name"},
				XOrder:   []string{"name"},
			},
			Handler: func(ctx *incur.CommandContext) (any, error) {
				return map[string]any{"message": "hello " + ctx.Args["name"].(string)}, nil
			},
		}),
	)

	if err := cli.Serve(os.Args[1:]); err != nil {
		os.Exit(1)
	}
}
$ greet world
# message: hello world
Multi-command CLI

Register subcommands with Command.

package main

import (
	"os"

	"github.com/smithersai/incur"
)

func main() {
	cli := incur.New("my-cli", incur.WithDescription("My CLI"))

	cli.Command("status", &incur.CommandDef{
		Description: "Show repo status",
		Handler: func(ctx *incur.CommandContext) (any, error) {
			return map[string]any{"clean": true}, nil
		},
	})

	cli.Command("install", &incur.CommandDef{
		Description: "Install a package",
		ArgsSchema: &incur.JSONSchema{
			Type: "object",
			Properties: map[string]*incur.JSONSchema{
				"package": {Type: "string", Description: "Package name"},
			},
			XOrder: []string{"package"},
		},
		OptionsSchema: &incur.JSONSchema{
			Type: "object",
			Properties: map[string]*incur.JSONSchema{
				"saveDev": {Type: "boolean", Description: "Save as dev dependency"},
			},
		},
		Alias: map[string]string{"saveDev": "D"},
		Handler: func(ctx *incur.CommandContext) (any, error) {
			return map[string]any{"added": 1, "packages": 451}, nil
		},
	})

	if err := cli.Serve(os.Args[1:]); err != nil {
		os.Exit(1)
	}
}
$ my-cli status
# clean: true

$ my-cli install express -D
# added: 1
# packages: 451
Sub-command CLI

Create a separate Cli and mount it with Group.

package main

import (
	"os"

	"github.com/smithersai/incur"
)

func main() {
	root := incur.New("my-cli", incur.WithDescription("My CLI"))

	pr := incur.New("pr", incur.WithDescription("Pull request commands"))
	pr.Command("list", &incur.CommandDef{
		Description: "List pull requests",
		OptionsSchema: &incur.JSONSchema{
			Type: "object",
			Properties: map[string]*incur.JSONSchema{
				"state": {
					Type:        "string",
					Enum:        []any{"open", "closed", "all"},
					Default:     "open",
					Description: "Pull request state",
				},
			},
		},
		Handler: func(ctx *incur.CommandContext) (any, error) {
			return map[string]any{
				"prs":   []any{},
				"state": ctx.Options["state"],
			}, nil
		},
	})

	root.Group("pr", pr)

	if err := root.Serve(os.Args[1:]); err != nil {
		os.Exit(1)
	}
}
$ my-cli pr list --state closed
# prs: []
# state: closed
Mount APIs as CLIs

Mount an HTTP handler with FetchHandler. The CLI translates trailing args and curl-style flags into HTTP requests.

package main

import (
	"encoding/json"
	"net/http"
	"os"

	"github.com/smithersai/incur"
)

func main() {
	app := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json")

		if r.Method == http.MethodGet && r.URL.Path == "/users" {
			_ = json.NewEncoder(w).Encode(map[string]any{
				"users": []map[string]any{{"id": 1, "name": "Alice"}},
			})
			return
		}

		if r.Method == http.MethodPost && r.URL.Path == "/users" {
			var body map[string]any
			_ = json.NewDecoder(r.Body).Decode(&body)
			_ = json.NewEncoder(w).Encode(map[string]any{"created": true, "name": body["name"]})
			return
		}

		http.NotFound(w, r)
	})

	cli := incur.New("my-cli", incur.WithDescription("My CLI"))
	cli.Command("api", &incur.CommandDef{
		Description:  "Proxy to HTTP API",
		FetchHandler: app,
	})

	if err := cli.Serve(os.Args[1:]); err != nil {
		os.Exit(1)
	}
}
$ my-cli api users
# users:
#   - id: 1
#     name: Alice

$ my-cli api users -X POST -d '{"name":"Bob"}'
# created: true
# name: Bob

Supported fetch command flags include -X/--method, -H/--header, -d/--data, and --body. Unknown long flags become query parameters, so my-cli api users --limit 5 becomes GET /users?limit=5.

FetchHandler accepts:

  • http.Handler
  • func(http.ResponseWriter, *http.Request)
  • func(*http.Request) (*http.Response, error)
  • func(*http.Request) *http.Response

Use FetchBasePath to prepend a path before forwarding:

cli.Command("api", &incur.CommandDef{
	FetchHandler:  app,
	FetchBasePath: "/api",
})
OpenAPI

Generate command definitions from an OpenAPI 3 spec with GenerateCommands.

package main

import (
	"log"
	"net/http"
	"os"

	"github.com/getkin/kin-openapi/openapi3"
	"github.com/smithersai/incur"
)

func main() {
	loader := openapi3.NewLoader()
	spec, err := loader.LoadFromFile("openapi.yaml")
	if err != nil {
		log.Fatal(err)
	}

	app := http.DefaultServeMux
	commands, err := incur.GenerateCommands(spec, app, incur.OpenAPIOptions{
		BasePath: "/api",
	})
	if err != nil {
		log.Fatal(err)
	}

	api := incur.New("api", incur.WithDescription("API commands"))
	for name, def := range commands {
		api.Command(name, def)
	}

	cli := incur.New("my-cli", incur.WithDescription("My CLI")).Group("api", api)
	if err := cli.Serve(os.Args[1:]); err != nil {
		os.Exit(1)
	}
}

If the spec has operationIds like listUsers, createUser, and getUser, users get:

$ my-cli api --help
# Commands:
#   createUser  Create a user
#   getUser     Get a user by ID
#   listUsers   List users

$ my-cli api listUsers --limit 5
$ my-cli api getUser 42
$ my-cli api createUser --name Bob
Serve CLIs as APIs

Expose a CLI as a normal http.Handler with cli.Handler().

package main

import (
	"log"
	"net/http"

	"github.com/smithersai/incur"
)

func main() {
	cli := incur.New("my-cli")
	cli.Command("users", &incur.CommandDef{
		ArgsSchema: &incur.JSONSchema{
			Type: "object",
			Properties: map[string]*incur.JSONSchema{
				"id": {Type: "number", Description: "User ID"},
			},
			XOrder: []string{"id"},
		},
		OptionsSchema: &incur.JSONSchema{
			Type: "object",
			Properties: map[string]*incur.JSONSchema{
				"limit": {Type: "number", Default: 10},
			},
		},
		Handler: func(ctx *incur.CommandContext) (any, error) {
			if ctx.Args["id"] != nil {
				return map[string]any{"id": ctx.Args["id"], "name": "Alice"}, nil
			}
			return map[string]any{
				"users": []map[string]any{{"id": 1, "name": "Alice"}},
				"limit": ctx.Options["limit"],
			}, nil
		},
	})

	log.Fatal(http.ListenAndServe(":8080", cli.Handler()))
}

Path segments map to commands and positional args, query params map to options, and POST JSON bodies map to options:

GET  /users?limit=5           -> my-cli users --limit 5
GET  /users/42                -> my-cli users 42
POST /users { "name": "Bob" } -> my-cli users --name Bob

Responses use the same JSON envelope as --full-output --format json:

{ "ok": true, "data": { "users": [] }, "meta": { "command": "users", "duration": "3ms" } }

cli.Fetch(req) runs the same handler and returns an *http.Response, which is useful in tests and in runtimes that want fetch-like request/response objects.

MCP over HTTP

The HTTP handler automatically exposes an MCP endpoint at /mcp. Non-/mcp paths keep routing to CLI commands.

POST /mcp  { "jsonrpc": "2.0", "method": "initialize", ... }
POST /mcp  { "jsonrpc": "2.0", "method": "tools/list", ... }
POST /mcp  { "jsonrpc": "2.0", "method": "tools/call", "params": { "name": "users", ... } }

Walkthrough

Agent discovery

Agents can only use your CLI if they know it exists. incur ships discovery mechanisms with every CLI:

# Generate and install agent skill files.
my-cli skills add

# List the skills incur would install for this CLI.
my-cli skills list
my-cli skills ls

# Register as an MCP server for supported agents.
my-cli mcp add

# Start as an MCP stdio server.
my-cli --mcp

# Print a compact command manifest.
my-cli --llms

# Print the full command manifest.
my-cli --llms-full

--llms defaults to Markdown. Use --format json or --format yaml for machine-readable manifests.

my-cli --llms --format json
my-cli --llms-full --format yaml
my-cli pr --llms-full
Session savings

The original incur design is built around reducing the tokens an agent spends before it can do useful work:

  • MCP exposes every tool schema up front; incur skills load only a small index until a specific command group is needed.
  • --llms gives agents a compact manifest without asking them to read a whole README.
  • Split skills keep large CLIs discoverable by group instead of as one large file.
  • TOON output removes JSON braces, repeated keys, and most quoting from command results.
  • --filter-output, --token-count, --token-limit, and --token-offset let an agent inspect large outputs without consuming the whole result at once.

The practical goal is the same as upstream TypeScript incur: agents should spend fewer tokens learning a CLI and more tokens using it correctly.

Call-to-actions

Return CTAs from ctx.OK or ctx.Error to suggest follow-up commands.

cli.Command("list", &incur.CommandDef{
	Description: "List items",
	Handler: func(ctx *incur.CommandContext) (any, error) {
		items := []map[string]any{{"id": 1, "title": "Fix bug"}}
		return ctx.OK(
			map[string]any{"items": items},
			incur.ResultOptions{
				CTA: &incur.CTABlock{
					Commands: []any{
						map[string]any{
							"command":     "get",
							"args":        map[string]any{"id": 1},
							"description": "View item",
						},
						map[string]any{
							"command":     "list",
							"options":     map[string]any{"state": "closed"},
							"description": "View closed",
						},
					},
				},
			},
		), nil
	},
})
$ my-cli list
# items:
#   - id: 1
#     title: Fix bug
# Suggested commands:
#   my-cli get 1       View item
#   my-cli list --state closed  View closed

CTA command entries can be strings or objects. Object commands may include command, args, options, and description; incur folds those into a shell command and prefixes the CLI name.

TOON output

incur defaults to TOON, a readable and token-efficient structured format.

cli.Command("hikes", &incur.CommandDef{
	Handler: func(ctx *incur.CommandContext) (any, error) {
		return map[string]any{
			"context": map[string]any{
				"task":     "Our favorite hikes together",
				"location": "Boulder",
				"season":   "spring_2025",
			},
			"friends": []string{"ana", "luis", "sam"},
			"hikes": []map[string]any{
				{"id": 1, "name": "Blue Lake Trail", "distanceKm": 7.5, "wasSunny": true},
				{"id": 2, "name": "Ridge Overlook", "distanceKm": 9.2, "wasSunny": false},
			},
		}, nil
	},
})
$ my-cli hikes
# context:
#   location: Boulder
#   season: spring_2025
#   task: Our favorite hikes together
# friends[3]: ana,luis,sam
# hikes[2]{distanceKm,id,name,wasSunny}:
#   7.5,1,Blue Lake Trail,true
#   9.2,2,Ridge Overlook,false

Switch formats with --format or --json:

my-cli hikes --format json
my-cli hikes --format yaml
my-cli hikes --format md
my-cli hikes --format jsonl
my-cli hikes --json

Set defaults globally or per-command:

cli := incur.New("my-cli", incur.WithFormat(incur.FormatJSON))

cli.Command("logs", &incur.CommandDef{
	Format:  incur.FormatJSONL,
	Handler: func(ctx *incur.CommandContext) (any, error) { return []any{}, nil },
})
Well-formed I/O

Arguments, options, environment variables, and output are described with JSON Schema.

cli.Command("deploy", &incur.CommandDef{
	Description: "Deploy an environment",
	ArgsSchema: &incur.JSONSchema{
		Type: "object",
		Properties: map[string]*incur.JSONSchema{
			"env": {
				Type:        "string",
				Enum:        []any{"staging", "production"},
				Description: "Environment to deploy",
			},
		},
		Required: []string{"env"},
		XOrder:   []string{"env"},
	},
	OptionsSchema: &incur.JSONSchema{
		Type: "object",
		Properties: map[string]*incur.JSONSchema{
			"force": {Type: "boolean", Description: "Skip safety checks"},
		},
	},
	EnvSchema: &incur.JSONSchema{
		Type: "object",
		Properties: map[string]*incur.JSONSchema{
			"DEPLOY_TOKEN": {Type: "string", Description: "Deploy token"},
		},
		Required: []string{"DEPLOY_TOKEN"},
	},
	OutputSchema: &incur.JSONSchema{
		Type: "object",
		Properties: map[string]*incur.JSONSchema{
			"url":      {Type: "string"},
			"duration": {Type: "number"},
		},
		Required: []string{"url", "duration"},
	},
	Handler: func(ctx *incur.CommandContext) (any, error) {
		return map[string]any{
			"url":      "https://" + ctx.Args["env"].(string) + ".example.com",
			"duration": 3.2,
		}, nil
	},
})

Go does not have Zod or TypeScript's generic inference, so this fork uses explicit JSONSchema structs. For positional args, set XOrder when order matters.

Reusing schemas with Zod

Use JSON Schema as the shared contract between TypeScript and Go. If a TypeScript project already owns the shape in Zod, emit JSON Schema with z.toJSONSchema() during build or codegen, commit the generated .json schema, and load that same file from Go:

argsSchema, err := incur.LoadSchema("schemas/deploy.args.json")
if err != nil {
	return err
}

cli.Command("deploy", &incur.CommandDef{
	ArgsSchema: argsSchema,
	Handler:   deploy,
})

If TypeScript also needs useful z.infer types from z.fromJSONSchema(), keep or generate a tiny .ts schema module instead of importing the .json file directly. JSON module imports cannot be marked as const, so the literal schema shape is easier to preserve in TypeScript source:

import * as z from "zod"

export const deployArgsSchema = {
  type: "object",
  properties: {
    env: { type: "string", enum: ["staging", "production"] },
  },
  required: ["env"],
  additionalProperties: false,
} as const

const DeployArgs = z.fromJSONSchema(deployArgsSchema)
type DeployArgs = z.infer<typeof DeployArgs>

Then emit the same object to schemas/deploy.args.json for Go to load.

Keep the shared subset JSON Schema-friendly: primitives, objects, arrays, enums, defaults, descriptions, required, and additionalProperties. Zod transforms, custom refinements, functions, dates, maps, and sets do not round-trip cleanly because they do not have direct JSON Schema equivalents.

Streaming

Use StreamHandler to emit chunks from a channel.

cli.Command("logs", &incur.CommandDef{
	Description: "Tail logs",
	StreamHandler: func(ctx *incur.CommandContext) (<-chan any, error) {
		ch := make(chan any)
		go func() {
			defer close(ch)
			ch <- "connecting..."
			ch <- map[string]any{"line": "streaming logs"}
			ch <- map[string]any{"line": "done"}
		}()
		return ch, nil
	},
})
$ my-cli logs
# connecting...
# line: streaming logs
# line: done

With --format json, chunks are buffered into a JSON array. With --format jsonl, each chunk is emitted as a record and the stream ends with a done record.

You can yield a final CTA or an error:

ch <- ctx.OK(nil, incur.ResultOptions{
	CTA: &incur.CTABlock{Commands: []any{"status"}},
})

ch <- ctx.Error(incur.ErrorOpts{
	Code:    "STREAM_FAILED",
	Message: "stream failed",
})
Output policy

Successful output is shown to everyone by default. Set OutputPolicyAgentOnly to hide successful data in human TTY mode while still returning it to agents, JSON callers, and HTTP/MCP consumers.

cli.Command("deploy", &incur.CommandDef{
	OutputPolicy: incur.OutputPolicyAgentOnly,
	Handler: func(ctx *incur.CommandContext) (any, error) {
		return map[string]any{"id": "deploy-123", "url": "https://staging.example.com"}, nil
	},
})

Set it at the root and override per command:

internal := incur.New("internal", incur.WithOutputPolicy(incur.OutputPolicyAgentOnly))

internal.Command("sync", &incur.CommandDef{
	Handler: func(ctx *incur.CommandContext) (any, error) {
		return map[string]any{"synced": true}, nil
	},
})

internal.Command("status", &incur.CommandDef{
	OutputPolicy: incur.OutputPolicyAll,
	Handler: func(ctx *incur.CommandContext) (any, error) {
		return map[string]any{"ok": true}, nil
	},
})
CLI name and agent detection

Command and middleware contexts include the root CLI name, display name, version, command path, selected format, and whether the current consumer looks like an agent.

cli.Command("check", &incur.CommandDef{
	Handler: func(ctx *incur.CommandContext) (any, error) {
		if !ctx.Agent {
			_, _ = os.Stderr.WriteString("Running local checks...\n")
		}

		authenticated := false // Replace with your auth check.
		if !authenticated {
			return ctx.Error(incur.ErrorOpts{
				Code:    "NOT_AUTHENTICATED",
				Message: "Not logged in. Run `" + ctx.Name + " auth login`.",
			}), nil
		}

		return map[string]any{"ok": true}, nil
	},
})
Deprecated options

Mark options as deprecated with Deprecated: true. Deprecated options show in help, skill docs, and JSON schema, and produce a TTY warning when used.

cli.Command("deploy", &incur.CommandDef{
	OptionsSchema: &incur.JSONSchema{
		Type: "object",
		Properties: map[string]*incur.JSONSchema{
			"zone":   {Type: "string", Description: "Availability zone", Deprecated: true},
			"region": {Type: "string", Description: "Target region"},
		},
	},
	Handler: func(ctx *incur.CommandContext) (any, error) {
		return map[string]any{"region": ctx.Options["region"]}, nil
	},
})
$ my-cli deploy --zone us-east-1
# Warning: --zone is deprecated
Middleware

Middleware executes in registration order, onion-style. Root and group middleware run before per-command middleware.

cli := incur.New("deploy-cli", incur.WithDescription("Deploy tools"))

cli.Use(func(ctx *incur.Context, next func() error) error {
	start := time.Now()
	err := next()
	fmt.Fprintf(os.Stderr, "took %s\n", time.Since(start))
	return err
})

cli.Command("deploy", &incur.CommandDef{
	Handler: func(ctx *incur.CommandContext) (any, error) {
		return map[string]any{"deployed": true}, nil
	},
})

Middleware can short-circuit with a structured error:

requireAuth := func(ctx *incur.Context, next func() error) error {
	if ctx.Get("user") == nil {
		ctx.Error(incur.ErrorOpts{
			Code:    "AUTH",
			Message: "must be logged in",
		})
		return nil
	}
	return next()
}

cli.Command("deploy", &incur.CommandDef{
	Middleware: []incur.MiddlewareFunc{requireAuth},
	Handler: func(ctx *incur.CommandContext) (any, error) {
		return map[string]any{"deployed": true}, nil
	},
})
Variables

Use WithVars to declare middleware variables and ctx.Set / ctx.Get to pass values to downstream middleware and command handlers.

cli := incur.New("my-cli",
	incur.WithVars(&incur.JSONSchema{
		Type: "object",
		Properties: map[string]*incur.JSONSchema{
			"user":      {Type: "string"},
			"requestId": {Type: "string"},
			"debug":     {Type: "boolean", Default: true},
		},
	}),
	incur.WithMiddleware(func(ctx *incur.Context, next func() error) error {
		ctx.Set("user", "alice")
		ctx.Set("requestId", "req_123")
		return next()
	}),
)

cli.Command("whoami", &incur.CommandDef{
	Handler: func(ctx *incur.CommandContext) (any, error) {
		return map[string]any{
			"user":      ctx.Get("user"),
			"requestId": ctx.Get("requestId"),
			"debug":     ctx.Get("debug"),
		}, nil
	},
})
Config file

Load option defaults from JSON config files with WithConfig.

cli := incur.New("my-cli",
	incur.WithConfig("my-cli.json", "~/.config/my-cli/config.json"),
)

This registers --config <path> and --no-config. First existing file wins unless overridden.

{
  "options": {
    "verbose": true
  },
  "commands": {
    "echo": {
      "options": {
        "upper": true,
        "prefix": "cfg"
      }
    },
    "project": {
      "commands": {
        "list": {
          "options": {
            "limit": 25,
            "save-dev": true
          }
        }
      }
    }
  }
}

Precedence is argv > config > schema defaults. Only command options are loaded; args, env, and built-in commands are unaffected.

Customize the flag or loader:

cli := incur.New("my-cli",
	incur.WithConfigOptions(incur.ConfigOptions{
		Files: []string{"my-cli.yaml"},
		Flag:  "--settings",
		Loader: func(path string) (map[string]any, error) {
			// Load YAML, TOML, remote config, etc.
			return map[string]any{}, nil
		},
	}),
)

Generate a JSON Schema for config files programmatically:

schema := incur.ConfigSchemaFromCli(cli)
Filtering output

Use --filter-output to prune command output to specific keys. It supports dot notation, array slices, and comma-separated paths.

cli.Command("users", &incur.CommandDef{
	Handler: func(ctx *incur.CommandContext) (any, error) {
		return map[string]any{
			"users": []map[string]any{
				{"name": "Alice", "email": "alice@example.com", "role": "admin"},
				{"name": "Bob", "email": "bob@example.com", "role": "user"},
				{"name": "Carol", "email": "carol@example.com", "role": "user"},
			},
		}, nil
	},
})
$ my-cli users --filter-output users.name
# [3]: Alice,Bob,Carol

$ my-cli users --filter-output users[0,2].name
# users[2]{name}:
#   Alice
#   Bob
Token pagination

Use --token-count, --token-limit, and --token-offset to manage large outputs. Token counts are estimates intended for agent-friendly pagination.

# Check output size.
my-cli users --token-count

# Limit output to the first page.
my-cli users --token-limit 200

# Read the next page.
my-cli users --token-offset 200 --token-limit 200
Command schema

Use --schema to inspect a command's JSON Schema.

my-cli install --schema
my-cli install --schema --format json
my-cli pr list --schema --format yaml

The schema output includes arguments, options, environment variables, and output when declared.

Shell completions

Every incur CLI has a built-in completions command.

# bash
eval "$(my-cli completions bash)"

# zsh
eval "$(my-cli completions zsh)"

# fish
my-cli completions fish | source

# nushell
my-cli completions nushell

Completions are dynamic: commands, command groups, aliases, options, short aliases, and enum values are suggested from the registered command tree.

Global options

Every incur CLI includes global options:

Flag Description
--filter-output <keys> Filter output by key paths such as foo,bar.baz,a[0,3]
--format <toon|json|yaml|md|jsonl> Select output format
--full-output Include the full envelope: ok, data or error, and meta
--help Show help
--json Shorthand for --format json
--llms Print a compact LLM-readable command manifest
--llms-full Print the full LLM-readable command manifest
--mcp Start as an MCP stdio server
--schema Show JSON Schema for a command
--token-count Print token count instead of output
--token-limit <n> Limit output to n tokens
--token-offset <n> Skip the first n output tokens
--version Show CLI version
--config <path> Load JSON option defaults when config is enabled
--no-config Disable config loading when config is enabled
Errors

Return structured errors with ctx.Error:

return ctx.Error(incur.ErrorOpts{
	Code:      "NOT_AUTHENTICATED",
	Message:   "must be logged in",
	Retryable: false,
	CTA: &incur.CTABlock{
		Description: "Authenticate to continue:",
		Commands:    []any{"auth login"},
	},
}), nil

Or return Go errors. NewIncurError, NewValidationError, and NewParseError preserve structured details and integrate with errors.Is / errors.As.

return nil, incur.NewIncurError(incur.IncurErrorOptions{
	Code:    "LOCKED",
	Message: "resource locked",
	Hint:    "Try again after the current deployment finishes.",
})
Code generation

The cmd/incur helper can generate Go structs and command registration code from JSON Schema files.

# incur.yaml
package: main
output: incur_generated.go
commands:
  - name: greet
    description: Greet someone
    args: schemas/greet.args.json
    output: schemas/greet.output.json
    handler: Greet
incur gen
incur gen path/to/incur.yaml
incur generate
incur generate path/to/incur.yaml

The generated source includes schema-backed structs, embedded schemas, and RegisterCommands(cli *incur.Cli).

API Reference

Core API
cli := incur.New("name",
	incur.WithDescription("Description"),
	incur.WithVersion("1.0.0"),
	incur.WithAliases("alias"),
	incur.WithFormat(incur.FormatTOON),
	incur.WithOutputPolicy(incur.OutputPolicyAll),
	incur.WithConfig("name.json"),
	incur.WithEnv(envSchema),
	incur.WithVars(varsSchema),
	incur.WithMiddleware(middleware...),
	incur.WithRootCommand(rootDef),
)

cli.Command("command name", def)
cli.Group("group", subCli)
cli.Use(middleware)
err := cli.Serve(os.Args[1:])
handler := cli.Handler()
resp := cli.Fetch(req)
CommandDef
type CommandDef struct {
	Name          string
	Description   string
	ArgsSchema    *incur.JSONSchema
	OptionsSchema *incur.JSONSchema
	EnvSchema     *incur.JSONSchema
	OutputSchema  *incur.JSONSchema
	Format        incur.Format
	Alias         map[string]string
	Aliases       []string
	Usage         string
	Hint          string
	Examples      []incur.Example
	Middleware    []incur.MiddlewareFunc
	Handler       incur.CommandHandler
	StreamHandler incur.StreamHandler
	FetchHandler  any
	FetchBasePath string
	OutputPolicy  incur.OutputPolicy
}
JSONSchema

JSONSchema covers the subset of JSON Schema incur needs for CLI parsing, help, manifests, validation, OpenAPI import, and codegen:

type JSONSchema struct {
	Type                 any
	Properties           map[string]*incur.JSONSchema
	Required             []string
	Items                *incur.JSONSchema
	Enum                 []any
	Default              any
	Description          string
	Const                any
	AnyOf                []*incur.JSONSchema
	Ref                  string
	Defs                 map[string]*incur.JSONSchema
	Format               string
	Deprecated           bool
	AdditionalProperties any
	XCount               bool
	XOrder               []string
}

Use ParseSchema, LoadSchema, and Validate when you want schemas from files or custom validation outside a CLI run.

Current status

  • This fork tracks the original TypeScript incur where that makes sense for Go.
  • The Go API is intentionally close in spirit, but not identical: schemas are explicit structs instead of Zod schemas, and Go cannot infer run callback types the same way TypeScript can.
  • Upstream test behavior is tracked at case level rather than approximated with smoke tests. See TEST_PARITY.md and TEST_PARITY_CASES.md, and run make verify-parity with an upstream checkout to detect stale mappings.
  • APIs may evolve before stable releases. Pin a commit if you need reproducible builds.

Contributing

PRs are welcome for bugs, parity gaps, docs, tests, and Go-specific improvements. For broad product or API design changes, check the upstream wevm/incur direction first so this fork stays recognizable.

License

MIT. See LICENSE.

Documentation

Overview

agents.go implements agent detection and skill installation for AI coding assistants.

command.go implements the command execution engine for incur CLIs.

completions.go implements shell completion generation and scripts for bash, zsh, fish, and nushell.

fetch.go implements the HTTP/Fetch handler for serving commands over HTTP.

filter.go implements the output filtering DSL for selecting and transforming command output.

help.go implements help text generation for commands and subcommands.

Package incur provides a CLI framework for building agent-friendly command-line tools. It supports multiple output formats, MCP integration, HTTP serving, and shell completions.

mcp.go implements the Model Context Protocol server over stdio and HTTP transports.

middleware.go defines middleware types and chain composition for command execution.

openapi.go implements automatic command generation from OpenAPI specifications.

parser.go implements argv, option, and environment variable parsing.

pm.go implements package-manager runner detection helpers.

schema.go provides JSON Schema utilities for command validation and codegen.

skill.go implements skill file generation for AI agent discovery.

syncmcp.go implements MCP server registration with AI agent configurations.

syncskills.go implements skill syncing between the CLI and agent configurations.

tokens.go implements token counting, limiting, and pagination.

typegen.go implements TypeScript declaration generation for registered commands.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ApplyFilter

func ApplyFilter(data any, paths []FilterPath) any

ApplyFilter applies parsed filter paths to data and returns a filtered copy.

func BuildRequest

func BuildRequest(input *FetchInput) (*http.Request, error)

BuildRequest builds an http.Request from FetchInput.

func ComposeMiddleware

func ComposeMiddleware(middlewares []MiddlewareFunc, final func() error) func() error

ComposeMiddleware composes middleware around a final command function. The first middleware in the slice is the outermost wrapper.

func ComposeMiddlewareWithContext

func ComposeMiddlewareWithContext(ctx *Context, middlewares []MiddlewareFunc, final func() error) func() error

ComposeMiddlewareWithContext composes middleware around a final command function using the provided context.

func ConfigSchemaFromCli

func ConfigSchemaFromCli(cli *Cli) map[string]any

ConfigSchemaFromCli builds a JSON Schema for command config files.

func DetectPackageSpecifier

func DetectPackageSpecifier(name string) string

DetectPackageSpecifier returns the command/package specifier for a CLI name.

func DetectRunner

func DetectRunner(env map[string]string) string

DetectRunner detects the package runner command from npm environment values.

func EstimateTokenCount

func EstimateTokenCount(text string) int

EstimateTokenCount estimates LLM token count for text.

func FormatCandidates

func FormatCandidates(shell Shell, candidates []Candidate) string

FormatCandidates formats completion candidates for a shell.

func FormatCommand

func FormatCommand(name string, opts CommandHelpOptions) string

FormatCommand formats leaf command help text.

func FormatRoot

func FormatRoot(name string, opts RootHelpOptions) string

FormatRoot formats root or group help text.

func FormatValue

func FormatValue(value any, format Format) (string, error)

FormatValue serializes a value into the requested output format.

func Generate

func Generate(name string, commands []CommandInfo, groups map[string]string) string

Generate renders a full Markdown skill document.

func GenerateCommands

func GenerateCommands(spec *openapi3.T, handler http.Handler, opts OpenAPIOptions) (map[string]*CommandDef, error)

GenerateCommands converts an OpenAPI spec to incur command definitions.

func HasInstalledSkills

func HasInstalledSkills(name string, opts SyncOptions) bool

HasInstalledSkills reports whether metadata and at least one installed path exist.

func Hash

func Hash(commands []CommandInfo) string

Hash returns a stable hash of command metadata for staleness checks.

func Index

func Index(name string, commands []CommandInfo, description string) string

Index renders a compact LLM command index.

func IsStreamingResponse

func IsStreamingResponse(resp *http.Response) bool

IsStreamingResponse reports whether a response is newline-delimited JSON.

func ParseEnv

func ParseEnv(schema *JSONSchema, source map[string]string) (map[string]any, error)

ParseEnv parses environment variable values through a schema.

func ParseStreamingResponse

func ParseStreamingResponse(resp *http.Response) (<-chan any, error)

ParseStreamingResponse parses an NDJSON response into a channel.

func ReadHash

func ReadHash(name string) string

ReadHash reads the last synced command hash.

func Register

func Register(shell Shell, name string) string

Register generates a shell hook script.

func RegisterAmp

func RegisterAmp(name, command string) (bool, error)

RegisterAmp writes Amp MCP settings directly.

func RemoveSkill

func RemoveSkill(name string, opts RemoveOptions) error

RemoveSkill removes a skill from canonical and agent-specific directories.

func ServeMCP

func ServeMCP(name, version string, commands map[string]*CommandEntry, opts MCPOptions) error

ServeMCP serves a minimal MCP-compatible JSON-RPC stdio server.

func SliceByTokens

func SliceByTokens(text string, offset int, limit int) string

SliceByTokens returns a token-window approximation of text.

func TypegenFromCli

func TypegenFromCli(cli *Cli) (string, error)

TypegenFromCli generates a TypeScript module augmentation for a CLI's commands.

Types

type Agent

type Agent struct {
	// Name is the display name.
	Name string
	// GlobalSkillsDir is the absolute global skills directory.
	GlobalSkillsDir string
	// ProjectSkillsDir is the project-relative skills directory.
	ProjectSkillsDir string
	// Universal means the agent uses .agents/skills directly.
	Universal bool
	// Detect checks whether the agent is installed.
	Detect func() bool
}

Agent describes an AI coding assistant skill location.

func AllAgents

func AllAgents() []Agent

AllAgents returns all known agent definitions.

func Detect

func Detect() []Agent

Detect is an alias for DetectAgents.

func DetectAgents

func DetectAgents() []Agent

DetectAgents returns installed agents.

type AgentInstall

type AgentInstall struct {
	// Agent is the display name.
	Agent string
	// Path is the installed skill path.
	Path string
	// Mode is "symlink" or "copy".
	Mode string
}

AgentInstall describes a per-agent skill install.

type CTABlock

type CTABlock struct {
	// Commands is a list of suggested commands.
	Commands []any `json:"commands"`
	// Description is an optional human-readable CTA label.
	Description string `json:"description,omitempty"`
}

CTABlock represents suggested follow-up commands for a result.

type CallToolOptions

type CallToolOptions struct {
	// Params are flat MCP tool arguments.
	Params map[string]any
	// EnvSchema is the CLI-level environment schema.
	EnvSchema *JSONSchema
	// VarsSchema is the CLI-level vars schema.
	VarsSchema *JSONSchema
	// Name is the root CLI name.
	Name string
	// Version is the CLI version.
	Version string
	// Middlewares are inherited middleware.
	Middlewares []MiddlewareFunc
}

CallToolOptions configures CallTool.

type Candidate

type Candidate struct {
	// Value is the candidate value.
	Value string `json:"value"`
	// Description is optional shell-visible help text.
	Description string `json:"description,omitempty"`
	// NoSpace asks the shell not to append a space.
	NoSpace bool `json:"-"`
}

Candidate is a completion candidate.

func Complete

func Complete(commands map[string]*CommandEntry, root *CommandDef, argv []string, index int) []Candidate

Complete computes completion candidates from the command tree.

type Cli

type Cli struct {
	// contains filtered or unexported fields
}

Cli is the root command router and command execution surface.

func New

func New(name string, opts ...Option) *Cli

New creates a CLI.

func (*Cli) Command

func (c *Cli) Command(name string, def *CommandDef) *Cli

Command registers a leaf command. Space-separated names create nested groups.

func (*Cli) Fetch

func (c *Cli) Fetch(req *http.Request) *http.Response

Fetch runs the CLI HTTP handler against req and returns a response.

func (*Cli) Group

func (c *Cli) Group(name string, sub *Cli) *Cli

Group registers a sub-CLI.

func (*Cli) Handler

func (c *Cli) Handler() http.Handler

Handler returns an HTTP handler for the command tree.

func (*Cli) Name

func (c *Cli) Name() string

Name returns the CLI name.

func (*Cli) Serve

func (c *Cli) Serve(argv []string) error

Serve executes the CLI with argv and writes to standard streams.

func (*Cli) ServeWithOptions

func (c *Cli) ServeWithOptions(argv []string, opts ServeOptions) error

ServeWithOptions executes the CLI with testable IO/environment overrides.

func (*Cli) Use

func (c *Cli) Use(mw MiddlewareFunc) *Cli

Use appends CLI-level middleware.

type CommandContext

type CommandContext struct {
	// Agent indicates agent-oriented output mode.
	Agent bool
	// Args are parsed positional arguments.
	Args map[string]any
	// Argv contains raw command tokens before schema parsing.
	Argv []string
	// Options are parsed named options.
	Options map[string]any
	// Env is parsed environment data.
	Env map[string]any
	// Command is the resolved command path.
	Command string
	// DisplayName is the invoked binary name.
	DisplayName string
	// Format is the selected output format.
	Format string
	// FormatExplicit indicates whether Format was explicit.
	FormatExplicit bool
	// Name is the root CLI name.
	Name string
	// Version is the CLI version.
	Version string
	// Vars holds middleware-scoped variables.
	Vars map[string]any
	// contains filtered or unexported fields
}

CommandContext is passed to command handlers.

func (*CommandContext) Error

func (ctx *CommandContext) Error(opts ErrorOpts) *Result

Error returns an unsuccessful structured result.

func (*CommandContext) Get

func (ctx *CommandContext) Get(key string) any

Get returns a handler-scoped variable.

func (*CommandContext) OK

func (ctx *CommandContext) OK(data any, opts ...ResultOptions) *Result

OK returns a successful structured result.

func (*CommandContext) Set

func (ctx *CommandContext) Set(key string, value any)

Set stores a handler-scoped variable.

type CommandDef

type CommandDef struct {
	// Name is the command path or final command segment.
	Name string
	// Description is shown in help, completions, skills, and manifests.
	Description string
	// ArgsSchema validates positional arguments.
	ArgsSchema *JSONSchema
	// OptionsSchema validates named options.
	OptionsSchema *JSONSchema
	// EnvSchema validates environment variables.
	EnvSchema *JSONSchema
	// OutputSchema describes returned data.
	OutputSchema *JSONSchema
	// Format is the command-level default output format.
	Format Format
	// VarsSchema describes middleware vars produced before handler execution.
	VarsSchema *JSONSchema
	// Alias maps option names to short aliases.
	Alias map[string]string
	// Aliases are alternate command names.
	Aliases []string
	// Usage overrides generated usage text in help output.
	Usage string
	// Hint is shown after command help.
	Hint string
	// Examples are rendered in help and skills.
	Examples []Example
	// Middleware runs around this command after CLI-level middleware.
	Middleware []MiddlewareFunc
	// Handler executes a non-streaming command.
	Handler CommandHandler
	// StreamHandler executes a streaming command.
	StreamHandler StreamHandler
	// FetchHandler marks this command as a fetch gateway when set by OpenAPI/fetch integrations.
	FetchHandler any
	// FetchBasePath is prepended to fetch gateway request paths.
	FetchBasePath string
	// OutputPolicy controls when successful data is printed.
	OutputPolicy OutputPolicy
}

CommandDef describes a leaf command.

type CommandEntry

type CommandEntry struct {
	// Name is the canonical segment name.
	Name string
	// Description is shown in help, skills, and completions.
	Description string
	// Def is set for leaf commands.
	Def *CommandDef
	// Group is set for command groups.
	Group *Cli
	// AliasTarget points to the canonical command name when this is an alias entry.
	AliasTarget string
}

CommandEntry is a registered command tree node.

type CommandError

type CommandError struct {
	// Code is a machine-readable error code.
	Code string `json:"code"`
	// Message is a human-readable error message.
	Message string `json:"message"`
	// Retryable indicates whether retrying may succeed.
	Retryable bool `json:"retryable,omitempty"`
}

CommandError is the structured error envelope for command results.

type CommandHandler

type CommandHandler func(ctx *CommandContext) (any, error)

CommandHandler executes a command and returns either raw data or *Result.

type CommandHelpOptions

type CommandHelpOptions struct {
	// Description is shown in the header.
	Description string
	// Version is shown in the header when set.
	Version string
	// Args is the positional arg schema.
	Args *JSONSchema
	// Env is the env schema.
	Env *JSONSchema
	// EnvSource provides current env values for display.
	EnvSource map[string]string
	// Options is the named option schema.
	Options *JSONSchema
	// Alias maps option names to short aliases.
	Alias map[string]string
	// Aliases are executable aliases.
	Aliases []string
	// Usage overrides generated usage.
	Usage string
	// Hint is shown after tables.
	Hint string
	// Examples are command examples.
	Examples []Example
	// Commands are nested commands shown after usage.
	Commands []HelpCommand
	// Root shows root-only integrations and global flags.
	Root bool
	// HideGlobalOptions hides global flags.
	HideGlobalOptions bool
	// ConfigFlag is the configured config flag.
	ConfigFlag string
}

CommandHelpOptions configures FormatCommand.

type CommandInfo

type CommandInfo struct {
	// Name is the full command path.
	Name string
	// Description describes the command.
	Description string
	// ArgsSchema describes positional args.
	ArgsSchema *JSONSchema
	// EnvSchema describes env vars.
	EnvSchema *JSONSchema
	// OptionsSchema describes named options.
	OptionsSchema *JSONSchema
	// OutputSchema describes output.
	OutputSchema *JSONSchema
	// Hint provides extra usage guidance.
	Hint string
	// Examples are usage examples.
	Examples []Example
}

CommandInfo is command metadata used by skill and LLM manifests.

type ConfigLoader

type ConfigLoader func(path string) (map[string]any, error)

ConfigLoader loads command config defaults from an optional path.

type ConfigOptions

type ConfigOptions struct {
	// Enabled enables config discovery and loading.
	Enabled bool
	// Files is the ordered list of config files to search.
	Files []string
	// Flag is the override flag name, defaulting to "--config".
	Flag string
	// Loader replaces JSON file parsing when set.
	Loader ConfigLoader
}

ConfigOptions configures config file loading.

type Context

type Context struct {
	// Agent indicates whether output is being consumed by an agent.
	Agent bool
	// Command is the resolved command path.
	Command string
	// DisplayName is the invoked binary name (e.g. alias).
	DisplayName string
	// Env holds parsed CLI-level environment variables.
	Env map[string]any
	// Format is the resolved output format (e.g. "toon", "json").
	Format string
	// FormatExplicit indicates whether the output format was explicitly requested.
	FormatExplicit bool
	// Name is the CLI name.
	Name string
	// Version is the CLI version string.
	Version string
	// Vars holds middleware-scoped variables.
	Vars map[string]any
	// contains filtered or unexported fields
}

Context is shared state available to middleware handlers.

func (*Context) Error

func (ctx *Context) Error(opts ErrorOpts) *Result

Error creates and stores an error result that can short-circuit middleware.

func (*Context) Get

func (ctx *Context) Get(key string) any

Get returns a middleware-scoped variable set by upstream handlers.

func (*Context) Set

func (ctx *Context) Set(key string, value any)

Set stores a middleware-scoped variable for downstream handlers.

type ErrorOpts

type ErrorOpts struct {
	// Code is a machine-readable error code.
	Code string
	// Message is a human-readable error message.
	Message string
	// Retryable indicates whether retrying may succeed.
	Retryable bool
	// CTA contains optional command suggestions.
	CTA *CTABlock
	// ExitCode overrides the default exit code when non-zero.
	ExitCode int
}

ErrorOpts configures a middleware error result.

type Example

type Example struct {
	// Description explains the example.
	Description string
	// Command is the example command line.
	Command string
}

Example documents a command invocation.

type ExecuteOptions

type ExecuteOptions struct {
	// Mode controls how inputs are parsed.
	Mode ParseMode
	// Argv is used by ParseModeArgv.
	Argv []string
	// Args is used by ParseModeSplit.
	Args []string
	// Options are raw options used by ParseModeSplit.
	Options map[string]any
	// Params are flat parameters used by ParseModeFlat.
	Params map[string]any
	// Defaults are option defaults from config files.
	Defaults any
	// EnvSource overrides process environment values.
	EnvSource map[string]string
	// EnvSchema is the CLI-level environment schema available to middleware.
	EnvSchema *JSONSchema
	// VarsSchema is the CLI-level vars schema available to middleware and handlers.
	VarsSchema *JSONSchema
	// Middlewares are CLI/group-level middleware.
	Middlewares []MiddlewareFunc
	// Agent indicates agent-oriented output mode.
	Agent bool
	// Command is the resolved command path.
	Command string
	// DisplayName is the invoked binary name.
	DisplayName string
	// Format is the selected output format.
	Format string
	// FormatExplicit indicates whether Format was set by the caller.
	FormatExplicit bool
	// Name is the root CLI name.
	Name string
	// Version is the CLI version.
	Version string
	// Vars contains initial middleware variables.
	Vars map[string]any
}

ExecuteOptions configures command execution.

type FetchInput

type FetchInput struct {
	// Path is the URL path or full URL.
	Path string
	// Method is the HTTP method.
	Method string
	// Headers are request headers.
	Headers map[string]string
	// Body is the request body.
	Body string
	// Query is query string data.
	Query map[string]string
	// contains filtered or unexported fields
}

FetchInput is a curl-style request description.

func ParseArgv

func ParseArgv(argv []string) (*FetchInput, error)

ParseArgv parses curl-style fetch argv.

type FetchOutput

type FetchOutput struct {
	// Data is the parsed response body.
	Data any
	// Headers are response headers.
	Headers map[string]string
	// OK reports 2xx status.
	OK bool
	// Status is the HTTP status code.
	Status int
}

FetchOutput is a parsed HTTP response.

func ParseResponse

func ParseResponse(resp *http.Response) (*FetchOutput, error)

ParseResponse parses an HTTP response body as JSON when possible.

type FieldError

type FieldError struct {
	// Path is the field path that failed validation (e.g. "user.email").
	Path string `json:"path"`
	// Expected is the expected value or type.
	Expected string `json:"expected"`
	// Received is the value that was actually received.
	Received string `json:"received"`
	// Message is a human-readable validation message.
	Message string `json:"message"`
}

FieldError represents a single field-level validation error.

func Validate

func Validate(schema *JSONSchema, data map[string]any) ([]FieldError, error)

Validate validates data against schema and returns field-level errors.

type FilterPath

type FilterPath []Segment

FilterPath is an ordered list of segments to traverse.

func ParseFilter

func ParseFilter(expression string) ([]FilterPath, error)

ParseFilter parses a filter expression into one or more filter paths.

type Format

type Format string

Format is the output format used by FormatValue.

const (
	// FormatTOON encodes values with TOON.
	FormatTOON Format = "toon"
	// FormatJSON encodes values as pretty JSON.
	FormatJSON Format = "json"
	// FormatYAML encodes values as YAML.
	FormatYAML Format = "yaml"
	// FormatMarkdown encodes values as Markdown.
	FormatMarkdown Format = "md"
	// FormatJSONL encodes values as JSON Lines.
	FormatJSONL Format = "jsonl"
)

type HelpCommand

type HelpCommand struct {
	// Name is the command name.
	Name string
	// Description is the command description.
	Description string
}

HelpCommand is a command row in root help output.

type IncurError

type IncurError struct {
	// Code is a machine-readable error code (e.g. "NOT_AUTHENTICATED").
	Code string
	// Message is a short, human-readable error message.
	Message string
	// Hint is an optional actionable hint for the user.
	Hint string
	// Retryable indicates whether the operation can be retried.
	Retryable bool
	// ExitCode, when non-zero, overrides the default exit code of 1.
	ExitCode int
	// contains filtered or unexported fields
}

IncurError is the primary CLI error type with a machine-readable code, optional hint, retryable flag, and configurable exit code.

func NewIncurError

func NewIncurError(opts IncurErrorOptions) *IncurError

NewIncurError constructs an IncurError from the given options.

func (*IncurError) Error

func (e *IncurError) Error() string

Error returns a human-readable representation. When a cause is present, its message is appended as a "Details:" section, mirroring the TypeScript BaseError behavior.

func (*IncurError) Unwrap

func (e *IncurError) Unwrap() error

Unwrap returns the underlying cause, enabling errors.Is/errors.As chain traversal.

type IncurErrorOptions

type IncurErrorOptions struct {
	Code      string
	Message   string
	Hint      string
	Retryable bool
	ExitCode  int
	Cause     error
}

IncurErrorOptions holds the parameters for constructing an IncurError.

type InstallOptions

type InstallOptions struct {
	// Agents overrides detected agents.
	Agents []Agent
	// Cwd is the working directory for project installs.
	Cwd string
	// Global installs globally unless explicitly false.
	Global *bool
}

InstallOptions configures InstallSkills.

type InstallResult

type InstallResult struct {
	// Paths are canonical installed skill paths.
	Paths []string
	// Agents are non-universal agent install details.
	Agents []AgentInstall
}

InstallResult is returned by InstallSkills.

func InstallSkills

func InstallSkills(sourceDir string, opts InstallOptions) (*InstallResult, error)

InstallSkills installs skill directories and links non-universal agents.

type JSONSchema

type JSONSchema struct {
	// Schema is the JSON Schema dialect URI.
	Schema string `json:"$schema,omitempty"`
	// Title is an optional human-readable schema title.
	Title string `json:"title,omitempty"`
	// Type is the JSON Schema type ("string", "number", "boolean", "array", "object"), or an array of types.
	Type any `json:"type,omitempty"`
	// Properties maps object property names to child schemas.
	Properties map[string]*JSONSchema `json:"properties,omitempty"`
	// Required lists required object property names.
	Required []string `json:"required,omitempty"`
	// Items is the schema for array items.
	Items *JSONSchema `json:"items,omitempty"`
	// Enum is the list of allowed values.
	Enum []any `json:"enum,omitempty"`
	// Default is the default value used when a field is omitted.
	Default any `json:"default,omitempty"`
	// Description is the schema description.
	Description string `json:"description,omitempty"`
	// Const is a single fixed value the field must equal.
	Const any `json:"const,omitempty"`
	// AnyOf stores alternate schema branches.
	AnyOf []*JSONSchema `json:"anyOf,omitempty"`
	// Ref is a JSON Schema $ref value.
	Ref string `json:"$ref,omitempty"`
	// Defs stores JSON Schema $defs.
	Defs map[string]*JSONSchema `json:"$defs,omitempty"`
	// Format is the JSON Schema format.
	Format string `json:"format,omitempty"`
	// Deprecated marks a schema as deprecated.
	Deprecated bool `json:"deprecated,omitempty"`
	// AdditionalProperties controls object keys outside Properties.
	AdditionalProperties any `json:"additionalProperties,omitempty"`
	// XCount marks a numeric option as a count flag.
	XCount bool `json:"x-count,omitempty"`
	// XOrder is an optional custom field order used for positional argument assignment.
	XOrder []string `json:"x-order,omitempty"`
}

JSONSchema represents a JSON Schema object used for parsing and validation.

func LoadSchema

func LoadSchema(path string) (*JSONSchema, error)

LoadSchema loads a JSON or YAML schema file from disk.

func ParseSchema

func ParseSchema(data []byte) (*JSONSchema, error)

ParseSchema parses a JSON or YAML schema document.

func (*JSONSchema) EnumValues

func (s *JSONSchema) EnumValues(name string) []string

EnumValues returns enum values for a property as strings.

func (*JSONSchema) IsArrayField

func (s *JSONSchema) IsArrayField(name string) bool

IsArrayField reports whether name resolves to an array property.

func (*JSONSchema) IsBooleanField

func (s *JSONSchema) IsBooleanField(name string) bool

IsBooleanField reports whether name resolves to a boolean property.

func (*JSONSchema) IsCountField

func (s *JSONSchema) IsCountField(name string) bool

IsCountField reports whether name resolves to a count flag property.

func (*JSONSchema) PropertyNames

func (s *JSONSchema) PropertyNames() []string

PropertyNames returns property names in deterministic schema order.

func (*JSONSchema) RequiredFields

func (s *JSONSchema) RequiredFields() []string

RequiredFields returns required field names in schema order.

func (*JSONSchema) ResolveType

func (s *JSONSchema) ResolveType(name string) string

ResolveType returns a human-readable JSON Schema type for a property.

type KeySegment

type KeySegment struct {
	Key string
}

KeySegment selects a named key from an object.

type ListedSkill

type ListedSkill struct {
	// Name is the skill name.
	Name string
	// Description describes the skill.
	Description string
	// Installed indicates whether metadata says it is installed.
	Installed bool
}

ListedSkill describes a generated skill and whether it is installed.

func ListSkills

func ListSkills(name string, commands map[string]*CommandEntry, opts SyncOptions) ([]ListedSkill, error)

ListSkills returns generated skills with installed status from metadata.

type MCPOptions

type MCPOptions struct {
	// Cli is the backing CLI.
	Cli *Cli
	// Reader overrides stdin.
	Reader io.Reader
	// Writer overrides stdout.
	Writer io.Writer
}

MCPOptions configures ServeMCP.

type MiddlewareFunc

type MiddlewareFunc func(ctx *Context, next func() error) error

MiddlewareFunc wraps command execution with before/after behavior.

type OpenAPIOptions

type OpenAPIOptions struct {
	// Prefix is prepended to generated command names.
	Prefix string
	// BasePath is prepended to generated request paths.
	BasePath string
	// BaseURL is used when generated handlers create outbound requests.
	BaseURL string
}

OpenAPIOptions configures command generation.

type Option

type Option func(*Cli)

Option configures a Cli.

func WithAliases

func WithAliases(aliases ...string) Option

WithAliases sets executable aliases for help and completions.

func WithConfig

func WithConfig(files ...string) Option

WithConfig enables config files and sets optional search paths.

func WithConfigFlag

func WithConfigFlag(flag string) Option

WithConfigFlag changes the config override flag.

func WithConfigOptions

func WithConfigOptions(opts ConfigOptions) Option

WithConfigOptions enables config loading with explicit options.

func WithDescription

func WithDescription(description string) Option

WithDescription sets the CLI description.

func WithEnv

func WithEnv(schema *JSONSchema) Option

WithEnv sets the CLI-level environment schema.

func WithFormat

func WithFormat(format Format) Option

WithFormat sets the default output format.

func WithMCP

func WithMCP(opts RegisterOptions) Option

WithMCP configures MCP registration defaults.

func WithMiddleware

func WithMiddleware(middlewares ...MiddlewareFunc) Option

WithMiddleware appends CLI-level middleware.

func WithOutputPolicy

func WithOutputPolicy(policy OutputPolicy) Option

WithOutputPolicy sets the default output policy.

func WithRootCommand

func WithRootCommand(def *CommandDef) Option

WithRootCommand sets the root command definition.

func WithSync

func WithSync(opts SyncOptions) Option

WithSync configures skill syncing defaults.

func WithVars

func WithVars(schema *JSONSchema) Option

WithVars sets the CLI-level middleware vars schema.

func WithVersion

func WithVersion(version string) Option

WithVersion sets the CLI version.

type OutputPolicy

type OutputPolicy string

OutputPolicy controls whether successful output is printed for humans, agents, or both.

const (
	// OutputPolicyAgentOnly prints command data for non-TTY/agent consumers by default.
	OutputPolicyAgentOnly OutputPolicy = "agent-only"
	// OutputPolicyAll prints command data for every consumer.
	OutputPolicyAll OutputPolicy = "all"
)

type ParseError

type ParseError struct {
	// Message is a short, human-readable error message.
	Message string
	// contains filtered or unexported fields
}

ParseError represents an argument-parsing failure (unknown flags, missing values, etc.).

func NewParseError

func NewParseError(opts ParseErrorOptions) *ParseError

NewParseError constructs a ParseError from the given options.

func (*ParseError) Error

func (e *ParseError) Error() string

Error returns a human-readable representation. When a cause is present, its message is appended as a "Details:" section.

func (*ParseError) Unwrap

func (e *ParseError) Unwrap() error

Unwrap returns the underlying cause, enabling errors.Is/errors.As chain traversal.

type ParseErrorOptions

type ParseErrorOptions struct {
	Message string
	Cause   error
}

ParseErrorOptions holds the parameters for constructing a ParseError.

type ParseMode

type ParseMode string

ParseMode selects how Execute interprets input.

const (
	// ParseModeArgv parses both positional args and options from argv tokens.
	ParseModeArgv ParseMode = "argv"
	// ParseModeSplit uses positional args from Args and options from Options.
	ParseModeSplit ParseMode = "split"
	// ParseModeFlat splits Params into args and options by schema membership.
	ParseModeFlat ParseMode = "flat"
)

type ParseOptions

type ParseOptions struct {
	// Args is the schema for positional arguments.
	Args *JSONSchema
	// Options is the schema for named options/flags.
	Options *JSONSchema
	// Alias maps option names to one-character short aliases.
	Alias map[string]string
	// Defaults provides config-backed option defaults.
	Defaults any
}

ParseOptions configures Parse behavior.

type ParseResult

type ParseResult struct {
	// Args are parsed positional argument values.
	Args map[string]any
	// Options are parsed named option values.
	Options map[string]any
}

ParseResult is the parsed result of argv tokens.

func Parse

func Parse(argv []string, opts ParseOptions) (ParseResult, error)

Parse parses argv tokens against positional arg and option schemas.

type RegisterOptions

type RegisterOptions struct {
	// Command overrides the command agents run.
	Command string
	// Global installs globally unless explicitly false.
	Global *bool
	// Agents targets specific agent names.
	Agents []string
	// AddMCPBin overrides the add-mcp executable.
	AddMCPBin string
}

RegisterOptions configures MCP registration.

type RegisterResult

type RegisterResult struct {
	// Command is the command registered with agents.
	Command string
	// Agents are agent names reported as registered.
	Agents []string
}

RegisterResult describes registered MCP agents.

func RegisterMCP

func RegisterMCP(name string, opts RegisterOptions) (*RegisterResult, error)

RegisterMCP registers name as an MCP server with supported agents.

type RemoveOptions

type RemoveOptions struct {
	// Cwd is the working directory for project installs.
	Cwd string
	// Global removes globally unless explicitly false.
	Global *bool
}

RemoveOptions configures RemoveSkill.

type Result

type Result struct {
	// OK indicates successful execution.
	OK bool
	// Data is the successful payload when OK is true.
	Data any
	// Error contains structured error information when OK is false.
	Error *CommandError
	// CTA contains optional command suggestions.
	CTA *CTABlock
	// Stream is an optional stream payload for streaming commands.
	Stream <-chan any
	// ExitCode is an optional process exit override for error results.
	ExitCode int
	// contains filtered or unexported fields
}

Result is the normalized output shape used by command execution.

func Execute

func Execute(cmd *CommandDef, opts ExecuteOptions) (*Result, error)

Execute parses inputs, runs middleware, invokes the command, and returns a result.

type ResultOptions

type ResultOptions struct {
	// CTA contains suggested follow-up commands.
	CTA *CTABlock
	// ExitCode overrides the process exit code.
	ExitCode int
}

ResultOptions configures ctx.OK.

type RootHelpOptions

type RootHelpOptions struct {
	// Aliases are executable aliases.
	Aliases []string
	// ConfigFlag is the configured config flag.
	ConfigFlag string
	// Description is shown in the header.
	Description string
	// Version is shown in the header when set.
	Version string
	// Commands are listed as subcommands.
	Commands []HelpCommand
	// Root shows root-only integrations and global flags.
	Root bool
	// RootCommand is an optional root command.
	RootCommand *CommandDef
	// EnvSource provides current env values for root command display.
	EnvSource map[string]string
	// HideGlobalOptions hides global flags.
	HideGlobalOptions bool
}

RootHelpOptions configures FormatRoot.

type Segment

type Segment interface {
	// contains filtered or unexported methods
}

Segment is a single part of a filter path. Implementations are KeySegment and SliceSegment.

type ServeOptions

type ServeOptions struct {
	// Stdin provides input for streaming built-ins such as --mcp.
	Stdin io.Reader
	// Stdout receives normal command output.
	Stdout io.Writer
	// Stderr receives diagnostics.
	Stderr io.Writer
	// Env overrides process environment values.
	Env map[string]string
	// Cwd overrides the current working directory for config discovery.
	Cwd string
	// DisplayName overrides the rendered command name.
	DisplayName string
	// Human overrides TTY detection for readable human output.
	Human *bool
}

ServeOptions configures ServeWithOptions.

type Shell

type Shell string

Shell is a supported shell name.

const (
	// ShellBash is bash.
	ShellBash Shell = "bash"
	// ShellFish is fish.
	ShellFish Shell = "fish"
	// ShellNushell is nushell.
	ShellNushell Shell = "nushell"
	// ShellZsh is zsh.
	ShellZsh Shell = "zsh"
)

type SkillFile

type SkillFile struct {
	// Dir is the skill directory name.
	Dir string
	// Name is the skill name.
	Name string
	// Description is a short description.
	Description string
	// Content is Markdown content.
	Content string
}

SkillFile is a generated skill file.

func Split

func Split(name string, commands []CommandInfo, depth int, groups map[string]string) []SkillFile

Split splits commands into skill files grouped by command depth.

type SliceSegment

type SliceSegment struct {
	Start int
	End   int
}

SliceSegment selects a range from an array, equivalent to JavaScript Array.prototype.slice(start, end).

type StreamHandler

type StreamHandler func(ctx *CommandContext) (<-chan any, error)

StreamHandler executes a streaming command. Stream items may be payload values, Result terminal values, or errors.

type SyncOptions

type SyncOptions struct {
	// Cwd is the project working directory.
	Cwd string
	// Depth is the command grouping depth.
	Depth int
	// Description is the root skill description.
	Description string
	// Global installs globally unless explicitly false.
	Global *bool
	// Include contains glob patterns for external skill dirs/files.
	Include []string
	// RootCommand is an optional root command.
	RootCommand *CommandDef
	// Suggestions are example prompts shown after sync.
	Suggestions []string
	// Agents overrides detected agents.
	Agents []Agent
}

SyncOptions configures skill syncing.

type SyncResult

type SyncResult struct {
	// Skills are generated skill records.
	Skills []SyncedSkill
	// Paths are canonical install paths.
	Paths []string
	// Agents are agent-specific install records.
	Agents []AgentInstall
}

SyncResult is returned by Sync.

func Sync

func Sync(name string, commands map[string]*CommandEntry, opts SyncOptions) (*SyncResult, error)

Sync generates skill files, installs them, and records metadata.

type SyncedSkill

type SyncedSkill struct {
	// Name is the skill name.
	Name string
	// Description describes the skill.
	Description string
	// Path is the canonical install path when available.
	Path string
}

SyncedSkill describes a synced skill.

type ToolContent

type ToolContent struct {
	// Type is usually "text".
	Type string `json:"type"`
	// Text is content text.
	Text string `json:"text"`
}

ToolContent is an MCP content item.

type ToolEntry

type ToolEntry struct {
	// Name is the MCP-safe tool name.
	Name string
	// Command is the full incur command path.
	Command string
	// Description describes the tool.
	Description string
	// InputSchema is the merged args/options JSON Schema.
	InputSchema *JSONSchema
	// OutputSchema describes the result payload.
	OutputSchema *JSONSchema
	// Def is the command definition.
	Def *CommandDef
	// Middlewares are middleware inherited from the command path.
	Middlewares []MiddlewareFunc
}

ToolEntry is an MCP tool backed by an incur command.

func CollectTools

func CollectTools(commands map[string]*CommandEntry, prefix []string) []ToolEntry

CollectTools recursively collects leaf commands as MCP tools.

type ToolResult

type ToolResult struct {
	// Content is MCP text content.
	Content []ToolContent `json:"content"`
	// IsError marks failed command calls.
	IsError bool `json:"isError,omitempty"`
}

ToolResult is an MCP tool call result.

func CallTool

func CallTool(tool *ToolEntry, params map[string]any, opts CallToolOptions) (*ToolResult, error)

CallTool executes a tool call.

type ValidationError

type ValidationError struct {
	// Message is a short, human-readable error message.
	Message string
	// FieldErrors holds per-field validation error details.
	FieldErrors []FieldError
	// contains filtered or unexported fields
}

ValidationError represents a validation failure, optionally carrying per-field error details.

func NewValidationError

func NewValidationError(opts ValidationErrorOptions) *ValidationError

NewValidationError constructs a ValidationError from the given options.

func (*ValidationError) Error

func (e *ValidationError) Error() string

Error returns a human-readable representation. When a cause is present, its message is appended as a "Details:" section.

func (*ValidationError) Unwrap

func (e *ValidationError) Unwrap() error

Unwrap returns the underlying cause, enabling errors.Is/errors.As chain traversal.

type ValidationErrorOptions

type ValidationErrorOptions struct {
	Message     string
	FieldErrors []FieldError
	Cause       error
}

ValidationErrorOptions holds the parameters for constructing a ValidationError.

Directories

Path Synopsis
cmd
incur command
Package main provides the incur CLI binary for code generation and tooling.
Package main provides the incur CLI binary for code generation and tooling.
Package codegen implements code generation from JSON Schema definitions, producing typed Go structs, validation functions, and command bindings.
Package codegen implements code generation from JSON Schema definitions, producing typed Go structs, validation functions, and command bindings.
examples
basic command

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL