praxis-agent 0.15.0 → 0.16.1

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/README.md CHANGED
@@ -15,10 +15,15 @@ Anthropic/OpenAI-compatible model access. Praxis deliberately excludes
15
15
  accounts, organizations, billing, managed enterprise policy, remote control,
16
16
  IDE surfaces, and telemetry control planes.
17
17
 
18
- Claude Code 2.1.208 is the compatibility, architecture, and design baseline.
19
- Every single-user CLI capability remains required unless it falls inside an
20
- explicit exclusion above. A similar Praxis surface is not a substitute for the
21
- corresponding Claude command or runtime contract.
18
+ Claude Code 2.1.208 remains the architecture, design, and observable
19
+ compatibility baseline for included and required single-user developer
20
+ capabilities. Only entries classified `required` block developer-core
21
+ closure; `deferred` entries are optional and demand-driven. Exclusions cover
22
+ the existing enterprise, authentication, hosted, and client surfaces plus the
23
+ explicitly classified subscription-bound integration, campaign, hidden
24
+ maintainer diagnostic, and build-experimental commands. A similar Praxis
25
+ surface is not a substitute for the corresponding Claude command or runtime
26
+ contract.
22
27
 
23
28
  ## Requirements
24
29
 
@@ -95,7 +100,8 @@ troubleshooting. Run `praxis --help` for the authoritative command surface.
95
100
  tabs, `/sandbox` mode/dependency/override/config controls, local cached
96
101
  `/release-notes`, Claude-compatible `/statusline` command execution and setup
97
102
  agent, source-aligned `/init` project-instruction onboarding with its enhanced
98
- skills/hooks flow, `/mcp`, `/memory` shared instruction and auto-memory
103
+ skills/hooks flow, provider-free per-session `/color` prompt-bar styling,
104
+ `/mcp`, `/memory` shared instruction and auto-memory
99
105
  access, and live extension-reload controls,
100
106
  cursor/history composer, per-session model/effort/permission controls,
101
107
  context/status/skill/task dashboards, prompt stash and continuation shortcuts,
@@ -1,3 +1,4 @@
1
+ import { type AgentColorName, type AgentColorSelection } from '../compatibility/claude/agent-color.js';
1
2
  import type { ClaudeConditionalRuleResolver } from '../compatibility/claude/context.js';
2
3
  import { type ClaudeFileResource, type ClaudeFileResourceConfig } from '../compatibility/claude/file-resources.js';
3
4
  import { type ClaudeDisplayTranscriptItem } from '../compatibility/claude/projection.js';
@@ -182,6 +183,7 @@ export declare class ClaudeSessionService {
182
183
  sessionNameSuggestion(sessionId: string, signal?: AbortSignal): Promise<string | null>;
183
184
  sessions(): Promise<SessionSummary[]>;
184
185
  inspect(sessionId: string): Promise<SessionInspection>;
186
+ readEffectiveAgentColor(sessionId: string): Promise<AgentColorName | undefined>;
185
187
  export(sessionId: string): Promise<Buffer>;
186
188
  transcript(sessionId: string, resumeSessionAt?: string): Promise<ClaudeDisplayTranscriptItem[]>;
187
189
  rename(sessionId: string, name: string): Promise<void>;
@@ -190,6 +192,9 @@ export declare class ClaudeSessionService {
190
192
  approveRecentlyDenied(sessionId: string, display: string): Promise<void>;
191
193
  retryRecentlyDenied(sessionId: string, display: string, signal?: AbortSignal): Promise<SessionRunResult>;
192
194
  recordBtwUsage(sessionId: string | undefined, permissionMode?: ClaudePermissionMode): Promise<string>;
195
+ recordColorUsage(sessionId: string | undefined, selection: AgentColorSelection, display: string, permissionMode?: ClaudePermissionMode, options?: {
196
+ createSession?: boolean;
197
+ }): Promise<string>;
193
198
  recordBackgroundUsage(sessionId: string | undefined, permissionMode?: ClaudePermissionMode): Promise<string>;
194
199
  recordBackgroundLaunch(sessionId: string): Promise<SessionForkCheckpoint>;
195
200
  compact(sessionId: string, signal?: AbortSignal, selection?: ManualCompactSelection): Promise<ManualCompactResult>;
@@ -214,6 +219,7 @@ export declare class ClaudeSessionService {
214
219
  private restoreWorktree;
215
220
  private paths;
216
221
  private appendCdCommand;
222
+ private appendAgentColorUsage;
217
223
  private appendSystemLocalCommand;
218
224
  private ensureLocalSession;
219
225
  private appendInputHistory;
@@ -2,6 +2,7 @@ import { randomUUID } from 'node:crypto';
2
2
  import { appendFile, copyFile, link, lstat, mkdir, readdir, realpath, stat, unlink, } from 'node:fs/promises';
3
3
  import { homedir } from 'node:os';
4
4
  import { basename, extname, isAbsolute, join, relative } from 'node:path';
5
+ import { AGENT_COLOR_DEFAULT, agentColorMessage, getClaudeEffectiveAgentColor, } from '../compatibility/claude/agent-color.js';
5
6
  import { createClaudeCompactEntries, formatClaudeCompactSummary, getCumulativeDroppedTokens, } from '../compatibility/claude/compaction.js';
6
7
  import { isClaudeSessionId, resolveClaudePaths, resolveClaudeScheduledTaskFile, } from '../compatibility/claude/paths.js';
7
8
  import { downloadClaudeFileResources, } from '../compatibility/claude/file-resources.js';
@@ -696,6 +697,11 @@ export class ClaudeSessionService {
696
697
  : {}),
697
698
  };
698
699
  }
700
+ async readEffectiveAgentColor(sessionId) {
701
+ this.assertSessionPersistence();
702
+ const recovery = await this.store(sessionId).loadReadOnly();
703
+ return getClaudeEffectiveAgentColor(recovery.entries, sessionId);
704
+ }
699
705
  async export(sessionId) {
700
706
  this.assertSessionPersistence();
701
707
  try {
@@ -934,6 +940,21 @@ export class ClaudeSessionService {
934
940
  await new Promise((resolve) => setTimeout(resolve, 25));
935
941
  }
936
942
  }
943
+ async recordColorUsage(sessionId, selection, display, permissionMode = 'default', options = {}) {
944
+ const output = agentColorMessage(selection);
945
+ const agentColor = selection.kind === 'color'
946
+ ? selection.color
947
+ : selection.kind === 'reset'
948
+ ? AGENT_COLOR_DEFAULT
949
+ : undefined;
950
+ const createLocalSession = sessionId === undefined || options.createSession === true;
951
+ const activeSessionId = await this.ensureLocalSession(sessionId, permissionMode, createLocalSession ? agentColor : undefined, options.createSession === true);
952
+ if (this.options.sessionPersistence !== false) {
953
+ await this.appendInputHistory(display, activeSessionId);
954
+ }
955
+ await this.appendAgentColorUsage(activeSessionId, display, output, createLocalSession ? undefined : agentColor);
956
+ return activeSessionId;
957
+ }
937
958
  async recordBackgroundUsage(sessionId, permissionMode = 'default') {
938
959
  this.assertWritable();
939
960
  const activeSessionId = await this.ensureLocalSession(sessionId, permissionMode);
@@ -2566,6 +2587,35 @@ export class ClaudeSessionService {
2566
2587
  throw new Error(`Claude local command conflict: ${result.reason}`);
2567
2588
  }
2568
2589
  }
2590
+ async appendAgentColorUsage(sessionId, display, output, agentColor) {
2591
+ const args = display.replace(/^\/color\s*/u, '').trim();
2592
+ while (true) {
2593
+ const result = await this.turnStore(sessionId).withLease(async (lease) => {
2594
+ const snapshot = await lease.load();
2595
+ if (snapshot.entries.length === 0) {
2596
+ throw new Error(`Claude session not found: ${sessionId}`);
2597
+ }
2598
+ const appended = await lease.appendMany(snapshot.tail, [
2599
+ ...(agentColor === undefined
2600
+ ? []
2601
+ : [
2602
+ {
2603
+ type: 'agent-color',
2604
+ agentColor,
2605
+ sessionId,
2606
+ },
2607
+ ]),
2608
+ ...this.localCommandEntries(sessionId, this.activeCwd(), this.logicalTailUuid(snapshot.tail), 'color', args, output),
2609
+ ]);
2610
+ if (appended.status === 'conflict') {
2611
+ throw new Error(`Claude color local command append conflict: ${appended.reason}`);
2612
+ }
2613
+ });
2614
+ if (result.status === 'completed')
2615
+ return;
2616
+ await new Promise((resolve) => setTimeout(resolve, 25));
2617
+ }
2618
+ }
2569
2619
  async appendSystemLocalCommand(sessionId, command, args, output) {
2570
2620
  while (true) {
2571
2621
  const result = await this.turnStore(sessionId).withLease(async (lease) => {
@@ -2583,13 +2633,22 @@ export class ClaudeSessionService {
2583
2633
  await new Promise((resolve) => setTimeout(resolve, 25));
2584
2634
  }
2585
2635
  }
2586
- async ensureLocalSession(sessionId, permissionMode) {
2587
- if (sessionId)
2636
+ async ensureLocalSession(sessionId, permissionMode, agentColor, createExplicit = false) {
2637
+ if (sessionId !== undefined && !createExplicit)
2588
2638
  return sessionId;
2589
2639
  this.assertWritable();
2590
- const createdSessionId = randomUUID();
2640
+ const createdSessionId = sessionId ?? randomUUID();
2591
2641
  const store = this.turnStore(createdSessionId);
2592
2642
  const created = await store.create([
2643
+ ...(agentColor === undefined
2644
+ ? []
2645
+ : [
2646
+ {
2647
+ type: 'agent-color',
2648
+ agentColor,
2649
+ sessionId: createdSessionId,
2650
+ },
2651
+ ]),
2593
2652
  { type: 'mode', mode: 'normal', sessionId: createdSessionId },
2594
2653
  {
2595
2654
  type: 'permission-mode',
@@ -8,6 +8,8 @@ import { type TuiMemoryFiles } from './tui/memory-files.js';
8
8
  import { type TuiDiffSnapshot } from './tui/git-diff.js';
9
9
  import { type TuiPermissionBehavior, type TuiPermissionRule } from './tui/permission-settings.js';
10
10
  import { type RecentlyDeniedStore } from './tui/recently-denied.js';
11
+ import { type AgentColorSelection } from '../compatibility/claude/agent-color.js';
12
+ import type { AgentColorName } from '../compatibility/claude/agent-color.js';
11
13
  import type { ClaudeResourceScope } from '../compatibility/claude/shared-resources.js';
12
14
  import type { TuiHookConfiguration } from './tui/hook-settings.js';
13
15
  import { type TuiSlashCommand } from './tui/slash-commands.js';
@@ -41,6 +43,8 @@ interface InteractiveSessionCommands {
41
43
  retryRecentlyDenied?(sessionId: string, display: string, signal?: AbortSignal): Promise<SessionRunResult>;
42
44
  answerSideQuestion?(sessionId: string | undefined, question: string, signal?: AbortSignal, onDelta?: (delta: string) => void, permissionMode?: ClaudePermissionMode): Promise<SideQuestionResult>;
43
45
  recordBtwUsage?(sessionId: string | undefined, permissionMode?: ClaudePermissionMode): Promise<string>;
46
+ recordColorUsage?(sessionId: string | undefined, selection: AgentColorSelection, display: string, permissionMode?: ClaudePermissionMode): Promise<string>;
47
+ agentColor?(sessionId: string): Promise<AgentColorName | undefined>;
44
48
  recordBackgroundUsage?(sessionId: string | undefined, permissionMode?: ClaudePermissionMode): Promise<string>;
45
49
  recordBackgroundLaunch?(sessionId: string): Promise<SessionForkCheckpoint>;
46
50
  forkSideQuestion?(sessionId: string, question: string, signal?: AbortSignal): Promise<SideQuestionForkResult>;
@@ -111,6 +115,7 @@ interface InteractiveAppProps {
111
115
  initialSessions: readonly SessionSummary[];
112
116
  initialPrompt?: string;
113
117
  initialHistory?: readonly TranscriptItem[];
118
+ initialSessionColor?: AgentColorName;
114
119
  signal?: AbortSignal;
115
120
  onCancel?: () => void;
116
121
  onTurnChange?: (turn: Promise<void> | null) => void;
@@ -179,7 +184,7 @@ interface InteractiveAppProps {
179
184
  releaseNotesLoader?: (configRoot: string) => Promise<string>;
180
185
  settingSources?: readonly ClaudeResourceScope[];
181
186
  }
182
- export declare function InteractiveApp({ factory, initialSessions, initialPrompt, initialHistory, signal, onCancel, onTurnChange, onCleanup, onRendererChange, onBackground, axScreenReader, allowNewSession, resume, display, terminalWidth, slashCommands, agents, allowDangerouslySkipPermissions, additionalDirectories, diffLoader, fileLoader, externalEditor, keybindingsConfigRoot, keybindingsFile, keybindingsLoader, keybindingsEditor, memoryFilesLoader, memoryEditor, memoryFolderOpener, suspendProcess, clipboardReader, clipboardWriter, sideQuestionClipboardWriter, exportWriter, permissionRuleStore, sandboxStore: suppliedSandboxStore, recentlyDeniedStore: suppliedRecentlyDeniedStore, terminalSetup: terminalSetupOverride, themeStore, initialThemeSettings, initialThemeLoadError, workspaceDirectoryResolver, workspaceDirectoryCompleter, runtimeSettings: suppliedRuntimeSettings, runtimeSettingsTarget, notificationWriter, elicitationUrlOpener, releaseNotesLoader, settingSources, }: InteractiveAppProps): import("react").JSX.Element;
187
+ export declare function InteractiveApp({ factory, initialSessions, initialPrompt, initialHistory, initialSessionColor, signal, onCancel, onTurnChange, onCleanup, onRendererChange, onBackground, axScreenReader, allowNewSession, resume, display, terminalWidth, slashCommands, agents, allowDangerouslySkipPermissions, additionalDirectories, diffLoader, fileLoader, externalEditor, keybindingsConfigRoot, keybindingsFile, keybindingsLoader, keybindingsEditor, memoryFilesLoader, memoryEditor, memoryFolderOpener, suspendProcess, clipboardReader, clipboardWriter, sideQuestionClipboardWriter, exportWriter, permissionRuleStore, sandboxStore: suppliedSandboxStore, recentlyDeniedStore: suppliedRecentlyDeniedStore, terminalSetup: terminalSetupOverride, themeStore, initialThemeSettings, initialThemeLoadError, workspaceDirectoryResolver, workspaceDirectoryCompleter, runtimeSettings: suppliedRuntimeSettings, runtimeSettingsTarget, notificationWriter, elicitationUrlOpener, releaseNotesLoader, settingSources, }: InteractiveAppProps): import("react").JSX.Element;
183
188
  export declare function runInteractive(options: {
184
189
  factory: InteractiveServiceFactory;
185
190
  initialPrompt?: string;
@@ -17,6 +17,7 @@ import { createClaudeStatusLineInput, StatusLine } from './tui/status-line.js';
17
17
  import { loadGitDiff, visiblePatchLines, } from './tui/git-diff.js';
18
18
  import { addTuiPermissionRule, loadTuiPermissionRules, removeTuiPermissionRule, } from './tui/permission-settings.js';
19
19
  import { createRecentlyDeniedStore, } from './tui/recently-denied.js';
20
+ import { agentColorMessage, parseAgentColorInput, } from '../compatibility/claude/agent-color.js';
20
21
  import { filterTuiSlashCommands, mergeTuiSlashCommands, slashCommandQuery, } from './tui/slash-commands.js';
21
22
  import { createComposerEditor, deleteComposerBackward, deleteComposerForward, deleteComposerToEnd, deleteComposerToStart, deleteComposerWordBackward, insertComposerText, moveComposerCursor, moveComposerCursorByWord, } from './tui/composer-editor.js';
22
23
  import { applyMentionReference, fileReferenceAtCursor, filterTuiMentionEntries, loadTuiFileEntries, } from './tui/file-picker.js';
@@ -247,7 +248,7 @@ const HIDDEN_TUI_SLASH_COMMANDS = new Set([
247
248
  'update',
248
249
  'usage',
249
250
  ]);
250
- export function InteractiveApp({ factory, initialSessions, initialPrompt, initialHistory = [], signal, onCancel, onTurnChange, onCleanup, onRendererChange, onBackground, axScreenReader = false, allowNewSession = true, resume, display = { version: 'dev', cwd: process.cwd() }, terminalWidth, slashCommands = EMPTY_SLASH_COMMANDS, agents = EMPTY_AGENTS, allowDangerouslySkipPermissions = false, additionalDirectories = [], diffLoader, fileLoader, externalEditor = editTuiPrompt, keybindingsConfigRoot, keybindingsFile = ensureTuiKeybindingsFile, keybindingsLoader = loadTuiKeybindings, keybindingsEditor = openTuiEditorFile, memoryFilesLoader = (configRoot, cwd) => loadTuiMemoryFiles({ configRoot, cwd }), memoryEditor = openTuiEditorFile, memoryFolderOpener = openTuiMemoryFolder, suspendProcess = suspendTuiProcess, clipboardReader = readTuiClipboard, clipboardWriter = writeTuiClipboard, sideQuestionClipboardWriter = writeTuiOsc52Clipboard, exportWriter = writeConversationExport, permissionRuleStore, sandboxStore: suppliedSandboxStore, recentlyDeniedStore: suppliedRecentlyDeniedStore, terminalSetup: terminalSetupOverride, themeStore, initialThemeSettings, initialThemeLoadError, workspaceDirectoryResolver = resolveTuiWorkspaceDirectory, workspaceDirectoryCompleter = completeTuiWorkspaceDirectory, runtimeSettings: suppliedRuntimeSettings, runtimeSettingsTarget, notificationWriter, elicitationUrlOpener = openTuiUrl, releaseNotesLoader = (configRoot) => loadClaudeReleaseNotes({ configRoot }), settingSources, }) {
251
+ export function InteractiveApp({ factory, initialSessions, initialPrompt, initialHistory = [], initialSessionColor, signal, onCancel, onTurnChange, onCleanup, onRendererChange, onBackground, axScreenReader = false, allowNewSession = true, resume, display = { version: 'dev', cwd: process.cwd() }, terminalWidth, slashCommands = EMPTY_SLASH_COMMANDS, agents = EMPTY_AGENTS, allowDangerouslySkipPermissions = false, additionalDirectories = [], diffLoader, fileLoader, externalEditor = editTuiPrompt, keybindingsConfigRoot, keybindingsFile = ensureTuiKeybindingsFile, keybindingsLoader = loadTuiKeybindings, keybindingsEditor = openTuiEditorFile, memoryFilesLoader = (configRoot, cwd) => loadTuiMemoryFiles({ configRoot, cwd }), memoryEditor = openTuiEditorFile, memoryFolderOpener = openTuiMemoryFolder, suspendProcess = suspendTuiProcess, clipboardReader = readTuiClipboard, clipboardWriter = writeTuiClipboard, sideQuestionClipboardWriter = writeTuiOsc52Clipboard, exportWriter = writeConversationExport, permissionRuleStore, sandboxStore: suppliedSandboxStore, recentlyDeniedStore: suppliedRecentlyDeniedStore, terminalSetup: terminalSetupOverride, themeStore, initialThemeSettings, initialThemeLoadError, workspaceDirectoryResolver = resolveTuiWorkspaceDirectory, workspaceDirectoryCompleter = completeTuiWorkspaceDirectory, runtimeSettings: suppliedRuntimeSettings, runtimeSettingsTarget, notificationWriter, elicitationUrlOpener = openTuiUrl, releaseNotesLoader = (configRoot) => loadClaudeReleaseNotes({ configRoot }), settingSources, }) {
251
252
  const { exit, suspendTerminal, waitUntilRenderFlush } = useApp();
252
253
  const width = useTerminalWidth(terminalWidth);
253
254
  const keybindingsRoot = useMemo(() => resolve(keybindingsConfigRoot ??
@@ -304,6 +305,7 @@ export function InteractiveApp({ factory, initialSessions, initialPrompt, initia
304
305
  const statusLineSessionId = useRef(resume?.sessionId ?? randomUUID());
305
306
  const sessionIdRef = useRef(resume?.sessionId ?? null);
306
307
  sessionIdRef.current = sessionId;
308
+ const [sessionColor, setSessionColor] = useState(initialSessionColor);
307
309
  const [sessionName, setSessionName] = useState(null);
308
310
  const [activeSessionSummary, setActiveSessionSummary] = useState(() => resume?.sessionId
309
311
  ? initialSessions.find((session) => session.sessionId === resume.sessionId)
@@ -443,6 +445,17 @@ export function InteractiveApp({ factory, initialSessions, initialPrompt, initia
443
445
  const matchingSlashCommands = useMemo(() => commandQuery === null
444
446
  ? []
445
447
  : filterTuiSlashCommands(allSlashCommands, commandQuery), [allSlashCommands, commandQuery]);
448
+ const commandArgumentHint = useMemo(() => {
449
+ if (shellMode || !input.startsWith('/'))
450
+ return undefined;
451
+ if (inputCursor !== input.length)
452
+ return undefined;
453
+ const match = /^\/(\S+) $/u.exec(input);
454
+ if (!match?.[1])
455
+ return undefined;
456
+ const command = allSlashCommands.find((candidate) => candidate.name === match[1]);
457
+ return command?.argumentHint;
458
+ }, [allSlashCommands, input, inputCursor, shellMode]);
446
459
  const commandPaletteVisible = !busy &&
447
460
  !permission &&
448
461
  !planApproval &&
@@ -1443,14 +1456,19 @@ export function InteractiveApp({ factory, initialSessions, initialPrompt, initia
1443
1456
  sessionLoadRef.current = loadId;
1444
1457
  if (nextSessionId === null) {
1445
1458
  setHistory([]);
1459
+ setSessionColor(undefined);
1446
1460
  return;
1447
1461
  }
1448
1462
  const loading = (async () => {
1449
1463
  try {
1450
1464
  const commands = await service();
1451
1465
  const transcript = await commands.transcript?.(nextSessionId);
1466
+ const agentColor = commands.agentColor === undefined
1467
+ ? undefined
1468
+ : await commands.agentColor(nextSessionId);
1452
1469
  if (sessionLoadRef.current === loadId) {
1453
1470
  setHistory(transcript ? [...transcript] : []);
1471
+ setSessionColor(agentColor);
1454
1472
  }
1455
1473
  }
1456
1474
  catch (error) {
@@ -1816,6 +1834,32 @@ export function InteractiveApp({ factory, initialSessions, initialPrompt, initia
1816
1834
  onTurnChange?.(showing);
1817
1835
  void showing.finally(() => onTurnChange?.(null));
1818
1836
  };
1837
+ const changeSessionColor = (display, selection) => {
1838
+ appendPromptHistory(display);
1839
+ if (selection.kind === 'color')
1840
+ setSessionColor(selection.color);
1841
+ else if (selection.kind === 'reset')
1842
+ setSessionColor(undefined);
1843
+ const changing = (async () => {
1844
+ try {
1845
+ const activeSessionId = await withLocalCommands(async (commands) => {
1846
+ if (!commands.recordColorUsage) {
1847
+ throw new Error('Session color is unavailable.');
1848
+ }
1849
+ return commands.recordColorUsage(sessionId ?? undefined, selection, display, runtimePreferencesRef.current.permissionMode);
1850
+ });
1851
+ if (activeSessionId)
1852
+ setSessionId(activeSessionId);
1853
+ append({ kind: 'user', text: display });
1854
+ append({ kind: 'local-result', text: agentColorMessage(selection) });
1855
+ }
1856
+ catch (error) {
1857
+ warn(error);
1858
+ }
1859
+ })();
1860
+ onTurnChange?.(changing);
1861
+ void changing.finally(() => onTurnChange?.(null));
1862
+ };
1819
1863
  const backgroundSession = () => {
1820
1864
  appendPromptHistory('/background');
1821
1865
  const backgrounding = (async () => {
@@ -4794,6 +4838,7 @@ export function InteractiveApp({ factory, initialSessions, initialPrompt, initia
4794
4838
  if (!prompt || prompt === '!')
4795
4839
  return;
4796
4840
  const copyCommand = /^\/copy(?:\s+(\d+))?$/u.exec(prompt);
4841
+ const colorCommand = /^\/color(?:\s+([\s\S]+))?$/u.exec(prompt);
4797
4842
  const sandboxCommand = /^\/sandbox(?:\s+([\s\S]+))?$/u.exec(prompt);
4798
4843
  const tuiCommand = /^\/tui(?:\s+(default|fullscreen))?$/u.exec(prompt);
4799
4844
  const renameCommand = /^\/rename(?:\s+(.+))?$/u.exec(prompt);
@@ -4808,12 +4853,14 @@ export function InteractiveApp({ factory, initialSessions, initialPrompt, initia
4808
4853
  else if (prompt === '/new') {
4809
4854
  statusLineSessionId.current = randomUUID();
4810
4855
  setSessionId(null);
4856
+ setSessionColor(undefined);
4811
4857
  setPendingFork(false);
4812
4858
  append({ kind: 'notice', text: 'Started a new session.' });
4813
4859
  }
4814
4860
  else if (prompt === '/clear') {
4815
4861
  statusLineSessionId.current = randomUUID();
4816
4862
  setSessionId(null);
4863
+ setSessionColor(undefined);
4817
4864
  setPendingFork(false);
4818
4865
  setHistory([]);
4819
4866
  setUsage(undefined);
@@ -4951,6 +4998,9 @@ export function InteractiveApp({ factory, initialSessions, initialPrompt, initia
4951
4998
  text: 'The /agents wizard has been removed.\n\nAsk Claude to create or update subagents for you (e.g. "create a code-reviewer subagent that ..."),\nor edit the files directly:\n • .claude/agents/ (this project)\n • ~/.claude/agents/ (all projects)\n\nDocs: https://code.claude.com/docs/en/sub-agents',
4952
4999
  });
4953
5000
  }
5001
+ else if (colorCommand) {
5002
+ changeSessionColor(prompt, parseAgentColorInput(colorCommand[1] ?? ''));
5003
+ }
4954
5004
  else if (btwCommand) {
4955
5005
  const sideQuestion = btwCommand[1]?.trim();
4956
5006
  if (sideQuestion)
@@ -5420,7 +5470,9 @@ export function InteractiveApp({ factory, initialSessions, initialPrompt, initia
5420
5470
  label: 'Save to file',
5421
5471
  description: 'Save the conversation to a file in the current directory',
5422
5472
  },
5423
- ], selectedIndex: menu.selectedIndex, footer: "Esc to cancel", width: width, screenReader: axScreenReader })) : menu.kind === 'copy' ? (_jsx(SelectionMenu, { title: "Copy", description: "Select content to copy:", options: menu.candidates, selectedIndex: menu.selectedIndex, footer: "Enter to copy \u00B7 w to write to /tmp/claude \u00B7 Esc to cancel", width: width, screenReader: axScreenReader })) : null) : (_jsxs(_Fragment, { children: [commandPaletteVisible ? (_jsx(CommandPalette, { commands: matchingSlashCommands, selectedIndex: selectedSlashCommandIndex, width: width, screenReader: axScreenReader })) : null, filePickerVisible ? (_jsx(MentionPicker, { entries: matchingMentionEntries, selectedIndex: selectedFileIndex, width: width, screenReader: axScreenReader })) : null, exitConfirmation ? (_jsx(Text, { color: activePalette.warning, children: "Press Ctrl-C again to exit" })) : null, _jsx(Composer, { input: shellMode ? input.slice(1) : input, cursor: shellMode ? Math.max(0, inputCursor - 1) : inputCursor, shellMode: shellMode, busy: busy, clipboardBusy: clipboardBusy, status: status, display: runtimeDisplay, ...(usage === undefined ? {} : { usage }), ...(costUsd === undefined ? {} : { costUsd }), width: width, screenReader: axScreenReader, hasThinking: hasDetailedTranscript, thinkingExpanded: thinkingExpanded, reduceMotion: runtimeSettings.reduceMotion, progressBar: runtimeSettings.progressBar, ...(runtimeSettings.turnDuration
5473
+ ], selectedIndex: menu.selectedIndex, footer: "Esc to cancel", width: width, screenReader: axScreenReader })) : menu.kind === 'copy' ? (_jsx(SelectionMenu, { title: "Copy", description: "Select content to copy:", options: menu.candidates, selectedIndex: menu.selectedIndex, footer: "Enter to copy \u00B7 w to write to /tmp/claude \u00B7 Esc to cancel", width: width, screenReader: axScreenReader })) : null) : (_jsxs(_Fragment, { children: [commandPaletteVisible ? (_jsx(CommandPalette, { commands: matchingSlashCommands, selectedIndex: selectedSlashCommandIndex, width: width, screenReader: axScreenReader })) : null, filePickerVisible ? (_jsx(MentionPicker, { entries: matchingMentionEntries, selectedIndex: selectedFileIndex, width: width, screenReader: axScreenReader })) : null, exitConfirmation ? (_jsx(Text, { color: activePalette.warning, children: "Press Ctrl-C again to exit" })) : null, _jsx(Composer, { input: shellMode ? input.slice(1) : input, cursor: shellMode ? Math.max(0, inputCursor - 1) : inputCursor, shellMode: shellMode, ...(sessionColor === undefined ? {} : { sessionColor }), ...(commandArgumentHint === undefined
5474
+ ? {}
5475
+ : { commandArgumentHint }), busy: busy, clipboardBusy: clipboardBusy, status: status, display: runtimeDisplay, ...(usage === undefined ? {} : { usage }), ...(costUsd === undefined ? {} : { costUsd }), width: width, screenReader: axScreenReader, hasThinking: hasDetailedTranscript, thinkingExpanded: thinkingExpanded, reduceMotion: runtimeSettings.reduceMotion, progressBar: runtimeSettings.progressBar, ...(runtimeSettings.turnDuration
5424
5476
  ? (() => {
5425
5477
  const duration = formatTurnDuration(turnDuration);
5426
5478
  return duration === undefined
@@ -5534,11 +5586,16 @@ export async function runInteractive(options) {
5534
5586
  initialAgentPromptResolved = true;
5535
5587
  }
5536
5588
  let initialHistory = [];
5589
+ let initialSessionColor;
5537
5590
  try {
5538
5591
  initialHistory =
5539
5592
  resume?.sessionId === undefined || listing.transcript === undefined
5540
5593
  ? []
5541
5594
  : await listing.transcript(resume.sessionId);
5595
+ initialSessionColor =
5596
+ resume?.sessionId === undefined || listing.agentColor === undefined
5597
+ ? undefined
5598
+ : await listing.agentColor(resume.sessionId);
5542
5599
  }
5543
5600
  catch (error) {
5544
5601
  try {
@@ -5563,7 +5620,7 @@ export async function runInteractive(options) {
5563
5620
  let cleanup = null;
5564
5621
  let backgrounded;
5565
5622
  rendererChange = null;
5566
- const instance = render(_jsx(InteractiveApp, { factory: options.factory, initialSessions: initialSessions, slashCommands: initialSlashCommands, agents: initialAgents, initialHistory: history, runtimeSettings: currentRuntimeSettings, ...(options.settingSources === undefined
5623
+ const instance = render(_jsx(InteractiveApp, { factory: options.factory, initialSessions: initialSessions, slashCommands: initialSlashCommands, agents: initialAgents, initialHistory: history, ...(initialSessionColor === undefined ? {} : { initialSessionColor }), runtimeSettings: currentRuntimeSettings, ...(options.settingSources === undefined
5567
5624
  ? {}
5568
5625
  : { settingSources: options.settingSources }), initialThemeSettings: initialThemeSettings, ...(initialThemeLoadError === undefined
5569
5626
  ? {}
@@ -206,6 +206,13 @@ export interface ProtocolResult {
206
206
  }
207
207
  export declare function createSuccessResult(result: ProtocolResult, info: CliRuntimeInfo, startedAt: number, modelTurns: number): Record<string, unknown>;
208
208
  export declare function createErrorResult(message: string, sessionId: string, startedAt: number, modelTurns: number): Record<string, unknown>;
209
+ /**
210
+ * Returns the argument text of a headless `/color` prompt, or undefined when
211
+ * the prompt is not a bare `/color` command. A trailing space matches with an
212
+ * empty argument (random color), while `/colorblue` and `/colorful` are not
213
+ * the command.
214
+ */
215
+ export declare function matchHeadlessColorCommand(prompt: string): string | undefined;
209
216
  export declare function parseCliInvocation(argv: readonly string[]): CliInvocation;
210
217
  export declare function readStreamJsonMessages(input: AsyncIterable<string | Uint8Array>): AsyncGenerator<StreamJsonMessage>;
211
218
  export declare function readStreamUserMessages(input: AsyncIterable<string | Uint8Array>): AsyncGenerator<StreamUserMessage>;
@@ -232,6 +239,12 @@ export declare class StreamJsonOutput {
232
239
  constructor(write: (value: unknown) => void, info: CliRuntimeInfo, sessionId: string, includePartialMessages: boolean, includeHookEvents?: boolean);
233
240
  init(): void;
234
241
  replayUser(message: StreamUserMessage['message']): void;
242
+ /**
243
+ * Emits the synthetic assistant message for a provider-free local command
244
+ * (e.g. /color). It must not touch the turn state, so the following result
245
+ * keeps num_turns 0 and no stream_event records are produced.
246
+ */
247
+ syntheticAssistant(text: string): void;
235
248
  controlRequest(request: {
236
249
  request_id: string;
237
250
  request: Record<string, unknown>;
@@ -80,6 +80,18 @@ export function createErrorResult(message, sessionId, startedAt, modelTurns) {
80
80
  uuid: randomUUID(),
81
81
  };
82
82
  }
83
+ /**
84
+ * Returns the argument text of a headless `/color` prompt, or undefined when
85
+ * the prompt is not a bare `/color` command. A trailing space matches with an
86
+ * empty argument (random color), while `/colorblue` and `/colorful` are not
87
+ * the command.
88
+ */
89
+ export function matchHeadlessColorCommand(prompt) {
90
+ const match = /^\/color(?:\s+([\s\S]*))?$/u.exec(prompt);
91
+ if (match === null)
92
+ return undefined;
93
+ return match[1] ?? '';
94
+ }
83
95
  const INPUT_FORMATS = ['text', 'stream-json'];
84
96
  const OUTPUT_FORMATS = ['text', 'json', 'stream-json'];
85
97
  const PERMISSION_MODES = [
@@ -1594,6 +1606,28 @@ export class StreamJsonOutput {
1594
1606
  replayUser(message) {
1595
1607
  this.write({ type: 'user', message, session_id: this.sessionId });
1596
1608
  }
1609
+ /**
1610
+ * Emits the synthetic assistant message for a provider-free local command
1611
+ * (e.g. /color). It must not touch the turn state, so the following result
1612
+ * keeps num_turns 0 and no stream_event records are produced.
1613
+ */
1614
+ syntheticAssistant(text) {
1615
+ this.write({
1616
+ type: 'assistant',
1617
+ message: {
1618
+ id: randomUUID(),
1619
+ type: 'message',
1620
+ role: 'assistant',
1621
+ model: '<synthetic>',
1622
+ content: [{ type: 'text', text }],
1623
+ stop_reason: 'stop_sequence',
1624
+ stop_sequence: '',
1625
+ usage: { input_tokens: 0, output_tokens: 0 },
1626
+ },
1627
+ parent_tool_use_id: null,
1628
+ session_id: this.sessionId,
1629
+ });
1630
+ }
1597
1631
  controlRequest(request) {
1598
1632
  this.write({ type: 'control_request', ...request });
1599
1633
  }
@@ -1,4 +1,4 @@
1
- export type ClaudeCommandDisposition = 'included' | 'deferred' | 'excluded';
1
+ export type ClaudeCommandDisposition = 'included' | 'required' | 'deferred' | 'excluded';
2
2
  export type ClaudeCommandVisibility = 'visible' | 'hidden' | 'conditional';
3
3
  export interface ClaudeCommandInventoryEntry {
4
4
  name: string;
@@ -14,7 +14,7 @@ export declare const CLAUDE_2_1_208_COMMAND_INVENTORY: readonly [{
14
14
  readonly name: "advisor";
15
15
  readonly disposition: "deferred";
16
16
  readonly visibility: "conditional";
17
- readonly reason: "Conditional advice mode is required parity work and is not implemented yet.";
17
+ readonly reason: "Conditional advice mode is an optional, demand-driven feature that does not block developer-core closure.";
18
18
  }, {
19
19
  readonly name: "agents";
20
20
  readonly disposition: "included";
@@ -29,18 +29,17 @@ export declare const CLAUDE_2_1_208_COMMAND_INVENTORY: readonly [{
29
29
  readonly visibility: "visible";
30
30
  }, {
31
31
  readonly name: "chrome";
32
- readonly disposition: "deferred";
32
+ readonly disposition: "excluded";
33
33
  readonly visibility: "conditional";
34
- readonly reason: "The CLI-driven Chrome integration is required parity work and is not implemented yet.";
34
+ readonly reason: "Claude-AI subscription-gated Chrome Beta; a future provider-neutral browser feature is separate from this command.";
35
35
  }, {
36
36
  readonly name: "clear";
37
37
  readonly disposition: "included";
38
38
  readonly visibility: "visible";
39
39
  }, {
40
40
  readonly name: "color";
41
- readonly disposition: "deferred";
41
+ readonly disposition: "included";
42
42
  readonly visibility: "visible";
43
- readonly reason: "The dedicated /color contract is required; /theme is not a substitute.";
44
43
  }, {
45
44
  readonly name: "compact";
46
45
  readonly disposition: "included";
@@ -64,7 +63,7 @@ export declare const CLAUDE_2_1_208_COMMAND_INVENTORY: readonly [{
64
63
  readonly visibility: "conditional";
65
64
  }, {
66
65
  readonly name: "cost";
67
- readonly disposition: "deferred";
66
+ readonly disposition: "required";
68
67
  readonly visibility: "conditional";
69
68
  readonly reason: "The dedicated /cost contract is required; /status is not a substitute.";
70
69
  }, {
@@ -73,7 +72,7 @@ export declare const CLAUDE_2_1_208_COMMAND_INVENTORY: readonly [{
73
72
  readonly visibility: "visible";
74
73
  }, {
75
74
  readonly name: "doctor";
76
- readonly disposition: "deferred";
75
+ readonly disposition: "required";
77
76
  readonly visibility: "conditional";
78
77
  readonly reason: "Interactive /doctor is required; the top-level command is not a substitute.";
79
78
  }, {
@@ -88,7 +87,7 @@ export declare const CLAUDE_2_1_208_COMMAND_INVENTORY: readonly [{
88
87
  readonly name: "fast";
89
88
  readonly disposition: "deferred";
90
89
  readonly visibility: "conditional";
91
- readonly reason: "The /fast state flow is required; model and effort controls are not a substitute.";
90
+ readonly reason: "The /fast state flow is an optional convenience; model and effort controls cover the core flow, so it is deferred without blocking developer-core closure.";
92
91
  }, {
93
92
  readonly name: "files";
94
93
  readonly disposition: "excluded";
@@ -96,9 +95,9 @@ export declare const CLAUDE_2_1_208_COMMAND_INVENTORY: readonly [{
96
95
  readonly reason: "The source command is restricted to the internal Ant user type.";
97
96
  }, {
98
97
  readonly name: "heapdump";
99
- readonly disposition: "deferred";
98
+ readonly disposition: "excluded";
100
99
  readonly visibility: "hidden";
101
- readonly reason: "The hidden heap-diagnostic contract is required parity work and is not implemented yet.";
100
+ readonly reason: "Hidden V8 maintainer diagnostic that writes a heap dump to Desktop; not a developer-core command.";
102
101
  }, {
103
102
  readonly name: "help";
104
103
  readonly disposition: "included";
@@ -189,7 +188,7 @@ export declare const CLAUDE_2_1_208_COMMAND_INVENTORY: readonly [{
189
188
  readonly name: "stats";
190
189
  readonly disposition: "deferred";
191
190
  readonly visibility: "visible";
192
- readonly reason: "Historical usage statistics are required parity work and are not implemented yet.";
191
+ readonly reason: "Historical usage statistics are an optional, demand-driven feature that does not block developer-core closure.";
193
192
  }, {
194
193
  readonly name: "status";
195
194
  readonly disposition: "included";
@@ -262,7 +261,7 @@ export declare const CLAUDE_2_1_208_COMMAND_INVENTORY: readonly [{
262
261
  readonly name: "insights";
263
262
  readonly disposition: "deferred";
264
263
  readonly visibility: "visible";
265
- readonly reason: "Retrospective insights are required parity work and are not implemented yet.";
264
+ readonly reason: "Retrospective insights are an optional, demand-driven feature that does not block developer-core closure.";
266
265
  }, {
267
266
  readonly name: "vim";
268
267
  readonly disposition: "included";
@@ -278,24 +277,24 @@ export declare const CLAUDE_2_1_208_COMMAND_INVENTORY: readonly [{
278
277
  readonly visibility: "conditional";
279
278
  }, {
280
279
  readonly name: "buddy";
281
- readonly disposition: "deferred";
280
+ readonly disposition: "excluded";
282
281
  readonly visibility: "conditional";
283
- readonly reason: "This conditional source mode is required parity work and is not implemented yet.";
282
+ readonly reason: "Compile/build-feature-gated experiment, not a stable single-user developer-core command.";
284
283
  }, {
285
284
  readonly name: "proactive";
286
- readonly disposition: "deferred";
285
+ readonly disposition: "excluded";
287
286
  readonly visibility: "conditional";
288
- readonly reason: "This conditional source mode is required parity work and is not implemented yet.";
287
+ readonly reason: "Compile/build-feature-gated experiment, not a stable single-user developer-core command.";
289
288
  }, {
290
289
  readonly name: "brief";
291
- readonly disposition: "deferred";
290
+ readonly disposition: "excluded";
292
291
  readonly visibility: "conditional";
293
- readonly reason: "This conditional source mode is required parity work and is not implemented yet.";
292
+ readonly reason: "Compile/build-feature-gated experiment, not a stable single-user developer-core command.";
294
293
  }, {
295
294
  readonly name: "assistant";
296
- readonly disposition: "deferred";
295
+ readonly disposition: "excluded";
297
296
  readonly visibility: "conditional";
298
- readonly reason: "This conditional source mode is required parity work and is not implemented yet.";
297
+ readonly reason: "Compile/build-feature-gated experiment, not a stable single-user developer-core command.";
299
298
  }, {
300
299
  readonly name: "remote-control";
301
300
  readonly disposition: "excluded";
@@ -310,17 +309,17 @@ export declare const CLAUDE_2_1_208_COMMAND_INVENTORY: readonly [{
310
309
  readonly name: "voice";
311
310
  readonly disposition: "deferred";
312
311
  readonly visibility: "conditional";
313
- readonly reason: "Conditional voice input is required parity work and is not implemented yet.";
312
+ readonly reason: "Conditional voice input is an optional, demand-driven feature that does not block developer-core closure.";
314
313
  }, {
315
314
  readonly name: "think-back";
316
- readonly disposition: "deferred";
315
+ readonly disposition: "excluded";
317
316
  readonly visibility: "conditional";
318
- readonly reason: "The conditional retrospective flow is required parity work and is not implemented yet.";
317
+ readonly reason: "The 2025 year-in-review campaign surface; a marketing flow, not a stable developer-core command.";
319
318
  }, {
320
319
  readonly name: "thinkback-play";
321
- readonly disposition: "deferred";
320
+ readonly disposition: "excluded";
322
321
  readonly visibility: "hidden";
323
- readonly reason: "The hidden retrospective playback flow is required parity work and is not implemented yet.";
322
+ readonly reason: "Hidden animation for the 2025 year-in-review campaign; a marketing flow, not a stable developer-core command.";
324
323
  }, {
325
324
  readonly name: "permissions";
326
325
  readonly disposition: "included";
@@ -376,9 +375,9 @@ export declare const CLAUDE_2_1_208_COMMAND_INVENTORY: readonly [{
376
375
  readonly visibility: "conditional";
377
376
  }, {
378
377
  readonly name: "torch";
379
- readonly disposition: "deferred";
378
+ readonly disposition: "excluded";
380
379
  readonly visibility: "conditional";
381
- readonly reason: "This source-gated behavior is required conditional parity work and is not implemented yet.";
380
+ readonly reason: "Compile/build-feature-gated experiment, not a stable single-user developer-core command.";
382
381
  }];
383
382
  export declare const CLAUDE_2_1_208_COMMAND_BY_NAME: ReadonlyMap<string, ClaudeCommandInventoryEntry>;
384
383
  //# sourceMappingURL=claude-command-inventory.d.ts.map