mcpField
E
extract-my-action-items

extract-my-action-items

github.com/casper-studios生产力15291 次安装8 周前更新
代码仓库

关于 extract-my-action-items

从 Fireflies 会议转录中提取行动项,可覆盖所有参会者或指定个人。技能通过并行子 Agent 处理转录片段,再汇总笔记并准备 Linear 提案和行动项文件。

主要功能

  1. 01提取所有参会者或个人的行动项
  2. 02按日期、关键词或 ID 搜索转录
  3. 03使用子 Agent 处理转录片段
  4. 04汇总带引文的分类笔记
  5. 05准备 Linear 工单提案
  6. 06生成清单并发送 Slack 私信

适用场景

  1. 01从会议转录中识别负责人和承诺
  2. 02记录行动项背后的讨论与决策
  3. 03准备待审核的 Linear 工单
  4. 04汇总并分发各参会者的行动项

MCP 相关

该技能指示子 Agent 使用 Fireflies MCP 工具查找并获取转录、摘要和转录文本。它还指定使用 Linear MCP 工具查询和处理工单。

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/casper-studios/casper-marketplace --skill extract-my-action-items

安装后在 Claude Code 中输入/extract-my-action-items确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
config.json
ticket-template.md
scripts/
fetch-slack-users.mjs
slack-post.mjs

详细介绍

提取行动项

使用并行子 Agent 从 Fireflies 转录内容中提取行动项。捕捉自动摘要容易遗漏的事项。

两种模式:

  • 所有参会者(默认): 未指定目标对象时,为每位参与者提取行动项
  • 单人: 指定目标对象时,仅为该人员提取行动项

第 1 阶段:确定模式

解析用户的调用:

  • 如果指定了目标人员 → 单人模式
  • 否则 → 所有参会者模式

从调用中提取搜索条件(日期、关键词或转录 ID)。

第 2 阶段:获取与预处理(子 Agent)

转录 API 返回 JSON 数组(或包含 JSON 数组的 MCP 包装对象)。在分块前先提取为纯文本。

请检查用户的本地 hooks 配置,避免运行被 hooks 拦截的命令。

基于 MCP 的提取
mkdir -p .claude/scratchpad
node -e "
  const fs = require('fs');
  let data = JSON.parse(fs.readFileSync(process.argv[1], 'utf8'));
  // Handle MCP wrapper: if top-level array has a .text field containing the real transcript, parse that
  if (data.length === 1 && typeof data[0]?.text === 'string') {
    // Extract speaker lines from the text content
    const lines = data[0].text.split('\n').filter(l => l.match(/^[A-Za-z].*?:/));
    fs.writeFileSync('.claude/scratchpad/transcript.txt', lines.join('\n'));
    const speakers = [...new Set(lines.map(l => l.split(':')[0].trim()))].sort();
    console.log('Speakers:', JSON.stringify(speakers));
    console.log('Total lines:', lines.length);
  } else {
    // Standard array of {speaker_name, text} objects
    const lines = data.map(e => (e.speaker_name || 'Unknown') + ': ' + (e.text || ''));
    fs.writeFileSync('.claude/scratchpad/transcript.txt', lines.join('\n'));
    const speakers = [...new Set(data.map(e => e.speaker_name).filter(Boolean))].sort();
    console.log('Speakers:', JSON.stringify(speakers));
    console.log('Total lines:', lines.length);
  }
" [TRANSCRIPT_JSON_FILE]

如果 MCP 客户端将转录 JSON 保存到了工具结果文件中,请将该文件路径作为参数传入。

基于 API 的提取

关键:编排器不得直接调用任何 Fireflies MCP 工具。所有 Fireflies 交互都必须在此子 Agent 内完成。

使用以下提示词启动一个 general-purpose 子 Agent:

Search Fireflies for a transcript matching: [SEARCH_CRITERIA]

1. Call `mcp__fireflies__fireflies_get_transcripts` to find the transcript (by date, keyword, or ID).
2. Call `mcp__fireflies__fireflies_get_summary` and `mcp__fireflies__fireflies_get_transcript` in parallel for the matched transcript.
3. The transcript API returns a JSON array. Extract to plain text:
   - With jq: jq -r '.[].text' < raw_transcript.json > .claude/scratchpad/transcript.txt
   - Fallback: python3 -c "import json,sys; print('\n'.join(e['text'] for e in json.load(sys.stdin)))" < raw_transcript.json > .claude/scratchpad/transcript.txt
4. Count lines: wc -l < .claude/scratchpad/transcript.txt
5. Extract the distinct speaker list from the transcript JSON:
   python3 -c "import json,sys; data=json.load(sys.stdin); print('\n'.join(sorted(set(e.get('speaker_name','') for e in data if e.get('speaker_name')))))" < raw_transcript.json

Return EXACTLY this (no other text):
- meeting_title: <title>
- meeting_date: <date>
- transcript_id: <id>
- transcript_path: .claude/scratchpad/transcript.txt
- line_count: <number>
- speakers: <comma-separated list>
- summary: <the Fireflies summary text>

等待子 Agent 完成。解析其返回值——这些值将作为后续阶段的输入。

第 3 阶段:并行提取

分块大小: 每块 ceil(total_lines / 5) 行,最少 200 行。调整分块数量,确保每块不少于 200 行。

每个分块启动一个 general-purpose 子 Agent。

单人模式提示词
Read lines [START] to [END] of [FILE_PATH].

Find ALL action items for [TARGET_PERSON]. Return each as:
- **Item**: what they committed to
- **Quote**: exact words from transcript
- **Context**: who else involved, any deadline
- **Discussion depth**: If this item emerged from extended back-and-forth (design decisions, technical debates, multi-speaker deliberation), include: what was proposed, what alternatives were considered, what was decided and WHY, specific technical details (field names, schema choices, API behaviors), open questions or deferred items, and connections to other people's work

Beyond obvious commitments ("I'll do X"), catch these non-obvious patterns:
- Self-notes: "I'll make a note to...", "let me jot down..."
- Admissions implying catch-up: "I dropped the ball on X", "I still haven't read X"
- Conditional offers that became commitments: "If we have time, I'm happy to..."
- Volunteering: "I guess I'll volunteer to..."
- Exploration tasks: "Let me spend a few hours with it"
- Questions/topics for external parties: "I need to ask [person/firm] about X", "thing to discuss with [party]"
所有参会者模式提示词
Read lines [START] to [END] of [FILE_PATH].

The meeting attendees are: [SPEAKER_LIST]

Find ALL action items for EVERY attendee. Group by person. For each item return:
- **Person**: who owns the action item
- **Item**: what they committed to
- **Quote**: exact words from transcript
- **Context**: who else involved, any deadline
- **Discussion depth**: If this item emerged from extended back-and-forth (design decisions, technical debates, multi-speaker deliberation), include: what was proposed, what alternatives were considered, what was decided and WHY, specific technical details (field names, schema choices, API behaviors), open questions or deferred items, and connections to other people's work

Beyond obvious commitments ("I'll do X"), catch these non-obvious patterns:
- Self-notes: "I'll make a note to...", "let me jot down..."
- Admissions implying catch-up: "I dropped the ball on X", "I still haven't read X"
- Conditional offers that became commitments: "If we have time, I'm happy to..."
- Volunteering: "I guess I'll volunteer to..."
- Exploration tasks: "Let me spend a few hours with it"
- Questions/topics for external parties: "I need to ask [person/firm] about X", "thing to discuss with [party]"
- Delegations: "[Person], can you handle X?", "I'll leave that to [person]"

第 4 阶段:综合整理笔记

合并子 Agent 的结果、去重,并归类到一份内容丰富的综合笔记文件中。这是主工作文档——所有细节都保存在这里。Linear 提案和最终行动项清单均由此生成。

写入 .claude/scratchpad/synthesized-notes-YYYY-MM-DD.md。仅包含有事项的类别。

综合整理深度

保留子 Agent 返回的完整 Discussion depth。切勿将讨论内容丰富的事项简化成一行。

  • 复选框标题 = 要交付的成果。正文 = 执行该事项所需的完整背景。
  • 如果子 Agent 为某个事项返回了多段背景信息,请保留这些内容。使用加粗小标题进行组织(例如:“根本原因:”“已达成一致的方案:”“未解决事项:”)。
  • 切勿将 N 个不同的决定合并为 1 个项目。应逐一列出。
  • 交叉链接相互依赖的事项(例如:“Emerson 财务期间表工作的依赖项”)。
  • 简单事项(凭据共享、快速调查)保持为一行。
  • 为每个事项附上转录内容中的原文引述。
类别
  1. 高优先级 / 技术 — 代码变更、错误修复、PR 审查、调查
  2. 结对 / 协作 — 已安排的同步会议、联合工作会
  3. 内容 / 研究 — 阅读、写作、实验、文档
  4. 向外部人员提出的问题 — 需要向团队以外的特定人员/公司提出的事项
  5. 探索 / 工具 — 工具评估、设置、环境任务
  6. 补做 — 明确承认已搁置或遗漏的事项
输出格式

单人模式:

# [Name] Synthesized Notes — [Meeting Title]

**Date:** [Date]
**Fireflies Link:** https://app.fireflies.ai/view/[TRANSCRIPT_ID]

## [Category Name]

- **Item title**
  - Context, decisions, and full detail
  - > "Exact quote"

所有参会者模式:

# Synthesized Notes — [Meeting Title]

**Date:** [Date]
**Fireflies Link:** https://app.fireflies.ai/view/[TRANSCRIPT_ID]

## [Person Name]

### [Category Name]

- **Item title**
  - Context, decisions, and full detail
  - > "Exact quote"

第 5 阶段:Linear 工单提案

根据综合笔记生成 Linear 工单的创建和更新提案。将第 4 阶段中丰富的背景信息和引述写入 Linear(作为评论或工单描述),使其成为事实来源。使用配置文件设置团队默认值,并查询当前周期中的活跃工单,以寻找可更新的候选项。

5a:配置解析

按以下顺序查找团队配置(以第一个匹配项为准):

  1. ~/.agents/configs/extract-my-action-items/config.json(用户覆盖配置)
  2. references/config.json(内置默认配置,相对于此技能文件)

如果找到用户配置,则使用它。否则,回退到内置的 config.json。

如果不存在用户配置,且内置配置的 team 字段为空,请停止并提示用户:

未找到 Linear 配置。请在以下路径创建用户配置:~/.agents/configs/extract-my-action-items/config.json

将内置的 references/config.json 复制为起始配置,并填写你的团队、项目、受派人和标签。

如果配置解析成功,则继续。

5b–5c:拉取活动工单并进行语义匹配(单个子 Agent)

重要:在同一个 general-purpose 子 Agent 中一并运行 5b 和 5c。 周期工单数据量很大,不应经过主上下文窗口。

使用以下提示词启动子 Agent:

## Task: Pull active Linear tickets and match against synthesized meeting notes

### Step 1: Pull active tickets

Config: team=[TEAM], states=[STATES_LIST], attendees=[SPEAKER_LIST]

1. `mcp__linear__list_teams` with query=[TEAM] → get team ID
2. `mcp__linear__list_cycles` with type="current" → get current cycle ID
3. In parallel:
   - `mcp__linear__list_issues` filtered by cycle + team (limit 250)
   - `mcp__linear__list_issues` for each attendee (assignee filter, state="In Progress")
4. Deduplicate and build a lookup table: {identifier, title, assignee, status}

### Step 2: Semantic matching

Read the synthesized notes at [SYNTHESIZED_NOTES_PATH].

For each item, classify as:
- **UPDATE [TICKET-ID]** — maps to an existing ticket. Explain what new info to append.
- **NEW TICKET** — distinct deliverable not covered. Suggest title, assignee, priority.
- **IDEA** — process improvement, behavioral commitment, or exploratory thought.

Group output by classification. For UPDATE items include ticket ID. For NEW TICKET items include suggested title, assignee, and priority.
5d:将提案草拟到暂存文件

使用 references/ticket-template.md 中的模板,写入 .claude/scratchpad/linear-proposals-YYYY-MM-DD.md。

  • 拟议更新: 对于每个 UPDATE 匹配项,使用新增反馈草拟评论正文(包含带日期的部分,其中包含综合笔记中的上下文和引文)。不要修改工单描述——更新应以评论形式发布。
  • 拟议新工单: 使用 send-to-linear 描述格式(用户故事、要求、验收标准),并提供综合笔记中的具体示例和原文引述。
  • 想法 / 需要进一步思考: 列出相关人员、上下文和原文引述。这些内容不会被跳过——它们会出现在提案文件中,但不会创建为完整工单。
5e:用户审核关卡

停止。 告知用户提案文件已就绪,路径为 .claude/scratchpad/linear-proposals-YYYY-MM-DD.md,并等待用户明确指示。

使用 AskUserQuestion:“Linear 工单提案已就绪。请查看文件,然后选择:”

  • “在 Linear 中创建/更新工单”——继续执行
  • “跳过——只处理 Slack 私信”——跳转到第 7 阶段

用户可以在批准前编辑暂存文件。批准后:

  1. 通过 Linear MCP 解析团队 ID、标签 ID、项目 ID 和当前周期(与 send-to-linear 第 6 阶段采用相同模式):
    • mcp__linear__list_teams → 团队 ID
    • mcp__linear__list_issue_labels → 标签 ID
    • mcp__linear__list_projects → 项目 ID(如果已配置)
    • 使用 type: "current" 调用 mcp__linear__list_cycles → 当前周期
  2. 对于更新: 使用 issueId 和草拟的评论正文调用 mcp__linear__create_comment。不要使用 mcp__linear__save_issue 修改描述。
  3. 对于新工单: 使用配置和提案中的所有字段(团队、项目、受派人、周期、状态、标签、标题、描述)调用 mcp__linear__save_issue
  4. 想法——不执行 Linear 操作(仅保留在提案文件中供参考)
  5. 报告结果并附上可点击链接,供用户核实:
    • 已更新工单: 为每个已评论工单提供 https://linear.app/[WORKSPACE]/issue/[TICKET-ID]
    • 已创建工单: 为每个新工单提供 https://linear.app/[WORKSPACE]/issue/[TICKET-ID](使用 save_issue 返回的标识符)
    • 从团队的组织键或配置(如果有)中获取 [WORKSPACE]

第 6 阶段:行动项清单

根据综合笔记生成一份简洁的行动项清单。Linear 是详细信息的唯一来源——清单仅作为便于浏览的索引,并附上链接。

如果某个条目对应 Linear 工单(在第 5 阶段更新或创建),请在条目中附上 Linear 链接。未发送到 Linear 的条目仅需提供一行描述。

输出

单人模式——写入 .claude/scratchpad/[name]-action-items-YYYY-MM-DD.md:

# [Name] Action Items — [Meeting Title]

**Date:** [Date]
**Fireflies Link:** https://app.fireflies.ai/view/[TRANSCRIPT_ID]

## [Category Name]

- [ ] **Item title** — [TICKET-ID](https://linear.app/[WORKSPACE]/issue/[TICKET-ID])
- [ ] **Item without ticket** — brief context

所有参会者模式——写入 .claude/scratchpad/action-items-YYYY-MM-DD.md:

# Action Items — [Meeting Title]

**Date:** [Date]
**Fireflies Link:** https://app.fireflies.ai/view/[TRANSCRIPT_ID]

## [Person Name]

### [Category Name]

- [ ] **Item title** — [TICKET-ID](https://linear.app/[WORKSPACE]/issue/[TICKET-ID])
- [ ] **Item without ticket** — brief context

## Quick Reference — Time-Sensitive

1. [Person] — [Item with deadline]

每个条目保持一行。

阶段 7:审核并通过 Slack 发送私信

  1. 使用 AskUserQuestion:“要通过 Slack 向每个人发送行动项私信吗?” — 选项:“发送私信”、“跳过 — 仅保留文件”
  2. 如果获得批准,请确保项目根目录中存在 .claude/slack-users.local.json:
    • 如果不存在,运行 node [SKILL_DIR]/scripts/fetch-slack-users.mjs(需要具有 users:read 权限范围的 SLACK_BOT_TOKEN),将输出展示给用户审核,然后保存到 .claude/slack-users.local.json(由 **/.claude/**/*.local.json 忽略)
    • 如果已存在,直接继续
  3. 使用输出文件路径运行捆绑脚本:
node [SKILL_DIR]/scripts/slack-post.mjs [OUTPUT_FILE_PATH]

该脚本通过 conversations.open + chat.postMessage,向每个人发送采用 Block Kit 格式的私信。需要环境变量 SLACK_BOT_TOKEN(具有 chat:write 和 im:write 权限范围)。

不同模式下的行为:

  • 所有参会者: 在 slack-users.local.json 中匹配到的每个人都会收到仅包含其行动项的私信。无法解析的姓名会跳过,并显示警告。
  • 单人: 向目标人员发送一条私信。

姓名解析支持精确匹配和模糊的名字匹配(例如,“Jelvin”会匹配到“Jelvin Base”)。脚本运行后,向用户报告所有被跳过的姓名。

  1. 发送(或跳过)后,删除本次运行期间创建的所有产物:transcript.txt、synthesized-notes-YYYY-MM-DD.md、行动项 Markdown 文件、linear-proposals-YYYY-MM-DD.md,以及此工作流期间写入 .claude/scratchpad/ 的任何其他临时文件。

示例调用

  • /extract-my-action-items — 所有参会者,最近一次会议
  • /extract-my-action-items standup — 所有参会者,搜索“standup”
  • /extract-my-action-items for Basti from yesterday — 单人
  • /extract-my-action-items 01KFY1RSEVVQW7MB1TKG4N2D20 — 所有参会者,指定的转录内容

评论

支持 Markdown