feat: free-form sessions, rename/end from the panel, live Claude Context (0.2.0)
build-deb / Lint, test, package (push) Successful in 2m0s
build-deb / Notify on failure (push) Skipped

- Sessions no longer need a project folder: any name works and starts in
  DEVDESK_DEFAULT_DIR unless it matches a project; `devdesk new NAME [PROJECT]`.
- Rename (✎) and end/forget (×) sessions from the Sessions panel, plus
  `devdesk rename`. The browser runs them as `devdesk attach --new|--rename|--end`
  through ttyd. Renames keep the running Claude and its recorded conversation:
  the hook, the run-wrapper and the statusline ask tmux for the current name.
- Claude Context panel is live: devdesk-statusline snapshots model,
  context-window use, cost and edits per session, which --list reports;
  Claude's footer gets a one-line summary. The panel now sits under Sessions.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-07 03:03:09 -04:00
co-authored by Claude Opus 5.5
parent 4b153c1960
commit 527cb1fda2
22 changed files with 703 additions and 171 deletions
+14 -1
View File
@@ -2,7 +2,7 @@ import test from 'node:test';
import assert from 'node:assert/strict';
import {
Client, FlowControl, decodeServer, encodeBinaryInput, encodeInit, encodeInput, encodeResize,
parseListOutput, parseTitle, sessionFromSearch, tokenUrl, wsUrl,
parseListOutput, parseTitle, sessionFromSearch, slugify, stripAnsi, tokenUrl, wsUrl,
} from '../web/src/protocol.js';
const text = (bytes) => new TextDecoder().decode(bytes);
@@ -34,6 +34,19 @@ test('URLs follow the page location, including a path prefix', () => {
assert.equal(tokenUrl(loc), 'https://dev.lotusguild.org/token');
const sub = { protocol: 'http:', host: 'localhost:7681', pathname: '/desk/index.html' };
assert.equal(wsUrl(sub, '--list'), 'ws://localhost:7681/desk/ws?arg=--list');
assert.equal(wsUrl(loc, ['--rename', 'old', 'new']), 'wss://dev.lotusguild.org/ws?arg=--rename&arg=old&arg=new');
});
test('free text becomes a valid session name', () => {
assert.equal(slugify(' PBS upgrade! '), 'PBS-upgrade');
assert.equal(slugify('--../etc'), 'etc');
assert.equal(slugify('tinker_tickets'), 'tinker_tickets');
assert.equal(slugify('!!!'), '');
assert.equal(slugify('x'.repeat(80)).length, 64);
});
test('ANSI colour and title sequences are stripped', () => {
assert.equal(stripAnsi('\x1b[1;31mdevdesk: no\x1b[0m\x1b]0;t\x07'), 'devdesk: no');
});
test('only valid session names are taken from the URL', () => {