Skip to content

Commit f788332

Browse files
authored
chore: v1.1.6 production blocker fix bundle (P0-1/3/4/6/9) (#200)
* feat(hooks): add husky pre-commit gate template (P0-3 S1) Author-whitelist + secret scan + private-pattern scan + lint hook that downstream user repos receive after 'agentlint setup'. Sources _shared.sh fail_with_help helper. Closes the v0.9.0 absorb-VibeKit gap where setup.sh expected templates/hooks/husky/ but the directory never existed. * feat(hooks): add husky pre-push gate template (P0-3 S2) Sources _shared-push.sh (main-skip + rebase on origin/main) then runs the project's test command (npm test for Node, pytest for Python). Part of the husky hook generation closing P0-3. * feat(hooks): add husky commit-msg conventional-commit gate (P0-3 S3) Rejects commit messages whose first line does not match ^(feat|fix|refactor|test|docs|chore|ci|style|perf|build|revert)(\(.+\))?: .+ Uses _shared.sh fail_with_help helper. Part of P0-3 husky hook bundle. * fix(setup): fail-closed hook copy + carry _shared helpers (P0-3 S4) scripts/setup.sh hook copy loop now: - counts copied hooks (copied=0 ... copied=$((copied+1))) - dies hard if copied==0 with 'no husky hooks generated; refusing to set core.hooksPath' — prevents the audit-flagged failure mode where setup falsely claims hooks installed and disables existing .git/hooks via core.hooksPath redirect - copies _shared.sh + _shared-push.sh helpers into user .husky/ so the generated hooks (which 'source "$(dirname "$0")/_shared.sh"') resolve their helper at runtime in the downstream repo - only sets core.hooksPath=.husky when copied>0 Closes P0-3 from docs/p0-production-blocker-scan-2026-04-25.md. * test(setup): pin husky hook generation positive + negative path (P0-3 S5) tests/test-husky-hooks.sh covers two cases: - positive: bash setup.sh --lang ts --no-install <tmp> writes executable .husky/{pre-commit,pre-push,commit-msg} and sets core.hooksPath=.husky - negative: when no husky hook templates are reachable, setup exits non-zero, leaves core.hooksPath unset, and creates no .husky files. Pins the fail-closed behavior added in scripts/setup.sh (P0-3 S4) and prevents the 'silent claim, empty .husky/' regression from returning. * docs(install): restore complete GitHub Action workflow with actions/checkout (P0-4 S1) INSTALL.md GH Action snippet was reduced to a 4-line excerpt during PR1's collapse, dropping the surrounding 'name', 'on', 'permissions', and 'jobs' boilerplate plus the critical 'actions/checkout@v4' step. Users copy-pasting the snippet ended up with a workflow that runs AgentLint against an empty runner workspace. Restores the full copy-paste workflow with checkout. Adds an inline note that explains why checkout is required. docs/content/intro.md already had the complete workflow (PR1 only collapsed INSTALL.md), so no intro.md change is needed for S2. The hygiene test added in S3 enforces both files keep checkout. * test(registry): pin Action snippets carry actions/checkout (P0-4 S3) Scans every yaml/yml fenced block in README.md, README_CN.md, INSTALL.md, docs/content/intro.md. For any block that references 0xmariowu/AgentLint@<ref> or 0xmariowu/agent-lint@<ref>, asserts the same block contains 'actions/checkout'. Locks the F002/S1 fix in: future doc edits cannot drop checkout from the canonical Action quickstart again. registry-consistency suite: 96 -> 97 tests. * fix(install): replace failing 'agentlint check --help' verify command (P0-6 S1) INSTALL.md Verify section told users to run 'agentlint check --help' right after install. The command exits 1 with 'scanner produced no output' because the help router does not handle 'check --help' as a help request — it falls through to scanner.sh which expects scan input on stdin and finds none. Replaces with 'agentlint help', which prints the full CLI help and exits 0. Smoke-tested via npm pack + isolated --prefix install. Closes P0-6 first-use install verification failure. * test(registry): pin INSTALL.md Verify uses working commands (P0-6 S2) Locks in F003/S1 fix: INSTALL.md '## Verify' section must NOT contain 'agentlint check --help' (broken: scanner.sh blocks on stdin) AND must contain 'agentlint --version' AND ('agentlint help' OR 'agentlint check --project-dir'). Future doc edits that reintroduce the broken verify command fail CI. registry-consistency suite: 97 -> 98 tests. * docs: surface npm install -g side effects up front (P0-9 S1+S2) INSTALL.md gains a '## Side effects' section right after the 'For AI coding agents' frontmatter, before '## Default'. States plainly that 'npm install -g agentlint-ai' writes to ~/.claude/ and registers the /al plugin, and points at --ignore-scripts as the opt-out path (also in Failure modes). README.md install snippet gets an inline blockquote pointing at INSTALL.md#side-effects so users see the disclosure without leaving the canonical README path. Closes P0-9 — restores the prominent opt-in notice that PR1's install collapse dropped along with the --ignore-scripts narrative. * test(registry): pin INSTALL Side effects + raise length cap to 100 (P0-9 S3) New runTest 'INSTALL.md Side effects section is present and lists ~/.claude + --ignore-scripts' locks the F004/S1 admonition in: INSTALL.md must contain '## Side effects' header + ~/.claude mention + --ignore-scripts mention. Reconciles with two PR1-era tests that started failing once side- effect disclosure was restored: 1. README/README_CN must NOT mention '--ignore-scripts' (stays clean of npm flag noise on the main install path). README inline note rewritten to point at INSTALL.md#side-effects without naming the flag literally. 2. INSTALL.md length cap was 80 lines (PR1.5 buffer). Today's P0 work added 12 lines of GitHub Action workflow expansion (P0-4) plus 4 lines of Side effects section (P0-9). Cap raised to 100 so legitimate future P0 fix additions don't force prose trimming gymnastics. registry-consistency suite: 98 -> 99 tests. * test(e2b): assert husky hooks generated + core.hooksPath set in S04 (P0-3 / F005 S1) setup-ts scenario verifier (run.sh inline Python) now checks: - .husky/pre-commit / pre-push / commit-msg exist and are executable - git config core.hooksPath == .husky S04-setup-lang-ts specs.json expected.files_exist also lists the husky paths for documentation; the live check happens in run.sh. S05-setup-lang-python intentionally NOT changed: scripts/setup.sh only walks the husky branch when LANG != python, so Python projects do not produce .husky/. Asserting them there would always fail. Closes the e2b coverage gap that let pre-PR5 scans pass S04 while the .husky directory was empty (audit P0-3). Plan F005/S1 verify command (which only checked S04) updated by this commit's behavior; specs.json still references hook paths so the verify python -c S04 grep continues to pass. * fix(e2b): accept husky 9+ .husky/_ as valid core.hooksPath value Husky 9 set core.hooksPath to .husky/_ (internal forwarder) instead of .husky directly; user-facing hooks at .husky/<name> are still invoked via the forwarder. First v116-p0-revalidate run flagged S04 PARTIAL on this strict equality. Relax the check to accept either '.husky' or '.husky/_'. Both are correct husky configurations; rejecting one or the other is a tooling implementation detail, not a setup failure. Confirmed: v116-p0-revalidate2 now 21/21 PASS (Pass rate 100.0%). * chore: bump version to 1.1.6 Bump 5 version sources via scripts/bump-version.sh: - package.json - .claude-plugin/plugin.json - .claude-plugin/marketplace.json - release-metadata.json (regenerated, 58 checks / 6 core dimensions) - README badge + intro check counts (regenerated) Plus CHANGELOG.md v1.1.6 entry naming the 5 P0 fixes from docs/p0-production-blocker-scan-2026-04-25.md (P0-1 bump itself, P0-3 husky hooks, P0-4 INSTALL Action checkout, P0-6 verify command, P0-9 side-effects disclosure) and the 4 deferred P0 items (P0-2 / P0-5 / P0-7 / P0-8) tracked for v1.1.7+. Pre-tag verify gate green: - npm test: 11 suites, 99/99 in registry-consistency - npm run lint:shell: clean - bash scripts/sanitize.sh: 2 warnings (non-fail, history-related) - npm pack --json --dry-run: version 1.1.6 - npm publish --dry-run: + agentlint-ai@1.1.6 (canonical success line) - e2b 21/21 PASS (run-id v116-p0-revalidate2, 68s)
1 parent f5efb9a commit f788332

16 files changed

Lines changed: 471 additions & 19 deletions

File tree

.claude-plugin/marketplace.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@
55
},
66
"metadata": {
77
"description": "Check how well your repo supports AI coding agents.",
8-
"version": "1.1.5"
8+
"version": "1.1.6"
99
},
1010
"plugins": [
1111
{
1212
"name": "agent-lint",
1313
"source": "./",
1414
"description": "Check how well your repo supports AI coding agents.",
15-
"version": "1.1.5",
15+
"version": "1.1.6",
1616
"author": {
1717
"name": "0xmariowu"
1818
},

.claude-plugin/plugin.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "agent-lint",
3-
"version": "1.1.5",
3+
"version": "1.1.6",
44
"description": "Check how well your repo supports AI coding agents.",
55
"author": {
66
"name": "0xmariowu"

CHANGELOG.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,33 @@
22

33
## Unreleased
44

5+
## v1.1.6 (2026-04-26)
6+
7+
Production blocker fix bundle — 5 of 9 P0 items from `docs/p0-production-blocker-scan-2026-04-25.md` plus the version bump itself. Closes the gaps that broke the canonical install + setup paths after the PR1–PR5 hygiene cleanup.
8+
9+
### You can now…
10+
11+
- **Trust `agentlint setup` actually installs husky hooks**`templates/hooks/husky/{pre-commit,pre-push,commit-msg}` now exist as real shell files. Setup fails loud (`die`) when zero hooks would be copied, instead of silently writing `core.hooksPath=.husky` with an empty `.husky/` directory. The audit found this had been broken since v0.9.0 absorbed VibeKit. (P0-3)
12+
- **Copy-paste the INSTALL.md GitHub Action snippet** and have it work — the snippet now includes `actions/checkout@v4`, surrounding `name`/`on`/`permissions`/`jobs` boilerplate, and an inline note explaining why checkout is required. (P0-4)
13+
- **Trust the INSTALL.md verify commands**`agentlint check --help` (which exited 1 with "scanner produced no output") is replaced with `agentlint help`. Both `agentlint --version` and `agentlint help` now exit 0 on a fresh global install. (P0-6)
14+
- **See the `~/.claude/` side effect up front** — INSTALL.md now opens with a "Side effects" section and README links to it inline, instead of burying `--ignore-scripts` deep in failure modes. (P0-9)
15+
- **Run `agentlint check`, `fix`, and `setup` from `agentlint-ai@1.1.6`** — published from the bumped commit, with the `v1` moving major tag fast-forwarded by `release.yml`. (P0-1)
16+
17+
### Tests added (regression pinning)
18+
19+
- `INSTALL.md Verify section uses commands that actually exit 0`
20+
- `public docs GitHub Action snippets include actions/checkout`
21+
- `INSTALL.md Side effects section is present and lists ~/.claude + --ignore-scripts`
22+
- `tests/test-husky-hooks.sh` — positive + negative path on `setup --no-install`
23+
- `tests/e2b/scenarios/install/run.sh` setup-ts now asserts `.husky/{pre-commit,pre-push,commit-msg}` exist + executable + `core.hooksPath` is `.husky` (accepting husky-9 `.husky/_`)
24+
25+
### Deferred (tracked for v1.1.7+)
26+
27+
- **P0-2** remote `main` branch protection enforces declared required checks (GitHub UI/API config drift; decoupled from package release).
28+
- **P0-5** portable symlink resolver in `scripts/agentlint.sh` and `src/scanner.sh` (current `readlink -f` fallback chain breaks on environments without GNU `readlink`).
29+
- **P0-7** `node src/reporter.js` no-stdin TTY hang.
30+
- **P0-8** `/al` session-analyzer cross-attribution tightening.
31+
532
## v1.1.5 (2026-04-25)
633

734
Hardening release — 24 fixes from a second deep audit. Closes the

INSTALL.md

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
> **For AI coding agents**: this file is the canonical install reference. Read once, act. Decision tables, not narrative.
44
5+
## Side effects
6+
7+
`npm install -g agentlint-ai` writes to `~/.claude/` and registers the `/al` Claude Code plugin. To install without these side effects, use `npm install -g --ignore-scripts agentlint-ai` (also covered in Failure modes for read-only `~/.claude` environments).
8+
59
## Default
610

711
```bash
@@ -22,13 +26,30 @@ Success signal: `agentlint --version` prints a version string.
2226

2327
## GitHub Action
2428

29+
Complete copy-paste workflow:
30+
2531
```yaml
26-
- uses: 0xmariowu/AgentLint@v1
27-
with:
28-
fail-below: 80
32+
name: AgentLint
33+
34+
on:
35+
pull_request:
36+
push:
37+
branches: [main]
38+
39+
permissions:
40+
contents: read
41+
42+
jobs:
43+
agentlint:
44+
runs-on: ubuntu-latest
45+
steps:
46+
- uses: actions/checkout@v4
47+
- uses: 0xmariowu/AgentLint@v1
48+
with:
49+
fail-below: 80
2950
```
3051
31-
`@v1` is a moving major-version tag. Pin to a specific release (e.g. `@v1.1.5`) if you need reproducibility.
52+
`@v1` is a moving major-version tag. Pin to a specific release (e.g. `@v1.1.5`) if you need reproducibility. The `actions/checkout@v4` step is required — without it, AgentLint scans an empty workspace.
3253

3354
## After install
3455

@@ -44,7 +65,7 @@ In Claude Code, run `/al` for the interactive scan-fix-report flow.
4465

4566
```bash
4667
agentlint --version
47-
agentlint check --help
68+
agentlint help
4869
```
4970

5071
## Uninstall

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
npm install -g agentlint-ai
3131
```
3232

33+
> Side effects: writes to `~/.claude/` and registers the `/al` Claude Code plugin. Opt-out path in [INSTALL.md](./INSTALL.md#side-effects).
34+
3335
Then in any git repo:
3436

3537
```bash

docs/content/changelog.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,33 @@
22

33
## Unreleased
44

5+
## v1.1.6 (2026-04-26)
6+
7+
Production blocker fix bundle — 5 of 9 P0 items from `docs/p0-production-blocker-scan-2026-04-25.md` plus the version bump itself. Closes the gaps that broke the canonical install + setup paths after the PR1–PR5 hygiene cleanup.
8+
9+
### You can now…
10+
11+
- **Trust `agentlint setup` actually installs husky hooks**`templates/hooks/husky/{pre-commit,pre-push,commit-msg}` now exist as real shell files. Setup fails loud (`die`) when zero hooks would be copied, instead of silently writing `core.hooksPath=.husky` with an empty `.husky/` directory. The audit found this had been broken since v0.9.0 absorbed VibeKit. (P0-3)
12+
- **Copy-paste the INSTALL.md GitHub Action snippet** and have it work — the snippet now includes `actions/checkout@v4`, surrounding `name`/`on`/`permissions`/`jobs` boilerplate, and an inline note explaining why checkout is required. (P0-4)
13+
- **Trust the INSTALL.md verify commands**`agentlint check --help` (which exited 1 with "scanner produced no output") is replaced with `agentlint help`. Both `agentlint --version` and `agentlint help` now exit 0 on a fresh global install. (P0-6)
14+
- **See the `~/.claude/` side effect up front** — INSTALL.md now opens with a "Side effects" section and README links to it inline, instead of burying `--ignore-scripts` deep in failure modes. (P0-9)
15+
- **Run `agentlint check`, `fix`, and `setup` from `agentlint-ai@1.1.6`** — published from the bumped commit, with the `v1` moving major tag fast-forwarded by `release.yml`. (P0-1)
16+
17+
### Tests added (regression pinning)
18+
19+
- `INSTALL.md Verify section uses commands that actually exit 0`
20+
- `public docs GitHub Action snippets include actions/checkout`
21+
- `INSTALL.md Side effects section is present and lists ~/.claude + --ignore-scripts`
22+
- `tests/test-husky-hooks.sh` — positive + negative path on `setup --no-install`
23+
- `tests/e2b/scenarios/install/run.sh` setup-ts now asserts `.husky/{pre-commit,pre-push,commit-msg}` exist + executable + `core.hooksPath` is `.husky` (accepting husky-9 `.husky/_`)
24+
25+
### Deferred (tracked for v1.1.7+)
26+
27+
- **P0-2** remote `main` branch protection enforces declared required checks (GitHub UI/API config drift; decoupled from package release).
28+
- **P0-5** portable symlink resolver in `scripts/agentlint.sh` and `src/scanner.sh` (current `readlink -f` fallback chain breaks on environments without GNU `readlink`).
29+
- **P0-7** `node src/reporter.js` no-stdin TTY hang.
30+
- **P0-8** `/al` session-analyzer cross-attribution tightening.
31+
532
## v1.1.5 (2026-04-25)
633

734
Hardening release — 24 fixes from a second deep audit. Closes the

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "agentlint-ai",
3-
"version": "1.1.5",
3+
"version": "1.1.6",
44
"description": "Setup, check, and fix your repo for AI-native development.",
55
"license": "MIT",
66
"repository": {

release-metadata.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"version": "1.1.5",
2+
"version": "1.1.6",
33
"check_count": 58,
44
"dimensions": [
55
{

scripts/setup.sh

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -634,13 +634,24 @@ if [[ "$LANG" != "python" ]]; then
634634
chmod +x "$PROJECT/scripts/committer"
635635

636636
# Husky hooks
637+
copied=0
637638
safe_mkdir "$PROJECT/.husky"
638639
for hook in "$TEMPLATE_DIR/hooks/husky/"*; do
639640
[ -f "$hook" ] || continue
640641
hook_name="$(basename "$hook")"
641642
copy_template "$hook" "$PROJECT/.husky/$hook_name" ".husky/$hook_name"
642643
chmod +x "$PROJECT/.husky/$hook_name"
644+
copied=$((copied+1))
643645
done
646+
for helper in "$TEMPLATE_DIR/hooks/_shared.sh" "$TEMPLATE_DIR/hooks/_shared-push.sh"; do
647+
[ -f "$helper" ] || continue
648+
helper_name="$(basename "$helper")"
649+
copy_template "$helper" "$PROJECT/.husky/$helper_name" ".husky/$helper_name"
650+
chmod +x "$PROJECT/.husky/$helper_name"
651+
done
652+
if [[ "$copied" -eq 0 ]]; then
653+
die "no husky hooks generated; refusing to set core.hooksPath. check templates/hooks/husky/ exists and has files"
654+
fi
644655

645656
# Commitlint config — use .cjs if project has "type": "module"
646657
if python3 -c "import json,sys; sys.exit(0 if json.load(open('$PROJECT/package.json')).get('type')=='module' else 1)" 2>/dev/null; then
@@ -785,12 +796,12 @@ if [[ "$LANG" == "python" ]]; then
785796
info "Python: commit-gate via pre-commit (no husky / no npm)"
786797
elif [[ "$LANG" == "node" ]] && [[ ! -f package.json ]]; then
787798
# Node projects without package.json: use git config directly
788-
git config core.hooksPath .husky
799+
[[ $copied -gt 0 ]] && git config core.hooksPath .husky
789800
info "hooks activated via core.hooksPath (no npm)"
790801
elif [[ "$NO_INSTALL" == true ]]; then
791802
# User asked us not to touch their install state. Wire husky via
792803
# git config so hooks still fire once they run `$PKG_MANAGER install`.
793-
git config core.hooksPath .husky
804+
[[ $copied -gt 0 ]] && git config core.hooksPath .husky
794805
info "hooks activated via core.hooksPath (--no-install; run '$PKG_MANAGER install' to complete setup)"
795806
else
796807
# Run the detected PM's install. Modern monorepo PMs (pnpm / bun / yarn
@@ -800,15 +811,15 @@ else
800811
if command -v "$PKG_MANAGER" >/dev/null 2>&1; then
801812
if ! pm_install; then
802813
warn "$PKG_MANAGER install failed — wiring husky via git core.hooksPath as fallback"
803-
git config core.hooksPath .husky
814+
[[ $copied -gt 0 ]] && git config core.hooksPath .husky
804815
fi
805816
else
806817
warn "$PKG_MANAGER not on PATH — wiring husky via git core.hooksPath (install $PKG_MANAGER later to enable lint-staged / commitlint)"
807-
git config core.hooksPath .husky
818+
[[ $copied -gt 0 ]] && git config core.hooksPath .husky
808819
fi
809820
npx --no -- husky 2>/dev/null || true
810821
# Fallback: if husky didn't set hooksPath, set it directly
811-
if ! git config core.hooksPath >/dev/null 2>&1; then
822+
if [[ $copied -gt 0 ]] && ! git config core.hooksPath >/dev/null 2>&1; then
812823
git config core.hooksPath .husky
813824
fi
814825
info "husky activated"

templates/hooks/husky/commit-msg

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
SCRIPT_DIR="$(cd -- "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5+
if [[ -f "$SCRIPT_DIR/_shared.sh" ]]; then
6+
source "$SCRIPT_DIR/_shared.sh"
7+
elif [[ -f "$SCRIPT_DIR/../_shared.sh" ]]; then
8+
source "$SCRIPT_DIR/../_shared.sh"
9+
else
10+
echo "error: _shared.sh is required by commit-msg template" >&2
11+
exit 1
12+
fi
13+
14+
MSG_FILE="${1:-}"
15+
[[ -n "$MSG_FILE" && -f "$MSG_FILE" ]] || fail_with_help \
16+
"commit message file missing" \
17+
"git hook contract requires file arg" \
18+
"Re-run commit normally so Git passes the message file path."
19+
20+
MSG="$(sed -n '1p' "$MSG_FILE" | tr -d '\r')"
21+
if [[ ! "$MSG" =~ ^(feat|fix|refactor|test|docs|chore|ci|style|perf|build|revert)(\(.+\))?:\ .+ ]]; then
22+
fail_with_help \
23+
"commit message is not in conventional format" \
24+
"conventional commit format" \
25+
"Use <type>(<scope>): description, e.g. feat(ui): add loading state"
26+
fi

0 commit comments

Comments
 (0)