@orderful/droid 0.41.0 → 0.42.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.
Files changed (31) hide show
  1. package/.claude-plugin/plugin.json +2 -0
  2. package/CHANGELOG.md +19 -0
  3. package/dist/bin/droid.js +132 -4
  4. package/dist/commands/integrations.d.ts.map +1 -1
  5. package/dist/commands/tui/components/IntegrationsDetails.d.ts.map +1 -1
  6. package/dist/commands/tui.d.ts.map +1 -1
  7. package/dist/integrations/github/index.d.ts +6 -0
  8. package/dist/integrations/github/index.d.ts.map +1 -0
  9. package/dist/integrations/github/index.ts +17 -0
  10. package/dist/integrations/github/references/setup.md +61 -0
  11. package/dist/integrations/granola/references/setup.md +54 -0
  12. package/dist/lib/types.d.ts +8 -0
  13. package/dist/lib/types.d.ts.map +1 -1
  14. package/dist/tools/meeting/.claude-plugin/plugin.json +22 -0
  15. package/dist/tools/meeting/TOOL.yaml +15 -0
  16. package/dist/tools/meeting/commands/meeting.md +35 -0
  17. package/dist/tools/meeting/skills/meeting/SKILL.md +105 -0
  18. package/dist/tools/meeting/skills/meeting/references/export-workflow.md +87 -0
  19. package/package.json +1 -1
  20. package/src/commands/integrations.ts +30 -0
  21. package/src/commands/tui/components/IntegrationsDetails.tsx +114 -0
  22. package/src/commands/tui.tsx +26 -4
  23. package/src/integrations/github/index.ts +17 -0
  24. package/src/integrations/github/references/setup.md +61 -0
  25. package/src/integrations/granola/references/setup.md +54 -0
  26. package/src/lib/types.ts +10 -0
  27. package/src/tools/meeting/.claude-plugin/plugin.json +22 -0
  28. package/src/tools/meeting/TOOL.yaml +15 -0
  29. package/src/tools/meeting/commands/meeting.md +35 -0
  30. package/src/tools/meeting/skills/meeting/SKILL.md +105 -0
  31. package/src/tools/meeting/skills/meeting/references/export-workflow.md +87 -0
@@ -0,0 +1,87 @@
1
+ # Export Workflow
2
+
3
+ Full procedure for `/meeting export {title}`.
4
+
5
+ ## E1. Resolve Meeting
6
+
7
+ 1. Use `ToolSearch` to load `mcp__granola__list_meetings`
8
+ 2. Call with `time_range: "last_30_days"` to get recent meetings
9
+ 3. Fuzzy-match `{title}` against meeting titles
10
+ 4. If multiple matches, present list and ask user to pick
11
+ 5. If no match, show recent meetings and ask user to clarify
12
+
13
+ ## E2. Get Meeting Data
14
+
15
+ 1. Use `ToolSearch` to load `mcp__granola__get_meetings`
16
+ 2. Call with the resolved meeting ID
17
+ 3. Extract: title, date, participants, summary
18
+
19
+ ## E3. Resolve Destination
20
+
21
+ If codex is in current context (user has loaded a codex repo this session):
22
+ - Export to `{codex_repo}/meetings/{date}-{slugified-title}.md`
23
+ - Confirm with user: "Export to codex at meetings/{filename}?"
24
+
25
+ If no codex in context:
26
+ - Ask: "Where should I export this meeting?"
27
+ - Codex — "Which codex repo?" (list available via `droid config --get repos`)
28
+ - Brain — Export to brain vault as a meeting doc
29
+ - Clipboard — Copy formatted markdown to clipboard
30
+ - Terminal — Just print it here
31
+
32
+ ## E4. Format Document
33
+
34
+ Generate markdown with this exact frontmatter structure:
35
+
36
+ ---
37
+ title: "{meeting title}"
38
+ type: meeting
39
+ source: granola
40
+ source_url: "https://notes.granola.ai/d/{meeting_id}"
41
+ date: {YYYY-MM-DD}
42
+ participants: [{comma-separated names}]
43
+ exported: {YYYY-MM-DD}
44
+ ---
45
+
46
+ # {meeting title}
47
+
48
+ **Date:** {full date and time}
49
+ **Participants:** {names with roles/orgs if available}
50
+
51
+ ## Summary
52
+
53
+ {Granola's structured summary from get_meetings}
54
+
55
+ ## Key Decisions
56
+
57
+ {Extract decisions from the summary, or note "No explicit decisions captured"}
58
+
59
+ ## Action Items
60
+
61
+ {Extract action items from the summary, or note "No action items captured"}
62
+
63
+ ## E5. Slugification Rules
64
+
65
+ Convert title to filename slug:
66
+ - Lowercase
67
+ - Replace spaces with hyphens
68
+ - Remove special characters except hyphens
69
+ - Strip leading/trailing hyphens
70
+ - Collapse multiple hyphens
71
+
72
+ Examples:
73
+ - "[Tech Design Review] Automated Partner Testing" → `automated-partner-testing`
74
+ - "Tyler / Bosun - Partner Testing" → `tyler-bosun-partner-testing`
75
+ - "🌅 Horizon Daily" → `horizon-daily`
76
+
77
+ Final filename: `{YYYY-MM-DD}-{slug}.md`
78
+
79
+ ## E6. Write File
80
+
81
+ Write the formatted document to the resolved destination path.
82
+
83
+ ## E7. Confirm
84
+
85
+ Tell the user:
86
+ - "Exported to: {path}"
87
+ - If codex: suggest committing the new file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orderful/droid",
3
- "version": "0.41.0",
3
+ "version": "0.42.0",
4
4
  "description": "AI workflow toolkit for sharing skills, commands, and agents across the team",
5
5
  "type": "module",
6
6
  "bin": {
@@ -399,6 +399,36 @@ export async function integrationsStatusCommand(): Promise<void> {
399
399
  }
400
400
 
401
401
  console.log('');
402
+
403
+ // GitHub
404
+ console.log(chalk.bold(' GitHub'));
405
+
406
+ const githubConfigured = getConfigValue('integrations.github.configured');
407
+
408
+ if (githubConfigured) {
409
+ console.log(chalk.green(' CLI: Installed'));
410
+ console.log(chalk.green(' Status: configured'));
411
+ } else {
412
+ console.log(chalk.yellow(' CLI: Not verified'));
413
+ console.log(chalk.gray(' Run: gh auth login'));
414
+ }
415
+
416
+ console.log('');
417
+
418
+ // Granola
419
+ console.log(chalk.bold(' Granola'));
420
+
421
+ const granolaConfigured = getConfigValue('integrations.granola.configured');
422
+
423
+ if (granolaConfigured) {
424
+ console.log(chalk.green(' MCP: Connected'));
425
+ console.log(chalk.green(' Status: configured'));
426
+ } else {
427
+ console.log(chalk.yellow(' MCP: Not yet verified'));
428
+ console.log(chalk.gray(' Use /mcp to add the Granola server, then verify with a meeting query'));
429
+ }
430
+
431
+ console.log('');
402
432
  }
403
433
 
404
434
  /**
@@ -148,6 +148,112 @@ function AtlassianDetails({ isFocused, selectedAction, connected }: { isFocused:
148
148
  );
149
149
  }
150
150
 
151
+ function GithubDetails({ isFocused, selectedAction, connected }: { isFocused: boolean; selectedAction: number; connected: boolean }) {
152
+ return (
153
+ <Box flexDirection="column" paddingLeft={2} flexGrow={1}>
154
+ <Text color={colors.text} bold>GitHub CLI</Text>
155
+
156
+ <Box flexDirection="column" marginTop={1}>
157
+ <Text color={colors.textDim} bold>Status</Text>
158
+ <Text>
159
+ <Text color={colors.textDim}> CLI </Text>
160
+ {connected
161
+ ? <Text color={colors.success}>Installed ✓</Text>
162
+ : <Text color="#fbbf24">Not detected</Text>}
163
+ </Text>
164
+ <Text>
165
+ <Text color={colors.textDim}> Auth </Text>
166
+ {connected
167
+ ? <Text color={colors.success}>Authenticated ✓</Text>
168
+ : <Text color="#fbbf24">Not yet verified</Text>}
169
+ </Text>
170
+ </Box>
171
+
172
+ <Box flexDirection="column" marginTop={1}>
173
+ <Text color={colors.textDim}>
174
+ {connected
175
+ ? 'Used by codex, tech-design, code-review, share'
176
+ : 'Run `gh auth login` to connect'}
177
+ </Text>
178
+ </Box>
179
+
180
+ {isFocused && (
181
+ <Box marginTop={2} flexDirection="row" gap={2}>
182
+ <Text
183
+ backgroundColor={selectedAction === 0 ? colors.primary : undefined}
184
+ color={selectedAction === 0 ? '#ffffff' : colors.textDim}
185
+ bold={selectedAction === 0}
186
+ >
187
+ {' '}Setup Guide{' '}
188
+ </Text>
189
+ </Box>
190
+ )}
191
+
192
+ {isFocused && (
193
+ <Box marginTop={1}>
194
+ <Text color={colors.textDim}>enter confirm {'·'} esc back</Text>
195
+ </Box>
196
+ )}
197
+
198
+ {!isFocused && (
199
+ <Box marginTop={2}>
200
+ <Text color={colors.textDim}>press enter for options</Text>
201
+ </Box>
202
+ )}
203
+ </Box>
204
+ );
205
+ }
206
+
207
+ function GranolaDetails({ isFocused, selectedAction, connected }: { isFocused: boolean; selectedAction: number; connected: boolean }) {
208
+ return (
209
+ <Box flexDirection="column" paddingLeft={2} flexGrow={1}>
210
+ <Text color={colors.text} bold>Granola</Text>
211
+
212
+ <Box flexDirection="column" marginTop={1}>
213
+ <Text color={colors.textDim} bold>MCP Server</Text>
214
+ <Text>
215
+ <Text color={colors.textDim}> Status </Text>
216
+ {connected
217
+ ? <Text color={colors.success}>Connected ✓</Text>
218
+ : <Text color="#fbbf24">Not yet verified</Text>}
219
+ </Text>
220
+ </Box>
221
+
222
+ <Box flexDirection="column" marginTop={1}>
223
+ <Text color={colors.textDim}>
224
+ {connected
225
+ ? 'Meeting notes and transcripts available via MCP'
226
+ : 'Use /mcp to add the Granola server'}
227
+ </Text>
228
+ </Box>
229
+
230
+ {isFocused && (
231
+ <Box marginTop={2} flexDirection="row" gap={2}>
232
+ <Text
233
+ backgroundColor={selectedAction === 0 ? colors.primary : undefined}
234
+ color={selectedAction === 0 ? '#ffffff' : colors.textDim}
235
+ bold={selectedAction === 0}
236
+ >
237
+ {' '}Setup Guide{' '}
238
+ </Text>
239
+ </Box>
240
+ )}
241
+
242
+ {isFocused && (
243
+ <Box marginTop={1}>
244
+ <Text color={colors.textDim}>enter confirm {'·'} esc back</Text>
245
+ </Box>
246
+ )}
247
+
248
+ {!isFocused && (
249
+ <Box marginTop={2}>
250
+ <Text color={colors.textDim}>press enter for options</Text>
251
+ </Box>
252
+ )}
253
+ </Box>
254
+ );
255
+ }
256
+
151
257
  export function IntegrationsDetails({
152
258
  isFocused,
153
259
  selectedAction,
@@ -157,6 +263,14 @@ export function IntegrationsDetails({
157
263
  return <AtlassianDetails isFocused={isFocused} selectedAction={selectedAction} connected={integration.connected} />;
158
264
  }
159
265
 
266
+ if (integration.id === 'github') {
267
+ return <GithubDetails isFocused={isFocused} selectedAction={selectedAction} connected={integration.connected} />;
268
+ }
269
+
270
+ if (integration.id === 'granola') {
271
+ return <GranolaDetails isFocused={isFocused} selectedAction={selectedAction} connected={integration.connected} />;
272
+ }
273
+
160
274
  // Default: Slack
161
275
  return <SlackDetails isFocused={isFocused} selectedAction={selectedAction} />;
162
276
  }
@@ -15,7 +15,7 @@ import {
15
15
  isToolInstalled,
16
16
  getToolUpdateStatus,
17
17
  } from '../lib/tools';
18
- import { configExists, loadConfig, getAutoUpdateConfig, getConfigValue } from '../lib/config';
18
+ import { configExists, loadConfig, getAutoUpdateConfig, getConfigValue, setConfigValue } from '../lib/config';
19
19
  import { type ConfigOption, type ToolManifest } from '../lib/types';
20
20
  import { detectAllPlatforms } from '../lib/platforms';
21
21
  import { getVersion } from '../lib/version';
@@ -38,12 +38,20 @@ import { ReposManagementScreen } from './tui/views/ReposManagementScreen';
38
38
  import { ReposViewerScreen } from './tui/views/ReposViewerScreen';
39
39
  import { useAppUpdate } from './tui/hooks/useAppUpdate';
40
40
  import { useToolUpdates } from './tui/hooks/useToolUpdates';
41
+ import { checkGhAuth } from '../integrations/github';
41
42
 
42
43
  // Module-level variable to store exit message (printed after leaving alternate screen)
43
44
  let exitMessage: string | null = null;
44
45
  // Module-level variable to store a CLI command to run after TUI exits
45
46
  let exitCommand: string[] | null = null;
46
47
 
48
+ const INTEGRATION_GUIDE_TITLES: Record<string, string> = {
49
+ slack: 'Slack Integration Setup',
50
+ atlassian: 'Atlassian Integration Setup',
51
+ github: 'GitHub CLI Setup',
52
+ granola: 'Granola Integration Setup',
53
+ };
54
+
47
55
  // Resolve path to bundled integrations (dist/integrations/ at runtime)
48
56
  const __tui_dirname = dirname(fileURLToPath(import.meta.url));
49
57
  const INTEGRATIONS_DIR = join(__tui_dirname, '../integrations');
@@ -181,8 +189,20 @@ function App() {
181
189
  const integrations = [
182
190
  { id: 'slack', name: 'Slack', connected: !!process.env.SLACK_USER_TOKEN },
183
191
  { id: 'atlassian', name: 'Atlassian', connected: !!getConfigValue('integrations.atlassian.configured') },
192
+ { id: 'github', name: 'GitHub', connected: !!getConfigValue('integrations.github.configured') },
193
+ { id: 'granola', name: 'Granola', connected: !!getConfigValue('integrations.granola.configured') },
184
194
  ];
185
195
 
196
+ // Detect GitHub CLI auth on first mount (async, caches to config)
197
+ useEffect(() => {
198
+ if (!getConfigValue('integrations.github.configured')) {
199
+ const isAuthed = checkGhAuth();
200
+ if (isAuthed) {
201
+ setConfigValue('integrations.github.configured', true);
202
+ }
203
+ }
204
+ }, []);
205
+
186
206
  useInput(
187
207
  (input, key) => {
188
208
  if (message) setMessage(null);
@@ -257,6 +277,10 @@ function App() {
257
277
  ];
258
278
  } else if (currentIntegration?.id === 'atlassian') {
259
279
  intActions = [{ id: 'guide' }];
280
+ } else if (currentIntegration?.id === 'github') {
281
+ intActions = [{ id: 'guide' }];
282
+ } else if (currentIntegration?.id === 'granola') {
283
+ intActions = [{ id: 'guide' }];
260
284
  }
261
285
 
262
286
  const maxIntAction = intActions.length - 1;
@@ -273,9 +297,7 @@ function App() {
273
297
  exit();
274
298
  } else if (actionId === 'guide') {
275
299
  const integrationId = currentIntegration?.id ?? 'slack';
276
- const guideTitle = currentIntegration?.id === 'atlassian'
277
- ? 'Atlassian Integration Setup'
278
- : 'Slack Integration Setup';
300
+ const guideTitle = INTEGRATION_GUIDE_TITLES[integrationId] ?? `${currentIntegration?.name} Setup`;
279
301
  const content = loadIntegrationReference(integrationId, 'setup.md');
280
302
  if (content) {
281
303
  setPreviousView('detail');
@@ -0,0 +1,17 @@
1
+ import { spawnSync } from 'child_process';
2
+
3
+ /**
4
+ * Check if the GitHub CLI (gh) is installed and authenticated.
5
+ * Returns true if `gh auth status` exits with code 0.
6
+ */
7
+ export function checkGhAuth(): boolean {
8
+ try {
9
+ const result = spawnSync('gh', ['auth', 'status'], {
10
+ stdio: 'ignore',
11
+ timeout: 5000,
12
+ });
13
+ return result.status === 0;
14
+ } catch {
15
+ return false;
16
+ }
17
+ }
@@ -0,0 +1,61 @@
1
+ # GitHub CLI Setup
2
+
3
+ ## Overview
4
+
5
+ The GitHub CLI (`gh`) integration connects droid to GitHub for PR creation, issue management, and repository operations. Several skills depend on it: **codex**, **tech-design**, **code-review**, and **share**.
6
+
7
+ ## Setup
8
+
9
+ ### 1. Install the GitHub CLI
10
+
11
+ ```bash
12
+ brew install gh
13
+ ```
14
+
15
+ ### 2. Authenticate
16
+
17
+ ```bash
18
+ gh auth login
19
+ ```
20
+
21
+ Follow the interactive prompts to authenticate with your GitHub account. Choose HTTPS as the preferred protocol.
22
+
23
+ ### 3. Verify
24
+
25
+ ```bash
26
+ gh auth status
27
+ ```
28
+
29
+ You should see your GitHub username and the scopes available. The TUI Integrations tab will show `GitHub ✓` once detection runs.
30
+
31
+ ## How It Works
32
+
33
+ Unlike Slack (which uses environment variables and OAuth) or Atlassian (which uses MCP), GitHub uses a locally installed CLI binary:
34
+
35
+ | Aspect | Slack | Atlassian | GitHub |
36
+ |--------|-------|-----------|--------|
37
+ | Auth method | OAuth + env vars | MCP server (managed by Claude Code) | `gh auth login` (local CLI) |
38
+ | Setup | `droid integrations setup slack` | `/mcp` in Claude Code | `brew install gh && gh auth login` |
39
+ | API access | `@slack/web-api` SDK | MCP tool calls (`mcp__claude_ai_Atlassian__*`) | `gh` CLI via Bash |
40
+ | Config flag | `integrations.slack.configured` | `integrations.atlassian.configured` | `integrations.github.configured` |
41
+
42
+ The `configured` flag is set automatically when the TUI detects a working `gh auth status` — no manual configuration needed.
43
+
44
+ ## Troubleshooting
45
+
46
+ | Issue | Resolution |
47
+ |-------|------------|
48
+ | `gh: command not found` | Install with `brew install gh` |
49
+ | Not authenticated | Run `gh auth login` and follow prompts |
50
+ | Wrong account | Run `gh auth logout` then `gh auth login` |
51
+ | Scopes missing | Run `gh auth refresh -s <scope>` to add scopes |
52
+ | "Not configured" in TUI | Relaunch the TUI — detection runs on startup |
53
+
54
+ ## Dependent Skills
55
+
56
+ These skills use `gh` and will fail without it:
57
+
58
+ - **codex** — Fetches PR metadata and diffs for context
59
+ - **tech-design** — Creates PRs for tech design documents
60
+ - **code-review** — Reads PR details, checks, and comments
61
+ - **share** — Creates PRs and interacts with GitHub APIs
@@ -0,0 +1,54 @@
1
+ # Granola Integration Setup
2
+
3
+ ## Overview
4
+
5
+ The Granola integration connects droid to your meeting notes, summaries, and transcripts through the Granola MCP server in Claude Code.
6
+
7
+ ## Setup
8
+
9
+ ### 1. Install Granola
10
+
11
+ Install and sign in to the Granola app first if you have not already.
12
+
13
+ ### 2. Add the Granola MCP Server
14
+
15
+ In Claude Code, run:
16
+
17
+ ```bash
18
+ /mcp
19
+ ```
20
+
21
+ Select **Granola** and complete the auth flow.
22
+
23
+ ### 3. Verify Connection
24
+
25
+ Ask Claude to list recent meetings. If successful, droid can use Granola MCP tools and the Integrations tab should show `Granola ✓`.
26
+
27
+ ## Available MCP Tools
28
+
29
+ Depending on your Granola MCP version, tools may include:
30
+
31
+ - `list_meetings`
32
+ - `get_meetings`
33
+ - `get_meeting_transcript`
34
+ - `query_granola_meetings`
35
+
36
+ ## How It Works
37
+
38
+ Granola follows the same integration model as Atlassian: MCP is managed by Claude Code, not by a local CLI.
39
+
40
+ | Aspect | Granola |
41
+ |--------|---------|
42
+ | Auth method | MCP server via Claude Code |
43
+ | Setup command | `/mcp` |
44
+ | Config flag | `integrations.granola.configured` |
45
+
46
+ No `droid integrations setup granola` command is required.
47
+
48
+ ## Troubleshooting
49
+
50
+ | Issue | Resolution |
51
+ |-------|------------|
52
+ | Granola not shown in `/mcp` | Update Claude Code and try again |
53
+ | Not connected in TUI | Re-run `/mcp` and verify with a meeting query |
54
+ | No meetings returned | Confirm the Granola app has synced meetings |
package/src/lib/types.ts CHANGED
@@ -67,9 +67,19 @@ export interface IntegrationAtlassianConfig {
67
67
  configured?: boolean;
68
68
  }
69
69
 
70
+ export interface IntegrationGithubConfig {
71
+ configured?: boolean;
72
+ }
73
+
74
+ export interface IntegrationGranolaConfig {
75
+ configured?: boolean;
76
+ }
77
+
70
78
  export interface IntegrationsConfig {
71
79
  slack?: IntegrationSlackConfig;
72
80
  atlassian?: IntegrationAtlassianConfig;
81
+ github?: IntegrationGithubConfig;
82
+ granola?: IntegrationGranolaConfig;
73
83
  }
74
84
 
75
85
  export interface DroidConfig {
@@ -0,0 +1,22 @@
1
+ {
2
+ "name": "droid-meeting",
3
+ "version": "0.1.0",
4
+ "description": "Work with meeting notes, summaries, and transcripts. List recent meetings, search content, generate context-aware summaries, export to codex. Backed by Granola MCP.",
5
+ "author": {
6
+ "name": "Orderful",
7
+ "url": "https://github.com/orderful"
8
+ },
9
+ "repository": "https://github.com/orderful/droid",
10
+ "license": "MIT",
11
+ "keywords": [
12
+ "droid",
13
+ "ai",
14
+ "meeting"
15
+ ],
16
+ "skills": [
17
+ "./skills/meeting/SKILL.md"
18
+ ],
19
+ "commands": [
20
+ "./commands/meeting.md"
21
+ ]
22
+ }
@@ -0,0 +1,15 @@
1
+ name: meeting
2
+ description: "Work with meeting notes, summaries, and transcripts. List recent meetings, search content, generate context-aware summaries, export to codex. Backed by Granola MCP."
3
+ version: 0.1.0
4
+ status: beta
5
+
6
+ includes:
7
+ skills:
8
+ - name: meeting
9
+ required: true
10
+ commands:
11
+ - name: meeting
12
+ is_alias: false
13
+ agents: []
14
+
15
+ dependencies: []
@@ -0,0 +1,35 @@
1
+ ---
2
+ name: meeting
3
+ description: "Work with meeting notes, summaries, and transcripts"
4
+ argument-hint: "[search {query} | summary {title} | summarize {title} | export {title} | decisions | last week]"
5
+ ---
6
+
7
+ # /meeting
8
+
9
+ **User invoked:** `/meeting $ARGUMENTS`
10
+
11
+ **Your task:** Invoke the **meeting skill** with these arguments.
12
+
13
+ ## Examples
14
+
15
+ - `/meeting` → List recent meetings (this week)
16
+ - `/meeting last week` → List meetings from last week
17
+ - `/meeting search auth decisions` → Search meetings for auth-related decisions
18
+ - `/meeting summary standup` → Quick Granola summary of the standup
19
+ - `/meeting summarize tech design review` → Context-aware summary using transcript
20
+ - `/meeting export partner testing review` → Export meeting to codex
21
+ - `/meeting decisions` → Pull decisions from recent meetings
22
+
23
+ ## Quick Reference
24
+
25
+ ```
26
+ /meeting # List this week's meetings
27
+ /meeting last week # List last week's meetings
28
+ /meeting search {query} # Natural language search
29
+ /meeting summary {title} # Quick summary (Granola)
30
+ /meeting summarize {title} # Deep summary (transcript + context)
31
+ /meeting export {title} # Export to codex
32
+ /meeting decisions # Decisions from recent meetings
33
+ ```
34
+
35
+ See the **meeting skill** for complete documentation.
@@ -0,0 +1,105 @@
1
+ ---
2
+ name: meeting
3
+ description: "Work with meeting notes, summaries, and transcripts. Use when user asks about meetings, wants to review notes, search decisions, or export to codex. User prompts like 'what did we discuss today', 'summarise the tech design review', 'export my meeting with Thea to codex'."
4
+ argument-hint: "[search {query} | summary {title} | summarize {title} | export {title} | decisions | last week]"
5
+ allowed-tools: [Read, Write, Edit, Glob, Grep, Bash(droid:*)]
6
+ ---
7
+
8
+ # Meeting Skill
9
+
10
+ Work with meeting notes, summaries, and transcripts via Granola MCP.
11
+
12
+ ## When to Use
13
+
14
+ - User asks about what was discussed in a meeting
15
+ - User wants a summary of a specific meeting
16
+ - User wants to search across meeting content for decisions/action items
17
+ - User asks to export meeting notes to codex
18
+ - Natural language like "what did we decide in the partner testing review?", "summarise my meeting with Thea", "what were the action items from today?"
19
+
20
+ ## When NOT to Use
21
+
22
+ - Calendar scheduling or upcoming events (this is about past meeting content)
23
+ - User is explicitly asking for Granola app features (this skill does not control Granola)
24
+
25
+ ## Prerequisites
26
+
27
+ Check if Granola MCP is available. Run:
28
+
29
+ ```bash
30
+ droid config --get integrations.granola.configured
31
+ ```
32
+
33
+ If not configured, tell user:
34
+ > "Granola MCP is not connected. Run `/mcp` in Claude Code to add the Granola integration, then try again."
35
+
36
+ If connected, proceed.
37
+
38
+ ## Commands
39
+
40
+ | Command | Action |
41
+ |---------|--------|
42
+ | `/meeting` | List recent meetings (this week) |
43
+ | `/meeting last week` | List meetings from last week |
44
+ | `/meeting search {query}` | Natural language search across all meetings |
45
+ | `/meeting summary {title}` | Quick summary from Granola (fast, no context cost) |
46
+ | `/meeting summarize {title}` | Context-aware summary using transcript + loaded project/codex context |
47
+ | `/meeting export {title}` | Export meeting to codex |
48
+ | `/meeting decisions` | Pull decisions from recent meetings |
49
+
50
+ Natural language is the primary interface. Users should not need these commands. Recognise meeting intent and route accordingly:
51
+ - "what did Calvin say about Mosaic?" → search
52
+ - "summarise the partner testing review" → summary or summarize
53
+ - "export today's standup to codex" → export
54
+ - "what decisions were made this week?" → decisions
55
+
56
+ ## Procedures
57
+
58
+ ### List meetings (`/meeting`, `/meeting last week`)
59
+
60
+ 1. Use `ToolSearch` to load `mcp__granola__list_meetings`
61
+ 2. Call with `time_range`: `"this_week"` (default) or `"last_week"`
62
+ 3. Present results as a table: title, date, participants
63
+
64
+ ### Search (`/meeting search {query}`)
65
+
66
+ 1. Use `ToolSearch` to load `mcp__granola__query_granola_meetings`
67
+ 2. Call with user's query
68
+ 3. Preserve citation links in the response
69
+ 4. Present the response to the user
70
+
71
+ ### Quick summary (`/meeting summary {title}`)
72
+
73
+ 1. First, list recent meetings to find the meeting ID matching `{title}` (fuzzy match)
74
+ 2. Use `ToolSearch` to load `mcp__granola__get_meetings`
75
+ 3. Call with the meeting ID
76
+ 4. Present the structured summary to the user
77
+
78
+ ### Context-aware summary (`/meeting summarize {title}`)
79
+
80
+ 1. First, list recent meetings to find the meeting ID matching `{title}` (fuzzy match)
81
+ 2. Warn user: "This will load the full transcript into context. The meeting was ~{duration}. Proceed, or use `/meeting summary` for a quicker Granola summary?"
82
+ 3. If user confirms, use `ToolSearch` to load `mcp__granola__get_meeting_transcript`
83
+ 4. Call with the meeting ID
84
+ 5. Generate a summary that incorporates any loaded project context, codex knowledge, or brain docs
85
+ 6. Alternative: use the Task tool with a subagent to process the transcript in a separate context window, then return a summary
86
+
87
+ ### Decisions (`/meeting decisions`)
88
+
89
+ 1. Use `ToolSearch` to load `mcp__granola__query_granola_meetings`
90
+ 2. Call with query: "What decisions were made and what are the action items from recent meetings?"
91
+ 3. Preserve citation links in the response
92
+
93
+ ### Export (`/meeting export {title}`)
94
+
95
+ See `references/export-workflow.md` for the full procedure.
96
+
97
+ ## Error Handling
98
+
99
+ | Error | Action |
100
+ |-------|--------|
101
+ | Granola MCP not available | Suggest `/mcp` to connect Granola |
102
+ | No meetings found for time range | Suggest a different time range |
103
+ | Meeting title not found | Show recent meetings, ask user to pick |
104
+ | Transcript too large for context | Offer subagent approach or fall back to Granola summary |
105
+ | Codex not in current context (export) | Prompt for destination (see export workflow) |