agent-messenger 1.0.0 → 1.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 (330) hide show
  1. package/.claude/commands/release.md +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.github/workflows/ci.yml +1 -1
  4. package/.github/workflows/e2e.yml.disabled +69 -0
  5. package/README.md +16 -14
  6. package/biome.json +33 -1
  7. package/bun.lock +63 -0
  8. package/dist/package.json +8 -4
  9. package/dist/src/cli.d.ts.map +1 -1
  10. package/dist/src/cli.js +4 -1
  11. package/dist/src/cli.js.map +1 -1
  12. package/dist/src/platforms/discord/cli.js +1 -1
  13. package/dist/src/platforms/discord/client.d.ts.map +1 -1
  14. package/dist/src/platforms/discord/client.js +3 -3
  15. package/dist/src/platforms/discord/client.js.map +1 -1
  16. package/dist/src/platforms/discord/commands/user.d.ts.map +1 -1
  17. package/dist/src/platforms/discord/commands/user.js +10 -1
  18. package/dist/src/platforms/discord/commands/user.js.map +1 -1
  19. package/dist/src/platforms/discord/credential-manager.d.ts.map +1 -1
  20. package/dist/src/platforms/discord/credential-manager.js +18 -12
  21. package/dist/src/platforms/discord/credential-manager.js.map +1 -1
  22. package/dist/src/platforms/slack/cli.js +1 -1
  23. package/dist/src/platforms/slack/credential-manager.d.ts.map +1 -1
  24. package/dist/src/platforms/slack/credential-manager.js +20 -6
  25. package/dist/src/platforms/slack/credential-manager.js.map +1 -1
  26. package/dist/src/platforms/slack/token-extractor.d.ts.map +1 -1
  27. package/dist/src/platforms/slack/token-extractor.js +34 -9
  28. package/dist/src/platforms/slack/token-extractor.js.map +1 -1
  29. package/dist/src/platforms/teams/cli.d.ts.map +1 -0
  30. package/dist/{cli.js → src/platforms/teams/cli.js} +11 -10
  31. package/dist/src/platforms/teams/cli.js.map +1 -0
  32. package/dist/src/platforms/teams/client.d.ts +32 -0
  33. package/dist/src/platforms/teams/client.d.ts.map +1 -0
  34. package/dist/src/platforms/teams/client.js +202 -0
  35. package/dist/src/platforms/teams/client.js.map +1 -0
  36. package/dist/src/platforms/teams/commands/auth.d.ts +14 -0
  37. package/dist/src/platforms/teams/commands/auth.d.ts.map +1 -0
  38. package/dist/src/platforms/teams/commands/auth.js +176 -0
  39. package/dist/src/platforms/teams/commands/auth.js.map +1 -0
  40. package/dist/src/platforms/teams/commands/channel.d.ts +13 -0
  41. package/dist/src/platforms/teams/commands/channel.d.ts.map +1 -0
  42. package/dist/src/platforms/teams/commands/channel.js +97 -0
  43. package/dist/src/platforms/teams/commands/channel.js.map +1 -0
  44. package/dist/src/platforms/teams/commands/file.d.ts +12 -0
  45. package/dist/src/platforms/teams/commands/file.d.ts.map +1 -0
  46. package/dist/src/platforms/teams/commands/file.js +104 -0
  47. package/dist/src/platforms/teams/commands/file.js.map +1 -0
  48. package/dist/{commands → src/platforms/teams/commands}/index.d.ts +5 -2
  49. package/dist/src/platforms/teams/commands/index.d.ts.map +1 -0
  50. package/dist/{commands → src/platforms/teams/commands}/index.js +5 -2
  51. package/dist/src/platforms/teams/commands/index.js.map +1 -0
  52. package/dist/src/platforms/teams/commands/message.d.ts +17 -0
  53. package/dist/src/platforms/teams/commands/message.d.ts.map +1 -0
  54. package/dist/src/platforms/teams/commands/message.js +133 -0
  55. package/dist/src/platforms/teams/commands/message.js.map +1 -0
  56. package/dist/src/platforms/teams/commands/reaction.d.ts +9 -0
  57. package/dist/src/platforms/teams/commands/reaction.d.ts.map +1 -0
  58. package/dist/src/platforms/teams/commands/reaction.js +68 -0
  59. package/dist/src/platforms/teams/commands/reaction.js.map +1 -0
  60. package/dist/src/platforms/teams/commands/snapshot.d.ts +10 -0
  61. package/dist/src/platforms/teams/commands/snapshot.d.ts.map +1 -0
  62. package/dist/src/platforms/teams/commands/snapshot.js +85 -0
  63. package/dist/src/platforms/teams/commands/snapshot.js.map +1 -0
  64. package/dist/src/platforms/teams/commands/team.d.ts +18 -0
  65. package/dist/src/platforms/teams/commands/team.d.ts.map +1 -0
  66. package/dist/src/platforms/teams/commands/team.js +130 -0
  67. package/dist/src/platforms/teams/commands/team.js.map +1 -0
  68. package/dist/src/platforms/teams/commands/user.d.ts.map +1 -0
  69. package/dist/src/platforms/teams/commands/user.js +88 -0
  70. package/dist/src/platforms/teams/commands/user.js.map +1 -0
  71. package/dist/src/platforms/teams/credential-manager.d.ts +18 -0
  72. package/dist/src/platforms/teams/credential-manager.d.ts.map +1 -0
  73. package/dist/src/platforms/teams/credential-manager.js +81 -0
  74. package/dist/src/platforms/teams/credential-manager.js.map +1 -0
  75. package/dist/src/platforms/teams/index.d.ts +4 -0
  76. package/dist/src/platforms/teams/index.d.ts.map +1 -0
  77. package/dist/src/platforms/teams/index.js +6 -0
  78. package/dist/src/platforms/teams/index.js.map +1 -0
  79. package/dist/src/platforms/teams/token-extractor.d.ts +36 -0
  80. package/dist/src/platforms/teams/token-extractor.d.ts.map +1 -0
  81. package/dist/src/platforms/teams/token-extractor.js +335 -0
  82. package/dist/src/platforms/teams/token-extractor.js.map +1 -0
  83. package/dist/src/platforms/teams/types.d.ts +209 -0
  84. package/dist/src/platforms/teams/types.d.ts.map +1 -0
  85. package/dist/src/platforms/teams/types.js +65 -0
  86. package/dist/src/platforms/teams/types.js.map +1 -0
  87. package/docs/teams.md +321 -0
  88. package/e2e/README.md +256 -0
  89. package/e2e/config.ts +45 -0
  90. package/e2e/discord.e2e.test.ts +252 -0
  91. package/e2e/helpers.ts +107 -0
  92. package/e2e/slack.e2e.test.ts +309 -0
  93. package/package.json +8 -4
  94. package/scripts/postbuild.ts +15 -0
  95. package/skills/agent-teams/SKILL.md +292 -0
  96. package/skills/agent-teams/references/authentication.md +375 -0
  97. package/skills/agent-teams/references/common-patterns.md +596 -0
  98. package/skills/agent-teams/templates/monitor-channel.sh +239 -0
  99. package/skills/agent-teams/templates/post-message.sh +224 -0
  100. package/skills/agent-teams/templates/team-summary.sh +210 -0
  101. package/src/cli.ts +4 -0
  102. package/src/platforms/discord/client.ts +3 -3
  103. package/src/platforms/discord/commands/auth.test.ts +48 -32
  104. package/src/platforms/discord/commands/channel.test.ts +54 -42
  105. package/src/platforms/discord/commands/file.test.ts +40 -53
  106. package/src/platforms/discord/commands/guild.test.ts +47 -27
  107. package/src/platforms/discord/commands/message.test.ts +54 -51
  108. package/src/platforms/discord/commands/reaction.test.ts +54 -42
  109. package/src/platforms/discord/commands/user.ts +12 -1
  110. package/src/platforms/discord/credential-manager.test.ts +137 -136
  111. package/src/platforms/discord/credential-manager.ts +20 -13
  112. package/src/platforms/discord/token-extractor.test.ts +133 -383
  113. package/{tests → src/platforms/slack}/cli.test.ts +3 -3
  114. package/{tests/slack-client.test.ts → src/platforms/slack/client.test.ts} +1 -1
  115. package/{tests → src/platforms/slack}/commands/auth.test.ts +25 -13
  116. package/{tests → src/platforms/slack}/commands/channel.test.ts +2 -2
  117. package/{tests → src/platforms/slack}/commands/file.test.ts +2 -2
  118. package/{tests → src/platforms/slack}/commands/message.test.ts +2 -2
  119. package/{tests → src/platforms/slack}/commands/reaction.test.ts +1 -1
  120. package/{tests → src/platforms/slack}/commands/snapshot.test.ts +117 -105
  121. package/{tests → src/platforms/slack}/commands/user.test.ts +3 -3
  122. package/{tests → src/platforms/slack}/commands/workspace.test.ts +44 -95
  123. package/{tests → src/platforms/slack}/credential-manager.test.ts +2 -2
  124. package/src/platforms/slack/credential-manager.ts +22 -7
  125. package/src/platforms/slack/token-extractor-node-test.ts +40 -0
  126. package/src/platforms/slack/token-extractor-node.test.ts +10 -0
  127. package/src/platforms/slack/token-extractor.ts +36 -10
  128. package/{tests → src/platforms/slack}/types.test.ts +1 -1
  129. package/src/platforms/teams/cli.ts +36 -0
  130. package/src/platforms/teams/client.test.ts +500 -0
  131. package/src/platforms/teams/client.ts +365 -0
  132. package/src/platforms/teams/commands/auth.test.ts +99 -0
  133. package/src/platforms/teams/commands/auth.ts +232 -0
  134. package/src/platforms/teams/commands/channel.test.ts +147 -0
  135. package/src/platforms/teams/commands/channel.ts +129 -0
  136. package/src/platforms/teams/commands/file.test.ts +88 -0
  137. package/src/platforms/teams/commands/file.ts +144 -0
  138. package/src/platforms/teams/commands/index.ts +12 -0
  139. package/src/platforms/teams/commands/message.test.ts +110 -0
  140. package/src/platforms/teams/commands/message.ts +188 -0
  141. package/src/platforms/teams/commands/reaction.test.ts +87 -0
  142. package/src/platforms/teams/commands/reaction.ts +104 -0
  143. package/src/platforms/teams/commands/snapshot.test.ts +35 -0
  144. package/src/platforms/teams/commands/snapshot.ts +115 -0
  145. package/src/platforms/teams/commands/team.test.ts +157 -0
  146. package/src/platforms/teams/commands/team.ts +164 -0
  147. package/src/platforms/teams/commands/user.test.ts +83 -0
  148. package/src/platforms/teams/commands/user.ts +112 -0
  149. package/src/platforms/teams/credential-manager.test.ts +178 -0
  150. package/src/platforms/teams/credential-manager.ts +92 -0
  151. package/src/platforms/teams/index.ts +5 -0
  152. package/src/platforms/teams/token-extractor.test.ts +429 -0
  153. package/src/platforms/teams/token-extractor.ts +462 -0
  154. package/src/platforms/teams/types.test.ts +226 -0
  155. package/src/platforms/teams/types.ts +140 -0
  156. package/tsconfig.json +1 -1
  157. package/dist/cli.d.ts.map +0 -1
  158. package/dist/cli.js.map +0 -1
  159. package/dist/commands/auth.d.ts +0 -3
  160. package/dist/commands/auth.d.ts.map +0 -1
  161. package/dist/commands/auth.js +0 -140
  162. package/dist/commands/auth.js.map +0 -1
  163. package/dist/commands/channel.d.ts +0 -3
  164. package/dist/commands/channel.d.ts.map +0 -1
  165. package/dist/commands/channel.js +0 -118
  166. package/dist/commands/channel.js.map +0 -1
  167. package/dist/commands/file.d.ts +0 -3
  168. package/dist/commands/file.d.ts.map +0 -1
  169. package/dist/commands/file.js +0 -113
  170. package/dist/commands/file.js.map +0 -1
  171. package/dist/commands/index.d.ts.map +0 -1
  172. package/dist/commands/index.js.map +0 -1
  173. package/dist/commands/message.d.ts +0 -3
  174. package/dist/commands/message.d.ts.map +0 -1
  175. package/dist/commands/message.js +0 -214
  176. package/dist/commands/message.js.map +0 -1
  177. package/dist/commands/reaction.d.ts +0 -3
  178. package/dist/commands/reaction.d.ts.map +0 -1
  179. package/dist/commands/reaction.js +0 -100
  180. package/dist/commands/reaction.js.map +0 -1
  181. package/dist/commands/snapshot.d.ts +0 -3
  182. package/dist/commands/snapshot.d.ts.map +0 -1
  183. package/dist/commands/snapshot.js +0 -88
  184. package/dist/commands/snapshot.js.map +0 -1
  185. package/dist/commands/user.d.ts.map +0 -1
  186. package/dist/commands/user.js +0 -96
  187. package/dist/commands/user.js.map +0 -1
  188. package/dist/commands/workspace.d.ts +0 -3
  189. package/dist/commands/workspace.d.ts.map +0 -1
  190. package/dist/commands/workspace.js +0 -89
  191. package/dist/commands/workspace.js.map +0 -1
  192. package/dist/lib/credential-manager.d.ts +0 -13
  193. package/dist/lib/credential-manager.d.ts.map +0 -1
  194. package/dist/lib/credential-manager.js +0 -58
  195. package/dist/lib/credential-manager.js.map +0 -1
  196. package/dist/lib/index.d.ts +0 -3
  197. package/dist/lib/index.d.ts.map +0 -1
  198. package/dist/lib/index.js +0 -3
  199. package/dist/lib/index.js.map +0 -1
  200. package/dist/lib/ref-manager.d.ts +0 -26
  201. package/dist/lib/ref-manager.d.ts.map +0 -1
  202. package/dist/lib/ref-manager.js +0 -92
  203. package/dist/lib/ref-manager.js.map +0 -1
  204. package/dist/lib/slack-client.d.ts +0 -37
  205. package/dist/lib/slack-client.d.ts.map +0 -1
  206. package/dist/lib/slack-client.js +0 -379
  207. package/dist/lib/slack-client.js.map +0 -1
  208. package/dist/lib/token-extractor.d.ts +0 -28
  209. package/dist/lib/token-extractor.d.ts.map +0 -1
  210. package/dist/lib/token-extractor.js +0 -401
  211. package/dist/lib/token-extractor.js.map +0 -1
  212. package/dist/src/platforms/discord/client.test.d.ts +0 -2
  213. package/dist/src/platforms/discord/client.test.d.ts.map +0 -1
  214. package/dist/src/platforms/discord/client.test.js +0 -367
  215. package/dist/src/platforms/discord/client.test.js.map +0 -1
  216. package/dist/src/platforms/discord/commands/auth.test.d.ts +0 -2
  217. package/dist/src/platforms/discord/commands/auth.test.d.ts.map +0 -1
  218. package/dist/src/platforms/discord/commands/auth.test.js +0 -65
  219. package/dist/src/platforms/discord/commands/auth.test.js.map +0 -1
  220. package/dist/src/platforms/discord/commands/channel.test.d.ts +0 -2
  221. package/dist/src/platforms/discord/commands/channel.test.d.ts.map +0 -1
  222. package/dist/src/platforms/discord/commands/channel.test.js +0 -136
  223. package/dist/src/platforms/discord/commands/channel.test.js.map +0 -1
  224. package/dist/src/platforms/discord/commands/file.test.d.ts +0 -2
  225. package/dist/src/platforms/discord/commands/file.test.d.ts.map +0 -1
  226. package/dist/src/platforms/discord/commands/file.test.js +0 -83
  227. package/dist/src/platforms/discord/commands/file.test.js.map +0 -1
  228. package/dist/src/platforms/discord/commands/guild.test.d.ts +0 -2
  229. package/dist/src/platforms/discord/commands/guild.test.d.ts.map +0 -1
  230. package/dist/src/platforms/discord/commands/guild.test.js +0 -100
  231. package/dist/src/platforms/discord/commands/guild.test.js.map +0 -1
  232. package/dist/src/platforms/discord/commands/message.test.d.ts +0 -2
  233. package/dist/src/platforms/discord/commands/message.test.d.ts.map +0 -1
  234. package/dist/src/platforms/discord/commands/message.test.js +0 -91
  235. package/dist/src/platforms/discord/commands/message.test.js.map +0 -1
  236. package/dist/src/platforms/discord/commands/reaction.test.d.ts +0 -2
  237. package/dist/src/platforms/discord/commands/reaction.test.d.ts.map +0 -1
  238. package/dist/src/platforms/discord/commands/reaction.test.js +0 -115
  239. package/dist/src/platforms/discord/commands/reaction.test.js.map +0 -1
  240. package/dist/src/platforms/discord/commands/snapshot.test.d.ts +0 -2
  241. package/dist/src/platforms/discord/commands/snapshot.test.d.ts.map +0 -1
  242. package/dist/src/platforms/discord/commands/snapshot.test.js +0 -25
  243. package/dist/src/platforms/discord/commands/snapshot.test.js.map +0 -1
  244. package/dist/src/platforms/discord/commands/user.test.d.ts +0 -2
  245. package/dist/src/platforms/discord/commands/user.test.d.ts.map +0 -1
  246. package/dist/src/platforms/discord/commands/user.test.js +0 -103
  247. package/dist/src/platforms/discord/commands/user.test.js.map +0 -1
  248. package/dist/src/platforms/discord/credential-manager.test.d.ts +0 -2
  249. package/dist/src/platforms/discord/credential-manager.test.d.ts.map +0 -1
  250. package/dist/src/platforms/discord/credential-manager.test.js +0 -136
  251. package/dist/src/platforms/discord/credential-manager.test.js.map +0 -1
  252. package/dist/src/platforms/discord/token-extractor.test.d.ts +0 -2
  253. package/dist/src/platforms/discord/token-extractor.test.d.ts.map +0 -1
  254. package/dist/src/platforms/discord/token-extractor.test.js +0 -789
  255. package/dist/src/platforms/discord/token-extractor.test.js.map +0 -1
  256. package/dist/src/platforms/discord/types.test.d.ts +0 -2
  257. package/dist/src/platforms/discord/types.test.d.ts.map +0 -1
  258. package/dist/src/platforms/discord/types.test.js +0 -211
  259. package/dist/src/platforms/discord/types.test.js.map +0 -1
  260. package/dist/src/shared/utils/concurrency.test.d.ts +0 -2
  261. package/dist/src/shared/utils/concurrency.test.d.ts.map +0 -1
  262. package/dist/src/shared/utils/concurrency.test.js +0 -39
  263. package/dist/src/shared/utils/concurrency.test.js.map +0 -1
  264. package/dist/tests/cli.test.d.ts +0 -2
  265. package/dist/tests/cli.test.d.ts.map +0 -1
  266. package/dist/tests/cli.test.js +0 -83
  267. package/dist/tests/cli.test.js.map +0 -1
  268. package/dist/tests/commands/.test-slack-data/Local Storage/leveldb/CURRENT +0 -1
  269. package/dist/tests/commands/.test-slack-data/Local Storage/leveldb/LOCK +0 -0
  270. package/dist/tests/commands/.test-slack-data/Local Storage/leveldb/LOG +0 -3
  271. package/dist/tests/commands/.test-slack-data/Local Storage/leveldb/LOG.old +0 -1
  272. package/dist/tests/commands/.test-slack-data/Local Storage/leveldb/MANIFEST-000004 +0 -0
  273. package/dist/tests/commands/auth.test.d.ts +0 -2
  274. package/dist/tests/commands/auth.test.d.ts.map +0 -1
  275. package/dist/tests/commands/auth.test.js +0 -304
  276. package/dist/tests/commands/auth.test.js.map +0 -1
  277. package/dist/tests/commands/channel.test.d.ts +0 -2
  278. package/dist/tests/commands/channel.test.d.ts.map +0 -1
  279. package/dist/tests/commands/channel.test.js +0 -166
  280. package/dist/tests/commands/channel.test.js.map +0 -1
  281. package/dist/tests/commands/file.test.d.ts +0 -2
  282. package/dist/tests/commands/file.test.d.ts.map +0 -1
  283. package/dist/tests/commands/file.test.js +0 -175
  284. package/dist/tests/commands/file.test.js.map +0 -1
  285. package/dist/tests/commands/message.test.d.ts +0 -2
  286. package/dist/tests/commands/message.test.d.ts.map +0 -1
  287. package/dist/tests/commands/message.test.js +0 -293
  288. package/dist/tests/commands/message.test.js.map +0 -1
  289. package/dist/tests/commands/reaction.test.d.ts +0 -2
  290. package/dist/tests/commands/reaction.test.d.ts.map +0 -1
  291. package/dist/tests/commands/reaction.test.js +0 -84
  292. package/dist/tests/commands/reaction.test.js.map +0 -1
  293. package/dist/tests/commands/snapshot.test.d.ts +0 -2
  294. package/dist/tests/commands/snapshot.test.d.ts.map +0 -1
  295. package/dist/tests/commands/snapshot.test.js +0 -280
  296. package/dist/tests/commands/snapshot.test.js.map +0 -1
  297. package/dist/tests/commands/user.test.d.ts +0 -2
  298. package/dist/tests/commands/user.test.d.ts.map +0 -1
  299. package/dist/tests/commands/user.test.js +0 -117
  300. package/dist/tests/commands/user.test.js.map +0 -1
  301. package/dist/tests/commands/workspace.test.d.ts +0 -2
  302. package/dist/tests/commands/workspace.test.d.ts.map +0 -1
  303. package/dist/tests/commands/workspace.test.js +0 -453
  304. package/dist/tests/commands/workspace.test.js.map +0 -1
  305. package/dist/tests/credential-manager.test.d.ts +0 -2
  306. package/dist/tests/credential-manager.test.d.ts.map +0 -1
  307. package/dist/tests/credential-manager.test.js +0 -199
  308. package/dist/tests/credential-manager.test.js.map +0 -1
  309. package/dist/tests/slack-client.test.d.ts +0 -2
  310. package/dist/tests/slack-client.test.d.ts.map +0 -1
  311. package/dist/tests/slack-client.test.js +0 -741
  312. package/dist/tests/slack-client.test.js.map +0 -1
  313. package/dist/tests/types.test.d.ts +0 -2
  314. package/dist/tests/types.test.d.ts.map +0 -1
  315. package/dist/tests/types.test.js +0 -215
  316. package/dist/tests/types.test.js.map +0 -1
  317. package/dist/types/index.d.ts +0 -369
  318. package/dist/types/index.d.ts.map +0 -1
  319. package/dist/types/index.js +0 -92
  320. package/dist/types/index.js.map +0 -1
  321. package/dist/utils/error-handler.d.ts +0 -2
  322. package/dist/utils/error-handler.d.ts.map +0 -1
  323. package/dist/utils/error-handler.js +0 -5
  324. package/dist/utils/error-handler.js.map +0 -1
  325. package/dist/utils/output.d.ts +0 -2
  326. package/dist/utils/output.d.ts.map +0 -1
  327. package/dist/utils/output.js +0 -4
  328. package/dist/utils/output.js.map +0 -1
  329. /package/dist/{cli.d.ts → src/platforms/teams/cli.d.ts} +0 -0
  330. /package/dist/{commands → src/platforms/teams/commands}/user.d.ts +0 -0
@@ -0,0 +1,188 @@
1
+ import { Command } from 'commander'
2
+ import { handleError } from '../../../shared/utils/error-handler'
3
+ import { formatOutput } from '../../../shared/utils/output'
4
+ import { TeamsClient } from '../client'
5
+ import { TeamsCredentialManager } from '../credential-manager'
6
+ import type { TeamsMessage } from '../types'
7
+
8
+ export async function sendAction(
9
+ teamId: string,
10
+ channelId: string,
11
+ content: string,
12
+ options: { pretty?: boolean }
13
+ ): Promise<void> {
14
+ try {
15
+ const credManager = new TeamsCredentialManager()
16
+ const config = await credManager.loadConfig()
17
+
18
+ if (!config?.token) {
19
+ console.log(
20
+ formatOutput({ error: 'Not authenticated. Run "auth extract" first.' }, options.pretty)
21
+ )
22
+ process.exit(1)
23
+ }
24
+
25
+ const client = new TeamsClient(config.token, config.token_expires_at)
26
+ const message = await client.sendMessage(teamId, channelId, content)
27
+
28
+ const output = {
29
+ id: message.id,
30
+ content: message.content,
31
+ author: message.author.displayName,
32
+ timestamp: message.timestamp,
33
+ }
34
+
35
+ console.log(formatOutput(output, options.pretty))
36
+ } catch (error) {
37
+ handleError(error as Error)
38
+ }
39
+ }
40
+
41
+ export async function listAction(
42
+ teamId: string,
43
+ channelId: string,
44
+ options: { limit?: number; pretty?: boolean }
45
+ ): Promise<void> {
46
+ try {
47
+ const credManager = new TeamsCredentialManager()
48
+ const config = await credManager.loadConfig()
49
+
50
+ if (!config?.token) {
51
+ console.log(
52
+ formatOutput({ error: 'Not authenticated. Run "auth extract" first.' }, options.pretty)
53
+ )
54
+ process.exit(1)
55
+ }
56
+
57
+ const client = new TeamsClient(config.token, config.token_expires_at)
58
+ const limit = options.limit || 50
59
+ const messages = await client.getMessages(teamId, channelId, limit)
60
+
61
+ const output = messages.map((msg: TeamsMessage) => ({
62
+ id: msg.id,
63
+ content: msg.content,
64
+ author: msg.author.displayName,
65
+ timestamp: msg.timestamp,
66
+ }))
67
+
68
+ console.log(formatOutput(output, options.pretty))
69
+ } catch (error) {
70
+ handleError(error as Error)
71
+ }
72
+ }
73
+
74
+ export async function getAction(
75
+ teamId: string,
76
+ channelId: string,
77
+ messageId: string,
78
+ options: { pretty?: boolean }
79
+ ): Promise<void> {
80
+ try {
81
+ const credManager = new TeamsCredentialManager()
82
+ const config = await credManager.loadConfig()
83
+
84
+ if (!config?.token) {
85
+ console.log(
86
+ formatOutput({ error: 'Not authenticated. Run "auth extract" first.' }, options.pretty)
87
+ )
88
+ process.exit(1)
89
+ }
90
+
91
+ const client = new TeamsClient(config.token, config.token_expires_at)
92
+ const message = await client.getMessage(teamId, channelId, messageId)
93
+
94
+ if (!message) {
95
+ console.log(formatOutput({ error: `Message not found: ${messageId}` }, options.pretty))
96
+ process.exit(1)
97
+ }
98
+
99
+ const output = {
100
+ id: message.id,
101
+ content: message.content,
102
+ author: message.author.displayName,
103
+ timestamp: message.timestamp,
104
+ }
105
+
106
+ console.log(formatOutput(output, options.pretty))
107
+ } catch (error) {
108
+ handleError(error as Error)
109
+ }
110
+ }
111
+
112
+ export async function deleteAction(
113
+ teamId: string,
114
+ channelId: string,
115
+ messageId: string,
116
+ options: { force?: boolean; pretty?: boolean }
117
+ ): Promise<void> {
118
+ try {
119
+ const credManager = new TeamsCredentialManager()
120
+ const config = await credManager.loadConfig()
121
+
122
+ if (!config?.token) {
123
+ console.log(
124
+ formatOutput({ error: 'Not authenticated. Run "auth extract" first.' }, options.pretty)
125
+ )
126
+ process.exit(1)
127
+ }
128
+
129
+ if (!options.force) {
130
+ console.log(
131
+ formatOutput({ warning: 'Use --force to confirm deletion', messageId }, options.pretty)
132
+ )
133
+ process.exit(0)
134
+ }
135
+
136
+ const client = new TeamsClient(config.token, config.token_expires_at)
137
+ await client.deleteMessage(teamId, channelId, messageId)
138
+
139
+ console.log(formatOutput({ deleted: messageId }, options.pretty))
140
+ } catch (error) {
141
+ handleError(error as Error)
142
+ }
143
+ }
144
+
145
+ export const messageCommand = new Command('message')
146
+ .description('Message commands')
147
+ .addCommand(
148
+ new Command('send')
149
+ .description('Send message to channel')
150
+ .argument('<team-id>', 'Team ID')
151
+ .argument('<channel-id>', 'Channel ID')
152
+ .argument('<content>', 'Message content')
153
+ .option('--pretty', 'Pretty print JSON output')
154
+ .action(sendAction)
155
+ )
156
+ .addCommand(
157
+ new Command('list')
158
+ .description('List messages from channel')
159
+ .argument('<team-id>', 'Team ID')
160
+ .argument('<channel-id>', 'Channel ID')
161
+ .option('--limit <n>', 'Number of messages to retrieve', '50')
162
+ .option('--pretty', 'Pretty print JSON output')
163
+ .action((teamId: string, channelId: string, options: any) => {
164
+ return listAction(teamId, channelId, {
165
+ limit: parseInt(options.limit, 10),
166
+ pretty: options.pretty,
167
+ })
168
+ })
169
+ )
170
+ .addCommand(
171
+ new Command('get')
172
+ .description('Get a single message by ID')
173
+ .argument('<team-id>', 'Team ID')
174
+ .argument('<channel-id>', 'Channel ID')
175
+ .argument('<message-id>', 'Message ID')
176
+ .option('--pretty', 'Pretty print JSON output')
177
+ .action(getAction)
178
+ )
179
+ .addCommand(
180
+ new Command('delete')
181
+ .description('Delete message')
182
+ .argument('<team-id>', 'Team ID')
183
+ .argument('<channel-id>', 'Channel ID')
184
+ .argument('<message-id>', 'Message ID')
185
+ .option('--force', 'Skip confirmation')
186
+ .option('--pretty', 'Pretty print JSON output')
187
+ .action(deleteAction)
188
+ )
@@ -0,0 +1,87 @@
1
+ import { afterEach, beforeEach, expect, mock, spyOn, test } from 'bun:test'
2
+ import { TeamsClient } from '../client'
3
+ import { TeamsCredentialManager } from '../credential-manager'
4
+ import { addAction, removeAction } from './reaction'
5
+
6
+ let clientAddReactionSpy: ReturnType<typeof spyOn>
7
+ let clientRemoveReactionSpy: ReturnType<typeof spyOn>
8
+ let credManagerLoadConfigSpy: ReturnType<typeof spyOn>
9
+
10
+ beforeEach(() => {
11
+ clientAddReactionSpy = spyOn(TeamsClient.prototype, 'addReaction').mockResolvedValue(undefined)
12
+ clientRemoveReactionSpy = spyOn(TeamsClient.prototype, 'removeReaction').mockResolvedValue(
13
+ undefined
14
+ )
15
+ credManagerLoadConfigSpy = spyOn(
16
+ TeamsCredentialManager.prototype,
17
+ 'loadConfig'
18
+ ).mockResolvedValue({
19
+ token: 'test-token',
20
+ current_team: null,
21
+ teams: {},
22
+ })
23
+ })
24
+
25
+ afterEach(() => {
26
+ clientAddReactionSpy?.mockRestore()
27
+ clientRemoveReactionSpy?.mockRestore()
28
+ credManagerLoadConfigSpy?.mockRestore()
29
+ })
30
+
31
+ test('add: sends correct POST request with emoji', async () => {
32
+ const consoleSpy = mock((_msg: string) => {})
33
+ const originalLog = console.log
34
+ console.log = consoleSpy
35
+
36
+ try {
37
+ await addAction('team123', 'ch123', 'msg123', 'like', { pretty: false })
38
+ expect(consoleSpy).toHaveBeenCalled()
39
+ const output = JSON.parse(consoleSpy.mock.calls[0][0])
40
+ expect(output.success).toBe(true)
41
+ expect(output.team_id).toBe('team123')
42
+ expect(output.channel_id).toBe('ch123')
43
+ expect(output.message_id).toBe('msg123')
44
+ expect(output.emoji).toBe('like')
45
+ } finally {
46
+ console.log = originalLog
47
+ }
48
+ })
49
+
50
+ test('remove: sends correct DELETE request with emoji', async () => {
51
+ const consoleSpy = mock((_msg: string) => {})
52
+ const originalLog = console.log
53
+ console.log = consoleSpy
54
+
55
+ try {
56
+ await removeAction('team123', 'ch123', 'msg123', 'like', { pretty: false })
57
+ expect(consoleSpy).toHaveBeenCalled()
58
+ const output = JSON.parse(consoleSpy.mock.calls[0][0])
59
+ expect(output.success).toBe(true)
60
+ expect(output.team_id).toBe('team123')
61
+ expect(output.channel_id).toBe('ch123')
62
+ expect(output.message_id).toBe('msg123')
63
+ expect(output.emoji).toBe('like')
64
+ } finally {
65
+ console.log = originalLog
66
+ }
67
+ })
68
+
69
+ test('add: handles missing token gracefully', async () => {
70
+ credManagerLoadConfigSpy?.mockResolvedValue(null)
71
+
72
+ const consoleSpy = mock((_msg: string) => {})
73
+ const originalLog = console.log
74
+ const originalExit = process.exit
75
+ process.exit = mock(() => {}) as any
76
+ console.log = consoleSpy
77
+
78
+ try {
79
+ await addAction('team123', 'ch123', 'msg123', 'like', { pretty: false })
80
+ expect(consoleSpy).toHaveBeenCalled()
81
+ const output = JSON.parse(consoleSpy.mock.calls[0][0])
82
+ expect(output.error).toBeDefined()
83
+ } finally {
84
+ console.log = originalLog
85
+ process.exit = originalExit
86
+ }
87
+ })
@@ -0,0 +1,104 @@
1
+ import { Command } from 'commander'
2
+ import { handleError } from '../../../shared/utils/error-handler'
3
+ import { formatOutput } from '../../../shared/utils/output'
4
+ import { TeamsClient } from '../client'
5
+ import { TeamsCredentialManager } from '../credential-manager'
6
+
7
+ export async function addAction(
8
+ teamId: string,
9
+ channelId: string,
10
+ messageId: string,
11
+ emoji: string,
12
+ options: { pretty?: boolean }
13
+ ): Promise<void> {
14
+ try {
15
+ const credManager = new TeamsCredentialManager()
16
+ const config = await credManager.loadConfig()
17
+
18
+ if (!config?.token) {
19
+ console.log(
20
+ formatOutput({ error: 'Not authenticated. Run "auth extract" first.' }, options.pretty)
21
+ )
22
+ process.exit(1)
23
+ }
24
+
25
+ const client = new TeamsClient(config.token, config.token_expires_at)
26
+ await client.addReaction(teamId, channelId, messageId, emoji)
27
+
28
+ console.log(
29
+ formatOutput(
30
+ {
31
+ success: true,
32
+ team_id: teamId,
33
+ channel_id: channelId,
34
+ message_id: messageId,
35
+ emoji,
36
+ },
37
+ options.pretty
38
+ )
39
+ )
40
+ } catch (error) {
41
+ handleError(error as Error)
42
+ }
43
+ }
44
+
45
+ export async function removeAction(
46
+ teamId: string,
47
+ channelId: string,
48
+ messageId: string,
49
+ emoji: string,
50
+ options: { pretty?: boolean }
51
+ ): Promise<void> {
52
+ try {
53
+ const credManager = new TeamsCredentialManager()
54
+ const config = await credManager.loadConfig()
55
+
56
+ if (!config?.token) {
57
+ console.log(
58
+ formatOutput({ error: 'Not authenticated. Run "auth extract" first.' }, options.pretty)
59
+ )
60
+ process.exit(1)
61
+ }
62
+
63
+ const client = new TeamsClient(config.token, config.token_expires_at)
64
+ await client.removeReaction(teamId, channelId, messageId, emoji)
65
+
66
+ console.log(
67
+ formatOutput(
68
+ {
69
+ success: true,
70
+ team_id: teamId,
71
+ channel_id: channelId,
72
+ message_id: messageId,
73
+ emoji,
74
+ },
75
+ options.pretty
76
+ )
77
+ )
78
+ } catch (error) {
79
+ handleError(error as Error)
80
+ }
81
+ }
82
+
83
+ export const reactionCommand = new Command('reaction')
84
+ .description('Reaction commands')
85
+ .addCommand(
86
+ new Command('add')
87
+ .description('Add emoji reaction to message')
88
+ .argument('<team-id>', 'Team ID')
89
+ .argument('<channel-id>', 'Channel ID')
90
+ .argument('<message-id>', 'Message ID')
91
+ .argument('<emoji>', 'Emoji name')
92
+ .option('--pretty', 'Pretty print JSON output')
93
+ .action(addAction)
94
+ )
95
+ .addCommand(
96
+ new Command('remove')
97
+ .description('Remove emoji reaction from message')
98
+ .argument('<team-id>', 'Team ID')
99
+ .argument('<channel-id>', 'Channel ID')
100
+ .argument('<message-id>', 'Message ID')
101
+ .argument('<emoji>', 'Emoji name')
102
+ .option('--pretty', 'Pretty print JSON output')
103
+ .action(removeAction)
104
+ )
@@ -0,0 +1,35 @@
1
+ import { expect, test } from 'bun:test'
2
+ import { snapshotCommand } from './snapshot'
3
+
4
+ test('snapshot: command is defined', () => {
5
+ expect(snapshotCommand).toBeDefined()
6
+ expect(snapshotCommand.name()).toBe('snapshot')
7
+ })
8
+
9
+ test('snapshot: command has correct description', () => {
10
+ expect(snapshotCommand.description()).toContain('team state')
11
+ })
12
+
13
+ test('snapshot: command has --channels-only option', () => {
14
+ const options = snapshotCommand.options
15
+ const channelsOnlyOption = options.find((opt) => opt.long === '--channels-only')
16
+ expect(channelsOnlyOption).toBeDefined()
17
+ })
18
+
19
+ test('snapshot: command has --users-only option', () => {
20
+ const options = snapshotCommand.options
21
+ const usersOnlyOption = options.find((opt) => opt.long === '--users-only')
22
+ expect(usersOnlyOption).toBeDefined()
23
+ })
24
+
25
+ test('snapshot: command has --limit option', () => {
26
+ const options = snapshotCommand.options
27
+ const limitOption = options.find((opt) => opt.long === '--limit')
28
+ expect(limitOption).toBeDefined()
29
+ })
30
+
31
+ test('snapshot: command has --team-id option', () => {
32
+ const options = snapshotCommand.options
33
+ const teamIdOption = options.find((opt) => opt.long === '--team-id')
34
+ expect(teamIdOption).toBeDefined()
35
+ })
@@ -0,0 +1,115 @@
1
+ import { Command } from 'commander'
2
+ import { parallelMap } from '../../../shared/utils/concurrency'
3
+ import { handleError } from '../../../shared/utils/error-handler'
4
+ import { formatOutput } from '../../../shared/utils/output'
5
+ import { TeamsClient } from '../client'
6
+ import { TeamsCredentialManager } from '../credential-manager'
7
+ import type { TeamsChannel } from '../types'
8
+
9
+ export async function snapshotAction(options: {
10
+ channelsOnly?: boolean
11
+ usersOnly?: boolean
12
+ limit?: number
13
+ teamId?: string
14
+ pretty?: boolean
15
+ }): Promise<void> {
16
+ try {
17
+ const credManager = new TeamsCredentialManager()
18
+ const config = await credManager.loadConfig()
19
+
20
+ if (!config?.token) {
21
+ console.log(
22
+ formatOutput({ error: 'Not authenticated. Run "auth extract" first.' }, options.pretty)
23
+ )
24
+ process.exit(1)
25
+ }
26
+
27
+ const teamId = options.teamId || config.current_team
28
+ if (!teamId) {
29
+ console.log(
30
+ formatOutput(
31
+ { error: 'No current team set. Run "team switch" first or use --team-id.' },
32
+ options.pretty
33
+ )
34
+ )
35
+ process.exit(1)
36
+ }
37
+
38
+ const client = new TeamsClient(config.token, config.token_expires_at)
39
+ const messageLimit = options.limit || 20
40
+
41
+ const snapshot: Record<string, unknown> = {}
42
+
43
+ const team = await client.getTeam(teamId)
44
+ snapshot.team = {
45
+ id: team.id,
46
+ name: team.name,
47
+ description: team.description,
48
+ }
49
+
50
+ if (!options.usersOnly) {
51
+ const channels = await client.listChannels(teamId)
52
+
53
+ snapshot.channels = channels.map((ch) => ({
54
+ id: ch.id,
55
+ name: ch.name,
56
+ type: ch.type,
57
+ }))
58
+
59
+ if (!options.channelsOnly) {
60
+ const channelMessages = await parallelMap(
61
+ channels,
62
+ async (channel: TeamsChannel) => {
63
+ const messages = await client.getMessages(teamId, channel.id, messageLimit)
64
+ return messages.map((msg) => ({
65
+ ...msg,
66
+ channel_name: channel.name,
67
+ }))
68
+ },
69
+ 5
70
+ )
71
+
72
+ snapshot.recent_messages = channelMessages.flat().map((msg) => ({
73
+ channel_id: msg.channel_id,
74
+ channel_name: msg.channel_name,
75
+ id: msg.id,
76
+ author: msg.author.displayName,
77
+ content: msg.content,
78
+ timestamp: msg.timestamp,
79
+ }))
80
+ }
81
+ }
82
+
83
+ if (!options.channelsOnly) {
84
+ const users = await client.listUsers(teamId)
85
+
86
+ snapshot.members = users.map((u) => ({
87
+ id: u.id,
88
+ displayName: u.displayName,
89
+ email: u.email || null,
90
+ }))
91
+ }
92
+
93
+ console.log(formatOutput(snapshot, options.pretty))
94
+ } catch (error) {
95
+ handleError(error as Error)
96
+ }
97
+ }
98
+
99
+ export const snapshotCommand = new Command()
100
+ .name('snapshot')
101
+ .description('Get comprehensive team state for AI agents')
102
+ .option('--channels-only', 'Include only channels (exclude messages and members)')
103
+ .option('--users-only', 'Include only members (exclude channels and messages)')
104
+ .option('--limit <n>', 'Number of recent messages per channel (default: 20)', '20')
105
+ .option('--team-id <id>', 'Team ID (defaults to current team)')
106
+ .option('--pretty', 'Pretty print JSON output')
107
+ .action(async (options) => {
108
+ await snapshotAction({
109
+ channelsOnly: options.channelsOnly,
110
+ usersOnly: options.usersOnly,
111
+ limit: parseInt(options.limit, 10),
112
+ teamId: options.teamId,
113
+ pretty: options.pretty,
114
+ })
115
+ })
@@ -0,0 +1,157 @@
1
+ import { afterEach, beforeEach, expect, spyOn, test } from 'bun:test'
2
+ import { TeamsClient } from '../client'
3
+ import { TeamsCredentialManager } from '../credential-manager'
4
+
5
+ let clientListTeamsSpy: ReturnType<typeof spyOn>
6
+ let clientGetTeamSpy: ReturnType<typeof spyOn>
7
+ let credManagerLoadConfigSpy: ReturnType<typeof spyOn>
8
+ let credManagerSetCurrentTeamSpy: ReturnType<typeof spyOn>
9
+ let credManagerGetCurrentTeamSpy: ReturnType<typeof spyOn>
10
+ let credManagerSaveConfigSpy: ReturnType<typeof spyOn>
11
+
12
+ beforeEach(() => {
13
+ clientListTeamsSpy = spyOn(TeamsClient.prototype, 'listTeams').mockResolvedValue([
14
+ { id: 'team-1', name: 'Team One', description: 'First team' },
15
+ { id: 'team-2', name: 'Team Two', description: 'Second team' },
16
+ ])
17
+
18
+ clientGetTeamSpy = spyOn(TeamsClient.prototype, 'getTeam').mockImplementation(
19
+ async (teamId: string) => {
20
+ if (teamId === 'team-1') {
21
+ return { id: 'team-1', name: 'Team One', description: 'First team' }
22
+ }
23
+ if (teamId === 'team-2') {
24
+ return { id: 'team-2', name: 'Team Two', description: 'Second team' }
25
+ }
26
+ throw new Error('Team not found')
27
+ }
28
+ )
29
+
30
+ credManagerLoadConfigSpy = spyOn(
31
+ TeamsCredentialManager.prototype,
32
+ 'loadConfig'
33
+ ).mockResolvedValue({
34
+ token: 'test-token',
35
+ current_team: 'team-1',
36
+ teams: {
37
+ 'team-1': { team_id: 'team-1', team_name: 'Team One' },
38
+ 'team-2': { team_id: 'team-2', team_name: 'Team Two' },
39
+ },
40
+ })
41
+
42
+ credManagerSetCurrentTeamSpy = spyOn(
43
+ TeamsCredentialManager.prototype,
44
+ 'setCurrentTeam'
45
+ ).mockResolvedValue(undefined)
46
+
47
+ credManagerGetCurrentTeamSpy = spyOn(
48
+ TeamsCredentialManager.prototype,
49
+ 'getCurrentTeam'
50
+ ).mockResolvedValue({ team_id: 'team-1', team_name: 'Team One' })
51
+
52
+ credManagerSaveConfigSpy = spyOn(
53
+ TeamsCredentialManager.prototype,
54
+ 'saveConfig'
55
+ ).mockResolvedValue(undefined)
56
+ })
57
+
58
+ afterEach(() => {
59
+ clientListTeamsSpy?.mockRestore()
60
+ clientGetTeamSpy?.mockRestore()
61
+ credManagerLoadConfigSpy?.mockRestore()
62
+ credManagerSetCurrentTeamSpy?.mockRestore()
63
+ credManagerGetCurrentTeamSpy?.mockRestore()
64
+ credManagerSaveConfigSpy?.mockRestore()
65
+ })
66
+
67
+ test('list: returns teams with current marker', async () => {
68
+ // given: credential manager with teams
69
+ const credManager = new TeamsCredentialManager()
70
+ const config = await credManager.loadConfig()
71
+
72
+ // when: checking teams
73
+ expect(config?.teams).toBeDefined()
74
+ expect(Object.keys(config!.teams)).toHaveLength(2)
75
+
76
+ // then: teams are returned
77
+ expect(config!.teams['team-1']).toBeDefined()
78
+ expect(config!.teams['team-2']).toBeDefined()
79
+ })
80
+
81
+ test('list: marks current team', async () => {
82
+ // given: credential manager with current team set
83
+ const credManager = new TeamsCredentialManager()
84
+ const config = await credManager.loadConfig()
85
+ const current = await credManager.getCurrentTeam()
86
+
87
+ // when: checking current team
88
+ expect(current?.team_id).toBe('team-1')
89
+
90
+ // then: current team is marked
91
+ expect(config!.current_team).toBe('team-1')
92
+ })
93
+
94
+ test('info: returns team details', async () => {
95
+ // given: teams client with team data
96
+ const client = new TeamsClient('test-token')
97
+ const team = await client.getTeam('team-1')
98
+
99
+ // when: getting team info
100
+ expect(team).toBeDefined()
101
+
102
+ // then: team details are returned
103
+ expect(team.id).toBe('team-1')
104
+ expect(team.name).toBe('Team One')
105
+ expect(team.description).toBe('First team')
106
+ })
107
+
108
+ test('info: throws error for non-existent team', async () => {
109
+ // given: teams client
110
+ const client = new TeamsClient('test-token')
111
+
112
+ // when: getting non-existent team
113
+ // then: error is thrown
114
+ try {
115
+ await client.getTeam('non-existent')
116
+ expect(true).toBe(false)
117
+ } catch (error) {
118
+ expect((error as Error).message).toContain('Team not found')
119
+ }
120
+ })
121
+
122
+ test('switch: updates current team', async () => {
123
+ // given: credential manager
124
+ const credManager = new TeamsCredentialManager()
125
+
126
+ // when: switching team
127
+ await credManager.setCurrentTeam('team-2', 'Team Two')
128
+
129
+ // then: setCurrentTeam is called
130
+ expect(credManager.setCurrentTeam).toHaveBeenCalledWith('team-2', 'Team Two')
131
+ })
132
+
133
+ test('current: returns current team info', async () => {
134
+ // given: credential manager with current team
135
+ const credManager = new TeamsCredentialManager()
136
+ const config = await credManager.loadConfig()
137
+
138
+ // when: getting current team
139
+ const current = await credManager.getCurrentTeam()
140
+
141
+ // then: current team is returned
142
+ expect(current?.team_id).toBe('team-1')
143
+ expect(config!.current_team).toBe('team-1')
144
+ })
145
+
146
+ test('remove: removes team from config', async () => {
147
+ // given: credential manager with teams
148
+ const credManager = new TeamsCredentialManager()
149
+ const config = await credManager.loadConfig()
150
+
151
+ // when: removing team
152
+ delete config!.teams['team-2']
153
+ await credManager.saveConfig(config!)
154
+
155
+ // then: saveConfig is called
156
+ expect(credManager.saveConfig).toHaveBeenCalled()
157
+ })