@hoverlover/cc-discord 0.2.1 → 0.2.3

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.
@@ -88,6 +88,7 @@
88
88
  "permissions": {
89
89
  "allow": [
90
90
  "Bash(send-discord:*)",
91
+ "Bash(send-imessage:*)",
91
92
  "Bash(wait-for-discord-messages:*)"
92
93
  ]
93
94
  }
@@ -0,0 +1,82 @@
1
+ ---
2
+ name: send-imessage
3
+ description: Use when the user asks to "send a text", "send an iMessage", "text someone", "send a message to [phone number]", "message [person]", or discusses sending SMS/iMessage. Also use when scheduling text messages or automating iMessage delivery.
4
+ ---
5
+
6
+ # Send iMessage
7
+
8
+ Send text messages via macOS Messages.app using the `send-imessage` CLI tool. This skill enables sending iMessages and SMS directly from the Mac.
9
+
10
+ ## When This Skill Applies
11
+
12
+ - User asks to send a text message or iMessage
13
+ - User asks to text someone (by name, phone number, or Apple ID)
14
+ - User wants to schedule or automate text message delivery
15
+ - User asks about messaging capabilities
16
+
17
+ ## Tool Usage
18
+
19
+ The `send-imessage` tool is available as a Bash command:
20
+
21
+ ```bash
22
+ send-imessage --to "<recipient>" "<message>"
23
+ ```
24
+
25
+ ### Parameters
26
+
27
+ | Flag | Required | Description |
28
+ |------|----------|-------------|
29
+ | `--to` | Yes | Phone number (10-digit US or with +1) or Apple ID email |
30
+ | `--sms` | No | Force SMS delivery instead of iMessage |
31
+ | `"message"` | Yes | The message text (positional argument) |
32
+
33
+ ### Examples
34
+
35
+ ```bash
36
+ # Send to a US phone number (auto-normalizes to +1)
37
+ send-imessage --to "4175551234" "Hey, just checking in!"
38
+
39
+ # Send to a number with country code
40
+ send-imessage --to "+14175551234" "Meeting at 3pm"
41
+
42
+ # Send to an Apple ID email
43
+ send-imessage --to "user@icloud.com" "Hello from Alfred!"
44
+
45
+ # Force SMS instead of iMessage
46
+ send-imessage --to "4175551234" --sms "This is an SMS"
47
+ ```
48
+
49
+ ## Phone Number Handling
50
+
51
+ The tool auto-normalizes US phone numbers:
52
+ - `4175551234` (10 digits) becomes `+14175551234`
53
+ - `14175551234` (11 digits) becomes `+14175551234`
54
+ - `+14175551234` stays as-is
55
+ - International numbers should include the `+` prefix
56
+
57
+ ## Important Notes
58
+
59
+ - **macOS only**: This tool uses AppleScript and requires macOS with Messages.app
60
+ - **Apple ID required**: The Mac must be signed into an Apple ID with iMessage enabled
61
+ - **First-run permissions**: macOS may prompt for automation permissions the first time
62
+ - **Delivery**: iMessage is used by default; use `--sms` flag to force SMS
63
+ - **No read receipts**: The tool can only send, not read incoming messages
64
+ - **Rate limiting**: Avoid sending many messages in rapid succession to prevent Apple throttling
65
+
66
+ ## Error Handling
67
+
68
+ If the send fails:
69
+ 1. Check that Messages.app is running and signed in
70
+ 2. Verify the recipient phone number or Apple ID is correct
71
+ 3. Check that macOS automation permissions are granted for the terminal/process
72
+ 4. Try the `--sms` flag if iMessage delivery fails
73
+
74
+ ## Combining with Scheduling
75
+
76
+ Use cron or systemd timers to schedule messages:
77
+
78
+ ```bash
79
+ # Example: send a daily 9am reminder
80
+ # crontab entry:
81
+ # 0 9 * * * /path/to/tools/send-imessage --to "4175551234" "Good morning! Don't forget your standup."
82
+ ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hoverlover/cc-discord",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "description": "Discord <-> Claude Code relay: use your Claude subscription to power per-channel AI bots",
5
5
  "type": "module",
6
6
  "bin": {
@@ -14,6 +14,7 @@
14
14
  "prompts/",
15
15
  "memory/",
16
16
  ".claude/settings.template.json",
17
+ ".claude/skills/",
17
18
  ".env.example",
18
19
  ".env.relay.example",
19
20
  ".env.worker.example"
@@ -88,6 +88,41 @@ export AGENT_ID="$CHANNEL_ID"
88
88
  export CLAUDE_AGENT_ID="${CLAUDE_AGENT_ID:-claude-discord}"
89
89
  export CLAUDE_RUNTIME_ID="${CLAUDE_RUNTIME_ID:-rt_$(date +%s)_${RANDOM}}"
90
90
 
91
+ # Determine Claude's project directory (where .claude/skills/ are discovered).
92
+ # - CC_DISCORD_HOME overrides everything
93
+ # - If cwd has a .claude/ directory (local dev / bun start from repo), use cwd
94
+ # - Otherwise default to ~/.cc-discord (bunx / installed package)
95
+ if [ -n "${CC_DISCORD_HOME:-}" ]; then
96
+ CLAUDE_PROJECT_DIR="$CC_DISCORD_HOME"
97
+ elif [ -d ".claude" ]; then
98
+ CLAUDE_PROJECT_DIR="$(pwd)"
99
+ else
100
+ CLAUDE_PROJECT_DIR="$HOME/.cc-discord"
101
+ fi
102
+
103
+ mkdir -p "$CLAUDE_PROJECT_DIR/.claude/skills"
104
+
105
+ # Seed built-in skills from the package into the project directory.
106
+ # Only copies skills that don't already exist (user modifications are preserved).
107
+ if [ -d "$ROOT_DIR/.claude/skills" ] && [ "$ROOT_DIR" != "$CLAUDE_PROJECT_DIR" ]; then
108
+ for skill_dir in "$ROOT_DIR/.claude/skills"/*/; do
109
+ skill_name="$(basename "$skill_dir")"
110
+ if [ ! -d "$CLAUDE_PROJECT_DIR/.claude/skills/$skill_name" ]; then
111
+ cp -r "$skill_dir" "$CLAUDE_PROJECT_DIR/.claude/skills/$skill_name"
112
+ echo "[channel-agent:$CHANNEL_NAME] Seeded skill: $skill_name"
113
+ fi
114
+ done
115
+ fi
116
+
117
+ # Copy settings.json into the project dir so Claude sees it as a project config.
118
+ if [ "$ROOT_DIR" != "$CLAUDE_PROJECT_DIR" ] && [ -f "$SETTINGS_PATH" ]; then
119
+ mkdir -p "$CLAUDE_PROJECT_DIR/.claude"
120
+ cp "$SETTINGS_PATH" "$CLAUDE_PROJECT_DIR/.claude/settings.json"
121
+ fi
122
+
123
+ cd "$CLAUDE_PROJECT_DIR"
124
+ echo "[channel-agent:$CHANNEL_NAME] Claude project dir: $CLAUDE_PROJECT_DIR"
125
+
91
126
  # Build the channel-specific system prompt
92
127
  SYSTEM_PROMPT="$(sed \
93
128
  -e "s|__CHANNEL_ID__|${CHANNEL_ID}|g" \
@@ -0,0 +1,2 @@
1
+ #!/bin/sh
2
+ exec bun "$(dirname "$0")/send-imessage.ts" "$@"
@@ -0,0 +1,96 @@
1
+ #!/usr/bin/env bun
2
+
3
+ /**
4
+ * Send an iMessage via macOS Messages.app using AppleScript.
5
+ *
6
+ * Usage:
7
+ * send-imessage --to "+14175551234" "Hello from Alfred!"
8
+ * send-imessage --to "+14175551234" --sms "This goes as SMS"
9
+ * send-imessage --to "user@icloud.com" "Hello via Apple ID"
10
+ */
11
+
12
+ import { $ } from "bun";
13
+
14
+ const args = process.argv.slice(2);
15
+ let to: string | null = null;
16
+ let useSms = false;
17
+ const textParts: string[] = [];
18
+
19
+ for (let i = 0; i < args.length; i++) {
20
+ const arg = args[i];
21
+ if (arg === "--to" && args[i + 1]) {
22
+ to = args[++i];
23
+ continue;
24
+ }
25
+ if (arg === "--sms") {
26
+ useSms = true;
27
+ continue;
28
+ }
29
+ if (arg === "--help" || arg === "-h") {
30
+ console.log(`
31
+ Usage: send-imessage --to <phone_or_appleid> [--sms] "message"
32
+
33
+ Options:
34
+ --to Phone number (with country code, e.g. +14175551234) or Apple ID email
35
+ --sms Force SMS instead of iMessage (uses SMS service)
36
+
37
+ Examples:
38
+ send-imessage --to "+14175551234" "Hello!"
39
+ send-imessage --to "user@icloud.com" "Hey there"
40
+ send-imessage --to "+14175551234" --sms "SMS fallback"
41
+ `);
42
+ process.exit(0);
43
+ }
44
+ textParts.push(arg);
45
+ }
46
+
47
+ const message = textParts.join(" ").trim();
48
+
49
+ if (!to) {
50
+ console.error("Error: --to is required. Use --help for usage.");
51
+ process.exit(1);
52
+ }
53
+
54
+ if (!message) {
55
+ console.error("Error: message text is required. Use --help for usage.");
56
+ process.exit(1);
57
+ }
58
+
59
+ // Normalize phone number: ensure it starts with +1 if it's a 10-digit US number
60
+ let normalizedTo = to;
61
+ if (/^\d{10}$/.test(to)) {
62
+ normalizedTo = `+1${to}`;
63
+ } else if (/^1\d{10}$/.test(to)) {
64
+ normalizedTo = `+${to}`;
65
+ }
66
+
67
+ // Escape single quotes for AppleScript
68
+ const escapedMessage = message.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
69
+ const escapedTo = normalizedTo.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
70
+
71
+ // Build AppleScript
72
+ const serviceFilter = useSms
73
+ ? 'service type = SMS'
74
+ : 'service type = iMessage';
75
+
76
+ const script = `tell application "Messages" to send "${escapedMessage}" to buddy "${escapedTo}" of (1st service whose ${serviceFilter})`;
77
+
78
+ try {
79
+ const result = await $`osascript -e ${script}`.quiet();
80
+
81
+ if (result.exitCode !== 0) {
82
+ const stderr = result.stderr.toString().trim();
83
+ console.error(`Failed to send iMessage: ${stderr}`);
84
+ process.exit(1);
85
+ }
86
+
87
+ console.log(`iMessage sent to ${normalizedTo}: "${message}"`);
88
+ process.exit(0);
89
+ } catch (err: unknown) {
90
+ const error = err as Error & { stderr?: Buffer };
91
+ const stderr = error.stderr?.toString().trim() || error.message;
92
+ console.error(`Failed to send iMessage: ${stderr}`);
93
+ process.exit(1);
94
+ }
95
+
96
+ export {};