wtt-connect 0.2.29 → 0.2.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wtt-connect",
3
- "version": "0.2.29",
3
+ "version": "0.2.30",
4
4
  "private": false,
5
5
  "description": "WTT-native connector daemon for Codex, Claude Code, Cursor, Gemini, ACP, and other coding agent surfaces.",
6
6
  "type": "module",
@@ -3,7 +3,7 @@ import { execFile } from 'node:child_process';
3
3
  import readline from 'node:readline';
4
4
  import { promisify } from 'node:util';
5
5
  import { log } from '../logger.js';
6
- import { parseSlashCommand } from '../slash.js';
6
+ import { CODEX_SLASH_COMMANDS, formatSlashCommandHelp, parseSlashCommand } from '../slash.js';
7
7
 
8
8
  const execFileAsync = promisify(execFile);
9
9
 
@@ -47,26 +47,21 @@ export class CodexAdapter {
47
47
 
48
48
  switch (parsed.command) {
49
49
  case '/help':
50
- return [
51
- 'Codex slash commands supported by WTT:',
52
- '- `/status`: show Codex runtime/session status.',
53
- '- `/model`: show the model selected by WTT for this run. Use the WTT model picker to switch models.',
54
- '- `/approvals`: show the current approval/sandbox mode.',
55
- '- `/diff`: show current git diff summary and patch excerpt.',
56
- '- `/review`: run Codex native code review for the current workspace.',
57
- '- `/init`: ask Codex to inspect the workspace and create/update AGENTS.md guidance.',
58
- '- `/clear`: clear the stored Codex resume thread for this WTT topic.',
59
- '- `/compact`: non-interactive Codex cannot compact an existing TUI context; WTT clears the stored resume thread instead.',
60
- ].join('\n');
50
+ return formatSlashCommandHelp(this.name);
61
51
  case '/status':
62
52
  return this.codexStatus(sessionKey, context);
63
53
  case '/model':
64
54
  return this.codexModel(context, parsed.args);
65
55
  case '/approvals':
56
+ case '/permissions':
66
57
  return this.codexApprovals();
67
58
  case '/diff':
68
59
  return this.gitDiff();
69
60
  case '/clear':
61
+ case '/new':
62
+ case '/reset':
63
+ case '/new-session':
64
+ case '/new-thread':
70
65
  return this.clearSession(sessionKey, 'clear');
71
66
  case '/compact':
72
67
  return this.clearSession(sessionKey, 'compact');
@@ -75,6 +70,7 @@ export class CodexAdapter {
75
70
  case '/init':
76
71
  return this.runInit(context, sessionKey);
77
72
  default:
73
+ if (CODEX_TUI_ONLY_COMMANDS.has(parsed.command)) return codexTuiOnlyMessage(parsed.command);
78
74
  return null;
79
75
  }
80
76
  }
@@ -180,6 +176,37 @@ export class CodexAdapter {
180
176
  }
181
177
  }
182
178
 
179
+ const CODEX_HEADLESS_COMPAT_COMMANDS = new Set([
180
+ '/help',
181
+ '/status',
182
+ '/model',
183
+ '/approvals',
184
+ '/permissions',
185
+ '/diff',
186
+ '/clear',
187
+ '/new',
188
+ '/reset',
189
+ '/new-session',
190
+ '/new-thread',
191
+ '/compact',
192
+ '/review',
193
+ '/init',
194
+ ]);
195
+
196
+ const CODEX_TUI_ONLY_COMMANDS = new Set(
197
+ CODEX_SLASH_COMMANDS.map(([cmd]) => cmd).filter((cmd) => !CODEX_HEADLESS_COMPAT_COMMANDS.has(cmd)),
198
+ );
199
+
200
+ function codexTuiOnlyMessage(command) {
201
+ return [
202
+ `Codex ${command} is a native interactive TUI slash command.`,
203
+ '',
204
+ 'WTT runs Codex through `codex exec` for chat/agent automation. In Codex CLI 0.133.0, `codex exec` treats slash text from stdin as a normal prompt instead of dispatching the TUI slash command.',
205
+ '',
206
+ 'Open the Agent Terminal to run this command in native Codex TUI, or use WTT-compatible commands such as `/status`, `/model`, `/permissions`, `/diff`, `/review`, `/init`, `/new`, `/clear`, and `/compact` from chat.',
207
+ ].join('\n');
208
+ }
209
+
183
210
  async function commandOutput(bin, args, cwd, timeoutMs = 30000, maxChars = 8000) {
184
211
  try {
185
212
  const { stdout, stderr } = await execFileAsync(bin, args, { cwd, timeout: timeoutMs, maxBuffer: Math.max(maxChars * 4, 1024 * 1024) });
package/src/runner.js CHANGED
@@ -20,7 +20,7 @@ import { buildRuntimeInfo } from './runtime-info.js';
20
20
  import { runShellCommand } from './shell-runner.js';
21
21
  import { TerminalSessionManager } from './terminal-session.js';
22
22
  import { isModelSwitcherEnabled, switchModelProvider } from './model-switcher.js';
23
- import { isAgentSlashCommand, parseSlashCommand } from './slash.js';
23
+ import { formatSlashCommandHelp, isAgentSlashCommand, parseSlashCommand, slashCommandsForAdapter } from './slash.js';
24
24
 
25
25
  const TERMINAL_STATUSES = new Set(['review', 'done', 'approved', 'cancelled']);
26
26
  const GENERATED_FILE_EXTENSIONS = new Set([
@@ -182,9 +182,10 @@ export class Runner {
182
182
  const discussionRouting = renderDiscussionRoutingInstruction(m, this.config);
183
183
  const currentDisplayName = effectiveAgentDisplayName(this.config, agentProfile);
184
184
  const isSlashPassthrough = isAgentSlashCommand(m, content);
185
+ const sessionKey = `wtt:topic:${topicId}:${adapter.name}:${sessionModelKey(modelConfig)}`;
185
186
  if (isSlashPassthrough) {
186
187
  const localSlash = await this.handleLocalSlashCommand(content, adapter, {
187
- sessionKey: `wtt:topic:${topicId}:${adapter.name}:${sessionModelKey(modelConfig)}`,
188
+ sessionKey,
188
189
  topicId,
189
190
  modelConfig,
190
191
  files: staged.files,
@@ -198,6 +199,7 @@ export class Runner {
198
199
  return;
199
200
  }
200
201
  }
202
+ const compactSummary = this.store.getSession(sessionKey).compactSummary || '';
201
203
  const prompt = isSlashPassthrough ? content : [
202
204
  'You are replying to a WTT Web conversation. Do not mention implementation internals unless asked.',
203
205
  `WTT topic_id: ${topicId}`,
@@ -212,6 +214,8 @@ export class Runner {
212
214
  discussionRouting ? '' : null,
213
215
  agentSoul,
214
216
  agentSoul ? '' : null,
217
+ renderCompactSummaryBlock(compactSummary),
218
+ compactSummary ? '' : null,
215
219
  'User message:',
216
220
  content,
217
221
  '',
@@ -221,7 +225,7 @@ export class Runner {
221
225
  'Reply naturally and concisely unless the user asks for detail.',
222
226
  ].filter(Boolean).join('\n');
223
227
  const output = await this.runAdapter(adapter, prompt, {
224
- sessionKey: `wtt:topic:${topicId}:${adapter.name}:${sessionModelKey(modelConfig)}`,
228
+ sessionKey,
225
229
  topicId,
226
230
  modelConfig,
227
231
  files: staged.files,
@@ -336,9 +340,18 @@ export class Runner {
336
340
  if (parsed.command === '/upgrade') {
337
341
  return this.handleUpgradeCommand(parsed.args);
338
342
  }
343
+ if (parsed.command === '/help' || parsed.command === '/?') {
344
+ return formatSlashCommandHelp(adapter.name);
345
+ }
339
346
  if (parsed.command === '/status') {
340
347
  return this.runtimeStatusText(adapter, context);
341
348
  }
349
+ if (parsed.command === '/model' || parsed.command === '/models') {
350
+ return this.runtimeModelText(adapter, context, parsed.args);
351
+ }
352
+ if (['/compact', '/compress'].includes(parsed.command)) {
353
+ return this.compactAdapterSession(adapter, context, parsed.command, parsed.args);
354
+ }
342
355
  if (['/clear', '/new', '/reset', '/new-session', '/new-thread'].includes(parsed.command)) {
343
356
  return this.clearAdapterSession(adapter, context.sessionKey || 'default', parsed.command);
344
357
  }
@@ -346,6 +359,9 @@ export class Runner {
346
359
  const handled = await adapter.handleSlashCommand(content, context);
347
360
  if (handled !== null && handled !== undefined) return handled;
348
361
  }
362
+ if (isKnownAdapterSlashCommand(adapter.name, parsed.command)) {
363
+ return headlessSlashUnsupportedMessage(adapter.name, parsed.command);
364
+ }
349
365
  return null;
350
366
  }
351
367
 
@@ -356,6 +372,72 @@ export class Runner {
356
372
  return `${adapterDisplayName(adapter.name)} session cleared for this WTT topic. The next turn starts a fresh agent thread/session.`;
357
373
  }
358
374
 
375
+ async compactAdapterSession(adapter, context = {}, command = '/compact', args = '') {
376
+ const sessionKey = context.sessionKey || 'default';
377
+ const topicId = String(context.topicId || '').trim();
378
+ if (!topicId) return this.clearAdapterSession(adapter, sessionKey, command);
379
+ const messages = await this.api.getTopicMessages(topicId, { limit: 160, includeHistory: true });
380
+ const transcript = formatTopicMessagesForCompaction(messages, {
381
+ currentAgentId: this.config.agentId,
382
+ excludeSlashCommands: new Set(['/compact', '/compress']),
383
+ });
384
+ if (!transcript) {
385
+ return `${adapterDisplayName(adapter.name)} compact skipped: no topic history was available.`;
386
+ }
387
+ const focus = String(args || '').trim();
388
+ const prompt = [
389
+ 'You are compacting a WTT agent conversation for continuation in a fresh headless CLI session.',
390
+ 'Create a durable context summary that will be injected before the next user message.',
391
+ '',
392
+ 'Requirements:',
393
+ '- Preserve user goals, constraints, decisions, unresolved questions, file paths, commands, errors, and current next steps.',
394
+ '- Preserve any model/tool/API/deployment details that matter for future turns.',
395
+ '- Drop greetings, duplicate status noise, progress chatter, and irrelevant old details.',
396
+ '- Do not invent facts. If uncertain, say so briefly.',
397
+ '- Keep it concise but complete enough to continue work without the previous CLI session.',
398
+ focus ? `- User focus for this compaction: ${focus}` : null,
399
+ '',
400
+ 'Return Markdown with these sections:',
401
+ '1. Current Goal',
402
+ '2. Key Facts And Decisions',
403
+ '3. Files/Commands/Environment',
404
+ '4. Open Issues',
405
+ '5. Next Steps',
406
+ '',
407
+ 'Conversation transcript:',
408
+ transcript,
409
+ ].filter(Boolean).join('\n');
410
+ await this.wtt.typing(topicId, 'start', {
411
+ statusText: `${adapterDisplayName(adapter.name)} 正在压缩当前上下文`,
412
+ statusKind: 'compact',
413
+ adapter: adapter.name,
414
+ model: context.modelConfig?.model || undefined,
415
+ ttlMs: 120000,
416
+ });
417
+ const compactSessionKey = `${sessionKey}:compact:${Date.now()}`;
418
+ const summary = stripHiddenContextLeak(await this.runAdapter(adapter, prompt, {
419
+ ...context,
420
+ sessionKey: compactSessionKey,
421
+ files: [],
422
+ images: [],
423
+ }) || '').trim();
424
+ if (!summary) return `${adapterDisplayName(adapter.name)} compact failed: summary was empty.`;
425
+ this.store.clearSession(sessionKey, {
426
+ adapter: adapter.name,
427
+ clearedBy: command,
428
+ compactSummary: truncate(summary, 30000),
429
+ compactedAt: new Date().toISOString(),
430
+ compactedMessageCount: messages.length,
431
+ });
432
+ if (adapter.threadBySession?.delete) adapter.threadBySession.delete(sessionKey);
433
+ if (adapter.sessionByKey?.delete) adapter.sessionByKey.delete(sessionKey);
434
+ return [
435
+ `${adapterDisplayName(adapter.name)} context compacted for this WTT topic. The next turn starts a fresh headless CLI session with this summary injected.`,
436
+ '',
437
+ summary,
438
+ ].join('\n');
439
+ }
440
+
359
441
  runtimeStatusText(adapter, context = {}) {
360
442
  const runtime = this.runtimeInfo();
361
443
  const modelConfig = context.modelConfig || {};
@@ -371,6 +453,22 @@ export class Runner {
371
453
  `- wtt-connect: ${runtime.wtt_connect || runtime.wttConnect || '(unknown)'}`,
372
454
  `- claude: ${runtime.claude_code || runtime.claude || '(unknown)'}`,
373
455
  `- codex: ${runtime.codex || '(unknown)'}`,
456
+ `- gemini: ${runtime.gemini || '(unknown)'}`,
457
+ ].join('\n');
458
+ }
459
+
460
+ runtimeModelText(adapter, context = {}, args = '') {
461
+ const runtime = this.runtimeInfo();
462
+ const modelConfig = context.modelConfig || {};
463
+ const model = modelConfig.model || runtime.current_model || runtime.model || this.config.model || '(config/default)';
464
+ const reasoning = modelConfig.reasoning_effort || modelConfig.reasoningEffort || runtime.reasoning_effort || runtime.thinking_mode || '(default)';
465
+ return [
466
+ `${adapterDisplayName(adapter.name)} model`,
467
+ `- current model: ${model}`,
468
+ `- reasoning/thinking: ${reasoning}`,
469
+ args?.trim()
470
+ ? '- note: WTT Web model picker controls the next headless run. Native interactive /model switching requires Agent Terminal.'
471
+ : '- use the WTT Web model picker to switch the next headless run.',
374
472
  ].join('\n');
375
473
  }
376
474
 
@@ -521,9 +619,27 @@ export class Runner {
521
619
  function adapterDisplayName(name) {
522
620
  if (name === 'claude-code') return 'Claude Code';
523
621
  if (name === 'codex') return 'Codex';
622
+ if (name === 'gemini') return 'Gemini';
524
623
  return name || 'Agent';
525
624
  }
526
625
 
626
+ function isKnownAdapterSlashCommand(adapterName, command) {
627
+ const normalized = String(command || '').trim().toLowerCase();
628
+ if (!normalized) return false;
629
+ return slashCommandsForAdapter(adapterName).some(([cmd]) => cmd === normalized);
630
+ }
631
+
632
+ function headlessSlashUnsupportedMessage(adapterName, command) {
633
+ const display = adapterDisplayName(adapterName);
634
+ return [
635
+ `${display} ${command} is a native interactive slash command.`,
636
+ '',
637
+ `WTT currently runs ${display} through its non-interactive/headless executable mode for chat automation. In this mode, the CLI does not reliably dispatch interactive slash commands; it may treat them as normal prompts instead.`,
638
+ '',
639
+ 'Use Agent Terminal for the native TUI command, or use WTT-compatible chat commands such as `/help`, `/status`, `/model`, `/new`, `/clear`, and `/compact`.',
640
+ ].join('\n');
641
+ }
642
+
527
643
  function wttSlashHelp() {
528
644
  return [
529
645
  'WTT slash commands:',
@@ -1256,3 +1372,41 @@ function renderTranscriptBlock(transcripts = []) {
1256
1372
  if (!transcripts.length) return '';
1257
1373
  return ['Audio transcripts:', ...transcripts.map((t) => `- ${t.file.name || t.file.path}: ${t.text}`)].join('\n');
1258
1374
  }
1375
+
1376
+ function renderCompactSummaryBlock(summary) {
1377
+ const value = String(summary || '').trim();
1378
+ if (!value) return '';
1379
+ return [
1380
+ 'Compacted prior WTT conversation context:',
1381
+ '```markdown',
1382
+ value,
1383
+ '```',
1384
+ 'Use this summary as the durable context from previous turns. Do not mention that compaction happened unless relevant.',
1385
+ ].join('\n');
1386
+ }
1387
+
1388
+ function formatTopicMessagesForCompaction(messages = [], options = {}) {
1389
+ const exclude = options.excludeSlashCommands || new Set();
1390
+ const currentAgentId = String(options.currentAgentId || '');
1391
+ const lines = [];
1392
+ for (const message of messages || []) {
1393
+ const content = String(message?.content || '').trim();
1394
+ if (!content) continue;
1395
+ const command = content.startsWith('/') ? content.split(/\s+/, 1)[0].toLowerCase() : '';
1396
+ if (command && exclude.has(command)) continue;
1397
+ const senderId = String(message?.sender_id || '');
1398
+ const senderType = String(message?.sender_type || '').toLowerCase();
1399
+ const role = senderId === currentAgentId || senderType === 'agent' ? 'assistant' : 'user';
1400
+ const name = String(message?.sender_display_name || senderId || role).slice(0, 80);
1401
+ const semantic = String(message?.semantic_type || '').toLowerCase();
1402
+ const text = truncateForTranscript(content, 5000);
1403
+ lines.push(`[${role}${name ? `:${name}` : ''}${semantic ? ` ${semantic}` : ''}] ${text}`);
1404
+ }
1405
+ return truncateForTranscript(lines.join('\n\n'), 90000);
1406
+ }
1407
+
1408
+ function truncateForTranscript(text, maxChars) {
1409
+ const value = String(text || '');
1410
+ if (value.length <= maxChars) return value;
1411
+ return `${value.slice(0, maxChars)}\n...[truncated ${value.length - maxChars} chars]`;
1412
+ }
package/src/slash.js CHANGED
@@ -20,3 +20,237 @@ export function isAgentSlashCommand(message = {}, content = '') {
20
20
  if (slashType === 'agent_passthrough') return true;
21
21
  return String(content || message?.content || '').trim().startsWith('/');
22
22
  }
23
+
24
+ export const CODEX_SLASH_COMMANDS = [
25
+ ['/agent', 'Configure or switch Codex agent settings.'],
26
+ ['/apps', 'Browse available Codex apps/connectors.'],
27
+ ['/plugins', 'Browse and manage Codex plugins.'],
28
+ ['/hooks', 'View and manage lifecycle hooks.'],
29
+ ['/clear', 'Clear the current terminal view and start fresh.'],
30
+ ['/compact', 'Summarize earlier context to free tokens.'],
31
+ ['/copy', 'Copy the latest response.'],
32
+ ['/diff', 'Show the current git diff, including untracked files.'],
33
+ ['/exit', 'Exit Codex. Alias: /quit.'],
34
+ ['/quit', 'Exit Codex. Alias: /exit.'],
35
+ ['/experimental', 'Toggle experimental Codex features.'],
36
+ ['/approve', 'Approve one retry after an auto-review denial.'],
37
+ ['/memories', 'Configure Codex memory use and generation.'],
38
+ ['/skills', 'Browse and use skills.'],
39
+ ['/feedback', 'Send diagnostics or feedback to Codex maintainers.'],
40
+ ['/init', 'Generate or update AGENTS.md guidance.'],
41
+ ['/logout', 'Sign out of Codex.'],
42
+ ['/mcp', 'List configured MCP tools; use verbose for diagnostics.'],
43
+ ['/mention', 'Attach a file or folder to the conversation.'],
44
+ ['/model', 'Choose the active model and reasoning effort.'],
45
+ ['/fast', 'Toggle or inspect Fast service tier when available.'],
46
+ ['/plan', 'Switch to plan mode, optionally with a prompt.'],
47
+ ['/goal', 'Set, view, pause, resume, or clear a task goal.'],
48
+ ['/personality', 'Choose response style.'],
49
+ ['/ps', 'Show background terminals and recent output.'],
50
+ ['/stop', 'Stop background terminals.'],
51
+ ['/clean', 'Alias for /stop.'],
52
+ ['/fork', 'Fork the current conversation into a new thread.'],
53
+ ['/side', 'Start an ephemeral side conversation.'],
54
+ ['/raw', 'Toggle raw scrollback mode.'],
55
+ ['/resume', 'Resume a saved conversation.'],
56
+ ['/new', 'Start a new conversation in the same workspace.'],
57
+ ['/review', 'Review the working tree.'],
58
+ ['/status', 'Show session configuration, token usage, and runtime status.'],
59
+ ['/debug-config', 'Print configuration diagnostics.'],
60
+ ['/statusline', 'Configure TUI status-line fields.'],
61
+ ['/title', 'Configure terminal title fields.'],
62
+ ['/theme', 'Choose syntax-highlighting theme.'],
63
+ ['/permissions', 'Adjust approval and sandbox permissions.'],
64
+ ['/ide', 'Configure IDE integration.'],
65
+ ['/keymap', 'Configure keyboard shortcuts.'],
66
+ ['/vim', 'Toggle Vim editing mode.'],
67
+ ['/sandbox-add-read-dir', 'Add an extra read-only sandbox directory.'],
68
+ ];
69
+
70
+ export const CLAUDE_CODE_SLASH_COMMANDS = [
71
+ ['/add-dir', 'Add a working directory for file access.'],
72
+ ['/agents', 'Manage agent/subagent configurations.'],
73
+ ['/autofix-pr', 'Spawn Claude Code on the web to fix PR/CI feedback.'],
74
+ ['/background', 'Detach the current session to run as a background agent. Alias: /bg.'],
75
+ ['/bg', 'Alias for /background.'],
76
+ ['/batch', 'Decompose large work across parallel worktrees/subagents.'],
77
+ ['/branch', 'Branch the current conversation. Alias: /fork.'],
78
+ ['/fork', 'Alias for /branch in Claude Code.'],
79
+ ['/btw', 'Ask a side question without bloating history.'],
80
+ ['/chrome', 'Configure Claude in Chrome settings.'],
81
+ ['/claude-api', 'Load Claude API reference or migration workflows.'],
82
+ ['/clear', 'Start a new conversation with empty context. Aliases: /reset, /new.'],
83
+ ['/reset', 'Alias for /clear.'],
84
+ ['/new', 'Alias for /clear.'],
85
+ ['/code-review', 'Review current diff and optionally apply fixes.'],
86
+ ['/color', 'Set prompt bar color.'],
87
+ ['/compact', 'Summarize conversation to free context.'],
88
+ ['/config', 'Open settings. Alias: /settings.'],
89
+ ['/settings', 'Alias for /config.'],
90
+ ['/context', 'Visualize context usage.'],
91
+ ['/copy', 'Copy a recent assistant response.'],
92
+ ['/cost', 'Alias for /usage.'],
93
+ ['/debug', 'Enable debug logging and troubleshoot.'],
94
+ ['/deep-research', 'Run a deep-research workflow.'],
95
+ ['/desktop', 'Continue in Claude Code Desktop. Alias: /app.'],
96
+ ['/app', 'Alias for /desktop.'],
97
+ ['/diff', 'Open an interactive diff viewer.'],
98
+ ['/doctor', 'Diagnose installation and settings.'],
99
+ ['/effort', 'Set reasoning effort level.'],
100
+ ['/exit', 'Exit or detach from a background session. Alias: /quit.'],
101
+ ['/quit', 'Alias for /exit.'],
102
+ ['/export', 'Export the current conversation.'],
103
+ ['/fast', 'Toggle fast mode.'],
104
+ ['/feedback', 'Submit feedback or a bug report. Aliases: /bug, /share.'],
105
+ ['/bug', 'Alias for /feedback.'],
106
+ ['/share', 'Alias for /feedback.'],
107
+ ['/fewer-permission-prompts', 'Generate an allowlist to reduce prompts.'],
108
+ ['/focus', 'Toggle focus view.'],
109
+ ['/goal', 'Set, view, or clear a persistent goal.'],
110
+ ['/heapdump', 'Write a heap snapshot for diagnostics.'],
111
+ ['/help', 'Show Claude Code help and commands.'],
112
+ ['/hooks', 'View hook configuration.'],
113
+ ['/ide', 'Manage IDE integrations.'],
114
+ ['/init', 'Initialize CLAUDE.md project guidance.'],
115
+ ['/insights', 'Analyze Claude Code sessions.'],
116
+ ['/install-github-app', 'Install Claude GitHub Actions app.'],
117
+ ['/install-slack-app', 'Install Claude Slack app.'],
118
+ ['/keybindings', 'Open keybindings configuration.'],
119
+ ['/login', 'Sign in to Anthropic.'],
120
+ ['/logout', 'Sign out from Anthropic.'],
121
+ ['/loop', 'Run a prompt repeatedly. Alias: /proactive.'],
122
+ ['/proactive', 'Alias for /loop.'],
123
+ ['/mcp', 'Manage MCP server connections and OAuth.'],
124
+ ['/memory', 'Edit memory files and auto-memory entries.'],
125
+ ['/mobile', 'Show mobile app QR code. Aliases: /ios, /android.'],
126
+ ['/ios', 'Alias for /mobile.'],
127
+ ['/android', 'Alias for /mobile.'],
128
+ ['/model', 'Switch model and save defaults.'],
129
+ ['/passes', 'Share Claude Code passes when eligible.'],
130
+ ['/permissions', 'Manage tool permissions. Alias: /allowed-tools.'],
131
+ ['/allowed-tools', 'Alias for /permissions.'],
132
+ ['/plan', 'Enter plan mode, optionally with a task.'],
133
+ ['/plugin', 'Manage Claude Code plugins.'],
134
+ ['/powerup', 'Discover Claude Code features.'],
135
+ ['/pr-comments', 'Legacy PR comments helper.'],
136
+ ['/privacy-settings', 'View/update privacy settings.'],
137
+ ['/radio', 'Open Claude FM radio.'],
138
+ ['/recap', 'Generate a one-line session recap.'],
139
+ ['/release-notes', 'View changelog.'],
140
+ ['/reload-plugins', 'Reload active plugins.'],
141
+ ['/reload-skills', 'Reload skills and command directories.'],
142
+ ['/remote-control', 'Enable remote control from claude.ai. Alias: /rc.'],
143
+ ['/rc', 'Alias for /remote-control.'],
144
+ ['/remote-env', 'Configure default remote environment.'],
145
+ ['/rename', 'Rename current session.'],
146
+ ['/resume', 'Resume a previous conversation. Alias: /continue.'],
147
+ ['/continue', 'Alias for /resume.'],
148
+ ['/review', 'Review a PR locally.'],
149
+ ['/rewind', 'Rewind conversation/code to a checkpoint. Aliases: /checkpoint, /undo.'],
150
+ ['/checkpoint', 'Alias for /rewind.'],
151
+ ['/undo', 'Alias for /rewind.'],
152
+ ['/run', 'Launch and verify the project app.'],
153
+ ['/run-skill-generator', 'Generate project run/verify skills.'],
154
+ ['/sandbox', 'Toggle sandbox mode where supported.'],
155
+ ['/schedule', 'Create or manage routines. Alias: /routines.'],
156
+ ['/routines', 'Alias for /schedule.'],
157
+ ['/scroll-speed', 'Adjust mouse wheel scroll speed.'],
158
+ ['/security-review', 'Analyze pending changes for security risks.'],
159
+ ['/setup-bedrock', 'Configure Amazon Bedrock.'],
160
+ ['/setup-vertex', 'Configure Google Vertex AI.'],
161
+ ['/simplify', 'Review changes for cleanup opportunities.'],
162
+ ['/skills', 'List and manage skills.'],
163
+ ['/stats', 'Alias for /usage.'],
164
+ ['/status', 'Open status/settings view.'],
165
+ ['/statusline', 'Configure status line.'],
166
+ ['/stickers', 'Order stickers.'],
167
+ ['/stop', 'Stop an attached background session.'],
168
+ ['/tasks', 'List and manage background tasks. Alias: /bashes.'],
169
+ ['/bashes', 'Alias for /tasks.'],
170
+ ['/team-onboarding', 'Generate team onboarding guide.'],
171
+ ['/teleport', 'Pull a web session into terminal. Alias: /tp.'],
172
+ ['/tp', 'Alias for /teleport.'],
173
+ ['/terminal-setup', 'Configure terminal keybindings.'],
174
+ ['/theme', 'Change color theme.'],
175
+ ['/tui', 'Set terminal UI renderer.'],
176
+ ['/ultraplan', 'Draft a plan in an ultraplan session.'],
177
+ ['/ultrareview', 'Run deep multi-agent code review.'],
178
+ ['/upgrade', 'Open plan upgrade page.'],
179
+ ['/usage', 'Show cost, usage limits, and activity stats.'],
180
+ ['/usage-credits', 'Configure usage credits.'],
181
+ ['/verify', 'Verify a code change by running the app.'],
182
+ ['/vim', 'Legacy Vim-mode command.'],
183
+ ['/voice', 'Toggle voice dictation.'],
184
+ ['/web-setup', 'Connect GitHub for Claude Code on the web.'],
185
+ ['/workflows', 'Watch and manage workflows.'],
186
+ ];
187
+
188
+ export const GEMINI_SLASH_COMMANDS = [
189
+ ['/about', 'Show version information.'],
190
+ ['/agents', 'Manage local and remote subagents.'],
191
+ ['/auth', 'Change authentication method.'],
192
+ ['/bug', 'File an issue or bug report.'],
193
+ ['/chat', 'Alias for /resume and checkpoint management.'],
194
+ ['/clear', 'Clear visible terminal/session history.'],
195
+ ['/commands', 'List or reload custom slash commands.'],
196
+ ['/compress', 'Compress chat context into a summary.'],
197
+ ['/copy', 'Copy the last output.'],
198
+ ['/directory', 'Manage workspace directories. Alias: /dir.'],
199
+ ['/dir', 'Alias for /directory.'],
200
+ ['/docs', 'Open Gemini CLI documentation.'],
201
+ ['/editor', 'Select editor integration.'],
202
+ ['/extensions', 'Manage Gemini CLI extensions.'],
203
+ ['/help', 'Show Gemini CLI help. Alias: /?.'],
204
+ ['/?', 'Alias for /help.'],
205
+ ['/hooks', 'Manage lifecycle hooks.'],
206
+ ['/ide', 'Manage IDE integration.'],
207
+ ['/init', 'Generate or update GEMINI.md.'],
208
+ ['/mcp', 'Manage MCP servers.'],
209
+ ['/memory', 'Inspect or refresh GEMINI.md memory.'],
210
+ ['/model', 'Manage model configuration.'],
211
+ ['/permissions', 'Manage folder trust and permissions.'],
212
+ ['/plan', 'Switch to plan mode.'],
213
+ ['/policies', 'List active policies.'],
214
+ ['/privacy', 'Display privacy notice and consent options.'],
215
+ ['/quit', 'Exit Gemini CLI. Alias: /exit.'],
216
+ ['/exit', 'Alias for /quit.'],
217
+ ['/restore', 'Restore files to a checkpoint.'],
218
+ ['/rewind', 'Navigate backward through conversation history.'],
219
+ ['/resume', 'Browse, resume, save, delete, or share sessions.'],
220
+ ['/settings', 'Open settings editor.'],
221
+ ['/shells', 'Toggle background shells view. Alias: /bashes.'],
222
+ ['/bashes', 'Alias for /shells.'],
223
+ ['/setup-github', 'Set up GitHub Actions integration.'],
224
+ ['/skills', 'Manage Agent Skills.'],
225
+ ['/stats', 'Show Gemini CLI session statistics.'],
226
+ ['/terminal-setup', 'Configure multiline keybindings.'],
227
+ ['/theme', 'Change visual theme.'],
228
+ ['/tools', 'Show available tools.'],
229
+ ['/upgrade', 'Open Gemini Code Assist upgrade page.'],
230
+ ['/vim', 'Toggle Vim mode.'],
231
+ ];
232
+
233
+ export function slashCommandsForAdapter(adapterName) {
234
+ const name = String(adapterName || '').trim().toLowerCase();
235
+ if (name === 'codex' || name.includes('codex')) return CODEX_SLASH_COMMANDS;
236
+ if (name === 'claude-code' || name === 'claude' || name.includes('claude')) return CLAUDE_CODE_SLASH_COMMANDS;
237
+ if (name === 'gemini' || name.includes('gemini')) return GEMINI_SLASH_COMMANDS;
238
+ return [
239
+ ['/help', 'Show help.'],
240
+ ['/status', 'Show WTT runtime status.'],
241
+ ['/model', 'Show or switch model when supported.'],
242
+ ['/clear', 'Clear the WTT topic session.'],
243
+ ['/new', 'Start a fresh WTT topic session.'],
244
+ ['/compact', 'Compact context when supported.'],
245
+ ];
246
+ }
247
+
248
+ export function formatSlashCommandHelp(adapterName) {
249
+ const title = adapterName ? `${adapterName} slash commands` : 'Agent slash commands';
250
+ return [
251
+ `${title}:`,
252
+ ...slashCommandsForAdapter(adapterName).map(([cmd, desc]) => `- \`${cmd}\`: ${desc}`),
253
+ '',
254
+ 'Note: WTT handles deterministic commands such as /status, /new, /clear, /compact, /diff, /review, and /init where possible. Other commands are passed through to the underlying CLI; interactive TUI-only commands may require opening the agent terminal.',
255
+ ].join('\n');
256
+ }
package/src/wtt-api.js CHANGED
@@ -76,6 +76,22 @@ export class WTTApi {
76
76
  return this.request('POST', '/tasks', { json: payload });
77
77
  }
78
78
 
79
+ async getTopicMessages(topicId, { limit = 120, includeHistory = true } = {}) {
80
+ if (!topicId) return [];
81
+ const params = new URLSearchParams({
82
+ limit: String(limit),
83
+ agent_id: this.config.agentId || '',
84
+ include_history: includeHistory ? 'true' : 'false',
85
+ });
86
+ try {
87
+ const rows = await this.request('GET', `/topics/${encodeURIComponent(topicId)}/messages?${params.toString()}`);
88
+ return Array.isArray(rows) ? rows : [];
89
+ } catch (err) {
90
+ log('warn', 'get_topic_messages failed', { topicId, error: err.message });
91
+ return [];
92
+ }
93
+ }
94
+
79
95
  async runTask(taskId, payload = {}) {
80
96
  return this.request('POST', `/tasks/${taskId}/run`, { json: payload });
81
97
  }