Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ docker compose up -d
docker compose logs -f claude-chat
```

No linting and no build step configured. `npm test` chains 83 test files under `test/`: 19 DOM-less render/UI-logic tests (`test/render/*.test.mjs`, run through `node --test`) plus 64 plain-`node` suites in `test/` covering the overload detector, env load order, multi-agent results, terminals, bots, telegram, updates, kanban scheduling, the Kanban card's run-settings badges (`kanban-run-badges.test.js` pins the card's model/effort/engine chain against the one `startTask` actually resolves — the two live in different files and the card silently lies when they drift), the board-only `create_task` status (`task-backlog.test.js`), the Kanban group form (`kanban-group-button.test.js` RUNS the real `buildChainForm` in a `vm` context so a stray free variable in its template throws there instead of silently killing the modal — see #115 — and parses every inline `<script>` of the board), i18n completeness, the config precedence resolver plus its secret masking, usage-limit detection, authentication-failure classification (`auth-errors.test.js`, which pins that the detector runs BEFORE the auto-continue in all three agent loops — a reorder there silently restores #86), the filesystem path guard (including the SVG sandbox header and the symlink rule on the `@`-mention search endpoints) plus the tunnel-blocks-terminal rule, WS session re-subscription, the SSH remote CLI-session import, the live engine pane / interactive-prompt watchdog, the subscription engine's spawn command (`engine-spawn-cmd.test.js` starts a real tmux session with a 40 KB system prompt and asserts the child's argv byte-identical — the tmux command may never carry the prompt inline, see #96), the cross-project global workspace aggregation, the add-project-from-Git-URL endpoint (`git-clone.test.js` boots a server whose `PATH` holds a fake `git` and pins the transport allowlist, the `--` argv shape and the failure cleanup — see #94), the rule that an SSH credential never leaves the server process, the Windows command-quoting oracle, the auth token lifecycle, the multi-agent dependency scheduler (waves, plan sanitising, and the rule that a failure warning must survive dep-context truncation), the SSH stream parser's three guards, the SSH run's termination guarantee (`ssh-termination.test.js` drives `ClaudeSSH.send()` through a fake ssh2 `Client` in `require.cache` and asserts `onDone` fires EXACTLY once on every ending — a missed one hangs the chat forever, a doubled one re-emits stderr) and the recovery contract of "Restart Session" (`session-restart.test.js` boots a real server against a fake `claude` that never exits, then pins that a restart ABORTS that turn and releases the session instead of refusing), the remote non-interactive shell environment (`remote-env.test.js`, which runs the generated prelude through real `bash -lc`: it must parse, print nothing on stdout, and never end on a false test — the caller chains `&& claude …` behind it), the remote CLI-list framing parser, the bot inbox's SQL seam (`bot-inbox.test.js` pins that `from_bot AS "from"` keeps the exact key `planInboxDelivery` reads — rename one without the other and every letter is silently retired as malformed), the one-time config/.env migration onto CCS_CONFIG_PATH and the mid-task clarification delivery contract on the subscription engine (`interrupt-delivery.test.js` — pins that the tmux injection block sits BEFORE the poll loop's completion `break`, that draining does not imply delivery, that a failed paste is re-queued and warns non-terminally, and that the task runner passes the same callbacks the chat path does), the CLAUDE.md / AGENTS.md discovery rules (`agents-md.test.js`, which also pins that AGENTS.md reaches the subprocess as `--append-system-prompt` and never as `--system-prompt`), and the remote file browser's three guard layers (`remote-files.test.js` runs the generated POSIX script through a real `/bin/sh` against a temp tree that contains symlinks OUT of the project; `remote-files-api.test.js` boots a server against a fake remote via `CCS_REMOTE_EXEC_HOOK` and drives `/api/files` the way the SPA does), the editor deep links (`editor-links.test.js` pins the two URI shapes literally — the browser link puts `vscode-remote` in the AUTHORITY and the CLI argument puts it in the SCHEME, and collapsing the two silently breaks one path; `editor-open-api.test.js` boots a real server with `PATH` pointed at an EMPTY directory, which both makes the `opened:'client'` fallback deterministic and guarantees the suite never launches an editor window on a developer's desktop), and the new-chat defaults chain (`chat-defaults.test.js` pins the pure resolver — the built-ins are asserted to be exactly what the SPA hardcoded before #58, and the choice lists to be exactly the toolbar's `data-v` sets and `MODEL_MAP`'s aliases; `chat-defaults-api.test.js` boots a real server in a throwaway `APP_DIR` and pins that a project writes back a SPARSE override object — a five-key snapshot passes every other assertion in that file and still breaks the feature). On the render side, `tables.test.mjs` also pins the ReDoS bound in renderMd step 3.4, `xss.test.mjs` runs 24 adversarial payloads end-to-end, and `forged-tokens.test.mjs` covers the case where user text contains the renderer's own placeholder control bytes, and `pane-font.test.mjs` pins the clamp DIRECTION of `_fitEnginePaneFont` (a wide engine pane may only shrink; a narrow split pane must be allowed to grow). `script-scope.test.mjs` pins which `<script>` block a helper is declared in — declarations hoist only within their own block, so a helper used by `loadSess()` must not live in the terminal block at the bottom of the file. Note the glob: a file under `test/render/` whose name does not end in `.test.mjs` is NEVER run — `_load.selftest.mjs` sat there unexecuted until it was renamed to `loader.test.mjs`. It runs serially and aborts on the first failing file. `.github/workflows/ci.yml` runs it on every push and PR to `main` (tmux installed, so the five tmux-dependent suites do not self-skip).
No linting and no build step configured. `npm test` chains 84 test files under `test/`: 19 DOM-less render/UI-logic tests (`test/render/*.test.mjs`, run through `node --test`) plus 65 plain-`node` suites in `test/` covering the overload detector, env load order, multi-agent results, terminals, bots, telegram, updates, kanban scheduling, the Kanban card's run-settings badges (`kanban-run-badges.test.js` pins the card's model/effort/engine chain against the one `startTask` actually resolves — the two live in different files and the card silently lies when they drift), the board-only `create_task` status (`task-backlog.test.js`), the Kanban group form (`kanban-group-button.test.js` RUNS the real `buildChainForm` in a `vm` context so a stray free variable in its template throws there instead of silently killing the modal — see #115 — and parses every inline `<script>` of the board), the on-screen terminal keys (`terminal-keys.test.js` runs the real #117 block from `index.html` in a `vm`: the Esc/Tab/arrow bytes including SS3 under DECCKM, a ONE-SHOT Ctrl that applies to the next key from either the row or the phone keyboard, and that a tap never takes focus — a focused button closes Gboard on every key), i18n completeness, the config precedence resolver plus its secret masking, usage-limit detection, authentication-failure classification (`auth-errors.test.js`, which pins that the detector runs BEFORE the auto-continue in all three agent loops — a reorder there silently restores #86), the filesystem path guard (including the SVG sandbox header and the symlink rule on the `@`-mention search endpoints) plus the tunnel-blocks-terminal rule, WS session re-subscription, the SSH remote CLI-session import, the live engine pane / interactive-prompt watchdog, the subscription engine's spawn command (`engine-spawn-cmd.test.js` starts a real tmux session with a 40 KB system prompt and asserts the child's argv byte-identical — the tmux command may never carry the prompt inline, see #96), the cross-project global workspace aggregation, the add-project-from-Git-URL endpoint (`git-clone.test.js` boots a server whose `PATH` holds a fake `git` and pins the transport allowlist, the `--` argv shape and the failure cleanup — see #94), the rule that an SSH credential never leaves the server process, the Windows command-quoting oracle, the auth token lifecycle, the multi-agent dependency scheduler (waves, plan sanitising, and the rule that a failure warning must survive dep-context truncation), the SSH stream parser's three guards, the SSH run's termination guarantee (`ssh-termination.test.js` drives `ClaudeSSH.send()` through a fake ssh2 `Client` in `require.cache` and asserts `onDone` fires EXACTLY once on every ending — a missed one hangs the chat forever, a doubled one re-emits stderr) and the recovery contract of "Restart Session" (`session-restart.test.js` boots a real server against a fake `claude` that never exits, then pins that a restart ABORTS that turn and releases the session instead of refusing), the remote non-interactive shell environment (`remote-env.test.js`, which runs the generated prelude through real `bash -lc`: it must parse, print nothing on stdout, and never end on a false test — the caller chains `&& claude …` behind it), the remote CLI-list framing parser, the bot inbox's SQL seam (`bot-inbox.test.js` pins that `from_bot AS "from"` keeps the exact key `planInboxDelivery` reads — rename one without the other and every letter is silently retired as malformed), the one-time config/.env migration onto CCS_CONFIG_PATH and the mid-task clarification delivery contract on the subscription engine (`interrupt-delivery.test.js` — pins that the tmux injection block sits BEFORE the poll loop's completion `break`, that draining does not imply delivery, that a failed paste is re-queued and warns non-terminally, and that the task runner passes the same callbacks the chat path does), the CLAUDE.md / AGENTS.md discovery rules (`agents-md.test.js`, which also pins that AGENTS.md reaches the subprocess as `--append-system-prompt` and never as `--system-prompt`), and the remote file browser's three guard layers (`remote-files.test.js` runs the generated POSIX script through a real `/bin/sh` against a temp tree that contains symlinks OUT of the project; `remote-files-api.test.js` boots a server against a fake remote via `CCS_REMOTE_EXEC_HOOK` and drives `/api/files` the way the SPA does), the editor deep links (`editor-links.test.js` pins the two URI shapes literally — the browser link puts `vscode-remote` in the AUTHORITY and the CLI argument puts it in the SCHEME, and collapsing the two silently breaks one path; `editor-open-api.test.js` boots a real server with `PATH` pointed at an EMPTY directory, which both makes the `opened:'client'` fallback deterministic and guarantees the suite never launches an editor window on a developer's desktop), and the new-chat defaults chain (`chat-defaults.test.js` pins the pure resolver — the built-ins are asserted to be exactly what the SPA hardcoded before #58, and the choice lists to be exactly the toolbar's `data-v` sets and `MODEL_MAP`'s aliases; `chat-defaults-api.test.js` boots a real server in a throwaway `APP_DIR` and pins that a project writes back a SPARSE override object — a five-key snapshot passes every other assertion in that file and still breaks the feature). On the render side, `tables.test.mjs` also pins the ReDoS bound in renderMd step 3.4, `xss.test.mjs` runs 24 adversarial payloads end-to-end, and `forged-tokens.test.mjs` covers the case where user text contains the renderer's own placeholder control bytes, and `pane-font.test.mjs` pins the clamp DIRECTION of `_fitEnginePaneFont` (a wide engine pane may only shrink; a narrow split pane must be allowed to grow). `script-scope.test.mjs` pins which `<script>` block a helper is declared in — declarations hoist only within their own block, so a helper used by `loadSess()` must not live in the terminal block at the bottom of the file. Note the glob: a file under `test/render/` whose name does not end in `.test.mjs` is NEVER run — `_load.selftest.mjs` sat there unexecuted until it was renamed to `loader.test.mjs`. It runs serially and aborts on the first failing file. `.github/workflows/ci.yml` runs it on every push and PR to `main` (tmux installed, so the five tmux-dependent suites do not self-skip).

## Architecture

Expand Down Expand Up @@ -1033,7 +1033,7 @@ message replays the chat's history into a fresh Claude session rather than losin

## How to Verify Changes

`npm test` runs 83 test files under `test/` (19 `test/render/*.test.mjs` + 64 `test/*.test.js`), and `.github/workflows/ci.yml` runs the same command on every push and PR to `main`. Nothing covers the live browser/WebSocket path, so also verify that manually:
`npm test` runs 84 test files under `test/` (19 `test/render/*.test.mjs` + 65 `test/*.test.js`), and `.github/workflows/ci.yml` runs the same command on every push and PR to `main`. Nothing covers the live browser/WebSocket path, so also verify that manually:

```bash
# 1. Start server
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"scripts": {
"start": "node server.js",
"dev": "node --watch server.js",
"test": "node --test test/render/*.test.mjs && node test/overload-detector.test.js && node test/usage-limit.test.js && node test/auth-errors.test.js && node test/env-load-order.test.js && node test/multi-agent-result.test.js && node test/terminal-session.test.js && node test/terminal-schema.test.js && node test/bots.test.js && node test/bot-inbox.test.js && node test/bots-api.test.js && node test/running-sessions.test.js && node test/session-liveness.test.js && node test/terminal-bridge.integration.test.js && node test/tmux-composite.test.js && node test/engine-spawn-cmd.test.js && node test/telegram-format.test.js && node test/telegram-behaviour.test.js && node test/ask-user-question.test.js && node test/delegate-terminal.test.js && node test/update-flow.test.js && node test/kanban-schedule.test.js && node test/kanban-run-badges.test.js && node test/task-backlog.test.js && node test/i18n-completeness.test.js && node test/config-resolve.test.js && node test/chat-defaults.test.js && node test/chat-defaults-api.test.js && node test/run-continuation.test.js && node test/worktree-manager.test.js && node test/setup-gate.test.js && node test/path-guard.test.js && node test/subscribe-nocatchup.test.js && node test/cli-import-remote.test.js && node test/remote-list-parse.test.js && node test/config-migrate.test.js && node test/engine-pane.test.js && node test/global-workspace.test.js && node test/ssh-secret.test.js && node test/agent-dag.test.js && node test/ssh-parser.test.js && node test/ssh-termination.test.js && node test/session-restart.test.js && node test/remote-env.test.js && node test/remote-files.test.js && node test/remote-files-api.test.js && node test/auth-token.test.js && node test/cmd-model.test.js && node test/origin-guard.test.js && node test/interrupted-recovery.test.js && node test/queue-persistence.test.js && node test/interrupt-idle-config.test.js && node test/agents-md.test.js && node test/editor-links.test.js && node test/editor-open-api.test.js && node test/interrupt-delivery.test.js && node test/terminal-pane-guard.integration.test.js && node test/composer-terminal.test.js && node test/claude-cli-status.test.js && node test/asar-helpers.test.js && node test/task-workers.test.js && node test/git-clone.test.js && node test/responsive-ui.test.js && node test/kanban-delegation.test.js && node test/kanban-group-button.test.js",
"test": "node --test test/render/*.test.mjs && node test/overload-detector.test.js && node test/usage-limit.test.js && node test/auth-errors.test.js && node test/env-load-order.test.js && node test/multi-agent-result.test.js && node test/terminal-session.test.js && node test/terminal-schema.test.js && node test/bots.test.js && node test/bot-inbox.test.js && node test/bots-api.test.js && node test/running-sessions.test.js && node test/session-liveness.test.js && node test/terminal-bridge.integration.test.js && node test/tmux-composite.test.js && node test/engine-spawn-cmd.test.js && node test/telegram-format.test.js && node test/telegram-behaviour.test.js && node test/ask-user-question.test.js && node test/delegate-terminal.test.js && node test/update-flow.test.js && node test/kanban-schedule.test.js && node test/kanban-run-badges.test.js && node test/task-backlog.test.js && node test/i18n-completeness.test.js && node test/config-resolve.test.js && node test/chat-defaults.test.js && node test/chat-defaults-api.test.js && node test/run-continuation.test.js && node test/worktree-manager.test.js && node test/setup-gate.test.js && node test/path-guard.test.js && node test/subscribe-nocatchup.test.js && node test/cli-import-remote.test.js && node test/remote-list-parse.test.js && node test/config-migrate.test.js && node test/engine-pane.test.js && node test/global-workspace.test.js && node test/ssh-secret.test.js && node test/agent-dag.test.js && node test/ssh-parser.test.js && node test/ssh-termination.test.js && node test/session-restart.test.js && node test/remote-env.test.js && node test/remote-files.test.js && node test/remote-files-api.test.js && node test/auth-token.test.js && node test/cmd-model.test.js && node test/origin-guard.test.js && node test/interrupted-recovery.test.js && node test/queue-persistence.test.js && node test/interrupt-idle-config.test.js && node test/agents-md.test.js && node test/editor-links.test.js && node test/editor-open-api.test.js && node test/interrupt-delivery.test.js && node test/terminal-pane-guard.integration.test.js && node test/composer-terminal.test.js && node test/claude-cli-status.test.js && node test/asar-helpers.test.js && node test/task-workers.test.js && node test/git-clone.test.js && node test/responsive-ui.test.js && node test/kanban-delegation.test.js && node test/kanban-group-button.test.js && node test/terminal-keys.test.js",
"postinstall": "node scripts/install-hooks.js",
"release": "node scripts/release.js",
"electron:dev": "electron .",
Expand Down
Loading
Loading