Skip to content

Edit hook passes an absolute path, so path-scoped .vale.ini sections never match #1

Description

@marko-builds

The PostToolUse hook in hooks/vale-edit.sh passes .tool_input.file_path to Vale unchanged. Claude Code always supplies that path absolute. Vale matches a path-scoped section against the file as it was asked for it, so a section like [docs/**/*.md] does not match an absolute path, and Vale reports nothing when it fails to match.

The result is that on a repository whose rules are path-scoped, the hook runs, exits 0, prints nothing, and looks exactly like a clean pass on a file it did not really check.

Checked against dcef76f (the installed plugin copy is byte-identical to it) with Vale 3.18.0 on Linux.

Reproduction

# .vale.ini
StylesPath = styles
MinAlertLevel = suggestion

[*.md]
BasedOnStyles =

[docs/**/*.md]
BasedOnStyles = MyStyle
# styles/MyStyle/Rule.yml
extends: existence
message: "found 'zzqq'"
level: error
tokens:
  - 'zzqq'

With docs/page.md containing A line containing zzqq here.:

$ cd /repo && vale --no-global --output=line docs/page.md
docs/page.md:1:19:MyStyle.Rule:found 'zzqq'          # exit 1

$ cd /repo && vale --no-global --output=line /repo/docs/page.md
                                                     # no output, exit 0

The second form is what the hook produces. Feeding the hook a payload for that same file returns nothing at all.

Suggested fix

Resolve the payload path against the repository root and run Vale from there:

REPO="$(git rev-parse --show-toplevel 2>/dev/null)" || exit 0
case "$file" in
  "$REPO"/*) rel="${file#"$REPO"/}" ;;
  /*) exit 0 ;;
  *) rel="$file" ;;
esac
alerts=$(cd "$REPO" && vale --no-exit --output=JSON "$rel")

A second, smaller point

The hook sends Vale's stderr to /dev/null. A configuration error (E201, E100) leaves stdout empty, so a config that fails to load is indistinguishable from a clean run. Reporting "Vale did not run" as its own outcome would make that visible instead of silently reassuring. Happy to split that into its own issue if you prefer.

Thanks for the plugin. The rest of the design is good, and the error-level-only choice in particular is the right call.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions