@orderful/droid 0.40.1 → 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 (42) hide show
  1. package/.claude-plugin/plugin.json +4 -0
  2. package/CHANGELOG.md +25 -0
  3. package/dist/bin/droid.js +216 -23
  4. package/dist/commands/integrations.d.ts.map +1 -1
  5. package/dist/commands/tui/components/IntegrationsDetails.d.ts +7 -1
  6. package/dist/commands/tui/components/IntegrationsDetails.d.ts.map +1 -1
  7. package/dist/commands/tui.d.ts.map +1 -1
  8. package/dist/integrations/atlassian/references/setup.md +59 -0
  9. package/dist/integrations/github/index.d.ts +6 -0
  10. package/dist/integrations/github/index.d.ts.map +1 -0
  11. package/dist/integrations/github/index.ts +17 -0
  12. package/dist/integrations/github/references/setup.md +61 -0
  13. package/dist/integrations/granola/references/setup.md +54 -0
  14. package/dist/lib/types.d.ts +12 -0
  15. package/dist/lib/types.d.ts.map +1 -1
  16. package/dist/tools/meeting/.claude-plugin/plugin.json +22 -0
  17. package/dist/tools/meeting/TOOL.yaml +15 -0
  18. package/dist/tools/meeting/commands/meeting.md +35 -0
  19. package/dist/tools/meeting/skills/meeting/SKILL.md +105 -0
  20. package/dist/tools/meeting/skills/meeting/references/export-workflow.md +87 -0
  21. package/dist/tools/share/.claude-plugin/plugin.json +22 -0
  22. package/dist/tools/share/TOOL.yaml +17 -0
  23. package/dist/tools/share/commands/share.md +32 -0
  24. package/dist/tools/share/skills/share/SKILL.md +211 -0
  25. package/package.json +1 -1
  26. package/src/commands/integrations.ts +45 -0
  27. package/src/commands/tui/components/IntegrationsDetails.tsx +185 -4
  28. package/src/commands/tui.tsx +63 -20
  29. package/src/integrations/atlassian/references/setup.md +59 -0
  30. package/src/integrations/github/index.ts +17 -0
  31. package/src/integrations/github/references/setup.md +61 -0
  32. package/src/integrations/granola/references/setup.md +54 -0
  33. package/src/lib/types.ts +15 -0
  34. package/src/tools/meeting/.claude-plugin/plugin.json +22 -0
  35. package/src/tools/meeting/TOOL.yaml +15 -0
  36. package/src/tools/meeting/commands/meeting.md +35 -0
  37. package/src/tools/meeting/skills/meeting/SKILL.md +105 -0
  38. package/src/tools/meeting/skills/meeting/references/export-workflow.md +87 -0
  39. package/src/tools/share/.claude-plugin/plugin.json +22 -0
  40. package/src/tools/share/TOOL.yaml +17 -0
  41. package/src/tools/share/commands/share.md +32 -0
  42. package/src/tools/share/skills/share/SKILL.md +211 -0
@@ -1,15 +1,19 @@
1
1
  import { Box, Text } from 'ink';
2
2
  import { colors } from '../constants';
3
3
 
4
+ export interface Integration {
5
+ id: string;
6
+ name: string;
7
+ connected: boolean;
8
+ }
9
+
4
10
  export interface IntegrationsDetailsProps {
5
11
  isFocused: boolean;
6
12
  selectedAction: number;
13
+ integration: Integration;
7
14
  }
8
15
 
9
- export function IntegrationsDetails({
10
- isFocused,
11
- selectedAction,
12
- }: IntegrationsDetailsProps) {
16
+ function SlackDetails({ isFocused, selectedAction }: { isFocused: boolean; selectedAction: number }) {
13
17
  const hasToken = !!process.env.SLACK_USER_TOKEN;
14
18
  const hasClientId = !!process.env.SLACK_CLIENT_ID;
15
19
  const hasClientSecret = !!process.env.SLACK_CLIENT_SECRET;
@@ -93,3 +97,180 @@ export function IntegrationsDetails({
93
97
  </Box>
94
98
  );
95
99
  }
100
+
101
+ function AtlassianDetails({ isFocused, selectedAction, connected }: { isFocused: boolean; selectedAction: number; connected: boolean }) {
102
+ return (
103
+ <Box flexDirection="column" paddingLeft={2} flexGrow={1}>
104
+ <Text color={colors.text} bold>Atlassian</Text>
105
+
106
+ <Box flexDirection="column" marginTop={1}>
107
+ <Text color={colors.textDim} bold>MCP Server</Text>
108
+ <Text>
109
+ <Text color={colors.textDim}> Status </Text>
110
+ {connected
111
+ ? <Text color={colors.success}>Connected ✓</Text>
112
+ : <Text color="#fbbf24">Not yet verified</Text>}
113
+ </Text>
114
+ </Box>
115
+
116
+ <Box flexDirection="column" marginTop={1}>
117
+ <Text color={colors.textDim}>
118
+ {connected
119
+ ? 'Confluence and Jira available via MCP'
120
+ : 'Use /share confluence to verify connection'}
121
+ </Text>
122
+ </Box>
123
+
124
+ {isFocused && (
125
+ <Box marginTop={2} flexDirection="row" gap={2}>
126
+ <Text
127
+ backgroundColor={selectedAction === 0 ? colors.primary : undefined}
128
+ color={selectedAction === 0 ? '#ffffff' : colors.textDim}
129
+ bold={selectedAction === 0}
130
+ >
131
+ {' '}Setup Guide{' '}
132
+ </Text>
133
+ </Box>
134
+ )}
135
+
136
+ {isFocused && (
137
+ <Box marginTop={1}>
138
+ <Text color={colors.textDim}>enter confirm {'·'} esc back</Text>
139
+ </Box>
140
+ )}
141
+
142
+ {!isFocused && (
143
+ <Box marginTop={2}>
144
+ <Text color={colors.textDim}>press enter for options</Text>
145
+ </Box>
146
+ )}
147
+ </Box>
148
+ );
149
+ }
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
+
257
+ export function IntegrationsDetails({
258
+ isFocused,
259
+ selectedAction,
260
+ integration,
261
+ }: IntegrationsDetailsProps) {
262
+ if (integration.id === 'atlassian') {
263
+ return <AtlassianDetails isFocused={isFocused} selectedAction={selectedAction} connected={integration.connected} />;
264
+ }
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
+
274
+ // Default: Slack
275
+ return <SlackDetails isFocused={isFocused} selectedAction={selectedAction} />;
276
+ }
@@ -15,7 +15,7 @@ import {
15
15
  isToolInstalled,
16
16
  getToolUpdateStatus,
17
17
  } from '../lib/tools';
18
- import { configExists, loadConfig, getAutoUpdateConfig } 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');
@@ -177,6 +185,24 @@ function App() {
177
185
  // Keep skills for configure view (tools configure via their primary skill)
178
186
  const skills = getBundledSkills();
179
187
 
188
+ // Dynamic integrations list
189
+ const integrations = [
190
+ { id: 'slack', name: 'Slack', connected: !!process.env.SLACK_USER_TOKEN },
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') },
194
+ ];
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
+
180
206
  useInput(
181
207
  (input, key) => {
182
208
  if (message) setMessage(null);
@@ -213,7 +239,7 @@ function App() {
213
239
  setSelectedAction(0);
214
240
  }
215
241
  if (key.downArrow) {
216
- const maxIndex = activeTab === 'tools' ? tools.length - 1 : activeTab === 'integrations' ? 0 : 0;
242
+ const maxIndex = activeTab === 'tools' ? tools.length - 1 : activeTab === 'integrations' ? integrations.length - 1 : 0;
217
243
  setSelectedIndex((prev) => {
218
244
  const newIndex = Math.min(maxIndex, prev + 1);
219
245
  // Scroll down if needed
@@ -239,12 +265,24 @@ function App() {
239
265
  setSelectedAction(0);
240
266
  }
241
267
  if (activeTab === 'integrations') {
242
- const hasClientCreds = !!process.env.SLACK_CLIENT_ID && !!process.env.SLACK_CLIENT_SECRET;
243
- const canRunSetup = hasClientCreds && !process.env.SLACK_USER_TOKEN;
244
- const intActions = [
245
- ...(canRunSetup ? [{ id: 'setup' }] : []),
246
- { id: 'guide' },
247
- ];
268
+ const currentIntegration = integrations[selectedIndex];
269
+ let intActions: { id: string }[] = [];
270
+
271
+ if (currentIntegration?.id === 'slack') {
272
+ const hasClientCreds = !!process.env.SLACK_CLIENT_ID && !!process.env.SLACK_CLIENT_SECRET;
273
+ const canRunSetup = hasClientCreds && !process.env.SLACK_USER_TOKEN;
274
+ intActions = [
275
+ ...(canRunSetup ? [{ id: 'setup' }] : []),
276
+ { id: 'guide' },
277
+ ];
278
+ } else if (currentIntegration?.id === 'atlassian') {
279
+ intActions = [{ id: 'guide' }];
280
+ } else if (currentIntegration?.id === 'github') {
281
+ intActions = [{ id: 'guide' }];
282
+ } else if (currentIntegration?.id === 'granola') {
283
+ intActions = [{ id: 'guide' }];
284
+ }
285
+
248
286
  const maxIntAction = intActions.length - 1;
249
287
  if (key.leftArrow) {
250
288
  setSelectedAction((prev) => Math.max(0, prev - 1));
@@ -254,14 +292,16 @@ function App() {
254
292
  }
255
293
  if (key.return) {
256
294
  const actionId = intActions[selectedAction]?.id;
257
- if (actionId === 'setup') {
295
+ if (actionId === 'setup' && currentIntegration?.id === 'slack') {
258
296
  exitCommand = ['droid', 'integrations', 'setup', 'slack'];
259
297
  exit();
260
298
  } else if (actionId === 'guide') {
261
- const content = loadIntegrationReference('slack', 'setup.md');
299
+ const integrationId = currentIntegration?.id ?? 'slack';
300
+ const guideTitle = INTEGRATION_GUIDE_TITLES[integrationId] ?? `${currentIntegration?.name} Setup`;
301
+ const content = loadIntegrationReference(integrationId, 'setup.md');
262
302
  if (content) {
263
303
  setPreviousView('detail');
264
- setReadmeContent({ title: 'Slack Integration Setup', content });
304
+ setReadmeContent({ title: guideTitle, content });
265
305
  setView('readme');
266
306
  } else {
267
307
  setMessage({ text: 'Could not load setup guide', type: 'error' });
@@ -612,14 +652,16 @@ function App() {
612
652
  )}
613
653
 
614
654
  {activeTab === 'integrations' && (
615
- <Box paddingX={1}>
616
- <Text>
617
- {selectedIndex === 0 ? <Text color={colors.primary}>{'>'}</Text> : <Text> </Text>}
618
- <Text> Slack</Text>
619
- {process.env.SLACK_USER_TOKEN
620
- ? <Text color={colors.success}> ✓</Text>
621
- : <Text color={colors.textDim}> ✗</Text>}
622
- </Text>
655
+ <Box flexDirection="column" paddingX={1}>
656
+ {integrations.map((integration, index) => (
657
+ <Text key={integration.id}>
658
+ {selectedIndex === index ? <Text color={colors.primary}>{'>'}</Text> : <Text> </Text>}
659
+ <Text> {integration.name}</Text>
660
+ {integration.connected
661
+ ? <Text color={colors.success}> ✓</Text>
662
+ : <Text color={colors.textDim}> ✗</Text>}
663
+ </Text>
664
+ ))}
623
665
  </Box>
624
666
  )}
625
667
 
@@ -658,10 +700,11 @@ function App() {
658
700
  />
659
701
  )}
660
702
 
661
- {activeTab === 'integrations' && (
703
+ {activeTab === 'integrations' && integrations[selectedIndex] && (
662
704
  <IntegrationsDetails
663
705
  isFocused={view === 'detail'}
664
706
  selectedAction={selectedAction}
707
+ integration={integrations[selectedIndex]}
665
708
  />
666
709
  )}
667
710
 
@@ -0,0 +1,59 @@
1
+ # Atlassian Integration Setup
2
+
3
+ ## Overview
4
+
5
+ The Atlassian integration connects droid to Confluence (and Jira) via the official Atlassian MCP server. This enables the `/share` tool to publish markdown files directly to Confluence pages.
6
+
7
+ ## Setup
8
+
9
+ ### 1. Add the Atlassian MCP Server
10
+
11
+ In Claude Code, run:
12
+
13
+ ```
14
+ /mcp
15
+ ```
16
+
17
+ Select **Atlassian** from the available MCP servers and follow the authentication prompts. This connects your Atlassian account (Confluence + Jira) to Claude Code.
18
+
19
+ ### 2. Verify Connection
20
+
21
+ Run `/share confluence` in a Claude Code session. If the connection is working, you'll see a list of available Confluence spaces.
22
+
23
+ Alternatively, the TUI Integrations tab will show `Atlassian ✓` once the MCP has been used successfully.
24
+
25
+ ## How It Works
26
+
27
+ Unlike Slack (which uses environment variables and OAuth), Atlassian uses Claude Code's built-in MCP server system:
28
+
29
+ | Aspect | Slack | Atlassian |
30
+ |--------|-------|-----------|
31
+ | Auth method | OAuth + env vars | MCP server (managed by Claude Code) |
32
+ | Setup | `droid integrations setup slack` | `/mcp` in Claude Code |
33
+ | API access | `@slack/web-api` SDK | MCP tool calls (`mcp__claude_ai_Atlassian__*`) |
34
+ | Config flag | `integrations.slack.configured` | `integrations.atlassian.configured` |
35
+
36
+ The `configured` flag is set automatically on first successful MCP call — no manual configuration needed.
37
+
38
+ ## Troubleshooting
39
+
40
+ | Issue | Resolution |
41
+ |-------|------------|
42
+ | MCP not available | Run `/mcp` in Claude Code to add the Atlassian server |
43
+ | No Confluence spaces listed | Check your Atlassian account has Confluence access |
44
+ | Permission errors | Verify your account has edit permissions in the target space |
45
+ | "Not configured" in TUI | Use `/share confluence` once — the flag is set on first success |
46
+
47
+ ## Usage
48
+
49
+ Once connected, use the `/share` command:
50
+
51
+ ```
52
+ /share confluence # Interactive - prompts for file
53
+ /share confluence path/to/file.md # Share specific file
54
+ ```
55
+
56
+ The share skill handles:
57
+ - Choosing a Confluence space and parent page
58
+ - Creating new pages or updating existing ones
59
+ - Storing the page ID in the file's frontmatter for future updates
@@ -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
@@ -63,8 +63,23 @@ export interface IntegrationSlackConfig {
63
63
  crosspost_channel?: string;
64
64
  }
65
65
 
66
+ export interface IntegrationAtlassianConfig {
67
+ configured?: boolean;
68
+ }
69
+
70
+ export interface IntegrationGithubConfig {
71
+ configured?: boolean;
72
+ }
73
+
74
+ export interface IntegrationGranolaConfig {
75
+ configured?: boolean;
76
+ }
77
+
66
78
  export interface IntegrationsConfig {
67
79
  slack?: IntegrationSlackConfig;
80
+ atlassian?: IntegrationAtlassianConfig;
81
+ github?: IntegrationGithubConfig;
82
+ granola?: IntegrationGranolaConfig;
68
83
  }
69
84
 
70
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.