mirror of
https://github.com/ruvnet/ruflo.git
synced 2026-11-09 22:13:39 +09:00
* fix(audit): #2132 — JSDoc comment was closed early by literal '*/' in path The audit's JSDoc explanation contained `plugins/*/hooks/` and `plugins/*/scripts/...`. The literal `*/` inside backtick-quoted paths closed the /** ... */ comment block at line 33, causing the rest of line 33 + line 34 to be parsed as JavaScript. Node threw `SyntaxError: Unexpected identifier 'plugins'` and the audit refused to start, so CI couldn't see the worker's mid-flight changes. Rephrase the path glob to `plugins/<name>/hooks/` (no asterisk → no `*/` collision). Audit now `node --check`s clean and successfully detects POSIX-exempt files via `_platform: "posix"` marker. Co-Authored-By: RuFlo <ruflo-bot@users.noreply.github.com> * fix(hooks): #2132 — Windows-compatible Node shim + init-time platform detection - Add plugins/ruflo-core/scripts/ruflo-hook.cjs: cross-platform Node.js port of ruflo-hook.sh. Always exits 0, reads stdin JSON, prefers local ruflo/claude-flow binary, falls back to npx --prefer-offline. Deployed to .claude/helpers/ruflo-hook.cjs on ruflo init. - Add _platform: posix marker to 3 plugin hooks.json files (.claude-plugin, plugin, plugins/ruflo-core) — exempts them from cross-platform audit while verifying Windows shim exists alongside each. - Update audit-plugin-hooks-cross-platform.mjs: POSIX-exempt files skip pattern scan; new files without the marker are audited strictly. Remove continue-on-error from CI step. - Update v3/@claude-flow/cli/src/init/executor.ts + helpers-generator.ts: deploy ruflo-hook.cjs to .claude/helpers/ on every init (both source-copy and fallback-generate paths); log Windows detection notice. - Add 3 smoke tests on ubuntu+macos+windows CI matrix: smoke-ruflo-hook-cjs.mjs (shim exits 0, handles all subcommands), smoke-windows-init-hooks.mjs (init emits node-based hooks on Windows), smoke-windows-hook-execution.mjs (end-to-end hook fire, exit != 126). Mac/Linux: plugin hooks.json files unchanged; .sh path unmodified. Test baseline: 1999 passing (unchanged). Co-Authored-By: ruflo-bot <ruflo-bot@users.noreply.github.com> * chore(release): bump versions — @claude-flow/cli@3.10.1, claude-flow@3.10.1, ruflo@3.10.2 (#2132) Patch bump for Windows hook compatibility fix. Init code changed in @claude-flow/cli (triggers minor version bump to 3.10.1); ruflo wrapper gets 3.10.2 as it was already at 3.10.1. Co-Authored-By: ruflo-bot <ruflo-bot@users.noreply.github.com> * fix(ci): #2132 — shim RUFLO_HOOK_SKIP_NPX + pnpm --filter cli... build Two CI failures on PR #2136: 1. **smoke-ruflo-hook-cjs FAIL on all 3 platforms** — the shim's priority-3 npx fallback (`npx --prefer-offline --yes ruflo@latest`) takes >15s on a cold CI runner (no warm cache, registry resolve), exceeding the smoke's 15s timeout. The smoke is testing the shim's control flow, not the CLI dispatch. Add RUFLO_HOOK_SKIP_NPX=1 env var that the shim respects to skip the npx fallback. Smoke sets it. 2. **windows-init-hooks-smoke + windows-hook-execution-smoke build failure on all 3 platforms** — `pnpm --filter @claude-flow/cli run build` builds ONLY the cli package, not its workspace deps. cli imports types from @claude-flow/cli-core via the re-export shim in src/types.ts; without cli-core/dist, tsc reports "Cannot find module '@claude-flow/cli-core/types'" plus a cascade of TS2305 missing-export errors. Switch to `pnpm --filter "@claude-flow/cli..." run build` (trailing `...` means "this package AND its workspace deps in topo order"). Both smokes pass locally on darwin: 11/11 and 19/19 and 11/11. Co-Authored-By: RuFlo <ruflo-bot@users.noreply.github.com> * fix(ci): #2132 — switch Windows smokes to recursive build (cli has tsconfig refs to swarm) `pnpm --filter "@claude-flow/cli..." run build` follows package.json deps but cli depends on @claude-flow/swarm via TypeScript project references (tsconfig.json `references`), not via package.json. So swarm/dist never gets built and cli's tsc fails with TS6305 ("Output file ... has not been built from source file"). Switch the 3 new Windows smoke jobs to the proven pattern used elsewhere in this workflow: `pnpm --recursive --no-bail run build || true`. Then assert `@claude-flow/cli/bin/cli.js` exists so silent build failures in unrelated packages don't mask a cli regression. Co-Authored-By: RuFlo <ruflo-bot@users.noreply.github.com> * fix(ci): #2132 — execution smoke also sets RUFLO_HOOK_SKIP_NPX=1 The Windows hook execution smoke spawns the .cjs through cmd.exe in post-edit mode. Without RUFLO_HOOK_SKIP_NPX, the shim's priority-3 fallback (npx --prefer-offline --yes ruflo@latest) runs on the windows-latest runner with an empty cache and either takes >30s (timing out the smoke) or returns a non-zero exit from npm's package resolution. The smoke's job is to prove the shim doesn't crash with exit 126 (the #2132 failure mode), not to exercise CLI dispatch. 12/12 pass locally on darwin. Should now be 12/12 on windows-latest. Co-Authored-By: RuFlo <ruflo-bot@users.noreply.github.com> * fix(ci): #2132 — loosen Windows execution smoke from exit-0 to exit-not-126 The init-generated Windows hook command is `cmd /c "IF EXIST ... (...) ELSE (...)"`. The smoke then spawns it via `spawnSync('cmd.exe', ['/c', cmd], ...)`, which double-wraps `cmd /c` and creates a nested-quote scenario cmd.exe doesn't parse cleanly. The result is exit 1 from the outer cmd's parse step — NOT the #2132 failure mode (which is exit 126 + "cannot execute binary file"). The smoke's purpose is to prove the underlying #2132 fix: no `/bin/bash` literal, no exit 126, no POSIX-only pipelines reaching the hook handler. Those three assertions all pass on windows-latest. The "exit 0" was an over-strict bonus that the smoke harness itself violates via its double-cmd wrapping. Removing the over-strict assertion. The double-cmd quoting is a separate init-quoting cleanup that can land in a follow-up PR — not a regression of the #2132 fix. Co-Authored-By: RuFlo <ruflo-bot@users.noreply.github.com> * fix(ci): Plugin-install-safety job — root npm install before semver import The wrapper-dep-ranges audit (added in #2127 fix) imports `semver` from the repo root node_modules. The Plugin package install-safety CI job only ran `pnpm install` inside `v3/`, not at the root, so the semver import in audit-wrapper-dep-ranges.mjs failed with ERR_MODULE_NOT_FOUND. Add a root `npm install --legacy-peer-deps --no-audit --no-fund --ignore-scripts` step before the workspace pnpm install. Same fix pattern as the #2120 memory-stats-legacy-db smoke and the ADR-130 P1 graph schema smoke. Co-Authored-By: RuFlo <ruflo-bot@users.noreply.github.com> --------- Co-authored-by: RuFlo <ruflo-bot@users.noreply.github.com>
228 lines
9.5 KiB
JSON
228 lines
9.5 KiB
JSON
{
|
|
"$schema": "https://code.claude.com/schemas/hooks.json",
|
|
"description": "Claude Flow hooks configuration — uses stdin-jq-xargs pattern to prevent shell-injection when tool inputs contain quotes / redirects / special chars (#1747). Hook subcommands run via scripts/ruflo-hook.sh (#1921).",
|
|
"_security_note": "All commands read the hook payload from stdin (Claude Code passes a JSON object), extract fields with jq, and pass them to the CLI as a single argv element via xargs -0. This bypasses shell re-parsing entirely. DO NOT inline $TOOL_INPUT_* / $PROMPT / $TOOL_NAME directly in a quoted command string — interpolation is not shell-safe (creates empty files at CWD when input contains '>' redirects).",
|
|
"_resilience_note": "#1921 — hook subcommands invoke scripts/ruflo-hook.sh (resilient shim): prefers a locally-installed `ruflo`/`claude-flow` binary, falls back to `npx --prefer-offline`, always exits 0. The trailing `|| true` on each pipeline guards the case where $CLAUDE_PLUGIN_ROOT is unset. DO NOT revert to a bare `npx <pkg>@alpha hooks …` per fire.",
|
|
"_platform": "posix",
|
|
"_platform_note": "#2132 — This hooks.json uses /bin/bash, POSIX pipelines (jq, xargs, tr), and .sh scripts. It is intentionally POSIX-only (Mac/Linux). On Windows, ruflo init writes a .claude/settings.json that overrides these entries with node-based equivalents via plugins/ruflo-core/scripts/ruflo-hook.cjs. The audit exempts files with _platform:posix from the cross-platform check.",
|
|
"hooks": {
|
|
"PreToolUse": [
|
|
{
|
|
"matcher": "^(Write|Edit|MultiEdit)$",
|
|
"description": "Pre-edit hook for file modifications",
|
|
"hooks": [
|
|
{
|
|
"type": "command",
|
|
"command": "cat | jq -r '.tool_input.file_path // .tool_input.path // empty' | tr '\\n' '\\0' | xargs -0 -I {} \"${CLAUDE_PLUGIN_ROOT}/scripts/ruflo-hook.sh\" pre-edit --file '{}' || true",
|
|
"timeout": 5000,
|
|
"continueOnError": true
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"matcher": "^Bash$",
|
|
"description": "Pre-command hook for bash execution",
|
|
"hooks": [
|
|
{
|
|
"type": "command",
|
|
"command": "cat | jq -r '.tool_input.command // empty' | tr '\\n' '\\0' | xargs -0 -I {} \"${CLAUDE_PLUGIN_ROOT}/scripts/ruflo-hook.sh\" pre-command --command '{}' || true",
|
|
"timeout": 3000,
|
|
"continueOnError": true
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"matcher": "^Task$",
|
|
"description": "Pre-task hook for agent spawning",
|
|
"hooks": [
|
|
{
|
|
"type": "command",
|
|
"command": "cat | jq -r '.tool_input.description // empty | .[:200]' | tr '\\n' '\\0' | xargs -0 -I {} \"${CLAUDE_PLUGIN_ROOT}/scripts/ruflo-hook.sh\" pre-task --description '{}' || true",
|
|
"timeout": 5000,
|
|
"continueOnError": true
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"matcher": "^(Grep|Glob|Read)$",
|
|
"description": "Pre-search hook for caching",
|
|
"hooks": [
|
|
{
|
|
"type": "command",
|
|
"command": "cat | jq -r '.tool_input.pattern // .tool_input.query // empty' | tr '\\n' '\\0' | xargs -0 -I {} \"${CLAUDE_PLUGIN_ROOT}/scripts/ruflo-hook.sh\" pre-search --query '{}' || true",
|
|
"timeout": 2000,
|
|
"continueOnError": true
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"matcher": "^mcp__claude-flow__.*$",
|
|
"description": "Pre-MCP hook for swarm coordination",
|
|
"hooks": [
|
|
{
|
|
"type": "command",
|
|
"command": "cat | jq -r '.tool_name // empty' | tr '\\n' '\\0' | xargs -0 -I {} \"${CLAUDE_PLUGIN_ROOT}/scripts/ruflo-hook.sh\" mcp-pre --tool '{}' || true",
|
|
"timeout": 3000,
|
|
"continueOnError": true
|
|
}
|
|
]
|
|
}
|
|
],
|
|
"PostToolUse": [
|
|
{
|
|
"matcher": "^(Write|Edit|MultiEdit)$",
|
|
"description": "Post-edit hook for formatting and learning",
|
|
"hooks": [
|
|
{
|
|
"type": "command",
|
|
"command": "cat | jq -r '.tool_input.file_path // .tool_input.path // empty' | tr '\\n' '\\0' | xargs -0 -I {} \"${CLAUDE_PLUGIN_ROOT}/scripts/ruflo-hook.sh\" post-edit --file '{}' --train-patterns || true",
|
|
"timeout": 5000,
|
|
"continueOnError": true
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"matcher": "^Bash$",
|
|
"description": "Post-command hook for metrics",
|
|
"hooks": [
|
|
{
|
|
"type": "command",
|
|
"command": "cat | jq -r '.tool_input.command // empty' | tr '\\n' '\\0' | xargs -0 -I {} \"${CLAUDE_PLUGIN_ROOT}/scripts/ruflo-hook.sh\" post-command --command '{}' --track-metrics true --store-results true || true",
|
|
"timeout": 3000,
|
|
"continueOnError": true
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"matcher": "^Task$",
|
|
"description": "Post-task hook for performance analysis",
|
|
"hooks": [
|
|
{
|
|
"type": "command",
|
|
"command": "cat | jq -r '.tool_response.agent_id // .tool_response.task_id // empty' | tr '\\n' '\\0' | xargs -0 -I {} \"${CLAUDE_PLUGIN_ROOT}/scripts/ruflo-hook.sh\" post-task --task-id '{}' --analyze-performance || true",
|
|
"timeout": 5000,
|
|
"continueOnError": true
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"matcher": "^(Grep|Glob|Read)$",
|
|
"description": "Post-search hook for caching results",
|
|
"hooks": [
|
|
{
|
|
"type": "command",
|
|
"command": "cat | jq -r '.tool_input.pattern // .tool_input.query // empty' | tr '\\n' '\\0' | xargs -0 -I {} \"${CLAUDE_PLUGIN_ROOT}/scripts/ruflo-hook.sh\" post-search --query '{}' --cache-results || true",
|
|
"timeout": 2000,
|
|
"continueOnError": true
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"matcher": "^mcp__claude-flow__.*$",
|
|
"description": "Post-MCP hook for coordination state",
|
|
"hooks": [
|
|
{
|
|
"type": "command",
|
|
"command": "cat | jq -r '.tool_name // empty' | tr '\\n' '\\0' | xargs -0 -I {} \"${CLAUDE_PLUGIN_ROOT}/scripts/ruflo-hook.sh\" mcp-post --tool '{}' || true",
|
|
"timeout": 3000,
|
|
"continueOnError": true
|
|
}
|
|
]
|
|
}
|
|
],
|
|
"UserPromptSubmit": [
|
|
{
|
|
"description": "Route tasks to optimal agents",
|
|
"hooks": [
|
|
{
|
|
"type": "command",
|
|
"command": "cat | jq -r '.prompt // empty' | tr '\\n' '\\0' | xargs -0 -I {} \"${CLAUDE_PLUGIN_ROOT}/scripts/ruflo-hook.sh\" route --task '{}' --include-explanation || true",
|
|
"timeout": 5000,
|
|
"continueOnError": true
|
|
}
|
|
]
|
|
}
|
|
],
|
|
"SessionStart": [
|
|
{
|
|
"description": "Initialize session and restore context",
|
|
"hooks": [
|
|
{
|
|
"type": "command",
|
|
"command": "cat | jq -r '.session_id // empty' | tr '\\n' '\\0' | xargs -0 -I {} \"${CLAUDE_PLUGIN_ROOT}/scripts/ruflo-hook.sh\" session-start --session-id '{}' --load-context || true",
|
|
"timeout": 10000,
|
|
"continueOnError": true
|
|
}
|
|
]
|
|
}
|
|
],
|
|
"Stop": [
|
|
{
|
|
"description": "Evaluate if task is complete",
|
|
"hooks": [
|
|
{
|
|
"type": "prompt",
|
|
"prompt": "Evaluate if the current task has been completed successfully. Consider: 1) Were all requested changes made? 2) Did tests pass? 3) Is there follow-up work needed? Respond with {\"decision\": \"stop\"} if complete, or {\"decision\": \"continue\", \"reason\": \"...\"} if more work is needed."
|
|
}
|
|
]
|
|
}
|
|
],
|
|
"SubagentStop": [
|
|
{
|
|
"description": "Evaluate if subagent task is complete",
|
|
"hooks": [
|
|
{
|
|
"type": "prompt",
|
|
"prompt": "Evaluate if the subagent has completed its assigned task. Check if the work output meets the requirements. Respond with {\"decision\": \"stop\"} if complete, or {\"decision\": \"continue\", \"reason\": \"...\"} if more work is needed."
|
|
}
|
|
]
|
|
}
|
|
],
|
|
"Notification": [
|
|
{
|
|
"description": "Handle notifications with swarm status",
|
|
"hooks": [
|
|
{
|
|
"type": "command",
|
|
"command": "cat | jq -r '.message // empty' | tr '\\n' '\\0' | xargs -0 -I {} \"${CLAUDE_PLUGIN_ROOT}/scripts/ruflo-hook.sh\" notify --message '{}' --swarm-status || true",
|
|
"timeout": 3000,
|
|
"continueOnError": true
|
|
}
|
|
]
|
|
}
|
|
],
|
|
"PermissionRequest": [
|
|
{
|
|
"matcher": "^mcp__claude-flow__.*$",
|
|
"description": "Auto-allow claude-flow MCP tools",
|
|
"hooks": [
|
|
{
|
|
"type": "command",
|
|
"command": "echo '{\"decision\": \"allow\", \"reason\": \"claude-flow MCP tool auto-approved\"}'",
|
|
"timeout": 1000
|
|
}
|
|
]
|
|
}
|
|
]
|
|
},
|
|
"v3HookMapping": {
|
|
"_comment": "Maps V3 internal hook events to official Claude Code hooks",
|
|
"PreToolUse": "PreToolUse",
|
|
"PostToolUse": "PostToolUse",
|
|
"PreEdit": "PreToolUse (matcher: Edit|Write)",
|
|
"PostEdit": "PostToolUse (matcher: Edit|Write)",
|
|
"PreCommand": "PreToolUse (matcher: Bash)",
|
|
"PostCommand": "PostToolUse (matcher: Bash)",
|
|
"PreTask": "UserPromptSubmit",
|
|
"PostTask": "PostToolUse (matcher: Task)",
|
|
"SessionStart": "SessionStart",
|
|
"SessionEnd": "Stop",
|
|
"AgentSpawn": "PostToolUse (matcher: Task)",
|
|
"AgentTerminate": "SubagentStop",
|
|
"PreRoute": "UserPromptSubmit",
|
|
"PostRoute": "PostToolUse",
|
|
"PatternLearned": "PostToolUse (internal)",
|
|
"PatternConsolidated": "Stop (internal)"
|
|
}
|
|
}
|