@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
@@ -22,8 +22,10 @@
22
22
  "./src/tools/comments/skills/comments/SKILL.md",
23
23
  "./src/tools/droid/skills/droid/SKILL.md",
24
24
  "./src/tools/droid/skills/droid-bootstrap/SKILL.md",
25
+ "./src/tools/meeting/skills/meeting/SKILL.md",
25
26
  "./src/tools/plan/skills/plan/SKILL.md",
26
27
  "./src/tools/project/skills/project/SKILL.md",
28
+ "./src/tools/share/skills/share/SKILL.md",
27
29
  "./src/tools/status-update/skills/status-update/SKILL.md",
28
30
  "./src/tools/tech-design/skills/tech-design/SKILL.md",
29
31
  "./src/tools/wrapup/skills/wrapup/SKILL.md"
@@ -35,8 +37,10 @@
35
37
  "./src/tools/codex/commands/codex.md",
36
38
  "./src/tools/comments/commands/comments.md",
37
39
  "./src/tools/droid/commands/setup.md",
40
+ "./src/tools/meeting/commands/meeting.md",
38
41
  "./src/tools/plan/commands/plan.md",
39
42
  "./src/tools/project/commands/project.md",
43
+ "./src/tools/share/commands/share.md",
40
44
  "./src/tools/status-update/commands/status-update.md",
41
45
  "./src/tools/tech-design/commands/tech-design.md",
42
46
  "./src/tools/wrapup/commands/wrapup.md"
package/CHANGELOG.md CHANGED
@@ -1,5 +1,30 @@
1
1
  # @orderful/droid
2
2
 
3
+ ## 0.42.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#250](https://github.com/Orderful/droid/pull/250) [`e92ed07`](https://github.com/Orderful/droid/commit/e92ed07c9c0f311b6bc39f506c5c8d36b44d9115) Thanks [@frytyler](https://github.com/frytyler)! - Add GitHub CLI (gh) integration to TUI and CLI status
8
+
9
+ - [#252](https://github.com/Orderful/droid/pull/252) [`4e24ce4`](https://github.com/Orderful/droid/commit/4e24ce4423fb3abdc1dc4607d89f13c638422308) Thanks [@frytyler](https://github.com/frytyler)! - Add Granola integration to TUI and `droid integrations status`, with setup guide docs.
10
+
11
+ - [#253](https://github.com/Orderful/droid/pull/253) [`b7b2e85`](https://github.com/Orderful/droid/commit/b7b2e85c6d73a932a5da40feadbc55f45f9d9602) Thanks [@frytyler](https://github.com/frytyler)! - Add meeting tool for working with meeting notes, summaries, and transcripts via Granola MCP.
12
+
13
+ New `/meeting` skill and command with support for:
14
+ - Listing recent meetings by time range
15
+ - Natural language search across meeting content
16
+ - Quick summaries (Granola) and context-aware summaries (transcript + project context)
17
+ - Exporting meetings to codex with source-neutral frontmatter
18
+ - Pulling decisions and action items from recent meetings
19
+
20
+ Includes Granola as a new integration with TUI status display and setup guide.
21
+
22
+ ## 0.41.0
23
+
24
+ ### Minor Changes
25
+
26
+ - [#248](https://github.com/Orderful/droid/pull/248) [`1172a61`](https://github.com/Orderful/droid/commit/1172a61758b44483126918352a580d9722ffc5b0) Thanks [@frytyler](https://github.com/frytyler)! - Add /share tool for sharing content to Confluence (publish pages) and Slack (post summaries with free-form instructions), and add Atlassian to the TUI Integrations tab
27
+
3
28
  ## 0.40.1
4
29
 
5
30
  ### Patch Changes
package/dist/bin/droid.js CHANGED
@@ -2533,7 +2533,7 @@ async function updateCommand(tool, options) {
2533
2533
  import { render, Box as Box16, Text as Text17, useInput as useInput9, useApp as useApp2 } from "ink";
2534
2534
  import { useState as useState10, useEffect } from "react";
2535
2535
  import { readFileSync as readFileSync9 } from "fs";
2536
- import { spawnSync as spawnSync3 } from "child_process";
2536
+ import { spawnSync as spawnSync4 } from "child_process";
2537
2537
  import { join as join11, dirname as dirname7 } from "path";
2538
2538
  import { fileURLToPath as fileURLToPath5 } from "url";
2539
2539
 
@@ -2821,10 +2821,7 @@ function SettingsDetails({
2821
2821
  // src/commands/tui/components/IntegrationsDetails.tsx
2822
2822
  import { Box as Box6, Text as Text6 } from "ink";
2823
2823
  import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
2824
- function IntegrationsDetails({
2825
- isFocused,
2826
- selectedAction
2827
- }) {
2824
+ function SlackDetails({ isFocused, selectedAction }) {
2828
2825
  const hasToken = !!process.env.SLACK_USER_TOKEN;
2829
2826
  const hasClientId = !!process.env.SLACK_CLIENT_ID;
2830
2827
  const hasClientSecret = !!process.env.SLACK_CLIENT_SECRET;
@@ -2886,6 +2883,122 @@ function IntegrationsDetails({
2886
2883
  !isFocused && /* @__PURE__ */ jsx6(Box6, { marginTop: 2, children: /* @__PURE__ */ jsx6(Text6, { color: colors.textDim, children: "press enter for options" }) })
2887
2884
  ] });
2888
2885
  }
2886
+ function AtlassianDetails({ isFocused, selectedAction, connected }) {
2887
+ return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", paddingLeft: 2, flexGrow: 1, children: [
2888
+ /* @__PURE__ */ jsx6(Text6, { color: colors.text, bold: true, children: "Atlassian" }),
2889
+ /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", marginTop: 1, children: [
2890
+ /* @__PURE__ */ jsx6(Text6, { color: colors.textDim, bold: true, children: "MCP Server" }),
2891
+ /* @__PURE__ */ jsxs6(Text6, { children: [
2892
+ /* @__PURE__ */ jsx6(Text6, { color: colors.textDim, children: " Status " }),
2893
+ connected ? /* @__PURE__ */ jsx6(Text6, { color: colors.success, children: "Connected \u2713" }) : /* @__PURE__ */ jsx6(Text6, { color: "#fbbf24", children: "Not yet verified" })
2894
+ ] })
2895
+ ] }),
2896
+ /* @__PURE__ */ jsx6(Box6, { flexDirection: "column", marginTop: 1, children: /* @__PURE__ */ jsx6(Text6, { color: colors.textDim, children: connected ? "Confluence and Jira available via MCP" : "Use /share confluence to verify connection" }) }),
2897
+ isFocused && /* @__PURE__ */ jsx6(Box6, { marginTop: 2, flexDirection: "row", gap: 2, children: /* @__PURE__ */ jsxs6(
2898
+ Text6,
2899
+ {
2900
+ backgroundColor: selectedAction === 0 ? colors.primary : void 0,
2901
+ color: selectedAction === 0 ? "#ffffff" : colors.textDim,
2902
+ bold: selectedAction === 0,
2903
+ children: [
2904
+ " ",
2905
+ "Setup Guide",
2906
+ " "
2907
+ ]
2908
+ }
2909
+ ) }),
2910
+ isFocused && /* @__PURE__ */ jsx6(Box6, { marginTop: 1, children: /* @__PURE__ */ jsxs6(Text6, { color: colors.textDim, children: [
2911
+ "enter confirm ",
2912
+ "\xB7",
2913
+ " esc back"
2914
+ ] }) }),
2915
+ !isFocused && /* @__PURE__ */ jsx6(Box6, { marginTop: 2, children: /* @__PURE__ */ jsx6(Text6, { color: colors.textDim, children: "press enter for options" }) })
2916
+ ] });
2917
+ }
2918
+ function GithubDetails({ isFocused, selectedAction, connected }) {
2919
+ return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", paddingLeft: 2, flexGrow: 1, children: [
2920
+ /* @__PURE__ */ jsx6(Text6, { color: colors.text, bold: true, children: "GitHub CLI" }),
2921
+ /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", marginTop: 1, children: [
2922
+ /* @__PURE__ */ jsx6(Text6, { color: colors.textDim, bold: true, children: "Status" }),
2923
+ /* @__PURE__ */ jsxs6(Text6, { children: [
2924
+ /* @__PURE__ */ jsx6(Text6, { color: colors.textDim, children: " CLI " }),
2925
+ connected ? /* @__PURE__ */ jsx6(Text6, { color: colors.success, children: "Installed \u2713" }) : /* @__PURE__ */ jsx6(Text6, { color: "#fbbf24", children: "Not detected" })
2926
+ ] }),
2927
+ /* @__PURE__ */ jsxs6(Text6, { children: [
2928
+ /* @__PURE__ */ jsx6(Text6, { color: colors.textDim, children: " Auth " }),
2929
+ connected ? /* @__PURE__ */ jsx6(Text6, { color: colors.success, children: "Authenticated \u2713" }) : /* @__PURE__ */ jsx6(Text6, { color: "#fbbf24", children: "Not yet verified" })
2930
+ ] })
2931
+ ] }),
2932
+ /* @__PURE__ */ jsx6(Box6, { flexDirection: "column", marginTop: 1, children: /* @__PURE__ */ jsx6(Text6, { color: colors.textDim, children: connected ? "Used by codex, tech-design, code-review, share" : "Run `gh auth login` to connect" }) }),
2933
+ isFocused && /* @__PURE__ */ jsx6(Box6, { marginTop: 2, flexDirection: "row", gap: 2, children: /* @__PURE__ */ jsxs6(
2934
+ Text6,
2935
+ {
2936
+ backgroundColor: selectedAction === 0 ? colors.primary : void 0,
2937
+ color: selectedAction === 0 ? "#ffffff" : colors.textDim,
2938
+ bold: selectedAction === 0,
2939
+ children: [
2940
+ " ",
2941
+ "Setup Guide",
2942
+ " "
2943
+ ]
2944
+ }
2945
+ ) }),
2946
+ isFocused && /* @__PURE__ */ jsx6(Box6, { marginTop: 1, children: /* @__PURE__ */ jsxs6(Text6, { color: colors.textDim, children: [
2947
+ "enter confirm ",
2948
+ "\xB7",
2949
+ " esc back"
2950
+ ] }) }),
2951
+ !isFocused && /* @__PURE__ */ jsx6(Box6, { marginTop: 2, children: /* @__PURE__ */ jsx6(Text6, { color: colors.textDim, children: "press enter for options" }) })
2952
+ ] });
2953
+ }
2954
+ function GranolaDetails({ isFocused, selectedAction, connected }) {
2955
+ return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", paddingLeft: 2, flexGrow: 1, children: [
2956
+ /* @__PURE__ */ jsx6(Text6, { color: colors.text, bold: true, children: "Granola" }),
2957
+ /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", marginTop: 1, children: [
2958
+ /* @__PURE__ */ jsx6(Text6, { color: colors.textDim, bold: true, children: "MCP Server" }),
2959
+ /* @__PURE__ */ jsxs6(Text6, { children: [
2960
+ /* @__PURE__ */ jsx6(Text6, { color: colors.textDim, children: " Status " }),
2961
+ connected ? /* @__PURE__ */ jsx6(Text6, { color: colors.success, children: "Connected \u2713" }) : /* @__PURE__ */ jsx6(Text6, { color: "#fbbf24", children: "Not yet verified" })
2962
+ ] })
2963
+ ] }),
2964
+ /* @__PURE__ */ jsx6(Box6, { flexDirection: "column", marginTop: 1, children: /* @__PURE__ */ jsx6(Text6, { color: colors.textDim, children: connected ? "Meeting notes and transcripts available via MCP" : "Use /mcp to add the Granola server" }) }),
2965
+ isFocused && /* @__PURE__ */ jsx6(Box6, { marginTop: 2, flexDirection: "row", gap: 2, children: /* @__PURE__ */ jsxs6(
2966
+ Text6,
2967
+ {
2968
+ backgroundColor: selectedAction === 0 ? colors.primary : void 0,
2969
+ color: selectedAction === 0 ? "#ffffff" : colors.textDim,
2970
+ bold: selectedAction === 0,
2971
+ children: [
2972
+ " ",
2973
+ "Setup Guide",
2974
+ " "
2975
+ ]
2976
+ }
2977
+ ) }),
2978
+ isFocused && /* @__PURE__ */ jsx6(Box6, { marginTop: 1, children: /* @__PURE__ */ jsxs6(Text6, { color: colors.textDim, children: [
2979
+ "enter confirm ",
2980
+ "\xB7",
2981
+ " esc back"
2982
+ ] }) }),
2983
+ !isFocused && /* @__PURE__ */ jsx6(Box6, { marginTop: 2, children: /* @__PURE__ */ jsx6(Text6, { color: colors.textDim, children: "press enter for options" }) })
2984
+ ] });
2985
+ }
2986
+ function IntegrationsDetails({
2987
+ isFocused,
2988
+ selectedAction,
2989
+ integration
2990
+ }) {
2991
+ if (integration.id === "atlassian") {
2992
+ return /* @__PURE__ */ jsx6(AtlassianDetails, { isFocused, selectedAction, connected: integration.connected });
2993
+ }
2994
+ if (integration.id === "github") {
2995
+ return /* @__PURE__ */ jsx6(GithubDetails, { isFocused, selectedAction, connected: integration.connected });
2996
+ }
2997
+ if (integration.id === "granola") {
2998
+ return /* @__PURE__ */ jsx6(GranolaDetails, { isFocused, selectedAction, connected: integration.connected });
2999
+ }
3000
+ return /* @__PURE__ */ jsx6(SlackDetails, { isFocused, selectedAction });
3001
+ }
2889
3002
 
2890
3003
  // src/commands/tui/components/PlatformBadges.tsx
2891
3004
  import { Box as Box7, Text as Text7 } from "ink";
@@ -4243,10 +4356,30 @@ function useToolUpdates({ onUpdateComplete }) {
4243
4356
  };
4244
4357
  }
4245
4358
 
4359
+ // src/integrations/github/index.ts
4360
+ import { spawnSync as spawnSync3 } from "child_process";
4361
+ function checkGhAuth() {
4362
+ try {
4363
+ const result = spawnSync3("gh", ["auth", "status"], {
4364
+ stdio: "ignore",
4365
+ timeout: 5e3
4366
+ });
4367
+ return result.status === 0;
4368
+ } catch {
4369
+ return false;
4370
+ }
4371
+ }
4372
+
4246
4373
  // src/commands/tui.tsx
4247
4374
  import { Fragment as Fragment2, jsx as jsx17, jsxs as jsxs16 } from "react/jsx-runtime";
4248
4375
  var exitMessage = null;
4249
4376
  var exitCommand = null;
4377
+ var INTEGRATION_GUIDE_TITLES = {
4378
+ slack: "Slack Integration Setup",
4379
+ atlassian: "Atlassian Integration Setup",
4380
+ github: "GitHub CLI Setup",
4381
+ granola: "Granola Integration Setup"
4382
+ };
4250
4383
  var __tui_dirname = dirname7(fileURLToPath5(import.meta.url));
4251
4384
  var INTEGRATIONS_DIR = join11(__tui_dirname, "../integrations");
4252
4385
  function loadIntegrationReference(integration, filename) {
@@ -4342,6 +4475,20 @@ function App() {
4342
4475
  };
4343
4476
  const tools = getBundledTools();
4344
4477
  const skills = getBundledSkills();
4478
+ const integrations2 = [
4479
+ { id: "slack", name: "Slack", connected: !!process.env.SLACK_USER_TOKEN },
4480
+ { id: "atlassian", name: "Atlassian", connected: !!getConfigValue("integrations.atlassian.configured") },
4481
+ { id: "github", name: "GitHub", connected: !!getConfigValue("integrations.github.configured") },
4482
+ { id: "granola", name: "Granola", connected: !!getConfigValue("integrations.granola.configured") }
4483
+ ];
4484
+ useEffect(() => {
4485
+ if (!getConfigValue("integrations.github.configured")) {
4486
+ const isAuthed = checkGhAuth();
4487
+ if (isAuthed) {
4488
+ setConfigValue("integrations.github.configured", true);
4489
+ }
4490
+ }
4491
+ }, []);
4345
4492
  useInput9(
4346
4493
  (input, key) => {
4347
4494
  if (message) setMessage(null);
@@ -4375,7 +4522,7 @@ function App() {
4375
4522
  setSelectedAction(0);
4376
4523
  }
4377
4524
  if (key.downArrow) {
4378
- const maxIndex = activeTab === "tools" ? tools.length - 1 : activeTab === "integrations" ? 0 : 0;
4525
+ const maxIndex = activeTab === "tools" ? tools.length - 1 : activeTab === "integrations" ? integrations2.length - 1 : 0;
4379
4526
  setSelectedIndex((prev) => {
4380
4527
  const newIndex = Math.min(maxIndex, prev + 1);
4381
4528
  if (newIndex >= scrollOffset + MAX_VISIBLE_ITEMS) {
@@ -4400,12 +4547,22 @@ function App() {
4400
4547
  setSelectedAction(0);
4401
4548
  }
4402
4549
  if (activeTab === "integrations") {
4403
- const hasClientCreds = !!process.env.SLACK_CLIENT_ID && !!process.env.SLACK_CLIENT_SECRET;
4404
- const canRunSetup = hasClientCreds && !process.env.SLACK_USER_TOKEN;
4405
- const intActions = [
4406
- ...canRunSetup ? [{ id: "setup" }] : [],
4407
- { id: "guide" }
4408
- ];
4550
+ const currentIntegration = integrations2[selectedIndex];
4551
+ let intActions = [];
4552
+ if (currentIntegration?.id === "slack") {
4553
+ const hasClientCreds = !!process.env.SLACK_CLIENT_ID && !!process.env.SLACK_CLIENT_SECRET;
4554
+ const canRunSetup = hasClientCreds && !process.env.SLACK_USER_TOKEN;
4555
+ intActions = [
4556
+ ...canRunSetup ? [{ id: "setup" }] : [],
4557
+ { id: "guide" }
4558
+ ];
4559
+ } else if (currentIntegration?.id === "atlassian") {
4560
+ intActions = [{ id: "guide" }];
4561
+ } else if (currentIntegration?.id === "github") {
4562
+ intActions = [{ id: "guide" }];
4563
+ } else if (currentIntegration?.id === "granola") {
4564
+ intActions = [{ id: "guide" }];
4565
+ }
4409
4566
  const maxIntAction = intActions.length - 1;
4410
4567
  if (key.leftArrow) {
4411
4568
  setSelectedAction((prev) => Math.max(0, prev - 1));
@@ -4415,14 +4572,16 @@ function App() {
4415
4572
  }
4416
4573
  if (key.return) {
4417
4574
  const actionId = intActions[selectedAction]?.id;
4418
- if (actionId === "setup") {
4575
+ if (actionId === "setup" && currentIntegration?.id === "slack") {
4419
4576
  exitCommand = ["droid", "integrations", "setup", "slack"];
4420
4577
  exit();
4421
4578
  } else if (actionId === "guide") {
4422
- const content = loadIntegrationReference("slack", "setup.md");
4579
+ const integrationId = currentIntegration?.id ?? "slack";
4580
+ const guideTitle = INTEGRATION_GUIDE_TITLES[integrationId] ?? `${currentIntegration?.name} Setup`;
4581
+ const content = loadIntegrationReference(integrationId, "setup.md");
4423
4582
  if (content) {
4424
4583
  setPreviousView("detail");
4425
- setReadmeContent({ title: "Slack Integration Setup", content });
4584
+ setReadmeContent({ title: guideTitle, content });
4426
4585
  setView("readme");
4427
4586
  } else {
4428
4587
  setMessage({ text: "Could not load setup guide", type: "error" });
@@ -4702,11 +4861,14 @@ function App() {
4702
4861
  " tools total"
4703
4862
  ] }) })
4704
4863
  ] }),
4705
- activeTab === "integrations" && /* @__PURE__ */ jsx17(Box16, { paddingX: 1, children: /* @__PURE__ */ jsxs16(Text17, { children: [
4706
- selectedIndex === 0 ? /* @__PURE__ */ jsx17(Text17, { color: colors.primary, children: ">" }) : /* @__PURE__ */ jsx17(Text17, { children: " " }),
4707
- /* @__PURE__ */ jsx17(Text17, { children: " Slack" }),
4708
- process.env.SLACK_USER_TOKEN ? /* @__PURE__ */ jsx17(Text17, { color: colors.success, children: " \u2713" }) : /* @__PURE__ */ jsx17(Text17, { color: colors.textDim, children: " \u2717" })
4709
- ] }) }),
4864
+ activeTab === "integrations" && /* @__PURE__ */ jsx17(Box16, { flexDirection: "column", paddingX: 1, children: integrations2.map((integration, index) => /* @__PURE__ */ jsxs16(Text17, { children: [
4865
+ selectedIndex === index ? /* @__PURE__ */ jsx17(Text17, { color: colors.primary, children: ">" }) : /* @__PURE__ */ jsx17(Text17, { children: " " }),
4866
+ /* @__PURE__ */ jsxs16(Text17, { children: [
4867
+ " ",
4868
+ integration.name
4869
+ ] }),
4870
+ integration.connected ? /* @__PURE__ */ jsx17(Text17, { color: colors.success, children: " \u2713" }) : /* @__PURE__ */ jsx17(Text17, { color: colors.textDim, children: " \u2717" })
4871
+ ] }, integration.id)) }),
4710
4872
  activeTab === "settings" && /* @__PURE__ */ jsx17(Box16, { paddingX: 1, children: /* @__PURE__ */ jsx17(Text17, { color: colors.textDim, children: "View and edit config" }) })
4711
4873
  ] }),
4712
4874
  message && /* @__PURE__ */ jsx17(Box16, { paddingX: 1, marginTop: 1, children: /* @__PURE__ */ jsx17(
@@ -4728,11 +4890,12 @@ function App() {
4728
4890
  selectedAction
4729
4891
  }
4730
4892
  ),
4731
- activeTab === "integrations" && /* @__PURE__ */ jsx17(
4893
+ activeTab === "integrations" && integrations2[selectedIndex] && /* @__PURE__ */ jsx17(
4732
4894
  IntegrationsDetails,
4733
4895
  {
4734
4896
  isFocused: view === "detail",
4735
- selectedAction
4897
+ selectedAction,
4898
+ integration: integrations2[selectedIndex]
4736
4899
  }
4737
4900
  ),
4738
4901
  activeTab === "settings" && /* @__PURE__ */ jsx17(
@@ -4755,7 +4918,7 @@ async function tuiCommand() {
4755
4918
  if (exitCommand) {
4756
4919
  const [cmd, ...args] = exitCommand;
4757
4920
  exitCommand = null;
4758
- spawnSync3(cmd, args, { stdio: "inherit" });
4921
+ spawnSync4(cmd, args, { stdio: "inherit" });
4759
4922
  return;
4760
4923
  }
4761
4924
  if (exitMessage) {
@@ -5341,6 +5504,36 @@ async function integrationsStatusCommand() {
5341
5504
  console.log(chalk11.yellow(" Status: not configured"));
5342
5505
  }
5343
5506
  console.log("");
5507
+ console.log(chalk11.bold(" Atlassian"));
5508
+ const atlassianConfigured = getConfigValue("integrations.atlassian.configured");
5509
+ if (atlassianConfigured) {
5510
+ console.log(chalk11.green(" MCP: Connected"));
5511
+ console.log(chalk11.green(" Status: configured"));
5512
+ } else {
5513
+ console.log(chalk11.yellow(" MCP: Not yet verified"));
5514
+ console.log(chalk11.gray(" Use /share confluence to verify, or see setup guide"));
5515
+ }
5516
+ console.log("");
5517
+ console.log(chalk11.bold(" GitHub"));
5518
+ const githubConfigured = getConfigValue("integrations.github.configured");
5519
+ if (githubConfigured) {
5520
+ console.log(chalk11.green(" CLI: Installed"));
5521
+ console.log(chalk11.green(" Status: configured"));
5522
+ } else {
5523
+ console.log(chalk11.yellow(" CLI: Not verified"));
5524
+ console.log(chalk11.gray(" Run: gh auth login"));
5525
+ }
5526
+ console.log("");
5527
+ console.log(chalk11.bold(" Granola"));
5528
+ const granolaConfigured = getConfigValue("integrations.granola.configured");
5529
+ if (granolaConfigured) {
5530
+ console.log(chalk11.green(" MCP: Connected"));
5531
+ console.log(chalk11.green(" Status: configured"));
5532
+ } else {
5533
+ console.log(chalk11.yellow(" MCP: Not yet verified"));
5534
+ console.log(chalk11.gray(" Use /mcp to add the Granola server, then verify with a meeting query"));
5535
+ }
5536
+ console.log("");
5344
5537
  }
5345
5538
  async function slackPostCommand(options) {
5346
5539
  let input;
@@ -1 +1 @@
1
- {"version":3,"file":"integrations.d.ts","sourceRoot":"","sources":["../../src/commands/integrations.ts"],"names":[],"mappings":"AAoNA,wBAAsB,6BAA6B,IAAI,OAAO,CAAC,IAAI,CAAC,CAuInE;AAED,wBAAsB,yBAAyB,IAAI,OAAO,CAAC,IAAI,CAAC,CAqC/D;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,CAAC,OAAO,EAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAsCnF"}
1
+ {"version":3,"file":"integrations.d.ts","sourceRoot":"","sources":["../../src/commands/integrations.ts"],"names":[],"mappings":"AAoNA,wBAAsB,6BAA6B,IAAI,OAAO,CAAC,IAAI,CAAC,CAuInE;AAED,wBAAsB,yBAAyB,IAAI,OAAO,CAAC,IAAI,CAAC,CAkF/D;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,CAAC,OAAO,EAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAsCnF"}
@@ -1,6 +1,12 @@
1
+ export interface Integration {
2
+ id: string;
3
+ name: string;
4
+ connected: boolean;
5
+ }
1
6
  export interface IntegrationsDetailsProps {
2
7
  isFocused: boolean;
3
8
  selectedAction: number;
9
+ integration: Integration;
4
10
  }
5
- export declare function IntegrationsDetails({ isFocused, selectedAction, }: IntegrationsDetailsProps): import("react/jsx-runtime").JSX.Element;
11
+ export declare function IntegrationsDetails({ isFocused, selectedAction, integration, }: IntegrationsDetailsProps): import("react/jsx-runtime").JSX.Element;
6
12
  //# sourceMappingURL=IntegrationsDetails.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"IntegrationsDetails.d.ts","sourceRoot":"","sources":["../../../../src/commands/tui/components/IntegrationsDetails.tsx"],"names":[],"mappings":"AAGA,MAAM,WAAW,wBAAwB;IACvC,SAAS,EAAE,OAAO,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,wBAAgB,mBAAmB,CAAC,EAClC,SAAS,EACT,cAAc,GACf,EAAE,wBAAwB,2CAmF1B"}
1
+ {"version":3,"file":"IntegrationsDetails.d.ts","sourceRoot":"","sources":["../../../../src/commands/tui/components/IntegrationsDetails.tsx"],"names":[],"mappings":"AAGA,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,wBAAwB;IACvC,SAAS,EAAE,OAAO,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,WAAW,CAAC;CAC1B;AAmPD,wBAAgB,mBAAmB,CAAC,EAClC,SAAS,EACT,cAAc,EACd,WAAW,GACZ,EAAE,wBAAwB,2CAe1B"}
@@ -1 +1 @@
1
- {"version":3,"file":"tui.d.ts","sourceRoot":"","sources":["../../src/commands/tui.tsx"],"names":[],"mappings":"AAuqBA,wBAAsB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAyBhD"}
1
+ {"version":3,"file":"tui.d.ts","sourceRoot":"","sources":["../../src/commands/tui.tsx"],"names":[],"mappings":"AAktBA,wBAAsB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAyBhD"}
@@ -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,6 @@
1
+ /**
2
+ * Check if the GitHub CLI (gh) is installed and authenticated.
3
+ * Returns true if `gh auth status` exits with code 0.
4
+ */
5
+ export declare function checkGhAuth(): boolean;
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/integrations/github/index.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,wBAAgB,WAAW,IAAI,OAAO,CAUrC"}
@@ -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 |
@@ -44,8 +44,20 @@ export interface IntegrationSlackConfig {
44
44
  configured?: boolean;
45
45
  crosspost_channel?: string;
46
46
  }
47
+ export interface IntegrationAtlassianConfig {
48
+ configured?: boolean;
49
+ }
50
+ export interface IntegrationGithubConfig {
51
+ configured?: boolean;
52
+ }
53
+ export interface IntegrationGranolaConfig {
54
+ configured?: boolean;
55
+ }
47
56
  export interface IntegrationsConfig {
48
57
  slack?: IntegrationSlackConfig;
58
+ atlassian?: IntegrationAtlassianConfig;
59
+ github?: IntegrationGithubConfig;
60
+ granola?: IntegrationGranolaConfig;
49
61
  }
50
62
  export interface DroidConfig {
51
63
  platform: Platform;