@plaud-ai/mcp 0.1.32 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-4QBEOJPX.js +92 -0
- package/dist/chunk-7KGB7GSZ.js +33 -0
- package/dist/chunk-MPCF6HMK.js +151 -0
- package/dist/chunk-SNSGVRCU.js +234 -0
- package/dist/chunk-UPEENHCG.js +70 -0
- package/dist/index.js +78 -30953
- package/dist/install-TU2Y3ARS.js +370 -0
- package/dist/server-3256GMAD.js +294 -0
- package/dist/{setup-3PYAGQRG.js → setup-7JTG3C2W.js} +17 -61
- package/package.json +17 -10
- package/plugin.json +1 -1
- package/skills/plaud-browse/SKILL.md +39 -0
- package/skills/plaud-digest/SKILL.md +41 -0
- package/skills/plaud-export/SKILL.md +51 -0
- package/skills/plaud-find/SKILL.md +54 -0
- package/skills/plaud-followup/SKILL.md +59 -0
- package/skills/plaud-read/SKILL.md +51 -0
- package/skills/plaud-shared/SKILL.md +67 -0
- package/dist/chunk-5JTZ7NKP.js +0 -144
- package/skills/plaud/SKILL.md +0 -40
|
@@ -1,58 +1,20 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
copyToClipboard,
|
|
3
|
+
getMcpEntry,
|
|
4
|
+
removeSkillsFromClaudeCode,
|
|
5
|
+
writeSkillsToClaudeCode
|
|
6
|
+
} from "./chunk-UPEENHCG.js";
|
|
7
|
+
import {
|
|
8
|
+
skillsCombined
|
|
9
|
+
} from "./chunk-4QBEOJPX.js";
|
|
4
10
|
|
|
5
11
|
// src/setup.ts
|
|
6
12
|
import { readFile, writeFile, mkdir, rm } from "fs/promises";
|
|
7
13
|
import { join, dirname } from "path";
|
|
8
|
-
import { homedir
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
var SKILLS_MARKER_END = "<!-- plaud-skills:end -->";
|
|
13
|
-
var SKILLS_BLOCK = `${SKILLS_MARKER_START}
|
|
14
|
-
${SKILLS_COMBINED}
|
|
15
|
-
${SKILLS_MARKER_END}`;
|
|
16
|
-
async function removeSkillsFromClaudeCode() {
|
|
17
|
-
const claudeMdPath = join(homedir(), ".claude", "CLAUDE.md");
|
|
18
|
-
let existing = "";
|
|
19
|
-
try {
|
|
20
|
-
existing = await readFile(claudeMdPath, "utf-8");
|
|
21
|
-
} catch {
|
|
22
|
-
return;
|
|
23
|
-
}
|
|
24
|
-
if (!existing.includes(SKILLS_MARKER_START)) {
|
|
25
|
-
return;
|
|
26
|
-
}
|
|
27
|
-
const updated = existing.replace(new RegExp(`\\n?${SKILLS_MARKER_START}[\\s\\S]*?${SKILLS_MARKER_END}\\n?`), "").trimEnd();
|
|
28
|
-
await writeFile(claudeMdPath, updated ? updated + "\n" : "", "utf-8");
|
|
29
|
-
}
|
|
30
|
-
async function writeSkillsToClaudeCode() {
|
|
31
|
-
const claudeMdPath = join(homedir(), ".claude", "CLAUDE.md");
|
|
32
|
-
let existing = "";
|
|
33
|
-
try {
|
|
34
|
-
existing = await readFile(claudeMdPath, "utf-8");
|
|
35
|
-
} catch {
|
|
36
|
-
}
|
|
37
|
-
if (existing.includes(SKILLS_MARKER_START)) {
|
|
38
|
-
const updated = existing.replace(
|
|
39
|
-
new RegExp(`${SKILLS_MARKER_START}[\\s\\S]*?${SKILLS_MARKER_END}`),
|
|
40
|
-
SKILLS_BLOCK
|
|
41
|
-
);
|
|
42
|
-
await mkdir(dirname(claudeMdPath), { recursive: true });
|
|
43
|
-
await writeFile(claudeMdPath, updated, "utf-8");
|
|
44
|
-
} else {
|
|
45
|
-
await mkdir(dirname(claudeMdPath), { recursive: true });
|
|
46
|
-
await writeFile(claudeMdPath, existing + (existing.endsWith("\n") ? "" : "\n") + SKILLS_BLOCK + "\n", "utf-8");
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
function copyToClipboard(content) {
|
|
50
|
-
const cmd = platform() === "win32" ? "clip" : "pbcopy";
|
|
51
|
-
const result = spawnSync(cmd, [], { input: content });
|
|
52
|
-
return result.status === 0;
|
|
53
|
-
}
|
|
54
|
-
function printSkillsPasteGuide() {
|
|
55
|
-
const copied = copyToClipboard(SKILLS_COMBINED);
|
|
14
|
+
import { homedir } from "os";
|
|
15
|
+
async function printSkillsPasteGuide() {
|
|
16
|
+
const combined = await skillsCombined();
|
|
17
|
+
const copied = copyToClipboard(combined);
|
|
56
18
|
console.log("");
|
|
57
19
|
if (copied) {
|
|
58
20
|
console.log("Plaud Skills copied to clipboard.");
|
|
@@ -63,13 +25,6 @@ function printSkillsPasteGuide() {
|
|
|
63
25
|
console.log(" Claude Desktop: Settings \u2192 Profile \u2192 Custom Instructions");
|
|
64
26
|
console.log(" Codex Desktop: refer to client documentation");
|
|
65
27
|
}
|
|
66
|
-
function getMcpEntry() {
|
|
67
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
68
|
-
return {
|
|
69
|
-
command: process.execPath,
|
|
70
|
-
args: [join(__dirname, "index.js")]
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
28
|
function getConfigPath() {
|
|
74
29
|
if (process.platform === "darwin") {
|
|
75
30
|
return join(homedir(), "Library", "Application Support", "Claude", "claude_desktop_config.json");
|
|
@@ -105,10 +60,11 @@ async function runCleanPlugin() {
|
|
|
105
60
|
async function runSetupCodex() {
|
|
106
61
|
const configPath = join(homedir(), ".codex", "config.toml");
|
|
107
62
|
const { command, args } = getMcpEntry();
|
|
63
|
+
const argsStr = args.map((a) => `"${a}"`).join(", ");
|
|
108
64
|
const entry = `
|
|
109
65
|
[mcp_servers.plaud]
|
|
110
66
|
command = "${command}"
|
|
111
|
-
args = [
|
|
67
|
+
args = [${argsStr}]
|
|
112
68
|
`;
|
|
113
69
|
let content = "";
|
|
114
70
|
try {
|
|
@@ -126,7 +82,7 @@ args = ["${args[0]}"]
|
|
|
126
82
|
console.log("Please restart Codex Desktop to complete the setup.");
|
|
127
83
|
await writeSkillsToClaudeCode();
|
|
128
84
|
console.log("Plaud Skills have been added to ~/.claude/CLAUDE.md for Claude Code.");
|
|
129
|
-
printSkillsPasteGuide();
|
|
85
|
+
await printSkillsPasteGuide();
|
|
130
86
|
process.exit(0);
|
|
131
87
|
}
|
|
132
88
|
async function runUnsetupCodex() {
|
|
@@ -142,7 +98,7 @@ async function runUnsetupCodex() {
|
|
|
142
98
|
console.log("Plaud is not configured in Codex Desktop. Nothing to remove.");
|
|
143
99
|
return;
|
|
144
100
|
}
|
|
145
|
-
const cleaned = content.replace(/\
|
|
101
|
+
const cleaned = content.replace(/\[mcp_servers\.plaud\]\n(?:(?!\[)[^\n]*\n)*/g, "").replace(/\n{3,}/g, "\n\n");
|
|
146
102
|
await writeFile(configPath, cleaned, "utf-8");
|
|
147
103
|
await removeSkillsFromClaudeCode();
|
|
148
104
|
console.log("Plaud has been removed from Codex Desktop.");
|
|
@@ -201,7 +157,7 @@ async function runSetup() {
|
|
|
201
157
|
console.log("Please restart Claude Desktop to complete the setup.");
|
|
202
158
|
await writeSkillsToClaudeCode();
|
|
203
159
|
console.log("Plaud Skills have been added to ~/.claude/CLAUDE.md for Claude Code.");
|
|
204
|
-
printSkillsPasteGuide();
|
|
160
|
+
await printSkillsPasteGuide();
|
|
205
161
|
process.exit(0);
|
|
206
162
|
}
|
|
207
163
|
export {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@plaud-ai/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -12,21 +12,28 @@
|
|
|
12
12
|
".mcp.json",
|
|
13
13
|
"skills"
|
|
14
14
|
],
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
"dev": "tsup --watch",
|
|
19
|
-
"clean": "rm -rf dist",
|
|
20
|
-
"prepublishOnly": "node -e \"const fs=require('fs'),v=require('./package.json').version,p=JSON.parse(fs.readFileSync('./plugin.json','utf8'));p.version=v;fs.writeFileSync('./plugin.json',JSON.stringify(p,null,2)+'\\n');\""
|
|
15
|
+
"publishConfig": {
|
|
16
|
+
"registry": "https://registry.npmjs.org/",
|
|
17
|
+
"access": "public"
|
|
21
18
|
},
|
|
22
19
|
"dependencies": {
|
|
23
20
|
"@modelcontextprotocol/sdk": "^1.12.0",
|
|
21
|
+
"express": "^5.2.1",
|
|
24
22
|
"open": "^10.2.0",
|
|
23
|
+
"pino": "^10.3.1",
|
|
25
24
|
"zod": "^4.3.6"
|
|
26
25
|
},
|
|
27
26
|
"devDependencies": {
|
|
28
|
-
"@
|
|
27
|
+
"@types/express": "^5.0.6",
|
|
29
28
|
"@types/node": "^25.5.0",
|
|
30
|
-
"typescript": "^5.7.0"
|
|
29
|
+
"typescript": "^5.7.0",
|
|
30
|
+
"@plaud-ai/shared": "0.1.0"
|
|
31
|
+
},
|
|
32
|
+
"scripts": {
|
|
33
|
+
"version:show": "node -p \"require('./package.json').version\"",
|
|
34
|
+
"prebuild": "node ../../scripts/sync-skills.mjs",
|
|
35
|
+
"build": "tsup",
|
|
36
|
+
"dev": "tsup --watch",
|
|
37
|
+
"clean": "rm -rf dist skills"
|
|
31
38
|
}
|
|
32
|
-
}
|
|
39
|
+
}
|
package/plugin.json
CHANGED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: plaud-browse
|
|
3
|
+
version: 1.0.0
|
|
4
|
+
description: "Browse, list, or paginate through Plaud recordings. Use when the user says 'what recordings do I have', 'show my recent recordings', 'list my recordings', or asks to see the most recent uploads."
|
|
5
|
+
metadata:
|
|
6
|
+
requires:
|
|
7
|
+
bins: []
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# plaud-browse
|
|
11
|
+
|
|
12
|
+
**Read [`plaud-shared`](../plaud-shared/SKILL.md) first** for auth and output conventions.
|
|
13
|
+
|
|
14
|
+
## When to use
|
|
15
|
+
|
|
16
|
+
- User wants to see what is in their library without a specific target in mind.
|
|
17
|
+
- User explicitly asks for a page, or says "next page", "more results".
|
|
18
|
+
- User asks "what's the most recent recording" — fetch page 1 and return the top item.
|
|
19
|
+
|
|
20
|
+
## Steps
|
|
21
|
+
|
|
22
|
+
1. Call `list_files` with `page=1` and `page_size=20` (default). No `query` / `date_from` / `date_to` unless the user said something that matches `plaud-find`.
|
|
23
|
+
2. Present results in a compact table: **ID**, **NAME**, **DATE** (`YYYY-MM-DD`), **DURATION** (`5m23s` style).
|
|
24
|
+
3. If the page looks like the whole library (fewer than `page_size` returned), tell the user there is no next page.
|
|
25
|
+
4. If the user asks for more, increment `page` by 1 and call again.
|
|
26
|
+
|
|
27
|
+
## Anti-patterns
|
|
28
|
+
|
|
29
|
+
- Do **not** fetch every page eagerly; pagination is lazy.
|
|
30
|
+
- Do **not** call `get_note` or `get_transcript` during a browse — that belongs to `plaud-read` and burns tokens.
|
|
31
|
+
- Do **not** expose raw timestamps or durations in milliseconds.
|
|
32
|
+
|
|
33
|
+
## Example
|
|
34
|
+
|
|
35
|
+
User: "show me my recordings"
|
|
36
|
+
|
|
37
|
+
Agent:
|
|
38
|
+
- `list_files(page=1, page_size=20)`
|
|
39
|
+
- Render table, mention "page 1, say 'next page' for more"
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: plaud-digest
|
|
3
|
+
version: 1.0.0
|
|
4
|
+
description: "Summarize multiple Plaud recordings into a digest. Use when the user says 'weekly report', 'digest of this month', 'what meetings did I have this week', 'recap of last quarter', or asks to roll up multiple recordings into one overview."
|
|
5
|
+
metadata:
|
|
6
|
+
requires:
|
|
7
|
+
bins: []
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# plaud-digest
|
|
11
|
+
|
|
12
|
+
**Read [`plaud-shared`](../plaud-shared/SKILL.md) first.**
|
|
13
|
+
|
|
14
|
+
## When to use
|
|
15
|
+
|
|
16
|
+
- User asks for a roll-up across multiple recordings.
|
|
17
|
+
- Time window is explicit ("this week") or implicit ("recap of recent meetings").
|
|
18
|
+
- Scope is "what happened", not "find one specific meeting" (that's `plaud-find`).
|
|
19
|
+
|
|
20
|
+
## Steps
|
|
21
|
+
|
|
22
|
+
1. **Resolve the window.** Use the date interpretation table in `plaud-find` for relative phrases.
|
|
23
|
+
2. **List the corpus.** `list_files` with `date_from` / `date_to`. Cap at 50 recordings — if the window returns more, ask the user to narrow it.
|
|
24
|
+
3. **Fetch notes in batch.** For each recording, call `get_note`. Do **not** call `get_transcript` unless a specific recording merits a deeper pull.
|
|
25
|
+
4. **Synthesize.** Produce a structured digest:
|
|
26
|
+
- **Headline** — one-line theme of the window.
|
|
27
|
+
- **By recording** — one bullet per recording: `• name (date, duration) — one-sentence takeaway`.
|
|
28
|
+
- **Recurring themes** — topics that appeared in ≥ 2 recordings.
|
|
29
|
+
- **Open action items** — aggregated across recordings, deduplicated.
|
|
30
|
+
5. **Cite sources.** Every non-trivial claim must reference the recording it came from, using the file name (not the raw ID unless the user asked).
|
|
31
|
+
|
|
32
|
+
## Budget
|
|
33
|
+
|
|
34
|
+
- Hard cap: 50 `get_note` calls per digest. If the window has more recordings, compress or ask user to narrow.
|
|
35
|
+
- Skip recordings where `note_list` is empty — mention them at the end under "unsummarized".
|
|
36
|
+
|
|
37
|
+
## Anti-patterns
|
|
38
|
+
|
|
39
|
+
- Do not load transcripts just to pad the digest.
|
|
40
|
+
- Do not synthesize across windows the user didn't ask for ("while we're at it, here's last month too").
|
|
41
|
+
- Do not invent action items that aren't in the notes — only aggregate what's there.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: plaud-export
|
|
3
|
+
version: 1.0.0
|
|
4
|
+
description: "Push Plaud content or a generated artifact to Notion, Slack, HubSpot, Linear, Gmail, or a custom webhook. Use when the user says 'save to Notion', 'post to Slack', 'send to webhook', 'file this in HubSpot', or asks to deliver recording content to an external system."
|
|
5
|
+
metadata:
|
|
6
|
+
requires:
|
|
7
|
+
bins: []
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# plaud-export
|
|
11
|
+
|
|
12
|
+
**Read [`plaud-shared`](../plaud-shared/SKILL.md) first.**
|
|
13
|
+
|
|
14
|
+
## When to use
|
|
15
|
+
|
|
16
|
+
- User has a ready artifact (from `plaud-followup`) or recording content and wants to **deliver** it somewhere.
|
|
17
|
+
- Destination is an external system (not the chat).
|
|
18
|
+
|
|
19
|
+
## Out of scope
|
|
20
|
+
|
|
21
|
+
- Generating the artifact — that's `plaud-followup`.
|
|
22
|
+
- Reading recording content — that's `plaud-read`.
|
|
23
|
+
|
|
24
|
+
This skill is the final leg: take content that already exists and send it.
|
|
25
|
+
|
|
26
|
+
## Steps
|
|
27
|
+
|
|
28
|
+
1. **Confirm the payload.**
|
|
29
|
+
- Recording summary (raw `get_note` content)?
|
|
30
|
+
- Generated artifact (email, SOAP, brief — already drafted)?
|
|
31
|
+
- Raw transcript excerpt?
|
|
32
|
+
2. **Confirm the destination + identifiers.** Ask for the exact target. Plaud does not store destination credentials.
|
|
33
|
+
3. **Deliver using the MCP tool or integration available in the user's environment.** Plaud MCP itself does not expose a `push` tool — this skill assumes another MCP (Notion MCP, Slack MCP, a webhook tool, Gmail send) is available in the session.
|
|
34
|
+
4. **Report the delivery URL** (Notion page URL, Slack message permalink, webhook HTTP status) back to the user.
|
|
35
|
+
|
|
36
|
+
## Destination identifier cheat-sheet
|
|
37
|
+
|
|
38
|
+
| Destination | Required identifier | Typical ask |
|
|
39
|
+
|---|---|---|
|
|
40
|
+
| Notion | page ID or database ID | "Which Notion page should this go under?" |
|
|
41
|
+
| Slack | channel name or ID | "Which channel? (e.g., `#sales` or `C0123`)" |
|
|
42
|
+
| HubSpot / Salesforce | CRM object ID (deal / contact / company) | "Which deal/contact should this attach to?" |
|
|
43
|
+
| Linear | team or project ID | "Which Linear team or project?" |
|
|
44
|
+
| Gmail | recipient email(s) | "Who should this email go to?" |
|
|
45
|
+
| Webhook | full URL | "Paste the webhook URL" |
|
|
46
|
+
|
|
47
|
+
## Anti-patterns
|
|
48
|
+
|
|
49
|
+
- Never persist destination credentials in the conversation or in files. Assume the MCP host provides them.
|
|
50
|
+
- Never send to a default destination ("I'll put it in `#general`") — always confirm.
|
|
51
|
+
- Never alter the artifact content during delivery. If Slack needs mrkdwn, convert format without changing meaning.
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: plaud-find
|
|
3
|
+
version: 1.0.0
|
|
4
|
+
description: "Find a specific Plaud recording by name keyword, date range, or topic. Use when the user says 'find the Weekly Sync', 'the meeting from Monday', 'the call about Q2', 'recordings last week', or describes what they're looking for rather than listing."
|
|
5
|
+
metadata:
|
|
6
|
+
requires:
|
|
7
|
+
bins: []
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# plaud-find
|
|
11
|
+
|
|
12
|
+
**Read [`plaud-shared`](../plaud-shared/SKILL.md) first.**
|
|
13
|
+
|
|
14
|
+
## Background
|
|
15
|
+
|
|
16
|
+
Plaud's `list_files` API does **not** accept `query` / `date_from` / `date_to` server-side — unknown params are silently ignored. Filtering happens client-side.
|
|
17
|
+
|
|
18
|
+
The MCP `list_files` tool accepts the same three optional params and performs the filter for you: pass the user's keyword and/or date window and let the tool paginate up to 5 pages.
|
|
19
|
+
|
|
20
|
+
## Steps
|
|
21
|
+
|
|
22
|
+
1. **Elicit criteria if vague.** If the user just said "find a recording", ask for at least one of:
|
|
23
|
+
- a name keyword (even a partial match),
|
|
24
|
+
- a rough date or date range,
|
|
25
|
+
- a duration range (less useful, ask only if name and date fail).
|
|
26
|
+
2. **Call `list_files`** with the filter params you gathered:
|
|
27
|
+
- `query=<keyword>` — case-insensitive substring match on `name`.
|
|
28
|
+
- `date_from=YYYY-MM-DD`, `date_to=YYYY-MM-DD` — inclusive window on `created_at`.
|
|
29
|
+
- Omit any that the user did not specify.
|
|
30
|
+
3. **If zero matches**, ask the user to broaden one axis (shorter keyword, wider date range).
|
|
31
|
+
4. **If many matches** (> 10), return the top 10 sorted by `created_at` desc and mention the total.
|
|
32
|
+
5. **Never auto-load transcripts**. Present the match list and wait for the user to pick one — that triggers `plaud-read`.
|
|
33
|
+
|
|
34
|
+
## Date interpretation rules
|
|
35
|
+
|
|
36
|
+
| User phrase | Filter |
|
|
37
|
+
|---|---|
|
|
38
|
+
| "today" | `date_from` = today, `date_to` = today |
|
|
39
|
+
| "yesterday" | both = yesterday |
|
|
40
|
+
| "this week" | Monday of this week → today |
|
|
41
|
+
| "last week" | Monday of last week → Sunday of last week |
|
|
42
|
+
| "this month" | 1st of this month → today |
|
|
43
|
+
| "last month" | 1st → last day of previous month |
|
|
44
|
+
| "from Monday" | `date_from` = the most recent Monday, no `date_to` |
|
|
45
|
+
|
|
46
|
+
Resolve relative dates against the **current date** (from conversation context), not the model's training cutoff.
|
|
47
|
+
|
|
48
|
+
## Example
|
|
49
|
+
|
|
50
|
+
User: "find the customer onboarding call from last week"
|
|
51
|
+
|
|
52
|
+
Agent:
|
|
53
|
+
- `list_files(query="onboarding", date_from="2026-04-13", date_to="2026-04-19")`
|
|
54
|
+
- Return matches: "Found 2 recordings. `abc123` Customer Onboarding — Acme (2026-04-15, 42m), `def456` Onboarding Q&A (2026-04-17, 18m). Which one?"
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: plaud-followup
|
|
3
|
+
version: 1.0.0
|
|
4
|
+
description: "Turn a Plaud recording into a follow-up email, thank-you note, action-item list, SOAP note, or meeting brief. Use when the user says 'draft follow-up', 'what were the action items', 'send thank-you email', 'turn this into a SOAP note', 'write the recap', or names an artifact to generate from a recording."
|
|
5
|
+
metadata:
|
|
6
|
+
requires:
|
|
7
|
+
bins: []
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# plaud-followup
|
|
11
|
+
|
|
12
|
+
**Read [`plaud-shared`](../plaud-shared/SKILL.md) first.**
|
|
13
|
+
|
|
14
|
+
## When to use
|
|
15
|
+
|
|
16
|
+
- User wants a **generated document** grounded in one recording.
|
|
17
|
+
- Target format is explicit (email, SOAP note, brief, action-item list) or implicit ("write the follow-up").
|
|
18
|
+
- If the user wants to *send* the output to Notion / Slack / a webhook, chain into `plaud-export` after drafting.
|
|
19
|
+
|
|
20
|
+
## Steps
|
|
21
|
+
|
|
22
|
+
1. **Identify the recording.** If the user didn't name one, hand off to `plaud-find` or `plaud-browse`.
|
|
23
|
+
2. **Fetch source content.**
|
|
24
|
+
- `get_note` first — usually enough for summaries and action items.
|
|
25
|
+
- `get_transcript` only if the artifact needs verbatim quotes (e.g., legal memo) or speaker attribution (e.g., SOAP).
|
|
26
|
+
3. **Generate the artifact** in the requested format. Ground every claim in the source; do not invent attendees, dates, decisions, or numbers.
|
|
27
|
+
4. **Present to the user** in the chat, then ask if they want to refine or export.
|
|
28
|
+
|
|
29
|
+
## Artifact templates
|
|
30
|
+
|
|
31
|
+
### Follow-up email
|
|
32
|
+
- To: attendees (from notes if listed).
|
|
33
|
+
- Subject: "Follow-up — {recording name}, {date}".
|
|
34
|
+
- Opening line: thanks + one-line meeting summary.
|
|
35
|
+
- Body: 3–5 bullets of key points.
|
|
36
|
+
- Action items: numbered list with owner and due date if mentioned.
|
|
37
|
+
- Closing: "Let me know if I missed anything."
|
|
38
|
+
|
|
39
|
+
### Thank-you email
|
|
40
|
+
- Short. One paragraph. One concrete thing you learned or appreciated from the call.
|
|
41
|
+
|
|
42
|
+
### Action-item list
|
|
43
|
+
- Plain markdown: `- [ ] {owner}: {item} (due {date})`.
|
|
44
|
+
- Mark owner as `?` if unclear from notes — do not guess.
|
|
45
|
+
|
|
46
|
+
### SOAP note (clinical)
|
|
47
|
+
- **Subjective** — patient's words (from transcript).
|
|
48
|
+
- **Objective** — observations (from transcript, not inferred).
|
|
49
|
+
- **Assessment** — summary's diagnosis if present.
|
|
50
|
+
- **Plan** — action items and next appointment.
|
|
51
|
+
|
|
52
|
+
### Meeting brief
|
|
53
|
+
- Attendees, date, duration, decisions, risks, next steps.
|
|
54
|
+
|
|
55
|
+
## Anti-patterns
|
|
56
|
+
|
|
57
|
+
- Never invent email recipients. If attendees weren't captured, ask the user.
|
|
58
|
+
- Never invent due dates. Mark as `due: TBD` if not stated.
|
|
59
|
+
- Do not send the email — this skill drafts. Handoff to `plaud-export` for delivery.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: plaud-read
|
|
3
|
+
version: 1.0.0
|
|
4
|
+
description: "Read the transcript, AI summary, notes, or download audio for a specific Plaud recording. Use when the user says 'show the transcript', 'summarize this', 'what was said', 'get audio', 'the notes from', or names a specific recording to dig into. Also covers extracting structured fields from a recording."
|
|
5
|
+
metadata:
|
|
6
|
+
requires:
|
|
7
|
+
bins: []
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# plaud-read
|
|
11
|
+
|
|
12
|
+
**Read [`plaud-shared`](../plaud-shared/SKILL.md) first.**
|
|
13
|
+
|
|
14
|
+
## When to use
|
|
15
|
+
|
|
16
|
+
- User names a specific recording (by name or ID) and wants to read its content.
|
|
17
|
+
- User asks for "transcript", "summary", "action items", "audio", "who said what", or a structured extraction ("action items, decisions, attendees").
|
|
18
|
+
- If the user did **not** specify a recording, hand off to `plaud-find` (by topic) or `plaud-browse` (by recency) first.
|
|
19
|
+
|
|
20
|
+
## Tool selection matrix
|
|
21
|
+
|
|
22
|
+
| User wants | Tool | Notes |
|
|
23
|
+
|---|---|---|
|
|
24
|
+
| AI summary, TL;DR, action items | `get_note` | Returns Markdown; usually enough — try this before `get_transcript` |
|
|
25
|
+
| Verbatim quotes, full dialogue | `get_transcript` | Timestamped; larger |
|
|
26
|
+
| Audio download link | `get_file` then use `presigned_url` | Link expires in 24h |
|
|
27
|
+
| Full metadata + availability flags | `get_file` | Check `source_list` / `note_list` populated before claiming content exists |
|
|
28
|
+
|
|
29
|
+
## Structured extraction workflow
|
|
30
|
+
|
|
31
|
+
If the user provides a schema (e.g., `{"action_items": [], "decisions": [], "attendees": []}`):
|
|
32
|
+
|
|
33
|
+
1. Call `get_note` first — the AI summary usually already contains these fields.
|
|
34
|
+
2. Only call `get_transcript` if the summary is missing a required field.
|
|
35
|
+
3. Return JSON matching the user's schema. Mark any missing field with `null` and note why.
|
|
36
|
+
|
|
37
|
+
Common schemas:
|
|
38
|
+
- Sales: `{ "pain_points": [], "follow_ups": [], "deal_stage": "" }`
|
|
39
|
+
- Clinical: `{ "diagnoses": [], "medications": [], "next_appointment": "" }`
|
|
40
|
+
- Project: `{ "action_items": [], "decisions": [], "attendees": [] }`
|
|
41
|
+
|
|
42
|
+
## Output
|
|
43
|
+
|
|
44
|
+
- Transcripts: preserve `[MM:SS - MM:SS] Speaker: content`. Do not reformat timestamps.
|
|
45
|
+
- Summaries: render Markdown directly in the reply.
|
|
46
|
+
- Audio: print the URL and mention "expires in 24h".
|
|
47
|
+
|
|
48
|
+
## Anti-patterns
|
|
49
|
+
|
|
50
|
+
- Do not call `get_transcript` speculatively — it's the largest payload.
|
|
51
|
+
- Do not paraphrase the AI summary unless the user asked; quote it verbatim.
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: plaud-shared
|
|
3
|
+
version: 1.0.0
|
|
4
|
+
description: "First read before any Plaud operation. Auth flow, error handling, output conventions, token refresh. Use when the user mentions Plaud for the first time in a session, or when any other Plaud skill is invoked."
|
|
5
|
+
metadata:
|
|
6
|
+
requires:
|
|
7
|
+
bins: []
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# plaud-shared
|
|
11
|
+
|
|
12
|
+
**CRITICAL — read this before calling any Plaud tool.** Applies to every other `plaud-*` skill.
|
|
13
|
+
|
|
14
|
+
## Authentication
|
|
15
|
+
|
|
16
|
+
- Plaud uses OAuth. Tokens are stored in `~/.plaud/tokens.json` and refreshed automatically.
|
|
17
|
+
- If any tool returns an auth error (message includes `Not authenticated` or `401`), call the `login` tool and wait for the browser callback. Do **not** retry the original tool until login returns success.
|
|
18
|
+
- Never ask the user to paste tokens. The `login` tool handles the whole flow.
|
|
19
|
+
|
|
20
|
+
## Tool inventory
|
|
21
|
+
|
|
22
|
+
| Tool | Purpose |
|
|
23
|
+
|---|---|
|
|
24
|
+
| `login` | Open browser for OAuth; blocks until callback or 2-min timeout |
|
|
25
|
+
| `logout` | Revoke and clear tokens |
|
|
26
|
+
| `get_current_user` | Verify who is signed in |
|
|
27
|
+
| `list_files` | Browse, paginate, filter recordings (supports `query`, `date_from`, `date_to`) |
|
|
28
|
+
| `get_file` | Full record incl. `presigned_url`, `source_list`, `note_list` |
|
|
29
|
+
| `get_note` | AI-generated summary and action items |
|
|
30
|
+
| `get_transcript` | Timestamped transcript with speaker labels |
|
|
31
|
+
|
|
32
|
+
## Error semantics
|
|
33
|
+
|
|
34
|
+
| Pattern in error message | Meaning | What to do |
|
|
35
|
+
|---|---|---|
|
|
36
|
+
| `401` / `Not authenticated` | Token missing or expired | Call `login`, then retry |
|
|
37
|
+
| `404` | File ID does not exist | Tell the user the ID is wrong; do not retry |
|
|
38
|
+
| `500` | Backend error (often an invalid ID too — see §7.1 of proposal) | Retry once; if still 500, treat as NOT_FOUND |
|
|
39
|
+
| `fetch failed` / `ECONNREFUSED` | Network problem | Abort; tell user to check connection |
|
|
40
|
+
|
|
41
|
+
## Output conventions
|
|
42
|
+
|
|
43
|
+
When presenting recordings to the user:
|
|
44
|
+
|
|
45
|
+
- Always show name, date, duration, and file ID — users need the ID to ask follow-up questions.
|
|
46
|
+
- Format durations human-readable: `23s`, `5m23s`, `1h05m`. Raw milliseconds are for logs only.
|
|
47
|
+
- Format dates as `YYYY-MM-DD` in local time.
|
|
48
|
+
- Transcripts: preserve `[MM:SS - MM:SS] Speaker: content` format.
|
|
49
|
+
- Notes: render Markdown directly.
|
|
50
|
+
|
|
51
|
+
## Data model quick reference
|
|
52
|
+
|
|
53
|
+
- `duration` field is **milliseconds**.
|
|
54
|
+
- `source_list` — array; each item with `data_type === "transaction"` holds the transcript segments (JSON-encoded string in `data_content`).
|
|
55
|
+
- `note_list` — array; each item with `data_type === "auto_sum_note"` holds the AI summary (Markdown in `data_content`).
|
|
56
|
+
- `presigned_url` — expires in 24 hours; re-fetch with `get_file` if stale.
|
|
57
|
+
|
|
58
|
+
## When to load which sibling skill
|
|
59
|
+
|
|
60
|
+
| User intent | Skill to follow |
|
|
61
|
+
|---|---|
|
|
62
|
+
| "List / show / browse my recordings" | `plaud-browse` |
|
|
63
|
+
| "Find the meeting about X" / "from Monday" | `plaud-find` |
|
|
64
|
+
| "Show transcript / summary / audio" | `plaud-read` |
|
|
65
|
+
| "Weekly digest" / "what did I have this month" | `plaud-digest` |
|
|
66
|
+
| "Draft follow-up" / "action items" / "thank-you email" | `plaud-followup` |
|
|
67
|
+
| "Save to Notion / Slack / webhook" | `plaud-export` |
|