Commit Graph
9 Commits
Author SHA1 Message Date
Andre Brait ae334f99f3 fix(hook): leave piped greps out of the nudge hint
A grep fed by a pipe filters that command's output, not the code, so its
pattern is not a graph lookup the agent skipped.

(cherry picked from commit f9f3c9ce3a)
2026-10-13 10:26:57 +01:00
Andre Brait cdbaa16245 fix(hook): harden nudge hints after review
Sanitise graph text before it reaches the hint (one line, no control
characters or backticks, capped length), skip the graph parse when the call
has nothing to look up, isolate hint failures so they can never cost the
nudge or the strict deny, skip value-taking flags (rg -t/-g, -A/-B/-C...)
and file-name searches (find/fd), drop declaration keywords from terms,
match labels only (no source-path tier, short terms exact-only), resolve a
relative read target against the project root, and align the strict deny's
wording with the narrowed nudge.

(cherry picked from commit f8ba08fe93)
2026-10-13 10:26:57 +01:00
Andre Brait ae2035efa0 feat(hook): show the graph nodes behind a nudged call, and tighten its wording
Agents treated the soft nudge as noise: it was the same placeholder text on
every call, and "or to modify/debug specific lines" excused nearly any
targeted read. A firing nudge (and the strict-mode deny) now carries:

- a command built from the call: `graphify explain "<symbol>"` when the
  searched word is a node label, `graphify query "<terms>"` otherwise, or
  `graphify explain "<file>"` for a read;
- up to five matching graph nodes with file and line, so the nudge shows
  what the graph already knows instead of only asking for it.

The lookup scans graph.json labels and paths directly (0.1-0.5 s on 3k-15k
node graphs; the full query stack costs ~1.5 s per hook call), skips graphs
over 32 MiB, and falls back to the plain nudge on any failure or no match.

The escape clause now allows raw reads only once the exact file and lines
are known, and the always-on rule names the moment to query: before the
first grep, search or source-file read.

(cherry picked from commit b51314765c)
2026-10-13 10:26:57 +01:00
41418864aa fix(hook): bound the soft read/search nudge per session (#3435)
The read/search nudge was re-injected on every qualifying Read/Glob/Grep/Bash
call with no dedup or session cap (issue #3435 measured ~8,348 injections,
~651k tokens, for 339 graphify calls), and an agent that just ran
`graphify query` was still nudged on its very next read. `_nudge_allowed`
now gates all three soft-nudge sites with a per-session + per-agent counter
(first nudge always fires; later ones suppressed while the query stamp is
fresh; hard cap via GRAPHIFY_HOOK_NUDGE_CAP, default 5), with 24h GC and
fail-open preserved. Subagents get independent budgets. The strict deny path
is unchanged.

Landed from PR #3443 (squashed). The README strict-mode note is omitted here
because that prose moved to docs.graphify.com in the README trim.

Co-Authored-By: Andre Brait <andrebrait@gmail.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-10-12 12:15:19 +01:00
Raphael Malikian bd20e4c31a fix(hook-guard): fire the Read nudge for PowerShell source files
hook-guard read returned early for .ps1/.psm1/.psd1 targets because the
extension gate (_HOOK_SOURCE_EXTS in graphify/cli.py) did not list them,
even though detect.py classifies those extensions as code and they are
extracted into the graph. In a PowerShell repo the Read/Glob nudge never
fired, only the search one.

Fixes Graphify-Labs/graphify#4271.

Signed-off-by: Raphael Malikian <rtmalikian@gmail.com>
Co-Authored-By: Claude <noreply@anthropic.com>
(cherry picked from commit 1ee08e70ba)
2026-10-10 21:37:04 +01:00
Deepanshu Pal efa296e817 test(hook-guard): cover custom GRAPHIFY_OUT reminder text (#4040)
Regression tests for #4040: read, stale-read and search reminders name the effective graph.json under a custom GRAPHIFY_OUT, and the default output dir stays byte-identical.

Co-Authored-By: Claude <noreply@anthropic.com>
(cherry picked from commit 31cb3bb1c0)
2026-10-04 02:07:00 +01:00
shobhitagnihotri69 8e5649fe83 fix(cli): resolve hook-guard output paths instead of substring matching (#3959)
(cherry picked from commit 3fd280e1b2)
2026-10-02 17:10:07 +01:00
abhay-codes07 3993f023f7 fix(cli): hook-guard search detection matches executed commands, not prose (#3121)
The hook-guard matched a search command appearing anywhere in the intercepted Bash string
(a comment, a quoted -m message, an echo), firing false nudges. Match only an executed
command position (start-of-segment after a shell operator, wrapper-aware), so prose lookalikes
stay quiet while genuine pipes/&&/;/$() invocations still fire.
2026-08-30 15:11:40 +01:00
safishamsiandClaude Opus 4.8 737a0b8454 test: rigorous edge cases for the hook-guard subcommand (#522)
Adds a wide matrix over _run_hook_guard covering the search/read detection
boundaries and the gemini BeforeTool contract:

- search: grep-family variants (grep/pgrep/egrep/fgrep/ripgrep), token matches
  (rg/find/fd/ack/ag with trailing space), piped commands; and silence for
  non-search commands, empty/missing/non-string command, 'find' without a
  trailing space, 'ag' mid-word, top-level vs nested tool_input, non-dict
  tool_input, and no-graph.
- read: source/framework extensions, uppercase and multi-dot names, Windows
  backslash paths, glob patterns; and silence for .json (not .js), lockfiles,
  images, extensionless files, an extension on a directory segment, targets
  under the (default and custom-named) output dir, and no-graph.
- fail-open: malformed/empty/binary stdin and a throwing graph-existence check
  never crash or block.
- gemini: always returns {"decision":"allow"}, nudges only with a graph, and
  stays "allow" even if the check throws.
- dispatch/exit/encoding via real subprocess: missing/unknown mode exits 0
  silently, every mode exits 0 (never blocks), and the read nudge's em dash
  round-trips as valid UTF-8.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-06 16:49:31 +01:00