create-bunspace 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 (182) hide show
  1. package/README.md +181 -0
  2. package/dist/bin.js +5755 -0
  3. package/dist/templates/monorepo/CLAUDE.md +164 -0
  4. package/dist/templates/monorepo/LICENSE +21 -0
  5. package/dist/templates/monorepo/MUST-FOLLOW-GUIDELINES.md +269 -0
  6. package/dist/templates/monorepo/README.md +74 -0
  7. package/dist/templates/monorepo/SYNC_VERIFICATION.md +1 -0
  8. package/dist/templates/monorepo/apps/example/package.json +19 -0
  9. package/dist/templates/monorepo/apps/example/src/index.ts +23 -0
  10. package/dist/templates/monorepo/apps/example/src/types/index.ts +7 -0
  11. package/dist/templates/monorepo/apps/example/src/utils/index.ts +7 -0
  12. package/dist/templates/monorepo/core/packages/main/package.json +41 -0
  13. package/dist/templates/monorepo/core/packages/main/rolldown.config.ts +24 -0
  14. package/dist/templates/monorepo/core/packages/main/src/index.ts +80 -0
  15. package/dist/templates/monorepo/core/packages/main/src/types/constants.ts +15 -0
  16. package/dist/templates/monorepo/core/packages/main/src/types/index.ts +8 -0
  17. package/dist/templates/monorepo/core/packages/main/src/types/main.types.ts +25 -0
  18. package/dist/templates/monorepo/core/packages/main/src/utils/index.ts +5 -0
  19. package/dist/templates/monorepo/core/packages/utils/package.json +43 -0
  20. package/dist/templates/monorepo/core/packages/utils/rolldown.config.ts +34 -0
  21. package/dist/templates/monorepo/core/packages/utils/src/index.ts +2 -0
  22. package/dist/templates/monorepo/core/packages/utils/src/logger.ts +68 -0
  23. package/dist/templates/monorepo/core/packages/utils/src/result.ts +146 -0
  24. package/dist/templates/monorepo/core/packages/utils/src/types/constants.ts +15 -0
  25. package/dist/templates/monorepo/core/packages/utils/src/types/index.ts +8 -0
  26. package/dist/templates/monorepo/core/packages/utils/src/types/utils.types.ts +32 -0
  27. package/dist/templates/monorepo/core/packages/utils/src/utils/index.ts +5 -0
  28. package/dist/templates/monorepo/oxlint.json +14 -0
  29. package/dist/templates/monorepo/package.json +39 -0
  30. package/dist/templates/monorepo/tsconfig.json +35 -0
  31. package/dist/templates/telegram-bot/.oxlintrc.json +33 -0
  32. package/dist/templates/telegram-bot/.prettierignore +5 -0
  33. package/dist/templates/telegram-bot/.prettierrc +26 -0
  34. package/dist/templates/telegram-bot/CLAUDE.deploy.md +356 -0
  35. package/dist/templates/telegram-bot/CLAUDE.dev.md +266 -0
  36. package/dist/templates/telegram-bot/CLAUDE.md +280 -0
  37. package/dist/templates/telegram-bot/Dockerfile +46 -0
  38. package/dist/templates/telegram-bot/README.md +245 -0
  39. package/dist/templates/telegram-bot/apps/.gitkeep +0 -0
  40. package/dist/templates/telegram-bot/bun.lock +208 -0
  41. package/dist/templates/telegram-bot/core/.env.example +71 -0
  42. package/dist/templates/telegram-bot/core/README.md +1067 -0
  43. package/dist/templates/telegram-bot/core/package.json +15 -0
  44. package/dist/templates/telegram-bot/core/src/config/env.ts +131 -0
  45. package/dist/templates/telegram-bot/core/src/config/index.ts +97 -0
  46. package/dist/templates/telegram-bot/core/src/config/logging.ts +110 -0
  47. package/dist/templates/telegram-bot/core/src/handlers/control.ts +85 -0
  48. package/dist/templates/telegram-bot/core/src/handlers/health.ts +83 -0
  49. package/dist/templates/telegram-bot/core/src/handlers/logs.ts +126 -0
  50. package/dist/templates/telegram-bot/core/src/index.ts +161 -0
  51. package/dist/templates/telegram-bot/core/src/middleware/auth.ts +41 -0
  52. package/dist/templates/telegram-bot/core/src/middleware/error-handler.ts +41 -0
  53. package/dist/templates/telegram-bot/core/src/middleware/logging.ts +1 -0
  54. package/dist/templates/telegram-bot/core/src/middleware/topics.ts +55 -0
  55. package/dist/templates/telegram-bot/core/src/types/bot.ts +92 -0
  56. package/dist/templates/telegram-bot/core/src/types/constants.ts +50 -0
  57. package/dist/templates/telegram-bot/core/src/types/result.ts +1 -0
  58. package/dist/templates/telegram-bot/core/src/utils/bot-manager.test.ts +111 -0
  59. package/dist/templates/telegram-bot/core/src/utils/bot-manager.ts +201 -0
  60. package/dist/templates/telegram-bot/core/src/utils/commands.ts +63 -0
  61. package/dist/templates/telegram-bot/core/src/utils/formatters.ts +82 -0
  62. package/dist/templates/telegram-bot/core/src/utils/instance-manager.ts +189 -0
  63. package/dist/templates/telegram-bot/core/src/utils/memory.ts +33 -0
  64. package/dist/templates/telegram-bot/core/src/utils/result.ts +26 -0
  65. package/dist/templates/telegram-bot/core/src/utils/telegram.ts +31 -0
  66. package/dist/templates/telegram-bot/core/src/utils/type-guards.ts +71 -0
  67. package/dist/templates/telegram-bot/core/tsconfig.json +9 -0
  68. package/dist/templates/telegram-bot/docker-compose.yml +37 -0
  69. package/dist/templates/telegram-bot/docs/cli-commands.md +377 -0
  70. package/dist/templates/telegram-bot/docs/development.md +363 -0
  71. package/dist/templates/telegram-bot/docs/environment.md +460 -0
  72. package/dist/templates/telegram-bot/docs/examples/middleware-auth.md +335 -0
  73. package/dist/templates/telegram-bot/docs/examples/simple-command.md +207 -0
  74. package/dist/templates/telegram-bot/docs/examples/webhook-setup.md +362 -0
  75. package/dist/templates/telegram-bot/docs/getting-started.md +223 -0
  76. package/dist/templates/telegram-bot/docs/troubleshooting.md +489 -0
  77. package/dist/templates/telegram-bot/package.json +49 -0
  78. package/dist/templates/telegram-bot/packages/utils/package.json +12 -0
  79. package/dist/templates/telegram-bot/packages/utils/src/index.ts +2 -0
  80. package/dist/templates/telegram-bot/packages/utils/src/logger.ts +72 -0
  81. package/dist/templates/telegram-bot/packages/utils/src/result.ts +80 -0
  82. package/dist/templates/telegram-bot/tools/README.md +47 -0
  83. package/dist/templates/telegram-bot/tools/commands/doctor.ts +460 -0
  84. package/dist/templates/telegram-bot/tools/commands/index.ts +35 -0
  85. package/dist/templates/telegram-bot/tools/commands/ngrok.ts +207 -0
  86. package/dist/templates/telegram-bot/tools/commands/setup.ts +368 -0
  87. package/dist/templates/telegram-bot/tools/commands/status.ts +140 -0
  88. package/dist/templates/telegram-bot/tools/index.ts +16 -0
  89. package/dist/templates/telegram-bot/tools/package.json +12 -0
  90. package/dist/templates/telegram-bot/tools/utils/index.ts +13 -0
  91. package/dist/templates/telegram-bot/tsconfig.json +22 -0
  92. package/dist/templates/telegram-bot/vitest.config.ts +29 -0
  93. package/package.json +35 -0
  94. package/templates/monorepo/CLAUDE.md +164 -0
  95. package/templates/monorepo/LICENSE +21 -0
  96. package/templates/monorepo/MUST-FOLLOW-GUIDELINES.md +269 -0
  97. package/templates/monorepo/README.md +74 -0
  98. package/templates/monorepo/apps/example/package.json +19 -0
  99. package/templates/monorepo/apps/example/src/index.ts +23 -0
  100. package/templates/monorepo/apps/example/src/types/index.ts +7 -0
  101. package/templates/monorepo/apps/example/src/utils/index.ts +7 -0
  102. package/templates/monorepo/core/packages/main/package.json +41 -0
  103. package/templates/monorepo/core/packages/main/rolldown.config.ts +24 -0
  104. package/templates/monorepo/core/packages/main/src/index.ts +80 -0
  105. package/templates/monorepo/core/packages/main/src/types/constants.ts +15 -0
  106. package/templates/monorepo/core/packages/main/src/types/index.ts +8 -0
  107. package/templates/monorepo/core/packages/main/src/types/main.types.ts +25 -0
  108. package/templates/monorepo/core/packages/main/src/utils/index.ts +5 -0
  109. package/templates/monorepo/core/packages/utils/package.json +43 -0
  110. package/templates/monorepo/core/packages/utils/rolldown.config.ts +34 -0
  111. package/templates/monorepo/core/packages/utils/src/index.ts +2 -0
  112. package/templates/monorepo/core/packages/utils/src/logger.ts +68 -0
  113. package/templates/monorepo/core/packages/utils/src/result.ts +146 -0
  114. package/templates/monorepo/core/packages/utils/src/types/constants.ts +15 -0
  115. package/templates/monorepo/core/packages/utils/src/types/index.ts +8 -0
  116. package/templates/monorepo/core/packages/utils/src/types/utils.types.ts +32 -0
  117. package/templates/monorepo/core/packages/utils/src/utils/index.ts +5 -0
  118. package/templates/monorepo/oxlint.json +14 -0
  119. package/templates/monorepo/package.json +39 -0
  120. package/templates/monorepo/tsconfig.json +35 -0
  121. package/templates/telegram-bot/.oxlintrc.json +33 -0
  122. package/templates/telegram-bot/.prettierignore +5 -0
  123. package/templates/telegram-bot/.prettierrc +26 -0
  124. package/templates/telegram-bot/CLAUDE.deploy.md +356 -0
  125. package/templates/telegram-bot/CLAUDE.dev.md +266 -0
  126. package/templates/telegram-bot/CLAUDE.md +280 -0
  127. package/templates/telegram-bot/Dockerfile +46 -0
  128. package/templates/telegram-bot/README.md +245 -0
  129. package/templates/telegram-bot/apps/.gitkeep +0 -0
  130. package/templates/telegram-bot/bun.lock +208 -0
  131. package/templates/telegram-bot/core/.env.example +71 -0
  132. package/templates/telegram-bot/core/README.md +1067 -0
  133. package/templates/telegram-bot/core/package.json +15 -0
  134. package/templates/telegram-bot/core/src/config/env.ts +131 -0
  135. package/templates/telegram-bot/core/src/config/index.ts +97 -0
  136. package/templates/telegram-bot/core/src/config/logging.ts +110 -0
  137. package/templates/telegram-bot/core/src/handlers/control.ts +85 -0
  138. package/templates/telegram-bot/core/src/handlers/health.ts +83 -0
  139. package/templates/telegram-bot/core/src/handlers/logs.ts +126 -0
  140. package/templates/telegram-bot/core/src/index.ts +161 -0
  141. package/templates/telegram-bot/core/src/middleware/auth.ts +41 -0
  142. package/templates/telegram-bot/core/src/middleware/error-handler.ts +41 -0
  143. package/templates/telegram-bot/core/src/middleware/logging.ts +1 -0
  144. package/templates/telegram-bot/core/src/middleware/topics.ts +55 -0
  145. package/templates/telegram-bot/core/src/types/bot.ts +92 -0
  146. package/templates/telegram-bot/core/src/types/constants.ts +50 -0
  147. package/templates/telegram-bot/core/src/types/result.ts +1 -0
  148. package/templates/telegram-bot/core/src/utils/bot-manager.test.ts +111 -0
  149. package/templates/telegram-bot/core/src/utils/bot-manager.ts +201 -0
  150. package/templates/telegram-bot/core/src/utils/commands.ts +63 -0
  151. package/templates/telegram-bot/core/src/utils/formatters.ts +82 -0
  152. package/templates/telegram-bot/core/src/utils/instance-manager.ts +189 -0
  153. package/templates/telegram-bot/core/src/utils/memory.ts +33 -0
  154. package/templates/telegram-bot/core/src/utils/result.ts +26 -0
  155. package/templates/telegram-bot/core/src/utils/telegram.ts +31 -0
  156. package/templates/telegram-bot/core/src/utils/type-guards.ts +71 -0
  157. package/templates/telegram-bot/core/tsconfig.json +9 -0
  158. package/templates/telegram-bot/docker-compose.yml +37 -0
  159. package/templates/telegram-bot/docs/cli-commands.md +377 -0
  160. package/templates/telegram-bot/docs/development.md +363 -0
  161. package/templates/telegram-bot/docs/environment.md +460 -0
  162. package/templates/telegram-bot/docs/examples/middleware-auth.md +335 -0
  163. package/templates/telegram-bot/docs/examples/simple-command.md +207 -0
  164. package/templates/telegram-bot/docs/examples/webhook-setup.md +362 -0
  165. package/templates/telegram-bot/docs/getting-started.md +223 -0
  166. package/templates/telegram-bot/docs/troubleshooting.md +489 -0
  167. package/templates/telegram-bot/package.json +49 -0
  168. package/templates/telegram-bot/packages/utils/package.json +12 -0
  169. package/templates/telegram-bot/packages/utils/src/index.ts +2 -0
  170. package/templates/telegram-bot/packages/utils/src/logger.ts +72 -0
  171. package/templates/telegram-bot/packages/utils/src/result.ts +80 -0
  172. package/templates/telegram-bot/tools/README.md +47 -0
  173. package/templates/telegram-bot/tools/commands/doctor.ts +460 -0
  174. package/templates/telegram-bot/tools/commands/index.ts +35 -0
  175. package/templates/telegram-bot/tools/commands/ngrok.ts +207 -0
  176. package/templates/telegram-bot/tools/commands/setup.ts +368 -0
  177. package/templates/telegram-bot/tools/commands/status.ts +140 -0
  178. package/templates/telegram-bot/tools/index.ts +16 -0
  179. package/templates/telegram-bot/tools/package.json +12 -0
  180. package/templates/telegram-bot/tools/utils/index.ts +13 -0
  181. package/templates/telegram-bot/tsconfig.json +22 -0
  182. package/templates/telegram-bot/vitest.config.ts +29 -0
@@ -0,0 +1,47 @@
1
+ # CLI Tools
2
+
3
+ ## Usage
4
+
5
+ ```bash
6
+ bun run cli # Show all commands
7
+ bun run cli ngrok # Run ngrok command
8
+ bun run ngrok # Shortcut for ngrok command
9
+ ```
10
+
11
+ ## Adding New Commands
12
+
13
+ 1. Create file in `tools/commands/mycommand.ts`
14
+ 2. Export default object with `BotCommand` interface
15
+ 3. Implement `register(program)` method
16
+
17
+ Example:
18
+
19
+ ```typescript
20
+ import { Command } from 'commander'
21
+ import type { BotCommand } from './index.js'
22
+
23
+ interface MyCommand extends BotCommand {
24
+ name: 'mycommand'
25
+ description: string
26
+ register: (program: Command) => void
27
+ }
28
+
29
+ const command: MyCommand = {
30
+ name: 'mycommand',
31
+ description: 'My command description',
32
+
33
+ register(program: Command) {
34
+ program
35
+ .command('mycommand')
36
+ .description('Does something')
37
+ .option('-o, --option <value>', 'Option description', 'default')
38
+ .action(async (options) => {
39
+ // Implementation
40
+ })
41
+ },
42
+ }
43
+
44
+ export default command
45
+ ```
46
+
47
+ Commands are auto-discovered and registered on CLI startup.
@@ -0,0 +1,460 @@
1
+ import { Command } from 'commander'
2
+ import { readFile } from 'fs/promises'
3
+ import { existsSync } from 'fs'
4
+ import { join } from 'path'
5
+ import chalk from 'chalk'
6
+ import type { BotCommand } from './index.js'
7
+
8
+ const cliLogger = {
9
+ info: (msg: string) => console.log(chalk.blue('ℹ'), msg),
10
+ success: (msg: string) => console.log(chalk.green('✓'), msg),
11
+ error: (msg: string) => console.error(chalk.red('✗'), msg),
12
+ warn: (msg: string) => console.log(chalk.yellow('⚠'), msg),
13
+ }
14
+
15
+ interface DoctorCommand extends BotCommand {
16
+ name: 'doctor'
17
+ description: string
18
+ register: (program: Command) => void
19
+ }
20
+
21
+ interface CheckResult {
22
+ name: string
23
+ status: 'pass' | 'warn' | 'fail'
24
+ message: string
25
+ details?: string
26
+ }
27
+
28
+ const command: DoctorCommand = {
29
+ name: 'doctor',
30
+ description: 'Diagnose bot configuration and environment',
31
+
32
+ register(program: Command) {
33
+ program
34
+ .command('doctor')
35
+ .description('Run diagnostics on bot configuration')
36
+ .action(async () => {
37
+ await handleDoctor()
38
+ })
39
+ },
40
+ }
41
+
42
+ export default command
43
+
44
+ async function handleDoctor(): Promise<void> {
45
+ console.log('')
46
+ console.log(chalk.cyan.bold('mks-telegram-bot Diagnostics'))
47
+ console.log('')
48
+
49
+ const checks: CheckResult[] = []
50
+
51
+ // Run all checks
52
+ checks.push(await checkNodeVersion())
53
+ checks.push(await checkBunVersion())
54
+ checks.push(await checkDependencies())
55
+ checks.push(await checkEnvFile())
56
+ checks.push(await checkRequiredEnvVars())
57
+ checks.push(await checkBotToken())
58
+ checks.push(await checkTmpDirectory())
59
+ checks.push(await checkLogsDirectory())
60
+ checks.push(await checkPorts())
61
+ checks.push(await checkGitIgnore())
62
+
63
+ // Display results
64
+ displayResults(checks)
65
+
66
+ // Exit with appropriate code
67
+ const failed = checks.filter((c) => c.status === 'fail')
68
+ if (failed.length > 0) {
69
+ process.exit(1)
70
+ }
71
+ }
72
+
73
+ async function checkNodeVersion(): Promise<CheckResult> {
74
+ const version = process.version
75
+ const versionParts = version.slice(1).split('.')
76
+ const major = Number.parseInt(versionParts[0] ?? '0', 10)
77
+
78
+ if (major >= 20) {
79
+ return {
80
+ name: 'Node.js version',
81
+ status: 'pass',
82
+ message: `Node.js ${version} (requires >= 20)`,
83
+ }
84
+ }
85
+
86
+ return {
87
+ name: 'Node.js version',
88
+ status: 'fail',
89
+ message: `Node.js ${version} (requires >= 20)`,
90
+ details: 'Please upgrade Node.js to version 20 or later',
91
+ }
92
+ }
93
+
94
+ async function checkBunVersion(): Promise<CheckResult> {
95
+ try {
96
+ const result = await Bun.spawn(['bun', '--version'], {
97
+ stdout: 'pipe',
98
+ stderr: 'pipe',
99
+ }).exited
100
+
101
+ if (result === 0) {
102
+ return {
103
+ name: 'Bun installation',
104
+ status: 'pass',
105
+ message: 'Bun is installed',
106
+ }
107
+ }
108
+
109
+ return {
110
+ name: 'Bun installation',
111
+ status: 'fail',
112
+ message: 'Bun not found',
113
+ details: 'Install Bun from https://bun.sh',
114
+ }
115
+ } catch {
116
+ return {
117
+ name: 'Bun installation',
118
+ status: 'fail',
119
+ message: 'Bun not found',
120
+ details: 'Install Bun from https://bun.sh',
121
+ }
122
+ }
123
+ }
124
+
125
+ async function checkDependencies(): Promise<CheckResult> {
126
+ const packageJsonPath = join(process.cwd(), 'package.json')
127
+ const lockPath = join(process.cwd(), 'bun.lock')
128
+
129
+ if (!existsSync(packageJsonPath)) {
130
+ return {
131
+ name: 'Dependencies',
132
+ status: 'fail',
133
+ message: 'package.json not found',
134
+ details: 'You are not in a valid project directory',
135
+ }
136
+ }
137
+
138
+ if (!existsSync(lockPath)) {
139
+ return {
140
+ name: 'Dependencies',
141
+ status: 'fail',
142
+ message: 'Dependencies not installed',
143
+ details: 'Run: bun install',
144
+ }
145
+ }
146
+
147
+ const corePackagePath = join(process.cwd(), 'core', 'node_modules')
148
+
149
+ if (!existsSync(corePackagePath)) {
150
+ return {
151
+ name: 'Dependencies',
152
+ status: 'fail',
153
+ message: 'Workspace dependencies not installed',
154
+ details: 'Run: bun install',
155
+ }
156
+ }
157
+
158
+ return {
159
+ name: 'Dependencies',
160
+ status: 'pass',
161
+ message: 'All dependencies installed',
162
+ }
163
+ }
164
+
165
+ async function checkEnvFile(): Promise<CheckResult> {
166
+ const envFiles = ['.env.local', '.env.staging', '.env.production']
167
+ const foundFiles: string[] = []
168
+
169
+ for (const file of envFiles) {
170
+ const path = join(process.cwd(), 'core', file)
171
+ if (existsSync(path)) {
172
+ foundFiles.push(file)
173
+ }
174
+ }
175
+
176
+ if (foundFiles.length > 0) {
177
+ return {
178
+ name: 'Environment files',
179
+ status: 'pass',
180
+ message: `Found: ${foundFiles.join(', ')}`,
181
+ }
182
+ }
183
+
184
+ return {
185
+ name: 'Environment files',
186
+ status: 'fail',
187
+ message: 'No .env file found',
188
+ details: 'Run: bun run setup',
189
+ }
190
+ }
191
+
192
+ async function checkRequiredEnvVars(): Promise<CheckResult> {
193
+ const envPath = join(process.cwd(), 'core', '.env.local')
194
+
195
+ if (!existsSync(envPath)) {
196
+ return {
197
+ name: 'Environment variables',
198
+ status: 'warn',
199
+ message: 'No .env.local file to check',
200
+ details: 'Run: bun run setup',
201
+ }
202
+ }
203
+
204
+ try {
205
+ const content = await readFile(envPath, 'utf-8')
206
+ const missing: string[] = []
207
+
208
+ // Check for required variables
209
+ if (!content.includes('TG_BOT_TOKEN=') || content.includes('TG_BOT_TOKEN=123456:')) {
210
+ missing.push('TG_BOT_TOKEN')
211
+ }
212
+
213
+ if (!content.includes('TG_MODE=')) {
214
+ missing.push('TG_MODE')
215
+ }
216
+
217
+ if (missing.length === 0) {
218
+ return {
219
+ name: 'Environment variables',
220
+ status: 'pass',
221
+ message: 'Required variables set',
222
+ }
223
+ }
224
+
225
+ return {
226
+ name: 'Environment variables',
227
+ status: 'fail',
228
+ message: `Missing: ${missing.join(', ')}`,
229
+ details: 'Edit core/.env.local or run: bun run setup',
230
+ }
231
+ } catch {
232
+ return {
233
+ name: 'Environment variables',
234
+ status: 'warn',
235
+ message: 'Could not read .env.local',
236
+ }
237
+ }
238
+ }
239
+
240
+ async function checkBotToken(): Promise<CheckResult> {
241
+ const envPath = join(process.cwd(), 'core', '.env.local')
242
+
243
+ if (!existsSync(envPath)) {
244
+ return {
245
+ name: 'Bot token validation',
246
+ status: 'warn',
247
+ message: 'No .env.local file found',
248
+ details: 'Run: bun run setup',
249
+ }
250
+ }
251
+
252
+ try {
253
+ const content = await readFile(envPath, 'utf-8')
254
+ const match = content.match(/TG_BOT_TOKEN=([^\n]+)/)
255
+
256
+ if (!match || match[1] === '123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11') {
257
+ return {
258
+ name: 'Bot token validation',
259
+ status: 'fail',
260
+ message: 'Invalid or placeholder bot token',
261
+ details: 'Get a token from @BotFather and run: bun run setup',
262
+ }
263
+ }
264
+
265
+ const token = (match[1] ?? '').trim()
266
+
267
+ // Try to validate with Telegram API
268
+ try {
269
+ const response = await fetch(`https://api.telegram.org/bot${token}/getMe`, {
270
+ signal: AbortSignal.timeout(5000),
271
+ })
272
+ const data = (await response.json()) as { ok: boolean; description?: string }
273
+
274
+ if (data.ok) {
275
+ return {
276
+ name: 'Bot token validation',
277
+ status: 'pass',
278
+ message: 'Bot token is valid',
279
+ }
280
+ }
281
+
282
+ return {
283
+ name: 'Bot token validation',
284
+ status: 'fail',
285
+ message: 'Invalid bot token',
286
+ details: data.description ?? 'Token rejected by Telegram API',
287
+ }
288
+ } catch {
289
+ return {
290
+ name: 'Bot token validation',
291
+ status: 'warn',
292
+ message: 'Could not validate token (network error)',
293
+ details: 'Check your internet connection',
294
+ }
295
+ }
296
+ } catch {
297
+ return {
298
+ name: 'Bot token validation',
299
+ status: 'warn',
300
+ message: 'Could not read bot token',
301
+ }
302
+ }
303
+ }
304
+
305
+ async function checkTmpDirectory(): Promise<CheckResult> {
306
+ const tmpPath = join(process.cwd(), 'core', 'tmp')
307
+
308
+ if (!existsSync(tmpPath)) {
309
+ return {
310
+ name: 'Temp directory',
311
+ status: 'warn',
312
+ message: 'core/tmp does not exist',
313
+ details: 'It will be created automatically when the bot starts',
314
+ }
315
+ }
316
+
317
+ // Check if writable
318
+ try {
319
+ const testFile = join(tmpPath, `.write-test-${Date.now()}`)
320
+ await Bun.write(testFile, 'test')
321
+ await Bun.file(testFile).delete()
322
+ return {
323
+ name: 'Temp directory',
324
+ status: 'pass',
325
+ message: 'core/tmp is writable',
326
+ }
327
+ } catch {
328
+ return {
329
+ name: 'Temp directory',
330
+ status: 'fail',
331
+ message: 'core/tmp is not writable',
332
+ details: 'Check permissions on core/tmp directory',
333
+ }
334
+ }
335
+ }
336
+
337
+ async function checkLogsDirectory(): Promise<CheckResult> {
338
+ const logsPath = join(process.cwd(), 'core', 'logs')
339
+
340
+ if (!existsSync(logsPath)) {
341
+ return {
342
+ name: 'Logs directory',
343
+ status: 'warn',
344
+ message: 'core/logs does not exist',
345
+ details: 'It will be created automatically when needed',
346
+ }
347
+ }
348
+
349
+ return {
350
+ name: 'Logs directory',
351
+ status: 'pass',
352
+ message: 'core/logs exists',
353
+ }
354
+ }
355
+
356
+ async function checkPorts(): Promise<CheckResult> {
357
+ const port = 3000
358
+ const server = Bun.serve({
359
+ port,
360
+ fetch: () => new Response('OK'),
361
+ })
362
+
363
+ const portInUse = server.port === port
364
+
365
+ await server.stop(true)
366
+
367
+ if (portInUse) {
368
+ return {
369
+ name: 'Port availability',
370
+ status: 'pass',
371
+ message: `Port ${port} is available`,
372
+ }
373
+ }
374
+
375
+ return {
376
+ name: 'Port availability',
377
+ status: 'warn',
378
+ message: `Port ${port} may be in use`,
379
+ details: 'If you have issues, check for other processes using this port',
380
+ }
381
+ }
382
+
383
+ async function checkGitIgnore(): Promise<CheckResult> {
384
+ const gitIgnorePath = join(process.cwd(), '.gitignore')
385
+
386
+ if (!existsSync(gitIgnorePath)) {
387
+ return {
388
+ name: 'Git ignore',
389
+ status: 'warn',
390
+ message: '.gitignore not found',
391
+ details: 'Consider adding .gitignore to exclude .env files',
392
+ }
393
+ }
394
+
395
+ try {
396
+ const content = await readFile(gitIgnorePath, 'utf-8')
397
+ const hasEnvIgnore = content.includes('.env') || content.includes('core/.env')
398
+
399
+ if (hasEnvIgnore) {
400
+ return {
401
+ name: 'Git ignore',
402
+ status: 'pass',
403
+ message: '.env files are excluded from git',
404
+ }
405
+ }
406
+
407
+ return {
408
+ name: 'Git ignore',
409
+ status: 'warn',
410
+ message: '.env files may not be excluded from git',
411
+ details: 'Add "*.env" or "core/.env*" to .gitignore',
412
+ }
413
+ } catch {
414
+ return {
415
+ name: 'Git ignore',
416
+ status: 'warn',
417
+ message: 'Could not read .gitignore',
418
+ }
419
+ }
420
+ }
421
+
422
+ function displayResults(checks: CheckResult[]): void {
423
+ const passed = checks.filter((c) => c.status === 'pass')
424
+ const warnings = checks.filter((c) => c.status === 'warn')
425
+ const failed = checks.filter((c) => c.status === 'fail')
426
+
427
+ // Display checks
428
+ for (const check of checks) {
429
+ const icon = check.status === 'pass' ? chalk.green('✓') : check.status === 'warn' ? chalk.yellow('⚠') : chalk.red('✗')
430
+ const name = check.name.padEnd(30)
431
+
432
+ console.log(`${icon} ${chalk.bold(name)} ${check.message}`)
433
+
434
+ if (check.details) {
435
+ console.log(` ${chalk.dim(check.details)}`)
436
+ }
437
+ }
438
+
439
+ // Summary
440
+ console.log('')
441
+ console.log(chalk.bold('Summary:'))
442
+ console.log(` ${chalk.green('✓')} Passed: ${passed.length}`)
443
+ console.log(` ${chalk.yellow('⚠')} Warnings: ${warnings.length}`)
444
+ console.log(` ${chalk.red('✗')} Failed: ${failed.length}`)
445
+ console.log('')
446
+
447
+ if (failed.length === 0) {
448
+ cliLogger.success('All checks passed! Your bot is ready to run.')
449
+ console.log('')
450
+ console.log('Next steps:')
451
+ console.log(' 1. Run: bun run dev')
452
+ console.log(' 2. Send /start to your bot')
453
+ } else {
454
+ cliLogger.error('Some checks failed. Please fix the issues above.')
455
+ console.log('')
456
+ console.log('Common fixes:')
457
+ console.log(' - Run: bun run setup')
458
+ console.log(' - Run: bun install')
459
+ }
460
+ }
@@ -0,0 +1,35 @@
1
+ import { Command } from 'commander'
2
+ import { readdirSync } from 'fs'
3
+ import { join } from 'path'
4
+ import { URL, fileURLToPath } from 'url'
5
+
6
+ const __dirname = fileURLToPath(new URL('.', import.meta.url))
7
+
8
+ export interface BotCommand {
9
+ name: string
10
+ description: string
11
+ register: (program: Command) => void
12
+ }
13
+
14
+ export async function registerCommands(program: Command): Promise<void> {
15
+ const commandsDir = join(__dirname)
16
+
17
+ const commandFiles = readdirSync(commandsDir).filter(
18
+ (file) => file.endsWith('.ts') && file !== 'index.ts'
19
+ )
20
+
21
+ // Sequential command loading is intentional for order preservation
22
+ /* oxlint-disable no-await-in-loop */
23
+ for (const file of commandFiles) {
24
+ try {
25
+ const module = await import(`./${file}`)
26
+ const command: BotCommand = module.default
27
+ if (command?.register) {
28
+ command.register(program)
29
+ }
30
+ } catch (error) {
31
+ console.error(`Failed to load command from ${file}:`, error)
32
+ }
33
+ }
34
+ /* oxlint-enable no-await-in-loop */
35
+ }