Add Ink skills and symlink installer
This commit is contained in:
@@ -0,0 +1,18 @@
|
|||||||
|
# AGENTS.md
|
||||||
|
|
||||||
|
`ink-skills` contains reusable Ink-specific Agent Skills and one symlink installer.
|
||||||
|
|
||||||
|
## Layout
|
||||||
|
|
||||||
|
- `skills/<name>/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.
|
||||||
@@ -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.
|
||||||
Executable
+151
@@ -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
|
||||||
@@ -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.
|
||||||
@@ -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 <operation argv...> # pure; emit pinned manifest
|
||||||
|
tool match <manifest> <selectors> # deterministic policy decision
|
||||||
|
tool apply <id> <hash> # 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.
|
||||||
@@ -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: <runtime output, installed artifact, matching source, or limitation>
|
||||||
|
SURFACE: <operator CLI, model built-in, or admitted external command>
|
||||||
|
FINDING: <observed/source-confirmed/missing/stale/not proven>
|
||||||
|
ACTION: <operator step, requirements step, or none>
|
||||||
|
```
|
||||||
|
|
||||||
|
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`.
|
||||||
Executable
+33
@@ -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'
|
||||||
Reference in New Issue
Block a user