Skip to content

Fix/headless bridges slash#114

Merged
chauncygu merged 3 commits into
mainfrom
fix/headless-bridges-slash
May 10, 2026
Merged

Fix/headless bridges slash#114
chauncygu merged 3 commits into
mainfrom
fix/headless-bridges-slash

Conversation

@chauncygu
Copy link
Copy Markdown
Contributor

No description provided.

chauncygu and others added 3 commits May 10, 2026 12:25
…resizable sidebar

ChatGPT-style session organization in the Chat UI, layered on top of the
slash-fix work in the previous commit. Five independent UX features behind
a single coordinated change so the schema migration only runs once.

Folders
  - New `folders` table (per-user, name unique). chat_sessions gains a
    nullable `folder_id` FK.
  - In-place migration in init_db(): PRAGMA table_info probes for the
    column on existing DBs and ALTER TABLE adds it. No Alembic, no manual
    upgrade steps.
  - Endpoints: GET/POST /api/folders, PATCH/DELETE /api/folders/{id},
    PATCH /api/sessions/{id}/folder. Cross-user folders return 404 the
    same way single-session ownership checks do.
  - Deleting a folder reparents its sessions to Ungrouped (folder_id =
    NULL) rather than deleting them. Repo layer NULLs explicitly because
    SQLite PRAGMA foreign_keys is off in this engine.

Drag-drop + Move-to context menu
  - Sessions are HTML5-draggable; folder rows + Ungrouped header are
    drop targets with visual highlight.
  - Right-click on a session: flat Move-to list with every folder, plus
    (Ungrouped) when applicable, plus "+ New folder…" for create-and-move
    in one click.
  - Right-click on a folder: Rename / Delete, with a confirm() that says
    "sessions become Ungrouped — they are NOT deleted".

Active-folder context (ChatGPT-style)
  - Click a folder name (not the disclosure arrow) to enter that folder.
    Row gets accent highlight; topbar grows a "Chat · in <Folder>"
    breadcrumb.
  - While a folder is active, + New and direct-typing auto-create both
    drop the new session into that folder. Switching to any session
    syncs the active context to that session's folder.
  - State persists across reloads via localStorage (cc-active-folder).
    Deleted folders auto-clear.

Batch operations
  - "Select" button enters multi-select mode (checkboxes, Select all
    respects the search filter). Footer action bar batch-deletes (single
    confirm + total-message count) or batch-exports as one combined
    Markdown (chats-N-sessions.md).
  - Endpoints: POST /api/sessions/batch_delete and batch_export.
  - Right-click is suppressed in select mode to avoid mode confusion.

Resizable sidebar
  - 4-px drag handle between sidebar and main pane (mouse + touch).
    Width clamped to 200–600 px and persisted to localStorage
    (cc-sidebar-w). Double-click resets to default. Hidden under
    @media (max-width: 768px) so the mobile drawer keeps its swipe
    behavior.

Tests
  - +10 in tests/test_web_api.py: folder CRUD, duplicate-409, move,
    delete-preserves-as-ungrouped, cross-user isolation, list-includes-
    folder_id, batch delete, batch delete cross-user, batch export,
    batch export empty 400.
  - File now at 31 tests, all passing. Zero regressions on the prior 21.

Docs
  - README news entry (today, latest), Web UI feature table, API ASCII
    diagram.
  - docs/news.md — same two May 10 entries (feature + earlier bug-fix).
  - docs/guides/web-ui.md — Layout, Persistence (5 tables), Session
    management table, new Folders & active-folder context section, HTTP
    API (Folders subsection, Sessions extras), Architecture notes
    (migration + delete_folder PRAGMA detail + two-step session
    placement), Tests 21 → 31.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
 follow-up)

In Docker / --web headless deploys, Telegram / Slack / WeChat bridges
silently dropped /<cmd> messages — no reply, no log line. The bridges'
poll loops gate on `session_ctx.handle_slash` and fall through to
`continue` when it's None. The interactive `repl()` wired the callback
at cheetahclaws.py:1142, but `_start_headless_bridges()` only set
`run_query` and `agent_state`, never `handle_slash`. So in any deploy
without a terminal REPL (Docker compose, --web), every Telegram /help,
/monitor, /model, /status was a no-op.

Fix:
  - Extract the slash handler (originally inlined inside repl()) into
    a module-level factory `_make_bridge_slash_handler(state, config,
    run_query)`. Same closure shape; takes the run_query callable so
    repl() and headless paths can each pass their own.
  - `_start_headless_bridges()` now sets
    `session_ctx.handle_slash = _make_bridge_slash_handler(...)` right
    after wiring `run_query`.
  - `repl()` switches to the same factory, deleting the duplicate
    inline definition. Single source of truth, no future drift between
    REPL and headless slash behavior.

Tests: tests/test_bridge_slash_handler.py — 5 cases:
  - simple commands return "simple", run_query untouched
  - __brainstorm__ sentinel dispatches one run_query with the strict
    todo-write rules
  - __worker__ sentinel dispatches one run_query per task in order
  - unknown sentinel is a no-op, doesn't crash
  - end-to-end pin: after _start_headless_bridges runs with bridge
    config present, session_ctx.handle_slash is callable. This is the
    direct regression guard — pre-fix this attribute was None, which
    was the actual user-visible bug.

Refs: #84 (file round-trip + clickable approve was fixed in a099a47;
this addresses the slash-command silence reported as a follow-up).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Brings in two commits from fix/web-ui:
  - b74e14a fix(web): slash commands no longer reply twice; --web --model X persists
  - 28f1ffd feat(web): folders + drag-drop + active-folder context + batch ops + resizable sidebar

Combined with the headless-bridges slash fix on this branch, fix/headless-bridges-slash now carries the full set of Chat UI + bridges work.
@chauncygu chauncygu merged commit 094ed1e into main May 10, 2026
6 checks passed
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