Releases: ucsandman/declick
Release list
declick 0.6.0
Five answers to the same problem, that an API sends more than the agent asked for and the whole of it lands in the context, plus warm MCP servers so the second call stops paying the first one's startup. The savings are measured: scripts/bench-tokens.mjs against nine real MCP servers (258 tools) puts the raw tool listing at 236,818 bytes and declick describe at 58,309, a 4.1x reduction (docs/bench.md). 631 tests pass.
--where k=von any verb filters a list before--fieldsand--limit, so rows are narrowed on the machine that has them. Repeatable, comma-separable, dotted paths, and eight operators:k=v,k!=v,k~re(case-insensitive regex),k>n,k>=n,k<n,k<=n, andk=*for present and not null. A number compares as a number and a bool as a bool; everything else is an exact string match, and a row with no value at the path fails the condition instead of stopping the run.meta.whereis{matched, of}andmeta.countis what matched. A condition alone unwraps the rows inside a response object the way--fieldsdoes. A condition on a verb that answers with one object is exit 1 naming the verb.- A default ceiling on what one envelope carries: 8192 bytes of
data, moved byDECLICK_MAX_BYTESor per call by--max-bytes N, and turned off with 0. It applies after--where,--rows,--fieldsand--limit, and never to--dry-run,--helpordescribe. A list drops tail rows and keeps at least one, a string is sliced, and an object keeps every key with its biggest values replaced by<N bytes; add --fields or --limit>, so the shape needed to write a--fieldssurvives any cap.meta.truncatedis true andmeta.cappedis{bytes, max, hint}; the exit code stays 0. A--eachbatch is held to the cap per item and never as a whole, since every entry is the record of a run that happened; a capped item carries its owncapped. --cache <seconds>on a read-only verb answers from the response the wire already gave. Entries live in~/.declick/<name>/cache/keyed on the adapter, the verb, its positional args and its own flags, so the same call shaped two ways reads one stored response. A hit skips the engine and reportsmeta.cache {hit: true, age}; a miss stores only a result that worked and reports{hit: false, stored: true}. The local policy still decides on a hit.--cacheon a mutating verb is exit 1 naming the verb,--dry-runneither reads nor writes,--cache 0andDECLICK_CACHE=offbypass it, anddeclick buildanddeclick removeclear it. A defaults file may set it per verb.declick audit --sumadds the run log up instead of listing it: one row per adapter and one total, sorted by bytes read. Every audit line now carriesbytes, the size of the envelope that run wrote to stdout, besidems, so212 calls, 340.2 KB read through adapters, 3 failedanswers what the adapters actually cost.--adapter,--sinceand--failednarrow the sum the way they narrow the lines; a line written beforebytesexisted counts as 0 rather than dropping the run. A run that asked for a cache recordscache: hitorcache: miss.where,max-bytesandcacheare reserved flag names now: a verb parameter called any of them is renamedparam-<name>on the next build.- The DashClaw guard has a contract test:
test/fixtures/dashclaw-guard-schema.jsonis DashClaw's guard input schema,test/guard-contract.test.mjsvalidates whatguardBody()sends against it, andnpm run qaposts one real body to$DASHCLAW_URLwhen a key is set. The 0.5.1 wire-format bug survived 566 tests because every guard test was a mock. - Warm MCP servers.
declick daemon start|stop|statusruns a detached per-user process that keeps stdio MCP servers alive between calls, so only the firstdeclick run <mcp adapter> <verb>pays the server's startup instead of every one of them. A run tries the daemon with a 300 ms connect budget and falls back to spawning its own server when nothing answers, so nothing breaks when it is not up; a served run carriesmeta.daemon: true. Servers are pooled per adapter and per what it was spawned from, dropped afterDECLICK_DAEMON_IDLE_MS(default 600000) idle, and the daemon exits once it has had none for the same window. The endpoint is per user (a named pipe on Windows,~/.declick/daemon.sockat 0600 elsewhere), every message carries the token in~/.declick/daemon.json(0600), and adaemon.jsonwhose pid is gone counts as no daemon.declick doctorreports it. HTTP MCP adapters are untouched, and the guard, the local policy and the audit line stay on the client side of the socket.
Install: npm i -g declick@0.6.0 · npm · declick.dev
declick 0.5.1
The DashClaw guard call now matches what a current DashClaw (5.31) accepts. Found by running strict mode against a real instance, which answered 401 to every call.
- The key is sent as
x-api-key. DashClaw readsAuthorization: Beareras an OAuth token, so anoc_livekey sent that way was "invalid token" and strict mode blocked every mutating verb. - The body is DashClaw's guard input:
action_type(withactionkept as the alias),agent_idandagent_nameofdeclick,declared_goal(declick run <adapter> <verb>),risk_score,target,systems_touched(the target host), and atoolobject carrying the adapter name, engine, method and redacted args, where a policy can match on them. The flat{tool, action, method, args}shape was silently stripped by the validator and refused withtool must be an object. - The call is
/api/guard?record=true, so every decision becomes an action record on the DashClaw dashboard, anddecision_idoraction_idis accepted as the approval id when the guard asks for one. - Tests, README and the site describe the new shape. 566 tests pass, qa 40 of 40.
Install: npm i -g declick@0.5.1 · npm · declick.dev
declick 0.5.0
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, ordeclick compose ops --steps ./chain.json(-reads stdin and keeps a copy the adapter owns, sodeclick build opsstill works);declick compose opsprints 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}withruna 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 owndeclickcommand 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 addrefuses a chain naming an adapter or verb that is not there, a template that names nothing, or areturnsthat points at no step. A failing step stops the chain with that step's exit code andstep <i> (<adapter> <verb>): <error>, withdata.stepscarrying everything that already ran;--dry-runpreviews 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.datais one entry per item (input,ok,exit,dataorerror) in input order, each shaped by its own--fields,--limitand--rows;metacarriescount,failedandeach: true. Every item is guarded on its own; a failing item does not stop the rest, a blocked item does (the entries behind it readnot 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.defaultslists the keys a run took from the file anddeclick describeprints them on one line. The file sits besidemanifest.json, sodeclick buildnever 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--clearthat fixes it, and leavesdescribe,lintandbuildworking.declick defaults <name> [--verb v] [--set k=v] [--unset k] [--clear] [--dry-run]prints or edits it, and every--setis checked against the flags that scope accepts.--no-defaultsandDECLICK_DEFAULTS=offskip the file. policy.json: a local per-verb policy that needs no service.$DECLICK_HOME/policy.json(orDECLICK_POLICY) holdsrules, each anadapterglob, averbglob, an optionalmutatingfilter, adecisionofallow,warnorblockand an optionalreason. First match wins; no file and no match are both allow. Ablockis exit 3 withblocked by policy: <reason>before anything is sent, including on read-only verbs, which never reached the guard before. Awarnwrites one stderr line and runs, and still defers to DashClaw when a key is set.--dry-runnever consults it. The file fails closed: unreadable JSON, a bad decision, arulesthat 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.sourceis"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 treetakes the same--grep, tested againstrole:nameandhref. The setup rules block and the Claude Code hook now point at it instead ofcurl | grep -c.- An openapi verb whose response is
text/event-streamis read as it arrives instead of buffered whole:datais an array of parsed events ({event?, id?, data}, JSON payloads parsed) andmeta.streamcarries{events, complete, truncatedByTimeout, ms}. When the timeout budget runs out mid-stream, the events already received come back withmeta.truncated: trueand exit 0 instead of a timeout with nothing. declick setupnever overwrites an adapter that already exists under the name it would pick: a server whose plain name collides falls back to<name>-mcponly when that name is free, otherwise it is skipped naming the adapter and its source. A bearer needed on the fallback attempt is reported asneeds <TOKEN> in the vault, not as a collision. The bin dir lands on the running process's PATH once installed, so setup no longer printsadd to PATH oncefor every adapter it builds.- The runtime's per-invocation work (flag check, policy, governance, credential scoping, the engine call) moved into one function, so
--eachand compose steps run the identical path. No change to what a single run answers.eachanddefaultsare reserved flag names now: a verb parameter called either is renamedparam-each/param-defaultson the next build.
Install: npm i -g declick@0.5.0 · npm · declick.dev
declick 0.4.1
- The standalone
revert.mjsin a setup snapshot ran nothing when its directory sat behind a symlink (macOS keeps temp dirs under/private/var, reached as/var): its "am I the entry point" check compared a real path with the typed one. Both sides now go through realpath. Caught by the macOS CI job on 0.4.0.
Install: npm i -g declick@0.4.1 · npm · declick.dev
declick 0.4.0
declick setup [--dry-run] [--no-adopt] [--no-rules] [--no-hook] [--no-path] [--keep-adapters]: wires declick into whatever agent is on the machine in one call. Puts~/.declick/binon PATH, builds an adapter for every MCP server it finds in.claude.json,.mcp.json, installed Claude Code plugins and Codex'sconfig.toml(a server needing a bearer it does not have is skipped and named, never dropped silently), adds a<!-- declick:start -->/<!-- declick:end -->rules block toCLAUDE.mdorAGENTS.mdtelling the agent to reach for a declick adapter before an MCP call, WebFetch, a browser read or raw curl, and (Claude Code only) installs a PreToolUse hook that nudges the model once per adapter per session. Running it twice is a no-op for the rules block and the hook and rebuilds no adapter already adapted.declick setup --revert: undoes exactly what setup did, from a byte-exact snapshot taken under~/.declick/setup/<timestamp>/before the first write. A file untouched since setup is restored byte for byte, or deleted if setup created it; a file edited since keeps the edits and loses only the rules block or the hook entry. The snapshot also carries a standalonerevert.mjs:node ~/.declick/setup/<timestamp>/revert.mjsrestores files with no dependency on the package, so it still works afternpm rm -g declick.declick uninstall [--yes] [--keep-adapters]: runs a revert if one is available, deletes~/.declickentirely, and prints thenpm rm -g declickline. Refuses without--yes, no interactive prompt.declick doctorreportsintegration: whether setup has run, whether a revert snapshot is available, which clients have the rules block and the hook, and how many discovered MCP servers are adapted. A~/.claudewith no rules block gets a warning pointing atdeclick setup, not a blocking failure.- The Claude Code hook ships in the package (
src/hooks/declick-nudge.cjs) and is copied to~/.declick/hooks/declick-nudge.cjsby setup; it maps an MCP tool call to its declick adapter through~/.declick/hooks/servers.jsonand never blocks a tool call. - New env:
DECLICK_CLIENT_HOMEnames the directory the agent clients live under (defaultos.homedir()), so every path setup touches can be redirected.DECLICK_NUDGE_OFF=1silences the hook.DECLICK_PATH_PROFILEis a test-only override that sends the shell-profile write to a named file instead of the real one.
Install: npm i -g declick@0.4.0 · npm · declick.dev
declick 0.3.2
- A reader that closes the pipe early (
declick add big-spec | head) gets a clean exit instead of an EPIPE stack trace, from both entry points. npm run qa(scripts/qa-real-specs.sh) is the release gate: six public specs compiled from their live URLs, real keyless calls, the auth path,path --installin a fresh login shell, the web page refusal and the Node guard, from the checkout or, withQA_FROM_NPM=1, from the published package. The 0.3.0 release shipped without it and none of those specs compiled.- CI jobs time out after ten minutes, so an open handle left by a failed test cannot hang a run.
Install: npm i -g declick@0.3.2 · npm · declick.dev
declick 0.3.1
Pre-launch QA of the published 0.3.0 on Windows and Linux, against Stripe, GitHub, Slack, Twilio, Openverse, api.weather.gov, apis.guru, two GraphQL endpoints, two reference MCP servers and a SQLite file. Every fix below came out of that pass and carries a regression test; 464 tests pass.
- License: 0.3.1 is the first release under the Elastic License 2.0 (SPDX
Elastic-2.0), reproduced in full inLICENSE. The source stays public and readable: use it, change it, redistribute it, ship it inside your own product. You may not offer it to others as a managed service. Release 0.3.0 as published on npm was released under the MIT license and stays under it. Commercial licenses for teams and production support: https://declick.dev. - Governance: a mutating verb with no
DASHCLAW_API_KEYset no longer writes a warning line to stderr. It runs, and the envelope recordsgovernance: {enabled: false, decision: "skipped", reason: "no guard configured"}. Piping a mutating call now produces the envelope and nothing else. - Skills:
addwritesSKILL.mdinto every agent skills directory that exists on the machine, not only~/.claude/skills:~/.codex/skills,~/.hermes/skills,~/.openclaw/skillsand~/.agents/skillswhen the directory is already there, and none are created for an agent that is not installed.DECLICK_SKILLSis a comma-separated override that replaces the list.declick skill <name> --printwrites one adapter's SKILL.md text to stdout instead of disk, for an AGENTS.md or a system prompt.skillTextis exported for callers that want to render it themselves. describepages itself instead of failing lint on a large surface: the page stays under the 2000 char ceiling and ends with a footer naming how many verbs are left, the total, and the flags that reach them (--grep,--offset,--limit,--verb).declick lintmeasures the paged page, so a 60 verb spec compiles.- YAML: zero-indent block sequences, plain-scalar folding, anchors before a sequence, sequence-item indent and multi-line quoted scalars all parse. Stripe's
spec3.yamlparses to 419 paths, Openverse's to 17. - Engine detection: a spec URL whose head does not show the openapi key no longer routes to the web engine, and a spec URL that answers 404 fails naming the status (
GET <url> -> 404) instead of reporting no recipes. run: a verb's compiledreturns.rowsPathis auto-unwrapped only when--fieldsor--limitis passed, so an unfiltered call returns the resource as the API sent it;rowsPathis compiled only for a list-shaped property.importreadsexport's envelope as it comes, sodeclick export <name> | declick import -round-trips without hand-editing the bundle.doctor:healthyfollowsblockingonly, so a fresh home whosebindirectory is not yet on PATH reports healthy with one warning rather than unhealthy.- A failed
addprints the first eight lint errors followed by a count of the rest, instead of one unreadable line. - Node below 24 prints one line naming the version it found and exits 1, from both
bin/declick.mjsandbin/run.mjs. The engine index pulls innode:sqliteat import time, so the check now runs before the engines load on either entry point. declick engineslists sqlite once, and reports desktop as not ready with "Windows only (deskclaw UI Automation)" on any platform that is not Windows.- Launcher: a launcher declick itself wrote is no longer read as a name collision, so
addandbuildwork afterdeclick path --installputs~/.declick/binon PATH. The.cmdtwin is written on Windows only. saveManifestnormalizes a description on the way to disk: collapsed to one line, then its first sentence, and a first sentence still over the bound cut back to the last word boundary (80 chars for a verb, 200 for args and flags).addandbuildnormalize before lint, so a spec with long, multi-line or backticked descriptions compiles: Stripe (594 verbs), GitHub (1224), Openverse and api.weather.gov all add from their public URLs.importstill lints the bundle as given, since a bundle is untrusted input.- The secret scanner skips
exampleanddefaultvalues, which are spec-provided text (GitHub's example SHAs are not credentials). - YAML: a mapping key that contains a colon (
read:pets:,x:y:) is a key, in block and flow context, so OAuth scope maps and a siblingapi_keyscheme survive; the YAML twin of the petstore spec now compiles the same auth keys as the JSON one. - OpenAPI: a spec that declares no
serversgets its origin as the base URL, not the spec's directory. A required header parameter that is not a security scheme (User-Agent,Accept) is an ordinary flag with a default, never an auth key.rowsPathis compiled only for a list-shaped property (data,items,results, ... or a small object whose other keys are pagination), in the openapi, mcp and postman engines alike;--fieldsnaming keys of the object itself projects the object instead of unwrapping. - A bad
--limit(0,abc) returns the failure envelope on stdout from both entry points instead of a bare stderr line. - Did-you-mean prefers a prefix match (
get-petsuggestsget-pet-by-id,descrsuggestsdescribe) before edit distance, in the runtime and the management CLI. addon a plain web page URL without theweb:prefix says it is not an API spec and shows both theweb:form and the spec alternative;engines --sourceon the same URL suggests theweb:form. A failedaddcapsdata.errorsat 50 and reportsdata.errorCount.path --installwrites the file the login shell reads:~/.zprofilefor zsh (the macOS default),~/.bash_profilewhen it exists for bash on macOS,fish_add_pathinconfig.fishfor fish,~/.profileotherwise. The stderr hint afteraddnames the same file.- The mcp and cli engines spawn
cmd.exeexplicitly instead of passingshell: true, so Node 24 no longer emits a DEP0190 warning into the output. creds: the "or run: creds mint " hint on an exit 4 appears only when that tool is on PATH.sqlite: aqueryverb called without--sqlsays so.add app:<w> --recipesruns the launcher and skill preflight before importing recipes, and a fresh adapter directory is rolled back when compile or lint refuses, so a refused add no longer leaves~/.declick/<name>/recipes/with no manifest.- README, docs, site and package description updated for agent-neutral skill discovery and the optional guard; DashClaw is explained and linked. The reference material (desktop recipe steps, the manifest, environment variables) moved to
docs/REFERENCE.md.
Install: npm i -g declick@0.3.1 · npm · declick.dev
declick 0.3.0
remove <name> <verb>now resolves the verb before touching disk: exit 2 for an unknown verb, exit 1 for a spec-backed engine (openapi verbs come from the spec, not per-verb files; rebuild with--verbsinstead), and--forceis required to delete the last desktop recipe (it would otherwise delete the whole adapter silently). The response for the last-verb case carriesadapterRemoved: true.--dry-runworks on every management command that writes:add,build,accept,import,skill,remove,path --install,desk arm|disarm. It compiles, lints and previews without writing anything, and setsmeta.dryRun: true.author,repairanduihave no preview (they drive a live app or a server) and refuse--dry-runwith an error, as doesadd --goalwithout--recipes.- Row projection:
--rows <path>unwraps a dotted array inside a response object (meta.rowsnames the path used,meta.extracarries the sibling fields such as a cursor or total). Without--rows, a verb whose spec says where its rows live (returns.rowsPath) auto-unwraps them when--fieldsor--limitis passed;describe,manifestand other non-verb payloads are never auto-unwrapped. --fieldsnow takes dotted paths (--fields error.code,items.0.name) resolved per row. A field list that matches nothing on any row is exit 1 naming the available top-level keys; a partial miss is reported inmeta.unknownFieldsinstead of failing.- openapi verbs carry a compiled
verb.returns({shape: 'array'|'object'|'scalar'|'none', fields, rowsPath?}) read from the first 2xx (ordefault) JSON response schema, capped at 30 fields withtruncated: true.describe --fullprints it as a->line;describe --jsonincludes it on every verb (nullwhen the spec has no response schema). Desktop verbs get a minimalreturns: {shape:'text', from:<read alias>}when the recipe declares one. importrefuses to silently replace an adapter that already exists under a differentsource,engineorbaseUrl: exit 1 naming what differs (data.diff), unless--force. The whole import is transactional: a mid-import failure rolls back only what that import created, leaving a prior adapter of the same name untouched.add/buildpreflight the launcher and skill name before writing anything, for the same reason.- Manifest validation (
declick lint, and everyadd/build/import) now rejects asource,window,baseUrl, arg name/description, flag name/description,returns.rowsPath/field name, orauth.enventry that spans multiple lines, contains a backtick, starts with#, or exceeds its length bound. This is the rule already applied to verb descriptions, now applied everywhere spec or bundle text reaches an agent. - SKILL.md generation is fence-safe: any
`` in an embedded description can no longer close the surrounding fenced block.writeSelfSkillno longer overwrites a hand-written `declick` skill in a skill dir; it only writes dirs where that file is absent or already carries the `Generated by declick` marker. - Seven new engines, all built in with no new dependency:
mcp(stdio and streamable http),web(a real browser over CDP; a miss returns the page's elements, never a screenshot),graphql,postman(postman v2.1 and insomnia v4),har,sqlite(tables and views become list/get/insert/update/delete plus a parameterizedquery), andcli(compiled from a tool's own--help). Swagger 2.0 and YAML specs compile through the openapi engine.declick engines --source <x>says where a source would land before anything is written;declick addnow also takes--host(which captured host is the API) and--url(the endpoint a GraphQL schema file does not carry). - New commands:
declick commandsanddeclick <cmd> --help(one row: flags, examples, whether it previews) render from the same table that generates the shippeddeclickskill, so the skill cannot drift from the CLI;declick auditreads the run log newest first;declick desk windows|tree|read|clipboardanddeclick web treereturn the desktop and a web page as data;declick import --exampleanddeclick manifest --schemaprint a valid bundle and the manifest field reference;declick describegained--grepand--offset;declick doctorseparatesblockingfromwarningsand probes each engine's tools. - Request flags on the HTTP engines:
--header,--base-url,--server,--content-type,--body-file,--output,--retry,--timeout,--verboseand--curl, listed bydescribe --full.--retryand--timeoutnow require a value instead of silently falling back to the default. - An api key that lives in the query string is masked in
--curland--verboseon live calls, the same as a header one; only the fetch itself sees the real value.--curlon a live multipart request now carries the body and its boundary content type instead of dropping both. - The cli engine quotes every argument it hands to a
.cmdor.battool, not only the ones containing a space: a value with&,|,>,<or^used to reach cmd.exe as a second command. Same fix for the mcp client's npx/npm shell path. - sqlite:
--limitis the output contract's flag, someta.countis the true row count andmeta.truncatedsays when there is more; a column named like a contract flag (limit,fields,output) is renamedparam-<name>and still reads and writes its own column, instead of making the whole table unaddressable. - A desktop recipe's
readstep re-resolves against a fresh tree before reading a property, so a read after aset,typeorclickreports what the action did instead of the value captured atfindtime. meta.credentialsnow recordsscopedToandsentTowhen a key is released across origins (--base-urlorDECLICK_ENV_ALLOW); the refusal on the blocked path no longer names the flag that would skip it.- Desktop recipes get a much larger step vocabulary:
read-all(every match of a path as rows),wait-for/wait-for-text(poll for an element or text up to a timeout),assert(compare a live value withequals/matches),clipboard get|set,dismiss(Escape),launch(start the app inline, or frommanifest.launchwhen the window is not open),scroll,expand,collapse,select,context(right-click),set(drive a toggle to a state), andoptional: trueon any step to skip instead of fail when its element is not there.declick desk windows|tree|read|clipboardexposes the same underlying reads as CLI commands. - Governance: once
DASHCLAW_API_KEYis set, strict is now the default: an unreachable, timed-out, or non-decision guard response is exit 3, not a warn-and-proceed (DECLICK_GUARD=openrestores the old fail-open behavior).DASHCLAW_URLhas no default endpoint and is required alongside the key. Every envelope carriesmeta.governance:{enabled,decision,reason}, and every invocation appends a redacted line to~/.declick/audit.jsonl(DECLICK_AUDIT=offto disable;declick auditreads it).declick uinow requires a per-startX-Declick-Tokenon every mutating POST. - deskclaw companion: declick 0.3 needs deskclaw 0.3.0 or newer for the attributed snapshot lines (
value=,toggle=,selected=,expanded=,offscreen=) the new read/assert steps depend on.
Install: npm i -g declick@0.3.0 · npm · declick.dev
declick 0.2.0
Everything an agent needs is now a declick command with the same JSON envelope and exit codes as the generated adapters.
- New commands:
run,status,doctor,auth,manifest,proposals,accept,recipes,recipe,skill,export,import,engines,version,path [--install],desk status|arm|disarm,remove <name> <verb>.helpexits 0.add --verbs a,b,--tag t,--engine e,--force;--recipesaccepts a file or-. - Every management command honors
--json(default when piped),--fields,--limit; errors are{ok:false,error,exit}envelopes.listsurvives a corrupt manifest.uiprints{url,port}on stdout and has an add form. - Flags:
--k=v,--no-flag, boolean flags never consume the next argument, repeated flags become arrays,--ends flags, bad--limitis exit 1, unknown flags on a verb are exit 1. Hyphenated query and body names work. - OpenAPI: relative server URLs resolve against the spec URL, server variables substitute, local
$refparameters and bodies resolve, path plus operation parameters dedupe, required query flags are enforced and shown, oauth2 / openIdConnect / cookie auth is sent, 204 and empty bodies succeed, form bodies use the right content type, missing operationIds no longer collide, reserved names are renamedparam-*, file sources are stored absolute. - Desktop:
findByPathbacktracks; a closed window is reported as such;last-error.jsonclears on success and after repair; undeclared{{vars}}are exit 1; stored recipes are validated; missing deskclaw is a clear message. - Authoring: the live replay goes through the governance guard; proposals are kept on every rejection including an unarmed desk; any ``` fence parses; timeouts are reported as timeouts; the child env is an allowlist; screen text is fenced as untrusted in the repair prompt;
mutatingdefaults to true. - Governance: shared
src/guard.mjswith a 3s timeout,r.okand decision checks,warnsurfaced, method-based risk scores,DECLICK_GUARD=strict. A block is a JSON envelope with exit 3. - Security: ui refuses foreign Host and Origin and non-JSON POSTs; adapter and verb names are validated on every path (no
../); SKILL.md frontmatter is quoted and sanitized and never overwrites a foreign skill; launchers refuse to shadow an existing executable; the secret scanner catches mid-string tokens and stops rejecting long kebab names; manifests are written atomically withmanifestVersion. removedeletes the launcher and the skill too.describeshows base URL or window and--full. SKILL.md gets runnable examples, thedeclick runfallback and engine-specific exit code advice. declick ships its own SKILL.md.- mcp and web stubs exit 1 (was 4, the auth code). YAML specs get a convert hint instead of a JSON parser error.
Install: npm i -g declick@0.2.0 · npm · declick.dev