Cursor AI Agent任务完成通知工具:提升开发效率的智能提醒方案
1. 项目概述与核心价值
如果你和我一样,每天大部分时间都泡在 Cursor 编辑器里,让 AI Agent 帮你写代码、重构项目或者生成文档,那你肯定遇到过这个场景:你给 Agent 下达了一个复杂的指令,然后切到浏览器或者另一个窗口去处理别的事情,时不时地回头看一眼进度条。有时候,Agent 已经悄无声息地完成了任务,而你却浑然不知,白白浪费了等待的时间。或者更糟的是,你同时在多个项目里开了多个 Agent,它们陆续完成工作,你却分不清哪个是哪个,还得一个个点开确认。这种体验上的小割裂,累积起来其实挺影响效率的。
cursorfinishsound这个项目,就是为了解决这个“最后一公里”的体验问题而生的。它的核心功能非常简单直接:当 Cursor 的 AI Agent 完成你交给它的最后一项任务时,自动触发一个通知。这个通知可以是两种形式:一声轻柔的提示音,或者一个用语音合成技术(TTS)念出当前项目文件夹名称的“机器人播报”。想象一下,你正在写邮件,耳边突然传来一个声音清晰地念出“backend-api”,你就立刻知道是那个处理用户认证的 Agent 已经搞定了,无需任何视线切换。对于需要同时管理多个并行开发任务,或者在“深度工作”与“等待 Agent”状态间频繁切换的开发者来说,这个小工具带来的心流保护和工作上下文的无缝衔接,价值远超其代码量。
这个项目本质上是一个轻量级的 Python 包,它巧妙地利用了 Cursor 编辑器的“Rules for AI”功能,将自定义的 Python 脚本注入到 Agent 的工作流末尾。其技术栈非常精简:核心是 Python 的标准库subprocess和os,用于执行系统命令和获取文件路径;语音播报功能依赖于经典的、跨平台的开源语音合成引擎espeak;而提示音则使用了 Python 的winsound(Windows)或playsound库。整个设计遵循了 Unix 哲学——“做一件事,并把它做好”,没有复杂的依赖和配置,开箱即用。
2. 环境准备与深度安装指南
安装cursorfinishsound的过程本身不复杂,但为了确保它在你的工作流中稳定可靠地运行,我们需要对几个关键环节进行深入配置。很多“能用但不好用”的问题,都源于安装时的小疏忽。
2.1 系统级依赖的精准安装
项目依赖espeak进行文本转语音。在 Ubuntu/Debian 及其衍生系统上,安装命令很简单:
sudo apt-get update && sudo apt-get install espeak -y注意:这里我强烈建议在执行安装前先运行
sudo apt-get update。这能确保你的包管理器索引是最新的,避免因仓库信息过期而安装到旧版本或不兼容的版本。espeak虽然稳定,但不同版本在语音清晰度和参数支持上可能有细微差别。
对于 macOS 用户,可以通过 Homebrew 安装:
brew install espeak而对于 Windows 用户,情况稍微特殊一些。espeak有 Windows 版本,但通常不推荐通过包管理器安装。更稳妥的做法是直接访问 espeak 项目页面 下载预编译的二进制文件,并将其所在目录(例如C:\Program Files\eSpeak\command_line\)添加到系统的PATH环境变量中。这样,Python 的subprocess模块才能在任何位置调用到espeak命令。添加PATH后,务必重启你的命令行终端或 Cursor 编辑器,以使环境变量生效。
2.2 Python 环境与包依赖管理
项目要求 Python 3.x。首先,用以下命令确认你的 Python 版本:
python3 --version # 或 python --version接下来是安装 Python 包依赖。项目根目录下的requirements.txt文件通常内容很简单,可能只包含playsound等库。但这里有一个非常重要的最佳实践:使用虚拟环境。
我强烈建议你不要在全局 Python 环境中安装这些依赖。因为不同项目可能需要不同版本的库,全局安装容易引发冲突。为cursorfinishsound单独创建一个虚拟环境是更清洁、更安全的选择。
# 1. 进入你计划放置 cursorfinishsound 的目录,例如你的工具目录 cd ~/my_dev_tools # 2. 克隆仓库 git clone https://github.com/eliaspfeffer/cursorfinishsound.git cd cursorfinishsound # 3. 创建虚拟环境(以 venv 模块为例) python3 -m venv venv # 4. 激活虚拟环境 # Linux/macOS source venv/bin/activate # Windows venv\Scripts\activate # 5. 在激活的虚拟环境中安装依赖 pip install -r requirements.txt激活虚拟环境后,你的命令行提示符通常会发生变化(前面会显示(venv)),这表明你正处在一个隔离的 Python 环境中。关键一步来了:你需要确保 Cursor 的 AI Agent 也在同一个虚拟环境中运行你的脚本。否则,Agent 会使用系统默认的 Python 环境,从而找不到已安装的playsound等包,导致脚本执行失败。
如何做到这一点?这取决于你如何调用 Agent。一个可靠的方法是,在 Cursor 的“Rules for AI”中,使用虚拟环境 Python 解释器的绝对路径来执行脚本。例如,你的规则可以这样写:
Always play /absolute/path/to/cursorfinishsound/venv/bin/python /absolute/path/to/cursorfinishsound/soundname.py when you have finished the last job, before you stop generating.注意,这里我们不再直接执行.py文件,而是通过指定虚拟环境中的python解释器来执行它。这是保证依赖一致性的最稳妥方法。
2.3 Cursor 规则配置的细节与原理
将规则添加到 Cursor 是集成的最后一步,也是最容易出错的一步。打开 Cursor,点击 Agent 窗口右上角的齿轮图标进入设置,找到 “Rules for AI” 部分。
你需要添加的规则文本是:
Always play /absolute/path/to/your/cursorfinishsound/soundname.py when you have finished the last job, before you stop generating.这里有三个必须注意的细节:
绝对路径:你必须将
/absolute/path/to/your/cursorfinishsound/替换成你电脑上cursorfinishsound文件夹的真实、完整路径。相对路径在 Cursor Agent 的执行上下文中很可能无法正确解析。在 Linux/macOS 上,你可以在终端中进入该文件夹,然后输入pwd命令获取绝对路径。在 Windows 上,你可以在文件资源管理器的地址栏复制路径(可能需要稍作修改,如将\替换为/或\\)。规则语义:这条规则的意思是“总是(Always)在完成最后一项工作后、停止生成前,播放(play)指定的脚本”。
play这个动词在这里是 Cursor 规则语法的一部分,它意味着“执行这个外部命令或脚本”。这条规则会被注入到 Agent 每次任务循环的末尾。脚本选择:你可以选择
soundname.py(播报文件夹名)或sound.py(仅播放提示音)。根据你的喜好和办公环境决定。在开放办公区,突然响起的机器人语音可能会打扰同事,此时提示音是更礼貌的选择。
配置完成后,保存设置。下次你启动一个新的 AI Agent 并让它执行任务时,这条规则就会生效。你可以先让 Agent 执行一个简单的任务(比如“写一个打印 hello world 的函数”)来测试通知是否正常工作。
3. 核心模块解析与定制化改造
理解了安装和配置,我们深入到代码层面,看看这两个核心脚本sound.py和soundname.py是如何工作的,以及如何根据你的个人喜好进行定制。
3.1sound.py:跨平台的轻柔提示音
sound.py的核心任务是播放一个简短、不刺耳的声音。它的实现需要处理不同操作系统的差异。
# sound.py 示例逻辑 import sys import os def play_gentle_notification(): """播放一个轻柔的提示音。""" try: if sys.platform == "win32": # Windows: 使用 winsound 播放系统提示音 import winsound winsound.MessageBeep(winsound.MB_ICONASTERISK) # 星号提示音,较为柔和 else: # Linux/macOS: 使用 playsound 播放一个本地音频文件 # 假设项目根目录下有一个 ‘notification.wav’ 文件 from playsound import playsound sound_file = os.path.join(os.path.dirname(__file__), 'notification.wav') playsound(sound_file) except Exception as e: # 静默失败,避免干扰主流程 print(f"Could not play sound: {e}", file=sys.stderr) if __name__ == "__main__": play_gentle_notification()实现要点与定制:
- Windows 路径:
winsound.MessageBeep()可以接受不同的常量参数,如MB_ICONHAND(关键停止)、MB_ICONQUESTION(疑问)。你可以替换MB_ICONASTERISK来尝试不同的系统提示音,找到最合你心意的那一个。 - 非 Windows 系统:这里默认使用
playsound库播放一个名为notification.wav的音频文件。你需要确保这个文件存在于项目根目录。你可以用任何音频编辑软件(如 Audacity)制作或下载一个简短的、令人愉悦的提示音(例如,一个轻柔的“叮”声或木琴音),时长最好在0.5秒以内,避免过长打扰。 - 错误处理:代码用
try...except包裹,并在失败时打印错误信息到标准错误流 (sys.stderr)。这是非常必要的,因为如果播放声音失败(例如找不到音频文件),我们不希望这个次要功能导致整个 Agent 结束流程崩溃。静默失败或记录日志是最佳实践。
3.2soundname.py:语音播报当前工作目录
这是项目的特色功能。soundname.py脚本会获取当前工作目录的文件夹名,并通过espeak用语音读出来。
# soundname.py 示例逻辑 import subprocess import os import sys def say_folder_name(): """使用 espeak 说出当前工作目录的父文件夹名称。""" try: # 获取当前工作目录 cwd = os.getcwd() # 提取当前目录的直系父文件夹名 parent_folder = os.path.basename(cwd) if not parent_folder: parent_folder = "Root or Unknown" # 处理根目录等情况 # 构建 espeak 命令 # -a 参数设置音量(0-200),默认是100,这里设为50更柔和 # -s 参数可以设置语速(单词每分钟),默认是160,可根据需要调整 command = ['espeak', '-a', '50', '-s', '150', parent_folder] # 执行命令 subprocess.run(command, check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) except FileNotFoundError: print("Error: 'espeak' command not found. Please ensure espeak is installed and in your PATH.", file=sys.stderr) except subprocess.CalledProcessError as e: print(f"Error running espeak: {e}", file=sys.stderr) except Exception as e: print(f"An unexpected error occurred: {e}", file=sys.stderr) if __name__ == "__main__": say_folder_name()核心机制解析:
- 获取目录:
os.getcwd()返回的是当前工作目录(Current Working Directory)。当 Cursor Agent 执行你的项目任务时,这个目录通常就是你打开的项目根目录。 - 提取文件夹名:
os.path.basename()从一个路径字符串中提取最后一部分。对于/home/user/projects/my-awesome-api,它返回my-awesome-api。 - 调用系统命令:
subprocess.run()是执行外部命令的标准方式。我们将命令和参数(如-a 50)放在一个列表里传递,这比拼接字符串更安全,可以避免 shell 注入等安全问题。check=True确保如果espeak命令执行失败(返回非零状态码),会抛出CalledProcessError异常。我们将标准输出和错误重定向到DEVNULL,是为了避免espeak的任何输出污染 Cursor Agent 的信息流。
深度定制选项:
- 音量 (
-a):-a 50表示50%的音量。如果你觉得声音太小或太大,可以调整这个值(范围0-200)。我个人的经验是,在安静的办公室,30-40 足够;如果戴着耳机,可以调到60-80。 - 语速 (
-s):-s 150表示每分钟说150个单词。默认160可能有点快,特别是对于长的项目名。你可以调到120-140让播报更清晰。-s 100会非常缓慢和清晰。 - 声音 (
-v):espeak支持多种语言和声音。你可以尝试-v en(英语)、-v de(德语)等,甚至有些版本支持-v mb-en1(男性声音)或-v f5(女性声音5号)。用命令espeak --voices可以查看所有可用声音。 - 处理复杂项目名:如果你的项目名包含下划线、连字符(如
my-awesome_api_v2),espeak的播报可能会有点奇怪。一个进阶的定制思路是,在脚本中对parent_folder字符串进行预处理,比如将下划线替换为空格:parent_folder = parent_folder.replace('_', ' ')。这样读出来就是“my awesome api v2”,听起来更自然。
4. 高级集成方案与实战心得
基础用法已经能覆盖大部分场景,但如果你希望这个工具更紧密地融入你的开发工作流,或者解决一些特定问题,可以考虑以下进阶方案。
4.1 多项目管理与上下文区分
当你同时在多个项目窗口中使用 Cursor 时,每个窗口的 Agent 都会播报自己所在的项目名。这本身就是一个完美的上下文区分。但有时,你可能在一个大项目里同时进行多个不相关的任务(比如同时开发前端组件和修复后端BUG),并且为每个任务开了独立的 Agent。由于它们都在同一个项目目录下,播报的名字是一样的,就无法区分。
解决方案:你可以修改soundname.py脚本,让它播报更具体的信息。例如,结合 Cursor 的 Agent 指令或者一个简单的环境变量。
环境变量法:在启动某个特定任务的 Agent 前,在终端里设置一个临时环境变量。
export TASK_NAME="Fix_User_Auth_BUG" # 然后在这个终端里启动 Cursor,或者确保 Cursor 能继承这个环境变量然后在
soundname.py中:import os task_name = os.environ.get('TASK_NAME') if task_name: folder_to_speak = f"{os.path.basename(os.getcwd())} - {task_name}" else: folder_to_speak = os.path.basename(os.getcwd()) # 然后用 folder_to_speak 去播报这样,播报的内容就会是
my-awesome-api - Fix_User_Auth_BUG。轻量级标记文件法:在项目子目录下创建一个特定的空文件(如
.agent_task_marker),然后在脚本中检查当前工作目录或其父目录中是否存在这个文件,并据此调整播报内容。这种方法更“持久”,但需要手动管理标记文件。
4.2 错误排查与日志记录
虽然我们做了错误处理,但有时通知不工作,你需要知道原因。一个增强健壮性的方法是引入简单的日志记录。
你可以修改脚本,将关键信息(如获取到的目录、执行的命令、发生的错误)写入一个日志文件,而不是仅仅打印到stderr(这部分输出在 Cursor 中可能看不到)。
import logging import os from datetime import datetime def setup_logging(): log_dir = os.path.expanduser("~/cursor_agent_logs") os.makedirs(log_dir, exist_ok=True) log_file = os.path.join(log_dir, f"finish_sound_{datetime.now().strftime('%Y%m%d')}.log") logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s', handlers=[ logging.FileHandler(log_file), logging.StreamHandler() # 同时输出到控制台(Cursor可能捕获到) ] ) return logging.getLogger(__name__) logger = setup_logging() def say_folder_name(): try: cwd = os.getcwd() parent_folder = os.path.basename(cwd) logger.info(f"Current working directory: {cwd}") logger.info(f"Folder to speak: {parent_folder}") # ... 其余代码 ... subprocess.run(command, check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) logger.info("espeak command executed successfully.") except Exception as e: logger.error(f"Failed to speak folder name: {e}", exc_info=True)这样,当出现问题时,你可以去~/cursor_agent_logs目录下查看日志,精准定位是路径问题、espeak命令问题还是其他运行时异常。
4.3 与系统通知中心集成(macOS/Linux)
对于追求极致体验的用户,你可能希望提示不仅仅是一段语音或声音,还能在系统通知中心显示一条横幅。这可以通过在 Python 脚本中调用系统命令来实现。
macOS:可以使用
osascript命令发送 AppleScript 指令来显示通知。def send_macos_notification(title, message): script = f'display notification "{message}" with title "{title}"' subprocess.run(['osascript', '-e', script])你可以在
play_gentle_notification或say_folder_name函数成功执行后,调用这个函数,发送一条如“Agent 任务完成于 [项目名]”的通知。Linux (使用
notify-send):许多 Linux 桌面环境(如 GNOME, KDE)都支持notify-send命令。def send_linux_notification(title, message): subprocess.run(['notify-send', title, message])
将系统通知与声音/语音结合,提供了视觉和听觉的双重保障,确保你不会错过任何完成信号。
5. 常见问题与排查技巧实录
在实际部署和使用cursorfinishsound的过程中,我遇到并总结了一些典型问题。这里列出一个速查表,帮助你快速定位和解决。
| 问题现象 | 可能原因 | 排查与解决方案 |
|---|---|---|
| 完全没有声音或语音 | 1. Cursor 规则路径错误。 2. Python 脚本执行权限问题(Linux/macOS)。 3. 虚拟环境未激活或路径不对。 | 1.检查绝对路径:在终端中手动执行一次规则中的完整命令,看是否能成功播放声音。例如:/path/to/venv/bin/python /path/to/cursorfinishsound/sound.py。2.检查权限:确保 .py文件有可执行权限 (chmod +x sound.py),或者通过 Python 解释器执行。3.确认虚拟环境:确保规则中指向的 Python 解释器路径是你的虚拟环境中的 python。 |
| 提示“ModuleNotFoundError: No module named ‘playsound’” | Python 依赖包未在 Agent 执行脚本的环境中安装。 | 1.激活虚拟环境:在终端中,进入项目目录,激活虚拟环境 (source venv/bin/activate),然后运行pip list确认playsound已安装。2.修正规则路径:确保 Cursor 规则中使用的 Python 是虚拟环境中的那个(绝对路径)。 3.全局安装(不推荐):作为临时测试,可以 pip install playsound进行全局安装。 |
| 语音播报不清晰、语速过快或无声 | 1.espeak未安装或不在PATH中。2. espeak命令参数(音量、语速)设置不当。3. 系统音量静音或过低。 | 1.验证 espeak:在终端直接运行espeak "hello world",看是否有语音输出。如果没有,重新安装或将其目录加入PATH。2.调整参数:修改 soundname.py中的-a(音量)和-s(语速)参数值,找到适合你听觉的设置。3.检查系统音频:确保电脑扬声器或耳机正常工作,且音量合适。 |
| 播报的文件夹名不是项目名 | 当前工作目录 (os.getcwd()) 不是项目根目录。Agent 可能在执行过程中切换了子目录。 | 这是一个已知的 Cursor Agent 行为。Agent 有时会在项目子目录中执行操作。你可以尝试修改脚本,向上追溯多层目录,或者使用一个更稳定的方式获取项目根目录,例如查找.git文件夹的所在目录(如果项目是 Git 仓库)。python<br>import os<br>def get_project_root(start_path='.'):<br> current_path = os.path.abspath(start_path)<br> while True:<br> if os.path.isdir(os.path.join(current_path, '.git')):<br> return current_path<br> parent_path = os.path.dirname(current_path)<br> if parent_path == current_path: # 到达根目录<br> break<br> current_path = parent_path<br> return os.path.abspath(start_path) # 回退到起始目录<br>project_root = get_project_root(os.getcwd())<br>folder_name = os.path.basename(project_root)<br> |
| 规则添加后,Agent 完成任务时无任何反应 | 1. Cursor 规则未保存或未生效。 2. 规则语法可能有误。 3. Agent 可能被配置为忽略外部命令。 | 1.重启 Cursor:有时更改规则后需要重启 Cursor 才能完全生效。 2.检查规则格式:确保规则是纯文本,没有多余的空格或换行符。可以尝试先写一条简单的测试规则,如执行 echo "done"。3.检查 Agent 设置:确认没有其他全局或项目级的规则覆盖或禁用了这条规则。 |
| Windows 下提示音太尖锐或不喜欢 | winsound.MessageBeep()使用的默认提示音类型不合适。 | 尝试更换winsound.MessageBeep()的参数。例如:winsound.MessageBeep(winsound.MB_ICONEXCLAMATION)(感叹号)winsound.MessageBeep(winsound.MB_ICONQUESTION)(问号)或者,可以统一使用 playsound库播放自定义的.wav文件,这样跨平台体验更一致。 |
我个人最常遇到的坑是“路径问题”和“虚拟环境问题”。我的经验是:永远使用绝对路径,并明确指出使用虚拟环境中的 Python 解释器。在配置完成后,务必通过“手动在终端执行规则中的完整命令”来进行验收测试。如果手动执行成功,但在 Cursor 中失败,那问题大概率出在 Cursor 规则本身的语法或上下文环境上,这时就需要仔细检查规则文本和 Cursor 的日志(如果有的话)。
这个项目虽然小巧,但它精准地解决了一个高频痛点。将它配置妥当后,你几乎会忘记它的存在,直到每一次任务完成时,那一声及时的提醒让你无需再分心等待。这种流畅感,正是高效开发工具应该提供的。
