@hoverlover/cc-discord 0.2.1 → 0.2.2

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
  }
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.2",
4
4
  "description": "Discord <-> Claude Code relay: use your Claude subscription to power per-channel AI bots",
5
5
  "type": "module",
6
6
  "bin": {
@@ -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 {};