Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
There was a problem hiding this comment.
20 issues found across 34 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="src/platforms/channelbot/commands/chat.test.ts">
<violation number="1" location="src/platforms/channelbot/commands/chat.test.ts:97">
P2: This test doesn't verify the default `opened` filter, so it will pass even if `listAction()` stops requesting only opened chats.</violation>
</file>
<file name="src/platforms/channelbot/client.ts">
<violation number="1" location="src/platforms/channelbot/client.ts:204">
P1: Automatic retries are applied to non-idempotent POST/PATCH/DELETE requests, which can duplicate Channel Talk writes after partial failures.</violation>
</file>
<file name="src/platforms/channelbot/commands/manager.ts">
<violation number="1" location="src/platforms/channelbot/commands/manager.ts:32">
P2: Validate `--limit` before passing it to the API; malformed values currently become `limit=NaN` or are partially parsed.</violation>
</file>
<file name="skills/agent-channelbot/templates/post-message.sh">
<violation number="1" location="skills/agent-channelbot/templates/post-message.sh:45">
P1: The retry loop is bypassed because command substitution exits the script under `set -e` when send fails.</violation>
</file>
<file name="src/platforms/channelbot/commands/group.ts">
<violation number="1" location="src/platforms/channelbot/commands/group.ts:33">
P2: Validate `--limit` before calling `listGroups`; invalid input is currently sent as `limit=NaN`.</violation>
</file>
<file name="src/platforms/channelbot/credential-manager.ts">
<violation number="1" location="src/platforms/channelbot/credential-manager.ts:24">
P1: `JSON.parse(content)` can throw a `SyntaxError` on a corrupted credentials file. Wrap it in a try-catch (or handle inline) so the method returns the default config instead of crashing, matching the graceful-degradation intent of the `safeParse` check below it.</violation>
</file>
<file name="src/platforms/channelbot/commands/snapshot.ts">
<violation number="1" location="src/platforms/channelbot/commands/snapshot.ts:113">
P2: These `total_*` fields are page sizes, not actual workspace totals.</violation>
<violation number="2" location="src/platforms/channelbot/commands/snapshot.ts:195">
P2: A failed snapshot still exits with status 0 because the action prints `{ error: ... }` instead of treating it as a command failure.</violation>
</file>
<file name="src/platforms/channelbot/commands/group.test.ts">
<violation number="1" location="src/platforms/channelbot/commands/group.test.ts:128">
P2: This test doesn't verify that `messagesAction()` uses the resolved group ID when fetching messages, so it can pass even if `@name` handling breaks.</violation>
</file>
<file name="skills/agent-channelbot/templates/monitor-chat.sh">
<violation number="1" location="skills/agent-channelbot/templates/monitor-chat.sh:29">
P2: Fetching only one open chat here means the monitor drops additional chats that open between polling intervals.</violation>
</file>
<file name="skills/agent-channelbot/SKILL.md">
<violation number="1" location="skills/agent-channelbot/SKILL.md:199">
P2: `message get` is documented as a direct message-ID lookup, but the implementation only searches the latest 100 messages, so older IDs will incorrectly come back as not found.</violation>
<violation number="2" location="skills/agent-channelbot/SKILL.md:410">
P2: The rate-limit troubleshooting text is incorrect: the client already auto-retries 429 responses using `Retry-After`, so this line tells users to compensate for behavior that is already built in.</violation>
</file>
<file name="src/platforms/channelbot/commands/auth.ts">
<violation number="1" location="src/platforms/channelbot/commands/auth.ts:151">
P1: Scope the default bot to a workspace; the current implementation stores one global bot name that is reused after `auth use` switches to another workspace.</violation>
</file>
<file name="src/platforms/channelbot/commands/message.test.ts">
<violation number="1" location="src/platforms/channelbot/commands/message.test.ts:166">
P2: Assert the forwarded pagination arguments here; checking only the call count does not verify `limit`, `sort`, or `since`.</violation>
</file>
<file name="skills/agent-channelbot/templates/workspace-summary.sh">
<violation number="1" location="skills/agent-channelbot/templates/workspace-summary.sh:37">
P2: Check for `jq` before the first parse so the template fails with a clear prerequisite error.</violation>
<violation number="2" location="skills/agent-channelbot/templates/workspace-summary.sh:49">
P1: Validate the snapshot payload for `.error` before summarizing it; otherwise API/auth failures are rendered as an empty workspace summary.</violation>
</file>
<file name="skills/agent-channelbot/references/common-patterns.md">
<violation number="1" location="skills/agent-channelbot/references/common-patterns.md:59">
P2: This poller skips the first new chat after startup because `LAST_CHAT_ID` starts empty and the `if` guard requires a non-empty previous ID.</violation>
<violation number="2" location="skills/agent-channelbot/references/common-patterns.md:90">
P2: Pass `--bot` on the closing message too; otherwise this example depends on an implicit default bot and can fail on a fresh setup.</violation>
</file>
<file name="src/platforms/channelbot/commands/message.ts">
<violation number="1" location="src/platforms/channelbot/commands/message.ts:48">
P1: Resolve `@name` groups before calling group message endpoints; auto-detected group targets are currently sent to the wrong API path.</violation>
<violation number="2" location="src/platforms/channelbot/commands/message.ts:98">
P2: This only searches the first 100 messages, so older message IDs can be reported as missing.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| const url = `${BASE_URL}${path}` | ||
| let lastError: Error | undefined | ||
|
|
||
| for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) { |
There was a problem hiding this comment.
P1: Automatic retries are applied to non-idempotent POST/PATCH/DELETE requests, which can duplicate Channel Talk writes after partial failures.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/platforms/channelbot/client.ts, line 204:
<comment>Automatic retries are applied to non-idempotent POST/PATCH/DELETE requests, which can duplicate Channel Talk writes after partial failures.</comment>
<file context>
@@ -0,0 +1,295 @@
+ const url = `${BASE_URL}${path}`
+ let lastError: Error | undefined
+
+ for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
+ await this.waitForRateLimit()
+
</file context>
| while [ $attempt -le $max_attempts ]; do | ||
| echo -e "${YELLOW}Attempt $attempt/$max_attempts...${NC}" | ||
|
|
||
| RESULT=$(agent-channelbot message send "$target" "$message" 2>&1) |
There was a problem hiding this comment.
P1: The retry loop is bypassed because command substitution exits the script under set -e when send fails.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At skills/agent-channelbot/templates/post-message.sh, line 45:
<comment>The retry loop is bypassed because command substitution exits the script under `set -e` when send fails.</comment>
<file context>
@@ -0,0 +1,112 @@
+ while [ $attempt -le $max_attempts ]; do
+ echo -e "${YELLOW}Attempt $attempt/$max_attempts...${NC}"
+
+ RESULT=$(agent-channelbot message send "$target" "$message" 2>&1)
+ MSG_ID=$(echo "$RESULT" | jq -r '.id // ""')
+
</file context>
| RESULT=$(agent-channelbot message send "$target" "$message" 2>&1) | |
| RESULT=$(agent-channelbot message send "$target" "$message" 2>&1 || true) |
| } | ||
|
|
||
| const content = await readFile(this.credentialsPath, 'utf-8') | ||
| const parsed = ChannelBotConfigSchema.safeParse(JSON.parse(content)) |
There was a problem hiding this comment.
P1: JSON.parse(content) can throw a SyntaxError on a corrupted credentials file. Wrap it in a try-catch (or handle inline) so the method returns the default config instead of crashing, matching the graceful-degradation intent of the safeParse check below it.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/platforms/channelbot/credential-manager.ts, line 24:
<comment>`JSON.parse(content)` can throw a `SyntaxError` on a corrupted credentials file. Wrap it in a try-catch (or handle inline) so the method returns the default config instead of crashing, matching the graceful-degradation intent of the `safeParse` check below it.</comment>
<file context>
@@ -0,0 +1,158 @@
+ }
+
+ const content = await readFile(this.credentialsPath, 'utf-8')
+ const parsed = ChannelBotConfigSchema.safeParse(JSON.parse(content))
+ if (!parsed.success) {
+ return { current: null, workspaces: {}, default_bot: null }
</file context>
| export async function botAction(name: string, options: ActionOptions): Promise<ActionResult> { | ||
| try { | ||
| const credManager = options._credManager ?? new ChannelBotCredentialManager() | ||
| await credManager.setDefaultBot(name) |
There was a problem hiding this comment.
P1: Scope the default bot to a workspace; the current implementation stores one global bot name that is reused after auth use switches to another workspace.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/platforms/channelbot/commands/auth.ts, line 151:
<comment>Scope the default bot to a workspace; the current implementation stores one global bot name that is reused after `auth use` switches to another workspace.</comment>
<file context>
@@ -0,0 +1,229 @@
+export async function botAction(name: string, options: ActionOptions): Promise<ActionResult> {
+ try {
+ const credManager = options._credManager ?? new ChannelBotCredentialManager()
+ await credManager.setDefaultBot(name)
+ return { success: true, default_bot: name }
+ } catch (error: unknown) {
</file context>
|
|
||
| echo -e "${YELLOW}Fetching workspace data...${NC}" >&2 | ||
|
|
||
| SNAPSHOT=$(agent-channelbot snapshot 2>&1) |
There was a problem hiding this comment.
P1: Validate the snapshot payload for .error before summarizing it; otherwise API/auth failures are rendered as an empty workspace summary.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At skills/agent-channelbot/templates/workspace-summary.sh, line 49:
<comment>Validate the snapshot payload for `.error` before summarizing it; otherwise API/auth failures are rendered as an empty workspace summary.</comment>
<file context>
@@ -0,0 +1,108 @@
+
+echo -e "${YELLOW}Fetching workspace data...${NC}" >&2
+
+SNAPSHOT=$(agent-channelbot snapshot 2>&1)
+
+if [ "$OUTPUT_JSON" = true ]; then
</file context>
| const manager = new ChannelBotCredentialManager(tempDir) | ||
| await listAction('chat1', { type: 'userchat', limit: '10', sort: 'asc', since: 'cursor123', _credManager: manager }) | ||
|
|
||
| expect(mockGetUserChatMessages).toHaveBeenCalledTimes(1) |
There was a problem hiding this comment.
P2: Assert the forwarded pagination arguments here; checking only the call count does not verify limit, sort, or since.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/platforms/channelbot/commands/message.test.ts, line 166:
<comment>Assert the forwarded pagination arguments here; checking only the call count does not verify `limit`, `sort`, or `since`.</comment>
<file context>
@@ -0,0 +1,187 @@
+ const manager = new ChannelBotCredentialManager(tempDir)
+ await listAction('chat1', { type: 'userchat', limit: '10', sort: 'asc', since: 'cursor123', _credManager: manager })
+
+ expect(mockGetUserChatMessages).toHaveBeenCalledTimes(1)
+ })
+ })
</file context>
| fi | ||
|
|
||
| AUTH_STATUS=$(agent-channelbot auth status 2>&1) || true | ||
| VALID=$(echo "$AUTH_STATUS" | jq -r '.valid // false') |
There was a problem hiding this comment.
P2: Check for jq before the first parse so the template fails with a clear prerequisite error.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At skills/agent-channelbot/templates/workspace-summary.sh, line 37:
<comment>Check for `jq` before the first parse so the template fails with a clear prerequisite error.</comment>
<file context>
@@ -0,0 +1,108 @@
+fi
+
+AUTH_STATUS=$(agent-channelbot auth status 2>&1) || true
+VALID=$(echo "$AUTH_STATUS" | jq -r '.valid // false')
+
+if [ "$VALID" != "true" ]; then
</file context>
| CHAT_ID="uc_abc123" | ||
|
|
||
| # Send a closing message | ||
| agent-channelbot message send "$CHAT_ID" "This issue has been resolved. Feel free to reach out if you need anything else!" |
There was a problem hiding this comment.
P2: Pass --bot on the closing message too; otherwise this example depends on an implicit default bot and can fail on a fresh setup.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At skills/agent-channelbot/references/common-patterns.md, line 90:
<comment>Pass `--bot` on the closing message too; otherwise this example depends on an implicit default bot and can fail on a fresh setup.</comment>
<file context>
@@ -0,0 +1,229 @@
+CHAT_ID="uc_abc123"
+
+# Send a closing message
+agent-channelbot message send "$CHAT_ID" "This issue has been resolved. Feel free to reach out if you need anything else!"
+
+# Close the chat (requires bot name)
</file context>
| agent-channelbot message send "$CHAT_ID" "This issue has been resolved. Feel free to reach out if you need anything else!" | |
| agent-channelbot message send "$CHAT_ID" "This issue has been resolved. Feel free to reach out if you need anything else!" --bot "Support Bot" |
| ```bash | ||
| #!/bin/bash | ||
|
|
||
| LAST_CHAT_ID="" |
There was a problem hiding this comment.
P2: This poller skips the first new chat after startup because LAST_CHAT_ID starts empty and the if guard requires a non-empty previous ID.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At skills/agent-channelbot/references/common-patterns.md, line 59:
<comment>This poller skips the first new chat after startup because `LAST_CHAT_ID` starts empty and the `if` guard requires a non-empty previous ID.</comment>
<file context>
@@ -0,0 +1,229 @@
+```bash
+#!/bin/bash
+
+LAST_CHAT_ID=""
+
+while true; do
</file context>
| LAST_CHAT_ID="" | |
| LAST_CHAT_ID=$(agent-channelbot chat list --state opened --limit 1 | jq -r '.chats[0].id // ""') |
| export async function getAction(target: string, messageId: string, options: MessageOptions): Promise<MessageResult> { | ||
| try { | ||
| const client = await getClient(options) | ||
| const limit = 100 |
There was a problem hiding this comment.
P2: This only searches the first 100 messages, so older message IDs can be reported as missing.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/platforms/channelbot/commands/message.ts, line 98:
<comment>This only searches the first 100 messages, so older message IDs can be reported as missing.</comment>
<file context>
@@ -0,0 +1,177 @@
+export async function getAction(target: string, messageId: string, options: MessageOptions): Promise<MessageResult> {
+ try {
+ const client = await getClient(options)
+ const limit = 100
+ const targetType = options.type || detectTargetType(target)
+
</file context>
There was a problem hiding this comment.
3 issues found across 6 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="src/platforms/channelbot/commands/message.ts">
<violation number="1" location="src/platforms/channelbot/commands/message.ts:149">
P2: Validate `--chat-limit` and `--limit` after parsing. Invalid numeric input currently becomes `NaN`, which can disable result limiting and send malformed limits to API calls.</violation>
</file>
<file name="skills/agent-channelbot/references/common-patterns.md">
<violation number="1" location="skills/agent-channelbot/references/common-patterns.md:145">
P2: Check the `message grep` exit status before treating missing `total_results` as zero, otherwise real API/auth errors get reported as “No messages found.”</violation>
</file>
<file name="src/platforms/channelbot/commands/message.test.ts">
<violation number="1" location="src/platforms/channelbot/commands/message.test.ts:74">
P2: Reimplementing `extractText` in the client mock masks regressions in the real text-extraction logic that `searchAction` depends on.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| const chatLimit = options.chatLimit ? parseInt(options.chatLimit, 10) : 50 | ||
| const limit = options.limit ? parseInt(options.limit, 10) : 20 |
There was a problem hiding this comment.
P2: Validate --chat-limit and --limit after parsing. Invalid numeric input currently becomes NaN, which can disable result limiting and send malformed limits to API calls.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/platforms/channelbot/commands/message.ts, line 149:
<comment>Validate `--chat-limit` and `--limit` after parsing. Invalid numeric input currently becomes `NaN`, which can disable result limiting and send malformed limits to API calls.</comment>
<file context>
@@ -123,12 +143,55 @@ export async function getAction(target: string, messageId: string, options: Mess
+export async function searchAction(query: string, options: SearchOptions): Promise<SearchResult> {
+ try {
+ const client = await getClient(options)
+ const chatLimit = options.chatLimit ? parseInt(options.chatLimit, 10) : 50
+ const limit = options.limit ? parseInt(options.limit, 10) : 20
+ const stateOption = options.state || 'all'
</file context>
| const chatLimit = options.chatLimit ? parseInt(options.chatLimit, 10) : 50 | |
| const limit = options.limit ? parseInt(options.limit, 10) : 20 | |
| const chatLimit = options.chatLimit ? parseInt(options.chatLimit, 10) : 50 | |
| const limit = options.limit ? parseInt(options.limit, 10) : 20 | |
| if (!Number.isInteger(chatLimit) || chatLimit <= 0) throw new Error('--chat-limit must be a positive integer') | |
| if (!Number.isInteger(limit) || limit <= 0) throw new Error('--limit must be a positive integer') |
| KEYWORD="리뷰" | ||
|
|
||
| # Search across all chat states (opened, closed, snoozed) | ||
| RESULTS=$(agent-channelbot message grep "$KEYWORD") |
There was a problem hiding this comment.
P2: Check the message grep exit status before treating missing total_results as zero, otherwise real API/auth errors get reported as “No messages found.”
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At skills/agent-channelbot/references/common-patterns.md, line 145:
<comment>Check the `message grep` exit status before treating missing `total_results` as zero, otherwise real API/auth errors get reported as “No messages found.”</comment>
<file context>
@@ -132,7 +132,36 @@ agent-channelbot snapshot --chats-only # Just UserChat summary
+KEYWORD="리뷰"
+
+# Search across all chat states (opened, closed, snoozed)
+RESULTS=$(agent-channelbot message grep "$KEYWORD")
+TOTAL=$(echo "$RESULTS" | jq -r '.total_results // 0')
+
</file context>
| static wrapTextInBlocks(text: string) { | ||
| return [{ type: 'text', value: text }] | ||
| } | ||
| static extractText(msg: { blocks?: Array<{ type: string; value?: string }>; plainText?: string }) { |
There was a problem hiding this comment.
P2: Reimplementing extractText in the client mock masks regressions in the real text-extraction logic that searchAction depends on.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/platforms/channelbot/commands/message.test.ts, line 74:
<comment>Reimplementing `extractText` in the client mock masks regressions in the real text-extraction logic that `searchAction` depends on.</comment>
<file context>
@@ -64,6 +71,14 @@ mock.module('../client', () => ({
static wrapTextInBlocks(text: string) {
return [{ type: 'text', value: text }]
}
+ static extractText(msg: { blocks?: Array<{ type: string; value?: string }>; plainText?: string }) {
+ const parts: string[] = []
+ for (const block of msg.blocks ?? []) {
</file context>
Summary
Add
agent-channelbot, a new platform integration for Channel Talk — a customer support messaging platform popular in Korea and Japan. Unlike existing Slack/Discord/Teams integrations that extract desktop app credentials, ChannelBot uses official Open API credentials (Access Key + Access Secret) for server-side and CI/CD use cases.Changes
Core Infrastructure (
src/platforms/channelbot/)types.ts— Zod schemas for Channel Talk API entities (Channel, UserChat, Group, Manager, Bot, Message, MessageBlock) with aChannelBotErrorclass carrying typed error codes.credential-manager.ts— Workspace-centric credential storage at~/.config/agent-messenger/channelbot-credentials.json. Supports multi-workspace management withcurrentworkspace tracking, per-workspace access key/secret pairs, and a default bot name setting. Channel Talk calls workspaces "channels," so the manager normalizes this terminology.client.ts— HTTP client wrapping Channel Talk's Open API v5. Includes automatic retry with exponential backoff for 5xx errors,Retry-Afterheader handling for 429 rate limits, and auth header injection on every request. Provides typed methods for all supported API endpoints.Command Groups (7 total)
auth—set,status,clear,list,use,remove,bot. Validates credentials against the API before saving.botsets a default bot name for message sending.message—send,list,get. Auto-detects target type:@name→ Group, otherwise → UserChat. Wraps plain text into Channel Talk'sblocksformat automatically.chat—list,get,close,delete. Manages UserChat (1:1 customer conversations) lifecycle.closeanddeleterequire bot identity and--forcerespectively.group—list,get,messages. Resolves groups by@nameor raw ID via the client'sresolveGroup()method.manager—list,get. Read-only access to human agents on the workspace.bot—list,create,delete. Manages bot identities used for automated messaging.snapshot— Single command returning comprehensive workspace state (groups with messages, UserChat summary, managers, bots). Supports--groups-onlyand--chats-onlyfilters.shared.ts— Common helpers for building aChannelBotClientfrom stored credentials and resolving global options (--workspace,--bot,--pretty).CLI Registration
cli.ts— ChannelBot CLI entry point with Commander.js, registering all 7 command groups and global options.index.ts— Barrel exports for the platform.src/cli.ts— Registerchannelbotsubcommand in the root CLI.package.json— Addagent-channelbotbin entry..claude-plugin/plugin.json— Register skill path.Skill Files (
skills/agent-channelbot/)SKILL.md— Full agent skill definition: key concepts, quick start, auth setup, memory management patterns, all commands with examples, output format, global options, error handling, limitations, and troubleshooting.references/authentication.md— Detailed credential lifecycle, multi-workspace management, environment variables for CI/CD.references/common-patterns.md— Six workflow patterns: send to UserChat, send to Group, poll for new chats, close after handling, snapshot for context, error handling with retry.templates/— Three runnable shell scripts:post-message.sh,monitor-chat.sh,workspace-summary.sh.README
Context
Channel Talk's Open API has some notable constraints that shaped this implementation:
agent-channel(user-token based) integration could fill these gaps.auth botcommand and--botflag.Testing
bun typecheck,bun lint, andbun run buildall clean.Summary by cubic
Adds
agent-channelbot, a Channel Talk integration using Open API credentials for server-side and CI/CD. Adds a CLI and skill to send messages, manage chats and bots, snapshot workspace state, and grep messages across UserChats.New Features
Retry-Afterhandling.~/.config/agent-messenger/channelbot-credentials.jsonwith multi-workspace support and a default bot name.agent-channelbotwith command groups:auth,message,chat,group,manager,bot,snapshotand options--workspace,--bot,--pretty. Themessagegroup addsgrepto search across UserChats by text, with filters and JSON output. Root CLI addschannelbotsubcommand;package.jsonadds theagent-channelbotbin;.claude-plugin/plugin.jsonregisters the skill.skills/agent-channelbot/; README updated withmessage grepand feature table. 118 new unit tests added; full suite passes.Migration
agent-channelbot auth set <access-key> <access-secret>.agent-channelbot auth bot <name>.Written for commit dcf7dcf. Summary will update on new commits.