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 +21 -10
- package/action.yml +73 -0
- package/dist/audit-report.d.ts +11 -0
- package/dist/audit-report.js +1 -0
- package/dist/audit-report.template.html +36 -26
- package/dist/cli-commands.d.ts +1 -1
- package/dist/cli-commands.js +0 -1
- package/dist/cli.js +62 -135
- package/dist/eval.js +17 -0
- package/dist/observe.d.ts +109 -0
- package/dist/observe.js +164 -0
- package/dist/scaffold-test.js +3 -2
- package/package.json +1 -1
- package/skills/debug-my-harness/SKILL.md +56 -0
- package/dist/core/hook-spec.d.ts +0 -74
- package/dist/core/hook-spec.js +0 -130
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>
|
|
95
|
+
<strong>Catch the silent breakage in your Claude Code & 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
|
-
**
|
|
110
|
+
**Your skills, hooks, and instructions are your agent's harness — the half you wrote, and the half nothing checks.**
|
|
98
111
|
|
|
99
|
-
|
|
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
|
-
|
|
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
|
|
164
|
-
|
|
165
|
-
|
|
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 '```'
|
package/dist/audit-report.d.ts
CHANGED
|
@@ -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
|
package/dist/audit-report.js
CHANGED
|
@@ -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. */
|