Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

114 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

minih

The miniest self-improving agent harness.

As of plan 018 (R6): agents now run with restricted permissions by default — read+MCP only, no shell/write/network. Existing installs are grandfathered via sidecar lockedDefault. Override per-run with --permissions yolo (legacy posture) or per-agent with permissions: frontmatter. See docs/how/permissions.md.

Define AI agents as folders containing prompt.md + optional schemas + instructions, then run them against @github/copilot-sdk. Every agent produces structured retrospective feedback — what worked, what was confusing, and a magic wand wish for what should change. This feedback loop makes both the agents and the harness better over time.

For long-running or multi-agent workflows, minih also supports coordination-aware agents: opt-in agents with an outside peer contract, run-scoped inbox/state files, outside CLI commands, and inside MCP tools for progress handoffs. Start with AGENTS_README.md#coordination-aware-agents, then use docs/how/coordination-loop-validator.md for the canonical rich worked example.

Quick Start

The Fast Way

export GH_TOKEN=$(gh auth token)
npx github:AI-Substrate/minih quickstart

One command — scaffolds a hello-world agent, runs it, shows the results. Zero to success in 60 seconds.

Each run's retro lands in docs/retros/<slug>.md (review before commit). See AGENTS_README.md § The Improvement Loop.

Pin a specific version: npx github:AI-Substrate/minih#v0.x.y quickstart

The Manual Way

1. Install

# Latest (HEAD)
npm install github:AI-Substrate/minih

# Or pin a release
npm install github:AI-Substrate/minih#v0.x.y

# minih uses @github/copilot-sdk as a peer dependency
npm install @github/copilot-sdk

2. Create Your First Agent

npx minih init my-agent

This creates:

agents/my-agent/
├── prompt.md             # Your agent's prompt (with YAML frontmatter)
├── output-schema.json    # JSON Schema for structured output
└── instructions.md       # Agent identity and rules

3. Edit the Prompt

Open agents/my-agent/prompt.md and write what your agent should do:

---
description: "Analyze code for common security issues"
tags: [security, review]
---

# Security Scan

## Objective
Scan the project for common security issues...

4. Run It

export GH_TOKEN=your-github-token
npx minih run my-agent

The agent executes, produces JSON output (with a self-improving retrospective), and stores everything in agents/my-agent/runs/<timestamp>/.

5. Check the Results

npx minih last-run my-agent    # Show latest run path
npx minih history my-agent     # List all past runs
npx minih validate my-agent    # Re-validate latest output against current schema

Agent Packs — install curated agents in one command

Don't hand-copy agents between projects. Install them instead:

minih agent install code-review-companion           # from the bundled registry
minih agent install github:owner/repo#main:agents/x # from any public GitHub repo
minih agent install /path/to/local/agent-folder     # from a local clone

Each install copies the agent's manifest-listed files into agents/<slug>/, writes a provenance sidecar (.minih-source.json), and is idempotent — re-running upgrades from upstream and atomic-swaps changed files while preserving runtime data (runs/, inbox/, state/).

Browse the bundled catalog:

minih agent list --available    # what you can install by slug
minih agent list                # what's already installed (with source-type column)
minih agent info <slug>         # provenance, manifest, per-file drift status

Full surface — manifest format, security model, error reference, curation — in docs/how/agent-pack.md.

Agent Folder Structure

agents/
├── _shared/
│   └── preamble.md            # Shared preamble prepended to every agent
├── my-agent/
│   ├── prompt.md              # Required — agent prompt with YAML frontmatter
│   ├── output-schema.json     # Optional — JSON Schema 2020-12 for output
│   ├── input-schema.json      # Optional — JSON Schema for input params
│   ├── instructions.md        # Optional — agent identity and rules
│   └── runs/                  # Auto-created — run artifacts (gitignored)
│       └── 2026-04-05T07-30-00-000Z/
│           ├── events.ndjson
│           ├── completed.json
│           └── output/
│               └── report.json

Frontmatter

Every prompt.md must have YAML frontmatter with at least a description:

---
description: "What this agent does — shown in `minih list`"
tags: [optional, categories]
---

System Output Contract

Every agent must produce JSON output containing summary and retrospective fields. These are enforced by the runner regardless of whether you define an output schema. Your agent-specific fields go alongside them.

See src/schemas/system-output.json for the full contract schema.

Minimal valid output:

{
  "summary": "Scanned 12 files, found 3 potential issues...",
  "retrospective": {
    "workedWell": "File discovery was fast and the project structure was clear.",
    "confusing": "Wasn't sure if I should scan node_modules or just src/.",
    "magicWand": "A MINIH_SCAN_PATHS env var listing which directories to scan would save me from guessing."
  }
}

The retrospective.magicWand is the most valuable thing an agent produces — it directly improves the system for every agent that runs after it.

CLI Reference

minih quickstart

Create and run your first agent in one command. No flags, no editing.

minih quickstart

Scaffolds agents/hello-world/prompt.md (if not exists), runs it, and shows next steps. Idempotent — safe to run multiple times.

minih run <slug>

Execute an agent.

minih run my-agent
minih run my-agent --model claude-sonnet-4 --timeout 600
minih run my-agent --param file_path=src/main.ts --param depth=3
minih run my-agent --label review-a  # Label rows in minih runs list/status
minih run my-agent --dry-run    # Preview prompt without executing
minih run my-agent --verbose    # Old-style timestamped event log
Flag Description
-m, --model <model> Model to use (default: claude-opus-4.6, override: MINIH_DEFAULT_MODEL)
-r, --reasoning <effort> Reasoning effort: low, medium, high, xhigh
-t, --timeout <seconds> Wall-clock budget in seconds (default: agent frontmatter or 900)
--stall-timeout <seconds> Inactivity watchdog: fail the run when no provider event arrives for this many seconds; 0 disables (default: 300)
--max-turns <count> Fail the run after this many consolidated assistant messages; 0 = unlimited (default: 0)
-p, --param <key=value> Input parameter (repeatable)
--mcp-config <path> Load MCP servers from a JSON config file
--skill-source <alias-or-path> Load local skills from a source such as .agents, global:agents, or path:<dir>
--skill <name> Load only a named skill from configured sources (repeatable)
--disable-skill <name> Disable/exclude a skill by name (repeatable)
--no-skills Disable .minih.json skills for this invocation
--label <label> Human-readable run label shown by minih runs list/status
--dry-run Preview assembled prompt without executing
--verbose Show all events with timestamps (default: pretty streaming)

Display modes: By default, minih run shows clean streaming output — thinking in gray italic, tool calls formatted with names, intent changes highlighted. Use --verbose for the timestamped line-per-event log. Non-TTY environments always use verbose mode.

Run budgets

Every run is bounded by three budgets so it always reaches a terminal artifact, even when the provider stream silently dies mid-response (issue #44):

Budget Flag (run + resume) Default 0 means run.json terminalReason
Wall-clock --timeout <seconds> agent frontmatter or 900 invalid (E108) timeout
Inactivity (stall watchdog) --stall-timeout <seconds> 300 disabled stalled-stream
Turns --max-turns <count> 0 unlimited max-turns

Semantics:

  • Any provider event resets the stall watchdog — text deltas, tool calls, thinking, everything. A stall fires only when the stream goes completely silent for the whole window. The synthetic run_stalled event is appended to events.ndjson when it fires.
  • A turn is one consolidated assistant message — streaming chunking, tool calls, and thinking never count toward --max-turns.
  • All three triggers write run.json status: 'failed' + the terminalReason above, write completed.json (result: 'timeout' for wall-clock, 'failed' for stall/turns), and exit 124 — cleanup of the SDK subprocess is deadline-bounded and escalates to a force-stop, so a wedged provider can never block the terminal writes.
  • The effective budgets are recorded in run.json under budgets: { timeoutSec, stallTimeoutSec, maxTurns }; minih status surfaces the reason (Reason: line + terminalReason in the envelope).
  • Known limitation (tool silence): a tool that legitimately runs longer than the stall window without emitting any event (e.g. a 6-minute build under the default 300s) will trip the watchdog. Raise --stall-timeout or pass --stall-timeout 0 for such agents.
  • Windows: detached-run stall behavior is untested on Windows (see issue #44's sibling report copilot-cli#2525); the watchdog logic itself is platform-neutral.

Skills config

Minih can pass locally installed Copilot/Claude-style skills to SDK-backed sessions without hardcoded absolute paths. Add a small repo config when you want defaults:

{
  "skills": {
    "sources": [".agents"],
    "include": ["minih-test-skill"],
    "exclude": []
  }
}

Or use one-off flags:

minih skills discover
minih skills doctor
minih run test-skills --skill-source .agents --skill minih-test-skill

Supported source aliases include .agents / repo:.agents, .claude, .github, global:agents, global:copilot, global:claude, global:pi, and path:<path>. Minih never loads user-global skills implicitly; configure .minih.json or pass flags explicitly.

minih list

List available agents with descriptions and required parameters.

minih list          # JSON envelope on stdout, table on stderr

minih doctor

Validate all agents for convention compliance.

minih doctor           # JSON on stdout, human report on stderr
minih doctor --strict  # Treat warnings as errors

minih check [slug]

Validate an explicit file against an agent's output schema.

minih check my-agent --file output.json     # Validate specific file
minih check                                  # Best effort inside a run — uses MINIH_OUTPUT_PATH if available
minih check my-agent --file input.json --input  # Validate against input schema

check is for file validation. To validate an already-completed run output, use minih validate <slug> --run <runId>.

minih init <slug>

Scaffold a new agent folder with templates.

minih init my-agent                # prompt + output-schema + instructions
minih init my-agent --with-input   # Also create input-schema.json
minih init my-agent --coordinated  # Also create outside.md + state schemas
minih init my-agent --no-output    # Skip output-schema.json

minih history <slug>

List past runs for an agent with timestamps and status. Resumed runs show a indicator.

minih runs list / minih runs status

Use these when you have multiple runs in flight, especially same-slug runs launched from parallel shells.

minih run worker --label case-a --param id=a &
minih run worker --label case-b --param id=b &

minih runs list --active                  # active/stale/dead runs across all agents
minih runs list --all --slug worker       # bounded history for one slug
minih runs status --run worker/<runId> --run worker/<otherRunId>

Rows include the agent slug, run ID, liveness, timestamps, counters, model/session IDs, optional label, and a bounded/redacted params summary. Liveness dead means the run's recorded process no longer exists (plan 025 — formerly misreported as stale); stale is reserved for live-but-quiet runs. Public rows intentionally identify runs by slug + runId; use follow-up commands with explicit --run <runId> rather than inspecting run folders directly.

When more than one active run exists for a slug, single-run commands that default to "latest" refuse with E170 and list candidates. Pass --run <runId> to target one run, or use read-only --latest where the command supports intentionally choosing the newest active run.

minih reconcile [slug]

Heal run records whose process is gone. A crashed or kill -9'd run leaves run.json claiming active forever; reconcile probes each recorded pid and flips dead ones to status: 'crashed' + terminalReason: 'pid-vanished' (existing diagnoses such as provider-stream-aborted are never overwritten).

minih reconcile worker            # heal one agent's runs
minih reconcile worker --run <id> # heal one specific run
minih reconcile --all             # heal everything under the agents dir
Flag Description
--run <runId> Limit to one run id (requires a slug)
--all Reconcile every agent under the agents dir (cannot combine with a slug or --run)

Lock-guarded — one pass per agents dir at a time (E190 RECONCILE_IN_PROGRESS on contention; stale and dead-owner locks are stolen automatically). Idempotent: a second pass reports nothing to heal. See docs/how/run-liveness.md for the full liveness model.

minih resume <slug> <message>

Send a follow-up message to a completed agent session. The session retains full conversation history — the agent remembers what it did in the original run.

minih resume smoke-test "You didn't validate the test output — check that too"
minih resume code-review --run 2026-04-06T10-04-29-715Z-e94a "Elaborate on the security concern"
Flag Description
--run <runId> Resume a specific run (default: latest)
-t, --timeout <seconds> Wall-clock budget in seconds (default: agent frontmatter or 900 — shared with run)
--stall-timeout <seconds> Inactivity watchdog; 0 disables (default: 300)
--max-turns <count> Turn budget; 0 = unlimited (default: 0)
--verbose Show all events with timestamps

System output validation (summary + retrospective) is not enforced on resume — it's a quick follow-up, not a full agent report.

minih connect <slug>

Print a ready-to-paste command to drop into the Copilot CLI with an agent's session history.

minih connect smoke-test              # Print command for latest run
minih connect smoke-test --run <id>   # Specific run
minih connect smoke-test --list       # Show all runs with session IDs
Flag Description
--run <runId> Connect to a specific run (default: latest)
--list List all runs with their session IDs

minih validate <slug>

Re-validate the most recent or specified completed run's output against the current schema (useful after updating your schema).

minih validate my-agent                 # Latest run
minih validate my-agent --run <runId>   # Specific completed run

minih last-run <slug>

Print the latest run directory and report path.

minih tail <slug>

Follow a running agent's event stream in real time, or print a bounded snapshot.

minih tail my-agent
minih tail my-agent --run <runId> --lines 20 --snapshot

If several active runs share the same slug, pass --run <runId> or inspect candidates first with minih runs list --active --slug <slug>.

Global Options

Flag Description
--agents-dir <path> Agents directory (default: agents)
-V, --version Show version

Environment Variables

The runner sets these during agent execution where the execution environment exposes them. The prompt's literal output path remains authoritative; use minih check <slug> --file <path> if a shell cannot see MINIH_OUTPUT_PATH.

Variable Description
MINIH Always 1 — detect you're inside a minih run
MINIH_AGENT_SLUG Current agent slug
MINIH_RUN_ID Unique run identifier (timestamp)
MINIH_RUN_DIR Absolute path to run artifacts folder
MINIH_OUTPUT_PATH Where to write output JSON when available; same target as the literal path in the prompt
MINIH_AGENTS_DIR Absolute path to agents directory
MINIH_PROJECT_ROOT Absolute path to project root
MINIH_MODEL Model being used
MINIH_TIMEOUT Timeout in seconds
MINIH_SCHEMA_PATH Path to output-schema.json (if exists)
MINIH_INSTRUCTIONS_PATH Path to instructions.md (if exists)
MINIH_PREAMBLE_PATH Path to preamble.md (if exists)
MINIH_HAS_INPUT_SCHEMA true if input-schema.json exists, else false
MINIH_PARAMS JSON-encoded input parameters. Values may be of any JSON type — strings, numbers, booleans, objects, arrays — depending on what each schema field declares and how the orchestrator passed them via -p key=value.

Default model: claude-opus-4.6. Override with MINIH_DEFAULT_MODEL env var or --model flag.

Examples

minih uses agents to test and improve itself. These are the best examples of how to write agents:

Agent Complexity What It Demonstrates
hello-world Minimal Just a prompt — the simplest possible agent
convention-check Basic Output schema, instructions, $ref to retrospective, CLI invocation
prompt-review Intermediate Input params (--param), cross-agent file reading
smoke-test Advanced Full CLI lifecycle test (init, doctor, check, dry-run)
coordination-smoke-test Advanced Minimal primitive check for the outside/inside coordination surface
coordination-loop-validator Advanced Rich worked example for the three-milestone outside/inside conversation loop
feedback-digest Advanced Cross-agent aggregation, feedback loop
self-review Complete Production-grade code review with complex schema

Start with hello-world, then read through in order. Each agent builds on the concepts introduced by the previous one.

Why minih? Skills Do Most of This

Both Claude Code and Copilot CLI have skills — markdown files that give an LLM a prompt and get a response. You could build most of what minih does as individual skills. So why does minih exist?

Skills bundle implicit concepts. minih decomposes them.

In a skill, everything is bundled into a single markdown file. Input parameters are a raw $ARGUMENTS string. Output is whatever the LLM feels like returning. Instructions are mixed into the prompt. Feedback doesn't exist unless you build it yourself. None of it is enforced — it's all implicit, all on the honour system.

minih takes each of those bundled concepts and makes them explicit, separate, enforceable first-class citizens — the same way you'd extract inline code into classes and interfaces:

Concept In a skill In minih
Input $ARGUMENTS — a raw string input-schema.json — typed, validated before execution
Output Whatever the LLM returns output-schema.json — AJV-enforced every run
Instructions Mixed into the prompt markdown instructions.md — separate concern
Shared context Global instructions file (one big blob) _shared/preamble.md — agent-specific, injected at assembly
Feedback Doesn't exist Mandatory retrospective — enforced by the runner
Run artifacts Gone when session ends First-class timestamped folder with events, metadata, output

Because each concept is its own file, you get composition for free — symlink an output-schema.json across agents that share a contract, $ref into shared schema fragments, or point multiple agents at the same instructions.md. Skills can also reference shared files via relative paths and bundled scripts/ directories, but since skill output isn't validated and input isn't typed, sharing a schema between skills doesn't buy you enforcement. In minih, shared files are shared contracts — the runner actually enforces what it finds.

Once these concepts are decomposed, they become infrastructure. The runner handles prompt assembly, the system output contract enforces retrospectives on every agent, the shared preamble injects project context, and validation runs automatically. When you improve the runner or add a capability like velocity tracking, every agent gets it for free without touching individual files.

Skills are inline code. minih is shared infrastructure.

What minih adds beyond skills

Capability Skills minih
Validated output Freeform text — no enforcement JSON Schema (AJV) validates every run
Run independently Must run inside a Claude/Copilot session Standalone CLI — npx minih run from any terminal, CI, or cron
Run history Gone when session ends Timestamped run folders with history and last-run
Event-level observability Not available events.ndjson captures every tool call, every message
Session resume Not available resume sends follow-ups; connect opens interactive sessions
Velocity tracking Not available Per-agent run-over-run timing with trend indicators
Self-improving feedback Must be built per-skill Mandatory retrospective with workedWell/confusing/magicWand on every run
Difficulty aggregation Not available minih difficulties aggregates friction reports across all agents
Prompt inspection Can't see the composed prompt minih inspect shows exactly what the LLM receives
Health checks Not available minih doctor validates all agents and harness structure
Update propagation Edit every skill file Change the runner, preamble, or schema once — all agents inherit
Namespace Every agent is a /slash command in your skills list Agents live in agents/ — your skills stay clean for workflow commands

Where skills win

Skills have real advantages: zero setup (drop a markdown file, it works), conversation context (they see your files, git state, current session), native tools (the host's grep, edit, bash), and orchestration (the LLM can auto-invoke them mid-conversation). Copilot CLI in particular has powerful task orchestration — /fleet decomposes work into parallel subagents, /tasks lets you manage background agents and shell sessions, /plan builds step-by-step execution plans, and /delegate hands off to a coding agent that creates branches and PRs autonomously. That kind of interactive, multi-agent orchestration is where skills shine and minih deliberately stays out of the way.

When NOT to use minih

minih is for repeatable, unattended agent runs that produce structured output. It's the wrong tool for interactive, user-driven work:

  • Research and exploration — you need the LLM to ask you clarifying questions, follow up on leads, and adjust direction mid-conversation. That's a skill.
  • Planning and architecture — iterative back-and-forth where you refine scope, make trade-offs, and approve decisions. That's a skill.
  • Implementation — editing files, running tests, fixing errors in a feedback loop with you watching. That's a skill.
  • One-off questions — "how does this module work?" or "what's the best approach here?" Just ask your agent directly.

The rule of thumb: if the task needs your input during execution, use a skill. If the task should run the same way every time and produce a structured report, use minih.

The complement

They're not competitors — they work together:

Skills (workflow layer)       →  /plan, /review, /research, /validate
                                       ↓ calls
minih (execution layer)       →  npx minih run smoke-test
                                       ↓ produces
Structured artifacts          →  report.json + events.ndjson + velocity + difficulties
                                       ↓ feeds back into
Skills + preamble + harness   →  self-improving loop

Skills are great for interactive workflow orchestration. minih is great for repeatable, observable, self-improving agent execution that persists beyond any single session.

The self-improving feedback loop described in the Philosophy section — where agents report friction, you fix it, and the next run is faster — is what makes minih more than a runner. Projects using this loop have seen complex multi-hour tasks compress to minutes over successive iterations, because every agent run leaves the system better than it found it. Skills don't have the infrastructure (run history, velocity tracking, difficulty ledger, mandatory retrospectives) to drive that loop automatically.

Output Format

All commands (except tail) output a JSON envelope on stdout:

{
  "command": "run",
  "status": "ok",
  "timestamp": "2026-04-05T07:30:00.000Z",
  "data": { ... }
}

Status values: ok (success), degraded (completed with validation issues), error (failure).

Human-readable formatting goes to stderr (when TTY is detected). Pipe stdout for programmatic consumption.

License

MIT

About

Mini Harness for Copilot SDK based agents.

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages