Skip to content

Commit d1db28f

Browse files
merge: resolve incident-tracker-plugin with origin/main
2 parents 3abbe6c + 535ff3a commit d1db28f

33 files changed

Lines changed: 2347 additions & 141 deletions

File tree

.claude-plugin/marketplace.json

Lines changed: 35 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
{
1212
"name": "1pass",
1313
"description": "Install and manage 1Password CLI (op) and op-exec in Claude Code sessions. Provides session-start auto-install for web sessions and comprehensive workflow skills.",
14-
"version": "0.3.1",
14+
"version": "0.3.2",
1515
"author": {
1616
"name": "Nathan Heaps"
1717
},
@@ -268,6 +268,18 @@
268268
"deep-research"
269269
]
270270
},
271+
{
272+
"name": "discord",
273+
"description": "Discord channel for Claude Code — messaging bridge with built-in access control. Manage pairing, allowlists, and policy via /discord:access.",
274+
"version": "0.0.4",
275+
"author": {
276+
"name": "Nathan Heaps"
277+
},
278+
"source": "./plugins/discord",
279+
"category": "utility",
280+
"tags": ["utility", "skill"],
281+
"keywords": ["discord", "messaging", "channel", "mcp"]
282+
},
271283
{
272284
"name": "edit-utils",
273285
"description": "File editing utilities: auto-formatting and linting for written/edited files with project-aware configuration",
@@ -512,25 +524,6 @@
512524
"tags": ["utility", "skill"],
513525
"keywords": ["plugin", "install", "update", "reload", "management"]
514526
},
515-
{
516-
"name": "product-development-and-sdlc",
517-
"description": "Iterative spec and user story writing with structured SDLC workflows. Guides incremental specification development through research, review, and refinement cycles.",
518-
"version": "0.1.1",
519-
"author": {
520-
"name": "Nathan Heaps"
521-
},
522-
"source": "./plugins/product-development-and-sdlc",
523-
"category": "utility",
524-
"tags": ["utility", "skill"],
525-
"keywords": [
526-
"spec",
527-
"specifications",
528-
"user-stories",
529-
"sdlc",
530-
"iterative-development",
531-
"product-management"
532-
]
533-
},
534527
{
535528
"name": "proxmox",
536529
"description": "Skills for managing Proxmox VE hosts and LXC containers — creating containers, running Docker in LXC, and hosting services like cloudflared.",
@@ -604,7 +597,7 @@
604597
{
605598
"name": "scm-utils",
606599
"description": "Source control management utilities for improving interactions with branches and PRs, both locally and in CI environments",
607-
"version": "0.1.14",
600+
"version": "0.2.0",
608601
"author": {
609602
"name": "Nathan Heaps"
610603
},
@@ -613,6 +606,27 @@
613606
"tags": ["utility", "command", "skill"],
614607
"keywords": ["git", "branch", "pr", "pull-request", "sync", "merge", "scm", "source-control"]
615608
},
609+
{
610+
"name": "sdlc-utils",
611+
"description": "Software development lifecycle utilities — skills organized by SDLC phase: plan, implement, review, test, deploy, and maintain.",
612+
"version": "0.2.0",
613+
"author": {
614+
"name": "Nathan Heaps"
615+
},
616+
"source": "./plugins/sdlc-utils",
617+
"category": "utility",
618+
"tags": ["utility", "skill"],
619+
"keywords": [
620+
"sdlc",
621+
"planning",
622+
"implementation",
623+
"code-review",
624+
"testing",
625+
"deployment",
626+
"maintenance",
627+
"software-engineering"
628+
]
629+
},
616630
{
617631
"name": "self-terminate",
618632
"description": "Allows Claude to gracefully terminate itself using SIGINT",

plugins/1pass/.claude-plugin/plugin.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "1pass",
3-
"version": "0.3.1",
3+
"version": "0.3.2",
44
"description": "Install and manage 1Password CLI (op) and op-exec in Claude Code sessions. Provides session-start auto-install for web sessions and comprehensive workflow skills.",
55
"author": {
66
"name": "Nathan Heaps",

plugins/1pass/skills/op-exec/SKILL.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,47 @@ also in bash sessions.
150150
- **Both (default)**: Most common — ensures secrets are available everywhere
151151
during the session and to all tool types.
152152

153+
## ENVIRONMENT Aggregator Pattern
154+
155+
The `ENVIRONMENT` item in 1Password (e.g., `op://AI-Jack/ENVIRONMENT`) serves as the
156+
canonical aggregator for all environment variables. Instead of adding separate items to
157+
`opExec.items`, add new secrets as fields to the ENVIRONMENT item:
158+
159+
1. In 1Password, add a new field to the ENVIRONMENT item with the desired env var name
160+
as the label (e.g., `DISCORD_BOT_TOKEN`)
161+
2. Set the field value to an `op://` reference pointing to the actual secret
162+
(e.g., `op://AI-Jack/discord--jack_oat_bot/token`)
163+
3. op-exec resolves references recursively, so the field value will be the actual secret
164+
at runtime
165+
4. The field label becomes the exported env var name (converted to UPPER_SNAKE_CASE)
166+
167+
This pattern means you only need one item in `opExec.items` (the ENVIRONMENT item) to
168+
manage all secrets. Adding separate items should be avoided unless the secret doesn't
169+
fit the aggregator pattern.
170+
171+
### Example
172+
173+
```
174+
ENVIRONMENT item fields:
175+
- TELEGRAM_BOT_TOKEN = op://AI-Jack/telegram-bot/token
176+
- DISCORD_BOT_TOKEN = op://AI-Jack/discord--jack_oat_bot/token
177+
- BRAINTRUST_API_KEY = op://AI-Jack/braintrust/api-key
178+
```
179+
180+
### Plugin Config with Aggregator
181+
182+
When using the aggregator pattern, the plugin config is minimal — just one item:
183+
184+
```yaml
185+
1pass:
186+
opExec:
187+
items:
188+
- "op://AI-Jack/ENVIRONMENT"
189+
```
190+
191+
All env vars are managed by adding/removing fields on that single 1Password item,
192+
rather than editing plugin configuration.
193+
153194
## Troubleshooting
154195
155196
### "op-exec: command not found"
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"name": "discord",
3+
"description": "Discord channel for Claude Code \u2014 messaging bridge with built-in access control. Manage pairing, allowlists, and policy via /discord:access.",
4+
"version": "0.0.4",
5+
"author": {
6+
"name": "Nathan Heaps",
7+
"email": "nsheaps@gmail.com",
8+
"url": "https://github.com/nsheaps"
9+
},
10+
"homepage": "https://github.com/nsheaps/ai-mktpl/tree/main/plugins/discord",
11+
"repository": "https://github.com/nsheaps/ai-mktpl",
12+
"keywords": ["discord", "messaging", "channel", "mcp"]
13+
}

plugins/discord/.mcp.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"mcpServers": {
3+
"discord": {
4+
"command": "bun",
5+
"args": ["run", "--cwd", "${CLAUDE_PLUGIN_ROOT}", "--shell=bun", "--silent", "start"]
6+
}
7+
}
8+
}

plugins/discord/.npmrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
registry=https://registry.npmjs.org/

plugins/discord/ACCESS.md

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
# Discord — Access & Delivery
2+
3+
Discord only allows DMs between accounts that share a server. Who can DM your bot depends on where it's installed: one private server means only that server's members can reach it; a public community means every member there can open a DM.
4+
5+
The **Public Bot** toggle in the Developer Portal (Bot tab, on by default) controls who can add the bot to new servers. Turn it off and only your own account can install it. This is your first gate, and it's enforced by Discord rather than by this process.
6+
7+
For DMs that do get through, the default policy is **pairing**. An unknown sender gets a 6-character code in reply and their message is dropped. You run `/discord:access pair <code>` from your assistant session to approve them. Once approved, their messages pass through.
8+
9+
All state lives in `~/.claude/channels/discord/access.json`. The `/discord:access` skill commands edit this file; the server re-reads it on every inbound message, so changes take effect without a restart. Set `DISCORD_ACCESS_MODE=static` to pin config to what was on disk at boot (pairing is unavailable in static mode since it requires runtime writes).
10+
11+
## At a glance
12+
13+
| | |
14+
| -------------- | --------------------------------------------------- |
15+
| Default policy | `pairing` |
16+
| Sender ID | User snowflake (numeric, e.g. `184695080709324800`) |
17+
| Group key | Channel snowflake — not guild ID |
18+
| Config file | `~/.claude/channels/discord/access.json` |
19+
20+
## DM policies
21+
22+
`dmPolicy` controls how DMs from senders not on the allowlist are handled.
23+
24+
| Policy | Behavior |
25+
| ------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
26+
| `pairing` (default) | Reply with a pairing code, drop the message. Approve with `/discord:access pair <code>`. |
27+
| `allowlist` | Drop silently. No reply. Use this once everyone who needs access is already on the list, or if pairing replies would attract spam. |
28+
| `disabled` | Drop everything, including allowlisted users and guild channels. |
29+
30+
```
31+
/discord:access policy allowlist
32+
```
33+
34+
## User IDs
35+
36+
Discord identifies users by **snowflakes**: permanent numeric IDs like `184695080709324800`. Usernames are mutable; snowflakes aren't. The allowlist stores snowflakes.
37+
38+
Pairing captures the ID automatically. To add someone manually, enable **User Settings → Advanced → Developer Mode** in Discord, then right-click any user and choose **Copy User ID**. Your own ID is available by right-clicking your avatar in the lower-left.
39+
40+
```
41+
/discord:access allow 184695080709324800
42+
/discord:access remove 184695080709324800
43+
```
44+
45+
## Guild channels
46+
47+
Guild channels are off by default. Opt each one in individually, keyed on the **channel** snowflake (not the guild). Threads inherit their parent channel's opt-in; no separate entry needed. Find channel IDs the same way as user IDs: Developer Mode, right-click the channel, Copy Channel ID.
48+
49+
```
50+
/discord:access group add 846209781206941736
51+
```
52+
53+
With the default `requireMention: true`, the bot responds only when @mentioned or replied to. Pass `--no-mention` to process every message in the channel, or `--allow id1,id2` to restrict which members can trigger it.
54+
55+
```
56+
/discord:access group add 846209781206941736 --no-mention
57+
/discord:access group add 846209781206941736 --allow 184695080709324800,221773638772129792
58+
/discord:access group rm 846209781206941736
59+
```
60+
61+
## Mention detection
62+
63+
In channels with `requireMention: true`, any of the following triggers the bot:
64+
65+
- A structured `@botname` mention (typed via Discord's autocomplete)
66+
- A reply to one of the bot's recent messages
67+
- A match against any regex in `mentionPatterns`
68+
69+
Example regex setup for a nickname trigger:
70+
71+
```
72+
/discord:access set mentionPatterns '["^hey claude\\b", "\\bassistant\\b"]'
73+
```
74+
75+
## Delivery
76+
77+
Configure outbound behavior with `/discord:access set <key> <value>`.
78+
79+
**`ackReaction`** reacts to inbound messages on receipt as a "seen" acknowledgment. Unicode emoji work directly; custom server emoji require the full `<:name:id>` form. The emoji ID is at the end of the URL when you right-click the emoji and copy its link. Empty string disables.
80+
81+
```
82+
/discord:access set ackReaction 🔨
83+
/discord:access set ackReaction ""
84+
```
85+
86+
**`replyToMode`** controls threading on chunked replies. When a long response is split, `first` (default) threads only the first chunk under the inbound message; `all` threads every chunk; `off` sends all chunks standalone.
87+
88+
**`textChunkLimit`** sets the split threshold. Discord rejects messages over 2000 characters, which is the hard ceiling.
89+
90+
**`chunkMode`** chooses the split strategy: `length` cuts exactly at the limit; `newline` prefers paragraph boundaries.
91+
92+
## Skill reference
93+
94+
| Command | Effect |
95+
| ---------------------------------------------- | -------------------------------------------------------------------------------------------------- |
96+
| `/discord:access` | Print current state: policy, allowlist, pending pairings, enabled channels. |
97+
| `/discord:access pair a4f91c` | Approve pairing code `a4f91c`. Adds the sender to `allowFrom` and sends a confirmation on Discord. |
98+
| `/discord:access deny a4f91c` | Discard a pending code. The sender is not notified. |
99+
| `/discord:access allow 184695080709324800` | Add a user snowflake directly. |
100+
| `/discord:access remove 184695080709324800` | Remove from the allowlist. |
101+
| `/discord:access policy allowlist` | Set `dmPolicy`. Values: `pairing`, `allowlist`, `disabled`. |
102+
| `/discord:access group add 846209781206941736` | Enable a guild channel. Flags: `--no-mention`, `--allow id1,id2`. |
103+
| `/discord:access group rm 846209781206941736` | Disable a guild channel. |
104+
| `/discord:access set ackReaction 🔨` | Set a config key: `ackReaction`, `replyToMode`, `textChunkLimit`, `chunkMode`, `mentionPatterns`. |
105+
106+
## Config file
107+
108+
`~/.claude/channels/discord/access.json`. Absent file is equivalent to `pairing` policy with empty lists, so the first DM triggers pairing.
109+
110+
```jsonc
111+
{
112+
// Handling for DMs from senders not in allowFrom.
113+
"dmPolicy": "pairing",
114+
115+
// User snowflakes allowed to DM.
116+
"allowFrom": ["184695080709324800"],
117+
118+
// Guild channels the bot is active in. Empty object = DM-only.
119+
"groups": {
120+
"846209781206941736": {
121+
// true: respond only to @mentions and replies.
122+
"requireMention": true,
123+
// Restrict triggers to these senders. Empty = any member (subject to requireMention).
124+
"allowFrom": [],
125+
},
126+
},
127+
128+
// Case-insensitive regexes that count as a mention.
129+
"mentionPatterns": ["^hey claude\\b"],
130+
131+
// Reaction on receipt. Empty string disables.
132+
"ackReaction": "👀",
133+
134+
// Threading on chunked replies: first | all | off
135+
"replyToMode": "first",
136+
137+
// Split threshold. Discord rejects > 2000.
138+
"textChunkLimit": 2000,
139+
140+
// length = cut at limit. newline = prefer paragraph boundaries.
141+
"chunkMode": "newline",
142+
}
143+
```

plugins/discord/CHANGES.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Changes from upstream
2+
3+
This plugin is derived from Anthropic's official
4+
[Discord channel plugin](https://github.com/anthropics/claude-code/tree/main/packages/channel-discord)
5+
(Apache-2.0). The following modifications were made for use in the `ai-mktpl`
6+
marketplace:
7+
8+
- **Bot message support** -- the upstream server ignores all bot-authored
9+
messages. This fork processes messages from other bots, which enables
10+
multi-agent relay scenarios (e.g. a bridge bot forwarding from another
11+
platform). Messages from the bot's own user ID are still ignored to prevent
12+
self-loops.
13+
- **Self-message guard** -- an explicit check against the bot's own user ID
14+
(`client.user.id`) replaces the blanket `message.author.bot` filter, so only
15+
the bot's own messages are dropped.

0 commit comments

Comments
 (0)