agent4discord 0.1.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 (78) hide show
  1. package/README.ko.md +134 -0
  2. package/README.md +170 -0
  3. package/dist/bot.d.ts +1 -0
  4. package/dist/bot.js +114 -0
  5. package/dist/bot.js.map +1 -0
  6. package/dist/cli.d.ts +2 -0
  7. package/dist/cli.js +27 -0
  8. package/dist/cli.js.map +1 -0
  9. package/dist/commands/index.d.ts +9 -0
  10. package/dist/commands/index.js +44 -0
  11. package/dist/commands/index.js.map +1 -0
  12. package/dist/commands/init.d.ts +5 -0
  13. package/dist/commands/init.js +152 -0
  14. package/dist/commands/init.js.map +1 -0
  15. package/dist/commands/model.d.ts +5 -0
  16. package/dist/commands/model.js +65 -0
  17. package/dist/commands/model.js.map +1 -0
  18. package/dist/commands/resume.d.ts +6 -0
  19. package/dist/commands/resume.js +113 -0
  20. package/dist/commands/resume.js.map +1 -0
  21. package/dist/config.d.ts +12 -0
  22. package/dist/config.js +49 -0
  23. package/dist/config.js.map +1 -0
  24. package/dist/formatters/chunker.d.ts +5 -0
  25. package/dist/formatters/chunker.js +46 -0
  26. package/dist/formatters/chunker.js.map +1 -0
  27. package/dist/formatters/embedBuilder.d.ts +28 -0
  28. package/dist/formatters/embedBuilder.js +32 -0
  29. package/dist/formatters/embedBuilder.js.map +1 -0
  30. package/dist/formatters/toolFormatter.d.ts +4 -0
  31. package/dist/formatters/toolFormatter.js +90 -0
  32. package/dist/formatters/toolFormatter.js.map +1 -0
  33. package/dist/guild.d.ts +22 -0
  34. package/dist/guild.js +41 -0
  35. package/dist/guild.js.map +1 -0
  36. package/dist/interactions/directoryBrowser.d.ts +61 -0
  37. package/dist/interactions/directoryBrowser.js +611 -0
  38. package/dist/interactions/directoryBrowser.js.map +1 -0
  39. package/dist/interactions/index.d.ts +5 -0
  40. package/dist/interactions/index.js +92 -0
  41. package/dist/interactions/index.js.map +1 -0
  42. package/dist/interactions/permissionHandler.d.ts +11 -0
  43. package/dist/interactions/permissionHandler.js +107 -0
  44. package/dist/interactions/permissionHandler.js.map +1 -0
  45. package/dist/interactions/sessionControls.d.ts +9 -0
  46. package/dist/interactions/sessionControls.js +95 -0
  47. package/dist/interactions/sessionControls.js.map +1 -0
  48. package/dist/sessions/eventHandler.d.ts +3 -0
  49. package/dist/sessions/eventHandler.js +209 -0
  50. package/dist/sessions/eventHandler.js.map +1 -0
  51. package/dist/sessions/sessionManager.d.ts +29 -0
  52. package/dist/sessions/sessionManager.js +203 -0
  53. package/dist/sessions/sessionManager.js.map +1 -0
  54. package/dist/sessions/sessionStore.d.ts +4 -0
  55. package/dist/sessions/sessionStore.js +29 -0
  56. package/dist/sessions/sessionStore.js.map +1 -0
  57. package/dist/sessions/streamHandler.d.ts +18 -0
  58. package/dist/sessions/streamHandler.js +119 -0
  59. package/dist/sessions/streamHandler.js.map +1 -0
  60. package/dist/sessions/toolProgress.d.ts +14 -0
  61. package/dist/sessions/toolProgress.js +65 -0
  62. package/dist/sessions/toolProgress.js.map +1 -0
  63. package/dist/sessions/usageTracker.d.ts +12 -0
  64. package/dist/sessions/usageTracker.js +222 -0
  65. package/dist/sessions/usageTracker.js.map +1 -0
  66. package/dist/setup.d.ts +1 -0
  67. package/dist/setup.js +101 -0
  68. package/dist/setup.js.map +1 -0
  69. package/dist/utils/filesystem.d.ts +11 -0
  70. package/dist/utils/filesystem.js +26 -0
  71. package/dist/utils/filesystem.js.map +1 -0
  72. package/dist/utils/logger.d.ts +1 -0
  73. package/dist/utils/logger.js +3 -0
  74. package/dist/utils/logger.js.map +1 -0
  75. package/dist/utils/plugins.d.ts +6 -0
  76. package/dist/utils/plugins.js +66 -0
  77. package/dist/utils/plugins.js.map +1 -0
  78. package/package.json +45 -0
package/README.ko.md ADDED
@@ -0,0 +1,134 @@
1
+ <p align="center">
2
+ <img src="docs/images/banner.png" alt="Agent4Discord" width="640">
3
+ </p>
4
+
5
+ <p align="center">
6
+ <strong>Discord를 통한 원격 Claude Code 세션</strong>
7
+ </p>
8
+
9
+ <p align="center">
10
+ <a href="README.md">English</a>
11
+ </p>
12
+
13
+ ---
14
+
15
+ Agent4Discord (A4D)는 [Claude Code](https://docs.anthropic.com/en/docs/claude-code)를 Discord 채널에서 사용할 수 있게 해주는 셀프호스트 Discord 봇입니다. 각 세션은 전용 채널에 매핑되고, 도구 호출은 스레드에 표시되며, 권한 요청은 인터랙티브 버튼으로 나타납니다.
16
+
17
+ **내 PC. 내 봇. 내 Claude Code 세션.**
18
+
19
+ ## 동작 방식
20
+
21
+ <p align="center">
22
+ <img src="docs/images/architecture.png" alt="아키텍처" width="640">
23
+ </p>
24
+
25
+ 1. 본인의 Discord 봇 토큰으로 PC에서 봇을 실행
26
+ 2. `/a4d init`으로 Discord 서버에 채널 구조 생성
27
+ 3. 작업 디렉토리를 선택하고 Claude Code 세션 시작
28
+ 4. Discord에서 Claude와 대화 — 스트리밍, 도구 호출, 권한 승인 모두 지원
29
+
30
+ ## 주요 기능
31
+
32
+ - **디렉토리 브라우저** — 셀렉트 메뉴와 버튼으로 파일시스템 탐색
33
+ - **모델 선택** — 세션 시작 시 opus/sonnet/haiku 선택 (기본값: opus)
34
+ - **실시간 스트리밍** — 텍스트 출력, 생각, 도구 진행률을 라이브 업데이트 임베드로 표시
35
+ - **도구 호출 스레드** — 각 도구 실행이 포맷된 입출력과 함께 개별 스레드로 생성
36
+ - **권한 제어** — 위험한 작업에 Allow/Deny 버튼 (안전한 도구는 자동 허용)
37
+ - **세션 재개** — CLI에서 만든 세션이나 중단된 세션을 `/a4d resume`으로 재개
38
+ - **사용량 트래커** — `#a4d-usage` 채널에서 세션 비용, 토큰, 속도 제한 표시
39
+ - **플러그인 지원** — 설치된 Claude Code 플러그인 (스킬, 훅) 자동 로드
40
+ - **CLI 연동** — CLI와 동일한 JSONL 저장소를 공유하여 세션 호환
41
+
42
+ ### 디렉토리 브라우저
43
+ ![디렉토리 브라우저](docs/images/screenshot-browser.png)
44
+
45
+ ### 세션 스트리밍
46
+ ![세션](docs/images/screenshot-session.png)
47
+
48
+ ### 권한 요청
49
+ ![권한](docs/images/screenshot-permission.png)
50
+
51
+ ## 빠른 시작
52
+
53
+ ### 사전 요구사항
54
+
55
+ - **Node.js** >= 20.x
56
+ - **Claude Code** 인증 완료 (`claude login` 또는 `ANTHROPIC_API_KEY`)
57
+ - **Discord 봇 토큰** ([여기서 생성](https://discord.com/developers/applications))
58
+
59
+ ### 설정
60
+
61
+ ```bash
62
+ npx agent4discord@latest --setup
63
+ ```
64
+
65
+ 설정 마법사가 안내합니다:
66
+ 1. Discord 봇 토큰 입력
67
+ 2. Client ID 입력
68
+ 3. Message Content Intent 활성화 확인
69
+ 4. 초대 URL 생성 및 브라우저 열기
70
+
71
+ ### 실행
72
+
73
+ ```bash
74
+ npx agent4discord@latest
75
+ ```
76
+
77
+ ### Discord에서
78
+
79
+ 1. 서버에서 `/a4d init` 실행
80
+ 2. `#a4d-session`에서 디렉토리 탐색
81
+ 3. **Session Start** 클릭, 모델 선택, 대화 시작
82
+
83
+ ## 명령어
84
+
85
+ | 명령어 | 설명 |
86
+ |---|---|
87
+ | `/a4d init` | 서버에 A4D 채널 구조 생성 |
88
+ | `/a4d resume` | 현재 채널에서 중단된 세션 재개 |
89
+ | `/a4d model <opus\|sonnet\|haiku>` | 세션 중 모델 변경 |
90
+
91
+ ## 채널 구조
92
+
93
+ ```
94
+ A4D - General
95
+ ├── #a4d-general — 상태 메시지
96
+ ├── #a4d-session — 디렉토리 브라우저 & 세션 시작
97
+ └── #a4d-usage — 사용량 & 속도 제한 트래커
98
+
99
+ A4D - Sessions
100
+ ├── #a4d-myproject — 활성 세션 채널
101
+ └── #a4d-another — 다른 세션
102
+ ```
103
+
104
+ ## 설정 파일
105
+
106
+ `~/.agent4discord/config.json`에 저장됩니다:
107
+
108
+ ```json
109
+ {
110
+ "discordToken": "봇-토큰",
111
+ "discordClientId": "클라이언트-ID",
112
+ "claudeModel": "opus",
113
+ "permissionMode": "default",
114
+ "logLevel": "info"
115
+ }
116
+ ```
117
+
118
+ ## 개발
119
+
120
+ ```bash
121
+ git clone https://github.com/raravel/Agent4Discord.git
122
+ cd Agent4Discord
123
+ npm install
124
+
125
+ # 자동 리로드 개발 모드
126
+ npx tsx watch src/cli.ts
127
+
128
+ # 타입 체크
129
+ npx tsc --noEmit
130
+ ```
131
+
132
+ ## 라이선스
133
+
134
+ MIT
package/README.md ADDED
@@ -0,0 +1,170 @@
1
+ <p align="center">
2
+ <img src="docs/images/banner.png" alt="Agent4Discord" width="640">
3
+ </p>
4
+
5
+ <p align="center">
6
+ <strong>Remote Claude Code sessions through Discord</strong>
7
+ </p>
8
+
9
+ <p align="center">
10
+ <a href="README.ko.md">한국어</a>
11
+ </p>
12
+
13
+ ---
14
+
15
+ Agent4Discord (A4D) is a self-hosted Discord bot that lets you interact with [Claude Code](https://docs.anthropic.com/en/docs/claude-code) through Discord channels. Each session maps to a dedicated channel, tool calls appear in threads, and permission requests show as interactive buttons.
16
+
17
+ **Your PC. Your bot. Your Claude Code sessions.**
18
+
19
+ ## How It Works
20
+
21
+ <p align="center">
22
+ <img src="docs/images/architecture.png" alt="Architecture" width="640">
23
+ </p>
24
+
25
+ 1. You run the bot on your PC with your own Discord bot token
26
+ 2. `/a4d init` sets up channels in your Discord server
27
+ 3. Pick a working directory and start a Claude Code session
28
+ 4. Chat with Claude through Discord — streaming, tool calls, and permissions all work
29
+
30
+ ## Features
31
+
32
+ - **Directory Browser** — Navigate your filesystem with select menus and buttons
33
+ - **Model Selection** — Choose opus/sonnet/haiku when starting a session (default: opus)
34
+ - **Real-time Streaming** — Live-updating embeds for text output, thinking, and tool progress
35
+ - **Tool Call Threads** — Each tool execution gets its own thread with formatted input/output
36
+ - **Permission Control** — Allow/Deny buttons for dangerous operations (auto-allow for safe tools)
37
+ - **Session Resume** — Resume CLI-created sessions or stopped sessions with `/a4d resume`
38
+ - **Usage Tracker** — `#a4d-usage` channel shows session costs, tokens, and rate limits
39
+ - **Plugin Support** — Auto-loads your installed Claude Code plugins (skills, hooks)
40
+ - **CLI Interop** — Sessions share the same JSONL storage as the CLI
41
+
42
+ ### Directory Browser
43
+ ![Directory Browser](docs/images/screenshot-browser.png)
44
+
45
+ ### Session with Streaming
46
+ ![Session](docs/images/screenshot-session.png)
47
+
48
+ ### Permission Request
49
+ ![Permission](docs/images/screenshot-permission.png)
50
+
51
+ ## Quick Start
52
+
53
+ ### Prerequisites
54
+
55
+ - **Node.js** >= 20.x
56
+ - **Claude Code** authenticated (`claude login` or `ANTHROPIC_API_KEY`)
57
+ - **Discord bot token** ([create one here](https://discord.com/developers/applications))
58
+
59
+ ### Setup
60
+
61
+ ```bash
62
+ npx agent4discord@latest --setup
63
+ ```
64
+
65
+ The setup wizard will:
66
+ 1. Ask for your Discord bot token
67
+ 2. Ask for your Client ID
68
+ 3. Verify Message Content Intent is enabled
69
+ 4. Generate an invite URL and open it in your browser
70
+
71
+ ### Run
72
+
73
+ ```bash
74
+ npx agent4discord@latest
75
+ ```
76
+
77
+ ### In Discord
78
+
79
+ 1. Run `/a4d init` in your server
80
+ 2. Go to `#a4d-session` and browse to a directory
81
+ 3. Click **Session Start**, pick a model, and start chatting
82
+
83
+ ## Commands
84
+
85
+ | Command | Description |
86
+ |---|---|
87
+ | `/a4d init` | Set up A4D channels in your server |
88
+ | `/a4d resume` | Resume a stopped session in the current channel |
89
+ | `/a4d model <opus\|sonnet\|haiku>` | Change model mid-session |
90
+
91
+ ## Channel Structure
92
+
93
+ ```
94
+ A4D - General
95
+ ├── #a4d-general — Status messages
96
+ ├── #a4d-session — Directory browser & session start
97
+ └── #a4d-usage — Usage & rate limit tracker
98
+
99
+ A4D - Sessions
100
+ ├── #a4d-myproject — Active session channel
101
+ └── #a4d-another — Another session
102
+ ```
103
+
104
+ ## Configuration
105
+
106
+ Config is stored at `~/.agent4discord/config.json`:
107
+
108
+ ```json
109
+ {
110
+ "discordToken": "your-bot-token",
111
+ "discordClientId": "your-client-id",
112
+ "claudeModel": "opus",
113
+ "permissionMode": "default",
114
+ "logLevel": "info"
115
+ }
116
+ ```
117
+
118
+ ## Development
119
+
120
+ ```bash
121
+ git clone https://github.com/raravel/Agent4Discord.git
122
+ cd Agent4Discord
123
+ npm install
124
+
125
+ # Dev mode with auto-reload
126
+ npx tsx watch src/cli.ts
127
+
128
+ # Type check
129
+ npx tsc --noEmit
130
+ ```
131
+
132
+ ## Project Structure
133
+
134
+ ```
135
+ src/
136
+ ├── cli.ts # Entry point
137
+ ├── setup.ts # Interactive setup wizard
138
+ ├── config.ts # Config loading (~/.agent4discord/)
139
+ ├── bot.ts # Discord client & event handlers
140
+ ├── guild.ts # Guild config persistence
141
+ ├── commands/
142
+ │ ├── index.ts # Slash command registry
143
+ │ ├── init.ts # /a4d init
144
+ │ ├── resume.ts # /a4d resume
145
+ │ └── model.ts # /a4d model
146
+ ├── interactions/
147
+ │ ├── index.ts # Interaction router
148
+ │ ├── directoryBrowser.ts # Directory browser UI
149
+ │ ├── sessionControls.ts # Stop/Archive buttons
150
+ │ └── permissionHandler.ts # Allow/Deny/Details buttons
151
+ ├── sessions/
152
+ │ ├── sessionManager.ts # SDK query() lifecycle
153
+ │ ├── sessionStore.ts # Session persistence
154
+ │ ├── eventHandler.ts # SDK events → Discord
155
+ │ ├── streamHandler.ts # Streaming text/thinking embeds
156
+ │ ├── toolProgress.ts # Tool execution progress
157
+ │ └── usageTracker.ts # Rate limit & cost tracking
158
+ ├── formatters/
159
+ │ ├── embedBuilder.ts # Discord embeds
160
+ │ ├── chunker.ts # Message chunking
161
+ │ └── toolFormatter.ts # Tool-specific formatting
162
+ └── utils/
163
+ ├── filesystem.ts # Directory listing
164
+ ├── plugins.ts # Plugin auto-loader
165
+ └── logger.ts # Logging
166
+ ```
167
+
168
+ ## License
169
+
170
+ MIT
package/dist/bot.d.ts ADDED
@@ -0,0 +1 @@
1
+ export declare function startBot(): Promise<void>;
package/dist/bot.js ADDED
@@ -0,0 +1,114 @@
1
+ import { Client, GatewayIntentBits, Events, } from 'discord.js';
2
+ import { loadConfig } from './config.js';
3
+ import { commands, registerCommands } from './commands/index.js';
4
+ import { routeInteraction } from './interactions/index.js';
5
+ import { sessionManager } from './sessions/sessionManager.js';
6
+ import { setupEventHandlers, getAndClearTurnThreads } from './sessions/eventHandler.js';
7
+ import { removeSessionFromGuild } from './sessions/sessionStore.js';
8
+ import { setupUsageTracker } from './sessions/usageTracker.js';
9
+ export async function startBot() {
10
+ const config = loadConfig();
11
+ const client = new Client({
12
+ intents: [
13
+ GatewayIntentBits.Guilds,
14
+ GatewayIntentBits.GuildMessages,
15
+ GatewayIntentBits.MessageContent,
16
+ ],
17
+ });
18
+ // --- Ready ---
19
+ client.on(Events.ClientReady, async (readyClient) => {
20
+ console.log(`Ready! Logged in as ${readyClient.user.tag} (${readyClient.guilds.cache.size} guilds)`);
21
+ // Auto-register slash commands for all guilds
22
+ for (const [guildId] of readyClient.guilds.cache) {
23
+ try {
24
+ await registerCommands(config.discordClientId, config.discordToken, guildId);
25
+ }
26
+ catch (err) {
27
+ console.error(`Failed to register commands for guild ${guildId}:`, err);
28
+ }
29
+ }
30
+ });
31
+ // --- Guild join ---
32
+ client.on(Events.GuildCreate, async (guild) => {
33
+ console.log(`Joined guild: ${guild.name} (${guild.id})`);
34
+ try {
35
+ await registerCommands(config.discordClientId, config.discordToken, guild.id);
36
+ }
37
+ catch (err) {
38
+ console.error(`Failed to register commands for guild ${guild.id}:`, err);
39
+ }
40
+ });
41
+ // --- Interaction handling ---
42
+ client.on(Events.InteractionCreate, async (interaction) => {
43
+ // Slash commands
44
+ if (interaction.isChatInputCommand()) {
45
+ const handler = commands.get(interaction.commandName);
46
+ if (handler) {
47
+ try {
48
+ await handler(interaction);
49
+ }
50
+ catch (err) {
51
+ console.error(`Error handling command "${interaction.commandName}":`, err);
52
+ const reply = { content: 'An error occurred while processing the command.', ephemeral: true };
53
+ if (interaction.replied || interaction.deferred) {
54
+ await interaction.followUp(reply);
55
+ }
56
+ else {
57
+ await interaction.reply(reply);
58
+ }
59
+ }
60
+ }
61
+ return;
62
+ }
63
+ // Buttons and select menus
64
+ if (interaction.isButton() || interaction.isStringSelectMenu()) {
65
+ try {
66
+ await routeInteraction(interaction);
67
+ }
68
+ catch (err) {
69
+ console.error(`Error handling interaction "${interaction.customId}":`, err);
70
+ }
71
+ return;
72
+ }
73
+ });
74
+ // --- Message handling ---
75
+ client.on(Events.MessageCreate, async (message) => {
76
+ if (message.author.bot)
77
+ return;
78
+ const session = sessionManager.getSession(message.channelId);
79
+ if (!session)
80
+ return;
81
+ // Archive previous turn's tool threads
82
+ const prevThreadIds = getAndClearTurnThreads(message.channelId);
83
+ for (const threadId of prevThreadIds) {
84
+ const thread = message.guild?.channels.cache.get(threadId);
85
+ if (thread?.isThread()) {
86
+ thread.setArchived(true).catch(() => { });
87
+ }
88
+ }
89
+ // Add hourglass reaction to indicate processing
90
+ await message.react('\u23f3').catch(() => { });
91
+ try {
92
+ sessionManager.sendMessage(message.channelId, message.content);
93
+ }
94
+ catch (err) {
95
+ console.error(`[message] Failed to relay message to session:`, err);
96
+ await message.reply('Could not send message to Claude Code session.').catch(() => { });
97
+ }
98
+ });
99
+ // --- Channel delete ---
100
+ client.on(Events.ChannelDelete, async (channel) => {
101
+ const session = sessionManager.getSession(channel.id);
102
+ if (!session)
103
+ return;
104
+ console.log(`[cleanup] Channel ${channel.id} deleted, cleaning up session ${session.sessionId} (guild: ${session.guildId})`);
105
+ sessionManager.removeSession(channel.id);
106
+ removeSessionFromGuild(session.guildId, channel.id);
107
+ });
108
+ // Wire up SDK event -> Discord message handlers
109
+ setupEventHandlers(client);
110
+ setupUsageTracker(client);
111
+ // Start
112
+ await client.login(config.discordToken);
113
+ }
114
+ //# sourceMappingURL=bot.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bot.js","sourceRoot":"","sources":["../src/bot.ts"],"names":[],"mappings":"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"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env node
2
+ import { parseArgs } from 'node:util';
3
+ const { values } = parseArgs({
4
+ options: {
5
+ setup: { type: 'boolean', default: false },
6
+ version: { type: 'boolean', default: false },
7
+ help: { type: 'boolean', default: false },
8
+ },
9
+ });
10
+ if (values.version) {
11
+ const { default: pkg } = await import('../package.json', { with: { type: 'json' } });
12
+ console.log(pkg.version);
13
+ process.exit(0);
14
+ }
15
+ if (values.help) {
16
+ console.log('Usage: agent4discord [--setup] [--version] [--help]');
17
+ process.exit(0);
18
+ }
19
+ if (values.setup) {
20
+ const { runSetup } = await import('./setup.js');
21
+ await runSetup();
22
+ }
23
+ else {
24
+ const { startBot } = await import('./bot.js');
25
+ await startBot();
26
+ }
27
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEtC,MAAM,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IAC3B,OAAO,EAAE;QACP,KAAK,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE;QAC1C,OAAO,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE;QAC5C,IAAI,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE;KAC1C;CACF,CAAC,CAAC;AAEH,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;IACnB,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,MAAM,MAAM,CAAC,iBAAiB,EAAE,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC;IACrF,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IACzB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAED,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,qDAAqD,CAAC,CAAC;IACnE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAED,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;IACjB,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,MAAM,CAAC,YAAY,CAAC,CAAC;IAChD,MAAM,QAAQ,EAAE,CAAC;AACnB,CAAC;KAAM,CAAC;IACN,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,MAAM,CAAC,UAAU,CAAC,CAAC;IAC9C,MAAM,QAAQ,EAAE,CAAC;AACnB,CAAC"}
@@ -0,0 +1,9 @@
1
+ import { type ChatInputCommandInteraction } from 'discord.js';
2
+ /** Handler function type for slash commands. */
3
+ export type CommandHandler = (interaction: ChatInputCommandInteraction) => Promise<void>;
4
+ /** Map of command names to their handler functions. */
5
+ export declare const commands: Map<string, CommandHandler>;
6
+ /**
7
+ * Register slash commands for a specific guild using the Discord REST API.
8
+ */
9
+ export declare function registerCommands(clientId: string, token: string, guildId: string): Promise<void>;
@@ -0,0 +1,44 @@
1
+ import { SlashCommandBuilder, PermissionFlagsBits, } from 'discord.js';
2
+ import { REST, Routes } from 'discord.js';
3
+ import { handleInit } from './init.js';
4
+ import { handleResume } from './resume.js';
5
+ import { handleModel } from './model.js';
6
+ /** The /a4d slash command definition. */
7
+ const a4dCommand = new SlashCommandBuilder()
8
+ .setName('a4d')
9
+ .setDescription('Agent4Discord commands')
10
+ .setDefaultMemberPermissions(PermissionFlagsBits.Administrator)
11
+ .addSubcommand((sub) => sub.setName('init').setDescription('Initialize Agent4Discord in this server'))
12
+ .addSubcommand((sub) => sub.setName('resume').setDescription('Resume a stopped session in this channel'))
13
+ .addSubcommand((sub) => sub.setName('model')
14
+ .setDescription('Change the model for this session')
15
+ .addStringOption((opt) => opt.setName('model')
16
+ .setDescription('Model to use')
17
+ .setRequired(true)
18
+ .addChoices({ name: 'Opus 4.6 (most capable)', value: 'opus' }, { name: 'Sonnet 4.6 (fast)', value: 'sonnet' }, { name: 'Haiku 4.5 (fastest)', value: 'haiku' })));
19
+ /** Map of command names to their handler functions. */
20
+ export const commands = new Map();
21
+ // Register the /a4d command handler with subcommand routing
22
+ commands.set('a4d', async (interaction) => {
23
+ const subcommand = interaction.options.getSubcommand();
24
+ if (subcommand === 'init') {
25
+ await handleInit(interaction);
26
+ }
27
+ else if (subcommand === 'resume') {
28
+ await handleResume(interaction);
29
+ }
30
+ else if (subcommand === 'model') {
31
+ await handleModel(interaction);
32
+ }
33
+ });
34
+ /**
35
+ * Register slash commands for a specific guild using the Discord REST API.
36
+ */
37
+ export async function registerCommands(clientId, token, guildId) {
38
+ const rest = new REST({ version: '10' }).setToken(token);
39
+ const commandData = [a4dCommand.toJSON()];
40
+ await rest.put(Routes.applicationGuildCommands(clientId, guildId), {
41
+ body: commandData,
42
+ });
43
+ }
44
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/commands/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mBAAmB,EACnB,mBAAmB,GAEpB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAKzC,yCAAyC;AACzC,MAAM,UAAU,GAAG,IAAI,mBAAmB,EAAE;KACzC,OAAO,CAAC,KAAK,CAAC;KACd,cAAc,CAAC,wBAAwB,CAAC;KACxC,2BAA2B,CAAC,mBAAmB,CAAC,aAAa,CAAC;KAC9D,aAAa,CAAC,CAAC,GAAG,EAAE,EAAE,CACrB,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,cAAc,CAAC,yCAAyC,CAAC,CAC9E;KACA,aAAa,CAAC,CAAC,GAAG,EAAE,EAAE,CACrB,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,cAAc,CAAC,0CAA0C,CAAC,CACjF;KACA,aAAa,CAAC,CAAC,GAAG,EAAE,EAAE,CACrB,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC;KACjB,cAAc,CAAC,mCAAmC,CAAC;KACnD,eAAe,CAAC,CAAC,GAAG,EAAE,EAAE,CACvB,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC;KACjB,cAAc,CAAC,cAAc,CAAC;KAC9B,WAAW,CAAC,IAAI,CAAC;KACjB,UAAU,CACT,EAAE,IAAI,EAAE,yBAAyB,EAAE,KAAK,EAAE,MAAM,EAAE,EAClD,EAAE,IAAI,EAAE,mBAAmB,EAAE,KAAK,EAAE,QAAQ,EAAE,EAC9C,EAAE,IAAI,EAAE,qBAAqB,EAAE,KAAK,EAAE,OAAO,EAAE,CAChD,CACJ,CACJ,CAAC;AAEJ,uDAAuD;AACvD,MAAM,CAAC,MAAM,QAAQ,GAAG,IAAI,GAAG,EAA0B,CAAC;AAE1D,4DAA4D;AAC5D,QAAQ,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,WAAwC,EAAE,EAAE;IACrE,MAAM,UAAU,GAAG,WAAW,CAAC,OAAO,CAAC,aAAa,EAAE,CAAC;IAEvD,IAAI,UAAU,KAAK,MAAM,EAAE,CAAC;QAC1B,MAAM,UAAU,CAAC,WAAW,CAAC,CAAC;IAChC,CAAC;SAAM,IAAI,UAAU,KAAK,QAAQ,EAAE,CAAC;QACnC,MAAM,YAAY,CAAC,WAAW,CAAC,CAAC;IAClC,CAAC;SAAM,IAAI,UAAU,KAAK,OAAO,EAAE,CAAC;QAClC,MAAM,WAAW,CAAC,WAAW,CAAC,CAAC;IACjC,CAAC;AACH,CAAC,CAAC,CAAC;AAEH;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,QAAgB,EAChB,KAAa,EACb,OAAe;IAEf,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IACzD,MAAM,WAAW,GAAG,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC;IAE1C,MAAM,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,wBAAwB,CAAC,QAAQ,EAAE,OAAO,CAAC,EAAE;QACjE,IAAI,EAAE,WAAW;KAClB,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,5 @@
1
+ import { type ChatInputCommandInteraction } from 'discord.js';
2
+ /**
3
+ * Handle `/a4d init` -- create the A4D category and channel structure.
4
+ */
5
+ export declare function handleInit(interaction: ChatInputCommandInteraction): Promise<void>;