Convert nextflow lint to a Stop hook (supersedes #5) - #10
Conversation
Rewrite the linter to address the review on the original PostToolUse implementation: - Stop hook lints once per turn, on the final file state — one JVM startup instead of N, no false positives on half-finished edits. - PostToolUse companion records edited .nf/.config paths to a per-session state file so Stop knows what to check. - Guard against missing jq (the original silently aborted at exit 127 on every edit when jq was absent — common on macOS). - Guard against re-entry via stop_hook_active so a persistent lint failure doesn't trap Claude in an infinite stop loop. - Skip silently when nextflow isn't installed (preserves the original graceful behavior). https://claude.ai/code/session_01BDX5Wh7CXx1hXbfJCyBmMN
|
This seems more reasonable since it waits for the agent to finish editing. It also seems better than encoding it in the skill and hoping the agent remembers to do it I wonder if it would be feasible to just run the LSP in the background and have the agent check the LSP diagnostics after every turn. That would eliminate the JVM overhead and you wouldn't need to record the edits because the LSP does that for you |
The Stop hook blocked the turn from ending (exit 2 prevents stopping), holding up the user while the JVM started. Mark it asyncRewake: it runs in the background, the user gets control back immediately, and a lint failure is surfaced to Claude as a system reminder to fix on the next turn instead of blocking. https://claude.ai/code/session_01BDX5Wh7CXx1hXbfJCyBmMN
Replace the Stop hook + PostToolUse record-edit companion + per-session state file with a single PostToolUse hook scoped by `if` patterns to .nf/.config files (gitignore-style, matches at any depth). asyncRewake runs the lint in the background per edit; a failure surfaces to Claude as a system reminder without blocking. Drops the state-tracking machinery entirely; the script now just extracts the edited path and lints that one file. https://claude.ai/code/session_01BDX5Wh7CXx1hXbfJCyBmMN
|
Related: nextflow-io/language-server#146 We can minimize the JVM overhead in any case by providing a native build |
https://code.claude.com/docs/en/plugins-reference#lsp-servers 😆 |
Supersedes #5 (closed). Adds a
nextflow lintintegration as anif-scoped, async PostToolUse hook, addressing @pditommaso's review on #5.Design
if-scoped PostToolUse (hooks/scripts/nextflow-lint.sh): fires only on.nf/.configedits. Theifpatterns (Edit(*.nf),Write(*.nf),MultiEdit(*.nf)and the*.configvariants) use gitignore semantics, so a bare*.nfmatches at any depth (root and nested). One handler per tool×extension sinceiftakes a single pattern.asyncRewake): the lint runs in the background — never blocks the edit. On a lint failure it exits 2 and the output is surfaced to Claude as a system reminder to fix, rather than a blocking nag.command -v jq,command -v nextflow— skips silently if either is missing (fixes the original macOS-no-jq exit-127 spam).Addressing the review
jqguardcommand -v jq || exit 0asyncRewake→ non-blocking system reminderif-scoped async PostToolUse, no Stop hook or state fileTradeoff worth a look, @pditommaso
This lints per edit rather than once per turn, so it can still run on a half-finished file mid-composition. Because it's async + non-blocking (a soft system reminder, not exit-2 blocking), a stray intermediate-state reminder is cheap and ignorable — but if you'd prefer strict once-per-turn-on-final-state semantics, that requires the Stop-hook approach (which can't use
if). Happy to switch.jq note
I checked whether the edited path is exposed as an env var so we could drop the jq dependency — it isn't (only on stdin JSON, not interpolatable into
command/args). So robust per-file targeting needs jq. The jq-free alternative is to lint the whole project dir each edit, but that's slower and re-surfaces unrelated pre-existing issues. Kept jq (guarded).Verified locally
shellcheck v0.10.0clean;claude plugin validateaccepts theasyncRewake+ifconfigTest plan
.nfin Claude Code; confirm the edit isn't blocked and a lint system-reminder arrives shortly after.nf; nothing reportedworkflows/main.nf); confirm theifglob matchesjq: silenthttps://claude.ai/code/session_01BDX5Wh7CXx1hXbfJCyBmMN