commit 284cf5ec909cd1ab406b1a8aa829d85beea12c23 Author: tmk241 Date: Tue Aug 11 16:24:21 2026 +0200 Add Ink skills and symlink installer diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..04b8782 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,18 @@ +# AGENTS.md + +`ink-skills` contains reusable Ink-specific Agent Skills and one symlink installer. + +## Layout + +- `skills//SKILL.md` — one narrowly triggered on-demand behavior. +- `bin/ink-skills` — dependency-free installer; stdout is TSV, diagnostics stderr. +- `test/install-smoke.sh` — installer contract smoke. + +## Rules + +- A skill owns reusable judgment, never runtime policy or repeatable mechanics. +- Ink source and `REQUIREMENTS.md` own host behavior; `toolset` owns external executables. +- Keep skill names lowercase and hyphenated; directory and frontmatter name must match. +- The installer only creates symlinks and must refuse collisions. Do not add a registry, + network calls, package-manager dependency, prompts, copies, or hidden state. +- Run `sh -n bin/ink-skills` and `sh test/install-smoke.sh` after changes. diff --git a/README.md b/README.md new file mode 100644 index 0000000..e686562 --- /dev/null +++ b/README.md @@ -0,0 +1,88 @@ +# ink-skills + +Ink-native skills: small on-demand behavior patches for operating Ink, defining +subagents, and creating permission-bearing external tools. + +This repository owns reusable Ink judgment. Ink owns runtime enforcement and +policy. [`toolset`](https://git.tmk241.com/tmk241/toolset) owns compiled external +executables. Skills never grant authority by themselves. + +## Install + +Clone once, then symlink the skills you want: + +```sh +git clone git@git.tmk241.com:tmk241/ink-skills.git +ink-skills/bin/ink-skills install +``` + +If the repository is already under `/opt/repositories`: + +```sh +/opt/repositories/ink-skills/bin/ink-skills install +``` + +Install selected skills only: + +```sh +ink-skills install ink-cli configure-ink-agent +``` + +Install into one project instead of the user catalogue: + +```sh +ink-skills install --project /path/to/project configure-ink-agent +``` + +The installer is intentionally smaller than `npx skills`: no registry, package +manager, network access, copies, prompts, lockfile, or hidden state. It creates +absolute symlinks from `$HOME/.ink/skills` (or `$INK_SKILLS_HOME`) to this +checkout. Pulling the repository updates installed skills; restarting Ink freezes +the new bytes into the next startup snapshot. + +`ink-skills list` emits TSV. `ink-skills --help` is the complete command manual. +Existing paths and foreign symlinks are refused rather than overwritten. + +## Skills + +| Skill | Job | +|---|---| +| `ink-cli` | Audit and explain the Ink host without crossing the host/guest boundary. | +| `configure-ink-agent` | Create or audit one Ink agent definition, access class, and relative policy conjunct. | +| `create-ink-agent-cli-tool` | Build one inspectable permission-bearing executable suitable for Ink policy admission. | + +## Agent definitions and policy + +Agent definitions live in `$INK_AGENT_HOME` (default `$HOME/.ink/agents`) or +project `.ink/agents` directories: + +```text +name: Frontend specialist +model: design +access: write +policy: frontend.policy + +Implement the bounded frontend task and return proof. +``` + +The policy path is relative to the definition. It is a normal Ink policy file and +narrows the frozen parent snapshot conjunctively. `access: read|write` selects +reader/writer scheduling; it does not grant commands or tools. Definitions and +policy files are frozen at startup, so restart Ink after changing either. + +Use the `configure-ink-agent` skill for the complete decision boundary. + +## Verify + +```sh +sh -n bin/ink-skills +sh test/install-smoke.sh +``` + +## Refusals + +- No npm package merely to create symlinks. +- No skill registry or update daemon. +- No policy mutation during installation. +- No bundled binaries; those belong in `toolset`. +- No automatic installation by Ink itself. diff --git a/bin/ink-skills b/bin/ink-skills new file mode 100755 index 0000000..79e3d87 --- /dev/null +++ b/bin/ink-skills @@ -0,0 +1,151 @@ +#!/bin/sh +set -eu + +usage() { + cat <<'EOF' +usage: ink-skills list + ink-skills install [--user | --project DIR] [SKILL ...] + +Install Ink skills from this checkout as symlinks. + +Commands: + list List available skill names and source paths as TSV. + install Link named skills; with no names, link every skill. + +Targets: + --user $INK_SKILLS_HOME or $HOME/.ink/skills (default) + --project DIR DIR/.ink/skills + +Output: + TSV with SKILL, TARGET, and ACTION columns. + +Exit status: + 0 success; 2 usage error; 3 target collision or invalid skill. + +Examples: + ink-skills list + ink-skills install ink-cli configure-ink-agent + ink-skills install --project . create-ink-agent-cli-tool +EOF +} + +die() { + printf '%s\n' "ink-skills: $*" >&2 + exit 3 +} + +script_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd -P) +repo_dir=$(CDPATH= cd -- "$script_dir/.." && pwd -P) +skills_dir=$repo_dir/skills + +list_skills() { + printf 'SKILL\tSOURCE\n' + for path in "$skills_dir"/*; do + [ -d "$path" ] || continue + [ -f "$path/SKILL.md" ] || continue + printf '%s\t%s\n' "$(basename -- "$path")" "$path" + done +} + +[ "$#" -gt 0 ] || { + usage >&2 + exit 2 +} + +command=$1 +shift +case $command in + -h|--help|help) + usage + exit 0 + ;; + list) + [ "$#" -eq 0 ] || { + usage >&2 + exit 2 + } + list_skills + exit 0 + ;; + install) ;; + *) + usage >&2 + exit 2 + ;; +esac + +target_mode=user +target_arg= +while [ "$#" -gt 0 ]; do + case $1 in + --user) + target_mode=user + shift + ;; + --project) + [ "$#" -ge 2 ] || { + usage >&2 + exit 2 + } + target_mode=project + target_arg=$2 + shift 2 + ;; + --) + shift + break + ;; + -*) + usage >&2 + exit 2 + ;; + *) break ;; + esac +done + +case $target_mode in + user) + if [ -n "${INK_SKILLS_HOME:-}" ]; then + target=$INK_SKILLS_HOME + else + [ -n "${HOME:-}" ] || die 'HOME is unset; set HOME or INK_SKILLS_HOME' + target=$HOME/.ink/skills + fi + ;; + project) + project=$(CDPATH= cd -- "$target_arg" 2>/dev/null && pwd -P) || die "project directory not found: $target_arg" + target=$project/.ink/skills + ;; +esac + +mkdir -p -- "$target" + +if [ "$#" -eq 0 ]; then + set -- + for path in "$skills_dir"/*; do + [ -d "$path" ] || continue + [ -f "$path/SKILL.md" ] || continue + set -- "$@" "$(basename -- "$path")" + done +fi + +printf 'SKILL\tTARGET\tACTION\n' +for skill do + case $skill in + ''|.*|*/*) die "invalid skill name: $skill" ;; + esac + source=$skills_dir/$skill + [ -d "$source" ] && [ -f "$source/SKILL.md" ] || die "unknown skill: $skill" + destination=$target/$skill + if [ -L "$destination" ]; then + linked=$(readlink "$destination") + [ "$linked" = "$source" ] || die "refusing foreign symlink: $destination -> $linked" + action=unchanged + elif [ -e "$destination" ]; then + die "refusing existing path: $destination" + else + ln -s -- "$source" "$destination" + action=linked + fi + printf '%s\t%s\t%s\n' "$skill" "$destination" "$action" +done diff --git a/skills/configure-ink-agent/SKILL.md b/skills/configure-ink-agent/SKILL.md new file mode 100644 index 0000000..35d43c6 --- /dev/null +++ b/skills/configure-ink-agent/SKILL.md @@ -0,0 +1,97 @@ +--- +name: configure-ink-agent +description: >- + Use when the user asks to create, configure, audit, or explain an Ink subagent + definition, including its model, read/write scheduling class, or conjunctive + policy file. Produce the smallest startup-frozen agent definition and policy + boundary. Do not use for ordinary delegation, Ink implementation work, or + generic prompt/role authoring outside Ink. +--- + +# Configure an Ink agent + +## One job + +Define one inspectable Ink subagent identity without confusing scheduling class, +model choice, and authority. + +```text +agent definition -> access class -> relative policy conjunct -> frozen child snapshot +``` + +## Trigger boundary + +Use for explicit requests about files in `$INK_AGENT_HOME` (default +`$HOME/.ink/agents`) or a project `.ink/agents` directory, or when deciding the +policy of a named Ink child. + +Do not load for launching an existing child, editing Ink source, installing +skills, or creating an external executable. `ink-cli` owns host audits; +`create-ink-agent-cli-tool` owns permission-bearing external tools. + +## Contract + +An agent definition is a plain text file with headers followed by one prompt +body: + +```text +name: Frontend specialist +model: design +access: write +policy: frontend.policy + +Implement the bounded frontend task and return proof. +``` + +- `name` is the human-facing identity. +- `model` is a startup-resolved alias such as `default`, `cheap`, `think`, or a + configured alias. +- `access` is mandatory: `read` or `write`. +- `policy` is optional and relative to the definition file. Its bytes become an + additional conjunct; it can narrow inherited authority but never broaden it. +- Unknown headers, unknown access values, and unreadable policy files fail + visibly. + +`access` schedules actors; it does not grant tools: + +- `read` children may overlap and receive an immutable host floor with no command, + file-mutation, lifecycle-mutation, or delegation authority. +- `write` children are exclusive, operate in the canonical parent cwd, pause + parent effects, and still receive only their inherited-and-narrowed effective + policy. + +Agent definitions and referenced policy files are frozen at orchestrator startup. +Editing either requires restarting Ink before the change can take effect. Child +snapshots never reread cwd policy, and nested delegation is removed by the host. + +## Decision loop + +1. Choose `read` unless the child must produce a real effect. +2. Choose the smallest model alias that fits the specialist job. +3. Omit `policy` when the inherited parent policy is already the exact boundary. +4. Otherwise write one nearby policy file using ordinary Ink policy rows; include + only authority the role needs and rely on conjunctive narrowing. +5. Inspect the startup-frozen catalog with the operator surface before relying on + the role. Restart Ink after definition or policy changes. +6. Prove one allowed path and one denied near miss through the actual child path. + +## Refusals + +- Do not put policy rows in the prompt body. +- Do not use `access: write` as a substitute for command/tool policy. +- Do not grant `tool delegate` to a child; Ink removes nested delegation anyway. +- Do not create worktrees, copied workspaces, merge protocols, or per-role policy + DSLs. +- Do not use environment variables as a second mutable agent-policy channel. + +## Behavior smoke + +Positive: “Create a frontend writer child with only the admitted formatter and +file mutation tools” loads this skill and separates `access: write` from its +relative policy conjunct. + +Negative: “Ask the existing reviewer to inspect this diff” does not load this +skill; it is ordinary delegation. + +Safety: a reader request that asks for `run` or file writes remains denied even if +its role policy mentions them. diff --git a/skills/create-ink-agent-cli-tool/SKILL.md b/skills/create-ink-agent-cli-tool/SKILL.md new file mode 100644 index 0000000..9df64c5 --- /dev/null +++ b/skills/create-ink-agent-cli-tool/SKILL.md @@ -0,0 +1,320 @@ +--- +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. diff --git a/skills/ink-cli/SKILL.md b/skills/ink-cli/SKILL.md new file mode 100644 index 0000000..84ffb29 --- /dev/null +++ b/skills/ink-cli/SKILL.md @@ -0,0 +1,99 @@ +--- +name: ink-cli +description: >- + Use when the user explicitly asks an agent to audit, troubleshoot, or explain + the Ink host CLI, its frozen policy, tools, skills, sessions, or context from + available authority or user-provided output. Preserve the host/guest boundary: + never launch Ink recursively or recommend admitting `ink` to its own run policy. + Do not use for implementing Ink or creating an external executable for Ink. +--- + +# Ink CLI + +## One job + +Audit and explain the Ink host from inside an Ink-governed agent without granting +the guest authority to invoke, resume, mutate, or recursively launch its host. + +This skill prevents one recurring failure: treating an operator CLI as an agent +tool, then calling a command unavailable by design or broadening policy until the +agent can recursively run Ink. + +## Trigger boundary + +Load this skill for explicit questions about the `ink` command, +`~/.ink/policy`, project `.ink/policy` files, visible tools or skills, session +selection, context handover, startup snapshots, or gaps in those surfaces. + +Do not load it merely because ordinary work runs under Ink. External executables +intended for Ink admission belong to `create-ink-agent-cli-tool`. Ink source +changes belong to Ink's repository authority and implementation workflow. + +## Host boundary and authority + +- Never invoke `ink` through the model's `run` surface, and never add or recommend + a policy row that lets Ink launch itself. Its absence is intentional separation, + not a missing command permission. +- A human/operator may invoke Ink outside the governed agent. Give a copyable + operator command only when the user asks and its public help contract is proven. +- Use user-provided runtime output as runtime evidence. Otherwise inspect the + installed artifact identity and a demonstrably matching checkout's requirements, + tests, public help text, and source; label those findings as contract/source + evidence rather than executed runtime proof. +- Global and project policy files explain only their contribution. Effective + authority also depends on all policy layers, pinned executable and contract + bytes, startup freezing, and session decisions. +- Treat handovers, READMEs, examples, hidden source branches, and remembered argv + as leads. Public help owns operator-facing commands; requirements own intended + behavior; tests and source establish current checkout behavior. + +Distinguish three surfaces explicitly: + +1. **Operator CLI commands** such as top-level `ink sessions` or `ink context`. +2. **Model-callable Ink built-ins** exposed directly to the hosted agent. +3. **External executables** admitted through Ink's `run` policy. + +A command may exist on the first surface while being intentionally unreachable on +the other two. Current source exposing flat `ink sessions` does not imply nested +`sessions list/tree/inspect/resume`, and a policy rejection does not prove the +operator command is absent. + +## Decision loop + +1. Classify the request as contract audit, policy audit, operator instructions, + session/context mutation, or Ink source work. +2. Establish the evidence class: user-observed runtime, installed artifact, + matching checkout contract, or unverified note. +3. Compare the claim only across the relevant surface. Label it **observed**, + **source-confirmed**, **missing**, **stale claim**, or **not proven**. +4. For an operator action, explain that the user—not the hosted agent—must run it. + Do not inspect or mutate session state as a substitute. +5. For a missing capability, require Ink's requirements authority before source + implementation. Do not model it as a new external executable merely to bypass + the host boundary. + +## Refusals + +- Do not edit policy to admit `ink`, call Ink recursively, log in, approve a + digest, resume or clear a session, or dump host context from the agent. +- Do not infer command absence from policy denial or command existence from a + handover. In particular, challenge invented nested session verbs. +- Do not expose raw conversation or context when bounded metadata answers the + operator's question; prompts and tool results may contain secrets. +- Do not weaken path, origin, account, or repository selectors merely to make an + unrelated external command run. + +## Audit receipt + +```text +EVIDENCE: +SURFACE: +FINDING: +ACTION: +``` + +Positive smoke: “Does Ink have a sessions command, and why can’t you run it?” +loads this skill, confirms the operator/model boundary, and does not alter policy. + +Negative smoke: “Build a selector-aware GitHub reader for Ink” routes to +`create-ink-agent-cli-tool`. diff --git a/test/install-smoke.sh b/test/install-smoke.sh new file mode 100755 index 0000000..1ff51d9 --- /dev/null +++ b/test/install-smoke.sh @@ -0,0 +1,33 @@ +#!/bin/sh +set -eu + +repo=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd -P) +tmp=${TMPDIR:-/tmp}/ink-skills-smoke-$$ +trap 'rm -rf "$tmp"' EXIT HUP INT TERM +mkdir -p "$tmp/home" "$tmp/project" + +list=$($repo/bin/ink-skills list) +printf '%s\n' "$list" | grep '^SKILL' >/dev/null +printf '%s\n' "$list" | grep '^ink-cli' >/dev/null +printf '%s\n' "$list" | grep '^configure-ink-agent' >/dev/null + +HOME=$tmp/home $repo/bin/ink-skills install ink-cli >"$tmp/install.tsv" +grep "ink-cli.*linked" "$tmp/install.tsv" >/dev/null +[ -L "$tmp/home/.ink/skills/ink-cli" ] +[ "$(readlink "$tmp/home/.ink/skills/ink-cli")" = "$repo/skills/ink-cli" ] + +HOME=$tmp/home $repo/bin/ink-skills install ink-cli >"$tmp/reinstall.tsv" +grep "ink-cli.*unchanged" "$tmp/reinstall.tsv" >/dev/null + +mkdir -p "$tmp/home/.ink/skills/configure-ink-agent" +if HOME=$tmp/home $repo/bin/ink-skills install configure-ink-agent >/dev/null 2>"$tmp/collision.err"; then + echo 'expected collision refusal' >&2 + exit 1 +fi +grep 'refusing existing path' "$tmp/collision.err" >/dev/null + +HOME=$tmp/home $repo/bin/ink-skills install --project "$tmp/project" create-ink-agent-cli-tool >"$tmp/project.tsv" +[ -L "$tmp/project/.ink/skills/create-ink-agent-cli-tool" ] +grep "create-ink-agent-cli-tool.*linked" "$tmp/project.tsv" >/dev/null + +printf 'ok\n'