vigiles 12.1.0 → 12.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -3,6 +3,15 @@
3
3
  This file is the FRONT DOOR + a marketing asset for someone who already lives
4
4
  in Claude Code / Codex. Optimize for a phone-skimmer.
5
5
 
6
+ TAGLINE = VERIFY-FIRST (decided 2026-07): "Catch the silent breakage in your
7
+ Claude Code & Codex setup" + subhead "verify your CLAUDE.md, skills, and hooks
8
+ are real — and prove they actually work." Lead with references-are-real (the
9
+ unique catch only vigiles makes); test/observe fold in AFTER. Do NOT revert to
10
+ the old test-first "tests your skills never had" tagline. Stay coding-scoped
11
+ (name CC/Codex + CLAUDE.md/skills/hooks) so it's never mistaken for a
12
+ general-agent tool. NOT "observability" as the headline (it undersells verify +
13
+ reads as the general-app category).
14
+
6
15
  SPINE = CONCEPT 5 (proof/demo-led). Lead with REAL, screenshotable catches on
7
16
  plugins people actually ship, THEN explain the mechanism. The proofs are not
8
17
  illustrative — every block traces to a real dogfood run captured in
@@ -83,7 +92,11 @@
83
92
  <h1 align="center">vigiles</h1>
84
93
 
85
94
  <p align="center">
86
- <strong>The tests your agent's skills and hooks never had.</strong>
95
+ <strong>Catch the silent breakage in your Claude Code &amp; Codex setup.</strong>
96
+ </p>
97
+
98
+ <p align="center">
99
+ Verify your CLAUDE.md, skills, and hooks are real — and prove they actually work.
87
100
  </p>
88
101
 
89
102
  <p align="center">
@@ -94,14 +107,11 @@
94
107
 
95
108
  ---
96
109
 
97
- **You installed some plugins and wrote a few skills. Do they actually work?**
110
+ **Your skills, hooks, and instructions are your agent's harness the half you wrote, and the half nothing checks.**
98
111
 
99
- Your skills, hooks, and instructions are your agent's **harness** the half you wrote
100
- and own, and the half nothing checks. A skill that never fires. Two skills the agent
101
- confuses. A subagent wired to a tool that doesn't exist. It breaks silently, and you
102
- find out mid-task.
112
+ A subagent wired to a tool that doesn't exist. A hook that looks like it blocks and doesn't. Two skills the agent can't tell apart. It all looks fine, and it breaks silently mid-task.
103
113
 
104
- It's a library with no tests. This runs them:
114
+ vigiles checks your harness is _real_, not just well-formed:
105
115
 
106
116
  ```bash
107
117
  npx vigiles audit
@@ -160,9 +170,10 @@ This subagent — a helper your main agent hands a task to — declares a tool t
160
170
  doesn't exist. The harness drops it silently, so the agent loses a capability it
161
171
  thinks it has. vigiles catches it and gives you the **one-line fix**.
162
172
 
163
- That's the whole idea — it checks your harness against reality, not style. Every path,
164
- script, code symbol, and linter rule, verified to exist _and_ be enabled across 7
165
- catalogs (ESLint, Ruff, Clippy + four more).
173
+ That's the whole idea — it checks your harness against reality, not style. Every
174
+ referenced tool, hook, file, script, and skill is verified to actually resolve and
175
+ where you name a linter rule, it's checked to exist _and_ be enabled (ESLint, Ruff,
176
+ Clippy and more).
166
177
  **[Full guide →](docs/verifying-instruction-files.md)**
167
178
 
168
179
  All three catches are free and need no model — and vigiles **prevents** other whole
package/action.yml CHANGED
@@ -38,6 +38,18 @@ inputs:
38
38
  description: "On pull_request events, post/update a sticky PR comment with the result ('true' or 'false'). Requires `pull-requests: write`."
39
39
  required: false
40
40
  default: "true"
41
+ capability-diff:
42
+ description: >
43
+ On pull_request events, compare the agent's capability surface (the
44
+ subagents' tool/effect blast radius) against the PR base and fold the diff
45
+ into the sticky comment (maps to `audit --capability-diff`). Requires
46
+ `fetch-depth: 0` on checkout so the base commit is present. 'true' or 'false'.
47
+ required: false
48
+ default: "false"
49
+ fail-on-widen:
50
+ description: "With capability-diff, fail the run when the PR WIDENS the blast radius (maps to the CLI's --fail-on-widen). 'true' or 'false'."
51
+ required: false
52
+ default: "false"
41
53
  github-token:
42
54
  description: "Token used to post the PR comment. Defaults to the workflow token."
43
55
  required: false
@@ -64,6 +76,8 @@ runs:
64
76
  VIGILES_CATALOG_ONLY: ${{ inputs.catalog-only }}
65
77
  VIGILES_ACTION_PATH: ${{ github.action_path }}
66
78
  VIGILES_COMMENT: ${{ inputs.comment }}
79
+ VIGILES_CAPABILITY_DIFF: ${{ inputs.capability-diff }}
80
+ VIGILES_FAIL_ON_WIDEN: ${{ inputs.fail-on-widen }}
67
81
  GH_TOKEN: ${{ inputs.github-token }}
68
82
  run: |
69
83
  set -euo pipefail
@@ -114,6 +128,57 @@ runs:
114
128
  status="${PIPESTATUS[0]}"
115
129
  set -e
116
130
 
131
+ # Capability diff (moat #2): did this PR WIDEN the agent's blast radius?
132
+ # Runs orthogonally to the main command — it's an additive PR-comment
133
+ # section, so it works whatever `command` gated above. The BEFORE tree is
134
+ # a detached worktree of the PR base; the AFTER tree is this checkout.
135
+ capdiff="" # markdown appended to the sticky comment
136
+ capstatus=0
137
+ if [[ "${VIGILES_CAPABILITY_DIFF:-false}" == "true" \
138
+ && "${GITHUB_EVENT_NAME:-}" == "pull_request" ]]; then
139
+ base_sha="$(jq -r '.pull_request.base.sha // empty' "${GITHUB_EVENT_PATH:-/dev/null}")"
140
+ if [[ -z "$base_sha" ]]; then
141
+ echo "::warning::capability-diff: no PR base sha in the event payload"
142
+ else
143
+ # A shallow checkout may not carry the base commit — fetch it first.
144
+ git fetch --no-tags --depth=1 origin "$base_sha" >/dev/null 2>&1 \
145
+ || git fetch --no-tags origin "$base_sha" >/dev/null 2>&1 || true
146
+ base_dir="$(mktemp -d)"
147
+ # Materialize the base tree with `git archive` (NOT a worktree): it
148
+ # exports stored blobs WITHOUT running checkout/smudge filters, so it's
149
+ # light and can't trip a required filter (e.g. git-crypt) the runner
150
+ # lacks. The agent surface (unencrypted) lands as plaintext to diff.
151
+ if git archive "$base_sha" 2>/dev/null | tar -x -C "$base_dir" 2>/dev/null; then
152
+ # Diff the SAME relative dir in both trees (support a subdir working-directory).
153
+ rel="$(git rev-parse --show-prefix 2>/dev/null || echo '')"
154
+ before="${base_dir%/}/${rel}"; before="${before%/}"
155
+ capout="$(mktemp)"
156
+ fail_flag=(); [[ "${VIGILES_FAIL_ON_WIDEN:-false}" == "true" ]] && fail_flag=(--fail-on-widen)
157
+ # --no-html --no-json: this run is for the diff only, so skip the
158
+ # report artifacts. The full audit still streams to the CI log; only
159
+ # the capability-diff paragraph is extracted for the sticky comment.
160
+ set +e
161
+ "${runner[@]}" audit . --capability-diff="$before" --no-html --no-json "${fail_flag[@]}" 2>&1 | tee "$capout"
162
+ capstatus="${PIPESTATUS[0]}"
163
+ set -e
164
+ # Pull out just the "Capability surface …" paragraph (a self-contained
165
+ # block with no internal blank lines), then drop the no-change case
166
+ # so a PR that touches nothing capability-relevant gets no section.
167
+ capdiff="$(awk '/Capability surface/{f=1} f&&NF{print} f&&!NF{exit}' "$capout")"
168
+ case "$capdiff" in *"unchanged"*) capdiff="" ;; esac
169
+ else
170
+ echo "::warning::capability-diff: could not materialize PR base ${base_sha} (need fetch-depth: 0?)"
171
+ fi
172
+ rm -rf "$base_dir" 2>/dev/null || true
173
+ fi
174
+ fi
175
+
176
+ # A capability WIDENING (fail-on-widen → exit 1) flips an otherwise-green
177
+ # run to failed, so the gate is visible in the output + PR comment.
178
+ if [[ "$capstatus" -ne 0 && "$status" -eq 0 ]]; then
179
+ status="$capstatus"
180
+ fi
181
+
117
182
  if [[ "$status" -eq 0 ]]; then
118
183
  echo "valid=true" >> "$GITHUB_OUTPUT"
119
184
  headline="✅ \`vigiles ${cmd}\` passed"
@@ -130,6 +195,14 @@ runs:
130
195
  echo
131
196
  echo "$headline"
132
197
  echo
198
+ if [[ -n "$capdiff" ]]; then
199
+ echo "### Capability diff"
200
+ echo
201
+ echo '```'
202
+ echo "$capdiff"
203
+ echo '```'
204
+ echo
205
+ fi
133
206
  echo '<details><summary>Output</summary>'
134
207
  echo
135
208
  echo '```'
@@ -13,6 +13,7 @@
13
13
  */
14
14
  import { type AuditScore } from "./audit-score.js";
15
15
  import { type Recommendation } from "./optimize.js";
16
+ import type { LedgerSummary } from "./observe.js";
16
17
  import type { AdoptabilityResult } from "./adoptability.js";
17
18
  import type { ScanReport, MarketplaceInfo } from "./scan.js";
18
19
  import type { PluginScore } from "./leaderboard.js";
@@ -95,10 +96,20 @@ export interface AuditReport {
95
96
  * at least one adoptable surface. Additive/optional — schema version unchanged.
96
97
  */
97
98
  readonly adoptable?: Adoptable;
99
+ /**
100
+ * The flight-recorder summary — what the harness actually DID in real sessions
101
+ * (hook/agent decisions, counts, recent denials), read off `.vigiles/runs.jsonl`.
102
+ * Present only when the local ledger has records. Additive/optional — schema
103
+ * version unchanged. The CLI reads + summarizes the ledger and passes it in, so
104
+ * the pure builder stays fs-free.
105
+ */
106
+ readonly observations?: LedgerSummary;
98
107
  }
99
108
  export interface BuildAuditReportOptions {
100
109
  readonly harness: string;
101
110
  readonly vigilesVersion: string;
111
+ /** The flight-recorder summary from the local ledger (omit when empty). */
112
+ readonly observations?: LedgerSummary;
102
113
  /**
103
114
  * The repo-relative paths of surfaces that exist but have no `.spec.ts` yet,
104
115
  * computed by the CLI's layout-aware `discoverAdoptableSurfaces` (so the pure
@@ -70,6 +70,7 @@ function buildAuditReport(report, opts) {
70
70
  untested: report.untested,
71
71
  },
72
72
  ...(adoptable ? { adoptable } : {}),
73
+ ...(opts.observations ? { observations: opts.observations } : {}),
73
74
  };
74
75
  }
75
76
  /** Assemble the versioned {@link LeaderboardReport} — pure, no clock. */