@orderful/droid 0.40.1 → 0.41.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 +2 -0
- package/CHANGELOG.md +6 -0
- package/dist/bin/droid.js +86 -21
- 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/lib/types.d.ts +4 -0
- package/dist/lib/types.d.ts.map +1 -1
- 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 +15 -0
- package/src/commands/tui/components/IntegrationsDetails.tsx +71 -4
- package/src/commands/tui.tsx +41 -20
- package/src/integrations/atlassian/references/setup.md +59 -0
- package/src/lib/types.ts +5 -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
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
"./src/tools/droid/skills/droid-bootstrap/SKILL.md",
|
|
25
25
|
"./src/tools/plan/skills/plan/SKILL.md",
|
|
26
26
|
"./src/tools/project/skills/project/SKILL.md",
|
|
27
|
+
"./src/tools/share/skills/share/SKILL.md",
|
|
27
28
|
"./src/tools/status-update/skills/status-update/SKILL.md",
|
|
28
29
|
"./src/tools/tech-design/skills/tech-design/SKILL.md",
|
|
29
30
|
"./src/tools/wrapup/skills/wrapup/SKILL.md"
|
|
@@ -37,6 +38,7 @@
|
|
|
37
38
|
"./src/tools/droid/commands/setup.md",
|
|
38
39
|
"./src/tools/plan/commands/plan.md",
|
|
39
40
|
"./src/tools/project/commands/project.md",
|
|
41
|
+
"./src/tools/share/commands/share.md",
|
|
40
42
|
"./src/tools/status-update/commands/status-update.md",
|
|
41
43
|
"./src/tools/tech-design/commands/tech-design.md",
|
|
42
44
|
"./src/tools/wrapup/commands/wrapup.md"
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# @orderful/droid
|
|
2
2
|
|
|
3
|
+
## 0.41.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#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
|
|
8
|
+
|
|
3
9
|
## 0.40.1
|
|
4
10
|
|
|
5
11
|
### Patch Changes
|
package/dist/bin/droid.js
CHANGED
|
@@ -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
|
|
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,48 @@ 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 IntegrationsDetails({
|
|
2919
|
+
isFocused,
|
|
2920
|
+
selectedAction,
|
|
2921
|
+
integration
|
|
2922
|
+
}) {
|
|
2923
|
+
if (integration.id === "atlassian") {
|
|
2924
|
+
return /* @__PURE__ */ jsx6(AtlassianDetails, { isFocused, selectedAction, connected: integration.connected });
|
|
2925
|
+
}
|
|
2926
|
+
return /* @__PURE__ */ jsx6(SlackDetails, { isFocused, selectedAction });
|
|
2927
|
+
}
|
|
2889
2928
|
|
|
2890
2929
|
// src/commands/tui/components/PlatformBadges.tsx
|
|
2891
2930
|
import { Box as Box7, Text as Text7 } from "ink";
|
|
@@ -4342,6 +4381,10 @@ function App() {
|
|
|
4342
4381
|
};
|
|
4343
4382
|
const tools = getBundledTools();
|
|
4344
4383
|
const skills = getBundledSkills();
|
|
4384
|
+
const integrations2 = [
|
|
4385
|
+
{ id: "slack", name: "Slack", connected: !!process.env.SLACK_USER_TOKEN },
|
|
4386
|
+
{ id: "atlassian", name: "Atlassian", connected: !!getConfigValue("integrations.atlassian.configured") }
|
|
4387
|
+
];
|
|
4345
4388
|
useInput9(
|
|
4346
4389
|
(input, key) => {
|
|
4347
4390
|
if (message) setMessage(null);
|
|
@@ -4375,7 +4418,7 @@ function App() {
|
|
|
4375
4418
|
setSelectedAction(0);
|
|
4376
4419
|
}
|
|
4377
4420
|
if (key.downArrow) {
|
|
4378
|
-
const maxIndex = activeTab === "tools" ? tools.length - 1 : activeTab === "integrations" ?
|
|
4421
|
+
const maxIndex = activeTab === "tools" ? tools.length - 1 : activeTab === "integrations" ? integrations2.length - 1 : 0;
|
|
4379
4422
|
setSelectedIndex((prev) => {
|
|
4380
4423
|
const newIndex = Math.min(maxIndex, prev + 1);
|
|
4381
4424
|
if (newIndex >= scrollOffset + MAX_VISIBLE_ITEMS) {
|
|
@@ -4400,12 +4443,18 @@ function App() {
|
|
|
4400
4443
|
setSelectedAction(0);
|
|
4401
4444
|
}
|
|
4402
4445
|
if (activeTab === "integrations") {
|
|
4403
|
-
const
|
|
4404
|
-
|
|
4405
|
-
|
|
4406
|
-
|
|
4407
|
-
|
|
4408
|
-
|
|
4446
|
+
const currentIntegration = integrations2[selectedIndex];
|
|
4447
|
+
let intActions = [];
|
|
4448
|
+
if (currentIntegration?.id === "slack") {
|
|
4449
|
+
const hasClientCreds = !!process.env.SLACK_CLIENT_ID && !!process.env.SLACK_CLIENT_SECRET;
|
|
4450
|
+
const canRunSetup = hasClientCreds && !process.env.SLACK_USER_TOKEN;
|
|
4451
|
+
intActions = [
|
|
4452
|
+
...canRunSetup ? [{ id: "setup" }] : [],
|
|
4453
|
+
{ id: "guide" }
|
|
4454
|
+
];
|
|
4455
|
+
} else if (currentIntegration?.id === "atlassian") {
|
|
4456
|
+
intActions = [{ id: "guide" }];
|
|
4457
|
+
}
|
|
4409
4458
|
const maxIntAction = intActions.length - 1;
|
|
4410
4459
|
if (key.leftArrow) {
|
|
4411
4460
|
setSelectedAction((prev) => Math.max(0, prev - 1));
|
|
@@ -4415,14 +4464,16 @@ function App() {
|
|
|
4415
4464
|
}
|
|
4416
4465
|
if (key.return) {
|
|
4417
4466
|
const actionId = intActions[selectedAction]?.id;
|
|
4418
|
-
if (actionId === "setup") {
|
|
4467
|
+
if (actionId === "setup" && currentIntegration?.id === "slack") {
|
|
4419
4468
|
exitCommand = ["droid", "integrations", "setup", "slack"];
|
|
4420
4469
|
exit();
|
|
4421
4470
|
} else if (actionId === "guide") {
|
|
4422
|
-
const
|
|
4471
|
+
const integrationId = currentIntegration?.id ?? "slack";
|
|
4472
|
+
const guideTitle = currentIntegration?.id === "atlassian" ? "Atlassian Integration Setup" : "Slack Integration Setup";
|
|
4473
|
+
const content = loadIntegrationReference(integrationId, "setup.md");
|
|
4423
4474
|
if (content) {
|
|
4424
4475
|
setPreviousView("detail");
|
|
4425
|
-
setReadmeContent({ title:
|
|
4476
|
+
setReadmeContent({ title: guideTitle, content });
|
|
4426
4477
|
setView("readme");
|
|
4427
4478
|
} else {
|
|
4428
4479
|
setMessage({ text: "Could not load setup guide", type: "error" });
|
|
@@ -4702,11 +4753,14 @@ function App() {
|
|
|
4702
4753
|
" tools total"
|
|
4703
4754
|
] }) })
|
|
4704
4755
|
] }),
|
|
4705
|
-
activeTab === "integrations" && /* @__PURE__ */ jsx17(Box16, { paddingX: 1, children: /* @__PURE__ */ jsxs16(Text17, { children: [
|
|
4706
|
-
selectedIndex ===
|
|
4707
|
-
/* @__PURE__ */
|
|
4708
|
-
|
|
4709
|
-
|
|
4756
|
+
activeTab === "integrations" && /* @__PURE__ */ jsx17(Box16, { flexDirection: "column", paddingX: 1, children: integrations2.map((integration, index) => /* @__PURE__ */ jsxs16(Text17, { children: [
|
|
4757
|
+
selectedIndex === index ? /* @__PURE__ */ jsx17(Text17, { color: colors.primary, children: ">" }) : /* @__PURE__ */ jsx17(Text17, { children: " " }),
|
|
4758
|
+
/* @__PURE__ */ jsxs16(Text17, { children: [
|
|
4759
|
+
" ",
|
|
4760
|
+
integration.name
|
|
4761
|
+
] }),
|
|
4762
|
+
integration.connected ? /* @__PURE__ */ jsx17(Text17, { color: colors.success, children: " \u2713" }) : /* @__PURE__ */ jsx17(Text17, { color: colors.textDim, children: " \u2717" })
|
|
4763
|
+
] }, integration.id)) }),
|
|
4710
4764
|
activeTab === "settings" && /* @__PURE__ */ jsx17(Box16, { paddingX: 1, children: /* @__PURE__ */ jsx17(Text17, { color: colors.textDim, children: "View and edit config" }) })
|
|
4711
4765
|
] }),
|
|
4712
4766
|
message && /* @__PURE__ */ jsx17(Box16, { paddingX: 1, marginTop: 1, children: /* @__PURE__ */ jsx17(
|
|
@@ -4728,11 +4782,12 @@ function App() {
|
|
|
4728
4782
|
selectedAction
|
|
4729
4783
|
}
|
|
4730
4784
|
),
|
|
4731
|
-
activeTab === "integrations" && /* @__PURE__ */ jsx17(
|
|
4785
|
+
activeTab === "integrations" && integrations2[selectedIndex] && /* @__PURE__ */ jsx17(
|
|
4732
4786
|
IntegrationsDetails,
|
|
4733
4787
|
{
|
|
4734
4788
|
isFocused: view === "detail",
|
|
4735
|
-
selectedAction
|
|
4789
|
+
selectedAction,
|
|
4790
|
+
integration: integrations2[selectedIndex]
|
|
4736
4791
|
}
|
|
4737
4792
|
),
|
|
4738
4793
|
activeTab === "settings" && /* @__PURE__ */ jsx17(
|
|
@@ -5341,6 +5396,16 @@ async function integrationsStatusCommand() {
|
|
|
5341
5396
|
console.log(chalk11.yellow(" Status: not configured"));
|
|
5342
5397
|
}
|
|
5343
5398
|
console.log("");
|
|
5399
|
+
console.log(chalk11.bold(" Atlassian"));
|
|
5400
|
+
const atlassianConfigured = getConfigValue("integrations.atlassian.configured");
|
|
5401
|
+
if (atlassianConfigured) {
|
|
5402
|
+
console.log(chalk11.green(" MCP: Connected"));
|
|
5403
|
+
console.log(chalk11.green(" Status: configured"));
|
|
5404
|
+
} else {
|
|
5405
|
+
console.log(chalk11.yellow(" MCP: Not yet verified"));
|
|
5406
|
+
console.log(chalk11.gray(" Use /share confluence to verify, or see setup guide"));
|
|
5407
|
+
}
|
|
5408
|
+
console.log("");
|
|
5344
5409
|
}
|
|
5345
5410
|
async function slackPostCommand(options) {
|
|
5346
5411
|
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,
|
|
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,CAoD/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;
|
|
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;AAyID,wBAAgB,mBAAmB,CAAC,EAClC,SAAS,EACT,cAAc,EACd,WAAW,GACZ,EAAE,wBAAwB,2CAO1B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tui.d.ts","sourceRoot":"","sources":["../../src/commands/tui.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"tui.d.ts","sourceRoot":"","sources":["../../src/commands/tui.tsx"],"names":[],"mappings":"AA4rBA,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
|
package/dist/lib/types.d.ts
CHANGED
|
@@ -44,8 +44,12 @@ export interface IntegrationSlackConfig {
|
|
|
44
44
|
configured?: boolean;
|
|
45
45
|
crosspost_channel?: string;
|
|
46
46
|
}
|
|
47
|
+
export interface IntegrationAtlassianConfig {
|
|
48
|
+
configured?: boolean;
|
|
49
|
+
}
|
|
47
50
|
export interface IntegrationsConfig {
|
|
48
51
|
slack?: IntegrationSlackConfig;
|
|
52
|
+
atlassian?: IntegrationAtlassianConfig;
|
|
49
53
|
}
|
|
50
54
|
export interface DroidConfig {
|
|
51
55
|
platform: Platform;
|
package/dist/lib/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/lib/types.ts"],"names":[],"mappings":"AAAA,oBAAY,QAAQ;IAClB,UAAU,gBAAgB;IAC1B,QAAQ,aAAa;IACrB,MAAM,WAAW;IACjB,WAAW,iBAAiB;CAC7B;AAID,QAAA,MAAM,WAAW,iBAAW,CAAC;AAC7B,OAAO,EAAE,WAAW,IAAI,MAAM,EAAE,CAAC;AACjC,MAAM,MAAM,MAAM,GAAG,QAAQ,CAAC;AAE9B;;;GAGG;AACH,wBAAgB,QAAQ,IAAI,MAAM,CAEjC;AAGD,oBAAY,aAAa;IACvB,QAAQ,aAAa;IACrB,MAAM,WAAW;CAClB;AAGD,MAAM,MAAM,gBAAgB,GAAG,aAAa,GAAG,MAAM,CAAC;AAEtD,oBAAY,WAAW;IACrB,MAAM,WAAW;IACjB,IAAI,SAAS;IACb,KAAK,UAAU;CAChB;AAED,oBAAY,gBAAgB;IAC1B,MAAM,WAAW;IACjB,OAAO,YAAY;IACnB,MAAM,WAAW;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;CACvC;AAED,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,OAAO,CAAC;IACb,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAGD,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEjD,MAAM,WAAW,sBAAsB;IACrC,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,CAAC,EAAE,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/lib/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "droid-share",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Share content to external platforms. Publish docs to Confluence or post summaries to Slack.",
|
|
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
|
+
"share"
|
|
15
|
+
],
|
|
16
|
+
"skills": [
|
|
17
|
+
"./skills/share/SKILL.md"
|
|
18
|
+
],
|
|
19
|
+
"commands": [
|
|
20
|
+
"./commands/share.md"
|
|
21
|
+
]
|
|
22
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
name: share
|
|
2
|
+
description: "Share content to external platforms. Publish docs to Confluence or post summaries to Slack."
|
|
3
|
+
version: 0.1.0
|
|
4
|
+
status: beta
|
|
5
|
+
|
|
6
|
+
includes:
|
|
7
|
+
skills:
|
|
8
|
+
- name: share
|
|
9
|
+
required: true
|
|
10
|
+
commands:
|
|
11
|
+
- name: share
|
|
12
|
+
is_alias: false
|
|
13
|
+
agents: []
|
|
14
|
+
|
|
15
|
+
dependencies: []
|
|
16
|
+
|
|
17
|
+
config_schema: {}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: share
|
|
3
|
+
description: "Share content to external platforms. Publish docs to Confluence or post summaries to Slack with free-form instructions."
|
|
4
|
+
argument-hint: "[confluence|slack] [{file|#channel}] [-- {instructions}]"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# /share
|
|
8
|
+
|
|
9
|
+
**User invoked:** `/share $ARGUMENTS`
|
|
10
|
+
|
|
11
|
+
**Your task:** Invoke the **share skill** with these arguments.
|
|
12
|
+
|
|
13
|
+
## Examples
|
|
14
|
+
|
|
15
|
+
- `/share` → Interactive mode, prompts for platform and file
|
|
16
|
+
- `/share confluence` → Publish to Confluence, prompts for file
|
|
17
|
+
- `/share confluence path/to/file.md` → Publish specific file to Confluence
|
|
18
|
+
- `/share slack #eng` → Share to Slack channel, prompts for content
|
|
19
|
+
- `/share slack #eng -- summarise the action items from this meeting`
|
|
20
|
+
- `/share slack #eng-design -- TLDR of this research doc with a link`
|
|
21
|
+
|
|
22
|
+
## Quick Reference
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
/share # Interactive
|
|
26
|
+
/share confluence # Publish doc to Confluence
|
|
27
|
+
/share confluence {file} # Publish specific file
|
|
28
|
+
/share slack #channel # Share to Slack (prompts for content)
|
|
29
|
+
/share slack #channel -- {instructions} # Share with specific instructions
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
See the **share skill** for complete documentation.
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: share
|
|
3
|
+
description: "Share content to external platforms. Publish docs to Confluence or post summaries to Slack. User prompts like 'share this to confluence', 'share on slack', 'post a summary to #eng'."
|
|
4
|
+
argument-hint: "[confluence|slack] [{file|#channel}] [-- {instructions}]"
|
|
5
|
+
allowed-tools:
|
|
6
|
+
[
|
|
7
|
+
Read,
|
|
8
|
+
Edit,
|
|
9
|
+
Glob,
|
|
10
|
+
Grep,
|
|
11
|
+
Bash(droid:*),
|
|
12
|
+
]
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# Share Skill
|
|
16
|
+
|
|
17
|
+
Share content to external platforms. Two modes:
|
|
18
|
+
|
|
19
|
+
- **Confluence** — publish a full markdown file as a Confluence page (create or update)
|
|
20
|
+
- **Slack** — process content with free-form instructions and post the result to a channel
|
|
21
|
+
|
|
22
|
+
## Supported Platforms
|
|
23
|
+
|
|
24
|
+
| Platform | Backend | Mode |
|
|
25
|
+
|----------|---------|------|
|
|
26
|
+
| Confluence | Atlassian MCP (`mcp__claude_ai_Atlassian__*`) | Publish document as page |
|
|
27
|
+
| Slack | `droid integrations slack post` | Process + post message |
|
|
28
|
+
|
|
29
|
+
## Procedure
|
|
30
|
+
|
|
31
|
+
### 1. Resolve Platform
|
|
32
|
+
|
|
33
|
+
**If platform provided in arguments** (e.g. `confluence`, `slack`): use it directly.
|
|
34
|
+
|
|
35
|
+
**If not provided:** Ask the user: "Where do you want to share? (Confluence / Slack)"
|
|
36
|
+
|
|
37
|
+
Then branch to the appropriate flow below.
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Confluence Flow
|
|
42
|
+
|
|
43
|
+
### C1. Resolve File
|
|
44
|
+
|
|
45
|
+
**If file path provided in arguments:** use it directly.
|
|
46
|
+
|
|
47
|
+
**If not provided:**
|
|
48
|
+
1. Check conversation context for an active file (e.g. brain doc being worked on)
|
|
49
|
+
2. If none, ask: "Which file would you like to publish?"
|
|
50
|
+
|
|
51
|
+
Read the file to confirm it exists.
|
|
52
|
+
|
|
53
|
+
### C2. Parse File
|
|
54
|
+
|
|
55
|
+
1. **Extract title:** Check YAML frontmatter for `title` field. If none, use the first `# heading`. If neither, use the filename without extension.
|
|
56
|
+
2. **Strip YAML frontmatter** from the content that will be sent (the `---` delimited block at the top). Keep the raw markdown body.
|
|
57
|
+
3. **Check for existing page ID:** Look for `confluence_page_id` in frontmatter — if present, this is an update rather than a create.
|
|
58
|
+
|
|
59
|
+
### C3. Verify Atlassian MCP Connection
|
|
60
|
+
|
|
61
|
+
Use `ToolSearch` to load `mcp__claude_ai_Atlassian__getAccessibleAtlassianResources`, then call it.
|
|
62
|
+
|
|
63
|
+
**If MCP is unavailable or errors:**
|
|
64
|
+
Tell the user: "Atlassian MCP is not connected. Run `/mcp` in Claude Code to add the Atlassian integration, then try again."
|
|
65
|
+
Stop here.
|
|
66
|
+
|
|
67
|
+
**If successful:**
|
|
68
|
+
- Extract the `id` (cloud ID) from the first accessible resource
|
|
69
|
+
- Mark integration as configured:
|
|
70
|
+
```bash
|
|
71
|
+
droid config --set "integrations.atlassian.configured=true"
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### C4. Resolve Confluence Space
|
|
75
|
+
|
|
76
|
+
Use `ToolSearch` to load `mcp__claude_ai_Atlassian__getConfluenceSpaces`, then call it with the cloud ID.
|
|
77
|
+
|
|
78
|
+
Present the list of spaces to the user and ask them to pick one.
|
|
79
|
+
|
|
80
|
+
### C5. Resolve Parent Page (Optional)
|
|
81
|
+
|
|
82
|
+
Ask the user: "Place this page under a specific parent, or at the space root?"
|
|
83
|
+
|
|
84
|
+
If they want a parent:
|
|
85
|
+
- Use `mcp__claude_ai_Atlassian__getPagesInConfluenceSpace` to list top-level pages
|
|
86
|
+
- Let the user pick a parent page
|
|
87
|
+
- If they need to go deeper, use `mcp__claude_ai_Atlassian__getConfluencePageDescendants` to navigate
|
|
88
|
+
|
|
89
|
+
If space root: no parent ID needed.
|
|
90
|
+
|
|
91
|
+
### C6. Confirm Title
|
|
92
|
+
|
|
93
|
+
Pre-fill from the extracted title (step C2). Ask the user to confirm or edit.
|
|
94
|
+
|
|
95
|
+
### C7. Create or Update Page
|
|
96
|
+
|
|
97
|
+
#### Update (frontmatter has `confluence_page_id`)
|
|
98
|
+
|
|
99
|
+
1. **Check for comments:** Use `mcp__claude_ai_Atlassian__getConfluencePageInlineComments` and `mcp__claude_ai_Atlassian__getConfluencePageFooterComments` to check for comments on the existing page.
|
|
100
|
+
- If comments exist, warn: "This page has N comments that may reference content being replaced. Continue?"
|
|
101
|
+
- If user declines, stop.
|
|
102
|
+
|
|
103
|
+
2. **Update:** Use `mcp__claude_ai_Atlassian__updateConfluencePage` with:
|
|
104
|
+
- `id`: the `confluence_page_id` from frontmatter
|
|
105
|
+
- `title`: confirmed title
|
|
106
|
+
- `body`: markdown content (frontmatter stripped)
|
|
107
|
+
- `contentFormat`: `"markdown"`
|
|
108
|
+
|
|
109
|
+
#### Create (no existing page ID)
|
|
110
|
+
|
|
111
|
+
Use `mcp__claude_ai_Atlassian__createConfluencePage` with:
|
|
112
|
+
- `spaceId`: chosen space ID
|
|
113
|
+
- `title`: confirmed title
|
|
114
|
+
- `body`: markdown content (frontmatter stripped)
|
|
115
|
+
- `contentFormat`: `"markdown"`
|
|
116
|
+
- `parentId`: parent page ID (if chosen, otherwise omit)
|
|
117
|
+
|
|
118
|
+
### C8. Update Frontmatter
|
|
119
|
+
|
|
120
|
+
After successful create or update, use `Edit` to add/update frontmatter fields in the source file:
|
|
121
|
+
|
|
122
|
+
- `confluence_page_id`: the page ID from the response
|
|
123
|
+
- `confluence_url`: the page URL (construct from cloud URL + page `_links.webui` or similar)
|
|
124
|
+
- `confluence_last_shared`: ISO 8601 timestamp of when this was shared
|
|
125
|
+
|
|
126
|
+
If the file has no frontmatter, add a `---` delimited block at the top.
|
|
127
|
+
|
|
128
|
+
### C9. Return Result
|
|
129
|
+
|
|
130
|
+
Tell the user:
|
|
131
|
+
- "Published to Confluence: {url}"
|
|
132
|
+
- Or "Updated Confluence page: {url}"
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## Slack Flow
|
|
137
|
+
|
|
138
|
+
### S1. Resolve Channel
|
|
139
|
+
|
|
140
|
+
**If `#channel` provided in arguments:** use it directly.
|
|
141
|
+
|
|
142
|
+
**If not provided:** Ask: "Which Slack channel?"
|
|
143
|
+
|
|
144
|
+
### S2. Resolve Source Content
|
|
145
|
+
|
|
146
|
+
**If file path provided in arguments:** read it.
|
|
147
|
+
|
|
148
|
+
**If not provided:**
|
|
149
|
+
1. Check conversation context for an active file (brain doc, codex doc, or any file being discussed)
|
|
150
|
+
2. If none, ask: "What content do you want to share?"
|
|
151
|
+
|
|
152
|
+
Read the file to get the full content.
|
|
153
|
+
|
|
154
|
+
### S3. Resolve Instructions
|
|
155
|
+
|
|
156
|
+
**If instructions provided after `--`:** use them.
|
|
157
|
+
|
|
158
|
+
**If not provided:** Ask: "How should I format this for Slack? (e.g. 'summarise the action items', 'TLDR with key decisions', 'list the open questions')"
|
|
159
|
+
|
|
160
|
+
### S4. Build Source Link
|
|
161
|
+
|
|
162
|
+
Check if the source content has a linkable reference:
|
|
163
|
+
|
|
164
|
+
| Source | Link format |
|
|
165
|
+
|--------|------------|
|
|
166
|
+
| File with `confluence_url` in frontmatter | Confluence page link |
|
|
167
|
+
| File in codex repo | Relative path reference |
|
|
168
|
+
| Brain doc with Obsidian path | Obsidian URI (for the user's own reference, not posted) |
|
|
169
|
+
| Other | File path (not posted — no useful link) |
|
|
170
|
+
|
|
171
|
+
If a meaningful URL exists (Confluence, GitHub), include it in the message.
|
|
172
|
+
|
|
173
|
+
### S5. Generate Message
|
|
174
|
+
|
|
175
|
+
Process the source content according to the user's instructions. The output should be a Slack-formatted message:
|
|
176
|
+
|
|
177
|
+
- Use Slack mrkdwn syntax (`*bold*`, `_italic_`, `• ` for bullets)
|
|
178
|
+
- Keep it concise — Slack messages should scan quickly
|
|
179
|
+
- If a source link is available, append it at the end: `:link: <{url}|View full document>`
|
|
180
|
+
- Do NOT dump the entire file — the point is to summarise/extract per the instructions
|
|
181
|
+
|
|
182
|
+
**Show the draft message to the user** and ask to confirm before posting.
|
|
183
|
+
|
|
184
|
+
### S6. Post to Slack
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
node -e 'process.stdout.write(JSON.stringify({channel:"{channel}",text:`{message}`,unfurl_links:false}))' | \
|
|
188
|
+
droid integrations slack post
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Check response for `"ok": true`.
|
|
192
|
+
|
|
193
|
+
### S7. Confirm
|
|
194
|
+
|
|
195
|
+
Tell the user: "Shared to #{channel}"
|
|
196
|
+
|
|
197
|
+
If there was a source link: "Included link to {source}"
|
|
198
|
+
|
|
199
|
+
---
|
|
200
|
+
|
|
201
|
+
## Error Handling
|
|
202
|
+
|
|
203
|
+
| Error | Action |
|
|
204
|
+
|-------|--------|
|
|
205
|
+
| Atlassian MCP not available | Suggest `/mcp` to connect Atlassian |
|
|
206
|
+
| No Confluence spaces returned | Check permissions — user may need Confluence access |
|
|
207
|
+
| Confluence create/update fails | Show error message from API response |
|
|
208
|
+
| No SLACK_USER_TOKEN | Suggest `droid integrations setup slack` |
|
|
209
|
+
| Slack post fails | Show error, offer to copy message to clipboard |
|
|
210
|
+
| File not found | Ask user to provide a valid path |
|
|
211
|
+
| No frontmatter to update | Create frontmatter block at top of file |
|
package/package.json
CHANGED
|
@@ -384,6 +384,21 @@ export async function integrationsStatusCommand(): Promise<void> {
|
|
|
384
384
|
}
|
|
385
385
|
|
|
386
386
|
console.log('');
|
|
387
|
+
|
|
388
|
+
// Atlassian
|
|
389
|
+
console.log(chalk.bold(' Atlassian'));
|
|
390
|
+
|
|
391
|
+
const atlassianConfigured = getConfigValue('integrations.atlassian.configured');
|
|
392
|
+
|
|
393
|
+
if (atlassianConfigured) {
|
|
394
|
+
console.log(chalk.green(' MCP: Connected'));
|
|
395
|
+
console.log(chalk.green(' Status: configured'));
|
|
396
|
+
} else {
|
|
397
|
+
console.log(chalk.yellow(' MCP: Not yet verified'));
|
|
398
|
+
console.log(chalk.gray(' Use /share confluence to verify, or see setup guide'));
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
console.log('');
|
|
387
402
|
}
|
|
388
403
|
|
|
389
404
|
/**
|
|
@@ -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,66 @@ 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
|
+
export function IntegrationsDetails({
|
|
152
|
+
isFocused,
|
|
153
|
+
selectedAction,
|
|
154
|
+
integration,
|
|
155
|
+
}: IntegrationsDetailsProps) {
|
|
156
|
+
if (integration.id === 'atlassian') {
|
|
157
|
+
return <AtlassianDetails isFocused={isFocused} selectedAction={selectedAction} connected={integration.connected} />;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Default: Slack
|
|
161
|
+
return <SlackDetails isFocused={isFocused} selectedAction={selectedAction} />;
|
|
162
|
+
}
|
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 } 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';
|
|
@@ -177,6 +177,12 @@ function App() {
|
|
|
177
177
|
// Keep skills for configure view (tools configure via their primary skill)
|
|
178
178
|
const skills = getBundledSkills();
|
|
179
179
|
|
|
180
|
+
// Dynamic integrations list
|
|
181
|
+
const integrations = [
|
|
182
|
+
{ id: 'slack', name: 'Slack', connected: !!process.env.SLACK_USER_TOKEN },
|
|
183
|
+
{ id: 'atlassian', name: 'Atlassian', connected: !!getConfigValue('integrations.atlassian.configured') },
|
|
184
|
+
];
|
|
185
|
+
|
|
180
186
|
useInput(
|
|
181
187
|
(input, key) => {
|
|
182
188
|
if (message) setMessage(null);
|
|
@@ -213,7 +219,7 @@ function App() {
|
|
|
213
219
|
setSelectedAction(0);
|
|
214
220
|
}
|
|
215
221
|
if (key.downArrow) {
|
|
216
|
-
const maxIndex = activeTab === 'tools' ? tools.length - 1 : activeTab === 'integrations' ?
|
|
222
|
+
const maxIndex = activeTab === 'tools' ? tools.length - 1 : activeTab === 'integrations' ? integrations.length - 1 : 0;
|
|
217
223
|
setSelectedIndex((prev) => {
|
|
218
224
|
const newIndex = Math.min(maxIndex, prev + 1);
|
|
219
225
|
// Scroll down if needed
|
|
@@ -239,12 +245,20 @@ function App() {
|
|
|
239
245
|
setSelectedAction(0);
|
|
240
246
|
}
|
|
241
247
|
if (activeTab === 'integrations') {
|
|
242
|
-
const
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
+
const currentIntegration = integrations[selectedIndex];
|
|
249
|
+
let intActions: { id: string }[] = [];
|
|
250
|
+
|
|
251
|
+
if (currentIntegration?.id === 'slack') {
|
|
252
|
+
const hasClientCreds = !!process.env.SLACK_CLIENT_ID && !!process.env.SLACK_CLIENT_SECRET;
|
|
253
|
+
const canRunSetup = hasClientCreds && !process.env.SLACK_USER_TOKEN;
|
|
254
|
+
intActions = [
|
|
255
|
+
...(canRunSetup ? [{ id: 'setup' }] : []),
|
|
256
|
+
{ id: 'guide' },
|
|
257
|
+
];
|
|
258
|
+
} else if (currentIntegration?.id === 'atlassian') {
|
|
259
|
+
intActions = [{ id: 'guide' }];
|
|
260
|
+
}
|
|
261
|
+
|
|
248
262
|
const maxIntAction = intActions.length - 1;
|
|
249
263
|
if (key.leftArrow) {
|
|
250
264
|
setSelectedAction((prev) => Math.max(0, prev - 1));
|
|
@@ -254,14 +268,18 @@ function App() {
|
|
|
254
268
|
}
|
|
255
269
|
if (key.return) {
|
|
256
270
|
const actionId = intActions[selectedAction]?.id;
|
|
257
|
-
if (actionId === 'setup') {
|
|
271
|
+
if (actionId === 'setup' && currentIntegration?.id === 'slack') {
|
|
258
272
|
exitCommand = ['droid', 'integrations', 'setup', 'slack'];
|
|
259
273
|
exit();
|
|
260
274
|
} else if (actionId === 'guide') {
|
|
261
|
-
const
|
|
275
|
+
const integrationId = currentIntegration?.id ?? 'slack';
|
|
276
|
+
const guideTitle = currentIntegration?.id === 'atlassian'
|
|
277
|
+
? 'Atlassian Integration Setup'
|
|
278
|
+
: 'Slack Integration Setup';
|
|
279
|
+
const content = loadIntegrationReference(integrationId, 'setup.md');
|
|
262
280
|
if (content) {
|
|
263
281
|
setPreviousView('detail');
|
|
264
|
-
setReadmeContent({ title:
|
|
282
|
+
setReadmeContent({ title: guideTitle, content });
|
|
265
283
|
setView('readme');
|
|
266
284
|
} else {
|
|
267
285
|
setMessage({ text: 'Could not load setup guide', type: 'error' });
|
|
@@ -612,14 +630,16 @@ function App() {
|
|
|
612
630
|
)}
|
|
613
631
|
|
|
614
632
|
{activeTab === 'integrations' && (
|
|
615
|
-
<Box paddingX={1}>
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
633
|
+
<Box flexDirection="column" paddingX={1}>
|
|
634
|
+
{integrations.map((integration, index) => (
|
|
635
|
+
<Text key={integration.id}>
|
|
636
|
+
{selectedIndex === index ? <Text color={colors.primary}>{'>'}</Text> : <Text> </Text>}
|
|
637
|
+
<Text> {integration.name}</Text>
|
|
638
|
+
{integration.connected
|
|
639
|
+
? <Text color={colors.success}> ✓</Text>
|
|
640
|
+
: <Text color={colors.textDim}> ✗</Text>}
|
|
641
|
+
</Text>
|
|
642
|
+
))}
|
|
623
643
|
</Box>
|
|
624
644
|
)}
|
|
625
645
|
|
|
@@ -658,10 +678,11 @@ function App() {
|
|
|
658
678
|
/>
|
|
659
679
|
)}
|
|
660
680
|
|
|
661
|
-
{activeTab === 'integrations' && (
|
|
681
|
+
{activeTab === 'integrations' && integrations[selectedIndex] && (
|
|
662
682
|
<IntegrationsDetails
|
|
663
683
|
isFocused={view === 'detail'}
|
|
664
684
|
selectedAction={selectedAction}
|
|
685
|
+
integration={integrations[selectedIndex]}
|
|
665
686
|
/>
|
|
666
687
|
)}
|
|
667
688
|
|
|
@@ -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
|
package/src/lib/types.ts
CHANGED
|
@@ -63,8 +63,13 @@ export interface IntegrationSlackConfig {
|
|
|
63
63
|
crosspost_channel?: string;
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
+
export interface IntegrationAtlassianConfig {
|
|
67
|
+
configured?: boolean;
|
|
68
|
+
}
|
|
69
|
+
|
|
66
70
|
export interface IntegrationsConfig {
|
|
67
71
|
slack?: IntegrationSlackConfig;
|
|
72
|
+
atlassian?: IntegrationAtlassianConfig;
|
|
68
73
|
}
|
|
69
74
|
|
|
70
75
|
export interface DroidConfig {
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "droid-share",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Share content to external platforms. Publish docs to Confluence or post summaries to Slack.",
|
|
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
|
+
"share"
|
|
15
|
+
],
|
|
16
|
+
"skills": [
|
|
17
|
+
"./skills/share/SKILL.md"
|
|
18
|
+
],
|
|
19
|
+
"commands": [
|
|
20
|
+
"./commands/share.md"
|
|
21
|
+
]
|
|
22
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
name: share
|
|
2
|
+
description: "Share content to external platforms. Publish docs to Confluence or post summaries to Slack."
|
|
3
|
+
version: 0.1.0
|
|
4
|
+
status: beta
|
|
5
|
+
|
|
6
|
+
includes:
|
|
7
|
+
skills:
|
|
8
|
+
- name: share
|
|
9
|
+
required: true
|
|
10
|
+
commands:
|
|
11
|
+
- name: share
|
|
12
|
+
is_alias: false
|
|
13
|
+
agents: []
|
|
14
|
+
|
|
15
|
+
dependencies: []
|
|
16
|
+
|
|
17
|
+
config_schema: {}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: share
|
|
3
|
+
description: "Share content to external platforms. Publish docs to Confluence or post summaries to Slack with free-form instructions."
|
|
4
|
+
argument-hint: "[confluence|slack] [{file|#channel}] [-- {instructions}]"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# /share
|
|
8
|
+
|
|
9
|
+
**User invoked:** `/share $ARGUMENTS`
|
|
10
|
+
|
|
11
|
+
**Your task:** Invoke the **share skill** with these arguments.
|
|
12
|
+
|
|
13
|
+
## Examples
|
|
14
|
+
|
|
15
|
+
- `/share` → Interactive mode, prompts for platform and file
|
|
16
|
+
- `/share confluence` → Publish to Confluence, prompts for file
|
|
17
|
+
- `/share confluence path/to/file.md` → Publish specific file to Confluence
|
|
18
|
+
- `/share slack #eng` → Share to Slack channel, prompts for content
|
|
19
|
+
- `/share slack #eng -- summarise the action items from this meeting`
|
|
20
|
+
- `/share slack #eng-design -- TLDR of this research doc with a link`
|
|
21
|
+
|
|
22
|
+
## Quick Reference
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
/share # Interactive
|
|
26
|
+
/share confluence # Publish doc to Confluence
|
|
27
|
+
/share confluence {file} # Publish specific file
|
|
28
|
+
/share slack #channel # Share to Slack (prompts for content)
|
|
29
|
+
/share slack #channel -- {instructions} # Share with specific instructions
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
See the **share skill** for complete documentation.
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: share
|
|
3
|
+
description: "Share content to external platforms. Publish docs to Confluence or post summaries to Slack. User prompts like 'share this to confluence', 'share on slack', 'post a summary to #eng'."
|
|
4
|
+
argument-hint: "[confluence|slack] [{file|#channel}] [-- {instructions}]"
|
|
5
|
+
allowed-tools:
|
|
6
|
+
[
|
|
7
|
+
Read,
|
|
8
|
+
Edit,
|
|
9
|
+
Glob,
|
|
10
|
+
Grep,
|
|
11
|
+
Bash(droid:*),
|
|
12
|
+
]
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# Share Skill
|
|
16
|
+
|
|
17
|
+
Share content to external platforms. Two modes:
|
|
18
|
+
|
|
19
|
+
- **Confluence** — publish a full markdown file as a Confluence page (create or update)
|
|
20
|
+
- **Slack** — process content with free-form instructions and post the result to a channel
|
|
21
|
+
|
|
22
|
+
## Supported Platforms
|
|
23
|
+
|
|
24
|
+
| Platform | Backend | Mode |
|
|
25
|
+
|----------|---------|------|
|
|
26
|
+
| Confluence | Atlassian MCP (`mcp__claude_ai_Atlassian__*`) | Publish document as page |
|
|
27
|
+
| Slack | `droid integrations slack post` | Process + post message |
|
|
28
|
+
|
|
29
|
+
## Procedure
|
|
30
|
+
|
|
31
|
+
### 1. Resolve Platform
|
|
32
|
+
|
|
33
|
+
**If platform provided in arguments** (e.g. `confluence`, `slack`): use it directly.
|
|
34
|
+
|
|
35
|
+
**If not provided:** Ask the user: "Where do you want to share? (Confluence / Slack)"
|
|
36
|
+
|
|
37
|
+
Then branch to the appropriate flow below.
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Confluence Flow
|
|
42
|
+
|
|
43
|
+
### C1. Resolve File
|
|
44
|
+
|
|
45
|
+
**If file path provided in arguments:** use it directly.
|
|
46
|
+
|
|
47
|
+
**If not provided:**
|
|
48
|
+
1. Check conversation context for an active file (e.g. brain doc being worked on)
|
|
49
|
+
2. If none, ask: "Which file would you like to publish?"
|
|
50
|
+
|
|
51
|
+
Read the file to confirm it exists.
|
|
52
|
+
|
|
53
|
+
### C2. Parse File
|
|
54
|
+
|
|
55
|
+
1. **Extract title:** Check YAML frontmatter for `title` field. If none, use the first `# heading`. If neither, use the filename without extension.
|
|
56
|
+
2. **Strip YAML frontmatter** from the content that will be sent (the `---` delimited block at the top). Keep the raw markdown body.
|
|
57
|
+
3. **Check for existing page ID:** Look for `confluence_page_id` in frontmatter — if present, this is an update rather than a create.
|
|
58
|
+
|
|
59
|
+
### C3. Verify Atlassian MCP Connection
|
|
60
|
+
|
|
61
|
+
Use `ToolSearch` to load `mcp__claude_ai_Atlassian__getAccessibleAtlassianResources`, then call it.
|
|
62
|
+
|
|
63
|
+
**If MCP is unavailable or errors:**
|
|
64
|
+
Tell the user: "Atlassian MCP is not connected. Run `/mcp` in Claude Code to add the Atlassian integration, then try again."
|
|
65
|
+
Stop here.
|
|
66
|
+
|
|
67
|
+
**If successful:**
|
|
68
|
+
- Extract the `id` (cloud ID) from the first accessible resource
|
|
69
|
+
- Mark integration as configured:
|
|
70
|
+
```bash
|
|
71
|
+
droid config --set "integrations.atlassian.configured=true"
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### C4. Resolve Confluence Space
|
|
75
|
+
|
|
76
|
+
Use `ToolSearch` to load `mcp__claude_ai_Atlassian__getConfluenceSpaces`, then call it with the cloud ID.
|
|
77
|
+
|
|
78
|
+
Present the list of spaces to the user and ask them to pick one.
|
|
79
|
+
|
|
80
|
+
### C5. Resolve Parent Page (Optional)
|
|
81
|
+
|
|
82
|
+
Ask the user: "Place this page under a specific parent, or at the space root?"
|
|
83
|
+
|
|
84
|
+
If they want a parent:
|
|
85
|
+
- Use `mcp__claude_ai_Atlassian__getPagesInConfluenceSpace` to list top-level pages
|
|
86
|
+
- Let the user pick a parent page
|
|
87
|
+
- If they need to go deeper, use `mcp__claude_ai_Atlassian__getConfluencePageDescendants` to navigate
|
|
88
|
+
|
|
89
|
+
If space root: no parent ID needed.
|
|
90
|
+
|
|
91
|
+
### C6. Confirm Title
|
|
92
|
+
|
|
93
|
+
Pre-fill from the extracted title (step C2). Ask the user to confirm or edit.
|
|
94
|
+
|
|
95
|
+
### C7. Create or Update Page
|
|
96
|
+
|
|
97
|
+
#### Update (frontmatter has `confluence_page_id`)
|
|
98
|
+
|
|
99
|
+
1. **Check for comments:** Use `mcp__claude_ai_Atlassian__getConfluencePageInlineComments` and `mcp__claude_ai_Atlassian__getConfluencePageFooterComments` to check for comments on the existing page.
|
|
100
|
+
- If comments exist, warn: "This page has N comments that may reference content being replaced. Continue?"
|
|
101
|
+
- If user declines, stop.
|
|
102
|
+
|
|
103
|
+
2. **Update:** Use `mcp__claude_ai_Atlassian__updateConfluencePage` with:
|
|
104
|
+
- `id`: the `confluence_page_id` from frontmatter
|
|
105
|
+
- `title`: confirmed title
|
|
106
|
+
- `body`: markdown content (frontmatter stripped)
|
|
107
|
+
- `contentFormat`: `"markdown"`
|
|
108
|
+
|
|
109
|
+
#### Create (no existing page ID)
|
|
110
|
+
|
|
111
|
+
Use `mcp__claude_ai_Atlassian__createConfluencePage` with:
|
|
112
|
+
- `spaceId`: chosen space ID
|
|
113
|
+
- `title`: confirmed title
|
|
114
|
+
- `body`: markdown content (frontmatter stripped)
|
|
115
|
+
- `contentFormat`: `"markdown"`
|
|
116
|
+
- `parentId`: parent page ID (if chosen, otherwise omit)
|
|
117
|
+
|
|
118
|
+
### C8. Update Frontmatter
|
|
119
|
+
|
|
120
|
+
After successful create or update, use `Edit` to add/update frontmatter fields in the source file:
|
|
121
|
+
|
|
122
|
+
- `confluence_page_id`: the page ID from the response
|
|
123
|
+
- `confluence_url`: the page URL (construct from cloud URL + page `_links.webui` or similar)
|
|
124
|
+
- `confluence_last_shared`: ISO 8601 timestamp of when this was shared
|
|
125
|
+
|
|
126
|
+
If the file has no frontmatter, add a `---` delimited block at the top.
|
|
127
|
+
|
|
128
|
+
### C9. Return Result
|
|
129
|
+
|
|
130
|
+
Tell the user:
|
|
131
|
+
- "Published to Confluence: {url}"
|
|
132
|
+
- Or "Updated Confluence page: {url}"
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## Slack Flow
|
|
137
|
+
|
|
138
|
+
### S1. Resolve Channel
|
|
139
|
+
|
|
140
|
+
**If `#channel` provided in arguments:** use it directly.
|
|
141
|
+
|
|
142
|
+
**If not provided:** Ask: "Which Slack channel?"
|
|
143
|
+
|
|
144
|
+
### S2. Resolve Source Content
|
|
145
|
+
|
|
146
|
+
**If file path provided in arguments:** read it.
|
|
147
|
+
|
|
148
|
+
**If not provided:**
|
|
149
|
+
1. Check conversation context for an active file (brain doc, codex doc, or any file being discussed)
|
|
150
|
+
2. If none, ask: "What content do you want to share?"
|
|
151
|
+
|
|
152
|
+
Read the file to get the full content.
|
|
153
|
+
|
|
154
|
+
### S3. Resolve Instructions
|
|
155
|
+
|
|
156
|
+
**If instructions provided after `--`:** use them.
|
|
157
|
+
|
|
158
|
+
**If not provided:** Ask: "How should I format this for Slack? (e.g. 'summarise the action items', 'TLDR with key decisions', 'list the open questions')"
|
|
159
|
+
|
|
160
|
+
### S4. Build Source Link
|
|
161
|
+
|
|
162
|
+
Check if the source content has a linkable reference:
|
|
163
|
+
|
|
164
|
+
| Source | Link format |
|
|
165
|
+
|--------|------------|
|
|
166
|
+
| File with `confluence_url` in frontmatter | Confluence page link |
|
|
167
|
+
| File in codex repo | Relative path reference |
|
|
168
|
+
| Brain doc with Obsidian path | Obsidian URI (for the user's own reference, not posted) |
|
|
169
|
+
| Other | File path (not posted — no useful link) |
|
|
170
|
+
|
|
171
|
+
If a meaningful URL exists (Confluence, GitHub), include it in the message.
|
|
172
|
+
|
|
173
|
+
### S5. Generate Message
|
|
174
|
+
|
|
175
|
+
Process the source content according to the user's instructions. The output should be a Slack-formatted message:
|
|
176
|
+
|
|
177
|
+
- Use Slack mrkdwn syntax (`*bold*`, `_italic_`, `• ` for bullets)
|
|
178
|
+
- Keep it concise — Slack messages should scan quickly
|
|
179
|
+
- If a source link is available, append it at the end: `:link: <{url}|View full document>`
|
|
180
|
+
- Do NOT dump the entire file — the point is to summarise/extract per the instructions
|
|
181
|
+
|
|
182
|
+
**Show the draft message to the user** and ask to confirm before posting.
|
|
183
|
+
|
|
184
|
+
### S6. Post to Slack
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
node -e 'process.stdout.write(JSON.stringify({channel:"{channel}",text:`{message}`,unfurl_links:false}))' | \
|
|
188
|
+
droid integrations slack post
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Check response for `"ok": true`.
|
|
192
|
+
|
|
193
|
+
### S7. Confirm
|
|
194
|
+
|
|
195
|
+
Tell the user: "Shared to #{channel}"
|
|
196
|
+
|
|
197
|
+
If there was a source link: "Included link to {source}"
|
|
198
|
+
|
|
199
|
+
---
|
|
200
|
+
|
|
201
|
+
## Error Handling
|
|
202
|
+
|
|
203
|
+
| Error | Action |
|
|
204
|
+
|-------|--------|
|
|
205
|
+
| Atlassian MCP not available | Suggest `/mcp` to connect Atlassian |
|
|
206
|
+
| No Confluence spaces returned | Check permissions — user may need Confluence access |
|
|
207
|
+
| Confluence create/update fails | Show error message from API response |
|
|
208
|
+
| No SLACK_USER_TOKEN | Suggest `droid integrations setup slack` |
|
|
209
|
+
| Slack post fails | Show error, offer to copy message to clipboard |
|
|
210
|
+
| File not found | Ask user to provide a valid path |
|
|
211
|
+
| No frontmatter to update | Create frontmatter block at top of file |
|