iFlow CLI Hooks 「从入门到实战」应用指南
Hooks(钩子)是 iFlow CLI 中的事件驱动机制,允许您在特定的生命周期事件发生时自动执行自定义命令。通过配置 Hooks,您可以实现工具调用前后的自动化处理、环境设置增强、会话停止时的清理操作等功能。
Hooks配置官方介绍见:Hooks | 心流开放平台
配置方法概述
1. Hook 类型(事件类型)
Hook 类型决定了 Hook 在何时触发。iFlow CLI 目前支持 9 种 Hooks 类型:
| 事件 | 触发时机 | 典型用途 |
|---|---|---|
| PreToolUse | 工具执行前 | 阻止危险命令、参数校验 |
| PostToolUse | 工具执行后 | 自动格式化、运行 linter |
| SetUpEnvironment | 会话开始时,环境信息设置阶段 | 动态生成项目信息、增强 AI 上下文 |
| Stop | 主会话结束时 | 清理资源、保存会话信息 |
| SubagentStop | 子代理会话结束时 | 任务完成度验证、错误检测 |
| SessionStart | 会话开始时(启动、恢复、清理、压缩) | 环境初始化、加载配置 |
| SessionEnd | 会话正常结束时 | 生成会话总结、归档记录 |
| UserPromptSubmit | 用户提交提示词前 | 内容过滤、敏感信息拦截 |
| Notification | iFlow 发送通知时 | 通知记录、第三方系统集成 |
2. Matcher(匹配器)
“matcher”: 精确控制哪些工具调用会触发该 Hook
{"matcher": "Bash", // 针对 Bash 命令"matcher": "Write|Edit", // 针对文件写入或编辑"matcher": "*", // 针对所有工具"matcher": "startup", // 仅在新会话启动时触发"matcher": ".*permission.*" // 正则表达式:匹配包含"permission"的通知
}
3. Command(命令)
Command 是 Hook 触发时执行的具体命令:
-
可以是简单的 Shell 命令:
echo 'Subagent task completed' -
可以是 Python 脚本:
python3 .iflow/hooks/security_check.py
4. 配置路径
your-project 或根目录~/
├── .iflow/
│ ├── settings.json
│ └── hooks/
│ └── security_check.py
├── .env # 敏感文件
├── .env.example
└── src/└── main.py
5. Hook 配置
配置文件路径:.iflow/seetings.json
多个 hooks 示例:
{"hooks": {"SessionStart": [{"matcher": "startup","hooks": [{"type": "command","command": "python3 ~/.iflow/hooks/session_start.py"}]}],"SessionEnd": [{"hooks": [{"type": "command","command": "python3 ~/.iflow/hooks/session_summary.py","timeout": 30}]}],"UserPromptSubmit": [{"hooks": [{"type": "command","command": "python3 ~/.iflow/hooks/content_filter.py","timeout": 10}]}]}
}
Hook类型
1. PreToolUse Hook
触发时机:在工具执行之前
用途:
-
验证工具参数
-
设置执行环境
-
记录工具调用日志
-
阻止不安全的操作
配置说明:
配置文件位置: ~/.iflow/settings.json
Hook 脚本位置: ~/.iflow/hooks/
示例配置:
添加一个钩子,记录 iFlow cli 运行的 shell 命令,在命令行中安装 jq 以进行 JSON 处理。
配置前,先本地安装 jq :
npm install jq
在 settings.json 文件中配置以下 Hook ,该 Hook 可以输出 ToolUse 的 bash 相关命令和描述
{"hooks": {"PreToolUse": [{"matcher": "Bash","hooks": [{"type": "command","command": "jq -r '\"\\(.tool_input.command) - \\(.tool_input.description // \"No description\")\"' >> ~/.iflow/bash-command-log.txt"}]}]}
}
如想查看具体的log信息,可通过命令 cat ~/.iflow/bash-command-log.txt 示例如下:

2. PostToolUse Hook
触发时机:在工具执行之后 用途:
-
处理工具执行结果
-
清理临时文件
-
发送通知
-
记录执行统计
和 PreToolUse Hook 一样可以使用 "matcher" 精确控制哪些工具调用会触发该 Hook
示例配置:
尝试添加一个 tool 编辑后自动格式化 TypeScript 文件的 Hook :
{"hooks": {"PostToolUse": [{"matcher": "Edit|Write","hooks": [{"type": "command","command": "jq -r '.tool_input.file_path' | { read file_path; if echo \"$file_path\" | grep -q '\\.ts$'; then npx prettier --write \"$file_path\"; fi; }"}]}]}
}
3. SetUpEnvironment Hook
触发时机:会话开始时,环境信息设置阶段 用途:
-
动态生成项目信息
-
设置运行时环境变量
-
增强 AI 的上下文信息
-
加载项目特定配置
示例配置:
创建一个 hook ,可以在每次会话开始时,调用 initial.py 打印所有进程信息
{"hooks": {"SetUpEnvironment": [{"hooks": [{"type": "command","command": "python3 ~/.iflow/hooks/initial.py","timeout": 30}]}]}
}
initial.py 脚本代码:(保存路径为 .iflow/hooks/initial.py)
#!/usr/bin/env python3
import os
import subprocess
import datetimetimestamp = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')# 扫描所有进程
try:result = subprocess.run(['ps', 'aux'], capture_output=True, text=True)all_processes = result.stdout
except Exception as e:all_processes = f'Error: {str(e)}'# 记录启动时的所有进程
with open(os.path.expanduser('~/.iflow/start.txt'), 'w') as f:f.write(f'{timestamp}\n')f.write('All Processes:\n')f.write(all_processes)
启动后打印示例如下:

4. Stop Hook
触发时机:主会话结束时 用途:
-
清理会话资源
-
保存会话信息
-
发送会话总结
-
执行清理脚本
示例配置:
尝试添加一个主会话结束的 hook ,每次退出后记录退出时间
{"hooks": {"Stop": [{"hooks": [{"type": "command","command": "python3 ~/.iflow/hooks/save.py","timeout": 30}]}]}
}
save.py 脚本代码:(保存路径为 .iflow/hooks/save.py)
#!/usr/bin/env python3
import os
import datetimetimestamp = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
with open(os.path.expanduser('~/.iflow/stop-time.txt'), 'w') as f:f.write(timestamp + '\n')
打开终端,进行操作后退出
cat ~/.iflow/stop-time.txt 查看退出时间

5. SubagentStop Hook
触发时机:子代理会话结束时
用途:在子代理停止前进行决策评估
使用场景:
-
任务完成度验证:确保子代理真正完成任务
-
错误检测:发现并阻止有错误的子代理停止
-
上下文完整性检查:确保已收集足够信息
-
质量控制:在子代理停止前进行智能审核
6. SessionStart Hook
触发时机:会话开始时(启动、恢复、清理、压缩)
用途:在会话开始时(启动、恢复、清理、压缩)执行初始化操作,设置环境变量、加载配置或提供上下文信息
支持的matcher值:
-
"startup"- 新会话启动 -
"resume"- 恢复已有会话 -
"clear"- 清理会话 -
"compress"- 压缩会话
7. SessionEnd Hook
触发时机:会话正常结束时
用途:在会话结束时运行。它们不能阻止会话终止,但可以执行清理任务
**实战案例:**每次会话结束时,自动生成包含会话 ID、时间戳和 Git 活动记录的 Markdown 文件,方便后续回顾和分析工作历史。
示例配置:
{"hooks": {"SessionEnd": [{"hooks": [{"type": "command","command": "python3 ~/.iflow/hooks/session_summary.py","timeout": 30}]}]}
}
session_summary.py 脚本代码:(保存路径为 .iflow/hooks/session_summary.py)
#!/usr/bin/env python3
import os
import datetime
import subprocesssession_id = os.environ.get('IFLOW_SESSION_ID', 'unknown')
timestamp = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
summary_dir = os.path.expanduser('~/.iflow/session-summaries')
os.makedirs(summary_dir, exist_ok=True)try:git_log = subprocess.check_output(['git', 'log', '--oneline', '-3']).decode().strip()
except:git_log = 'No git repository'summary_content = f'''# Session Summary**ID:** {session_id}
**Time:** {timestamp}**Git Log:**
```
{git_log}
```
'''with open(f'{summary_dir}/session-{session_id}.md', 'w') as f:f.write(summary_content)
8. UserPromptSubmit Hook
触发时机:用户提交提示词和iFlow处理之前
用途:在用户提交提示词前进行内容过滤,检测并阻止包含敏感信息的输入
示例配置:
可使用 matcher 匹配特定内容,例如:“matcher”: “.sensitive.”
{"hooks": {"UserPromptSubmit": [{"hooks": [{"type": "command","command": "python3 ~/.iflow/hooks/content_filter.py","timeout": 10}]}]}
}
content_filter.py 脚本代码:(保存路径为 .iflow/hooks/content_filter.py)
#!/usr/bin/env python3
import json
import sys
import re
import datetime# Load input from stdin
try:input_data = json.load(sys.stdin)
except json.JSONDecodeError as e:print(f"Error: Invalid JSON input: {e}", file=sys.stderr)sys.exit(1)prompt = input_data.get("prompt", "")# Check for sensitive patterns
sensitive_patterns = [(r"(?i)\b(password|secret|key|token)\s*[:=]", "Prompt contains potential secrets"),
]for pattern, message in sensitive_patterns:if re.search(pattern, prompt):# Use JSON output to block with a specific reasonoutput = {"decision": "block","reason": f"Security policy violation: {message}. Please rephrase your request without sensitive information."}print(json.dumps(output))sys.exit(0)# Add current time to context
context = f"Current time: {datetime.datetime.now()}"
print(context)"""
The following is also equivalent:
print(json.dumps({"hookSpecificOutput": {"hookEventName": "UserPromptSubmit","additionalContext": context,},
}))
"""# Allow the prompt to proceed with the additional context
sys.exit(0)
实战案例
案例 1: 敏感文件保护 Hook
阻止 iFlow CLI 编辑敏感文件,防止意外修改关键配置。
Hook 配置:
{"hooks": {"PreToolUse": [{"matcher": "Edit|Write|Shell","hooks": [{"type": "command","command": "python3 .iflow/hooks/security_check.py"}]}]}
}
Python 脚本实现:
创建 .iflow/hooks/security_check.py 文件:
#!/usr/bin/env python3
"""
敏感文件保护 Hook
阻止对敏感文件和目录的编辑、写入和删除操作
"""
import json
import sys# 从 stdin 读取 Hook 输入数据
try:data = json.load(sys.stdin)
except json.JSONDecodeError as e:print(f"Error: Invalid JSON input: {e}", file=sys.stderr)sys.exit(1)# 获取工具输入参数
tool_input = data.get('tool_input', {})# 获取文件路径或命令内容
path = tool_input.get('file_path', '') or tool_input.get('command', '')# 定义敏感文件列表
sensitive_files = ['.env','package-lock.json','.git/'
]# 检查路径中是否包含敏感文件
matched = [p for p in sensitive_files if p in path]if matched:# 构建详细的错误消息error_message = f"""⚠️ 安全保护:禁止操作敏感文件/目录检测到操作涉及敏感内容: {", ".join(matched)}敏感文件列表包括:
- .env (环境变量配置,可能包含密钥)
- package-lock.json (依赖锁定文件,不应手动编辑)
- .git/ (Git 内部文件,不应直接修改)如需操作这些文件,请手动执行或联系管理员。"""print(error_message, file=sys.stderr)# 退出码 2 表示阻止操作并将 stderr 显示给 LLMsys.exit(2)# 退出码 0 表示允许操作
sys.exit(0)
脚本权限设置:
chmod +x .iflow/hooks/security_check.py
工作原理:
-
使用
PreToolUsehook 在文件编辑前检查 -
通过
matcher: "Edit|Write"匹配编辑和写入操作 -
使用 Python 一行脚本检查文件路径
-
如果路径包含敏感文件(
.env、package-lock.json、.git/),返回退出码 2 阻止操作 -
退出码 0 表示允许操作继续
保护文件列表:
-
.env- 环境变量配置文件(可能包含密钥) -
package-lock.json- 依赖锁定文件(不应手动编辑) -
.git/- Git 内部文件(不应直接修改)
案例 2: TypeScript 代码自动格式化
在编辑 TypeScript 文件后自动进行代码格式化,确保代码风格一致。
Hook 配置:
{"hooks": {"PostToolUse": [{"matcher": "Edit|Write","hooks": [{"type": "command","command": "jq -r '.tool_input.file_path' | { read file_path; if echo \"$file_path\" | grep -q '\\.ts$'; then npx prettier --write \"$file_path\"; fi; }"}]}]}
}
工作原理:
-
使用
PostToolUsehook 在文件编辑后触发 -
通过
matcher: "Edit|Write"匹配编辑和写入操作 -
使用
jq提取文件路径 -
检查文件扩展名是否为
.ts -
如果是 TypeScript 文件,自动运行
prettier --write格式化
案例 3: Markdown 文件自动格式化
自动修复 Markdown 文件中缺失的代码块语言标签和格式问题,提升文档可读性。
Hook 配置:
{"hooks": {"PostToolUse": [{"matcher": "Edit|Write","hooks": [{"type": "command","command": "python3 .iflow/hooks/markdown_formatter.py"}]}]}}
Python 脚本实现:
创建 .iflow/hooks/markdown_formatter.py 文件:
#!/usr/bin/env python3
"""
Markdown formatter for iFlow CLI output.
Fixes missing language tags and spacing issues while preserving code content.
"""
import json
import sys
import re
import osdef detect_language(code):"""Best-effort language detection from code content."""s = code.strip()# JSON detectionif re.search(r'^\s*[{\[]', s):try:json.loads(s)return 'json'except:pass# Python detectionif re.search(r'^\s*def\s+\w+\s*\(', s, re.M) or \re.search(r'^\s*(import|from)\s+\w+', s, re.M) or \re.search(r'^\s*class\s+\w+', s, re.M):return 'python'# JavaScript/TypeScript detection if re.search(r'\b(function\s+\w+\s*\(|const\s+\w+\s*=|let\s+\w+\s*=|var\s+\w+\s*=)', s) or \re.search(r'=>|console\.(log|error)|import\s+.*from', s):return 'javascript'# Bash detectionif re.search(r'^#!.*\b(bash|sh)\b', s, re.M) or \re.search(r'\b(echo|grep|sed|awk)\b', s) or \re.search(r'^\s*\$\s+', s, re.M):return 'bash'# SQL detectionif re.search(r'\b(SELECT|INSERT|UPDATE|DELETE|CREATE|DROP|ALTER)\s+', s, re.I):return 'sql'# YAML detectionif re.search(r'^\s*\w+:\s*$', s, re.M) and re.search(r'^\s+-\s+', s, re.M):return 'yaml'return 'text'def format_markdown(content):"""Format markdown content with language detection."""# Fix unlabeled code fencesdef add_lang_to_fence(match):indent, info, body, closing = match.groups()if not info.strip():lang = detect_language(body)return f"{indent}```{lang}\n{body}{closing}\n"return match.group(0)fence_pattern = r'(?ms)^([ \t]{0,3})```([^\n]*)\n(.*?)(\n\1```)\s*$'content = re.sub(fence_pattern, add_lang_to_fence, content)# Fix excessive blank linescontent = re.sub(r'\n{3,}', '\n\n', content)return content.rstrip() + '\n'def get_file_path(input_data):"""Extract file path from various possible input structures."""if not input_data:return None# Try tool_input first (most common)tool_input = input_data.get('tool_input', {})if isinstance(tool_input, dict):for key in ['file_path', 'target_file', 'path', 'filepath']:file_path = tool_input.get(key)if file_path and isinstance(file_path, str):return file_path# Try direct keysfor key in ['file_path', 'target_file', 'path', 'filepath']:file_path = input_data.get(key)if file_path and isinstance(file_path, str):return file_path# Try tool_resulttool_result = input_data.get('tool_result', {})if isinstance(tool_result, dict):for key in ['file_path', 'target_file', 'path', 'filepath']:file_path = tool_result.get(key)if file_path and isinstance(file_path, str):return file_pathreturn None# Main execution
try:if sys.stdin.isatty():sys.exit(0)input_data = json.load(sys.stdin)file_path = get_file_path(input_data)if not file_path:sys.exit(0)if not file_path.endswith(('.md', '.mdx')):sys.exit(0)if not os.path.exists(file_path):sys.exit(0)with open(file_path, 'r', encoding='utf-8') as f:content = f.read()formatted = format_markdown(content)if formatted != content:with open(file_path, 'w', encoding='utf-8') as f:f.write(formatted)except (json.JSONDecodeError, Exception):sys.exit(0)
脚本权限设置:
chmod +x .iflow/hooks/markdown_formatter.py
功能特性:
-
自动检测未标记代码块的编程语言
-
添加适当的语言标签以实现语法高亮
-
修复过多的空白行,同时保留代码内容
-
仅处理 Markdown 文件(
.md、.mdx)
最佳实践总结
-
明确 Hook 用途: 每个 hook 应该专注于单一职责,便于维护和调试
-
安全第一: 始终审查 hook 脚本,避免执行不受信任的代码
-
性能考虑: PostToolUse hooks 会在每次工具调用后执行,注意脚本执行时间
-
错误处理: Hook 脚本应包含适当的错误处理,避免中断工作流
-
测试验证: 在生产环境使用前,先在测试项目中验证 hook 行为
-
文档记录: 为团队共享的 hooks 编写清晰的文档说明
