@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.
- package/.claude-plugin/plugin.json +4 -0
- package/CHANGELOG.md +25 -0
- package/dist/bin/droid.js +216 -23
- package/dist/commands/integrations.d.ts.map +1 -1
- package/dist/commands/tui/components/IntegrationsDetails.d.ts +7 -1
- package/dist/commands/tui/components/IntegrationsDetails.d.ts.map +1 -1
- package/dist/commands/tui.d.ts.map +1 -1
- package/dist/integrations/atlassian/references/setup.md +59 -0
- package/dist/integrations/github/index.d.ts +6 -0
- package/dist/integrations/github/index.d.ts.map +1 -0
- package/dist/integrations/github/index.ts +17 -0
- package/dist/integrations/github/references/setup.md +61 -0
- package/dist/integrations/granola/references/setup.md +54 -0
- package/dist/lib/types.d.ts +12 -0
- package/dist/lib/types.d.ts.map +1 -1
- package/dist/tools/meeting/.claude-plugin/plugin.json +22 -0
- package/dist/tools/meeting/TOOL.yaml +15 -0
- package/dist/tools/meeting/commands/meeting.md +35 -0
- package/dist/tools/meeting/skills/meeting/SKILL.md +105 -0
- package/dist/tools/meeting/skills/meeting/references/export-workflow.md +87 -0
- package/dist/tools/share/.claude-plugin/plugin.json +22 -0
- package/dist/tools/share/TOOL.yaml +17 -0
- package/dist/tools/share/commands/share.md +32 -0
- package/dist/tools/share/skills/share/SKILL.md +211 -0
- package/package.json +1 -1
- package/src/commands/integrations.ts +45 -0
- package/src/commands/tui/components/IntegrationsDetails.tsx +185 -4
- package/src/commands/tui.tsx +63 -20
- package/src/integrations/atlassian/references/setup.md +59 -0
- package/src/integrations/github/index.ts +17 -0
- package/src/integrations/github/references/setup.md +61 -0
- package/src/integrations/granola/references/setup.md +54 -0
- package/src/lib/types.ts +15 -0
- package/src/tools/meeting/.claude-plugin/plugin.json +22 -0
- package/src/tools/meeting/TOOL.yaml +15 -0
- package/src/tools/meeting/commands/meeting.md +35 -0
- package/src/tools/meeting/skills/meeting/SKILL.md +105 -0
- package/src/tools/meeting/skills/meeting/references/export-workflow.md +87 -0
- package/src/tools/share/.claude-plugin/plugin.json +22 -0
- package/src/tools/share/TOOL.yaml +17 -0
- package/src/tools/share/commands/share.md +32 -0
- 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
|
-
|
|
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
|
+
}
|
package/src/commands/tui.tsx
CHANGED
|
@@ -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' ?
|
|
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
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
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
|
|
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:
|
|
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
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
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.
|