{
  "version": "0.99.0",
  "tools": [
    {
      "name": "nika:assert",
      "bare": "assert",
      "category": "core",
      "description": "Fail-fast guard: fail the task when condition is false (else no-op).",
      "args": [
        {
          "name": "condition",
          "required": true,
          "type": "boolean",
          "desc": "the resolved boolean to check"
        },
        {
          "name": "message",
          "required": false,
          "type": "string",
          "desc": "failure message"
        }
      ]
    },
    {
      "name": "nika:chart",
      "bare": "chart",
      "category": "media",
      "description": "Render a DETERMINISTIC chart artifact from rows + a semantic spec (bar | line | area_band | scatter | heatmap) — byte-identical SVG saved at `out`, sha256 in outputs (the trace-chain receipt) · optional Vega-Lite sibling via compile_to. Pure compute + one permit-gated write: no network, no clock, re-runs are idempotent.",
      "args": [
        {
          "name": "data",
          "required": true,
          "type": "string",
          "desc": "rows · array of flat objects (strings + numbers) · or { path: <json file> }"
        },
        {
          "name": "semantics",
          "required": false,
          "type": "object",
          "desc": "field → usd | duration_ms | tokens | count | delta | percent | timestamp | category (drives formatting + palettes · delta ⇒ diverging anchored 0)"
        },
        {
          "name": "chart",
          "required": true,
          "type": "object",
          "desc": "{ type, x, y, y_lo?, y_hi? (area_band bounds), y2? (actual overlay), color? (series split · heatmap value), title?, width?, height? } — x/y/… are field names"
        },
        {
          "name": "out",
          "required": true,
          "type": "string",
          "desc": "artifact path ending .svg (fs-permit gated · parents created · idempotent)"
        },
        {
          "name": "compile_to",
          "required": false,
          "type": "string",
          "desc": "vega_lite — also writes the .vl.json sibling next to the svg"
        }
      ]
    },
    {
      "name": "nika:compose",
      "bare": "compose",
      "category": "introspection",
      "description": "Statically check a Nika workflow draft you wrote · returns the full `nika check` verdict as JSON (conformance + secret-flow + permits + the termination/cost certificate) · NEVER executes it. Iterate until valid, then deliver the draft. Agent loops only.",
      "args": [
        {
          "name": "workflow_yaml",
          "required": true,
          "type": "string",
          "desc": "the complete workflow YAML draft"
        }
      ]
    },
    {
      "name": "nika:convert",
      "bare": "convert",
      "category": "data",
      "description": "Convert between json·yaml·toml·csv (from/to · identity rejected).",
      "args": [
        {
          "name": "input",
          "required": true,
          "desc": "the data (string for text formats)"
        },
        {
          "name": "from",
          "required": true,
          "type": "string",
          "desc": "json|yaml|toml|csv"
        },
        {
          "name": "to",
          "required": true,
          "type": "string",
          "desc": "json|yaml|toml|csv"
        },
        {
          "name": "has_header",
          "required": false,
          "type": "boolean",
          "desc": "CSV only (default true)"
        },
        {
          "name": "formula_guard",
          "required": false,
          "type": "boolean",
          "desc": "CSV only · prefix formula-triggering cells (=+-@) with ' against spreadsheet injection · default false (alters data)"
        }
      ]
    },
    {
      "name": "nika:date",
      "bare": "date",
      "category": "data",
      "description": "Timestamp arithmetic · op-discriminated (now|add|subtract|format|parse|diff) · strftime grammar · ISO 8601 out.",
      "args": [
        {
          "name": "op",
          "required": true,
          "type": "string",
          "desc": "now | add | subtract | format | parse | diff"
        },
        {
          "name": "tz",
          "required": false,
          "type": "string",
          "desc": "IANA timezone (now · default UTC)"
        },
        {
          "name": "base",
          "required": false,
          "type": "string",
          "desc": "ISO 8601 base (add/subtract)"
        },
        {
          "name": "duration",
          "required": false,
          "type": "string",
          "desc": "ISO 8601 span like PT1h (add/subtract)"
        },
        {
          "name": "input",
          "required": false,
          "type": "string",
          "desc": "the timestamp to render (format) or the text to read (parse)"
        },
        {
          "name": "format",
          "required": false,
          "type": "string",
          "desc": "strftime grammar e.g. %Y-%m-%d (format/parse)"
        },
        {
          "name": "start",
          "required": false,
          "type": "string",
          "desc": "ISO 8601 (diff)"
        },
        {
          "name": "end",
          "required": false,
          "type": "string",
          "desc": "ISO 8601 (diff)"
        },
        {
          "name": "unit",
          "required": false,
          "type": "string",
          "desc": "seconds (default) | milliseconds | minutes | hours | days | weeks (diff)"
        }
      ]
    },
    {
      "name": "nika:done",
      "bare": "done",
      "category": "core",
      "description": "Mark the current agent loop complete (the loop-completion sentinel · agent loops only).",
      "args": [
        {
          "name": "result",
          "required": false,
          "desc": "optional final value (any JSON)"
        }
      ]
    },
    {
      "name": "nika:edit",
      "bare": "edit",
      "category": "file",
      "description": "In-place literal find/replace (all occurrences · count caps it). NOT regex.",
      "args": [
        {
          "name": "path",
          "required": true,
          "type": "string",
          "desc": "file to edit"
        },
        {
          "name": "find",
          "required": true,
          "type": "string",
          "desc": "the literal string to find"
        },
        {
          "name": "replace",
          "required": true,
          "type": "string",
          "desc": "the replacement"
        },
        {
          "name": "count",
          "required": false,
          "type": "integer",
          "desc": "cap replacements"
        }
      ]
    },
    {
      "name": "nika:emit",
      "bare": "emit",
      "category": "core",
      "description": "Emit a custom machine event for subscribers/journal (distinct from log).",
      "args": [
        {
          "name": "event_type",
          "required": true,
          "type": "string",
          "desc": "^[a-z][a-z0-9_.-]*$"
        },
        {
          "name": "payload",
          "required": false,
          "desc": "any JSON value"
        }
      ]
    },
    {
      "name": "nika:fetch",
      "bare": "fetch",
      "category": "network",
      "description": "HTTP request + content extraction · returns the extracted body (non-2xx is an error). SSRF-defended.",
      "args": [
        {
          "name": "url",
          "required": false,
          "type": "string",
          "desc": "the URL"
        },
        {
          "name": "method",
          "required": false,
          "type": "string",
          "desc": "GET (default) | POST | PUT | DELETE | PATCH | HEAD"
        },
        {
          "name": "headers",
          "required": false,
          "type": "object",
          "desc": "string map · auth rides here: { x-api-key: \"${{ secrets.KEY }}\" }"
        },
        {
          "name": "body",
          "required": false,
          "desc": "request body (objects auto-JSON) · at most one of body/form/multipart"
        },
        {
          "name": "form",
          "required": false,
          "type": "object",
          "desc": "application/x-www-form-urlencoded scalar fields · POST/PUT/PATCH only"
        },
        {
          "name": "multipart",
          "required": false,
          "type": "array",
          "desc": "multipart/form-data parts · {name, value} text or {name, path, filename?, content_type?} file (path is permits.fs.read-gated · ≤32 MiB total) · POST/PUT/PATCH only"
        },
        {
          "name": "traverse",
          "required": false,
          "type": "object",
          "desc": "bounded same-origin crawl · { max_pages: 1..=25 (required), respect_robots?: bool (default true) } · GET only · excludes mode/selector/jq/body/form/multipart · emits { url, page_count, pages[], assets } page digests"
        },
        {
          "name": "mode",
          "required": false,
          "type": "string",
          "desc": "markdown (default) | article | text | selector | jq | metadata | links | feed | sitemap | raw"
        },
        {
          "name": "selector",
          "required": false,
          "type": "string",
          "desc": "CSS selector (mode: selector only)"
        },
        {
          "name": "jq",
          "required": false,
          "type": "string",
          "desc": "a jq expression (mode: jq only · the one data language)"
        }
      ]
    },
    {
      "name": "nika:glob",
      "bare": "glob",
      "category": "file",
      "description": "Glob match · returns paths sorted lexicographically.",
      "args": [
        {
          "name": "pattern",
          "required": true,
          "type": "string",
          "desc": "e.g. ./src/**/*.rs"
        },
        {
          "name": "exclude",
          "required": false,
          "desc": "a glob pattern or a list of them to drop from the result"
        }
      ]
    },
    {
      "name": "nika:grep",
      "bare": "grep",
      "category": "file",
      "description": "Recursive regex search · returns {path,line,match} sorted by (path,line).",
      "args": [
        {
          "name": "pattern",
          "required": true,
          "type": "string",
          "desc": "RE2-class regex"
        },
        {
          "name": "path",
          "required": false,
          "type": "string",
          "desc": "root to search (default .)"
        },
        {
          "name": "case_insensitive",
          "required": false,
          "type": "boolean",
          "desc": ""
        }
      ]
    },
    {
      "name": "nika:hash",
      "bare": "hash",
      "category": "data",
      "description": "Content hashing · blake3 (default) | sha256 | sha512 · hex (default) or base64.",
      "args": [
        {
          "name": "content",
          "required": true,
          "type": "string",
          "desc": "the content to hash"
        },
        {
          "name": "algo",
          "required": false,
          "type": "string",
          "desc": "blake3 | sha256 | sha512"
        },
        {
          "name": "encoding",
          "required": false,
          "type": "string",
          "desc": "hex | base64"
        }
      ]
    },
    {
      "name": "nika:image_fx",
      "bare": "image_fx",
      "category": "media",
      "description": "Apply deterministic artistic effects to a PNG (dither · palette · duotone · pixelate · halftone · grain · vignette · chromatic_aberration · scanlines · glitch · ascii) — byte-identical forever for identical input+args; the recipe rides the artifact (tEXt) and outputs carry path + sha256, bytes never inline.",
      "args": [
        {
          "name": "input",
          "required": true,
          "type": "string",
          "desc": "source PNG path (read · permit-gated fs.read · PNG v1: produce upstream via image_generate format png)"
        },
        {
          "name": "out",
          "required": true,
          "type": "string",
          "desc": "output artifact path (.png · .txt/.ans for ascii text/ansi emits) — rides the declared permits.fs boundary"
        },
        {
          "name": "ops",
          "required": true,
          "type": "array",
          "desc": "ordered single-key op maps · resize{width,height,filter} · crop{x,y,width,height} · levels{brightness,contrast} · grayscale · palette_map{palette} · dither{mode,palette} · duotone{dark,light} · pixelate{block} · halftone{cell,angle} · grain{intensity} · vignette{strength} · chromatic_aberration{shift} · scanlines{strength,period} · glitch{line_shift,channel_shift,blocks} · ascii{cols,emit} (ascii must be last)"
        },
        {
          "name": "seed",
          "required": false,
          "type": "integer",
          "desc": "stochastic-op seed (grain · glitch) · default 0 · the seed IS the style"
        }
      ]
    },
    {
      "name": "nika:image_generate",
      "bare": "image_generate",
      "category": "media",
      "description": "Generate image assets (local compat servers · openai gpt-image-2 · gemini gemini-3.1-flash-image · xai grok-imagine-image · mock for offline runs) — saves files under output_dir and returns paths + dimensions + sha256 (+ a provenance manifest); image bytes never ride outputs.",
      "args": [
        {
          "name": "provider",
          "required": false,
          "type": "string",
          "desc": "local | openai | gemini | xai | mock (inferred from model: when omitted · local is never inferred)"
        },
        {
          "name": "model",
          "required": false,
          "type": "string",
          "desc": "provider model id · defaults: stablediffusion (local · LocalAI convention) · gpt-image-2 · gemini-3.1-flash-image · grok-imagine-image · mock-image-1"
        },
        {
          "name": "prompt",
          "required": true,
          "type": "string",
          "desc": "the creative brief the provider renders"
        },
        {
          "name": "mode",
          "required": false,
          "type": "string",
          "desc": "generate (default · text→image) | edit (source image(s) + instruction)"
        },
        {
          "name": "image",
          "required": false,
          "type": "string",
          "desc": "mode:edit · one source image PATH (read · permit-gated fs.read)"
        },
        {
          "name": "images",
          "required": false,
          "type": "string",
          "desc": "mode:edit · source image PATHS (multi-ref · capped per provider)"
        },
        {
          "name": "mask",
          "required": false,
          "type": "string",
          "desc": "mode:edit · optional pixel-mask PATH (openai/local only · refused elsewhere)"
        },
        {
          "name": "n",
          "required": false,
          "type": "integer",
          "desc": "variant count 1..=10 (gemini runs n sequential calls)"
        },
        {
          "name": "aspect_ratio",
          "required": false,
          "type": "string",
          "desc": "1:1 | 16:9 | 9:16 | 4:3 | 3:4 | 3:2 | 2:3 | 21:9"
        },
        {
          "name": "size",
          "required": false,
          "type": "string",
          "desc": "exact WIDTHxHEIGHT (gpt-image-2 arbitrary /16 sizes · gemini folds to a size class) or auto — an exact size wins over aspect_ratio"
        },
        {
          "name": "quality",
          "required": false,
          "type": "string",
          "desc": "auto (default) | low | medium | high | ultra (folds to high on openai · model-tier on gemini)"
        },
        {
          "name": "format",
          "required": false,
          "type": "string",
          "desc": "png (default) | jpeg | webp — magic bytes are the authority on what lands"
        },
        {
          "name": "compression",
          "required": false,
          "type": "integer",
          "desc": "0..=100 · jpeg/webp only (openai output_compression)"
        },
        {
          "name": "background",
          "required": false,
          "type": "string",
          "desc": "auto (default) | transparent (needs png/webp and a supporting model) | opaque"
        },
        {
          "name": "seed",
          "required": false,
          "type": "integer",
          "desc": "gemini best-effort · openai has no seed (warned + dropped)"
        },
        {
          "name": "reference_images",
          "required": false,
          "type": "array",
          "desc": "media roadmap — rejected loudly in V1"
        },
        {
          "name": "provider_options",
          "required": false,
          "type": "object",
          "desc": "vetted pass-through · openai {moderation, user} · gemini {thinking_level, image_size} · xai {user, resolution}"
        },
        {
          "name": "output_dir",
          "required": true,
          "type": "string",
          "desc": "directory the assets land in (rides the declared permits.fs boundary)"
        },
        {
          "name": "filename_prefix",
          "required": false,
          "type": "string",
          "desc": "filename stem (else metadata.page_slug, else `image`) — sanitized, traversal-free"
        },
        {
          "name": "save",
          "required": false,
          "type": "boolean",
          "desc": "V1 contract: stays true — assets land on disk, never inline"
        },
        {
          "name": "manifest",
          "required": false,
          "type": "boolean",
          "desc": "write the provenance manifest JSON beside the assets (default true)"
        },
        {
          "name": "metadata",
          "required": false,
          "type": "object",
          "desc": "free provenance fields (campaign · page_slug · locale …) echoed to output + manifest"
        },
        {
          "name": "timeout_ms",
          "required": false,
          "type": "integer",
          "desc": "per-request deadline · default 180000 · 1000..=600000"
        },
        {
          "name": "debug",
          "required": false,
          "type": "boolean",
          "desc": "echo the sanitized raw provider response (base64 stripped)"
        }
      ]
    },
    {
      "name": "nika:inspect",
      "bare": "inspect",
      "category": "introspection",
      "description": "Introspect the running workflow · view: cost | records | dag_info | threads.",
      "args": [
        {
          "name": "view",
          "required": true,
          "type": "string",
          "desc": "cost | records | dag_info | threads"
        }
      ]
    },
    {
      "name": "nika:jq",
      "bare": "jq",
      "category": "data",
      "description": "Run a jq expression over input · THE data-transform language (map·filter·select·reshape). Emits exactly one value.",
      "args": [
        {
          "name": "expression",
          "required": true,
          "type": "string",
          "desc": "the jq program"
        },
        {
          "name": "input",
          "required": false,
          "desc": "any JSON value (or an array for multi-input ops)"
        }
      ]
    },
    {
      "name": "nika:json_diff",
      "bare": "json_diff",
      "category": "data",
      "description": "JSON diff · returns an RFC 6902 JSON Patch.",
      "args": [
        {
          "name": "before",
          "required": true,
          "desc": "the original value"
        },
        {
          "name": "after",
          "required": true,
          "desc": "the new value"
        }
      ]
    },
    {
      "name": "nika:json_merge_patch",
      "bare": "json_merge_patch",
      "category": "data",
      "description": "RFC 7396 merge patch (null deletes a key) over a target object.",
      "args": [
        {
          "name": "target",
          "required": true,
          "type": "object",
          "desc": ""
        },
        {
          "name": "patch",
          "required": true,
          "type": "object",
          "desc": ""
        }
      ]
    },
    {
      "name": "nika:log",
      "bare": "log",
      "category": "core",
      "description": "Emit a human-readable diagnostic log line to the run stream (best-effort · never fails).",
      "args": [
        {
          "name": "level",
          "required": false,
          "type": "string",
          "desc": "debug | info | warn | error (default info)"
        },
        {
          "name": "message",
          "required": true,
          "type": "string",
          "desc": "the log message"
        },
        {
          "name": "data",
          "required": false,
          "desc": "optional structured context (any JSON)"
        }
      ]
    },
    {
      "name": "nika:notify",
      "bare": "notify",
      "category": "network",
      "description": "Send a notification · v0.1 engines support the webhook channel.",
      "args": [
        {
          "name": "channel",
          "required": false,
          "type": "string",
          "desc": "webhook (v0.1) | slack | email | discord | sms (gated)"
        },
        {
          "name": "target",
          "required": true,
          "type": "string",
          "desc": "the webhook URL"
        },
        {
          "name": "message",
          "required": true,
          "type": "string",
          "desc": "the alert text"
        },
        {
          "name": "severity",
          "required": false,
          "type": "string",
          "desc": "info (default) | warn | error"
        },
        {
          "name": "data",
          "required": false,
          "desc": "optional structured context (any JSON · rides the payload beside the message)"
        }
      ]
    },
    {
      "name": "nika:prompt",
      "bare": "prompt",
      "category": "core",
      "description": "Ask a human (confirm|input|choice). Blocks until answered; uses default when headless.",
      "args": [
        {
          "name": "mode",
          "required": false,
          "type": "string",
          "desc": "confirm (default) | input | choice"
        },
        {
          "name": "message",
          "required": true,
          "type": "string",
          "desc": "the question"
        },
        {
          "name": "choices",
          "required": false,
          "type": "array",
          "desc": "choice mode only"
        },
        {
          "name": "default",
          "required": false,
          "desc": "fallback when no human can answer"
        }
      ]
    },
    {
      "name": "nika:read",
      "bare": "read",
      "category": "file",
      "description": "Read a file · returns its text (or opaque bytes with binary: true).",
      "args": [
        {
          "name": "path",
          "required": true,
          "type": "string",
          "desc": "file path"
        },
        {
          "name": "binary",
          "required": false,
          "type": "boolean",
          "desc": "return opaque bytes (default false)"
        }
      ]
    },
    {
      "name": "nika:tts_generate",
      "bare": "tts_generate",
      "category": "media",
      "description": "Synthesize speech audio (local compat servers · openai gpt-4o-mini-tts · elevenlabs · mock for offline runs) — saves ONE audio file under output_dir and returns path + format + sha256 + duration (+ a provenance manifest); audio bytes never ride outputs.",
      "args": [
        {
          "name": "provider",
          "required": false,
          "type": "string",
          "desc": "local | openai | elevenlabs | mock (inferred from model: when omitted · local is never inferred)"
        },
        {
          "name": "model",
          "required": false,
          "type": "string",
          "desc": "provider model id · defaults: tts-1 (local · LocalAI convention) · gpt-4o-mini-tts · eleven_multilingual_v2 · mock-tts-1"
        },
        {
          "name": "text",
          "required": true,
          "type": "string",
          "desc": "the text to speak · ≤4096 chars (fan long scripts out with for_each)"
        },
        {
          "name": "voice",
          "required": false,
          "type": "string",
          "desc": "provider voice · defaults: alloy (openai/local) · Rachel's id (elevenlabs) · sine (mock)"
        },
        {
          "name": "format",
          "required": false,
          "type": "string",
          "desc": "mp3 | wav | auto (default · provider-native · the saved extension follows the bytes)"
        },
        {
          "name": "speed",
          "required": false,
          "type": "number",
          "desc": "0.25..=4.0 (openai/local · warned-dropped elsewhere)"
        },
        {
          "name": "output_dir",
          "required": true,
          "type": "string",
          "desc": "directory the audio lands in (created if missing · fs-permit gated)"
        },
        {
          "name": "filename_prefix",
          "required": false,
          "type": "string",
          "desc": "filename stem (default: metadata.page_slug, else `speech`)"
        },
        {
          "name": "manifest",
          "required": false,
          "type": "boolean",
          "desc": "write the sidecar provenance manifest (default true)"
        },
        {
          "name": "metadata",
          "required": false,
          "type": "object",
          "desc": "free-form provenance echoed into output + manifest"
        },
        {
          "name": "timeout_ms",
          "required": false,
          "type": "integer",
          "desc": "per-request cap · default 120000 (local 300000) · max 600000"
        }
      ]
    },
    {
      "name": "nika:uuid",
      "bare": "uuid",
      "category": "data",
      "description": "Generate a UUID · v7 (default · sortable) or v4 (random).",
      "args": [
        {
          "name": "version",
          "required": false,
          "type": "string",
          "desc": "v7 (default) | v4"
        }
      ]
    },
    {
      "name": "nika:validate",
      "bare": "validate",
      "category": "data",
      "description": "Validate data against a JSON Schema · returns {valid, errors} (invalid data is a report, not a failure).",
      "args": [
        {
          "name": "data",
          "required": true,
          "desc": "the value (or a YAML string with format: yaml)"
        },
        {
          "name": "schema",
          "required": true,
          "desc": "a JSON Schema"
        },
        {
          "name": "format",
          "required": false,
          "type": "string",
          "desc": "json (default) | yaml"
        }
      ]
    },
    {
      "name": "nika:wait",
      "bare": "wait",
      "category": "core",
      "description": "Pause: relative duration (e.g. \"30s\") XOR absolute until (ISO 8601). Exactly one.",
      "args": [
        {
          "name": "duration",
          "required": false,
          "type": "string",
          "desc": "Go-duration string · relative"
        },
        {
          "name": "until",
          "required": false,
          "type": "string",
          "desc": "ISO 8601 timestamp · absolute"
        },
        {
          "name": "timeout",
          "required": false,
          "type": "string",
          "desc": "optional cap for absolute wait"
        }
      ]
    },
    {
      "name": "nika:write",
      "bare": "write",
      "category": "file",
      "description": "Write a file · returns the path. overwrite defaults true · create_dirs defaults false.",
      "args": [
        {
          "name": "path",
          "required": true,
          "type": "string",
          "desc": "destination path"
        },
        {
          "name": "content",
          "required": true,
          "type": "string",
          "desc": "the content to write"
        },
        {
          "name": "overwrite",
          "required": false,
          "type": "boolean",
          "desc": ""
        },
        {
          "name": "create_dirs",
          "required": false,
          "type": "boolean",
          "desc": ""
        }
      ]
    }
  ]
}
