praxis-agent 0.20.18 → 0.20.21

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
@@ -88,7 +88,9 @@ troubleshooting. Run `praxis --help` for the authoritative command surface.
88
88
 
89
89
  ## What Praxis provides
90
90
 
91
- - **Local agent runtime** — Claude-style responsive TUI with a shared-command
91
+ - **Local agent runtime** — Claude-style responsive TUI with a fixed fullscreen
92
+ viewport, non-shrinking composer/status area, and complete bounded welcome
93
+ surface, plus a shared-command
92
94
  slash palette, tabbed help and shortcut surfaces, searchable resume picker,
93
95
  restored active-branch conversation history, streaming and expandable
94
96
  thinking, grouped multi-file reads, globally expandable tool results,
@@ -11,7 +11,7 @@ import { permissionRuleValueToString } from '../permissions/permission-updates.j
11
11
  import { AgentRunCancelledError } from '../core/runtime.js';
12
12
  import { claudePermissionActionKey, claudePermissionRuleMatches, } from '../permissions/claude-permission-resolver.js';
13
13
  import { redactSensitiveText, sensitiveEnvironmentValues, } from '../platform/sensitive-data.js';
14
- import { CommandPalette, BtwPanel, Composer, DiffDashboard, DialogFrame, ExternalEditorWait, HelpMenu, HookDashboard, ListDashboard, MemoryDashboard, MentionPicker, ModelMenu, PermissionDashboard, SelectionMenu, SessionPicker, ThemePicker, CustomThemeEditor, Transcript, WelcomePanel, useTerminalWidth, } from './tui/claude-style.js';
14
+ import { CommandPalette, BtwPanel, Composer, DiffDashboard, DialogFrame, ExternalEditorWait, HelpMenu, HookDashboard, ListDashboard, MemoryDashboard, MentionPicker, ModelMenu, PermissionDashboard, SelectionMenu, SessionPicker, ThemePicker, CustomThemeEditor, Transcript, WelcomePanel, useTerminalRows, useTerminalWidth, } from './tui/claude-style.js';
15
15
  import { loadTuiMemoryFiles, openTuiMemoryFolder, } from './tui/memory-files.js';
16
16
  import { loadClaudeReleaseNotes } from './tui/release-notes.js';
17
17
  import { createClaudeStatusLineInput, StatusLine } from './tui/status-line.js';
@@ -264,6 +264,7 @@ const HIDDEN_TUI_SLASH_COMMANDS = new Set([
264
264
  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, doctorLoader, 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, }) {
265
265
  const { exit, suspendTerminal, waitUntilRenderFlush } = useApp();
266
266
  const width = useTerminalWidth(terminalWidth);
267
+ const rows = useTerminalRows();
267
268
  const keybindingsRoot = useMemo(() => resolve(keybindingsConfigRoot ??
268
269
  process.env.CLAUDE_CONFIG_DIR ??
269
270
  resolve(homedir(), '.claude')), [keybindingsConfigRoot]);
@@ -320,6 +321,7 @@ export function InteractiveApp({ factory, initialSessions, initialPrompt, initia
320
321
  const [themeSettings, setThemeSettings] = useState(initialThemeSettings ?? DEFAULT_TUI_THEME_SETTINGS);
321
322
  const [runtimeSettings, setRuntimeSettings] = useState(suppliedRuntimeSettings ??
322
323
  projectRuntimeSettings({ settings: {}, state: {} }));
324
+ const fixedViewport = runtimeSettings.tui === 'fullscreen' && rows !== undefined;
323
325
  const runtimeSettingsRef = useRef(runtimeSettings);
324
326
  runtimeSettingsRef.current = runtimeSettings;
325
327
  runtimeGitignoreRef.current = runtimeSettings.gitignore;
@@ -413,6 +415,11 @@ export function InteractiveApp({ factory, initialSessions, initialPrompt, initia
413
415
  const [costUsd, setCostUsd] = useState();
414
416
  const [contextWindowTokens, setContextWindowTokens] = useState(display.contextWindowTokens);
415
417
  const [history, setHistory] = useState([...initialHistory]);
418
+ // Startup diagnostics are useful before the first prompt, but they are not
419
+ // conversation history and must not suppress the new-session welcome panel.
420
+ const hasConversationHistory = history.some((item) => item.kind !== 'notice' &&
421
+ item.kind !== 'warning' &&
422
+ item.kind !== 'local-result');
416
423
  const sessionLoadRef = useRef(0);
417
424
  const [turnDiffs, setTurnDiffs] = useState([]);
418
425
  const turnNumberRef = useRef(0);
@@ -5626,7 +5633,9 @@ export function InteractiveApp({ factory, initialSessions, initialPrompt, initia
5626
5633
  }
5627
5634
  editComposer();
5628
5635
  });
5629
- return (_jsx(TuiThemeProvider, { settings: themeSettings, children: _jsx(Box, { flexDirection: "column", children: selectingSession ? (_jsx(SessionPicker, { sessions: filteredPickerChoices, selectedIndex: selectedIndex, screenReader: axScreenReader, query: sessionSearch })) : (_jsxs(_Fragment, { children: [!axScreenReader && history.length === 0 && !sessionId ? (_jsx(WelcomePanel, { display: runtimeDisplay, width: width, showTips: runtimeSettings.tips })) : null, sessionId ? (_jsxs(Text, { dimColor: true, children: ["Session ", sessionId.slice(0, 8)] })) : null, _jsx(Transcript, { items: history, activeText: activeText, activeThinking: activeThinking, thinkingExpanded: thinkingExpanded, detailedTranscript: thinkingExpanded || runtimeSettings.verbose, screenReader: axScreenReader }), externalEditorRequest !== null ||
5636
+ return (_jsx(TuiThemeProvider, { settings: themeSettings, children: _jsx(Box, { flexDirection: "column", ...(!fixedViewport
5637
+ ? {}
5638
+ : { height: rows, overflowY: 'hidden' }), children: selectingSession ? (_jsx(SessionPicker, { sessions: filteredPickerChoices, selectedIndex: selectedIndex, screenReader: axScreenReader, query: sessionSearch })) : (_jsxs(_Fragment, { children: [!axScreenReader && !hasConversationHistory && !sessionId ? (_jsx(WelcomePanel, { display: runtimeDisplay, width: width, showTips: runtimeSettings.tips })) : null, sessionId ? (_jsxs(Text, { dimColor: true, children: ["Session ", sessionId.slice(0, 8)] })) : null, _jsx(Transcript, { items: history, activeText: activeText, activeThinking: activeThinking, thinkingExpanded: thinkingExpanded, detailedTranscript: thinkingExpanded || runtimeSettings.verbose, screenReader: axScreenReader }), externalEditorRequest !== null ||
5630
5639
  keybindingsEditing ||
5631
5640
  memoryEditorRequest !== null ? (_jsx(ExternalEditorWait, { screenReader: axScreenReader })) : permission ? (permission.kind === 'tool' && toolPermissionModel ? (_jsx(ToolPermissionDialog, { model: toolPermissionModel, selection: permissionSelection, feedbackMode: permissionFeedbackMode, feedback: input, ruleEditor: permissionRuleEditor, screenReader: axScreenReader })) : (_jsxs(DialogFrame, { title: `Retry interrupted ${permission.call.name}?`, screenReader: axScreenReader, children: [_jsx(Box, { flexDirection: "column", paddingX: 1, paddingY: 1, children: _jsx(Text, { bold: true, children: describeTool(permission.call, sensitiveValues) }) }), _jsx(Text, { children: "Do you want to proceed?" }), _jsxs(Text, { inverse: !axScreenReader && permissionSelection === 0, children: [selectionPrefix(permissionSelection === 0, axScreenReader), "1. Yes"] }), _jsxs(Text, { inverse: !axScreenReader && permissionSelection === 1, children: [selectionPrefix(permissionSelection === 1, axScreenReader), "2. No"] }), permissionFeedbackMode ? (_jsxs(Text, { children: ["\u203A", ' ', input ||
5632
5641
  (permissionSelection === 0
@@ -5762,7 +5771,7 @@ export function InteractiveApp({ factory, initialSessions, initialPrompt, initia
5762
5771
  sessionName,
5763
5772
  usage?.inputTokens,
5764
5773
  usage?.outputTokens,
5765
- ].join(':'), ...(settingSources === undefined ? {} : { settingSources }) })] }))] })) }) }));
5774
+ ].join(':'), width: width, ...(settingSources === undefined ? {} : { settingSources }) })] }))] })) }) }));
5766
5775
  }
5767
5776
  export async function runInteractive(options) {
5768
5777
  const controller = new AbortController();
@@ -71,6 +71,7 @@ export interface TuiBtwEntry {
71
71
  error?: string;
72
72
  }
73
73
  export declare function useTerminalWidth(override?: number): number;
74
+ export declare function useTerminalRows(override?: number): number | undefined;
74
75
  export declare function WelcomePanel({ display, width, showTips, }: {
75
76
  display: TuiDisplayMetadata;
76
77
  width: number;
@@ -22,6 +22,23 @@ export function useTerminalWidth(override) {
22
22
  }, [override, stdout]);
23
23
  return Math.max(32, width);
24
24
  }
25
+ export function useTerminalRows(override) {
26
+ const { stdout } = useStdout();
27
+ const initialRows = override ?? (stdout.isTTY ? stdout.rows : undefined);
28
+ const [rows, setRows] = useState(initialRows);
29
+ useEffect(() => {
30
+ if (override !== undefined) {
31
+ setRows(override);
32
+ return;
33
+ }
34
+ const resize = () => setRows(stdout.rows);
35
+ stdout.on('resize', resize);
36
+ return () => {
37
+ stdout.off('resize', resize);
38
+ };
39
+ }, [override, stdout]);
40
+ return rows === undefined ? undefined : Math.max(12, rows);
41
+ }
25
42
  function compactPath(cwd) {
26
43
  const home = process.env.HOME;
27
44
  return home && cwd.startsWith(`${home}/`)
@@ -44,6 +61,45 @@ function permissionLabel(mode) {
44
61
  return 'permissions default';
45
62
  }
46
63
  }
64
+ function compactPermissionLabel(mode) {
65
+ switch (mode) {
66
+ case 'acceptEdits':
67
+ return 'accept edits';
68
+ case 'bypassPermissions':
69
+ return 'bypass';
70
+ case 'dontAsk':
71
+ return 'dont ask';
72
+ case 'plan':
73
+ return 'plan';
74
+ case 'auto':
75
+ return 'auto';
76
+ default:
77
+ return 'default';
78
+ }
79
+ }
80
+ // Width-prioritized composer footer left text. The footer is one deliberate,
81
+ // non-wrapping line: mode and the busy/effort state always win, while
82
+ // shortcuts/agents/thinking hints are dropped or shortened as width shrinks.
83
+ function composerFooterLeft(width, busy, hasThinking, thinkingExpanded, mode, compactMode, includeHints = true) {
84
+ const cancelOrShortcut = busy ? 'esc to interrupt' : '? for shortcuts';
85
+ if (!includeHints) {
86
+ if (busy)
87
+ return `${width >= 60 ? mode : compactMode} · ${cancelOrShortcut}`;
88
+ return width >= 60 ? mode : compactMode;
89
+ }
90
+ if (width >= 60) {
91
+ let text = `${mode} · ${cancelOrShortcut}`;
92
+ if (width >= 80)
93
+ text += ' · ← for agents';
94
+ if (width >= 100 && hasThinking) {
95
+ text += ` · ctrl+o ${thinkingExpanded ? 'collapse' : 'expand'}`;
96
+ }
97
+ return text;
98
+ }
99
+ if (busy)
100
+ return `${compactMode} · ${cancelOrShortcut}`;
101
+ return width >= 40 ? mode : compactMode;
102
+ }
47
103
  function selectionPrefix(selected, screenReader) {
48
104
  if (selected)
49
105
  return screenReader ? 'Selected: ' : ' ❯ ';
@@ -54,7 +110,7 @@ export function WelcomePanel({ display, width, showTips, }) {
54
110
  if (!showTips)
55
111
  return null;
56
112
  const panelWidth = Math.min(100, Math.max(32, width));
57
- const wide = panelWidth >= 68;
113
+ const wide = panelWidth >= 72;
58
114
  const brand = 'Praxis';
59
115
  const model = display.model ?? 'provider default';
60
116
  const effort = display.effort ? ` · ${display.effort} effort` : '';
@@ -63,9 +119,11 @@ export function WelcomePanel({ display, width, showTips, }) {
63
119
  // ╭───Praxis Code vX.Y.Z ───...───╮
64
120
  // Fixed prefix/suffix = "╭───"(4) + "Praxis"(6) + " Code vX.Y.Z "(8 + version)
65
121
  // + "╮"(1), so the fill keeps the row exactly at panelWidth.
66
- const fill = Math.max(1, panelWidth - display.version.length - 19);
67
- const identity = `Welcome to ${brand} · ${model}${effort} · ${cwd}`;
68
- return (_jsxs(Box, { flexDirection: "column", width: panelWidth, children: [_jsxs(Text, { color: palette.muted, children: ['╭───', _jsx(Text, { color: palette.brand, bold: true, children: brand }), ` Code v${display.version} `, _jsx(Text, { dimColor: true, children: '─'.repeat(fill) }), '╮'] }), wide ? (_jsxs(_Fragment, { children: [_jsx(Text, { children: identity }), _jsx(Text, { children: "/init to create CLAUDE.md \u00B7 /config to open settings" })] })) : (_jsxs(_Fragment, { children: [_jsxs(Text, { children: ["Welcome to ", brand] }), _jsxs(Text, { children: [model, effort] }), _jsx(Text, { children: cwd }), _jsx(Text, { children: "/init to create CLAUDE.md" }), _jsx(Text, { children: "/config to open settings" })] }))] }));
122
+ const title = `${brand} Code v${display.version}`;
123
+ const fill = Math.max(1, panelWidth - title.length - 6);
124
+ const leftWidth = Math.max(12, Math.floor((panelWidth - 4) / 2));
125
+ const rightWidth = Math.max(12, panelWidth - leftWidth - 4);
126
+ return (_jsxs(Box, { flexDirection: "column", width: panelWidth, children: [_jsxs(Text, { color: palette.muted, children: ['╭───', _jsx(Text, { color: palette.brand, bold: true, children: brand }), ` Code v${display.version} `, _jsx(Text, { dimColor: true, children: '─'.repeat(fill) }), '╮'] }), _jsxs(Box, { borderStyle: "round", borderColor: palette.muted, borderTop: false, flexDirection: wide ? 'row' : 'column', width: panelWidth, paddingX: 1, children: [_jsxs(Box, { alignItems: wide ? 'center' : undefined, flexDirection: "column", width: wide ? leftWidth : '100%', children: [_jsx(Text, { children: " " }), _jsxs(Text, { bold: true, children: ["Welcome to ", brand] }), _jsx(Text, { children: " " }), _jsx(Text, { color: palette.brand, bold: true, children: "\u2590\u259B\u2588\u2588\u2588\u259C\u258C" }), _jsx(Text, { color: palette.brand, children: "\u259D\u259C\u2588\u2588\u2588\u2588\u2588\u259B\u2598" }), _jsx(Text, { color: palette.brand, children: " \u2598\u2598 \u259D\u259D" }), _jsx(Text, { children: " " }), _jsxs(Text, { wrap: "truncate-end", children: [model, effort] }), _jsx(Text, { dimColor: true, wrap: "truncate-end", children: cwd })] }), _jsxs(Box, { flexDirection: "column", width: wide ? rightWidth : '100%', marginTop: wide ? 0 : 1, children: [_jsx(Text, { bold: true, children: "Get started" }), _jsx(Text, { wrap: "truncate-end", children: "/init to create CLAUDE.md" }), _jsx(Text, { wrap: "truncate-end", children: "/config to open settings" }), _jsx(Text, { dimColor: true, children: " " }), _jsx(Text, { bold: true, children: "Shared with Claude Code" }), _jsx(Text, { dimColor: true, wrap: "truncate-end", children: "Sessions \u00B7 memory \u00B7 skills" })] })] })] }));
69
127
  }
70
128
  const INLINE_SEGMENT_CACHE_MAX = 4096;
71
129
  const inlineSegmentCache = new Map();
@@ -866,7 +924,12 @@ export function Composer({ input, cursor, busy, clipboardBusy = false, status, d
866
924
  : `Prompt: ${input}` }));
867
925
  const line = '─'.repeat(Math.max(12, Math.min(100, width)));
868
926
  const separatorColor = sessionColor === undefined ? undefined : palette.sessionColors[sessionColor];
869
- return (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [usage ? (_jsxs(Text, { dimColor: true, children: ["Context \u00B7 ", usage.inputTokens + usage.outputTokens, " tokens", display.contextWindowTokens
927
+ const footerWidth = Math.min(100, width);
928
+ const footerMode = `⏵⏵ ${permissionLabel(display.permissionMode)}`;
929
+ const footerCompactMode = `⏵⏵ ${compactPermissionLabel(display.permissionMode)}`;
930
+ const footerLeft = composerFooterLeft(footerWidth, busy, hasThinking, thinkingExpanded, footerMode, footerCompactMode);
931
+ const footerMessageLeft = composerFooterLeft(footerWidth, busy, hasThinking, thinkingExpanded, footerMode, footerCompactMode, false);
932
+ return (_jsxs(Box, { flexDirection: "column", marginTop: 1, flexShrink: 0, children: [usage ? (_jsxs(Text, { dimColor: true, children: ["Context \u00B7 ", usage.inputTokens + usage.outputTokens, " tokens", display.contextWindowTokens
870
933
  ? ` / ${display.contextWindowTokens} (${Math.min(100, Math.round(((usage.inputTokens + usage.outputTokens) /
871
934
  display.contextWindowTokens) *
872
935
  100))}%)`
@@ -878,9 +941,7 @@ export function Composer({ input, cursor, busy, clipboardBusy = false, status, d
878
941
  ? 'Enter a shell command'
879
942
  : 'Try "review this project"' }))] })), _jsx(Text, { ...(separatorColor === undefined
880
943
  ? { dimColor: true }
881
- : { color: separatorColor }), children: line }), shortcutsVisible ? (_jsx(ShortcutHelp, { width: width })) : (_jsxs(Box, { width: Math.min(100, width), children: [_jsx(Text, { dimColor: true, children: shellMode ? ('! for bash mode') : (_jsxs(_Fragment, { children: ["\u23F5\u23F5 ", permissionLabel(display.permissionMode), " \u00B7", ' ', busy ? 'esc to interrupt' : '? for shortcuts', " \u00B7 \u2190 for agents", hasThinking
882
- ? ` · ctrl+o ${thinkingExpanded ? 'collapse' : 'expand'}`
883
- : ''] })) }), _jsx(Box, { flexGrow: 1 }), footerMessage ? (footerMessage.isError ? (_jsx(Text, { color: palette.error, children: footerMessage.text })) : (_jsx(Text, { dimColor: true, children: footerMessage.text }))) : prStatus ? (_jsx(Text, { dimColor: true, children: prStatus })) : turnDuration ? (_jsxs(Text, { dimColor: true, children: ["Cooked for ", turnDuration] })) : display.effort ? (_jsxs(Text, { children: [_jsxs(Text, { color: palette.accent, children: ["\u25CF ", display.effort] }), _jsxs(Text, { dimColor: true, children: [' ', "\u00B7 ", editorMode === 'vim' ? 'vim' : '/effort'] })] })) : null] }))] }));
944
+ : { color: separatorColor }), children: line }), shortcutsVisible ? (_jsx(ShortcutHelp, { width: width })) : (_jsx(Box, { width: footerWidth, children: _jsx(Text, { wrap: "truncate", children: shellMode ? (_jsx(Text, { dimColor: true, children: "! for bash mode" })) : footerMessage ? (_jsxs(Text, { children: [_jsxs(Text, { dimColor: true, children: [footerMessageLeft, " \u00B7 "] }), footerMessage.isError ? (_jsx(Text, { color: palette.error, children: footerMessage.text })) : (_jsx(Text, { dimColor: true, children: footerMessage.text }))] })) : prStatus ? (_jsxs(Text, { children: [_jsxs(Text, { dimColor: true, children: [footerLeft, " \u00B7 "] }), _jsx(Text, { dimColor: true, children: prStatus })] })) : turnDuration ? (_jsxs(Text, { children: [_jsxs(Text, { dimColor: true, children: [footerLeft, " \u00B7 "] }), _jsxs(Text, { dimColor: true, children: ["Cooked for ", turnDuration] })] })) : display.effort ? (_jsxs(Text, { children: [_jsx(Text, { dimColor: true, children: footerLeft }), _jsx(Text, { dimColor: true, children: " \u00B7 " }), _jsxs(Text, { color: palette.accent, children: ["\u25CF ", display.effort] }), footerWidth >= 100 ? (_jsxs(Text, { dimColor: true, children: [' ', "\u00B7 ", editorMode === 'vim' ? 'vim' : '/effort'] })) : null] })) : (_jsx(Text, { dimColor: true, children: footerLeft })) }) }))] }));
884
945
  }
885
946
  export function DialogFrame({ title, children, screenReader, }) {
886
947
  const palette = useTuiPalette();
@@ -61,6 +61,7 @@ export declare function executeClaudeStatusLine(setting: ClaudeStatusLineSetting
61
61
  cwd: string;
62
62
  signal?: AbortSignal;
63
63
  timeoutMs?: number;
64
+ columns?: number;
64
65
  }): Promise<string | undefined>;
65
66
  export declare function createClaudeStatusLineInput(options: {
66
67
  configRoot: string;
@@ -78,11 +79,12 @@ export declare function createClaudeStatusLineInput(options: {
78
79
  contextWindowTokens?: number;
79
80
  vimMode?: 'INSERT' | 'NORMAL';
80
81
  }): ClaudeStatusLineInput;
81
- export declare function StatusLine({ configRoot, cwd, input, refreshKey, settingSources, }: {
82
+ export declare function StatusLine({ configRoot, cwd, input, refreshKey, width, settingSources, }: {
82
83
  configRoot: string;
83
84
  cwd: string;
84
85
  input: ClaudeStatusLineInput;
85
86
  refreshKey: string;
87
+ width?: number;
86
88
  settingSources?: readonly ClaudeResourceScope[];
87
89
  }): import("react").JSX.Element | null;
88
90
  //# sourceMappingURL=status-line.d.ts.map
@@ -49,10 +49,16 @@ export async function executeClaudeStatusLine(setting, input, options = {
49
49
  };
50
50
  let child;
51
51
  try {
52
+ const env = { ...process.env };
53
+ if (options.columns !== undefined &&
54
+ Number.isFinite(options.columns) &&
55
+ options.columns > 0) {
56
+ env.COLUMNS = String(Math.trunc(options.columns));
57
+ }
52
58
  child = spawn(commandShell(), commandShellArguments(setting.command), {
53
59
  cwd: options.cwd,
54
60
  detached: process.platform !== 'win32',
55
- env: process.env,
61
+ env,
56
62
  stdio: ['pipe', 'pipe', 'ignore'],
57
63
  });
58
64
  }
@@ -169,7 +175,7 @@ export function createClaudeStatusLineInput(options) {
169
175
  ...(options.vimMode ? { vim: { mode: options.vimMode } } : {}),
170
176
  };
171
177
  }
172
- export function StatusLine({ configRoot, cwd, input, refreshKey, settingSources, }) {
178
+ export function StatusLine({ configRoot, cwd, input, refreshKey, width, settingSources, }) {
173
179
  const [text, setText] = useState();
174
180
  const [padding, setPadding] = useState(0);
175
181
  const timer = useRef(undefined);
@@ -193,7 +199,11 @@ export function StatusLine({ configRoot, cwd, input, refreshKey, settingSources,
193
199
  setText(undefined);
194
200
  return;
195
201
  }
196
- const result = await executeClaudeStatusLine(loaded.setting, latestInput.current, { cwd, signal: current.signal });
202
+ const result = await executeClaudeStatusLine(loaded.setting, latestInput.current, {
203
+ cwd,
204
+ signal: current.signal,
205
+ ...(width === undefined ? {} : { columns: width }),
206
+ });
197
207
  if (!current.signal.aborted)
198
208
  setText(result);
199
209
  }
@@ -201,7 +211,7 @@ export function StatusLine({ configRoot, cwd, input, refreshKey, settingSources,
201
211
  if (!current.signal.aborted)
202
212
  setText(undefined);
203
213
  }
204
- }, [configRoot, cwd, settingSources]);
214
+ }, [configRoot, cwd, settingSources, width]);
205
215
  const schedule = useCallback(() => {
206
216
  if (timer.current)
207
217
  clearTimeout(timer.current);
@@ -229,6 +239,6 @@ export function StatusLine({ configRoot, cwd, input, refreshKey, settingSources,
229
239
  }, [configRoot, cwd, schedule]);
230
240
  if (!text)
231
241
  return null;
232
- return (_jsx(Box, { paddingX: padding, children: _jsx(Text, { dimColor: true, wrap: "truncate", children: text }) }));
242
+ return (_jsx(Box, { paddingX: padding, flexShrink: 0, children: _jsx(Text, { dimColor: true, wrap: "truncate", children: text }) }));
233
243
  }
234
244
  //# sourceMappingURL=status-line.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "praxis-agent",
3
- "version": "0.20.18",
3
+ "version": "0.20.21",
4
4
  "description": "Local-first, single-user general agent for the command line.",
5
5
  "license": "MIT",
6
6
  "author": "wuqisen",