mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-08 16:39:42 +02:00
feat(sessions): land auto-naming opt-in, in the prefix form, from the first user prompt only
Finishes #376. The contributed keystroke tracker sat on the raw byte stream and named tabs wrong five ways (every prompt, every write path, a bare Esc eating the next prompt's first character, pasted newlines as Enter, any CSI clearing the draft) and replaced the whole name, which dropped the case from the tab and reset the w<n> counter. This lands the feature with each of those closed: - First prompt means the first: applyAutoName() flips a placeholder to `auto` whether or not the string changed. nameSource is now the tri-state placeholder | auto | manual; the name setter is the only manual path. - Only user-originated input counts: write()/writeViaMux() take SessionWriteOptions.fromUser, set by the browser WS path and POST /input only, so Ralph, respawn, cron, approvals and the trust-dialog keys can never name a tab. A startMode 'shell' CLI never feeds the tracker (a capability, not an id check); the send-key route feeds trackUserInput() because its line feed bypasses the session. - Prefix form `w3-case: title`: parseSessionPrefix() already renders it as the title with the prefix in the tooltip and the next-session counter still matches it. Composed within MAX_SESSION_NAME_LENGTH. - Tracker rules per key: bare Esc resolves at chunk end; mouse/focus reports, Tab, cursor keys, Shift+Tab are no-ops; Up/Down and Ctrl+P/N/R taint the draft so Enter submits nothing rather than a fragment; bracketed-paste newlines and Ctrl+J / Shift+Enter join with one space; the draft keeps its head past 8192 code points; an escape past 64 bytes is abandoned. - Title: slash commands by shape (a path is a prompt), `!` escapes refused, first sentence only past 8 code points ("e.g." is not a title), 72 code points on a word boundary. - Synced `autoNameSessions` setting, default OFF (the prompt reaches mux-sessions.json, session:updated and /api/search), App Settings -> Appearance -> Tabs, read fresh per prompt after the eligibility check. Tests: test/session-auto-name.test.ts (tracker, title, composition, ownership, emit gating), the wiring test (once, prefix, setting off, manual protected), test/routes/session-name-routes.test.ts (PUT /name flips to manual and persists). Verified live on an isolated instance: API and browser-typed prompts name the tab, a second prompt does not, shells and renamed tabs are untouched, nameSource survives a restart. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -12,7 +12,6 @@
|
||||
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { Session } from '../src/session.js';
|
||||
import { deriveAutoSessionName, SubmittedPromptTracker } from '../src/session-auto-name.js';
|
||||
|
||||
describe('session submit anchor', () => {
|
||||
it('records the pane Enter and carries it into persisted state', () => {
|
||||
@@ -54,31 +53,3 @@ describe('session submit anchor', () => {
|
||||
expect(recovered.lastSubmitAt).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('automatic session names', () => {
|
||||
it('builds a bounded title from the first sentence without exposing controls', () => {
|
||||
expect(deriveAutoSessionName(' 修复登录跳转问题。\n不要改数据库')).toBe('修复登录跳转问题。');
|
||||
expect(deriveAutoSessionName('/clear')).toBeNull();
|
||||
expect(deriveAutoSessionName('\x1b[31m整理项目文档\x1b[0m')).toBe('整理项目文档');
|
||||
expect(Array.from(deriveAutoSessionName('a'.repeat(200)) ?? '')).toHaveLength(72);
|
||||
});
|
||||
|
||||
it('tracks chunked typing, backspace, and Enter without treating arrows as prompt text', () => {
|
||||
const tracker = new SubmittedPromptTracker();
|
||||
expect(tracker.feed('修复登')).toEqual([]);
|
||||
expect(tracker.feed('录跳转\x7f问题\r')).toEqual(['修复登录跳问题']);
|
||||
expect(tracker.feed('旧内容\x1b[A新内容\r')).toEqual(['新内容']);
|
||||
});
|
||||
|
||||
it('keeps manual names protected while generated names remain eligible', () => {
|
||||
const generated = new Session({ workingDir: '/tmp', name: 'w1-demo' });
|
||||
expect(generated.nameSource).toBe('auto');
|
||||
expect(generated.applyAutoName('修复登录')).toBe(true);
|
||||
expect(generated.applyAutoName('继续重命名')).toBe(true);
|
||||
|
||||
const manual = new Session({ workingDir: '/tmp', name: '我的工作窗口' });
|
||||
expect(manual.nameSource).toBe('manual');
|
||||
expect(manual.applyAutoName('不应覆盖')).toBe(false);
|
||||
expect(manual.name).toBe('我的工作窗口');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user