Skip to content

declick 0.5.0

Choose a tag to compare

@ucsandman ucsandman released this 04 Sep 14:31
· 27 commits to main since this release

Every item the 0.3.0 gap audit deferred except the macOS/Linux desktop backend. 566 tests pass.

  • compose, a tenth engine: a chain of verbs from adapters you already built becomes one verb with one envelope, so an agent stops re-deriving the same two or three calls every session. declick add compose:./chain.json --name ops, or declick compose ops --steps ./chain.json (- reads stdin and keeps a copy the adapter owns, so declick build ops still works); declick compose ops prints the chain step by step. A chain file is {"compose": true, "verbs": [{name, description, args, flags, steps, returns}]}; a step is {"run": "<adapter> <verb> {arg}", "as": "name", "optional": false} with run a shell-style string or an argv array. Templates read the composite's arguments and flags ({id}) and earlier steps' data ({pet.owner.name}). Every step runs as its own declick command in a child process, so it keeps its own governance decision, credentials, flag defaults and audit line; a mutating chain is put to DashClaw once for the composite and once per mutating step. declick add refuses a chain naming an adapter or verb that is not there, a template that names nothing, or a returns that points at no step. A failing step stops the chain with that step's exit code and step <i> (<adapter> <verb>): <error>, with data.steps carrying everything that already ran; --dry-run previews every step and sends nothing; a chain that calls itself stops at eight levels deep.
  • --each <file|-> on any verb: run it once per item in a file of inputs, sequentially, and get one envelope back. NDJSON, a JSON array, or stdin; an item is {"args": [...], "flags": {...}} or a flat object of the verb's arg and flag names. Command-line args and flags are the default for every item and the item overrides per key, except --dry-run, which an item may turn on and never off. data is one entry per item (input, ok, exit, data or error) in input order, each shaped by its own --fields, --limit and --rows; meta carries count, failed and each: true. Every item is guarded on its own; a failing item does not stop the rest, a blocked item does (the entries behind it read not run: item N was blocked, exit 3). Exit 0 when every item is ok, otherwise the first failing item's code. A missing file, a line that is not JSON or an item that is neither shape is exit 1 naming the line, and nothing runs.
  • Per-adapter flag defaults in ~/.declick/<name>/defaults.json: {"*": {"limit": 20, "fields": "id,name"}, "find-pets-by-status": {"status": "sold"}}. The * scope applies to every verb, a verb scope wins over *, a flag typed on the command line wins over both; values are parsed exactly like the tokens they stand for. meta.defaults lists the keys a run took from the file and declick describe prints them on one line. The file sits beside manifest.json, so declick build never clobbers it. A key the verb does not accept is exit 1 naming the file and the key; a file that is not valid JSON is exit 1 naming the --clear that fixes it, and leaves describe, lint and build working. declick defaults <name> [--verb v] [--set k=v] [--unset k] [--clear] [--dry-run] prints or edits it, and every --set is checked against the flags that scope accepts. --no-defaults and DECLICK_DEFAULTS=off skip the file.
  • policy.json: a local per-verb policy that needs no service. $DECLICK_HOME/policy.json (or DECLICK_POLICY) holds rules, each an adapter glob, a verb glob, an optional mutating filter, a decision of allow, warn or block and an optional reason. First match wins; no file and no match are both allow. A block is exit 3 with blocked by policy: <reason> before anything is sent, including on read-only verbs, which never reached the guard before. A warn writes one stderr line and runs, and still defers to DashClaw when a key is set. --dry-run never consults it. The file fails closed: unreadable JSON, a bad decision, a rules that is not an array or an unknown field makes every run exit 1 naming the file. declick policy [--check <adapter> <verb>] [--example] shows the rules, which one wins for a verb, and an example file. meta.governance.source is "policy" when the local file decided, on the envelope and the audit line.
  • declick web text <url> [--selector css] [--grep re] [--limit N]: the page's visible text as numbered lines, so "does the page say X" is one call with an exit code (2 when nothing matches) instead of a page dump. declick web tree takes the same --grep, tested against role:name and href. The setup rules block and the Claude Code hook now point at it instead of curl | grep -c.
  • An openapi verb whose response is text/event-stream is read as it arrives instead of buffered whole: data is an array of parsed events ({event?, id?, data}, JSON payloads parsed) and meta.stream carries {events, complete, truncatedByTimeout, ms}. When the timeout budget runs out mid-stream, the events already received come back with meta.truncated: true and exit 0 instead of a timeout with nothing.
  • declick setup never overwrites an adapter that already exists under the name it would pick: a server whose plain name collides falls back to <name>-mcp only when that name is free, otherwise it is skipped naming the adapter and its source. A bearer needed on the fallback attempt is reported as needs <TOKEN> in the vault, not as a collision. The bin dir lands on the running process's PATH once installed, so setup no longer prints add to PATH once for every adapter it builds.
  • The runtime's per-invocation work (flag check, policy, governance, credential scoping, the engine call) moved into one function, so --each and compose steps run the identical path. No change to what a single run answers. each and defaults are reserved flag names now: a verb parameter called either is renamed param-each / param-defaults on the next build.

Install: npm i -g declick@0.5.0 · npm · declick.dev