Add a Playwright-based dashboard UX verification script for the Settings page. Covers hash-route nav stability, /goal toggle persistence, single restart confirmation, and serious/critical axe checks.
* Overhaul dashboard UX with two-pane rooms and live turn persistence
- Two-pane room board (Discord-style list + detail) with sender role
colors, inbox attention badges, and per-role left-border sectioning
- Settings page: nickname + language extracted to /#/settings; sidebar
freed up for mini stats (rooms, queue, inbox) and nav badges
- react-markdown + remark-gfm for message body (tables, code, links,
proper line breaks); unified timeline merges outputs and human messages
- Live turn elapsed counter ticks every second; paused state preserved
across service restarts via new progress_text column on paired_turns
(migration 015) so in-flight context survives reboots
- Batched /api/rooms-timeline endpoint with server-side stale-while-
revalidate cache + gzip; cold ~3s -> warm ~10ms
- Optimistic UI for outgoing messages with rollback on failure;
nickname propagated to sender_name on send
- Density sweep: tighter paddings, radii, font sizes; tokens align
across panel/card/sidebar/timeline
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* Strip redundant LIVE/intent/state labels from turn header
The left dot already signals LIVE and the role chip already shows the
turn role; the standalone LIVE label, intentKind em, and state pill
were repeating that information. Keep only role chip, time, elapsed,
and a "중단됨" label when paused.
Also picks up Prettier-formatted long lines in i18n and backend files
left over from the previous commit.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* Convert sidebar to lucide-react icon rail and fix optimistic UI
- Replace 168px text-label sidebar with 56px flush-left icon rail
(lucide-react icons: Gauge/Inbox/Activity/MessageSquare/Clock/Settings)
Discord/Slack pattern: rail against left edge, no centered shell margin
- Optimistic UI now survives polling: pending messages tracked in a
separate state and merged into the timeline render; an effect prunes
pending entries once the server snapshot confirms them by content match
- LIVE elapsed counter shows seconds at all granularities (formatLiveElapsed
uses m+s/h+m+s instead of dropping seconds after 60s)
- Refresh button shows spin animation while busy
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* Make Rooms the default landing view
Rooms is the primary surface (chat-style live agent flow), so reorder
nav (Rooms first) and switch DEFAULT_VIEW from inbox to rooms. The
topbar fallback label also changes from 'usage' to 'rooms'.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* Submit room message on Enter, allow Shift+Enter for newline
IME composition (event.nativeEvent.isComposing) is excluded so that
mid-Hangul Enter does not trigger send.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* Viewport-lock rooms view to remove double scroll
The rooms view now fills the viewport (height: 100vh - topbar) with
list and detail each scrolling in their own column. The page itself
no longer scrolls when the detail thread grows long, so the wheel
scrolls whichever column the cursor is over.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* Flatten rooms view, single-line compose with icon send button
Drop nested card chrome on rooms view: outer panel now has no padding,
border, or background so the rail / list / detail sit flush against
the page. Internal panes (rooms-list, rooms-detail) lose their own
borders and rely on a single divider line for separation.
Compose textarea is single-row by default (rows=1) and grows up to
~5 lines; the send button is icon-only (Send from lucide-react),
fixed 36x36, pinned to the bottom of the detail pane. The redundant
'메시지' h4 and the 세부 fold are removed — the room title in the
header already establishes context.
Detail pane is now a flex column where only the timeline scrolls;
the head sticks at top and the compose pins at bottom (messenger
pattern), so the input is always reachable.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* Fix rooms layout: viewport lock, restore panel tone, ensure thread scrolls
- Lock body to viewport height (overflow:hidden) only when rooms view is
active, via body:has(.view-stack.view-rooms). Section nav becomes
static so the layout fits exactly in the viewport instead of being
clipped under the sticky header.
- Restore subtle panel background and border on .rooms-list and
.rooms-detail so chat content has tonal contrast against the page bg
(pure black was unreadable). Outer panel chrome stays flat.
- Make the thread section the only flex:1 child of the pinned card so
it actually scrolls when content overflows; everything else (head,
meta, current turn, watcher fold, compose) stays flex:none.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* Make rooms-detail itself scroll with sticky head and compose
Drop the brittle flex-column-with-1fr-child setup; just let .rooms-detail
be overflow-y:auto and pin .room-card-head to top and
.room-compose-section to bottom via position:sticky. The head and
compose stay visible while the thread between them scrolls naturally.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* Match compose-section background to panel
The sticky compose bar was using var(--bg) (near-black) which jarred
against the surrounding panel tone. Use var(--panel) so it sits
continuously with the room detail.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* Fix compose full-width and hide internal IPC payloads
- The compose section's full-width grid was being clobbered by the
later .room-section { display: grid } rule (same specificity, source
order won). Bump specificity to .room-section.room-compose-section
with display: block so the inner form/textarea fills the panel.
- Filter out internal protocol payloads from the timeline:
isInternalProtocolPayload() detects {"author":..."recipient":...}
shaped agent IPC messages and excludes them from humanMessages and
outputEntries. These are routing metadata, not human-readable chat.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* Tighten rooms width: explicit 100% on detail/card, responsive list
- Make rooms-list responsive (clamp(180px, 16vw, 240px)) so on smaller
screens the chat area gets more room
- Add explicit width: 100% to rooms-twopane, rooms-detail, and the
pinned room-card-v2 so nothing collapses to content-size
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* Remove timeline left indent and broaden IPC payload filter
- Drop the 3px colored left border + 12px left padding on each
timeline item; messages were starting 29px from the panel edge
(3 border + 12 item padding + 14 thread padding). Role colors are
already conveyed by the role chip, so the border is redundant.
- Tighten thread-section padding from 14px to 12px to match compose.
- Broaden isInternalProtocolPayload to also catch <sub-agent-prompt>,
<tool-call>, <internal> wrapped payloads — these were leaking into
the visible thread when the agent emitted them as message content.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* Restore role color borders and tighten left padding
- Bring back the role-colored left border on timeline items (2px now,
was 3px) so owner/reviewer/arbiter/human are visually distinct
- Tighten left-side spacing across the chat panel: thread section
padding-left 12 -> 4, item padding-left 12 -> 8, card head
padding-left 14 -> 8, compose-section padding-left 12 -> 4. Content
now starts ~10px from the detail panel's inner edge instead of ~30px
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* Unclamp output text in unified timeline + add measure debug toggle
- The legacy .room-output-item p / .room-message-item p / .room-latest-output p
rules used -webkit-line-clamp:2/4 to truncate previews. The unified
timeline doesn't use those classes so it shouldn't be clamped, but
add a defensive override on .room-timeline-body .parsed-body that
unclamps and clears max-height in case any sibling rule leaks in.
- Add ?debug=1 (outline panels) and ?measure=1 (overlay container x/w
in viewport coords) toggles via App.tsx so we can diagnose layout
issues without npm-installing devtools.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* Tighten rooms layout left/right margins
Drop view-panel and section-nav left margin from 12px to 8px. Removes
the visible empty band between icon-rail and rooms-list (was 12px,
now 8px).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* Remove internal left padding in rooms-detail panel
The detail panel had 4-8px of left padding inside the thread and
compose sections, leaving an empty band on the left. Drop padding-left
to 0 on .room-thread-section and .room-compose-section. Timeline items
keep their own 8px padding-left (for the role border + text gap), and
the textarea inherits the same flush-left starting position as
messages — they align vertically now.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* Fix 26px left empty band: zero out .room-expanded padding in pinned mode
Root cause located via pixel scan + DOM dump: the legacy
.room-expanded wrapper (used by the click-to-expand layout) has
padding: 14px 0 4px 26px to align expanded content with the head's
pulse-dot column. In the pinned twopane layout it was leaving a
26px empty band between the panel border and the chat content.
Override .room-expanded inside .room-card-v2.is-pinned to zero
padding/margin/border-top/gap, so thread-section and compose-section
align flush at the panel's inner left edge.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* Add Playwright + dash-inspect helper script
Playwright (chromium) is installed so future layout debugging can use
real DOM/computed-style queries instead of pixel scans on screenshots.
Helper commands:
bun scripts/dash-inspect.ts shot [route]
bun scripts/dash-inspect.ts measure '<selector>'
bun scripts/dash-inspect.ts eval '<expr>'
bun scripts/dash-inspect.ts dom '<selector>'
bun scripts/dash-inspect.ts click '<selector>'
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* Auto-scroll chat to bottom on send and on new messages
- When the user sends a message, scroll the rooms-detail panel to the
very bottom over 4 frames (covers the post-render layout settle).
- Add a near-bottom tracker: a ref flips false when the user scrolls
more than 80px from the bottom, true otherwise. The auto-follow
effect (triggered when messages/outputs/pending counts change) only
scrolls if the user was already near the bottom — so reading scrollback
doesn't get yanked.
- Initial room selection and any new content added while at-bottom
always shows the latest message.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* Add Settings page: model role config + multi-account management
Backend:
- src/settings-store.ts: filesystem reader for Claude (~/.claude,
~/.claude-accounts/N) and Codex (~/.codex, ~/.codex-accounts/N)
account directories. Returns safe metadata only (expiresAt, scopes,
subscriptionType, planType, accountId from JWT) — never raw tokens.
Atomic .env writer for OWNER/REVIEWER/ARBITER MODEL/EFFORT pairs.
Account directory deletion (refuses index 0). Claude account add via
pasted OAuth JWT (decodes exp/sub from payload, writes new
~/.claude-accounts/N/.credentials.json).
- src/web-dashboard-server.ts: new endpoints
GET /api/settings/accounts
GET /api/settings/models
PUT /api/settings/models (PATCH alias)
POST /api/settings/accounts/claude (body: { token })
DELETE /api/settings/accounts/{provider}/{index}
Frontend:
- apps/dashboard/src/api.ts: typed clients for the new endpoints.
- apps/dashboard/src/App.tsx: SettingsPanel split into 일반 / 모델 / 계정.
Model section: editable owner/reviewer/arbiter model+effort fields
with dirty tracking and a save button. Account section: per-provider
list with delete buttons (default index 0 protected) and a token-paste
field for adding a Claude account. Both sections expose a "스택 재시작"
button that calls the existing /api/services/stack/actions endpoint
with a confirmation prompt — changes only take effect after restart.
UX scope: phase B (model edit + account delete) + phase D (account add
via paste) per the recommendation; full OAuth flow not included.
Restart is gated behind a confirm() since it kills running agents.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* Format: Prettier follow-up on settings PR
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* Show cron / external-trigger messages inline (separate from CI watcher)
trusted_external_bot messages (Sentry Cron, GitHub webhook triggers,
etc.) were lumped into the CI-watcher fold and hidden by default.
Split them: WATCHER_RE now only matches actual CI status messages
(Build/Deploy/Lint/Release/[CI]/[Watcher]/GitHub Actions on bot kind),
so the watcher fold stays focused on CI noise.
trusted_external_bot messages now get rendered inline in the unified
timeline as cronMessages (sourceKind preserved). senderRoleClass
returns 'role-cron' for senders matching cron/sentry/webhook so the
chip and left border show in muted gray to visually distinguish from
agent and human messages.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* Live progress fallback: scan TASK_STATUS messages for current turn only
When paired_turns.progress_text is empty (codex runner doesn't always
populate it in time), fall back to scanning the room's messages table
for ones starting with the TASK_STATUS_MESSAGE_PREFIX (the invisible
3-char prefix used on Discord live-edit messages). Strip the prefix
and the trailing elapsed indicator (e.g. "\n\n12초"), then render in
the LIVE timeline entry.
Filters:
- timestamp must be >= turn.createdAt (don't show pre-turn history)
- sender role must match the current turn's role (don't show a stale
reviewer status when owner is now speaking)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* Server fallback: derive live progressText from messages when column empty
Keep paired_turns.progress_text as the primary source (populated by
recordTurnProgress in MessageTurnController). When the column is empty
on an active turn — happens for codex turns where the controller path
isn't fully wired, or any path where ingestion lags — scan the messages
table for the most recent TASK_STATUS_MESSAGE_PREFIX-prefixed message
that belongs to the current turn (sender role match + timestamp >=
turn.createdAt) and use that as progressText.
This makes the dashboard converge with Discord without requiring the
column to be reliably written: if Discord shows a live edit, the
ingest pipeline writes it to messages, and we surface it.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* Poll room timelines every 1.5s on rooms view (was every 15s)
The rooms-detail thread was reusing the 15s overview refresh tick,
which made the chat lag noticeably behind Discord. Add a dedicated
setInterval(1500ms) fetch on the rooms view (cleared when leaving),
with an in-flight guard so overlapping ticks don't pile up. Backend
already has a 2s stale-while-revalidate cache so this stays cheap.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* Real-time rooms updates via Server-Sent Events
Backend: GET /api/stream emits text/event-stream with 'rooms-timeline'
events whenever the server's roomsTimelineCache rebuilds (key change).
A 1.5s tick checks the cache; if builtAt advanced, push the new JSON,
otherwise send a SSE comment heartbeat ': ping ts\n\n' to keep proxies
from idle-closing the connection. AbortSignal handler closes the
ReadableStream when the client disconnects.
Frontend: replace setInterval(1500ms) on rooms view with EventSource.
Initial fetch primes the data so first paint isn't blank, then SSE
takes over for instant updates. EventSource auto-reconnects on
disconnect; while it's flapping, a 2s polling fallback runs so data
keeps flowing. When SSE recovers (next 'rooms-timeline' event) the
polling fallback is cleared.
Latency: previously 0–1.5s polling lag; now ~tick latency (50–150ms)
once cache rebuilds.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* Add breathing room between timeline header and body
Increase room-timeline-item gap from 2px to 6px so the role chip /
timestamp line has visible breathing room above the parsed body —
header → body relationship reads more clearly. Bump vertical padding
from 6px to 8px and item separator from 1px to 2px to match.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* Hide the 2px processing bar in pinned twopane to free role-color borders
.room-card-v2.status-processing::before draws a 2px green bar at the
card's left edge. In pinned twopane mode it sits right on top of each
timeline item's role-colored left border (owner blue / reviewer
orange / arbiter green / human yellow), masking them. The header's
pulse dot already conveys "processing", so suppress the bar in pinned
mode and let the role colors show through.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* Polish room compose: tighter pill input + override global button min-height
The 48px button min-height global rule was forcing the compose grid track to
48px, pushing the textarea content visibly down inside the pill. Adding
min-height: 0 on the compose button lets the track collapse to ~29px so the
placeholder lines up with the send button vertically.
Also slimmed paddings (form 3px vert, textarea 4px vert) and added bottom
breathing room on the compose section (8px → 14px) at user request.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* Format styles.css to multi-line transition values
Prettier 3.8.1 prefers multi-line transition shorthand. CI was failing
format:check on these single-line transitions in the room-compose rules.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Adds a disabled-by-default loopback web dashboard MVP with read-only control-plane views, prompt preview redaction, Vite React UI, and validation coverage.
- Dashboard memory: use /proc/meminfo MemAvailable instead of os.freemem()
to exclude Linux buffer/cache from reported usage
- Add build:container npm script for reviewer Docker image rebuild
- Update deploy commands in CLAUDE.md and README.md to include build:container
- Replace better-sqlite3 with bun:sqlite (native, no native addon build)
- Change all spawn('node') to spawn('bun') for agent processes
- Update package.json scripts: node→bun, tsx→bun, npm→bun
- Add bun-types for tsc compatibility
- Add bun:sqlite→better-sqlite3 shim for vitest (tests run on Node.js)
- Update Dockerfile: install bun alongside Node.js (CLIs need Node)
- Update setup/platform.ts: getNodePath() resolves bun binary
- Remove better-sqlite3 from production dependencies (devDep only for tests)
Add automatic memory integration so EJClaw host directly calls Memento
MCP for recall at session start and reflect on compact, removing
reliance on agent voluntary tool use.
Stage 1: New sessions get room memory briefing injected into system
prompt (CLAUDE.md / AGENTS.md) via host-side MCP client.
Stage 2: PreCompact hook automatically calls reflect + remember to
persist session summaries as room-memory fragments.
Also restores missing source files (token-rotation, codex-token-rotation,
claude-usage exports) to fix full build from source.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Major reliability improvements to the message processing pipeline:
- Add messages.seq monotonic cursor replacing timestamp-based cursors,
preventing message loss from timestamp collisions with LIMIT queries
- Add work_items table separating agent production from delivery,
enabling delivery retry without re-running the agent
- Propagate Discord send failures instead of silently swallowing them
- Add Claude 429 → Kimi K2.5 automatic provider fallback with cooldown
- Fix follow-up turn state reset in live index.ts path (not just
message-runtime.ts) to prevent final output from being lost
- Add restart context tracking for graceful restart announcements
- Lazy migration from timestamp cursors to seq cursors for existing data
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Rewrite container-runner.ts to spawn node processes directly instead
of Docker/Apple Container
- Runner paths configurable via env vars (NANOCLAW_GROUP_DIR, etc.)
with container-path defaults for backwards compat
- Pass real credentials directly (no credential proxy needed)
- Remove ensureContainerSystemRunning() and startCredentialProxy()
from startup
- Add build:runners script for building agent-runner and codex-runner
- Fix TS strict mode errors in agent-runner ipc-mcp-stdio.ts
- Add Codex as alternative agent type (agentType: 'codex' per group)
- Codex container runner with session resume and 100-turn limit
- Dual Discord bot support (DISCORD_BOT_TOKEN + DISCORD_CODEX_BOT_TOKEN)
- Each bot handles only its agentType's registered groups
- Discord typing indicator refreshes every 8s (was single-shot, expired after ~10s)
- Agent runner 100-turn limit to prevent infinite bot-to-bot loops
- Apple Container runtime support (convert-to-apple-container skill)
- Session commands (/compact) support
- DB migration for agent_type column
* refactor: implement channel architecture and dynamic setup
- Introduced ChannelRegistry for dynamic channel loading
- Decoupled WhatsApp from core index.ts and config.ts
- Updated setup wizard to support ENABLED_CHANNELS selection
- Refactored IPC and group registration to be channel-aware
- Verified with 359 passing tests and clean typecheck
* style: fix formatting in config.ts to pass CI
* refactor(setup): full platform-agnostic transformation
- Harmonized all instructional text and help prompts
- Implemented conditional guards for WhatsApp-specific steps
- Normalized CLI terminology across all 4 initial channels
- Unified troubleshooting and verification logic
- Verified 369 tests pass with clean typecheck
* feat(skills): transform WhatsApp into a pluggable skill
- Created .claude/skills/add-whatsapp with full 5-phase interactive setup
- Fixed TS7006 'implicit any' error in IpcDeps
- Added auto-creation of STORE_DIR to prevent crashes on fresh installs
- Verified with 369 passing tests and clean typecheck
* refactor(skills): move WhatsApp from core to pluggable skill
- Move src/channels/whatsapp.ts to add-whatsapp skill add/ folder
- Move src/channels/whatsapp.test.ts to skill add/ folder
- Move src/whatsapp-auth.ts to skill add/ folder
- Create modify/ for barrel file (src/channels/index.ts)
- Create tests/ with skill package validation test
- Update manifest with adds/modifies lists
- Remove WhatsApp deps from core package.json (now skill-managed)
- Remove WhatsApp-specific ghost language from types.ts
- Update SKILL.md to reflect skill-apply workflow
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor(skills): move setup/whatsapp-auth.ts into WhatsApp skill
The WhatsApp auth setup step is channel-specific — move it from core
to the add-whatsapp skill so core stays minimal.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor(skills): convert Telegram skill to pluggable channel pattern
Replace the old direct-integration approach (modifying src/index.ts,
src/config.ts, src/routing.test.ts) with self-registration via the
channel registry, matching the WhatsApp skill pattern.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(skills): fix add-whatsapp build failure and improve auth flow
- Add missing @types/qrcode-terminal to manifest npm_dependencies
(build failed after skill apply without it)
- Make QR-browser the recommended auth method (terminal QR too small,
pairing codes expire too fast)
- Remove "replace vs alongside" question — channels are additive
- Add pairing code retry guidance and QR-browser fallback
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: remove hardcoded WhatsApp default and stale Baileys comment
- ENABLED_CHANNELS now defaults to empty (fresh installs must configure
channels explicitly via /setup; existing installs already have .env)
- Remove Baileys-specific comment from storeMessageDirect() in db.ts
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor(skills): convert Discord, Slack, Gmail skills to pluggable channel pattern
All channel skills now use the same self-registration pattern:
- registerChannel() factory at module load time
- Barrel file append (src/channels/index.ts) instead of orchestrator modifications
- No more *_ONLY flags (DISCORD_ONLY, SLACK_ONLY) — use ENABLED_CHANNELS instead
- Removed ~2500 lines of old modify/ files (src/index.ts, src/config.ts, src/routing.test.ts)
Gmail retains its container-runner.ts and agent-runner modifications (MCP
mount + server config) since those are independent of channel wiring.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: use getRegisteredChannels instead of ENABLED_CHANNELS
Remove the ENABLED_CHANNELS env var entirely. The orchestrator now
iterates getRegisteredChannelNames() from the channel registry —
channels self-register via barrel imports and their factories return
null when credentials are missing, so unconfigured channels are
skipped automatically.
Deleted setup/channels.ts (and its tests) since its sole purpose was
writing ENABLED_CHANNELS to .env. Refactored verify, groups, and
environment setup steps to detect channels by credential presence
instead of reading ENABLED_CHANNELS.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* docs: add breaking change notice and whatsapp migration instructions
CHANGELOG.md documents the pluggable channel architecture shift and
provides migration steps for existing WhatsApp users.
CLAUDE.md updated: Quick Context reflects multi-channel architecture,
Key Files lists registry.ts instead of whatsapp.ts, and a new
Troubleshooting section directs users to /add-whatsapp if WhatsApp
stops connecting after upgrade.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* docs: rewrite READMEs for pluggable multi-channel architecture
Reflects the architectural shift from a hardcoded WhatsApp bot to a
pluggable channel platform. Adds upgrading notice, Mermaid architecture
diagram, CI/License/TypeScript/PRs badges, and clarifies that slash
commands run inside the Claude Code CLI.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* docs: move pluggable channel architecture details to SPEC.md
Revert READMEs to original tone with only two targeted changes:
- Add upgrading notice for WhatsApp breaking change
- Mention pluggable channels in "What It Supports"
Move Mermaid diagram, channel registry internals, factory pattern
explanation, and self-registration walkthrough into docs/SPEC.md.
Update stale WhatsApp-specific references in SPEC.md to be
channel-agnostic.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* docs: move upgrading notice to CHANGELOG, add changelog link
Remove the "Upgrading from Pre-Pluggable Versions" section from
README.md — breaking change details belong in the CHANGELOG. Add a
Changelog section linking to CHANGELOG.md.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* docs: expand CHANGELOG with full PR #500 changes
Cover all changes: channel registry, WhatsApp moved to skill, removed
core dependencies, all 5 skills simplified, orchestrator refactored,
setup decoupled. Use Claude Code CLI instructions for migration.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* chore: bump version to 1.2.0 for pluggable channel architecture
Minor version bump — new functionality (pluggable channels) with a
managed migration path for existing WhatsApp users. Update version
references in CHANGELOG and update skill.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Fix skill application
* fix: use slotted barrel file to prevent channel merge conflicts
Pre-allocate a named comment slot for each channel in
src/channels/index.ts, separated by blank lines. Each skill's
modify file only touches its own slot, so three-way merges
never conflict when applying multiple channels.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: resolve real chat ID during setup for token-based channels
Instead of registering with `pending@telegram` (which never matches
incoming messages), the setup skill now runs an inline bot that waits
for the user to send /chatid, capturing the real chat ID before
registration.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: setup delegates to channel skills, fix group sync and Discord metadata
- Restructure setup SKILL.md to delegate channel setup to individual
channel skills (/add-whatsapp, /add-telegram, etc.) instead of
reimplementing auth/registration inline with broken placeholder JIDs
- Move channel selection to step 5 where it's immediately acted on
- Fix setup/groups.ts: write sync script to temp file instead of passing
via node -e which broke on shell escaping of newlines
- Fix Discord onChatMetadata missing channel and isGroup parameters
- Add .tmp-* to .gitignore for temp sync script cleanup
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: align add-whatsapp skill with main setup patterns
Add headless detection for auth method selection, structured inline
error handling, dedicated number DM flow, and reorder questions to
match main's trigger-first flow.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: add missing auth script to package.json
The add-whatsapp skill adds src/whatsapp-auth.ts but doesn't add
the corresponding npm script. Setup and SKILL.md reference `npm run auth`
for WhatsApp QR terminal authentication.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: update Discord skill tests to match onChatMetadata signature
The onChatMetadata callback now takes 5 arguments (jid, timestamp,
name, channel, isGroup) but the Discord skill tests only expected 3.
This caused skill application to roll back on test failure.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* docs: replace 'pluggable' jargon with clearer language
User-facing text now says "multi-channel" or describes what it does.
Developer-facing text uses "self-registering" or "channel registry".
Also removes extra badge row from README.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* docs: align Chinese README with English version
Remove extra badges, replace pluggable jargon, remove upgrade section
(now in CHANGELOG), add missing intro line and changelog section,
fix setup FAQ answer.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: warn on installed-but-unconfigured channels instead of silent skip
Channels with missing credentials now emit WARN logs naming the exact
missing variable, so misconfigurations surface instead of being hidden.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* docs: simplify changelog to one-liner with compare link
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: add isMain flag and channel-prefixed group folders
Replace MAIN_GROUP_FOLDER constant with explicit isMain boolean on
RegisteredGroup. Group folders now use channel prefix convention
(e.g., whatsapp_main, telegram_family-chat) to prevent cross-channel
collisions.
- Add isMain to RegisteredGroup type and SQLite schema (with migration)
- Replace all folder-based main group checks with group.isMain
- Add --is-main flag to setup/register.ts
- Strip isMain from IPC payload (defense in depth)
- Update MCP tool description for channel-prefixed naming
- Update all channel SKILL.md files and documentation
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: gavrielc <gabicohen22@yahoo.com>
Co-authored-by: Koshkoshinski <daniel.milliner@gmail.com>