diff --git a/.github/ISSUE_TEMPLATE/00-blank.yml b/.github/ISSUE_TEMPLATE/00-blank.yml
new file mode 100644
index 0000000..4c42ecd
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/00-blank.yml
@@ -0,0 +1,35 @@
+name: "Blank issue"
+description: "Open an issue that doesn't fit any of the structured templates below."
+body:
+ - type: textarea
+ id: body
+ attributes:
+ label: Issue
+ description: "Describe what's going on. Include reproduction steps, expected vs actual, and any relevant `anc` output."
+ validations:
+ required: true
+ - type: textarea
+ id: ai-disclosure
+ attributes:
+ label: AI disclosure
+ description: "One sentence: what part of this issue was AI-written, what was human-written?"
+ placeholder: "Entirely human-written."
+ validations:
+ required: true
+ - type: textarea
+ id: agent-instructions
+ attributes:
+ label: For agents
+ description: "Agents: expand this section before filing."
+ value: |
+
+ Agent filing instructions
+
+ 1. **Pick a structured template first.** False positives, scoring bugs, and feature requests have dedicated forms — use them when they fit.
+ 2. **Search first.** Run `gh search issues --repo brettdavies/agentnative-cli ""` to check for duplicates.
+ 3. **AI disclosure is required.** Fill the field above honestly.
+ 4. **Wrong repo?** Spec questions, principle edits, and CLI grading live on [brettdavies/agentnative](https://github.com/brettdavies/agentnative). Site bugs live on [brettdavies/agentnative-site](https://github.com/brettdavies/agentnative-site).
+ 5. See [CONTRIBUTING.md](https://github.com/brettdavies/agentnative/blob/main/CONTRIBUTING.md) for full guidelines.
+
+ validations:
+ required: false
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
index a5375eb..1114a8d 100644
--- a/.github/ISSUE_TEMPLATE/config.yml
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -1,8 +1,8 @@
blank_issues_enabled: false
contact_links:
- name: "Spec questions, principle edits, or CLI grading"
- url: "https://github.com/brettdavies/agentnative-cli/issues/new/choose"
+ url: "https://github.com/brettdavies/agentnative/issues/new/choose"
about: "For anything about the standard itself, file on the spec repo."
- name: "Site bugs (rendering, performance, deployment)"
- url: "https://github.com/brettdavies/agentnative-cli-site/issues/new/choose"
+ url: "https://github.com/brettdavies/agentnative-site/issues/new/choose"
about: "For bugs on anc.dev, file on the site repo."
diff --git a/.github/ISSUE_TEMPLATE/false-positive.yml b/.github/ISSUE_TEMPLATE/false-positive.yml
index 5c4a23a..31b1600 100644
--- a/.github/ISSUE_TEMPLATE/false-positive.yml
+++ b/.github/ISSUE_TEMPLATE/false-positive.yml
@@ -49,10 +49,10 @@ body:
Agent filing instructions
- 1. **Search first.** Run `gh search issues --repo brettdavies/agentnative-cli-cli ""` to check for duplicates.
+ 1. **Search first.** Run `gh search issues --repo brettdavies/agentnative-cli ""` to check for duplicates.
2. **AI disclosure is required.** Fill the field above honestly.
- 3. **Checker bugs only.** For spec questions or principle edits, file on [brettdavies/agentnative-cli](https://github.com/brettdavies/agentnative-cli). For site bugs, file on [brettdavies/agentnative-cli-site](https://github.com/brettdavies/agentnative-cli-site).
- 4. See [CONTRIBUTING.md](https://github.com/brettdavies/agentnative-cli/blob/main/CONTRIBUTING.md) for full guidelines.
+ 3. **Checker bugs only.** For spec questions or principle edits, file on [brettdavies/agentnative](https://github.com/brettdavies/agentnative). For site bugs, file on [brettdavies/agentnative-site](https://github.com/brettdavies/agentnative-site).
+ 4. See [CONTRIBUTING.md](https://github.com/brettdavies/agentnative/blob/main/CONTRIBUTING.md) for full guidelines.
validations:
required: false
diff --git a/.github/ISSUE_TEMPLATE/feature-request.yml b/.github/ISSUE_TEMPLATE/feature-request.yml
index a208395..ea94f32 100644
--- a/.github/ISSUE_TEMPLATE/feature-request.yml
+++ b/.github/ISSUE_TEMPLATE/feature-request.yml
@@ -35,10 +35,10 @@ body:
Agent filing instructions
- 1. **Search first.** Run `gh search issues --repo brettdavies/agentnative-cli-cli ""` to check for duplicates.
+ 1. **Search first.** Run `gh search issues --repo brettdavies/agentnative-cli ""` to check for duplicates.
2. **AI disclosure is required.** Fill the field above honestly.
- 3. **Checker features only.** For spec changes, file on [brettdavies/agentnative-cli](https://github.com/brettdavies/agentnative-cli).
- 4. See [CONTRIBUTING.md](https://github.com/brettdavies/agentnative-cli/blob/main/CONTRIBUTING.md) for full guidelines.
+ 3. **Checker features only.** For spec changes, file on [brettdavies/agentnative](https://github.com/brettdavies/agentnative).
+ 4. See [CONTRIBUTING.md](https://github.com/brettdavies/agentnative/blob/main/CONTRIBUTING.md) for full guidelines.
validations:
required: false
diff --git a/.github/ISSUE_TEMPLATE/grade-a-cli.yml b/.github/ISSUE_TEMPLATE/grade-a-cli.yml
deleted file mode 100644
index 039df70..0000000
--- a/.github/ISSUE_TEMPLATE/grade-a-cli.yml
+++ /dev/null
@@ -1,56 +0,0 @@
-name: "Grade a CLI"
-description: "Report findings from scoring a real CLI against the standard."
-labels: ["cli-grading"]
-body:
- - type: input
- id: tool
- attributes:
- label: CLI tool
- description: "Name and version of the tool you scored."
- placeholder: "ripgrep 14.1.0"
- validations:
- required: true
- - type: textarea
- id: findings
- attributes:
- label: Findings
- description: |
- Which principles did it pass, fail, or surprise you on?
- Focus on cases where the spec got it wrong — a MUST that punishes good design,
- or a gap where the spec is silent.
- placeholder: |
- P3 (Progressive Help): PASS — excellent `--help` hierarchy.
- P4 (Actionable Errors): FAIL on exit codes — uses 1 for everything.
- P7 (Bounded Responses): Spec is silent on streaming output — rg streams by default, which is fine for agents but the spec doesn't acknowledge it.
- validations:
- required: true
- - type: textarea
- id: spec-feedback
- attributes:
- label: What should the spec change?
- description: "Optional. If your grading revealed a spec gap or mistake, describe it."
- validations:
- required: false
- - type: textarea
- id: ai-disclosure
- attributes:
- label: AI disclosure
- description: "One sentence: what part of this report was AI-written, what was human-written?"
- placeholder: "Scoring done by hand; write-up drafted with AI assistance."
- validations:
- required: true
- - type: textarea
- id: agent-instructions
- attributes:
- label: For agents
- description: "Agents: expand this section before filing."
- value: |
-
- Agent filing instructions
-
- 1. **Search first.** Run `gh search issues --repo brettdavies/agentnative-cli ""` to check for duplicates.
- 2. **AI disclosure is required.** Fill the field above honestly.
- 3. See [CONTRIBUTING.md](https://github.com/brettdavies/agentnative-cli/blob/main/CONTRIBUTING.md) for full guidelines.
-
- validations:
- required: false
diff --git a/.github/ISSUE_TEMPLATE/pressure-test.yml b/.github/ISSUE_TEMPLATE/pressure-test.yml
deleted file mode 100644
index 4a74073..0000000
--- a/.github/ISSUE_TEMPLATE/pressure-test.yml
+++ /dev/null
@@ -1,87 +0,0 @@
-name: "Pressure-test a principle"
-description: "Propose a principle edit: merge, split, rewording, or requirement-tier change."
-labels: ["pressure-test"]
-body:
- - type: dropdown
- id: principle
- attributes:
- label: Principle
- description: Which principle does this affect?
- options:
- - "P1 — Non-Interactive by Default"
- - "P2 — Structured, Parseable Output"
- - "P3 — Progressive Help Discovery"
- - "P4 — Fail Fast with Actionable Errors"
- - "P5 — Safe Retries and Explicit Mutation Boundaries"
- - "P6 — Composable and Predictable Command Structure"
- - "P7 — Bounded, High-Signal Responses"
- - "Multiple / cross-cutting"
- validations:
- required: true
- - type: dropdown
- id: change-type
- attributes:
- label: Type of change
- options:
- - "Rewording (prose clarity, no requirement-tier change)"
- - "Promotion (SHOULD to MUST, MAY to SHOULD)"
- - "Demotion (MUST to SHOULD, SHOULD to MAY)"
- - "Merge (combine two requirements)"
- - "Split (break a requirement into two)"
- - "New requirement"
- - "Remove requirement"
- validations:
- required: true
- - type: textarea
- id: evidence
- attributes:
- label: Evidence
- description: |
- Name a real CLI, the specific MUST/SHOULD/MAY, and what happened.
- The strongest proposals cite a tool where the current spec text produces the wrong outcome.
- placeholder: |
- Tool: gh (GitHub CLI)
- Requirement: P4 MUST "distinct exit codes for distinct failure categories"
- Observed: gh uses exit code 1 for everything — auth, network, not-found.
- Problem: The current MUST is correct but underspecified — it doesn't say how many categories are enough.
- validations:
- required: true
- - type: textarea
- id: proposal
- attributes:
- label: Proposed change
- description: What should the spec say instead? Quote the current text and show the diff.
- validations:
- required: true
- - type: input
- id: human-reviewer
- attributes:
- label: Human reviewer
- description: "GitHub handle of the human who reviewed and approved this submission."
- placeholder: "@username"
- validations:
- required: true
- - type: textarea
- id: ai-disclosure
- attributes:
- label: AI disclosure
- description: "One sentence: what part of this submission was AI-written, what was human-written?"
- placeholder: "Evidence gathered by hand; proposed wording drafted with Claude and edited."
- validations:
- required: true
- - type: textarea
- id: agent-instructions
- attributes:
- label: For agents
- description: "Agents: expand this section before filing."
- value: |
-
- Agent filing instructions
-
- 1. **Search first.** Run `gh search issues --repo brettdavies/agentnative-cli ""` to check for duplicates.
- 2. **AI disclosure is required.** Fill the field above honestly.
- 3. **Human reviewer is required.** A human must review and approve spec change proposals before submission.
- 4. See [CONTRIBUTING.md](https://github.com/brettdavies/agentnative-cli/blob/main/CONTRIBUTING.md) for full guidelines.
-
- validations:
- required: false
diff --git a/.github/ISSUE_TEMPLATE/scoring-bug.yml b/.github/ISSUE_TEMPLATE/scoring-bug.yml
index 3639a84..9e2d8bd 100644
--- a/.github/ISSUE_TEMPLATE/scoring-bug.yml
+++ b/.github/ISSUE_TEMPLATE/scoring-bug.yml
@@ -39,10 +39,10 @@ body:
Agent filing instructions
- 1. **Search first.** Run `gh search issues --repo brettdavies/agentnative-cli-cli ""` to check for duplicates.
+ 1. **Search first.** Run `gh search issues --repo brettdavies/agentnative-cli ""` to check for duplicates.
2. **AI disclosure is required.** Fill the field above honestly.
3. **Checker bugs only.** For false positives/negatives specifically, use the "False positive / false negative" template instead.
- 4. See [CONTRIBUTING.md](https://github.com/brettdavies/agentnative-cli/blob/main/CONTRIBUTING.md) for full guidelines.
+ 4. See [CONTRIBUTING.md](https://github.com/brettdavies/agentnative/blob/main/CONTRIBUTING.md) for full guidelines.
validations:
required: false
diff --git a/.github/ISSUE_TEMPLATE/spec-question.yml b/.github/ISSUE_TEMPLATE/spec-question.yml
deleted file mode 100644
index 43c9633..0000000
--- a/.github/ISSUE_TEMPLATE/spec-question.yml
+++ /dev/null
@@ -1,53 +0,0 @@
-name: "Spec question"
-description: "Ask a question about the agent-native CLI standard."
-labels: ["question"]
-body:
- - type: dropdown
- id: principle
- attributes:
- label: Principle (if applicable)
- description: Which principle does this relate to?
- options:
- - "P1 — Non-Interactive by Default"
- - "P2 — Structured, Parseable Output"
- - "P3 — Progressive Help Discovery"
- - "P4 — Fail Fast with Actionable Errors"
- - "P5 — Safe Retries and Explicit Mutation Boundaries"
- - "P6 — Composable and Predictable Command Structure"
- - "P7 — Bounded, High-Signal Responses"
- - "General / cross-cutting"
- validations:
- required: true
- - type: textarea
- id: question
- attributes:
- label: Question
- description: "What would you like to know about the standard?"
- placeholder: |
- Does P4's MUST on exit codes apply to CLIs that use a single non-zero code
- for all errors but include structured error details in stderr JSON?
- validations:
- required: true
- - type: textarea
- id: ai-disclosure
- attributes:
- label: AI disclosure
- description: "One sentence: what part of this question was AI-written, what was human-written?"
- placeholder: "Entirely human-written."
- validations:
- required: true
- - type: textarea
- id: agent-instructions
- attributes:
- label: For agents
- description: "Agents: expand this section before filing."
- value: |
-
- Agent filing instructions
-
- 1. **Search first.** Run `gh search issues --repo brettdavies/agentnative-cli ""` to check for duplicates.
- 2. **AI disclosure is required.** Fill the field above honestly.
- 3. See [CONTRIBUTING.md](https://github.com/brettdavies/agentnative-cli/blob/main/CONTRIBUTING.md) for full guidelines.
-
- validations:
- required: false
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
index 9d917c7..fdead33 100644
--- a/.github/pull_request_template.md
+++ b/.github/pull_request_template.md
@@ -90,6 +90,8 @@
**Created:**
+**Renamed:**
+
**Deleted:**
## Key Features
diff --git a/.github/workflows/skill-fixture-drift.yml b/.github/workflows/skill-fixture-drift.yml
new file mode 100644
index 0000000..3d13131
--- /dev/null
+++ b/.github/workflows/skill-fixture-drift.yml
@@ -0,0 +1,28 @@
+# Drift gate for tests/fixtures/skill.json against upstream
+# agentnative-site. Companion to the cargo-level test 12
+# (host_map_matches_site_skill_json) which catches drift between the Rust
+# map and this fixture; this workflow catches drift between the fixture
+# and the canonical site contract.
+#
+# Failure means the site has updated src/data/skill.json without a
+# corresponding fixture refresh. Run `scripts/sync-skill-fixture.sh`
+# locally, review the diff, commit, push.
+
+name: skill-fixture-drift
+
+on:
+ pull_request:
+ push:
+ branches: [main, dev]
+
+permissions:
+ contents: read
+
+jobs:
+ drift-check:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ - name: Run drift check
+ run: bash scripts/sync-skill-fixture.sh --check
diff --git a/AGENTS.md b/AGENTS.md
index 24c0cc1..e4cdd44 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -28,17 +28,64 @@ anc . --source
# Suppress inapplicable MUSTs for a categorical exception
anc . --audit-profile human-tui
+
+# Install the companion skill bundle into your host's skills dir
+anc skill install claude_code # ~/.claude/skills/agent-native-cli
+anc skill install --dry-run codex # print resolved git command, don't run
+anc skill install factory --output json # emit envelope on success and error
```
Bare `anc` (no arguments) prints help and exits 2. This is a non-negotiable fork-bomb guard: when agentnative dogfoods
-itself, children spawned without arguments must not recurse into `check .`.
+itself, children spawned without arguments must not recurse into `check .`. Bare `anc skill` likewise prints help and
+exits 2.
+
+## Skill install
+
+`anc skill install ` clones the `agentnative-skill` bundle into a host's canonical skills directory. Six hosts
+ship at v0.1: `claude_code`, `codex`, `cursor`, `factory`, `kiro`, `opencode`. `--help` enumerates them; the JSON
+envelope's `host` field reports the chosen one verbatim.
+
+Output envelope (`--output json`) is uniform across success and error and across `--dry-run` and live install:
+
+```json
+{
+ "action": "skill-install",
+ "host": "claude_code",
+ "mode": "dry-run",
+ "command": "git clone --depth 1 ",
+ "destination": "",
+ "destination_status": "absent",
+ "status": "success",
+ "would_succeed": true
+}
+```
+
+Field-presence rules: `would_succeed` only on `mode: "dry-run"`; `exit_code` only on `mode: "install"` AND only when
+`git` actually spawned (e.g. `git-not-found` leaves it absent); `reason` only when `status: "error"`, with one of the
+typed values `destination-not-empty` / `destination-is-file` / `home-not-set` / `git-not-found` / `git-clone-failed`.
+`destination_status` is one of `absent` / `empty-dir` / `non-empty-dir` / `file`.
+
+Exit codes follow the P4 convention: `0` for success, `1` for any envelope error (typed `reason` set), `2` for clap
+usage errors (unknown host, missing positional, bare `anc skill`).
+
+The `git clone` invocation runs with named-const hardening (`GIT_HARDEN_FLAGS`, `GIT_HARDEN_ENV_REMOVE`,
+`GIT_HARDEN_ENV_SET` — the last includes `GIT_CONFIG_GLOBAL=/dev/null` and `GIT_CONFIG_SYSTEM=/dev/null` to disable
+user-controlled git config, plus `GIT_TERMINAL_PROMPT=0`). No `sh -c`, no `env_clear`. Defense against `insteadOf`
+URL-rewriting comes from disabling user config wholesale, not from a `-c url..insteadOf=` flag (which would do the
+opposite of blocking).
+
+The host map (`SkillHost` enum, `KNOWN_HOSTS`, `resolve_host`, `host_envelope_str`) is **build-time-generated** from
+`src/skill_install/skill.json` by `build.rs::emit_skill_hosts`. To add or change a host, edit the JSON (or run `bash
+scripts/sync-skill-fixture.sh` to pull the upstream site contract) and `cargo build` regenerates the Rust map — no hand
+edits to `src/skill_install.rs`. CI's `skill-fixture-drift.yml` runs `--check` on every PR to catch fixture vs upstream
+drift.
## Agent-facing JSON surface
-`anc check --output json` emits a `schema_version: "0.3"` scorecard. The schema is at `0.x` while `anc` is
+`anc check --output json` emits a `schema_version: "0.5"` scorecard. The schema is at `0.x` while `anc` is
pre-launch — shape may evolve before first public release, when it locks at `1.0`. During `0.x`, additive fields are the
-norm; consumers should feature-detect new keys rather than pinning to an exact value. The current shape includes five
-scorecard-level fields beyond the base `results` / `summary`:
+norm; consumers should feature-detect new keys rather than pinning to an exact value. The current shape includes the
+following scorecard-level fields beyond the base `results` / `summary`:
- `audience` — `"agent-optimized"` / `"mixed"` / `"human-primary"` / `null`. Derived from 4 signal behavioral checks
(`p1-non-interactive`, `p2-json-output`, `p7-quiet`, `p6-no-color-behavioral`). Informational only; never gates totals
@@ -51,6 +98,21 @@ scorecard-level fields beyond the base `results` / `summary`:
- `spec_version` — the `agentnative-spec` version this CLI was built against. Sourced at build time from
`src/principles/spec/VERSION` by `build.rs`; reads `"unknown"` if that file was missing at build time. Pin against
this to know which spec contract the scorecard's requirement IDs reference.
+- `tool` — `{ name, binary, version }`. Identifies what was scored. `version` is best-effort (manifest field for project
+ mode, ` --version` / `-V` for binary/command mode); `null` when probing fails or is declined by the self-spawn
+ guard. Schema `0.4` addition.
+- `anc` — `{ version, commit }`. Identifies the `anc` build that produced the scorecard. `commit` is `null` for builds
+ outside a Git checkout. Informational, not signed provenance. Schema `0.4` addition.
+- `run` — `{ invocation, started_at, duration_ms, platform: { os, arch } }`. `invocation` reflects what the user typed
+ (captured pre-injection). `started_at` is RFC 3339 UTC. Schema `0.4` addition.
+- `target` — `{ kind, path, command }`. `kind` is `"project"` / `"binary"` / `"command"`. The unused field is always
+ `null`, never missing. Schema `0.4` addition.
+- `badge` — `{ eligible, score_pct, embed_markdown, scorecard_url, badge_url, convention_url }`. Agent-native badge
+ derivation from the live run. `score_pct` is the rounded percent of `pass / (pass + warn + fail)` (Skips and Errors
+ excluded from the ratio). `eligible` is true iff `score_pct >= 80` and a tool slug was derivable. `embed_markdown` is
+ `null` below the floor (do-not-nag contract). `scorecard_url` / `badge_url` are populated whenever a slug exists, even
+ below the floor; `convention_url` always points at `https://anc.dev/badge`. Schema `0.5` addition. The text-mode hint
+ (`--output text`) prints the same embed snippet only when eligible; below-floor runs print nothing badge-related.
`--audit-profile` accepts exactly 4 values: `human-tui`, `file-traversal`, `posix-utility`, `diagnostic-only`. Unknown
values exit 2 with a structured error. The full per-category mapping of suppressed check IDs is committed to
@@ -111,17 +173,17 @@ cargo test -- --ignored # fixture tests (slower)
The canonical specification of the 7 agent-readiness principles lives in
[`brettdavies/agentnative`](https://github.com/brettdavies/agentnative), one file per principle under `principles/`. A
-pinned snapshot is **vendored** into this crate at `src/principles/spec/`, and `build.rs` parses its frontmatter at
-build time to generate the `REQUIREMENTS` slice — IDs in the spec frontmatter are the contract this CLI checks against.
-There is no manual sync of requirement IDs; only the `Check::covers()` declarations are hand-maintained.
+snapshot is **vendored** into this crate at `src/principles/spec/`, and `build.rs` parses its frontmatter at build time
+to generate the `REQUIREMENTS` slice — IDs in the spec frontmatter are the contract this CLI checks against. There is no
+manual sync of requirement IDs; only the `Check::covers()` declarations are hand-maintained.
The `anc` checks in `src/checks/` themselves are derived **manually** from each principle's prose. When a principle's
spec adds, removes, or reworks a requirement, propagate to the relevant check(s) deliberately.
-**Resync cadence:** rerun `scripts/sync-spec.sh` after every new `agentnative-spec` tag. The default `SPEC_REF` in the
-script is the current pin; bump via `SPEC_REF=v0.2.1 scripts/sync-spec.sh` when adopting a newer spec release. The
-companion `repository_dispatch` from the spec's publish workflow is the canonical trigger; if a future GitHub Action
-opens a resync PR automatically, this script becomes that action's body.
+**Resync cadence:** rerun `scripts/sync-spec.sh` after every new `agentnative-spec` tag. The script queries the remote
+for the latest `v*` tag automatically and falls back to a local checkout (`$HOME/dev/agentnative-spec` by default) if
+the remote is unreachable. The companion `repository_dispatch` from the spec's publish workflow is the canonical
+trigger; if a future GitHub Action opens a resync PR automatically, this script becomes that action's body.
For iteration workflow, pressure-test protocol, and per-file structure of the spec itself, see
[`agentnative:principles/AGENTS.md`](https://github.com/brettdavies/agentnative/blob/main/principles/AGENTS.md). Read
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6ea5481..d23a016 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,49 @@
All notable changes to this project will be documented in this file.
+## [0.3.0] - 2026-05-01
+
+### Added
+
+- Add four scorecard metadata blocks (`tool`, `anc`, `run`, `target`) to `--output json` — identifies the scored tool/version, the `anc` build that produced the scorecard, the user-typed invocation with timestamp and duration, and the resolved target (project / binary / command). by @brettdavies in [#34](https://github.com/brettdavies/agentnative-cli/pull/34)
+- Add `time = "=0.3.47"` dependency for RFC 3339 timestamps in `run.started_at`.
+- Add `anc skill install ` subcommand to install the [`agentnative-skill`](https://github.com/brettdavies/agentnative-skill) bundle into a host's canonical skills directory. Six hosts: `claude_code`, `codex`, `cursor`, `factory`, `kiro`, `opencode`. by @brettdavies in [#35](https://github.com/brettdavies/agentnative-cli/pull/35)
+- Add `--dry-run` flag (P5): prints the resolved `git clone` command without spawning. Captures cleanly via `eval $(anc skill install --dry-run )`.
+- Add `--output {text,json}` flag (P2): JSON envelope is uniform across success and error and across dry-run / live install. Typed `reason` on error (`destination-not-empty`, `destination-is-file`, `home-not-set`, `git-not-found`, `git-clone-failed`).
+- `--output text` now appends an agent-native badge embed hint after the summary line when the tool clears the 80% eligibility floor. Below the floor, nothing badge-related is printed (the convention's "do not nag" rule). by @brettdavies in [#36](https://github.com/brettdavies/agentnative-cli/pull/36)
+- `--output json` scorecard now includes a `badge` block (`eligible`, `score_pct`, `embed_markdown`, `scorecard_url`, `badge_url`, `convention_url`). `embed_markdown` is `null` below the floor; `scorecard_url` / `badge_url` are populated whenever a tool slug exists, since the site renders an SVG for every scored tool.
+- `scripts/sync-dev-after-release.sh` — backports `Cargo.toml` `[package].version`, `Cargo.lock`, and `CHANGELOG.md` from `main` to `dev` after a release tag publishes. Surgical (preserves dev's other Cargo.toml lines), idempotent (re-runs are a no-op when dev is already in sync), and signed via the operator's normal commit signing — satisfies `protect-dev`'s `required_signatures` ruleset without needing a CI bot identity. by @brettdavies in [#37](https://github.com/brettdavies/agentnative-cli/pull/37)
+
+### Changed
+
+- Vendoring now always tracks the latest published spec tag — `SPEC_REF` env override removed. Run `bash scripts/sync-spec.sh` to refresh; no environment configuration required. by @brettdavies in [#33](https://github.com/brettdavies/agentnative-cli/pull/33)
+- Bump scorecard `schema_version` from `"0.3"` to `"0.4"` (additive within the documented `0.x` policy — older consumers feature-detect). by @brettdavies in [#34](https://github.com/brettdavies/agentnative-cli/pull/34)
+- Bump `rust-version` from `1.87` to `1.88` (let-chain stabilization).
+- Bumped scorecard `schema_version` from `"0.4"` to `"0.5"`. Pre-`0.5` consumers feature-detect the new `badge` key and continue to work. by @brettdavies in [#36](https://github.com/brettdavies/agentnative-cli/pull/36)
+- `p7-naked-println` source check now exempts `build.rs` at any crate root. Cargo build scripts use `println!("cargo:…")` directives by protocol; flagging them produces noise without an alternative API. Misnamed `src/build.rs` or `tests/build.rs` files stay flagged. by @brettdavies in [#38](https://github.com/brettdavies/agentnative-cli/pull/38)
+- README refreshed for current state: schema 0.5 with `badge` block, `--audit-profile ` documented under CLI Reference, `target.path` documented as basename-only (PII-safe), refreshed Example Output to match the live 33-check dogfood and the post-summary badge embed hint. by @brettdavies in [#40](https://github.com/brettdavies/agentnative-cli/pull/40)
+- `--output json` scorecard `anc` block no longer includes a `commit` field. `anc.version` (the crate version pin) remains as the build identity. Removed because the per-build Git SHA capture made cached builds fragile (stale SHAs across local commits) without solving any consumer-facing problem — `anc.version` already identifies the released binary unambiguously, and pre-launch no public consumer of `anc.commit` exists. by @brettdavies in [#47](https://github.com/brettdavies/agentnative-cli/pull/47)
+
+### Fixed
+
+- Eliminated four `.unwrap()` calls on infallible operations across `src/skill_install.rs` and `build.rs`. Replaced with `.expect("…")` naming the upstream contract that guarantees `Some`/`Ok`. No behavior change — these were already infallible; the `expect` messages document why. by @brettdavies in [#38](https://github.com/brettdavies/agentnative-cli/pull/38)
+- `target.path` in `anc check --output json` now emits the basename of the resolved target instead of the canonicalized absolute path, eliminating a home-directory / username PII leak that flowed into committed scorecards, badge URLs, and agent-posted artifacts. Project mode emits the directory name (e.g. `"agentnative-cli"`); binary mode emits the file name (e.g. `"anc"`); command mode unchanged at `null`. No schema bump — value semantics changed, schema shape did not. by @brettdavies in [#39](https://github.com/brettdavies/agentnative-cli/pull/39)
+- Corrected cross-repo URLs in `.github/ISSUE_TEMPLATE/` so contact links and agent-filing instructions point at the right repos. Spec repo references switched from `agentnative-cli` to `agentnative`; site repo references switched from `agentnative-cli-site` to `agentnative-site`; the `agentnative-cli-cli` double-suffix typo in agent gh-search guidance was corrected to `agentnative-cli`. Affects `config.yml`, `false-positive.yml`, `feature-request.yml`, `scoring-bug.yml`. by @brettdavies in [#42](https://github.com/brettdavies/agentnative-cli/pull/42)
+
+### Documentation
+
+- `AGENTS.md` and `src/principles/spec/README.md` updated to reflect the simpler vendor flow. by @brettdavies in [#33](https://github.com/brettdavies/agentnative-cli/pull/33)
+- Document the four new top-level objects in README.md, AGENTS.md, and CLAUDE.md, including the publishing-PII review reminder for `run.invocation` and `target.path`. by @brettdavies in [#34](https://github.com/brettdavies/agentnative-cli/pull/34)
+- Add `## Install the skill` section to README with one-line examples per host and the manual `git clone` fallback for hosts not yet in the binary's map. by @brettdavies in [#35](https://github.com/brettdavies/agentnative-cli/pull/35)
+- `RELEASES.md` § "After publish — sync ``dev`` with the release" documents the backport step, supersedes the prior "never back-merged" rule for these three specific files, and points operators at the script. by @brettdavies in [#37](https://github.com/brettdavies/agentnative-cli/pull/37)
+- Add the `[](https://anc.dev/score/anc)` badge plus crates.io and license shields at the top of `README.md`. by @brettdavies in [#40](https://github.com/brettdavies/agentnative-cli/pull/40)
+- Trim `.github/ISSUE_TEMPLATE/` to `false-positive`, `feature-request`, `scoring-bug`, plus a new `00-blank.yml` that lets a Blank issue option sit first in the chooser ahead of the structured forms. Spec-side templates (`pressure-test`, `grade-a-cli`, `spec-question`) were duplicates of the spec repo's set from before the rename — they belong on `brettdavies/agentnative` only, and `config.yml` already redirects there.
+- Add `scripts/SYNCS.md` — cross-repo sync map covering every spec/skill/coverage/release data flow with mechanism, payload, trigger, and drift check per edge. Includes a flowchart of inbound/outbound edges, a release-pipeline sequence diagram, and a cadence summary table reducing the system to "automatic vs manual" per sync point. by @brettdavies in [#41](https://github.com/brettdavies/agentnative-cli/pull/41)
+- `RELEASES.md` § "Releasing dev to main" step 4 replaced with a triple-diff verification block (A: main→release, B: release→dev, C: dev→main) plus a `git cherry HEAD origin/dev` patch-id check. The new flow catches both directions of drift before the release tag goes out — guarded paths leaking IN (the original concern) and missed cherry-picks that should have shipped (the new concern). Discovered during v0.3.0 prep when an ad-hoc triple-diff caught 4 `.github/ISSUE_TEMPLATE/*.yml` files that had drifted on `main` since the v0.1.1 squash. by @brettdavies in [#45](https://github.com/brettdavies/agentnative-cli/pull/45)
+- `RELEASES.md` § "Releasing dev to main" step 4 — expanded the `git cherry` patch-id check comment with squash-merge triage guidance (three expected noise sources, what a real miss looks like, and a two-command triage recipe). Discovered during v0.3.0 prep when the check produced 55 noisy `+` lines that all turned out to be expected; the original comment didn't explain that this is normal in a squash-merge workflow. by @brettdavies in [#46](https://github.com/brettdavies/agentnative-cli/pull/46)
+
+**Full Changelog**: [v0.2.0...v0.3.0](https://github.com/brettdavies/agentnative-cli/compare/v0.2.0...v0.3.0)
+
## [0.2.0] - 2026-04-29
### Added
diff --git a/CLAUDE.md b/CLAUDE.md
index 01e0e95..3e0ed99 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -14,8 +14,8 @@ Design doc: `~/.gstack/projects/brettdavies-agentnative/brett-main-design-202603
## Skill Routing
-When the user's request matches an available skill, ALWAYS invoke it using the Skill
-tool as your FIRST action. Do NOT answer directly, do NOT use other tools first.
+When the user's request matches an available skill, ALWAYS invoke it using the Skill tool as your FIRST action. Do NOT
+answer directly, do NOT use other tools first.
**gstack skills (ideation, planning, shipping, ops):**
@@ -39,9 +39,9 @@ For the full routing table, see `~/.claude/skills/docs/workflow-routing.md`.
## Documented Solutions
-`docs/solutions/` (symlink to `~/dev/solutions-docs/`) — searchable archive of past
-solutions and best practices, organized by category with YAML frontmatter (`module`, `tags`, `problem_type`). Search
-with `qmd query "" --collection solutions`. Relevant when implementing or debugging in documented areas.
+`docs/solutions/` (symlink to `~/dev/solutions-docs/`) — searchable archive of past solutions and best practices,
+organized by category with YAML frontmatter (`module`, `tags`, `problem_type`). Search with `qmd query ""
+--collection solutions`. Relevant when implementing or debugging in documented areas.
## gstack Project History
@@ -66,8 +66,8 @@ Key decisions already made:
## Source Check Convention
-Most source checks follow this structure (a few legacy helpers in `output_module.rs` and `error_types.rs` use
-different helper shapes but still satisfy the core contract that `run()` is the sole `CheckResult` constructor):
+Most source checks follow this structure (a few legacy helpers in `output_module.rs` and `error_types.rs` use different
+helper shapes but still satisfy the core contract that `run()` is the sole `CheckResult` constructor):
- **Struct** implements `Check` trait with `id()`, `label()`, `group()`, `layer()`, `applicable()`, `run()`
- **`check_x()` helper** takes `(source: &str)` (or `(source: &str, file: &str)` when evidence needs file location
@@ -139,21 +139,122 @@ either source.
Regenerate whenever you add a requirement, change a check's `covers()`, or rename a check ID. The regeneration is a
deliberate commit, not a build-time artifact — the matrix is citable from outside this repo.
-## Scorecard v1.1 Fields
+## Scorecard v0.5 Fields
-`src/scorecard/mod.rs` emits `schema_version: "1.1"` with three additions over the v1.0 shape:
+`src/scorecard/mod.rs` emits `schema_version: "0.5"`. The schema evolves additively during the `0.x` pre-launch window —
+consumers feature-detect each addition rather than pinning exact shape. Cumulative history:
-- `coverage_summary` — three-way `{must, should, may} × {total, verified}` counts, computed from the checks that
- actually ran. Populated every run.
+- `0.2` — `coverage_summary` (three-way `{must, should, may} × {total, verified}` counts), `audience`, `audit_profile`.
+- `0.3` — `spec_version` (vendored agentnative-spec version, sourced by `build.rs` from `src/principles/spec/VERSION`).
+- `0.4` — four top-level objects making the scorecard self-describing: `tool`, `anc`, `run`, `target`.
+- `0.5` — `badge` block surfacing agent-native badge eligibility, embed snippet, and badge/scorecard URLs derived from
+ the live run.
+
+Existing field semantics:
+
+- `coverage_summary` — populated every run. Checks suppressed by `--audit-profile` do not count toward `verified`.
- `audience` — `Option`, derived by `src/scorecard/audience.rs::classify()` from the 4 signal behavioral checks.
- Emits `"agent-optimized"`, `"mixed"`, `"human-primary"`, or `null` when any signal check is missing from results
- (including when suppressed by `--audit-profile`). The classifier is read-only over results and never gates totals or
- exit codes — per CEO review Finding #3, label mismatches are fixed via registry, not classifier logic.
+ Emits `"agent-optimized"`, `"mixed"`, `"human-primary"`, or `null` when any signal check is missing (including
+ `--audit-profile` suppression). Read-only over results; never gates totals or exit codes — per CEO review Finding #3,
+ label mismatches are fixed via registry, not classifier logic.
- `audit_profile` — `Option`, echoes the applied `--audit-profile` flag value (`"human-tui"`,
`"file-traversal"`, `"posix-utility"`, `"diagnostic-only"`). `null` when no profile is set.
-
-Consumers (notably the site's `/score/` page) must feature-detect the new fields — pre-v1.1 scorecards lack
-them. v0.1.2 scorecards carry `audience: null` and `audit_profile: null`; v0.1.3+ populates both.
+- `spec_version` — `&'static str` — the vendored spec version this `anc` build was compiled against.
+
+`0.4` additions (defined as serde-derived sub-structs in `src/scorecard/mod.rs`):
+
+- `tool` — `ToolInfo { name: String, binary: Option, version: Option }`. Built in `main.rs`'s
+ `build_tool_info`. Project mode prefers the manifest version (`Cargo.toml`/`pyproject.toml`); command/binary mode
+ probes ` --version` then `-V` via a fresh `BinaryRunner` with a 2-second timeout. Self-spawn guard compares the
+ resolved binary path against `std::env::current_exe()` — recursion declined → `tool.version: null`.
+- `anc` — `AncInfo { version: &'static str }`. `version` is a build-time constant emitted by `build.rs` into
+ `$OUT_DIR/build_info.rs` (re-exported from `src/build_info.rs`). The `commit` field shipped pre-`0.5` was dropped
+ before the v0.3.0 tag — the version pin is sufficient build identity for scorecard consumers and the
+ `cargo:rerun-if-changed` watches on `.git/` made cached-build SHAs fragile across local commits.
+- `run` — `RunInfo { invocation, started_at, duration_ms, platform: { os, arch } }`. `invocation` is captured **before**
+ `inject_default_subcommand` rewrites argv (so `anc .` records as `"anc ."`, not `"anc check ."`). `started_at` is RFC
+ 3339 UTC via the `time` crate (pinned `=0.3.45`). `duration_ms` uses `Instant` for monotonic measurement.
+ `platform.{os,arch}` come from `std::env::consts`.
+- `target` — `TargetInfo { kind: String, path: Option, command: Option }`. `kind` is one of `"project"`,
+ `"binary"`, `"command"`. `path` is the **basename** of the resolved target (directory name in project mode, file name
+ in binary mode) — never the absolute path, which would leak operator PII (home-dir username, org/employer dir
+ structure) into committed scorecards, README badge URLs, and any agent-posted artifact. `null` for `command` mode.
+ Pathological paths where `Path::file_name()` returns `None` (e.g. `/`, `..`) fall back to `null`. The unused field is
+ always `null`, never missing. See `src/main.rs::build_target_info` for the leak-vector rationale; the regression guard
+ lives in `tests/scorecard_schema_v05.rs::schema_v05_target_path_carries_no_separators`.
+
+`0.5` addition (`BadgeInfo` in `src/scorecard/mod.rs`):
+
+- `badge` — `BadgeInfo { eligible, score_pct, embed_markdown, scorecard_url, badge_url, convention_url }`. Computed by
+ `compute_badge(results, tool_name)` from the leaderboard's pass-rate (`pass / (pass + warn + fail)`) — Skips and
+ Errors are excluded from both sides of the ratio. `eligible` is true iff `score_pct >= BADGE_ELIGIBILITY_FLOOR_PCT`
+ (currently `80`) **and** a tool slug was derivable; `embed_markdown` is `Some` only when `eligible` (the do-not-nag
+ contract from the site's badge convention). `scorecard_url` / `badge_url` are populated whenever a slug exists, even
+ below the floor — the site renders an SVG for every scored tool so a regression below the floor shifts color rather
+ than 404s. `convention_url` is the fixed `https://anc.dev/badge` pointer. URLs are anchored at `BADGE_BASE_URL =
+ "https://anc.dev"` so the URL pattern lives in one place. Authority for the floor is the site's published convention
+ (`agentnative-site/content/badge.md`); when the spec convention merges off `feat/badge-claim-convention` it will move
+ into the vendored spec via `sync-spec`. Text mode (`--output text`) appends a post-summary hint via
+ `BadgeInfo::text_hint()` when `eligible`; the same `tool.name` is used for the slug so the JSON `embed_markdown` and
+ the printed hint can never disagree.
+
+Always-present null contract: `tool.version`, `tool.binary`, `target.path`, `target.command` serialize as JSON `null`
+when not applicable, never as missing keys. Consumers can access these paths unconditionally. The exception is
+`audience_reason`, which uses `skip_serializing_if = "Option::is_none"` — its absence carries information (audience has
+a label).
+
+Consumers (notably the site's `/score/` page) must feature-detect the new fields — pre-`0.4` scorecards lack the
+four metadata blocks; pre-`0.5` scorecards lack `badge`. The site's `agentnative-site/registry.yaml` will eventually
+drop its parallel `version` / `scored_at` fields once consumers read those facts from the scorecard's `tool.version` /
+`run.started_at`. That follow-up lives in the `agentnative-site` repo, not here.
+
+## Skill Install Verb
+
+`anc skill install ` ships the `agentnative-skill` bundle into a host's canonical skills directory. The host map
+is **build-time-generated** from `src/skill_install/skill.json` (a verbatim copy of
+`agentnative-site/src/data/skill.json`) into `$OUT_DIR/generated_hosts.rs` by `build.rs::emit_skill_hosts`.
+`src/skill_install.rs` `include!`s the generated file; there is no hand-maintained host enum or destination table. To
+add or change a host, edit `skill.json` (or run `bash scripts/sync-skill-fixture.sh` to pull the upstream site contract)
+and `cargo build` regenerates the Rust map. There is no `skill.json` parsing in production, no HTTPS fetch at runtime,
+and no allowlist validator — the host map is compile-time data.
+
+CI catches drift between the committed fixture and the upstream site contract:
+
+- `scripts/sync-skill-fixture.sh --check` (CI workflow `skill-fixture-drift.yml`) clones the upstream site at
+ `SKILL_SITE_REF` (default `dev`, since the site uses a dev/main forever-branch flow) and `cmp`s the live blob against
+ `src/skill_install/skill.json`. Runs on every PR and on push to main/dev.
+
+Drift between the fixture and the generated Rust map cannot occur within a single build: `cargo:rerun-if-changed` on the
+JSON file forces regeneration whenever the fixture moves. The earlier `host_map_matches_site_skill_json` test (test 12)
+was deleted as provably redundant after this refactor.
+
+The `git clone` invocation runs with named-const hardening that defeats ambient git-config and env subversion. The full
+surface lives in `src/skill_install.rs`:
+
+- `GIT_HARDEN_FLAGS: &[&str]` — five `-c key=value` pairs (`credential.helper=`, `core.askPass=`,
+ `protocol.allow=never`, `protocol.https.allow=always`, `http.followRedirects=false`). Applied via `Command::args`
+ *before* the `clone` subcommand — git's required position for top-level `-c` options.
+- `GIT_HARDEN_ENV_REMOVE: &[&str]` — five env vars stripped via `env_remove` (`GIT_SSH{,_COMMAND}`, `GIT_PROXY_COMMAND`,
+ `GIT_ASKPASS`, `GIT_EXEC_PATH`).
+- `GIT_HARDEN_ENV_SET: &[(&str, &str)]` — three env vars **set** on the spawned process. The
+ `GIT_CONFIG_GLOBAL=/dev/null` and `GIT_CONFIG_SYSTEM=/dev/null` pair disables every layer of user-controlled git
+ config — the actual defense against `insteadOf` URL-rewriting attacks (an earlier draft tried `-c
+ url..insteadOf=`, which does the *opposite* of blocking and doubles the clone URL). `GIT_TERMINAL_PROMPT=0`
+ blocks credential prompts; git's default-when-unset is to prompt, which is the wrong default for a non-interactive
+ subcommand.
+
+**Rules for changes touching skill install:**
+
+- NEVER call `Command::env_clear()` — it strips PATH and breaks git's helper resolution. Use `env_remove` per var.
+- NEVER use `sh -c` or any shell-mediated invocation. Tokens go directly to `git` via `Command::args`.
+- NEVER reintroduce `skill.json` parsing in production code. The fixture is a build-time codegen input, not a runtime
+ resource.
+- NEVER hand-edit `SkillHost`, `KNOWN_HOSTS`, `resolve_host`, or `host_envelope_str` in `src/skill_install.rs` — those
+ identifiers come from the generated `$OUT_DIR/generated_hosts.rs` and any apparent definition in source is the
+ include! macro. To add a host, edit `src/skill_install/skill.json` (or run the sync script) and rebuild.
+- The codegen rejects malformed install commands at build time. Each `install.` value MUST tokenize as exactly
+ `git clone --depth 1 ` (six whitespace-separated tokens, dest not ending in `.git`). The validation
+ mirrors `agentnative-site/src/build/skill.mjs` so the two binaries reject the same inputs.
## Dogfooding Safety
diff --git a/Cargo.lock b/Cargo.lock
index bcce0ae..9432723 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4,7 +4,7 @@ version = 4
[[package]]
name = "agentnative"
-version = "0.2.0"
+version = "0.3.0"
dependencies = [
"anyhow",
"assert_cmd",
@@ -18,7 +18,9 @@ dependencies = [
"serde",
"serde_json",
"serde_yaml",
+ "tempfile",
"thiserror",
+ "time",
"toml",
]
@@ -276,6 +278,15 @@ version = "0.8.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
+[[package]]
+name = "deranged"
+version = "0.5.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c"
+dependencies = [
+ "powerfmt",
+]
+
[[package]]
name = "difflib"
version = "0.4.0"
@@ -472,6 +483,12 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be"
+[[package]]
+name = "num-conv"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c6673768db2d862beb9b39a78fdcb1a69439615d5794a1be50caa9bc92c81967"
+
[[package]]
name = "num-traits"
version = "0.2.19"
@@ -493,6 +510,12 @@ version = "1.70.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
+[[package]]
+name = "powerfmt"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
+
[[package]]
name = "predicates"
version = "3.1.4"
@@ -760,6 +783,37 @@ dependencies = [
"syn",
]
+[[package]]
+name = "time"
+version = "0.3.47"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c"
+dependencies = [
+ "deranged",
+ "itoa",
+ "num-conv",
+ "powerfmt",
+ "serde_core",
+ "time-core",
+ "time-macros",
+]
+
+[[package]]
+name = "time-core"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca"
+
+[[package]]
+name = "time-macros"
+version = "0.2.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215"
+dependencies = [
+ "num-conv",
+ "time-core",
+]
+
[[package]]
name = "toml"
version = "0.8.23"
diff --git a/Cargo.toml b/Cargo.toml
index ad0c448..0fb3ffe 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "agentnative"
-version = "0.2.0"
+version = "0.3.0"
edition = "2024"
description = "The agent-native CLI linter — check whether your CLI follows agent-readiness principles"
license = "MIT OR Apache-2.0"
@@ -11,7 +11,7 @@ keywords = ["cli", "linter", "agent", "ast-grep", "developer-tools"]
categories = ["command-line-utilities", "development-tools"]
authors = ["Brett Davies "]
readme = "README.md"
-rust-version = "1.87"
+rust-version = "1.88"
exclude = [
".claude/",
".context/",
@@ -55,11 +55,18 @@ thiserror = "2"
# Platform
libc = "0.2"
+# RFC 3339 timestamps for scorecard `run.started_at`. Pinned exact (pre-1.0
+# convention). `formatting` feature enables `OffsetDateTime::format` with the
+# `Rfc3339` well-known description; default `std` feature is required for
+# `OffsetDateTime::now_utc`.
+time = { version = "=0.3.47", default-features = false, features = ["std", "formatting"] }
+
[build-dependencies]
# Vendored spec frontmatter parser (build.rs → $OUT_DIR/generated_requirements.rs).
# serde_yaml is deprecated upstream but still functional; pinned tightly.
# Re-evaluate (saphyr / yaml-rust2) if cargo-deny flags it.
serde = { version = "1.0", features = ["derive"] }
+serde_json = "1.0"
serde_yaml = "=0.9.34"
[dev-dependencies]
@@ -68,6 +75,10 @@ insta = "1"
predicates = "3"
# Used by tests/build_parser.rs to exercise the build_support/parser.rs module.
serde_yaml = "=0.9.34"
+# Tempdir / tempfile fixtures for skill_install destination-check tests.
+# Already a transitive dev-dep via insta — declared here so it's a direct
+# dependency we control.
+tempfile = "3"
[package.metadata.binstall]
pkg-url = "{ repo }/releases/download/v{ version }/agentnative-{ target }.tar.gz"
diff --git a/README.md b/README.md
index 0b0a7c9..f500f4e 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,13 @@
# agentnative
+[](https://anc.dev/score/anc)
+[](https://crates.io/crates/agentnative)
+[](#license)
+
The agent-native CLI linter. Checks whether your CLI follows the 7 agent-readiness principles.
+`anc` dogfoods the spec it enforces — the badge above is its own live score.
+
## Install
The crate is published as `agentnative`. The binary is called `anc`.
@@ -20,6 +26,42 @@ cargo binstall agentnative
# https://github.com/brettdavies/agentnative-cli/releases
```
+## Install the skill
+
+`anc` ships a companion skill bundle (`agentnative-skill`) that teaches AI coding agents how to operate the linter and
+where to apply the principles. Install it with one command per host:
+
+```bash
+anc skill install claude_code # ~/.claude/skills/agent-native-cli
+anc skill install codex # ~/.codex/skills/agent-native-cli
+anc skill install cursor # ~/.cursor/skills/agent-native-cli
+anc skill install factory # ~/.factory/skills/agent-native-cli (Factory Droid)
+anc skill install kiro # ~/.kiro/skills/agent-native-cli (Kiro)
+anc skill install opencode # ~/.config/opencode/skills/agent-native-cli
+```
+
+Inspect the resolved command before running it:
+
+```bash
+anc skill install --dry-run claude_code
+# git clone --depth 1 https://github.com/brettdavies/agentnative-skill.git /home/you/.claude/skills/agent-native-cli
+```
+
+JSON output (mode `dry-run` and `install`, success and error) is uniform — agents can rely on the same envelope shape
+across every outcome:
+
+```bash
+anc skill install --dry-run claude_code --output json
+```
+
+If the site adds a host before this `anc` release knows about it, fall back to a manual `git clone`:
+
+```bash
+git clone --depth 1 https://github.com/brettdavies/agentnative-skill.git /agent-native-cli
+```
+
+The host map is hardcoded in this binary; new hosts ship via patch release after the site updates `skill.json`.
+
## Quick Start
```bash
@@ -61,6 +103,8 @@ agentnative checks your CLI against seven agent-readiness principles:
```text
P1 — Non-Interactive by Default
[PASS] Non-interactive by default (p1-non-interactive)
+ [PASS] Flags advertise env-var bindings in --help (p1-env-hints)
+ [PASS] TTY detection for color output (p1-tty-detection-source)
[PASS] No interactive prompt dependencies (p1-non-interactive-source)
P3 — Progressive Help
@@ -69,19 +113,30 @@ P3 — Progressive Help
P4 — Actionable Errors
[PASS] Rejects invalid arguments (p4-bad-args)
+ [PASS] Structured error types (p4-error-types)
+ [PASS] Exit codes use named constants (p4-exit-codes)
[PASS] No process::exit outside main (p4-process-exit)
+ [PASS] Dedicated error module exists (p4-error-module)
P6 — Composable Structure
[PASS] Handles SIGPIPE gracefully (p6-sigpipe)
- [PASS] Respects NO_COLOR (p6-no-color)
+ [PASS] Respects NO_COLOR (p6-no-color-behavioral)
[PASS] Shell completions support (p6-completions)
Code Quality
[PASS] No .unwrap() in source (code-unwrap)
-30 checks: 26 pass, 2 warn, 0 fail, 2 skip, 0 error
+33 checks: 28 pass, 1 warn, 0 fail, 4 skip, 0 error
+
+🏆 Score: 97% — your tool qualifies for the agent-native badge.
+ Embed in your README:
+ [](https://anc.dev/score/anc)
+ Convention: https://anc.dev/badge
```
+The badge hint appears in `text` output when a tool scores at or above the 80% eligibility floor. Below the floor, `anc`
+prints nothing badge-related — the convention is to surface the embed only when earned.
+
## Three Check Layers
agentnative uses three layers to analyze your CLI:
@@ -95,9 +150,9 @@ agentnative uses three layers to analyze your CLI:
## CLI Reference
-When the first non-flag argument is not a recognized subcommand, `check` is inserted automatically. `anc .`,
-`anc -q .`, and `anc --command ripgrep` all resolve to `anc check …`. Bare `anc` (no arguments) still prints help and
-exits 2 — this is deliberate fork-bomb prevention when agentnative dogfoods itself.
+When the first non-flag argument is not a recognized subcommand, `check` is inserted automatically. `anc .`, `anc -q .`,
+and `anc --command ripgrep` all resolve to `anc check …`. Bare `anc` (no arguments) still prints help and exits 2 — this
+is deliberate fork-bomb prevention when agentnative dogfoods itself.
```text
Usage: anc check [OPTIONS] [PATH]
@@ -106,26 +161,34 @@ Arguments:
[PATH] Path to project directory or binary [default: .]
Options:
- --command Resolve a command from PATH and run behavioral checks against it
- --binary Run only behavioral checks (skip source analysis)
- --source Run only source checks (skip behavioral)
- --principle Filter checks by principle number (1-7)
- --output