--- name: create-ink-agent-cli-tool description: >- Use when creating, implementing, splitting, or reviewing a compiled, permission-bearing executable intended for admission through Ink policy. Produce one inspectable executable per authority boundary with discoverable help/contract and falsifiable safety proof. Do not use for Ink's own commands, ordinary or one-off CLIs/scripts, or skill authoring. --- # Create Ink agent CLI tool ## One job Design a **permission-sized executable** whose name and argv expose its reachable effects so Ink can discover, digest-pin, and grant it without granting a platform. One executable need not mean one source file: share private build-time modules when that does not widen runtime authority. ## Inspect and decide Read only what can change the boundary: - the exact job and every reachable side effect; - Ink's current requirements, policy grammar, and mutation protocol; - neighboring tools and existing executables that may already satisfy the job; - resource, credential, selector, target, packaging, and proof contracts; - repository requirements and tests. Project authority outranks this skill. Missing selector semantics, credentials, recovery rules, or external contracts are blockers—not adapter opportunities. ```text JOB -> REUSE? -> EFFECTS -> BOUNDARY -> CONTRACT -> ARTIFACT -> PROOF ``` 1. Reuse a directly inspectable executable only when granting its whole reachable surface is honest; otherwise build the coherent missing boundary, not a wrapper. 2. Enumerate reads, mutations, network effects, secrets, state, children, and config/plugin discovery; split where approval, blast radius, or recovery differ. 3. Define argv, selectors, streams, errors, dependencies, exhaustive versus bounded output, and any tool-owned semantic presentation without recreating shell grammar or a host-wide effect ontology. 4. State the runtime artifact honestly, then falsify its boundary, behavior, presentation, and portability claims through Ink's real run entry point. ## Permission boundary A digest proves **which executable** runs; it does not narrow that executable's accepted argv, config, plugins, or helpers. Reuse is correct only when granting the entire reachable surface is honest. Split executables when approval, blast radius, credentials, selectors, drift, or recovery differ. In particular: - read and mutation are separate executables even when they share transport; - upload and deletion split when recovery or approval differs; - a generic request, cloud-admin, SDK, MCP, or `call NAME JSON` dispatcher is not a permission boundary; - one executable per endpoint is needless fragmentation; - several read verbs may share one executable only when resource family, credential scope, selectors, trust, and output contract are the same. A script/dispatcher is the authority of its interpreter and every reachable helper unless the host pins and revalidates all of them. Resolve first-party helpers beside the script, constrain ambient lookup, and inventory dependencies. If the whole boundary is too broad, admit the narrower child executable instead. ## Readers and selectors A read executable must be structurally unable to mutate through any accepted flag, subcommand, config, plugin, callback, helper, credential refresh, cache, history, or telemetry path. For HTTP this normally means fixed GET/HEAD behavior, not an arbitrary method flag. “Dry run” does not turn a mutator into a reader. Use executable-level policy when every accepted invocation is one honest grant. Add semantic selectors only for variable resources users may reasonably grant differently, such as canonical path/root prefix, origin, repository, or account. - Canonicalize selector values before access and matching. - Prove an adjacent no-match causes no resource access. - Do not expose invariant facts such as `action=read` or `method=GET`; the executable already says that. - Stdin-only transforms normally need no selectors because they acquire only supplied input. - A pattern is a selector only when two patterns are genuinely different grants, not merely different queries below the same approved root. Share read/mutation parsing or transport only through private build-time modules. ## Patterns and search Shell globs, path filters, and content patterns are different contracts. - Never reinterpret argv as shell syntax or perform ambient shell expansion. - Accept a glob, fixed string, regex, or domain expression only through an explicit mode such as `--include-glob`, `--fixed`, or `--ere`; never infer the mode from punctuation. - Keep matching as a separate stdin filter when all candidate bytes or records have already been supplied. - Put matching inside a resource reader only when it avoids opening, transferring, traversing, or disclosing irrelevant resources inside that same reader boundary. Canonical root/path selectors remain the authority boundary. - Define one small established grammar. State root-relative versus basename matching, separators, case/dotfile/symlink behavior, pattern combination, include/exclude precedence, and invalid-pattern failure. Validate before access where possible. `find`/`fd` forms that execute or delete and `rg` forms that launch preprocessors are not read boundaries merely because one intended invocation only searches. A narrow native search tool is justified when it removes reachable effects, adds canonical path selectors, or supplies the required static portable artifact. Implement the coherent missing subset, not a compatibility facade or renamed wrapper. ## Mutators Ink mutators use its elected semantic protocol: ```text tool stage # pure; emit pinned manifest tool match # deterministic policy decision tool apply # revalidate; perform once ``` Required properties: - `stage` performs no external effect and resolves no secrets; - the manifest pins executable identity, canonical effect, authority subject, non-secret inputs, and drift-sensitive hashes; - selectors are variable semantic facts, never argv prefixes, shell text, or executable invariants; - `apply` revalidates executable, manifest, policy snapshot, inputs, and drift; - credentials resolve only at apply; - retries cannot duplicate success; use upstream idempotency or reconciliation; - an indeterminate upstream outcome is reported as indeterminate, never retried blindly or called exactly-once; - receipts are stable, secret-free, and sufficient for recovery; - no direct mutation path remains beside the protocol unless project authority explicitly places it outside policy. ## Semantic presentation When Ink must show a domain operation better than raw stdout, the executable must construct one canonical staged operation and derive a deterministic, bounded **presentation projection** from it. The projection is not a second description: the manifest binds the operation and exact semantic projection bytes. Ink owns layout; the tool owns meaning. Use the repository's elected projection envelope and bounds exactly; never invent per-tool presentation formats. Within that contract, use a small display vocabulary rather than universal effect kinds: - headline and canonical target; - ordered key/value facts; - bounded text or unified diff; - outcome and optional evidence reference. Filesystem, HTTP, cloud, and infrastructure tools express their own semantics with those primitives. For example, an HTTP mutator supplies method, canonical origin/path, bounded body summary, status, and final URL as facts; it does not ask Ink to understand an `http` effect type. An infrastructure tool supplies account, resource, region, and requested change as facts; it does not create a renderer branch for its provider. The projection is presentation evidence, never authority: - canonicalize argv and pinned inputs once into the staged operation; - derive projection records purely from that value and hash their exact semantic bytes with the manifest; - render approval from the exact staged projection stored under that identity; - apply accepts only staged id plus hash, never replacement target, body, effect, or projection inputs; - the receipt identifies the exact staged manifest and may add only outcome and evidence facts; it cannot rewrite approved records; - final rendering reuses the staged projection and overlays only validated receipt facts, so there is no independently generated “final projection” to drift; - it grants no policy selector, credential, retry, or execution semantics; - it is secret-free, width-independent, stable across locale/timezone, and bounded with an explicit omission or evidence handle; - hash semantic records, never width-, theme-, or expansion-dependent rows; - tools never emit colors, cursor control, borders, wrapping, or terminal layout; - legacy and ordinary commands without a projection remain valid and use Ink's generic argv/stdout/stderr fallback. Do not add presentation metadata when argv plus bounded output already answers what happened. Do add it for permission-bearing operations whose approval would otherwise expose manifests, hashes, encoded payloads, or provider internals. ## CLI and stream contract `tool --help` is the tested human contract: effects, argv, streams, ordering, exits, environment/config precedence, credential timing, dependencies, pattern semantics, and one realistic pipeline. Explicit help succeeds on stdout; usage errors fail on stderr. Selector help gives value grammar, canonicalization, and a least-authority policy row, including AND within one row and alternatives across rows. Keep argv unsurprising: options before operands, `--` ends options, `-` denotes a natural stream, secrets never enter argv, and unknown, incompatible, or trailing arguments fail. No hidden account, cwd, config, resource search, prompt, pager, color, progress, or `/dev/tty` access. Stdout is records, stderr diagnostics; records stream with bounded memory, ordinary SIGPIPE stays quiet, and machine output is independent of display width, locale, and timezone unless documented. Choose one output posture: - **Exhaustive:** emit every record and let ordinary pipes narrow it; no hidden limit, ranking, or cursor. - **Bounded:** bound before generation, disclose omissions, and provide a stable cursor, detail command, omitted count, or retrievable handle. Line-oriented TSV contains no literal TAB, LF, CR, or NUL; use a documented versioned codec or NUL-record mode only when round-trip values require it. JSON is opt-in when structure is the payload, never the default adapter around records. Add `tool contract` only for Ink capability discovery. Keep it deterministic, versioned, and machine-readable, with exactly one row per variable selector and none for invariants. Metadata never chooses policy defaults. Ink pins executable and contract bytes; schema versions belong in output, not executable, policy, or skill names. Make the executable name and one-line job obvious on the collection's normal install or help surface; do not create a parallel catalog that repeats `--help` or `contract`. In a recommended Ink policy row, make text after `--` useful to the provider: lead with one copyable invocation, then the job. For semantic mutators advertise only `NAME stage ...` and state that Ink owns match/apply. ## Portability and artifact Name the claim precisely: - **POSIX-specified:** behavior is actually specified by POSIX; - **POSIX-portable custom:** a custom CLI using portable POSIX facilities; - **POSIX-shaped:** composes with POSIX streams but uses extensions; - **platform-specific:** requires named platform facilities. Cross-compilation proves compilation, not runtime portability. Test representative targets and ordinary hostile conditions: missing HOME/config, non-writable cwd, EOF/short I/O, non-TTY streams, interruption, and relevant locale/timezone variation. Avoid assumptions about `/proc`, GNU userland, bash, writable state, or fixed path lengths. “Single executable” permits only documented runtime libraries; “static” permits none. Inspect the produced artifact. Do not add daemons, plugins, sidecars, interpreters, or generated-client machinery unless they are the named job. ## Proof Use the smallest matrix that can falsify the actual claims: - help/contract agree with accepted argv and selectors; - main path and one forbidden near miss with zero unintended effect; - each reader selector has an adjacent no-match before access; - each mutator has admitted, approval-required, refused, drift, duplicate, and indeterminate/recovery outcomes as applicable; - a projected operation renders the exact staged target and change before and after apply, with only receipt-bound outcome/evidence added; mutation of stored projection bytes, replacement apply inputs, malformed or oversized records, secret-bearing data, and receipt identity mismatch all fail closed; - exhaustive output preserves every fixture record; bounded selection proves its bound, omission signal, and continuation/detail path; - stdout/stderr redirection, `tool | head`, empty/malformed input, `-`, and a path beginning with `-`; - selected record codec edge cases, including TAB/LF/CR/backslash/NUL as relevant; - clean environment, non-TTY, target runtime smoke, and artifact dependency inspection; - Ink discovers, pins, decides, and executes the tool end to end. Do not build a universal harness. One meaningful forbidden-near-miss test is worth more than broad ceremonial coverage. ## Refusals Do not: - force one source file when one executable is the requirement; - wrap or partially clone a utility whose whole admitted surface already fits; - combine read and mutation for code reuse; - expose a generic request/admin/registry platform; - invent invariant selectors, a universal effect ontology, or executable-name branches in Ink's renderer; - let tools choose terminal styling or let presentation metadata grant authority; - treat argv prefixes, globs, or regexes as canonical path authority; - add pagination where a complete stream and pipes are simpler; - dump bodies from a bounded selection or silently truncate a complete stream; - claim read-only, portable, static, or exactly-once without falsifiable proof; - register or recommend the tool before its help, boundary, near miss, policy, composition, and target artifact are proven. ## Handoff Report only: - `job` and `boundary`; - argv/stream/output posture; - authority selectors and credential timing; - artifact and runtime dependencies; - proof through Ink's real run path, including staged-projection/receipt identity; - remaining blast radius or untested target. ## Evaluation - **Mutator:** an S3 uploader splits from deletion, uses `stage/match/apply`, resolves credentials at apply, and proves duplicate and indeterminate outcomes. - **Reader:** repository reads use canonical `path-prefix` plus an adjacent no-access proof; executable identity makes `action=read` an invalid selector. - **Reuse:** grant existing `grep` only if its whole surface is honest; otherwise build the missing bounded reader, not a compatibility wrapper. - **Pattern:** a reader may own a root-relative glob only to avoid access; matching supplied records remains a stdin filter, not path authority. - **Presentation:** an HTTP mutator derives method, canonical target, and bounded body summary from one staged operation; Ink renders the exact stored projection without an `httpsend` branch and overlays receipt-bound status/final URL only. - **Presentation sprawl:** “support AWS changes” does not add an AWS effect enum or renderer branch; the permission-sized infrastructure tool projects account, region, resource, requested change, outcome, and evidence through the elected generic vocabulary. - **Portability/sprawl:** a cross-build alone proves neither runtime portability nor static linkage; refuse daemons, SDKs, dispatchers, and policy editing.