A HARNESS FOR YOUR REPOS, OPERATED BY AGENTS.
Why a jig.
Drop-in AGENTS.md
An opinionated AGENTS.md + agent-map lands in the repo and tells the agent what the surface is, where to look, and what is in-bounds.
Stable contract surface
The agent calls into scripts/jig, which dispatches to manifest-defined tools — checks, tests, schema verification, migrations, release steps — and writes a receipt for every call. A Makefile adapter ships for humans; the contract doesn't depend on it.
Typed jig runtime
Each subcommand declares its arguments and return shape in .agent/jig-contract.json — the agent sends validated input and gets structured JSON back, not parsed stdout. An MCP server (Model Context Protocol) lets models discover and call those tools directly. No shell flags to memorize, no targets to guess.
Work gates + receipts
Required gates run on every change. Every run writes append-only JSONL receipts to .agent/state/ so you can audit what an agent attempted, when, and what passed.
Init → adopt → update.
The contract, on paper.
Rendered into your repo
Files jig writes and maintains. Everything outside this list stays repo-owned.
target-repo/ ├── .jig.toml — public config + renderer answers ├── .mcp.json — MCP wiring for agents ├── AGENTS.md — managed block; rest is yours ├── agent-map.md — surface area, in plain text ├──.agent/│ ├── PLANS.md — in-flight plans │ ├── jig-contract.json— machine-readable surface │ └──state/— append-only receipts (JSONL) ├── Makefile — optional adapter over scripts/jig ├──scripts/│ ├── jig — launcher; pins runtime version │ ├── install-jig.sh │ └── enforce-coverage.js └──.github/workflows/├── ci.yml └── release.yml
What an agent leaves behind
A streaming tail of .agent/state/receipts.jsonl. Reviewable. Diffable. Auditable.
Drive it from the terminal.
What you're watching
- scripts/jig agent doctor — verifies your environment is wired up for jig and its expected agent skills.
- scripts/jig agent bootstrap — installs the Codex-side skills the harness expects, pulling from the complementary jig-skills marketplace by default. No separate checkout required.
- Pass --marketplace <source> to override: a GitHub shorthand (bpcakes/jig-skills) or a local path (../jig-skills) both work.
Hostnames, not ports.
scripts/jig dev brings up every app in your repo behind a single local reverse proxy — no port juggling, no hand-typed localhost:5173, no stale tabs. Each app gets a stable hostname scoped to your repo.
A separate crate, crates/jig-dev-proxy, owns route storage, HTTP / HTTPS forwarding, HTTP/2 & WebSockets, a local CA, and optional launchd / systemd service files. It runs alongside the harness — not inside the jig contract — because routes, ports, and certs are mutable machine-local state, not auditable work.
[dev] proxy_port = 1355 https_port = 1443 https = false http2 = true lan = false tld = "localhost" workspace_discovery = false [[dev.apps]] name = "api" kind = "env-port" command = "cargo run --bin api" [[dev.apps]] name = "web" dir = "apps/web" kind = "vite" argv = ["bun", "run", "dev"]
Sealed. Audited. Redacted.
vault run --env TOKEN=gh_token -- gh release create resolves a named secret from an encrypted local vault, injects it into the child's environment only, redacts known forms of it from output, and records the run in a tamper-evident audit log.
A separate crate, crates/jig-vault, owns the encrypted store, broker, and audit chain. Argon2id passphrase derivation, XChaCha20-Poly1305 AEAD, owner-only filesystem permissions. Plaintext never lands in repo state, MCP results, command receipts, or Debug output.
Once a child process receives a secret in its environment, that process can use or disclose it. Redaction catches accidental output, not malicious transformations or side channels. The vault shrinks what your repo, your receipts, and your MCP transcripts can leak — it doesn't replace a real secret manager for production.
What it gates, what it leaves alone.
cargo fmt, cargo clippy — all assumed present on the default path..sqlx/ metadata + forward-only migrations.80%.~/.jig/proxy, outside .agent/state.AGENTS.md files, and schema implementations remain entirely repo-owned.Quickstart, two paths.
New repo · init
Bootstrap a target repo from the template, committed mode, with SQLx migrations.
# from anywhere jig init /path/to/target-repo \ --template /path/to/jig-sh \ --template-mode committed \ --repo-name target-repo \ --rust-migration-dir migrations # tooling-only? skip sqlx: jig init /path/to/target-repo \ --template /path/to/jig-sh \ --sqlx-enabled false
Existing repo · adopt
Drop the harness into a repo you already have. Only managed blocks change.
# inside the repo jig adopt . \ --template /path/to/jig-sh \ --template-mode committed \ --repo-name target-repo \ --rust-migration-dir migrations # later, pull in template updates jig update jig update --recopy # full re-render jig update --force # overwrite managed
Agents don't need more freedom.
They need a contract — and a witness.
Likely questions.
Why not just put instructions in AGENTS.md? +
scripts/jig tools with declared inputs and outputs in .agent/jig-contract.json, so the agent's actions are checkable, and writes receipts so they are auditable.Is jig opinionated? +
cargo fmt, cargo clippy, forward-only SQL migrations) is the most fleshed-out today. JavaScript/TypeScript web apps are supported via Bun (lint, typecheck, build:bundle, test:coverage). Other stacks land as the modular language adapters are extracted — if your stack isn't on either path yet, jig is currently the wrong tool, but the architecture is built so it doesn't have to stay that way.What languages and stacks does jig support today? +
frontend_apps config, with lint, typecheck, build:bundle, and test:coverage scripts run through Bun. The dev proxy, vault, receipts, gates, and CI workflows are stack-agnostic — they work the same regardless of the language under them. A modular language-adapter design is underway so additional stacks can plug in without forking jig itself.What does jig not generate? +
AGENTS.md files, your schema dump implementation, and the bulk of your repo. The template stays narrow on purpose — it only renders harness assets and the marked block of the root AGENTS.md.How do I update an adopted repo? +
jig update. By default it refuses to overwrite managed files you've edited locally. Pass --force if you really want the template to win, or --recopy to re-render from the stored answers in .jig.toml.Does it work without a database? +
--sqlx-enabled false. The migration and schema-check contract pieces are skipped; the rest of the harness is unaffected.Why is the dev proxy not part of the contract? +
jig-dev-proxy crate stores its state under ~/.jig/proxy (or JIG_PROXY_STATE_DIR) and is intentionally absent from .agent/jig-contract.json. scripts/jig dev and scripts/jig proxy ... are runtime-owned conveniences that sit beside the contract, not inside it.How do I get HTTPS locally without warnings? +
scripts/jig proxy cert generate to create a local CA + wildcard leaf, then scripts/jig proxy cert trust once to add the CA to your OS trust store. cert untrust reverses it. Browsers then see https://<app>.<repo>.localhost as a green padlock — no per-app cert juggling.Why does the vault exist if I already have 1Password / Vault / AWS Secrets Manager? +
Debug output. For production secret management, keep using your existing provider — the vault is a blast-radius reducer for local development, not a cloud KMS.Why can't I pass the passphrase on the command line? +
jig vault reads the passphrase only from JIG_VAULT_PASSPHRASE in the process environment, then clears the child-process variable after capture. Best-effort process hygiene — not a guarantee the OS overwrote every backing byte, but better than a flag any ps can see.How are releases done? +
Release GitHub Actions workflow. It prepares the release commit, updates CHANGELOG.md from conventional-commit prefixes, tags, publishes jig-sh to crates.io via trusted publishing, and creates a GitHub Release. A local release script remains the entry point for local validation and manual recovery.