Skip to content

Add agent-channelbot platform for Channel Talk#54

Open
devxoul wants to merge 18 commits intomainfrom
feat/channel-talk
Open

Add agent-channelbot platform for Channel Talk#54
devxoul wants to merge 18 commits intomainfrom
feat/channel-talk

Conversation

@devxoul
Copy link
Owner

@devxoul devxoul commented Mar 12, 2026

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 a ChannelBotError class carrying typed error codes.
  • credential-manager.ts — Workspace-centric credential storage at ~/.config/agent-messenger/channelbot-credentials.json. Supports multi-workspace management with current workspace 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-After header handling for 429 rate limits, and auth header injection on every request. Provides typed methods for all supported API endpoints.

Command Groups (7 total)

  • authset, status, clear, list, use, remove, bot. Validates credentials against the API before saving. bot sets a default bot name for message sending.
  • messagesend, list, get. Auto-detects target type: @name → Group, otherwise → UserChat. Wraps plain text into Channel Talk's blocks format automatically.
  • chatlist, get, close, delete. Manages UserChat (1:1 customer conversations) lifecycle. close and delete require bot identity and --force respectively.
  • grouplist, get, messages. Resolves groups by @name or raw ID via the client's resolveGroup() method.
  • managerlist, get. Read-only access to human agents on the workspace.
  • botlist, 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-only and --chats-only filters.
  • shared.ts — Common helpers for building a ChannelBotClient from 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 — Register channelbot subcommand in the root CLI.
  • package.json — Add agent-channelbot bin 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

  • Add ChannelBot to badges, installation list, skills list, SkillPad badges, platform feature table, and guides section.

Context

Channel Talk's Open API has some notable constraints that shaped this implementation:

  • No reactions, search, edit, or file upload — the Open API doesn't support these. A future agent-channel (user-token based) integration could fill these gaps.
  • Bot name required for writes — sending messages and closing chats require specifying a bot name that exists in the workspace, hence the auth bot command and --bot flag.
  • "Channel" = workspace — Channel Talk's API uses "channel" to mean the entire workspace, not a chat channel. The code and docs normalize this to avoid confusion with Slack/Discord channel semantics.

Testing

  • 118 dedicated unit tests across 8 test files covering types/schemas, credential manager, HTTP client (retry, rate limiting, auth headers), and all 7 command groups.
  • All 1017 tests in the full suite pass.
  • bun typecheck, bun lint, and bun run build all 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

    • New typed client for Channel Talk Open API v5 with auth injection, retries, and 429 Retry-After handling.
    • Workspace credential manager at ~/.config/agent-messenger/channelbot-credentials.json with multi-workspace support and a default bot name.
    • CLI agent-channelbot with command groups: auth, message, chat, group, manager, bot, snapshot and options --workspace, --bot, --pretty. The message group adds grep to search across UserChats by text, with filters and JSON output. Root CLI adds channelbot subcommand; package.json adds the agent-channelbot bin; .claude-plugin/plugin.json registers the skill.
    • Skill docs and runnable templates under skills/agent-channelbot/; README updated with message grep and feature table. 118 new unit tests added; full suite passes.
  • Migration

    • Requires Channel Talk Access Key and Access Secret: agent-channelbot auth set <access-key> <access-secret>.
    • Set a default bot for writes: agent-channelbot auth bot <name>.

Written for commit dcf7dcf. Summary will update on new commits.

@vercel
Copy link

vercel bot commented Mar 12, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
agent-messenger Ignored Ignored Mar 12, 2026 8:24am

Request Review

Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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++) {
Copy link

@cubic-dev-ai cubic-dev-ai bot Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>
Fix with Cubic

while [ $attempt -le $max_attempts ]; do
echo -e "${YELLOW}Attempt $attempt/$max_attempts...${NC}"

RESULT=$(agent-channelbot message send "$target" "$message" 2>&1)
Copy link

@cubic-dev-ai cubic-dev-ai bot Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>
Suggested change
RESULT=$(agent-channelbot message send "$target" "$message" 2>&1)
RESULT=$(agent-channelbot message send "$target" "$message" 2>&1 || true)
Fix with Cubic

}

const content = await readFile(this.credentialsPath, 'utf-8')
const parsed = ChannelBotConfigSchema.safeParse(JSON.parse(content))
Copy link

@cubic-dev-ai cubic-dev-ai bot Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>
Fix with Cubic

export async function botAction(name: string, options: ActionOptions): Promise<ActionResult> {
try {
const credManager = options._credManager ?? new ChannelBotCredentialManager()
await credManager.setDefaultBot(name)
Copy link

@cubic-dev-ai cubic-dev-ai bot Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>
Fix with Cubic


echo -e "${YELLOW}Fetching workspace data...${NC}" >&2

SNAPSHOT=$(agent-channelbot snapshot 2>&1)
Copy link

@cubic-dev-ai cubic-dev-ai bot Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>
Fix with Cubic

const manager = new ChannelBotCredentialManager(tempDir)
await listAction('chat1', { type: 'userchat', limit: '10', sort: 'asc', since: 'cursor123', _credManager: manager })

expect(mockGetUserChatMessages).toHaveBeenCalledTimes(1)
Copy link

@cubic-dev-ai cubic-dev-ai bot Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>
Fix with Cubic

fi

AUTH_STATUS=$(agent-channelbot auth status 2>&1) || true
VALID=$(echo "$AUTH_STATUS" | jq -r '.valid // false')
Copy link

@cubic-dev-ai cubic-dev-ai bot Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>
Fix with Cubic

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!"
Copy link

@cubic-dev-ai cubic-dev-ai bot Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>
Suggested change
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"
Fix with Cubic

```bash
#!/bin/bash

LAST_CHAT_ID=""
Copy link

@cubic-dev-ai cubic-dev-ai bot Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>
Suggested change
LAST_CHAT_ID=""
LAST_CHAT_ID=$(agent-channelbot chat list --state opened --limit 1 | jq -r '.chats[0].id // ""')
Fix with Cubic

export async function getAction(target: string, messageId: string, options: MessageOptions): Promise<MessageResult> {
try {
const client = await getClient(options)
const limit = 100
Copy link

@cubic-dev-ai cubic-dev-ai bot Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>
Fix with Cubic

Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +149 to +150
const chatLimit = options.chatLimit ? parseInt(options.chatLimit, 10) : 50
const limit = options.limit ? parseInt(options.limit, 10) : 20
Copy link

@cubic-dev-ai cubic-dev-ai bot Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>
Suggested change
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')
Fix with Cubic

KEYWORD="리뷰"

# Search across all chat states (opened, closed, snoozed)
RESULTS=$(agent-channelbot message grep "$KEYWORD")
Copy link

@cubic-dev-ai cubic-dev-ai bot Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>
Fix with Cubic

static wrapTextInBlocks(text: string) {
return [{ type: 'text', value: text }]
}
static extractText(msg: { blocks?: Array<{ type: string; value?: string }>; plainText?: string }) {
Copy link

@cubic-dev-ai cubic-dev-ai bot Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>
Fix with Cubic

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant